am bb9062f2: am e93dd9cd: Bump the timeout to accommodate for slower devices.

* commit 'bb9062f2a414709805c049e3a074c2b63efc1154':
  Bump the timeout to accommodate for slower devices.
diff --git a/.gitignore b/.gitignore
index b8a343c..07a80d6 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,3 +4,6 @@
 .project
 .cproject
 .classpath
+/bin
+.idea/*
+.idea/
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 ce12fff..615e955 100644
--- a/CtsTestCaseList.mk
+++ b/CtsTestCaseList.mk
@@ -40,7 +40,6 @@
     CtsHoloDeviceApp \
     CtsMonkeyApp \
     CtsMonkeyApp2 \
-    CtsSampleDeviceApp \
     CtsSomeAccessibilityServices \
     CtsTestStubs \
     SignatureTest \
@@ -74,6 +73,7 @@
     CtsAdminTestCases \
     CtsAnimationTestCases \
     CtsAppTestCases \
+    CtsAppWidgetTestCases \
     CtsBluetoothTestCases \
     CtsCalendarcommon2TestCases \
     CtsContentTestCases \
@@ -88,6 +88,7 @@
     CtsGraphicsTestCases \
     CtsGraphics2TestCases \
     CtsHardwareTestCases \
+    CtsHoloTestCases \
     CtsJniTestCases \
     CtsKeystoreTestCases \
     CtsLocationTestCases \
@@ -103,11 +104,11 @@
     CtsPermission2TestCases \
     CtsPreferenceTestCases \
     CtsPreference2TestCases \
+    CtsPrintTestCases \
     CtsProviderTestCases \
     CtsRenderscriptTestCases \
     CtsRenderscriptGraphicsTestCases \
     CtsRsCppTestCases \
-    CtsSampleDeviceTestCases \
     CtsSaxTestCases \
     CtsSecurityTestCases \
     CtsSpeechTestCases \
@@ -115,6 +116,9 @@
     CtsTextTestCases \
     CtsTextureViewTestCases \
     CtsThemeTestCases \
+    CtsTvTestCases \
+    CtsUiAutomationTestCases \
+    CtsUiRenderingTestCases \
     CtsUtilTestCases \
     CtsViewTestCases \
     CtsWebkitTestCases \
@@ -129,10 +133,10 @@
 cts_host_libraries := \
     CtsAdbTests \
     CtsAppSecurityTests \
+    CtsHoloHostTestCases \
     CtsHostJank \
     CtsHostUi \
     CtsMonkeyTestCases \
-    CtsSampleHostTestCases \
     CtsUsbTests
 
 # Native test executables that need to have associated test XMLs.
@@ -148,7 +152,14 @@
     CtsUiAutomatorTests
 
 cts_device_jars := \
-    CtsDeviceJank
+    CtsDeviceJank \
+    CtsPrintInstrument
+
+cts_device_executables := \
+    print-instrument
+
+cts_target_junit_tests := \
+    CtsJdwp
 
 # All the files that will end up under the repository/testcases
 # directory of the final CTS distribution.
@@ -156,15 +167,17 @@
     $(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-ui-lib-paths,$(cts_target_junit_tests)) \
+    $(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.
 CTS_TEST_XMLS := $(call cts-get-test-xmls,$(cts_host_libraries)) \
     $(call cts-get-test-xmls,$(cts_test_packages)) \
     $(call cts-get-test-xmls,$(cts_native_exes)) \
+    $(call cts-get-test-xmls,$(cts_target_junit_tests)) \
     $(call cts-get-test-xmls,$(cts_ui_tests))
 
-
 # The following files will be placed in the tools directory of the CTS distribution
 CTS_TOOLS_LIST :=
diff --git a/apps/CtsVerifier/AndroidManifest.xml b/apps/CtsVerifier/AndroidManifest.xml
index a84e350..b67bdf8 100644
--- a/apps/CtsVerifier/AndroidManifest.xml
+++ b/apps/CtsVerifier/AndroidManifest.xml
@@ -17,11 +17,11 @@
 
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
       package="com.android.cts.verifier"
-      android:versionCode="1"
-      android:versionName="4.4W_r1">
+      android:versionCode="3"
+      android:versionName="4.4_r3">
 
     <!-- Using 10+ for more complete NFC support... -->
-    <uses-sdk android:minSdkVersion="12"></uses-sdk>
+    <uses-sdk android:minSdkVersion="19"></uses-sdk>
 
     <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
     <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
@@ -215,6 +215,12 @@
         <service android:name=".bluetooth.BleServerService"
                 android:label="ble_server_service_name" />
 
+        <service android:name=".bluetooth.BleAdvertiserService"
+                android:label="@string/ble_advertiser_service_name" />
+
+        <service android:name=".bluetooth.BleScannerService"
+                android:label="@string/ble_scanner_service_name" />
+
         <activity android:name=".bluetooth.BleClientTestActivity"
                 android:label="@string/ble_client_test_name"
                 android:configChanges="keyboardHidden|orientation|screenSize">
@@ -325,6 +331,72 @@
             <meta-data android:name="test_parent" android:value="com.android.cts.verifier.bluetooth.BluetoothTestActivity" />
         </activity>
 
+        <activity android:name=".bluetooth.BleScannerTestActivity"
+                android:label="@string/ble_scanner_test_name"
+                android:configChanges="keyboardHidden|orientation|screenSize">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.cts.intent.category.MANUAL_TEST" />
+            </intent-filter>
+            <meta-data android:name="test_category" android:value="@string/bt_le" />
+            <meta-data android:name="test_parent" android:value="com.android.cts.verifier.bluetooth.BluetoothTestActivity" />
+        </activity>
+
+        <activity android:name=".bluetooth.BleScannerPrivacyMacActivity"
+                android:label="@string/ble_privacy_mac_name"
+                android:configChanges="keyboardHidden|orientation|screenSize">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.cts.intent.category.MANUAL_TEST" />
+            </intent-filter>
+            <meta-data android:name="test_category" android:value="@string/bt_le" />
+            <meta-data android:name="test_parent" android:value="com.android.cts.verifier.bluetooth.BleScannerTestActivity" />
+        </activity>
+
+        <activity android:name=".bluetooth.BleScannerPowerLevelActivity"
+                android:label="@string/ble_power_level_name"
+                android:configChanges="keyboardHidden|orientation|screenSize">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.cts.intent.category.MANUAL_TEST" />
+            </intent-filter>
+            <meta-data android:name="test_category" android:value="@string/bt_le" />
+            <meta-data android:name="test_parent" android:value="com.android.cts.verifier.bluetooth.BleScannerTestActivity" />
+        </activity>
+
+        <activity android:name=".bluetooth.BleAdvertiserTestActivity"
+                android:label="@string/ble_advertiser_test_name"
+                android:configChanges="keyboardHidden|orientation|screenSize">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.cts.intent.category.MANUAL_TEST" />
+            </intent-filter>
+            <meta-data android:name="test_category" android:value="@string/bt_le" />
+            <meta-data android:name="test_parent" android:value="com.android.cts.verifier.bluetooth.BluetoothTestActivity" />
+        </activity>
+
+        <activity android:name=".bluetooth.BleAdvertiserPrivacyMacActivity"
+                android:label="@string/ble_privacy_mac_name"
+                android:configChanges="keyboardHidden|orientation|screenSize">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.cts.intent.category.MANUAL_TEST" />
+            </intent-filter>
+            <meta-data android:name="test_category" android:value="@string/bt_le" />
+            <meta-data android:name="test_parent" android:value="com.android.cts.verifier.bluetooth.BleAdvertiserTestActivity" />
+        </activity>
+
+        <activity android:name=".bluetooth.BleAdvertiserPowerLevelActivity"
+                android:label="@string/ble_power_level_name"
+                android:configChanges="keyboardHidden|orientation|screenSize">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.cts.intent.category.MANUAL_TEST" />
+            </intent-filter>
+            <meta-data android:name="test_category" android:value="@string/bt_le" />
+            <meta-data android:name="test_parent" android:value="com.android.cts.verifier.bluetooth.BleAdvertiserTestActivity" />
+        </activity>
+
         <activity android:name=".suid.SuidFilesActivity"
                 android:label="@string/suid_files"
                 android:configChanges="keyboardHidden|orientation|screenSize">
diff --git a/apps/CtsVerifier/res/layout/ble_advertiser_power_level.xml b/apps/CtsVerifier/res/layout/ble_advertiser_power_level.xml
new file mode 100644
index 0000000..ad45c52
--- /dev/null
+++ b/apps/CtsVerifier/res/layout/ble_advertiser_power_level.xml
@@ -0,0 +1,45 @@
+<?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.
+-->
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:orientation="vertical"
+        android:padding="10dip"
+        >
+
+    <LinearLayout android:orientation="horizontal"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_centerInParent="true"
+            >
+        <Button android:id="@+id/ble_power_level_start"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="@string/ble_power_level_start"
+                />
+        <Button android:id="@+id/ble_power_level_stop"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="@string/ble_power_level_stop"
+                />
+    </LinearLayout>
+
+    <include android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_alignParentBottom="true"
+            layout="@layout/pass_fail_buttons"
+            />
+</RelativeLayout>
diff --git a/apps/CtsVerifier/res/layout/ble_advertiser_test.xml b/apps/CtsVerifier/res/layout/ble_advertiser_test.xml
new file mode 100644
index 0000000..7db2b4e
--- /dev/null
+++ b/apps/CtsVerifier/res/layout/ble_advertiser_test.xml
@@ -0,0 +1,33 @@
+<?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.
+-->
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:orientation="vertical"
+        android:padding="10dip"
+        >
+    <ListView android:id="@+id/ble_advertiser_tests"
+            android:layout_height="fill_parent"
+            android:layout_width="match_parent"
+            android:padding="10dip"
+            />
+
+    <include android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_alignParentBottom="true"
+            layout="@layout/pass_fail_buttons"
+            />
+</RelativeLayout>
diff --git a/apps/CtsVerifier/res/layout/ble_privacy_mac.xml b/apps/CtsVerifier/res/layout/ble_privacy_mac.xml
new file mode 100644
index 0000000..2609654
--- /dev/null
+++ b/apps/CtsVerifier/res/layout/ble_privacy_mac.xml
@@ -0,0 +1,45 @@
+<?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.
+-->
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:orientation="vertical"
+        android:padding="10dip"
+        >
+
+    <LinearLayout android:orientation="horizontal"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_centerInParent="true"
+            >
+        <Button android:id="@+id/ble_privacy_mac_start"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="@string/ble_privacy_mac_start"
+                />
+        <Button android:id="@+id/ble_privacy_mac_stop"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="@string/ble_privacy_mac_stop"
+                />
+    </LinearLayout>
+
+    <include android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_alignParentBottom="true"
+            layout="@layout/pass_fail_buttons"
+            />
+</RelativeLayout>
diff --git a/apps/CtsVerifier/res/layout/ble_scanner_power_level.xml b/apps/CtsVerifier/res/layout/ble_scanner_power_level.xml
new file mode 100644
index 0000000..b1fd2df
--- /dev/null
+++ b/apps/CtsVerifier/res/layout/ble_scanner_power_level.xml
@@ -0,0 +1,163 @@
+<?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.
+-->
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:orientation="vertical"
+        android:padding="10dip"
+        >
+
+    <LinearLayout android:orientation="vertical"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_centerInParent="true"
+            >
+        <LinearLayout android:orientation="horizontal"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:layout_centerInParent="true"
+                >
+            <TextView android:text="@string/ble_ultra_low"
+                  android:layout_width="100dp"
+                  android:layout_height="wrap_content"
+            />
+            <TextView android:id="@+id/ble_ultra_low_mac"
+                  android:layout_width="200dp"
+                  android:layout_height="wrap_content"
+            />
+            <TextView android:id="@+id/ble_ultra_low_rssi"
+                  android:layout_width="100dp"
+                  android:layout_height="wrap_content"
+            />
+        </LinearLayout>
+        <LinearLayout android:orientation="horizontal"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content">
+            <TextView android:layout_width="100dp"
+                  android:layout_height="wrap_content"/>
+            <TextView android:id="@+id/ble_ultra_low_count"
+                    android:layout_width="100dp"
+                    android:layout_height="wrap_content"
+            />
+            <TextView android:id="@+id/ble_ultra_low_set_power"
+                    android:layout_width="100dp"
+                    android:layout_height="wrap_content"
+            />
+        </LinearLayout>
+        <LinearLayout android:orientation="horizontal"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:layout_centerInParent="true"
+                >
+            <TextView android:text="@string/ble_low"
+                  android:layout_width="100dp"
+                  android:layout_height="wrap_content"
+            />
+            <TextView android:id="@+id/ble_low_mac"
+                  android:layout_width="200dp"
+                  android:layout_height="wrap_content"
+            />
+            <TextView android:id="@+id/ble_low_rssi"
+                  android:layout_width="100dp"
+                  android:layout_height="wrap_content"
+            />
+        </LinearLayout>
+        <LinearLayout android:orientation="horizontal"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content">
+            <TextView android:layout_width="100dp"
+                  android:layout_height="wrap_content"/>
+            <TextView android:id="@+id/ble_low_count"
+                    android:layout_width="100dp"
+                    android:layout_height="wrap_content"
+            />
+            <TextView android:id="@+id/ble_low_set_power"
+                    android:layout_width="100dp"
+                    android:layout_height="wrap_content"
+            />
+        </LinearLayout>
+        <LinearLayout android:orientation="horizontal"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:layout_centerInParent="true"
+                >
+            <TextView android:text="@string/ble_medium"
+                  android:layout_width="100dp"
+                  android:layout_height="wrap_content"
+            />
+            <TextView android:id="@+id/ble_medium_mac"
+                  android:layout_width="200dp"
+                  android:layout_height="wrap_content"
+            />
+            <TextView android:id="@+id/ble_medium_rssi"
+                  android:layout_width="100dp"
+                  android:layout_height="wrap_content"
+            />
+        </LinearLayout>
+        <LinearLayout android:orientation="horizontal"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content">
+            <TextView android:layout_width="100dp"
+                  android:layout_height="wrap_content"/>
+            <TextView android:id="@+id/ble_medium_count"
+                    android:layout_width="100dp"
+                    android:layout_height="wrap_content"
+            />
+            <TextView android:id="@+id/ble_medium_set_power"
+                    android:layout_width="100dp"
+                    android:layout_height="wrap_content"
+            />
+        </LinearLayout>
+        <LinearLayout android:orientation="horizontal"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:layout_centerInParent="true"
+                >
+            <TextView android:text="@string/ble_high"
+                  android:layout_width="100dp"
+                  android:layout_height="wrap_content"
+            />
+            <TextView android:id="@+id/ble_high_mac"
+                  android:layout_width="200dp"
+                  android:layout_height="wrap_content"
+            />
+            <TextView android:id="@+id/ble_high_rssi"
+                  android:layout_width="100dp"
+                  android:layout_height="wrap_content"
+            />
+        </LinearLayout>
+        <LinearLayout android:orientation="horizontal"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content">
+            <TextView android:layout_width="100dp"
+                  android:layout_height="wrap_content"/>
+            <TextView android:id="@+id/ble_high_count"
+                    android:layout_width="100dp"
+                    android:layout_height="wrap_content"
+            />
+            <TextView android:id="@+id/ble_high_set_power"
+                    android:layout_width="100dp"
+                    android:layout_height="wrap_content"
+            />
+        </LinearLayout>
+    </LinearLayout>
+
+    <include android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_alignParentBottom="true"
+            layout="@layout/pass_fail_buttons"
+            />
+</RelativeLayout>
diff --git a/apps/CtsVerifier/res/layout/ble_scanner_privacy_mac.xml b/apps/CtsVerifier/res/layout/ble_scanner_privacy_mac.xml
new file mode 100644
index 0000000..f636705
--- /dev/null
+++ b/apps/CtsVerifier/res/layout/ble_scanner_privacy_mac.xml
@@ -0,0 +1,75 @@
+<?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.
+-->
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:orientation="vertical"
+        android:padding="10dip"
+        >
+
+    <LinearLayout android:orientation="vertical"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_centerInParent="true"
+            >
+        <LinearLayout android:orientation="horizontal"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:layout_centerInParent="true"
+                >
+            <TextView android:text="@string/ble_address"
+                  android:layout_width="100dp"
+                  android:layout_height="wrap_content"
+            />
+            <TextView android:id="@+id/ble_mac_address"
+                  android:layout_weight="1"
+                  android:layout_width="0dp"
+                  android:layout_height="wrap_content"
+            />
+        </LinearLayout>
+        <LinearLayout android:orientation="horizontal"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:layout_centerInParent="true"
+                >
+            <TextView android:id="@+id/ble_mac_count"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+            />
+            <TextView android:id="@+id/ble_resp_count"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:layout_below="@+id/ble_mac_count"
+            />
+        </LinearLayout>
+        <LinearLayout android:orientation="horizontal"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:layout_centerInParent="true"
+                >
+            <TextView android:id="@+id/ble_timer"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+            />
+        </LinearLayout>
+    </LinearLayout>
+
+    <include android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_alignParentBottom="true"
+            layout="@layout/pass_fail_buttons"
+            />
+</RelativeLayout>
diff --git a/apps/CtsVerifier/res/layout/ble_scanner_test.xml b/apps/CtsVerifier/res/layout/ble_scanner_test.xml
new file mode 100644
index 0000000..c685d57
--- /dev/null
+++ b/apps/CtsVerifier/res/layout/ble_scanner_test.xml
@@ -0,0 +1,35 @@
+<?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.
+-->
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:orientation="vertical"
+        android:padding="10dip"
+        >
+    <include android:id="@+id/pass_fail_buttons"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_alignParentBottom="true"
+            layout="@layout/pass_fail_buttons"
+            />
+    <ListView android:id="@+id/ble_scanner_tests"
+            android:layout_height="wrap_content"
+            android:layout_width="match_parent"
+            android:layout_above="@id/pass_fail_buttons"
+            android:layout_alignParentTop="true"
+            android:padding="10dip"
+            />
+</RelativeLayout>
diff --git a/apps/CtsVerifier/res/values/colors.xml b/apps/CtsVerifier/res/values/colors.xml
new file mode 100644
index 0000000..707255b
--- /dev/null
+++ b/apps/CtsVerifier/res/values/colors.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <color name="red">#F00</color>
+    <color name="green">#0F0</color>
+</resources>
diff --git a/apps/CtsVerifier/res/values/strings.xml b/apps/CtsVerifier/res/values/strings.xml
index d93649e..1fb7ecb 100644
--- a/apps/CtsVerifier/res/values/strings.xml
+++ b/apps/CtsVerifier/res/values/strings.xml
@@ -227,6 +227,29 @@
     <string name="ble_server_reliable_write">Waiting on reliable write from client</string>
     <string name="ble_server_receiving_disconnect">Waiting on disconnection from BLE client</string>
 
+    <!-- BLE advertiser side strings -->
+    <string name="ble_advertiser_test_name">BLE Advertiser Test</string>
+    <string name="ble_advertiser_test_info">The BLE test must be done simultaneously on two devices, an advertiser and a scanner. This device is the advertiser.</string>
+    <string name="ble_advertiser_service_name">Bluetooth LE Advertiser Handler Service</string>
+    <string name="ble_privacy_mac_start">Start</string>
+    <string name="ble_privacy_mac_stop">Stop</string>
+    <string name="ble_privacy_mac_name">BLE Privacy Mac</string>
+    <string name="ble_privacy_mac_info">BLE Advertiser should advertise in non-repeating MAC address.</string>
+    <string name="ble_power_level_start">Start</string>
+    <string name="ble_power_level_stop">Stop</string>
+    <string name="ble_power_level_name">BLE Tx Power Level</string>
+    <string name="ble_power_level_info">BLE Advertiser advertises in 4 different power levels. Scanner should receive them in different strength of Rssi, cannot receive weak signals beyond several feet.</string>
+
+    <!-- BLE scanner side strings -->
+    <string name="ble_scanner_test_name">BLE Scanner Test</string>
+    <string name="ble_scanner_service_name">Bluetooth LE Scanner Handler Service</string>
+    <string name="ble_scanner_test_info">The BLE test must be done simultaneously on two devices, an advertiser and a scanner. This device is the scanner.</string>
+    <string name="ble_scanner_privacy_mac">Hold for 15 min to see if receive a different MAC address from advertiser.</string>
+    <string name="ble_ultra_low">Ultra low</string>
+    <string name="ble_low">Low</string>
+    <string name="ble_medium">Medium</string>
+    <string name="ble_high">High</string>
+
     <!-- Strings for FeatureSummaryActivity -->
     <string name="feature_summary">Hardware/Software Feature Summary</string>
     <string name="feature_summary_info">This is a test for...</string>
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleAdvertiserPowerLevelActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleAdvertiserPowerLevelActivity.java
new file mode 100644
index 0000000..3568002
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleAdvertiserPowerLevelActivity.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.verifier.bluetooth;
+
+import com.android.cts.verifier.PassFailButtons;
+import com.android.cts.verifier.R;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.Button;
+import android.widget.Toast;
+
+public class BleAdvertiserPowerLevelActivity extends PassFailButtons.Activity {
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.ble_advertiser_power_level);
+        setPassFailButtonClickListeners();
+        setInfoResources(R.string.ble_power_level_name,
+                         R.string.ble_power_level_info, -1);
+
+        ((Button) findViewById(R.id.ble_power_level_start))
+            .setOnClickListener(new OnClickListener() {
+                @Override
+                public void onClick(View v) {
+                    Intent intent = new Intent(BleAdvertiserPowerLevelActivity.this,
+                                               BleAdvertiserService.class);
+                    intent.putExtra(BleAdvertiserService.EXTRA_COMMAND,
+                                    BleAdvertiserService.COMMAND_START_POWER_LEVEL);
+                    startService(intent);
+                }
+            });
+        ((Button) findViewById(R.id.ble_power_level_stop))
+            .setOnClickListener(new OnClickListener() {
+                @Override
+                public void onClick(View v) {
+                    stopAdvertising();
+                }
+            });
+
+    }
+
+    @Override
+    public void onResume() {
+        super.onResume();
+        IntentFilter filter = new IntentFilter();
+        filter.addAction(BleAdvertiserService.BLE_START_POWER_LEVEL);
+        filter.addAction(BleAdvertiserService.BLE_STOP_POWER_LEVEL);
+        registerReceiver(onBroadcast, filter);
+    }
+
+    @Override
+    public void onPause() {
+        super.onPause();
+        unregisterReceiver(onBroadcast);
+    }
+
+    @Override
+    public void onDestroy() {
+        super.onDestroy();
+        stopAdvertising();
+    }
+
+    private void stopAdvertising() {
+        Intent intent = new Intent(BleAdvertiserPowerLevelActivity.this,
+                                   BleAdvertiserService.class);
+        intent.putExtra(BleAdvertiserService.EXTRA_COMMAND,
+                        BleAdvertiserService.COMMAND_STOP_POWER_LEVEL);
+        startService(intent);
+    }
+
+    private void showMessage(String msg) {
+        Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();
+    }
+
+    private BroadcastReceiver onBroadcast = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            switch (intent.getAction()) {
+                case BleAdvertiserService.BLE_START_POWER_LEVEL:
+                    showMessage("Start advertising, please hold for a while.");
+                    break;
+                case BleAdvertiserService.BLE_STOP_POWER_LEVEL:
+                    showMessage("Stop advertising");
+                    break;
+            }
+        }
+    };
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleAdvertiserPrivacyMacActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleAdvertiserPrivacyMacActivity.java
new file mode 100644
index 0000000..f2bfd9c
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleAdvertiserPrivacyMacActivity.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.verifier.bluetooth;
+
+import com.android.cts.verifier.PassFailButtons;
+import com.android.cts.verifier.R;
+
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Bundle;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.Button;
+import android.widget.Toast;
+
+public class BleAdvertiserPrivacyMacActivity extends PassFailButtons.Activity {
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.ble_privacy_mac);
+        setPassFailButtonClickListeners();
+        setInfoResources(R.string.ble_privacy_mac_name,
+                         R.string.ble_privacy_mac_info, -1);
+
+        ((Button) findViewById(R.id.ble_privacy_mac_start))
+            .setOnClickListener(new OnClickListener() {
+                @Override
+                public void onClick(View v) {
+                    Intent intent = new Intent(BleAdvertiserPrivacyMacActivity.this,
+                                               BleAdvertiserService.class);
+                    intent.putExtra(BleAdvertiserService.EXTRA_COMMAND,
+                                    BleAdvertiserService.COMMAND_START_ADVERTISE);
+                    startService(intent);
+                }
+            });
+        ((Button) findViewById(R.id.ble_privacy_mac_stop))
+            .setOnClickListener(new OnClickListener() {
+                @Override
+                public void onClick(View v) {
+                    stopAdvertising();
+                }
+            });
+
+    }
+
+    @Override
+    public void onResume() {
+        super.onResume();
+        IntentFilter filter = new IntentFilter();
+        filter.addAction(BleAdvertiserService.BLE_START_ADVERTISE);
+        filter.addAction(BleAdvertiserService.BLE_STOP_ADVERTISE);
+        registerReceiver(onBroadcast, filter);
+    }
+
+    @Override
+    public void onPause() {
+        super.onPause();
+        unregisterReceiver(onBroadcast);
+    }
+
+    @Override
+    public void onDestroy() {
+        super.onDestroy();
+        stopAdvertising();
+    }
+
+    private void showMessage(String msg) {
+        Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();
+    }
+
+    private void stopAdvertising() {
+        Intent intent = new Intent(BleAdvertiserPrivacyMacActivity.this,
+                                   BleAdvertiserService.class);
+        intent.putExtra(BleAdvertiserService.EXTRA_COMMAND,
+                        BleAdvertiserService.COMMAND_STOP_ADVERTISE);
+        startService(intent);
+    }
+
+    private BroadcastReceiver onBroadcast = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            switch (intent.getAction()) {
+                case BleAdvertiserService.BLE_START_ADVERTISE:
+                    showMessage("Start advertising, please hold for 15 min");
+                    break;
+                case BleAdvertiserService.BLE_STOP_ADVERTISE:
+                    showMessage("Stop advertising");
+                    break;
+            }
+        }
+    };
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleAdvertiserService.java b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleAdvertiserService.java
new file mode 100644
index 0000000..7191f58
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleAdvertiserService.java
@@ -0,0 +1,200 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.verifier.bluetooth;
+
+import java.util.UUID;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import android.app.Service;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothGattServer;
+import android.bluetooth.BluetoothGattServerCallback;
+import android.bluetooth.BluetoothManager;
+import android.bluetooth.le.BluetoothLeAdvertiser;
+import android.bluetooth.le.AdvertiseCallback;
+import android.bluetooth.le.AdvertiseData;
+import android.bluetooth.le.AdvertiseSettings;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.ParcelUuid;
+import android.util.Log;
+import android.widget.Toast;
+
+public class BleAdvertiserService extends Service {
+
+    public static final boolean DEBUG = true;
+    public static final String TAG = "BleAdvertiseService";
+
+    public static final int COMMAND_START_ADVERTISE = 0;
+    public static final int COMMAND_STOP_ADVERTISE = 1;
+    public static final int COMMAND_START_POWER_LEVEL = 2;
+    public static final int COMMAND_STOP_POWER_LEVEL = 3;
+
+    public static final String BLE_START_ADVERTISE =
+            "com.android.cts.verifier.bluetooth.BLE_START_ADVERTISE";
+    public static final String BLE_STOP_ADVERTISE =
+            "com.android.cts.verifier.bluetooth.BLE_STOP_ADVERTISE";
+    public static final String BLE_START_POWER_LEVEL =
+            "com.android.cts.verifier.bluetooth.BLE_START_POWER_LEVEL";
+    public static final String BLE_STOP_POWER_LEVEL =
+            "com.android.cts.verifier.bluetooth.BLE_STOP_POWER_LEVEL";
+
+    public static final String EXTRA_COMMAND =
+            "com.android.cts.verifier.bluetooth.EXTRA_COMMAND";
+
+    protected static final UUID PRIVACY_MAC_UUID =
+            UUID.fromString("00009999-0000-1000-8000-00805f9b34fb");
+    protected static final UUID POWER_LEVEL_UUID =
+            UUID.fromString("00008888-0000-1000-8000-00805f9b34fb");
+    protected static final UUID SCAN_RESP_UUID =
+            UUID.fromString("00007777-0000-1000-8000-00805f9b34fb");
+    public static final byte MANUFACTURER_TEST_ID = (byte)0x07;
+    public static final byte[] PRIVACY_MAC_DATA = new byte[]{3, 1, 4};
+    public static final byte[] PRIVACY_RESPONSE = new byte[]{9, 2, 6};
+    public static final byte[] POWER_LEVEL_DATA = new byte[]{1, 5, 0};
+    public static final byte[] POWER_LEVEL_MASK = new byte[]{1, 1, 0};
+
+    private BluetoothManager mBluetoothManager;
+    private BluetoothAdapter mBluetoothAdapter;
+    private BluetoothLeAdvertiser mAdvertiser;
+    private BluetoothGattServer mGattServer;
+    private AdvertiseCallback mCallback;
+    private Handler mHandler;
+
+    private int[] mPowerLevel;
+    private Map<Integer, AdvertiseCallback> mPowerCallback;
+
+    @Override
+    public void onCreate() {
+        super.onCreate();
+
+        mBluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
+        mBluetoothAdapter = mBluetoothManager.getAdapter();
+        mAdvertiser = mBluetoothAdapter.getBluetoothLeAdvertiser();
+        mGattServer = mBluetoothManager.openGattServer(getApplicationContext(),
+            new BluetoothGattServerCallback() {});
+        mCallback = new BLEAdvertiseCallback();
+        mHandler = new Handler();
+
+        mPowerLevel = new int[]{
+            AdvertiseSettings.ADVERTISE_TX_POWER_ULTRA_LOW,
+            AdvertiseSettings.ADVERTISE_TX_POWER_LOW,
+            AdvertiseSettings.ADVERTISE_TX_POWER_MEDIUM,
+            AdvertiseSettings.ADVERTISE_TX_POWER_HIGH};
+        mPowerCallback = new HashMap<Integer, AdvertiseCallback>();
+        for (int x : mPowerLevel) {
+            mPowerCallback.put(x, new BLEAdvertiseCallback());
+        }
+    }
+
+    @Override
+    public int onStartCommand(Intent intent, int flags, int startId) {
+        if (intent != null) handleIntent(intent);
+        return START_NOT_STICKY;
+    }
+
+    @Override
+    public IBinder onBind(Intent intent) {
+        return null;
+    }
+
+    @Override
+    public void onDestroy() {
+        super.onDestroy();
+        mAdvertiser.stopAdvertising(mCallback);
+    }
+
+    private void handleIntent(Intent intent) {
+        int command = intent.getIntExtra(EXTRA_COMMAND, -1);
+
+        switch (command) {
+            case COMMAND_START_ADVERTISE:
+                AdvertiseData data = new AdvertiseData.Builder()
+                    .addManufacturerData(MANUFACTURER_TEST_ID, new byte[]{MANUFACTURER_TEST_ID, 0})
+                    .addServiceData(new ParcelUuid(PRIVACY_MAC_UUID), PRIVACY_MAC_DATA)
+                    .build();
+                AdvertiseData response = new AdvertiseData.Builder()
+                    .addManufacturerData(MANUFACTURER_TEST_ID, new byte[]{MANUFACTURER_TEST_ID, 0})
+                    .addServiceData(new ParcelUuid(SCAN_RESP_UUID), PRIVACY_RESPONSE)
+                    .build();
+
+                AdvertiseSettings setting = new AdvertiseSettings.Builder()
+                    .setAdvertiseMode(AdvertiseSettings.ADVERTISE_MODE_LOW_LATENCY)
+                    .setTxPowerLevel(AdvertiseSettings.ADVERTISE_TX_POWER_MEDIUM)
+                    .setIsConnectable(false)
+                    .build();
+                mAdvertiser.startAdvertising(setting, data, response, mCallback);
+                sendBroadcast(new Intent(BLE_START_ADVERTISE));
+                break;
+            case COMMAND_STOP_ADVERTISE:
+                mAdvertiser.stopAdvertising(mCallback);
+                sendBroadcast(new Intent(BLE_STOP_ADVERTISE));
+                break;
+            case COMMAND_START_POWER_LEVEL:
+                for (int t : mPowerLevel) {
+                    AdvertiseData d = new AdvertiseData.Builder()
+                        .addManufacturerData(MANUFACTURER_TEST_ID,
+                            new byte[]{MANUFACTURER_TEST_ID, 0})
+                        .addServiceData(new ParcelUuid(POWER_LEVEL_UUID),
+                            new byte[]{1, 5, (byte)t})
+                        .setIncludeTxPowerLevel(true)
+                        .build();
+                    AdvertiseSettings settings = new AdvertiseSettings.Builder()
+                        .setTxPowerLevel(AdvertiseSettings.ADVERTISE_MODE_LOW_LATENCY)
+                        .setTxPowerLevel(t)
+                        .build();
+                    mAdvertiser.startAdvertising(settings, d, mPowerCallback.get(t));
+                }
+                sendBroadcast(new Intent(BLE_START_POWER_LEVEL));
+                break;
+            case COMMAND_STOP_POWER_LEVEL:
+                for (int t : mPowerLevel) {
+                    mAdvertiser.stopAdvertising(mPowerCallback.get(t));
+                }
+                sendBroadcast(new Intent(BLE_STOP_POWER_LEVEL));
+                break;
+            default:
+                showMessage("Unrecognized command: " + command);
+                break;
+        }
+    }
+
+    private void showMessage(final String msg) {
+        mHandler.post(new Runnable() {
+            public void run() {
+                Toast.makeText(BleAdvertiserService.this, msg, Toast.LENGTH_SHORT).show();
+            }
+        });
+    }
+
+    private class BLEAdvertiseCallback extends AdvertiseCallback {
+        @Override
+        public void onStartFailure(int errorCode) {
+            Log.e(TAG, "fail. Error code: " + errorCode);
+        }
+
+        @Override
+        public void onStartSuccess(AdvertiseSettings setting) {
+            if (DEBUG) Log.d(TAG, "success.");
+        }
+    }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleAdvertiserTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleAdvertiserTestActivity.java
new file mode 100644
index 0000000..637ef71
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleAdvertiserTestActivity.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.verifier.bluetooth;
+
+import com.android.cts.verifier.ManifestTestListAdapter;
+import com.android.cts.verifier.PassFailButtons;
+import com.android.cts.verifier.R;
+
+import android.os.Bundle;
+
+public class BleAdvertiserTestActivity extends PassFailButtons.TestListActivity {
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.pass_fail_list);
+        setPassFailButtonClickListeners();
+        setInfoResources(R.string.ble_advertiser_test_name, R.string.ble_advertiser_test_info, -1);
+
+        setTestListAdapter(new ManifestTestListAdapter(this, getClass().getName()));
+    }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleScannerPowerLevelActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleScannerPowerLevelActivity.java
new file mode 100644
index 0000000..925c766
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleScannerPowerLevelActivity.java
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.verifier.bluetooth;
+
+import com.android.cts.verifier.PassFailButtons;
+import com.android.cts.verifier.R;
+
+import java.util.Set;
+import java.util.Map;
+import java.util.HashMap;
+
+import android.bluetooth.le.AdvertiseSettings;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Bundle;
+import android.util.Log;
+import android.widget.TextView;
+import android.widget.Toast;
+
+public class BleScannerPowerLevelActivity extends PassFailButtons.Activity {
+
+    private static final String TAG = "BleScannerPowerLevel";
+
+    private Map<Integer, TextView> mMacText;
+    private Map<Integer, TextView> mCountText;
+    private Map<Integer, TextView> mRssiText;
+    private Map<Integer, TextView> mSetPowerText;
+    private Map<Integer, Integer> mCount;
+    private int[] mPowerLevel;
+
+    private static final int[] POWER_DBM = {-21, -15, -7, 1, 9};
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.ble_scanner_power_level);
+        setPassFailButtonClickListeners();
+        setInfoResources(R.string.ble_power_level_name,
+                         R.string.ble_power_level_info, -1);
+
+        mCount = new HashMap<Integer, Integer>();
+        mRssiText = new HashMap<Integer, TextView>();
+        mCountText = new HashMap<Integer, TextView>();
+        mMacText = new HashMap<Integer, TextView>();
+        mSetPowerText = new HashMap<Integer, TextView>();
+        mPowerLevel = new int[]{AdvertiseSettings.ADVERTISE_TX_POWER_ULTRA_LOW,
+            AdvertiseSettings.ADVERTISE_TX_POWER_LOW,
+            AdvertiseSettings.ADVERTISE_TX_POWER_MEDIUM,
+            AdvertiseSettings.ADVERTISE_TX_POWER_HIGH};
+
+        for (int i : mPowerLevel) {
+            mCount.put(i, 0);
+        }
+        mMacText.put(AdvertiseSettings.ADVERTISE_TX_POWER_ULTRA_LOW,
+            (TextView)findViewById(R.id.ble_ultra_low_mac));
+        mMacText.put(AdvertiseSettings.ADVERTISE_TX_POWER_LOW,
+            (TextView)findViewById(R.id.ble_low_mac));
+        mMacText.put(AdvertiseSettings.ADVERTISE_TX_POWER_MEDIUM,
+            (TextView)findViewById(R.id.ble_medium_mac));
+        mMacText.put(AdvertiseSettings.ADVERTISE_TX_POWER_HIGH,
+            (TextView)findViewById(R.id.ble_high_mac));
+
+        mCountText.put(AdvertiseSettings.ADVERTISE_TX_POWER_ULTRA_LOW,
+            (TextView)findViewById(R.id.ble_ultra_low_count));
+        mCountText.put(AdvertiseSettings.ADVERTISE_TX_POWER_LOW,
+            (TextView)findViewById(R.id.ble_low_count));
+        mCountText.put(AdvertiseSettings.ADVERTISE_TX_POWER_MEDIUM,
+            (TextView)findViewById(R.id.ble_medium_count));
+        mCountText.put(AdvertiseSettings.ADVERTISE_TX_POWER_HIGH,
+            (TextView)findViewById(R.id.ble_high_count));
+
+        mRssiText.put(AdvertiseSettings.ADVERTISE_TX_POWER_ULTRA_LOW,
+            (TextView)findViewById(R.id.ble_ultra_low_rssi));
+        mRssiText.put(AdvertiseSettings.ADVERTISE_TX_POWER_LOW,
+            (TextView)findViewById(R.id.ble_low_rssi));
+        mRssiText.put(AdvertiseSettings.ADVERTISE_TX_POWER_MEDIUM,
+            (TextView)findViewById(R.id.ble_medium_rssi));
+        mRssiText.put(AdvertiseSettings.ADVERTISE_TX_POWER_HIGH,
+            (TextView)findViewById(R.id.ble_high_rssi));
+
+        mSetPowerText.put(AdvertiseSettings.ADVERTISE_TX_POWER_ULTRA_LOW,
+            (TextView)findViewById(R.id.ble_ultra_low_set_power));
+        mSetPowerText.put(AdvertiseSettings.ADVERTISE_TX_POWER_LOW,
+            (TextView)findViewById(R.id.ble_low_set_power));
+        mSetPowerText.put(AdvertiseSettings.ADVERTISE_TX_POWER_MEDIUM,
+            (TextView)findViewById(R.id.ble_medium_set_power));
+        mSetPowerText.put(AdvertiseSettings.ADVERTISE_TX_POWER_HIGH,
+            (TextView)findViewById(R.id.ble_high_set_power));
+
+        Intent intent = new Intent(this, BleScannerService.class);
+        intent.putExtra(BleScannerService.EXTRA_COMMAND, BleScannerService.COMMAND_POWER_LEVEL);
+        startService(intent);
+    }
+
+    @Override
+    public void onResume() {
+        super.onResume();
+
+        IntentFilter filter = new IntentFilter();
+        filter.addAction(BleScannerService.BLE_POWER_LEVEL);
+        registerReceiver(onBroadcast, filter);
+    }
+
+    @Override
+    public void onPause() {
+        super.onPause();
+        unregisterReceiver(onBroadcast);
+    }
+
+    @Override
+    public void onDestroy() {
+        super.onDestroy();
+        stopService(new Intent(this, BleScannerService.class));
+    }
+
+    private BroadcastReceiver onBroadcast = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            switch (intent.getAction()) {
+                case BleScannerService.BLE_POWER_LEVEL:
+                    int powerLevelBit = intent.getIntExtra(
+                        BleScannerService.EXTRA_POWER_LEVEL_BIT, -1);
+                    int powerLevel = intent.getIntExtra(BleScannerService.EXTRA_POWER_LEVEL, -2);
+                    if (powerLevelBit < 0 || powerLevelBit > 3) {
+                      Toast.makeText(context, "Invalid power level", Toast.LENGTH_SHORT).show();
+                      break;
+                    }
+                    Integer t = mCount.get(powerLevelBit) + 1;
+                    mCount.put(powerLevelBit, t);
+                    mCountText.get(powerLevelBit).setText(t.toString());
+                    mMacText.get(powerLevelBit)
+                        .setText(intent.getStringExtra(BleScannerService.EXTRA_MAC_ADDRESS));
+                    mRssiText.get(powerLevelBit)
+                        .setText(intent.getStringExtra(BleScannerService.EXTRA_RSSI));
+                    if (POWER_DBM[powerLevelBit] == powerLevel) {
+                        mSetPowerText.get(powerLevelBit).setText("Valid power level");
+                    } else {
+                        mSetPowerText.get(powerLevelBit)
+                            .setText("Unknown BLe advertise tx power: " + powerLevel);
+                    }
+                    break;
+            }
+        }
+    };
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleScannerPrivacyMacActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleScannerPrivacyMacActivity.java
new file mode 100644
index 0000000..b7e4129
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleScannerPrivacyMacActivity.java
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.verifier.bluetooth;
+
+import com.android.cts.verifier.PassFailButtons;
+import com.android.cts.verifier.R;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Bundle;
+import android.os.CountDownTimer;
+import android.widget.TextView;
+import android.widget.Toast;
+
+public class BleScannerPrivacyMacActivity extends PassFailButtons.Activity {
+
+    private static final String TAG = "BleScannerPrivacyMac";
+
+    private int mMacCount;
+    private int mRespCount;
+    private TextView mMacText;
+    private TextView mCountText;
+    private TextView mRespText;
+    private TextView mTimerText;
+    private CountDownTimer mTimer;
+    private static final long REFRESH_MAC_TIME = 930000; // 15.5 min
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.ble_scanner_privacy_mac);
+        setPassFailButtonClickListeners();
+        setInfoResources(R.string.ble_privacy_mac_name,
+                         R.string.ble_privacy_mac_info, -1);
+        getPassButton().setEnabled(false);
+        mMacCount = 0;
+        mRespCount = 0;
+        mMacText = (TextView)findViewById(R.id.ble_mac_address);
+        mCountText = (TextView)findViewById(R.id.ble_mac_count);
+        mRespText = (TextView)findViewById(R.id.ble_resp_count);
+        mTimerText = (TextView)findViewById(R.id.ble_timer);
+
+        mTimer = new CountDownTimer(REFRESH_MAC_TIME, 1000) {
+            @Override
+            public void onTick(long millis) {
+                int min = (int)millis / 60000;
+                int sec = ((int)millis / 1000) % 60;
+                mTimerText.setText(min + ":" + sec);
+            }
+
+            @Override
+            public void onFinish() {
+                mTimerText.setTextColor(getResources().getColor(R.color.red));
+                mTimerText.setText("Time is up!");
+                stop();
+            }
+        };
+
+    }
+
+    @Override
+    public void onResume() {
+        super.onResume();
+
+        IntentFilter filter = new IntentFilter();
+        filter.addAction(BleScannerService.BLE_PRIVACY_NEW_MAC_RECEIVE);
+        filter.addAction(BleScannerService.BLE_MAC_ADDRESS);
+        filter.addAction(BleScannerService.BLE_SCAN_RESP);
+        registerReceiver(onBroadcast, filter);
+
+        if (mMacCount == 0) {
+            Intent intent = new Intent(this, BleScannerService.class);
+            intent.putExtra(BleScannerService.EXTRA_COMMAND, BleScannerService.COMMAND_PRIVACY_MAC);
+            startService(intent);
+        }
+    }
+
+
+    private BroadcastReceiver onBroadcast = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            switch (intent.getAction()) {
+                case BleScannerService.BLE_PRIVACY_NEW_MAC_RECEIVE:
+                    mMacText.append(", " + intent.getStringExtra(BleScannerService.EXTRA_MAC_ADDRESS));
+                    Toast.makeText(context, "New MAC address detected", Toast.LENGTH_SHORT).show();
+                    mTimerText.setTextColor(getResources().getColor(R.color.green));
+                    mTimerText.append("   Get new MAC address.");
+                    mTimer.cancel();
+                    getPassButton().setEnabled(true);
+                    break;
+                case BleScannerService.BLE_MAC_ADDRESS:
+                    if (mMacCount == 0) {
+                        mMacText.setText(intent.getStringExtra(BleScannerService.EXTRA_MAC_ADDRESS));
+                        mTimer.start();
+                    }
+                    mMacCount++;
+                    mCountText.setText("Count: " + mMacCount);
+                    break;
+                case BleScannerService.BLE_SCAN_RESP:
+                    mRespCount++;
+                    mRespText.setText("Response: " + mRespCount);
+                    break;
+            }
+        }
+    };
+
+    @Override
+    public void onPause() {
+        super.onPause();
+        unregisterReceiver(onBroadcast);
+    }
+
+    private void stop() {
+        stopService(new Intent(this, BleScannerService.class));
+    }
+
+    @Override
+    public void onDestroy() {
+        super.onDestroy();
+        stop();
+    }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleScannerService.java b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleScannerService.java
new file mode 100644
index 0000000..495f89d
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleScannerService.java
@@ -0,0 +1,203 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.verifier.bluetooth;
+
+import android.app.Service;
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothManager;
+import android.bluetooth.le.BluetoothLeScanner;
+import android.bluetooth.le.ScanCallback;
+import android.bluetooth.le.ScanFilter;
+import android.bluetooth.le.ScanSettings;
+import android.bluetooth.le.ScanRecord;
+import android.bluetooth.le.ScanResult;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.ParcelUuid;
+import android.util.Log;
+import android.widget.Toast;
+
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+
+public class BleScannerService extends Service {
+
+    public static final boolean DEBUG = true;
+    public static final String TAG = "BleScannerService";
+
+    public static final int COMMAND_PRIVACY_MAC = 0;
+    public static final int COMMAND_POWER_LEVEL = 1;
+
+    public static final String BLE_PRIVACY_NEW_MAC_RECEIVE =
+            "com.android.cts.verifier.bluetooth.BLE_PRIVACY_NEW_MAC_RECEIVE";
+    public static final String BLE_MAC_ADDRESS =
+            "com.android.cts.verifier.bluetooth.BLE_MAC_ADDRESS";
+    public static final String BLE_POWER_LEVEL =
+            "com.android.cts.verifier.bluetooth.BLE_POWER_LEVEL";
+    public static final String BLE_SCAN_RESP =
+            "com.android.cts.verifier.bluetooth.BLE_SCAN_RESP";
+
+    public static final String EXTRA_COMMAND =
+            "com.google.cts.verifier.bluetooth.EXTRA_COMMAND";
+    public static final String EXTRA_MAC_ADDRESS =
+            "com.google.cts.verifier.bluetooth.EXTRA_MAC_ADDRESS";
+    public static final String EXTRA_RSSI =
+            "com.google.cts.verifier.bluetooth.EXTRA_RSSI";
+    public static final String EXTRA_POWER_LEVEL =
+            "com.google.cts.verifier.bluetooth.EXTRA_POWER_LEVEL";
+    public static final String EXTRA_POWER_LEVEL_BIT =
+            "com.google.cts.verifier.bluetooth.EXTRA_POWER_LEVEL_BIT";
+
+    private static final byte MANUFACTURER_TEST_ID = (byte)0x07;
+
+    private BluetoothManager mBluetoothManager;
+    private BluetoothAdapter mAdapter;
+    private BluetoothLeScanner mScanner;
+    private ScanCallback mCallback;
+    private Handler mHandler;
+    private String mOldMac;
+
+    @Override
+    public void onCreate() {
+        super.onCreate();
+
+        mCallback = new BLEScanCallback();
+        mHandler = new Handler();
+        mOldMac = null;
+
+        mBluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
+        mAdapter = mBluetoothManager.getAdapter();
+        mScanner = mAdapter.getBluetoothLeScanner();
+    }
+
+    @Override
+    public int onStartCommand(Intent intent, int flags, int startId) {
+        if (mScanner != null) {
+            List<ScanFilter> filters = new ArrayList<ScanFilter>();
+            ScanSettings.Builder settingBuilder = new ScanSettings.Builder()
+                    .setCallbackType(ScanSettings.CALLBACK_TYPE_ALL_MATCHES);
+
+            int command = intent.getIntExtra(EXTRA_COMMAND, -1);
+            switch (command) {
+                case COMMAND_PRIVACY_MAC:
+                    filters.add(new ScanFilter.Builder()
+                        .setManufacturerData(MANUFACTURER_TEST_ID,
+                            new byte[]{MANUFACTURER_TEST_ID, 0})
+                        .setServiceData(new ParcelUuid(BleAdvertiserService.PRIVACY_MAC_UUID),
+                            BleAdvertiserService.PRIVACY_MAC_DATA)
+                        .build());
+                    filters.add(new ScanFilter.Builder()
+                        .setManufacturerData(MANUFACTURER_TEST_ID,
+                            new byte[]{MANUFACTURER_TEST_ID, 0})
+                        .setServiceData(new ParcelUuid(BleAdvertiserService.SCAN_RESP_UUID),
+                            BleAdvertiserService.PRIVACY_RESPONSE)
+                        .build());
+                    settingBuilder.setScanMode(ScanSettings.SCAN_RESULT_TYPE_FULL);
+                    break;
+                case COMMAND_POWER_LEVEL:
+                    filters.add(new ScanFilter.Builder()
+                        .setManufacturerData(MANUFACTURER_TEST_ID,
+                            new byte[]{MANUFACTURER_TEST_ID, 0})
+                        .setServiceData(new ParcelUuid(BleAdvertiserService.POWER_LEVEL_UUID),
+                            BleAdvertiserService.POWER_LEVEL_DATA,
+                            BleAdvertiserService.POWER_LEVEL_MASK)
+                        .build());
+                    settingBuilder.setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY);
+                    break;
+            }
+            mOldMac = null;
+            mScanner.startScan(filters, settingBuilder.build(), mCallback);
+        }
+        return START_NOT_STICKY;
+    }
+
+    @Override
+    public IBinder onBind(Intent intent) {
+        return null;
+    }
+
+    @Override
+    public void onDestroy() {
+        super.onDestroy();
+        mScanner.stopScan(mCallback);
+    }
+
+    private void showMessage(final String msg) {
+        mHandler.post(new Runnable() {
+            public void run() {
+                Toast.makeText(BleScannerService.this, msg, Toast.LENGTH_SHORT).show();
+            }
+        });
+    }
+
+    private class BLEScanCallback extends ScanCallback {
+        @Override
+        public void onScanResult(int callBackType, ScanResult result) {
+            if (callBackType != ScanSettings.CALLBACK_TYPE_ALL_MATCHES) {
+                Log.e(TAG, "onScanResult fail. callBackType is not CALLBACK_TYPE_ALL_MATCHES");
+                return;
+            }
+
+            ScanRecord record = result.getScanRecord();
+            String mac = result.getDevice().getAddress();
+            Map<ParcelUuid, byte[]> serviceData = record.getServiceData();
+
+            if (serviceData.get(new ParcelUuid(BleAdvertiserService.PRIVACY_MAC_UUID)) != null) {
+                Intent privacyIntent = new Intent(BLE_MAC_ADDRESS);
+                privacyIntent.putExtra(EXTRA_MAC_ADDRESS, mac);
+                sendBroadcast(privacyIntent);
+
+                if (mOldMac == null) {
+                    mOldMac = mac;
+                } else if (!mOldMac.equals(mac)) {
+                    mOldMac = mac;
+                    Intent newIntent = new Intent(BLE_PRIVACY_NEW_MAC_RECEIVE);
+                    newIntent.putExtra(EXTRA_MAC_ADDRESS, mac);
+                    sendBroadcast(newIntent);
+                }
+            }
+
+            if (serviceData.get(new ParcelUuid(BleAdvertiserService.POWER_LEVEL_UUID)) != null) {
+                byte[] data =
+                        serviceData.get(new ParcelUuid(BleAdvertiserService.POWER_LEVEL_UUID));
+                if (data.length == 3) {
+                    Intent powerIntent = new Intent(BLE_POWER_LEVEL);
+                    powerIntent.putExtra(EXTRA_MAC_ADDRESS, result.getDevice().getAddress());
+                    powerIntent.putExtra(EXTRA_POWER_LEVEL, record.getTxPowerLevel());
+                    powerIntent.putExtra(EXTRA_RSSI, new Integer(result.getRssi()).toString());
+                    powerIntent.putExtra(EXTRA_POWER_LEVEL_BIT, (int)data[2]);
+                    sendBroadcast(powerIntent);
+                }
+            }
+
+            if (serviceData.get(new ParcelUuid(BleAdvertiserService.SCAN_RESP_UUID)) != null) {
+                Intent responseIntent = new Intent(BLE_SCAN_RESP);
+                sendBroadcast(responseIntent);
+            }
+        }
+
+        public void onScanFailed(int errorCode) {
+            Log.e(TAG, "Scan fail. Error code: " + new Integer(errorCode).toString());
+        }
+
+    }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleScannerTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleScannerTestActivity.java
new file mode 100644
index 0000000..1f54917
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleScannerTestActivity.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.verifier.bluetooth;
+
+import com.android.cts.verifier.ManifestTestListAdapter;
+import com.android.cts.verifier.PassFailButtons;
+import com.android.cts.verifier.R;
+
+import android.os.Bundle;
+
+public class BleScannerTestActivity extends PassFailButtons.TestListActivity {
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.pass_fail_list);
+        setPassFailButtonClickListeners();
+        setInfoResources(R.string.ble_scanner_test_name,
+                         R.string.ble_scanner_test_info, -1);
+        setTestListAdapter(new ManifestTestListAdapter(this, getClass().getName()));
+    }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleServerStartActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleServerStartActivity.java
index ec31fde..089afde 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleServerStartActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleServerStartActivity.java
@@ -37,7 +37,6 @@
 
 public class BleServerStartActivity extends PassFailButtons.Activity {
 
-    private List<Test> mTestList;
     private TestAdapter mTestAdapter;
     private int mAllPassed;
 
@@ -50,9 +49,7 @@
                          R.string.ble_server_start_info, -1);
         getPassButton().setEnabled(false);
 
-        mTestList = setupTestList();
-        mTestAdapter = new TestAdapter(this, mTestList);
-
+        mTestAdapter = new TestAdapter(this, setupTestList());
         ListView listView = (ListView) findViewById(R.id.ble_server_tests);
         listView.setAdapter(mTestAdapter);
 
@@ -88,109 +85,50 @@
         stopService(new Intent(this, BleServerService.class));
     }
 
-    private List<Test> setupTestList() {
-        ArrayList<Test> testList = new ArrayList<Test>();
-        testList.add(new Test(R.string.ble_server_add_service));
-        testList.add(new Test(R.string.ble_server_receiving_connect));
-        testList.add(new Test(R.string.ble_server_read_characteristic));
-        testList.add(new Test(R.string.ble_server_write_characteristic));
-        testList.add(new Test(R.string.ble_server_read_descriptor));
-        testList.add(new Test(R.string.ble_server_write_descriptor));
-        testList.add(new Test(R.string.ble_server_reliable_write));
-        testList.add(new Test(R.string.ble_server_receiving_disconnect));
+    private List<Integer> setupTestList() {
+        ArrayList<Integer> testList = new ArrayList<Integer>();
+        testList.add(R.string.ble_server_add_service);
+        testList.add(R.string.ble_server_receiving_connect);
+        testList.add(R.string.ble_server_read_characteristic);
+        testList.add(R.string.ble_server_write_characteristic);
+        testList.add(R.string.ble_server_read_descriptor);
+        testList.add(R.string.ble_server_write_descriptor);
+        testList.add(R.string.ble_server_reliable_write);
+        testList.add(R.string.ble_server_receiving_disconnect);
         return testList;
     }
 
-    class Test {
-        private boolean passed;
-        private int instructions;
-
-        private Test(int instructions) {
-            passed = false;
-            this.instructions = instructions;
-        }
-    }
-
     private BroadcastReceiver onBroadcast = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
             String action = intent.getAction();
             if (action == BleServerService.BLE_SERVICE_ADDED) {
-                mTestList.get(0).passed = true;
+                mTestAdapter.setTestPass(0);
                 mAllPassed |= 0x01;
             } else if (action == BleServerService.BLE_SERVER_CONNECTED) {
-                mTestList.get(1).passed = true;
+                mTestAdapter.setTestPass(1);
                 mAllPassed |= 0x02;
             } else if (action == BleServerService.BLE_CHARACTERISTIC_READ_REQUEST) {
-                mTestList.get(2).passed = true;
+                mTestAdapter.setTestPass(2);
                 mAllPassed |= 0x04;
             } else if (action == BleServerService.BLE_CHARACTERISTIC_WRITE_REQUEST) {
-                mTestList.get(3).passed = true;
+                mTestAdapter.setTestPass(3);
                 mAllPassed |= 0x08;
             } else if (action == BleServerService.BLE_DESCRIPTOR_READ_REQUEST) {
-                mTestList.get(4).passed = true;
+                mTestAdapter.setTestPass(4);
                 mAllPassed |= 0x10;
             } else if (action == BleServerService.BLE_DESCRIPTOR_WRITE_REQUEST) {
-                mTestList.get(5).passed = true;
+                mTestAdapter.setTestPass(5);
                 mAllPassed |= 0x20;
             } else if (action == BleServerService.BLE_EXECUTE_WRITE) {
-                mTestList.get(6).passed = true;
+                mTestAdapter.setTestPass(6);
                 mAllPassed |= 0x40;
             } else if (action == BleServerService.BLE_SERVER_DISCONNECTED) {
-                mTestList.get(7).passed = true;
+                mTestAdapter.setTestPass(7);
                 mAllPassed |= 0x80;
             }
             mTestAdapter.notifyDataSetChanged();
             if (mAllPassed == 0xFF) getPassButton().setEnabled(true);
         }
     };
-
-    class TestAdapter extends BaseAdapter {
-        Context context;
-        List<Test> tests;
-        LayoutInflater inflater;
-
-        public TestAdapter(Context context, List<Test> tests) {
-            this.context = context;
-            inflater = LayoutInflater.from(context);
-            this.tests = tests;
-        }
-
-        @Override
-        public int getCount() {
-            return tests.size();
-        }
-
-        @Override
-        public Object getItem(int position) {
-            return tests.get(position);
-        }
-
-        @Override
-        public long getItemId(int position) {
-            return position;
-        }
-
-        @Override
-        public View getView(int position, View convertView, ViewGroup parent) {
-            ViewGroup vg;
-
-            if (convertView != null) {
-                vg = (ViewGroup) convertView;
-            } else {
-                vg = (ViewGroup) inflater.inflate(R.layout.ble_server_start_item, null);
-            }
-
-            Test test = tests.get(position);
-            if (test.passed) {
-                ((ImageView) vg.findViewById(R.id.status)).setImageResource(R.drawable.fs_good);
-            } else {
-                ((ImageView) vg.findViewById(R.id.status)).
-                                setImageResource(R.drawable.fs_indeterminate);
-            }
-            ((TextView) vg.findViewById(R.id.instructions)).setText(test.instructions);
-
-            return vg;
-        }
-    }
-}
\ No newline at end of file
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/TestAdapter.java b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/TestAdapter.java
new file mode 100644
index 0000000..631fe36
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/TestAdapter.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.verifier.bluetooth;
+
+import com.android.cts.verifier.PassFailButtons;
+import com.android.cts.verifier.R;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import android.content.Context;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.BaseAdapter;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+class TestAdapter extends BaseAdapter {
+    Context context;
+    List<Test> tests;
+    LayoutInflater inflater;
+
+    private class Test {
+        private boolean mPassed;
+        private final int mInstructions;
+
+        protected Test(int instructions) {
+            this.mPassed = false;
+            this.mInstructions = instructions;
+        }
+    }
+
+    public TestAdapter(Context context, List<Integer> testInstructions) {
+        this.context = context;
+        inflater = LayoutInflater.from(context);
+        this.tests = new ArrayList<Test>();
+        for (int t : testInstructions) {
+            this.tests.add(new Test(t));
+        }
+    }
+
+    @Override
+    public int getCount() {
+        return tests.size();
+    }
+
+    @Override
+    public Object getItem(int position) {
+        return tests.get(position);
+    }
+
+    public void setTestPass(int position) {
+        tests.get(position).mPassed = true;
+    }
+
+    @Override
+    public long getItemId(int position) {
+        return position;
+    }
+
+    @Override
+    public View getView(int position, View convertView, ViewGroup parent) {
+        ViewGroup vg;
+
+        if (convertView != null) {
+            vg = (ViewGroup) convertView;
+        } else {
+            vg = (ViewGroup) inflater.inflate(R.layout.ble_server_start_item, null);
+        }
+
+        Test test = tests.get(position);
+        if (test.mPassed) {
+            ((ImageView) vg.findViewById(R.id.status)).setImageResource(R.drawable.fs_good);
+        } else {
+            ((ImageView) vg.findViewById(R.id.status)).
+                            setImageResource(R.drawable.fs_indeterminate);
+        }
+        ((TextView) vg.findViewById(R.id.instructions)).setText(test.mInstructions);
+
+        return vg;
+    }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/camera/video/CameraVideoActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/camera/video/CameraVideoActivity.java
index b010f79..f93723c 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/camera/video/CameraVideoActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/camera/video/CameraVideoActivity.java
@@ -449,7 +449,8 @@
                 CamcorderProfile.QUALITY_CIF,
                 CamcorderProfile.QUALITY_480P,
                 CamcorderProfile.QUALITY_720P,
-                CamcorderProfile.QUALITY_1080P
+                CamcorderProfile.QUALITY_1080P,
+                CamcorderProfile.QUALITY_2160P
         };
 
         String[] nameArray = {
@@ -460,7 +461,8 @@
                 "CIF",
                 "480P",
                 "720P",
-                "1080P"
+                "1080P",
+                "2160P"
         };
 
         ArrayList<VideoSizeNamePair> availableSizes =
@@ -499,7 +501,8 @@
                 CamcorderProfile.QUALITY_CIF,
                 CamcorderProfile.QUALITY_480P,
                 CamcorderProfile.QUALITY_720P,
-                CamcorderProfile.QUALITY_1080P
+                CamcorderProfile.QUALITY_1080P,
+                CamcorderProfile.QUALITY_2160P
         };
 
         ArrayList<ResolutionQuality> qualityList = new ArrayList<ResolutionQuality>();
diff --git a/build/config.mk b/build/config.mk
index b737ef7..e127e90 100644
--- a/build/config.mk
+++ b/build/config.mk
@@ -16,4 +16,5 @@
 BUILD_CTS_PACKAGE := cts/build/test_package.mk
 BUILD_CTS_GTEST_PACKAGE := cts/build/test_gtest_package.mk
 BUILD_CTS_HOST_JAVA_LIBRARY := cts/build/test_host_java_library.mk
+BUILD_CTS_TARGET_JAVA_LIBRARY := cts/build/test_target_java_library.mk
 BUILD_CTS_UI_JAVA_LIBRARY := cts/build/test_uiautomator.mk
diff --git a/build/test_target_java_library.mk b/build/test_target_java_library.mk
new file mode 100644
index 0000000..7e14f97
--- /dev/null
+++ b/build/test_target_java_library.mk
@@ -0,0 +1,44 @@
+# 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.
+
+#
+# Builds a host library and defines a rule to generate the associated test
+# package XML needed by CTS.
+#
+# Disable by default so "m cts" will work in emulator builds
+LOCAL_DEX_PREOPT := false
+include $(BUILD_JAVA_LIBRARY)
+cts_library_jar := $(CTS_TESTCASES_OUT)/$(LOCAL_MODULE).jar
+$(cts_library_jar): $(LOCAL_BUILT_MODULE) | $(ACP)
+	$(copy-file-to-target)
+
+cts_library_xml := $(CTS_TESTCASES_OUT)/$(LOCAL_MODULE).xml
+$(cts_library_xml): $(cts_library_jar)
+$(cts_library_xml): PRIVATE_PATH := $(LOCAL_PATH)/src
+$(cts_library_xml): PRIVATE_TEST_PACKAGE := $(LOCAL_CTS_TEST_PACKAGE)
+$(cts_library_xml): PRIVATE_LIBRARY := $(LOCAL_MODULE)
+$(cts_library_xml): PRIVATE_JAR_PATH := $(LOCAL_MODULE).jar
+$(cts_library_xml): PRIVATE_RUNTIME_ARGS := $(LOCAL_CTS_TARGET_RUNTIME_ARGS)
+$(cts_library_xml): $(TARGET_OUT_JAVA_LIBRARIES)/$(LOCAL_MODULE).jar $(CTS_EXPECTATIONS) $(CTS_JAVA_TEST_SCANNER_DOCLET) $(CTS_JAVA_TEST_SCANNER) $(CTS_XML_GENERATOR)
+	$(hide) echo Generating test description for target library $(PRIVATE_LIBRARY)
+	$(hide) mkdir -p $(CTS_TESTCASES_OUT)
+	$(hide) $(CTS_JAVA_TEST_SCANNER) -s $(PRIVATE_PATH) \
+						-d $(CTS_JAVA_TEST_SCANNER_DOCLET) | \
+			$(CTS_XML_GENERATOR) -t jUnitDeviceTest \
+						-j $(PRIVATE_JAR_PATH) \
+						-n $(PRIVATE_LIBRARY) \
+						-p $(PRIVATE_TEST_PACKAGE) \
+						-e $(CTS_EXPECTATIONS) \
+						-x "runtimeArgs->$(PRIVATE_RUNTIME_ARGS)" \
+						-o $@
diff --git a/development/ide/eclipse/.classpath b/development/ide/eclipse/.classpath
index 2ddb4d8..b95d52a 100644
--- a/development/ide/eclipse/.classpath
+++ b/development/ide/eclipse/.classpath
@@ -90,6 +90,7 @@
     <classpathentry kind="src" path="external/easymock/src"/>
     <classpathentry kind="src" path="out/target/common/obj/APPS/CtsAccessibilityServiceTestCases_intermediates/src/src"/>
     <classpathentry kind="src" path="out/target/common/obj/APPS/CtsTestStubs_intermediates/src/src"/>
+    <classpathentry kind="src" path="out/target/common/obj/APPS/CtsTestStubs_intermediates/src/renderscript/src"/>
     <classpathentry kind="src" path="out/target/common/obj/APPS/CtsVerifier_intermediates/src"/>
     <classpathentry kind="src" path="out/target/common/obj/APPS/CtsVerifierTests_intermediates/src"/>
 </classpath>
diff --git a/hostsidetests/appsecurity/.gitignore b/hostsidetests/appsecurity/.gitignore
new file mode 100644
index 0000000..5e56e04
--- /dev/null
+++ b/hostsidetests/appsecurity/.gitignore
@@ -0,0 +1 @@
+/bin
diff --git a/hostsidetests/appsecurity/src/com/android/cts/appsecurity/AppSecurityTests.java b/hostsidetests/appsecurity/src/com/android/cts/appsecurity/AppSecurityTests.java
index 88b05fb..702002c 100644
--- a/hostsidetests/appsecurity/src/com/android/cts/appsecurity/AppSecurityTests.java
+++ b/hostsidetests/appsecurity/src/com/android/cts/appsecurity/AppSecurityTests.java
@@ -40,6 +40,8 @@
  */
 public class AppSecurityTests extends DeviceTestCase implements IBuildReceiver {
 
+    private static final String RUNNER = "android.support.test.runner.AndroidJUnitRunner";
+
     // testSharedUidDifferentCerts constants
     private static final String SHARED_UI_APK = "CtsSharedUidInstall.apk";
     private static final String SHARED_UI_PKG = "com.android.cts.shareuidinstall";
@@ -509,7 +511,7 @@
             String testMethodName) throws DeviceNotAvailableException {
 
         RemoteAndroidTestRunner testRunner = new RemoteAndroidTestRunner(pkgName,
-                getDevice().getIDevice());
+                RUNNER, getDevice().getIDevice());
         if (testClassName != null && testMethodName != null) {
             testRunner.setMethodName(testClassName, testMethodName);
         }
@@ -560,7 +562,7 @@
             throws DeviceNotAvailableException {
         // TODO: move this to RemoteAndroidTestRunner once it supports users
         final String cmd = "am instrument --user " + userId + " -w -r -e class " + testClassName
-                + "#" + testMethodName + " " + pkgName + "/android.test.InstrumentationTestRunner";
+                + "#" + testMethodName + " " + pkgName + "/" + RUNNER;
         Log.i(LOG_TAG, "Running " + cmd + " on " + getDevice().getSerialNumber());
 
         CollectingTestListener listener = new CollectingTestListener();
diff --git a/hostsidetests/appsecurity/test-apps/AppAccessData/Android.mk b/hostsidetests/appsecurity/test-apps/AppAccessData/Android.mk
index 308992e..2c9c624 100644
--- a/hostsidetests/appsecurity/test-apps/AppAccessData/Android.mk
+++ b/hostsidetests/appsecurity/test-apps/AppAccessData/Android.mk
@@ -21,6 +21,7 @@
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
 LOCAL_SDK_VERSION := current
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
 
 LOCAL_PACKAGE_NAME := CtsAppAccessData
 
diff --git a/hostsidetests/appsecurity/test-apps/AppAccessData/AndroidManifest.xml b/hostsidetests/appsecurity/test-apps/AppAccessData/AndroidManifest.xml
index 3824ef3..ffc104e 100644
--- a/hostsidetests/appsecurity/test-apps/AppAccessData/AndroidManifest.xml
+++ b/hostsidetests/appsecurity/test-apps/AppAccessData/AndroidManifest.xml
@@ -26,7 +26,7 @@
         <uses-library android:name="android.test.runner" />
     </application>
 
-    <instrumentation android:name="android.test.InstrumentationTestRunner"
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
                      android:targetPackage="com.android.cts.appaccessdata"
                      android:label="Test to create app data."/>
 </manifest>
diff --git a/hostsidetests/appsecurity/test-apps/AppWithData/Android.mk b/hostsidetests/appsecurity/test-apps/AppWithData/Android.mk
index 8bcb045..098ce9c 100644
--- a/hostsidetests/appsecurity/test-apps/AppWithData/Android.mk
+++ b/hostsidetests/appsecurity/test-apps/AppWithData/Android.mk
@@ -21,6 +21,7 @@
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
 LOCAL_SDK_VERSION := current
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
 
 LOCAL_PACKAGE_NAME := CtsAppWithData
 
diff --git a/hostsidetests/appsecurity/test-apps/AppWithData/AndroidManifest.xml b/hostsidetests/appsecurity/test-apps/AppWithData/AndroidManifest.xml
index 9decbcd..598ebe7 100644
--- a/hostsidetests/appsecurity/test-apps/AppWithData/AndroidManifest.xml
+++ b/hostsidetests/appsecurity/test-apps/AppWithData/AndroidManifest.xml
@@ -27,7 +27,7 @@
         <uses-library android:name="android.test.runner" />
     </application>
 
-    <instrumentation android:name="android.test.InstrumentationTestRunner"
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
                      android:targetPackage="com.android.cts.appwithdata"
                      android:label="Test to create app data."/>
 </manifest>
diff --git a/hostsidetests/appsecurity/test-apps/ExternalStorageApp/Android.mk b/hostsidetests/appsecurity/test-apps/ExternalStorageApp/Android.mk
index bc99560..afc8764 100644
--- a/hostsidetests/appsecurity/test-apps/ExternalStorageApp/Android.mk
+++ b/hostsidetests/appsecurity/test-apps/ExternalStorageApp/Android.mk
@@ -18,6 +18,7 @@
 
 LOCAL_MODULE_TAGS := tests
 LOCAL_SDK_VERSION := current
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 LOCAL_PACKAGE_NAME := CtsExternalStorageApp
diff --git a/hostsidetests/appsecurity/test-apps/ExternalStorageApp/AndroidManifest.xml b/hostsidetests/appsecurity/test-apps/ExternalStorageApp/AndroidManifest.xml
index 0ba6684..9d8f615 100644
--- a/hostsidetests/appsecurity/test-apps/ExternalStorageApp/AndroidManifest.xml
+++ b/hostsidetests/appsecurity/test-apps/ExternalStorageApp/AndroidManifest.xml
@@ -20,7 +20,7 @@
         <uses-library android:name="android.test.runner" />
     </application>
 
-    <instrumentation android:name="android.test.InstrumentationTestRunner"
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
         android:targetPackage="com.android.cts.externalstorageapp" />
 
 </manifest>
diff --git a/hostsidetests/appsecurity/test-apps/InstrumentationAppDiffCert/Android.mk b/hostsidetests/appsecurity/test-apps/InstrumentationAppDiffCert/Android.mk
index 268ac73..e8ce3b8 100644
--- a/hostsidetests/appsecurity/test-apps/InstrumentationAppDiffCert/Android.mk
+++ b/hostsidetests/appsecurity/test-apps/InstrumentationAppDiffCert/Android.mk
@@ -21,6 +21,7 @@
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
 LOCAL_SDK_VERSION := current
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
 
 LOCAL_PACKAGE_NAME := CtsInstrumentationAppDiffCert
 
diff --git a/hostsidetests/appsecurity/test-apps/InstrumentationAppDiffCert/AndroidManifest.xml b/hostsidetests/appsecurity/test-apps/InstrumentationAppDiffCert/AndroidManifest.xml
index 8102656..a958c4c 100644
--- a/hostsidetests/appsecurity/test-apps/InstrumentationAppDiffCert/AndroidManifest.xml
+++ b/hostsidetests/appsecurity/test-apps/InstrumentationAppDiffCert/AndroidManifest.xml
@@ -34,7 +34,7 @@
     A self-instrumenting test runner, that will try to start the above instrumentation and
     verify it fails.
      -->
-    <instrumentation android:name="android.test.InstrumentationTestRunner"
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
                      android:targetPackage="com.android.cts.instrumentationdiffcertapp"
                      android:label="Test for instrumentation with different cert" />
 </manifest>
diff --git a/hostsidetests/appsecurity/test-apps/MultiUserStorageApp/Android.mk b/hostsidetests/appsecurity/test-apps/MultiUserStorageApp/Android.mk
index 48f88b8..1dd109a 100644
--- a/hostsidetests/appsecurity/test-apps/MultiUserStorageApp/Android.mk
+++ b/hostsidetests/appsecurity/test-apps/MultiUserStorageApp/Android.mk
@@ -18,6 +18,7 @@
 
 LOCAL_MODULE_TAGS := tests
 LOCAL_SDK_VERSION := current
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src) \
     ../ExternalStorageApp/src/com/android/cts/externalstorageapp/CommonExternalStorageTest.java
diff --git a/hostsidetests/appsecurity/test-apps/MultiUserStorageApp/AndroidManifest.xml b/hostsidetests/appsecurity/test-apps/MultiUserStorageApp/AndroidManifest.xml
index 6ace31b..8b599f2 100644
--- a/hostsidetests/appsecurity/test-apps/MultiUserStorageApp/AndroidManifest.xml
+++ b/hostsidetests/appsecurity/test-apps/MultiUserStorageApp/AndroidManifest.xml
@@ -20,7 +20,7 @@
         <uses-library android:name="android.test.runner" />
     </application>
 
-    <instrumentation android:name="android.test.InstrumentationTestRunner"
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
         android:targetPackage="com.android.cts.multiuserstorageapp" />
 
     <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
diff --git a/hostsidetests/appsecurity/test-apps/PermissionDeclareApp/Android.mk b/hostsidetests/appsecurity/test-apps/PermissionDeclareApp/Android.mk
index 938b325..60d6fad 100644
--- a/hostsidetests/appsecurity/test-apps/PermissionDeclareApp/Android.mk
+++ b/hostsidetests/appsecurity/test-apps/PermissionDeclareApp/Android.mk
@@ -21,6 +21,7 @@
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
 LOCAL_SDK_VERSION := current
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
 
 LOCAL_PACKAGE_NAME := CtsPermissionDeclareApp
 
diff --git a/hostsidetests/appsecurity/test-apps/PermissionDeclareAppCompat/Android.mk b/hostsidetests/appsecurity/test-apps/PermissionDeclareAppCompat/Android.mk
index acdc20f..ba7285c 100644
--- a/hostsidetests/appsecurity/test-apps/PermissionDeclareAppCompat/Android.mk
+++ b/hostsidetests/appsecurity/test-apps/PermissionDeclareAppCompat/Android.mk
@@ -21,6 +21,7 @@
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
 LOCAL_SDK_VERSION := 16
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
 
 LOCAL_PACKAGE_NAME := CtsPermissionDeclareAppCompat
 
diff --git a/hostsidetests/appsecurity/test-apps/ReadExternalStorageApp/Android.mk b/hostsidetests/appsecurity/test-apps/ReadExternalStorageApp/Android.mk
index 44e4bef..3e392e3 100644
--- a/hostsidetests/appsecurity/test-apps/ReadExternalStorageApp/Android.mk
+++ b/hostsidetests/appsecurity/test-apps/ReadExternalStorageApp/Android.mk
@@ -18,6 +18,7 @@
 
 LOCAL_MODULE_TAGS := tests
 LOCAL_SDK_VERSION := current
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src) \
     ../ExternalStorageApp/src/com/android/cts/externalstorageapp/CommonExternalStorageTest.java
diff --git a/hostsidetests/appsecurity/test-apps/ReadExternalStorageApp/AndroidManifest.xml b/hostsidetests/appsecurity/test-apps/ReadExternalStorageApp/AndroidManifest.xml
index f6582b9..03884c9 100644
--- a/hostsidetests/appsecurity/test-apps/ReadExternalStorageApp/AndroidManifest.xml
+++ b/hostsidetests/appsecurity/test-apps/ReadExternalStorageApp/AndroidManifest.xml
@@ -20,7 +20,7 @@
         <uses-library android:name="android.test.runner" />
     </application>
 
-    <instrumentation android:name="android.test.InstrumentationTestRunner"
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
         android:targetPackage="com.android.cts.readexternalstorageapp" />
 
     <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
diff --git a/hostsidetests/appsecurity/test-apps/SharedUidInstall/Android.mk b/hostsidetests/appsecurity/test-apps/SharedUidInstall/Android.mk
index 25ba1fe..76187ab 100644
--- a/hostsidetests/appsecurity/test-apps/SharedUidInstall/Android.mk
+++ b/hostsidetests/appsecurity/test-apps/SharedUidInstall/Android.mk
@@ -21,6 +21,7 @@
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
 LOCAL_SDK_VERSION := current
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
 
 LOCAL_PACKAGE_NAME := CtsSharedUidInstall
 
diff --git a/hostsidetests/appsecurity/test-apps/SharedUidInstallDiffCert/Android.mk b/hostsidetests/appsecurity/test-apps/SharedUidInstallDiffCert/Android.mk
index a00b009..c1422ec 100644
--- a/hostsidetests/appsecurity/test-apps/SharedUidInstallDiffCert/Android.mk
+++ b/hostsidetests/appsecurity/test-apps/SharedUidInstallDiffCert/Android.mk
@@ -21,6 +21,7 @@
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
 LOCAL_SDK_VERSION := current
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
 
 LOCAL_PACKAGE_NAME := CtsSharedUidInstallDiffCert
 
diff --git a/hostsidetests/appsecurity/test-apps/SimpleAppInstall/Android.mk b/hostsidetests/appsecurity/test-apps/SimpleAppInstall/Android.mk
index 3cd78cf..fb925cd 100644
--- a/hostsidetests/appsecurity/test-apps/SimpleAppInstall/Android.mk
+++ b/hostsidetests/appsecurity/test-apps/SimpleAppInstall/Android.mk
@@ -21,6 +21,7 @@
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
 LOCAL_SDK_VERSION := current
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
 
 LOCAL_PACKAGE_NAME := CtsSimpleAppInstall
 
diff --git a/hostsidetests/appsecurity/test-apps/SimpleAppInstallDiffCert/Android.mk b/hostsidetests/appsecurity/test-apps/SimpleAppInstallDiffCert/Android.mk
index 5fbc910..2224d72 100644
--- a/hostsidetests/appsecurity/test-apps/SimpleAppInstallDiffCert/Android.mk
+++ b/hostsidetests/appsecurity/test-apps/SimpleAppInstallDiffCert/Android.mk
@@ -21,6 +21,7 @@
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
 LOCAL_SDK_VERSION := current
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
 
 LOCAL_PACKAGE_NAME := CtsSimpleAppInstallDiffCert
 
diff --git a/hostsidetests/appsecurity/test-apps/TargetInstrumentationApp/Android.mk b/hostsidetests/appsecurity/test-apps/TargetInstrumentationApp/Android.mk
index cc87e29..38a0511 100644
--- a/hostsidetests/appsecurity/test-apps/TargetInstrumentationApp/Android.mk
+++ b/hostsidetests/appsecurity/test-apps/TargetInstrumentationApp/Android.mk
@@ -21,6 +21,7 @@
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
 LOCAL_SDK_VERSION := current
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
 
 LOCAL_PACKAGE_NAME := CtsTargetInstrumentationApp
 
diff --git a/hostsidetests/appsecurity/test-apps/UsePermissionDiffCert/Android.mk b/hostsidetests/appsecurity/test-apps/UsePermissionDiffCert/Android.mk
index d836042..8878c47 100644
--- a/hostsidetests/appsecurity/test-apps/UsePermissionDiffCert/Android.mk
+++ b/hostsidetests/appsecurity/test-apps/UsePermissionDiffCert/Android.mk
@@ -22,6 +22,7 @@
     ../PermissionDeclareApp/src/com/android/cts/permissiondeclareapp/GrantUriPermission.java
 
 LOCAL_SDK_VERSION := current
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
 
 LOCAL_PACKAGE_NAME := CtsUsePermissionDiffCert
 
diff --git a/hostsidetests/appsecurity/test-apps/UsePermissionDiffCert/AndroidManifest.xml b/hostsidetests/appsecurity/test-apps/UsePermissionDiffCert/AndroidManifest.xml
index b7307bb..7acf98f 100644
--- a/hostsidetests/appsecurity/test-apps/UsePermissionDiffCert/AndroidManifest.xml
+++ b/hostsidetests/appsecurity/test-apps/UsePermissionDiffCert/AndroidManifest.xml
@@ -38,5 +38,5 @@
     </application>
 
     <instrumentation android:targetPackage="com.android.cts.usespermissiondiffcertapp"
-            android:name="android.test.InstrumentationTestRunner"/>
+            android:name="android.support.test.runner.AndroidJUnitRunner"/>
 </manifest>
diff --git a/hostsidetests/appsecurity/test-apps/UsePermissionDiffCert/src/com/android/cts/usespermissiondiffcertapp/AccessPermissionWithDiffSigTest.java b/hostsidetests/appsecurity/test-apps/UsePermissionDiffCert/src/com/android/cts/usespermissiondiffcertapp/AccessPermissionWithDiffSigTest.java
index db26eec..8629a87 100644
--- a/hostsidetests/appsecurity/test-apps/UsePermissionDiffCert/src/com/android/cts/usespermissiondiffcertapp/AccessPermissionWithDiffSigTest.java
+++ b/hostsidetests/appsecurity/test-apps/UsePermissionDiffCert/src/com/android/cts/usespermissiondiffcertapp/AccessPermissionWithDiffSigTest.java
@@ -591,6 +591,7 @@
         grantIntent.setClass(getContext(),
                 service ? ReceiveUriService.class : ReceiveUriActivity.class);
         Intent intent = new Intent();
+        intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
         intent.setComponent(GRANT_URI_PERM_COMP);
         intent.setAction(service ? GrantUriPermission.ACTION_START_SERVICE
                 : GrantUriPermission.ACTION_START_ACTIVITY);
@@ -598,6 +599,27 @@
         getContext().sendBroadcast(intent);
     }
 
+    private void grantClipUriPermissionViaContext(Uri uri, int mode) {
+        Intent intent = new Intent();
+        intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+        intent.setComponent(GRANT_URI_PERM_COMP);
+        intent.setAction(GrantUriPermission.ACTION_GRANT_URI);
+        intent.putExtra(GrantUriPermission.EXTRA_PACKAGE_NAME, getContext().getPackageName());
+        intent.putExtra(GrantUriPermission.EXTRA_URI, uri);
+        intent.putExtra(GrantUriPermission.EXTRA_MODE, mode);
+        getContext().sendBroadcast(intent);
+    }
+
+    private void revokeClipUriPermissionViaContext(Uri uri, int mode) {
+        Intent intent = new Intent();
+        intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+        intent.setComponent(GRANT_URI_PERM_COMP);
+        intent.setAction(GrantUriPermission.ACTION_REVOKE_URI);
+        intent.putExtra(GrantUriPermission.EXTRA_URI, uri);
+        intent.putExtra(GrantUriPermission.EXTRA_MODE, mode);
+        getContext().sendBroadcast(intent);
+    }
+
     private void assertReadingClipAllowed(ClipData clip) {
         for (int i=0; i<clip.getItemCount(); i++) {
             ClipData.Item item = clip.getItemAt(i);
@@ -1355,4 +1377,166 @@
         getContext().sendOrderedBroadcast(intent, null, receiver, null, 0, null, null);
         receiver.assertSuccess("unexpected outgoing persisted Uri status");
     }
+
+    /**
+     * Validate behavior of prefix permission grants.
+     */
+    public void testGrantPrefixUriPermission() throws Exception {
+        final Uri target = Uri.withAppendedPath(PERM_URI_GRANTING, "foo1");
+        final Uri targetMeow = Uri.withAppendedPath(target, "meow");
+        final Uri targetMeowCat = Uri.withAppendedPath(targetMeow, "cat");
+
+        final ClipData clip = makeSingleClipData(target);
+        final ClipData clipMeow = makeSingleClipData(targetMeow);
+        final ClipData clipMeowCat = makeSingleClipData(targetMeowCat);
+
+        // Make sure we can't see the target
+        assertReadingClipNotAllowed(clip, "reading should have failed");
+        assertWritingClipNotAllowed(clip, "writing should have failed");
+
+        // Give ourselves prefix read access
+        ReceiveUriActivity.clearStarted();
+        grantClipUriPermission(clipMeow, Intent.FLAG_GRANT_READ_URI_PERMISSION
+                | Intent.FLAG_GRANT_PREFIX_URI_PERMISSION, false);
+        ReceiveUriActivity.waitForStart();
+
+        // Verify prefix read access
+        assertReadingClipNotAllowed(clip, "reading should have failed");
+        assertReadingClipAllowed(clipMeow);
+        assertReadingClipAllowed(clipMeowCat);
+        assertWritingClipNotAllowed(clip, "writing should have failed");
+        assertWritingClipNotAllowed(clipMeow, "writing should have failed");
+        assertWritingClipNotAllowed(clipMeowCat, "writing should have failed");
+
+        // Now give ourselves exact write access
+        ReceiveUriActivity.clearNewIntent();
+        grantClipUriPermission(clip, Intent.FLAG_GRANT_WRITE_URI_PERMISSION, false);
+        ReceiveUriActivity.waitForNewIntent();
+
+        // Verify we have exact write access, but not prefix write
+        assertReadingClipNotAllowed(clip, "reading should have failed");
+        assertReadingClipAllowed(clipMeow);
+        assertReadingClipAllowed(clipMeowCat);
+        assertWritingClipAllowed(clip);
+        assertWritingClipNotAllowed(clipMeow, "writing should have failed");
+        assertWritingClipNotAllowed(clipMeowCat, "writing should have failed");
+
+        ReceiveUriActivity.finishCurInstanceSync();
+    }
+
+    public void testGrantPersistablePrefixUriPermission() {
+        final ContentResolver resolver = getContext().getContentResolver();
+
+        final Uri target = Uri.withAppendedPath(PERM_URI_GRANTING, "foo2");
+        final Uri targetMeow = Uri.withAppendedPath(target, "meow");
+
+        final ClipData clip = makeSingleClipData(target);
+        final ClipData clipMeow = makeSingleClipData(targetMeow);
+
+        // Make sure we can't see the target
+        assertReadingClipNotAllowed(clip, "reading should have failed");
+
+        // Give ourselves prefix read access
+        ReceiveUriActivity.clearStarted();
+        grantClipUriPermission(clip, Intent.FLAG_GRANT_READ_URI_PERMISSION
+                | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION
+                | Intent.FLAG_GRANT_PREFIX_URI_PERMISSION, false);
+        ReceiveUriActivity.waitForStart();
+
+        // Verify prefix read access
+        assertReadingClipAllowed(clip);
+        assertReadingClipAllowed(clipMeow);
+
+        // Verify we can persist direct grant
+        long before = System.currentTimeMillis();
+        resolver.takePersistableUriPermission(target, Intent.FLAG_GRANT_READ_URI_PERMISSION);
+        long after = System.currentTimeMillis();
+        assertPersistedUriPermission(target, Intent.FLAG_GRANT_READ_URI_PERMISSION, before, after);
+
+        // But we can't take anywhere under the prefix
+        try {
+            resolver.takePersistableUriPermission(targetMeow,
+                    Intent.FLAG_GRANT_READ_URI_PERMISSION);
+            fail("taking under prefix should have failed");
+        } catch (SecurityException expected) {
+        }
+
+        // Should still have access regardless of taking
+        assertReadingClipAllowed(clip);
+        assertReadingClipAllowed(clipMeow);
+
+        // And clean up our grants
+        resolver.releasePersistableUriPermission(target, Intent.FLAG_GRANT_READ_URI_PERMISSION);
+        assertNoPersistedUriPermission();
+
+        ReceiveUriActivity.finishCurInstanceSync();
+    }
+
+    /**
+     * Validate behavior of directly granting/revoking permission grants.
+     */
+    public void testDirectGrantRevokeUriPermission() throws Exception {
+        final ContentResolver resolver = getContext().getContentResolver();
+
+        final Uri target = Uri.withAppendedPath(PERM_URI_GRANTING, "foo3");
+        final Uri targetMeow = Uri.withAppendedPath(target, "meow");
+        final Uri targetMeowCat = Uri.withAppendedPath(targetMeow, "cat");
+
+        final ClipData clip = makeSingleClipData(target);
+        final ClipData clipMeow = makeSingleClipData(targetMeow);
+        final ClipData clipMeowCat = makeSingleClipData(targetMeowCat);
+
+        // Make sure we can't see the target
+        assertReadingClipNotAllowed(clipMeow, "reading should have failed");
+        assertWritingClipNotAllowed(clipMeow, "writing should have failed");
+
+        // Give ourselves some grants:
+        // /meow/cat  WRITE|PERSISTABLE
+        // /meow      READ|PREFIX
+        // /meow      WRITE
+        grantClipUriPermissionViaContext(targetMeowCat, Intent.FLAG_GRANT_WRITE_URI_PERMISSION
+                | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION);
+        grantClipUriPermissionViaContext(targetMeow, Intent.FLAG_GRANT_READ_URI_PERMISSION
+                | Intent.FLAG_GRANT_PREFIX_URI_PERMISSION);
+        grantClipUriPermissionViaContext(targetMeow, Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+        SystemClock.sleep(2000);
+
+        long before = System.currentTimeMillis();
+        resolver.takePersistableUriPermission(targetMeowCat, Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+        long after = System.currentTimeMillis();
+        assertPersistedUriPermission(targetMeowCat, Intent.FLAG_GRANT_WRITE_URI_PERMISSION, before, after);
+
+        // Verify they look good
+        assertReadingClipNotAllowed(clip, "reading should have failed");
+        assertReadingClipAllowed(clipMeow);
+        assertReadingClipAllowed(clipMeowCat);
+        assertWritingClipNotAllowed(clip, "writing should have failed");
+        assertWritingClipAllowed(clipMeow);
+        assertWritingClipAllowed(clipMeowCat);
+
+        // Revoke anyone with write under meow
+        revokeClipUriPermissionViaContext(targetMeow, Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+        SystemClock.sleep(2000);
+
+        // This should have nuked persisted permission at lower level, but it
+        // shoulnd't have touched our prefix read.
+        assertReadingClipNotAllowed(clip, "reading should have failed");
+        assertReadingClipAllowed(clipMeow);
+        assertReadingClipAllowed(clipMeowCat);
+        assertWritingClipNotAllowed(clip, "writing should have failed");
+        assertWritingClipNotAllowed(clipMeow, "writing should have failed");
+        assertWritingClipNotAllowed(clipMeowCat, "writing should have failed");
+        assertNoPersistedUriPermission();
+
+        // Revoking read at top of tree should nuke everything else
+        revokeClipUriPermissionViaContext(target, Intent.FLAG_GRANT_READ_URI_PERMISSION);
+        SystemClock.sleep(2000);
+        assertReadingClipNotAllowed(clip, "reading should have failed");
+        assertReadingClipNotAllowed(clipMeow, "reading should have failed");
+        assertReadingClipNotAllowed(clipMeowCat, "reading should have failed");
+        assertWritingClipNotAllowed(clip, "writing should have failed");
+        assertWritingClipNotAllowed(clipMeow, "writing should have failed");
+        assertWritingClipNotAllowed(clipMeowCat, "writing should have failed");
+        assertNoPersistedUriPermission();
+    }
 }
diff --git a/hostsidetests/appsecurity/test-apps/WriteExternalStorageApp/Android.mk b/hostsidetests/appsecurity/test-apps/WriteExternalStorageApp/Android.mk
index 4352bfb..a98fcea 100644
--- a/hostsidetests/appsecurity/test-apps/WriteExternalStorageApp/Android.mk
+++ b/hostsidetests/appsecurity/test-apps/WriteExternalStorageApp/Android.mk
@@ -18,6 +18,7 @@
 
 LOCAL_MODULE_TAGS := tests
 LOCAL_SDK_VERSION := current
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src) \
     ../ExternalStorageApp/src/com/android/cts/externalstorageapp/CommonExternalStorageTest.java
diff --git a/hostsidetests/appsecurity/test-apps/WriteExternalStorageApp/AndroidManifest.xml b/hostsidetests/appsecurity/test-apps/WriteExternalStorageApp/AndroidManifest.xml
index 82910aa..37e39e9 100644
--- a/hostsidetests/appsecurity/test-apps/WriteExternalStorageApp/AndroidManifest.xml
+++ b/hostsidetests/appsecurity/test-apps/WriteExternalStorageApp/AndroidManifest.xml
@@ -20,7 +20,7 @@
         <uses-library android:name="android.test.runner" />
     </application>
 
-    <instrumentation android:name="android.test.InstrumentationTestRunner"
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
         android:targetPackage="com.android.cts.writeexternalstorageapp" />
 
     <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
diff --git a/hostsidetests/holo/app/Android.mk b/hostsidetests/holo/app/Android.mk
index a5a7bf1..d390418 100644
--- a/hostsidetests/holo/app/Android.mk
+++ b/hostsidetests/holo/app/Android.mk
@@ -26,7 +26,7 @@
 
 LOCAL_PROGUARD_ENABLED := disabled
 
-LOCAL_JAVA_LIBRARIES := android.test.runner
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
diff --git a/hostsidetests/holo/app/AndroidManifest.xml b/hostsidetests/holo/app/AndroidManifest.xml
index 70e908c..d27c266 100755
--- a/hostsidetests/holo/app/AndroidManifest.xml
+++ b/hostsidetests/holo/app/AndroidManifest.xml
@@ -34,7 +34,7 @@
     </application>
 
     <!--  self-instrumenting test package. -->
-    <instrumentation android:name="android.test.InstrumentationTestRunner"
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
                      android:targetPackage="android.holo.app"
                      android:label="Generates Holo reference images"/>
 
diff --git a/hostsidetests/usb/SerialTestApp/Android.mk b/hostsidetests/usb/SerialTestApp/Android.mk
index d36b98e..a8f51ad 100644
--- a/hostsidetests/usb/SerialTestApp/Android.mk
+++ b/hostsidetests/usb/SerialTestApp/Android.mk
@@ -20,8 +20,6 @@
 
 LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
 
-LOCAL_JAVA_LIBRARIES := android.test.runner
-
 LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
diff --git a/hostsidetests/usb/SerialTestApp/AndroidManifest.xml b/hostsidetests/usb/SerialTestApp/AndroidManifest.xml
index 0667d60..a75dd75 100644
--- a/hostsidetests/usb/SerialTestApp/AndroidManifest.xml
+++ b/hostsidetests/usb/SerialTestApp/AndroidManifest.xml
@@ -22,5 +22,5 @@
     </application>
     <instrumentation
         android:targetPackage="com.android.cts.usb.serialtest"
-        android:name="android.test.InstrumentationCtsTestRunner" />
+        android:name="android.support.test.runner.AndroidJUnitRunner" />
 </manifest>
diff --git a/hostsidetests/usb/src/com/android/cts/usb/TestUsbTest.java b/hostsidetests/usb/src/com/android/cts/usb/TestUsbTest.java
index f53d210..a8ac3e0 100644
--- a/hostsidetests/usb/src/com/android/cts/usb/TestUsbTest.java
+++ b/hostsidetests/usb/src/com/android/cts/usb/TestUsbTest.java
@@ -51,7 +51,7 @@
 public class TestUsbTest extends DeviceTestCase implements IBuildReceiver {
 
     private static final String LOG_TAG = "TestUsbTest";
-    private static final String CTS_RUNNER = "android.test.InstrumentationCtsTestRunner";
+    private static final String CTS_RUNNER = "android.support.test.runner.AndroidJUnitRunner";
     private static final String PACKAGE_NAME = "com.android.cts.usb.serialtest";
     private static final String APK_NAME="CtsUsbSerialTestApp.apk";
     private ITestDevice mDevice;
diff --git a/libs/deviceutil/Android.mk b/libs/deviceutil/Android.mk
index 1b7db18..d5a2c57 100644
--- a/libs/deviceutil/Android.mk
+++ b/libs/deviceutil/Android.mk
@@ -20,12 +20,12 @@
     $(call all-java-files-under, src) \
     $(call all-java-files-under, ../commonutil/src)
 
-LOCAL_JAVA_LIBRARIES := android.test.runner
-
 LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
 
 LOCAL_MODULE_TAGS := optional
 
 LOCAL_MODULE := ctsdeviceutil
 
+LOCAL_SDK_VERSION := current
+
 include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/libs/runner/Android.mk b/libs/runner/Android.mk
index 629c1c2..9642f53 100644
--- a/libs/runner/Android.mk
+++ b/libs/runner/Android.mk
@@ -18,10 +18,12 @@
 
 LOCAL_SRC_FILES := $(call all-java-files-under, ../../tests/core/runner/src)
 
-LOCAL_JAVA_LIBRARIES := android.test.runner
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
 
 LOCAL_MODULE_TAGS := optional
 
 LOCAL_MODULE := ctstestrunner
 
+LOCAL_SDK_VERSION := current
+
 include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/libs/testserver/src/android/webkit/cts/CtsTestServer.java b/libs/testserver/src/android/webkit/cts/CtsTestServer.java
index 928a68e..22cbb7b 100644
--- a/libs/testserver/src/android/webkit/cts/CtsTestServer.java
+++ b/libs/testserver/src/android/webkit/cts/CtsTestServer.java
@@ -85,6 +85,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;
 
@@ -101,20 +102,22 @@
     private static final String DOWNLOAD_ID_PARAMETER = "downloadId";
     private static final String NUM_BYTES_PARAMETER = "numBytes";
 
-    public static final String ASSET_PREFIX = "/assets/";
-    public static final String RAW_PREFIX = "raw/";
-    public static final String FAVICON_ASSET_PATH = ASSET_PREFIX + "webkit/favicon.png";
-    public static final String APPCACHE_PATH = "/appcache.html";
-    public static final String APPCACHE_MANIFEST_PATH = "/appcache.manifest";
-    public static final String REDIRECT_PREFIX = "/redirect";
-    public static final String QUERY_REDIRECT_PATH = "/alt_redirect";
-    public static final String DELAY_PREFIX = "/delayed";
-    public static final String BINARY_PREFIX = "/binary";
-    public static final String COOKIE_PREFIX = "/cookie";
-    public static final String AUTH_PREFIX = "/auth";
-    public static final String SHUTDOWN_PREFIX = "/shutdown";
+    private static final String ASSET_PREFIX = "/assets/";
+    private static final String RAW_PREFIX = "raw/";
+    private static final String FAVICON_ASSET_PATH = ASSET_PREFIX + "webkit/favicon.png";
+    private static final String APPCACHE_PATH = "/appcache.html";
+    private static final String APPCACHE_MANIFEST_PATH = "/appcache.manifest";
+    private static final String REDIRECT_PREFIX = "/redirect";
+    private static final String QUERY_REDIRECT_PATH = "/alt_redirect";
+    private static final String DELAY_PREFIX = "/delayed";
+    private static final String BINARY_PREFIX = "/binary";
+    private static final String SET_COOKIE_PREFIX = "/setcookie";
+    private static final String COOKIE_PREFIX = "/cookie";
+    private static final String LINKED_SCRIPT_PREFIX = "/linkedscriptprefix";
+    private static final String AUTH_PREFIX = "/auth";
+    private static final String SHUTDOWN_PREFIX = "/shutdown";
     public static final String NOLENGTH_POSTFIX = "nolength";
-    public static final int DELAY_MILLIS = 2000;
+    private static final int DELAY_MILLIS = 2000;
 
     public static final String AUTH_REALM = "Android CTS";
     public static final String AUTH_USER = "cts";
@@ -126,6 +129,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;
@@ -133,13 +143,14 @@
     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;
     private final Map<String, HttpRequest> mLastRequestMap = new HashMap<String, HttpRequest>();
     private long mDocValidity;
     private long mDocAge;
+    private X509TrustManager mTrustManager;
 
     /**
      * Create and start a local HTTP server instance.
@@ -168,19 +179,43 @@
      * @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 {
+        this(context, sslMode, new CtsTrustManager());
+    }
+
+    /**
+     * 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.
+     * @param trustManager the trustManager
+     * @throws Exception
+     */
+    public CtsTestServer(Context context, SslMode sslMode, X509TrustManager trustManager)
+            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>();
+        mTrustManager = trustManager;
         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();
     }
 
@@ -219,7 +254,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.
@@ -228,13 +265,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();
         }
     }
 
@@ -246,7 +284,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) {
@@ -259,6 +297,13 @@
     }
 
     /**
+     * @return a trust manager array of size 1.
+     */
+    private X509TrustManager[] getTrustManagers() {
+        return new X509TrustManager[] { mTrustManager };
+    }
+
+    /**
      * {@link HostnameVerifier} that verifies everybody. This permits
      * the client to trust the web server and call
      * {@link CtsTestServer#shutdown()}.
@@ -293,8 +338,20 @@
      * @param path The path of the asset. See {@link AssetManager#open(String)}
      */
     public String getDelayedAssetUrl(String path) {
+        return getDelayedAssetUrl(path, DELAY_MILLIS);
+    }
+
+    /**
+     * Return an artificially delayed absolute URL that refers to the given asset. This can be
+     * used to emulate a slow HTTP server or connection.
+     * @param path The path of the asset. See {@link AssetManager#open(String)}
+     * @param delayMs The number of milliseconds to delay the request
+     */
+    public String getDelayedAssetUrl(String path, int delayMs) {
         StringBuilder sb = new StringBuilder(getBaseUri());
         sb.append(DELAY_PREFIX);
+        sb.append("/");
+        sb.append(delayMs);
         sb.append(ASSET_PREFIX);
         sb.append(path);
         return sb.toString();
@@ -313,7 +370,6 @@
         return sb.toString();
     }
 
-
     /**
      * Return an absolute URL that indirectly refers to the given asset.
      * When a client fetches this URL, the server will respond with a temporary redirect (302)
@@ -359,6 +415,43 @@
         return sb.toString();
     }
 
+    /**
+     * getSetCookieUrl returns a URL that attempts to set the cookie
+     * "key=value" when fetched.
+     * @param path a suffix to disambiguate mulitple Cookie URLs.
+     * @param key the key of the cookie.
+     * @return the url for a page that attempts to set the cookie.
+     */
+    public String getSetCookieUrl(String path, String key, String value) {
+        StringBuilder sb = new StringBuilder(getBaseUri());
+        sb.append(SET_COOKIE_PREFIX);
+        sb.append(path);
+        sb.append("?key=");
+        sb.append(key);
+        sb.append("&value=");
+        sb.append(value);
+        return sb.toString();
+    }
+
+    /**
+     * getLinkedScriptUrl returns a URL for a page with a script tag where
+     * src equals the URL passed in.
+     * @param path a suffix to disambiguate mulitple Linked Script URLs.
+     * @param url the src of the script tag.
+     * @return the url for the page with the script link in.
+     */
+    public String getLinkedScriptUrl(String path, String url) {
+        StringBuilder sb = new StringBuilder(getBaseUri());
+        sb.append(LINKED_SCRIPT_PREFIX);
+        sb.append(path);
+        sb.append("?url=");
+        try {
+            sb.append(URLEncoder.encode(url, "UTF-8"));
+        } catch (UnsupportedEncodingException e) {
+        }
+        return sb.toString();
+    }
+
     public String getBinaryUrl(String mimeType, int contentLength) {
         StringBuilder sb = new StringBuilder(getBaseUri());
         sb.append(BINARY_PREFIX);
@@ -520,12 +613,14 @@
             path = FAVICON_ASSET_PATH;
         }
         if (path.startsWith(DELAY_PREFIX)) {
+            String delayPath = path.substring(DELAY_PREFIX.length() + 1);
+            String delay = delayPath.substring(0, delayPath.indexOf('/'));
+            path = delayPath.substring(delay.length());
             try {
-                Thread.sleep(DELAY_MILLIS);
+                Thread.sleep(Integer.valueOf(delay));
             } catch (InterruptedException ignored) {
                 // ignore
             }
-            path = path.substring(DELAY_PREFIX.length());
         }
         if (path.startsWith(AUTH_PREFIX)) {
             // authentication required
@@ -561,6 +656,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
                 }
@@ -636,8 +733,20 @@
             }
 
             response.addHeader("Set-Cookie", "count=" + count + "; path=" + COOKIE_PREFIX);
-            response.setEntity(createEntity("<html><head><title>" + cookieString +
-                    "</title></head><body>" + cookieString + "</body></html>"));
+            response.setEntity(createPage(cookieString.toString(), cookieString.toString()));
+        } else if (path.startsWith(SET_COOKIE_PREFIX)) {
+            response = createResponse(HttpStatus.SC_OK);
+            Uri parsedUri = Uri.parse(uriString);
+            String key = parsedUri.getQueryParameter("key");
+            String value = parsedUri.getQueryParameter("value");
+            String cookie = key + "=" + value;
+            response.addHeader("Set-Cookie", cookie);
+            response.setEntity(createPage(cookie, cookie));
+        } else if (path.startsWith(LINKED_SCRIPT_PREFIX)) {
+            response = createResponse(HttpStatus.SC_OK);
+            String src = Uri.parse(uriString).getQueryParameter("url");
+            String scriptTag = "<script src=\"" + src + "\"></script>";
+            response.setEntity(createPage("LinkedScript", scriptTag));
         } else if (path.equals(USERAGENT_PATH)) {
             response = createResponse(HttpStatus.SC_OK);
             Header agentHeader = request.getFirstHeader("User-Agent");
@@ -645,8 +754,7 @@
             if (agentHeader != null) {
                 agent = agentHeader.getValue();
             }
-            response.setEntity(createEntity("<html><head><title>" + agent + "</title></head>" +
-                    "<body>" + agent + "</body></html>"));
+            response.setEntity(createPage(agent, agent));
         } else if (path.equals(TEST_DOWNLOAD_PATH)) {
             response = createTestDownloadResponse(Uri.parse(uriString));
         } else if (path.equals(SHUTDOWN_PREFIX)) {
@@ -727,12 +835,7 @@
         // Fill in error reason. Avoid use of the ReasonPhraseCatalog, which is Locale-dependent.
         String reason = getReasonString(status);
         if (reason != null) {
-            StringBuffer buf = new StringBuffer("<html><head><title>");
-            buf.append(reason);
-            buf.append("</title></head><body>");
-            buf.append(reason);
-            buf.append("</body></html>");
-            response.setEntity(createEntity(buf.toString()));
+            response.setEntity(createPage(reason, reason));
         }
         return response;
     }
@@ -751,6 +854,14 @@
         return null;
     }
 
+    /**
+     * Create a string entity for a bare bones html page with provided title and body.
+     */
+    private static StringEntity createPage(String title, String bodyContent) {
+        return createEntity("<html><head><title>" + title + "</title></head>" +
+                "<body>" + bodyContent + "</body></html>");
+    }
+
     private static HttpResponse createTestDownloadResponse(Uri uri) throws IOException {
         String downloadId = uri.getQueryParameter(DOWNLOAD_ID_PARAMETER);
         int numBytes = uri.getQueryParameter(NUM_BYTES_PARAMETER) != null
@@ -794,7 +905,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);
@@ -829,13 +940,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);
 
@@ -851,19 +962,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(), mServer.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/Android.mk b/suite/audio_quality/Android.mk
index 65abd23..491596e 100644
--- a/suite/audio_quality/Android.mk
+++ b/suite/audio_quality/Android.mk
@@ -22,7 +22,7 @@
 CTS_AUDIO_INSTALL_DIR := $(HOST_OUT)/cts-audio-quality/android-cts-audio-quality
 CTS_AUDIO_QUALITY_ZIP := $(HOST_OUT)/cts-audio-quality/android-cts-audio-quality.zip
 
-cts_audio_quality_client_apk := $(TARGET_OUT_DATA_APPS)/CtsAudioClient.apk
+cts_audio_quality_client_apk := $(TARGET_OUT_DATA_APPS)/CtsAudioClient/CtsAudioClient.apk
 cts_audio_quality_host_bins := $(HOST_OUT)/bin/cts_audio_quality_test $(HOST_OUT)/bin/cts_audio_quality
 $(CTS_AUDIO_QUALITY_ZIP): PRIVATE_CLIENT_APK := $(cts_audio_quality_client_apk)
 $(CTS_AUDIO_QUALITY_ZIP): PRIVATE_HOST_BINS := $(cts_audio_quality_host_bins)
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/browserbench/Android.mk b/suite/cts/deviceTests/browserbench/Android.mk
index 6fdb06d..3696bcd 100644
--- a/suite/cts/deviceTests/browserbench/Android.mk
+++ b/suite/cts/deviceTests/browserbench/Android.mk
@@ -18,15 +18,13 @@
 # don't include this package in any target
 LOCAL_MODULE_TAGS := optional
 
-LOCAL_JAVA_LIBRARIES := android.test.runner
-
 LOCAL_STATIC_JAVA_LIBRARIES := ctsdeviceutil ctstestrunner ctstestserver
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
 LOCAL_PACKAGE_NAME := CtsDeviceBrowserBench
 
-LOCAL_SDK_VERSION := current
+LOCAL_SDK_VERSION := 16
 
 include $(BUILD_CTS_PACKAGE)
 
diff --git a/suite/cts/deviceTests/browserbench/AndroidManifest.xml b/suite/cts/deviceTests/browserbench/AndroidManifest.xml
index 16626ad..4bf5b5e 100644
--- a/suite/cts/deviceTests/browserbench/AndroidManifest.xml
+++ b/suite/cts/deviceTests/browserbench/AndroidManifest.xml
@@ -24,6 +24,6 @@
     <application>
         <uses-library android:name="android.test.runner" />
     </application>
-    <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
             android:targetPackage="com.android.cts.browser" />
 </manifest>
diff --git a/suite/cts/deviceTests/browserbench/src/com/android/cts/browser/BrowserBenchTest.java b/suite/cts/deviceTests/browserbench/src/com/android/cts/browser/BrowserBenchTest.java
index 81e6a21..997f730 100644
--- a/suite/cts/deviceTests/browserbench/src/com/android/cts/browser/BrowserBenchTest.java
+++ b/suite/cts/deviceTests/browserbench/src/com/android/cts/browser/BrowserBenchTest.java
@@ -16,9 +16,7 @@
 
 package com.android.cts.browser;
 
-import android.content.Context;
 import android.content.Intent;
-import android.content.pm.PackageManager;
 import android.cts.util.WatchDog;
 import android.net.Uri;
 import android.provider.Browser;
@@ -126,10 +124,6 @@
 
     @TimeoutReq(minutes = 60)
     public void testOctane() throws InterruptedException {
-        if (!hasWebViewFeature(getContext())) {
-            Log.w(TAG, "Skipping testOctane");
-            return;
-        }
         String url = mWebServer.getAssetUrl(OCTANE_START_FILE) + "?auto=1";
         final int kRepeat = 5;
         doTest(url, ResultType.LOWER_BETTER, ResultUnit.MS,
@@ -173,9 +167,4 @@
             numberToProcess++;
         }
     }
-
-    private static boolean hasWebViewFeature(Context context) {
-        // Query the system property that determines if there is a functional WebView on the device
-        return context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WEBVIEW);
-    }
 }
diff --git a/suite/cts/deviceTests/dram/Android.mk b/suite/cts/deviceTests/dram/Android.mk
index 861a313..13de747 100644
--- a/suite/cts/deviceTests/dram/Android.mk
+++ b/suite/cts/deviceTests/dram/Android.mk
@@ -18,8 +18,6 @@
 # don't include this package in any target
 LOCAL_MODULE_TAGS := tests
 
-LOCAL_JAVA_LIBRARIES := android.test.runner
-
 LOCAL_STATIC_JAVA_LIBRARIES := ctsdeviceutil ctstestrunner
 
 LOCAL_JNI_SHARED_LIBRARIES := libctsdram_jni
diff --git a/suite/cts/deviceTests/dram/AndroidManifest.xml b/suite/cts/deviceTests/dram/AndroidManifest.xml
index 70f6b11..c9aaf3d 100644
--- a/suite/cts/deviceTests/dram/AndroidManifest.xml
+++ b/suite/cts/deviceTests/dram/AndroidManifest.xml
@@ -23,7 +23,7 @@
     <application>
         <uses-library android:name="android.test.runner" />
     </application>
-    <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
             android:targetPackage="com.android.cts.dram"
             android:label="DRAM bandwidth measurement" />
 </manifest>
diff --git a/suite/cts/deviceTests/filesystemperf/Android.mk b/suite/cts/deviceTests/filesystemperf/Android.mk
index 5f0606e..843d21a 100644
--- a/suite/cts/deviceTests/filesystemperf/Android.mk
+++ b/suite/cts/deviceTests/filesystemperf/Android.mk
@@ -18,8 +18,6 @@
 # don't include this package in any target
 LOCAL_MODULE_TAGS := optional
 
-LOCAL_JAVA_LIBRARIES := android.test.runner
-
 LOCAL_STATIC_JAVA_LIBRARIES := ctsdeviceutil ctstestrunner
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
diff --git a/suite/cts/deviceTests/filesystemperf/AndroidManifest.xml b/suite/cts/deviceTests/filesystemperf/AndroidManifest.xml
index dc90a94..329bf19 100644
--- a/suite/cts/deviceTests/filesystemperf/AndroidManifest.xml
+++ b/suite/cts/deviceTests/filesystemperf/AndroidManifest.xml
@@ -23,7 +23,7 @@
     <application>
         <uses-library android:name="android.test.runner" />
     </application>
-    <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
             android:targetPackage="com.android.cts.filesystemperf"
             android:label="UI Latency measurement" />
 </manifest>
diff --git a/suite/cts/deviceTests/opengl/Android.mk b/suite/cts/deviceTests/opengl/Android.mk
index 8617436..7e93dd7 100644
--- a/suite/cts/deviceTests/opengl/Android.mk
+++ b/suite/cts/deviceTests/opengl/Android.mk
@@ -18,8 +18,6 @@
 # don't include this package in any target
 LOCAL_MODULE_TAGS := tests
 
-LOCAL_JAVA_LIBRARIES := android.test.runner
-
 LOCAL_STATIC_JAVA_LIBRARIES := ctsdeviceutil ctstestrunner
 
 LOCAL_JNI_SHARED_LIBRARIES := libctsopengl_jni
diff --git a/suite/cts/deviceTests/opengl/AndroidManifest.xml b/suite/cts/deviceTests/opengl/AndroidManifest.xml
index a0273ec..05fb5be 100644
--- a/suite/cts/deviceTests/opengl/AndroidManifest.xml
+++ b/suite/cts/deviceTests/opengl/AndroidManifest.xml
@@ -46,7 +46,7 @@
     </application>
 
     <instrumentation
-        android:name="android.test.InstrumentationCtsTestRunner"
+        android:name="android.support.test.runner.AndroidJUnitRunner"
         android:label="OpenGL ES 2.0 Benchmark"
         android:targetPackage="com.android.cts.opengl" />
 
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/suite/cts/deviceTests/simplecpu/Android.mk b/suite/cts/deviceTests/simplecpu/Android.mk
index 0cc73cc..cc25223 100644
--- a/suite/cts/deviceTests/simplecpu/Android.mk
+++ b/suite/cts/deviceTests/simplecpu/Android.mk
@@ -18,8 +18,6 @@
 # don't include this package in any target
 LOCAL_MODULE_TAGS := tests
 
-LOCAL_JAVA_LIBRARIES := android.test.runner
-
 LOCAL_STATIC_JAVA_LIBRARIES := ctsdeviceutil ctstestrunner
 
 LOCAL_JNI_SHARED_LIBRARIES := libctscpu_jni
diff --git a/suite/cts/deviceTests/simplecpu/AndroidManifest.xml b/suite/cts/deviceTests/simplecpu/AndroidManifest.xml
index 69e4ad2..e5c1c2b 100644
--- a/suite/cts/deviceTests/simplecpu/AndroidManifest.xml
+++ b/suite/cts/deviceTests/simplecpu/AndroidManifest.xml
@@ -23,7 +23,7 @@
     <application>
         <uses-library android:name="android.test.runner" />
     </application>
-    <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
             android:targetPackage="com.android.cts.simplecpu"
             android:label="Very simple CPU benchmarking" />
 </manifest>
diff --git a/suite/cts/deviceTests/ui/Android.mk b/suite/cts/deviceTests/ui/Android.mk
index 17287b2..ee52172 100644
--- a/suite/cts/deviceTests/ui/Android.mk
+++ b/suite/cts/deviceTests/ui/Android.mk
@@ -20,8 +20,6 @@
 
 LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
 
-LOCAL_JAVA_LIBRARIES := android.test.runner
-
 LOCAL_STATIC_JAVA_LIBRARIES := ctsdeviceutil ctstestrunner
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
diff --git a/suite/cts/deviceTests/ui/AndroidManifest.xml b/suite/cts/deviceTests/ui/AndroidManifest.xml
index 1be3eed..b41008e 100644
--- a/suite/cts/deviceTests/ui/AndroidManifest.xml
+++ b/suite/cts/deviceTests/ui/AndroidManifest.xml
@@ -36,8 +36,12 @@
     </application>
 
     <instrumentation
-        android:name="android.test.InstrumentationCtsTestRunner"
+        android:name="android.support.test.runner.AndroidJUnitRunner"
         android:label="UI Latency measurement"
-        android:targetPackage="com.android.cts.ui" />
+        android:targetPackage="com.android.cts.ui" >
+        <meta-data
+            android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
 
-</manifest>
\ No newline at end of file
+</manifest>
diff --git a/suite/cts/deviceTests/videoperf/Android.mk b/suite/cts/deviceTests/videoperf/Android.mk
index 6ace48f..cb398a9 100644
--- a/suite/cts/deviceTests/videoperf/Android.mk
+++ b/suite/cts/deviceTests/videoperf/Android.mk
@@ -18,8 +18,6 @@
 # don't include this package in any target
 LOCAL_MODULE_TAGS := optional
 
-LOCAL_JAVA_LIBRARIES := android.test.runner
-
 LOCAL_STATIC_JAVA_LIBRARIES := ctsdeviceutil ctstestrunner
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
diff --git a/suite/cts/deviceTests/videoperf/AndroidManifest.xml b/suite/cts/deviceTests/videoperf/AndroidManifest.xml
index 631141d..ca01298 100644
--- a/suite/cts/deviceTests/videoperf/AndroidManifest.xml
+++ b/suite/cts/deviceTests/videoperf/AndroidManifest.xml
@@ -23,7 +23,11 @@
     <application>
         <uses-library android:name="android.test.runner" />
     </application>
-    <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
             android:targetPackage="com.android.cts.videoperf"
-            android:label="UI Latency measurement" />
+            android:label="UI Latency measurement" >
+        <meta-data
+            android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
 </manifest>
diff --git a/suite/cts/hostTests/uihost/appA/Android.mk b/suite/cts/hostTests/uihost/appA/Android.mk
index 48d9009..3e76fdb 100644
--- a/suite/cts/hostTests/uihost/appA/Android.mk
+++ b/suite/cts/hostTests/uihost/appA/Android.mk
@@ -20,8 +20,6 @@
 
 LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
 
-LOCAL_JAVA_LIBRARIES := android.test.runner
-
 LOCAL_STATIC_JAVA_LIBRARIES := ctsdeviceutil ctstestrunner
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
diff --git a/suite/cts/hostTests/uihost/appB/Android.mk b/suite/cts/hostTests/uihost/appB/Android.mk
index 812637e..13af40f 100644
--- a/suite/cts/hostTests/uihost/appB/Android.mk
+++ b/suite/cts/hostTests/uihost/appB/Android.mk
@@ -20,8 +20,6 @@
 
 LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
 
-LOCAL_JAVA_LIBRARIES := android.test.runner
-
 LOCAL_STATIC_JAVA_LIBRARIES := ctsdeviceutil ctstestrunner
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
diff --git a/suite/cts/hostTests/uihost/control/Android.mk b/suite/cts/hostTests/uihost/control/Android.mk
index 565e2c0..3770918 100644
--- a/suite/cts/hostTests/uihost/control/Android.mk
+++ b/suite/cts/hostTests/uihost/control/Android.mk
@@ -20,8 +20,6 @@
 
 LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
 
-LOCAL_JAVA_LIBRARIES := android.test.runner
-
 LOCAL_STATIC_JAVA_LIBRARIES := ctsdeviceutil ctstestrunner
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
diff --git a/suite/cts/hostTests/uihost/control/AndroidManifest.xml b/suite/cts/hostTests/uihost/control/AndroidManifest.xml
index 9901d50..e4b10f2 100644
--- a/suite/cts/hostTests/uihost/control/AndroidManifest.xml
+++ b/suite/cts/hostTests/uihost/control/AndroidManifest.xml
@@ -24,5 +24,9 @@
     </application>
     <instrumentation
         android:targetPackage="com.android.cts.taskswitching.control"
-        android:name="android.test.InstrumentationCtsTestRunner" />
+        android:name="android.support.test.runner.AndroidJUnitRunner" >
+        <meta-data
+            android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
 </manifest>
diff --git a/tests/Android.mk b/tests/Android.mk
index 6621518..3f5b753 100644
--- a/tests/Android.mk
+++ b/tests/Android.mk
@@ -30,9 +30,7 @@
 # Resource unit tests use a private locale and some densities
 LOCAL_AAPT_FLAGS = -c xx_YY -c cs -c small -c normal -c large -c xlarge \
         -c 320dpi -c 240dpi -c 160dpi -c 32dpi \
-	-c kok,kok_IN,kok_419,kok_419_VARIANT,kok_Knda_419,kok_Knda_419_VARIANT,kok_VARIANT,kok_Knda,tgl,tgl_PH \
-        --preferred-configurations 320dpi --preferred-configurations 240dpi \
-        --preferred-configurations 160dpi --preferred-configurations 32dpi
+        -c kok,kok_IN,kok_419,kok_419_VARIANT,kok_Knda_419,kok_Knda_419_VARIANT,kok_VARIANT,kok_Knda,tgl,tgl_PH
 
 LOCAL_PACKAGE_NAME := CtsTestStubs
 
diff --git a/tests/AndroidManifest.xml b/tests/AndroidManifest.xml
index 29caf33..37cd471 100644
--- a/tests/AndroidManifest.xml
+++ b/tests/AndroidManifest.xml
@@ -132,6 +132,7 @@
 
     <application android:label="Android TestCase"
                 android:icon="@drawable/size_48x48"
+                android:multiArch="true"
                 android:name="android.app.cts.MockApplication"
                 android:supportsRtl="true">
 
@@ -587,6 +588,18 @@
             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.hardware.camera2.cts.Camera2MultiViewStubActivity"
+            android:label="Camera2MultiViewStubActivity"
+            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/ProcessTest/Android.mk b/tests/ProcessTest/Android.mk
index a2958fe..5611b3b 100644
--- a/tests/ProcessTest/Android.mk
+++ b/tests/ProcessTest/Android.mk
@@ -20,8 +20,6 @@
 # and when built explicitly put it in the data partition
 LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
 
-LOCAL_JAVA_LIBRARIES := android.test.runner
-
 LOCAL_AAPT_FLAGS = -c xx_YY -c cs
 
 LOCAL_SRC_FILES := $(call all-subdir-java-files)
@@ -31,6 +29,8 @@
 LOCAL_DEX_PREOPT := false
 LOCAL_PROGUARD_ENABLED := disabled
 
+LOCAL_SDK_VERSION := current
+
 include $(BUILD_PACKAGE)
 
 include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/tests/ProcessTest/AndroidManifest.xml b/tests/ProcessTest/AndroidManifest.xml
index b860bde..c7cf635 100644
--- a/tests/ProcessTest/AndroidManifest.xml
+++ b/tests/ProcessTest/AndroidManifest.xml
@@ -18,7 +18,7 @@
        android:sharedUserId="com.android.cts.process.uidpid_test">
 
     <!-- InstrumentationTestRunner for AndroidTests -->
-    <instrumentation android:name="android.test.InstrumentationTestRunner"
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
                      android:targetPackage="com.android.cts.process"
                      android:label="Test process"/>
     <application>
diff --git a/tests/SignatureTest/Android.mk b/tests/SignatureTest/Android.mk
index 696f99e..fc794e8 100644
--- a/tests/SignatureTest/Android.mk
+++ b/tests/SignatureTest/Android.mk
@@ -20,12 +20,12 @@
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
-LOCAL_JAVA_LIBRARIES := android.test.runner
-
 LOCAL_PACKAGE_NAME := SignatureTest
 
 LOCAL_SDK_VERSION := current
 
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
+
 # To be passed in on command line
 CTS_API_VERSION ?= current
 ifeq (current,$(CTS_API_VERSION))
diff --git a/tests/SignatureTest/AndroidManifest.xml b/tests/SignatureTest/AndroidManifest.xml
index b4813e6..89d8590 100644
--- a/tests/SignatureTest/AndroidManifest.xml
+++ b/tests/SignatureTest/AndroidManifest.xml
@@ -23,7 +23,7 @@
         <uses-library android:name="android.test.runner"/>
     </application>
 
-    <instrumentation android:name="android.test.InstrumentationTestRunner"
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
                      android:targetPackage="android.tests.sigtest"
                      android:label="API Signature Test"/>
 
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/SignatureTest/tests/Android.mk b/tests/SignatureTest/tests/Android.mk
index bdd0a90..0796670 100644
--- a/tests/SignatureTest/tests/Android.mk
+++ b/tests/SignatureTest/tests/Android.mk
@@ -6,8 +6,6 @@
 # and when built explicitly put it in the data partition
 LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
 
-LOCAL_JAVA_LIBRARIES := android.test.runner
-
 # Include all test java files.
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
@@ -19,4 +17,8 @@
 
 LOCAL_DEX_PREOPT := false
 
+LOCAL_SDK_VERSION := current
+
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
+
 include $(BUILD_PACKAGE)
diff --git a/tests/SignatureTest/tests/AndroidManifest.xml b/tests/SignatureTest/tests/AndroidManifest.xml
index ab8a6d6..49b3827 100644
--- a/tests/SignatureTest/tests/AndroidManifest.xml
+++ b/tests/SignatureTest/tests/AndroidManifest.xml
@@ -20,7 +20,7 @@
         <uses-library android:name="android.test.runner"/>
     </application>
 
-    <instrumentation android:name="android.test.InstrumentationTestRunner"
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
                      android:targetPackage="android.tests.sigtest"
                      android:label="SignatureTest Functional Testset"/>
 
diff --git a/tests/acceleration/Android.mk b/tests/acceleration/Android.mk
index ef96a24..a6d6022 100644
--- a/tests/acceleration/Android.mk
+++ b/tests/acceleration/Android.mk
@@ -24,8 +24,6 @@
 
 LOCAL_PROGUARD_ENABLED := disabled
 
-LOCAL_JAVA_LIBRARIES := android.test.runner
-
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
 LOCAL_PACKAGE_NAME := CtsAccelerationTestStubs
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/tests/security/assets/selinux_policy.xml b/tests/assets/selinux_policy.xml
similarity index 100%
rename from tests/tests/security/assets/selinux_policy.xml
rename to tests/assets/selinux_policy.xml
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/AndroidManifest.xml b/tests/core/libcore/com/AndroidManifest.xml
index 4e37ef4..8790a7e 100644
--- a/tests/core/libcore/com/AndroidManifest.xml
+++ b/tests/core/libcore/com/AndroidManifest.xml
@@ -22,8 +22,12 @@
         <uses-library android:name="android.test.runner" />
     </application>
 
-    <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
                      android:targetPackage="android.core.tests.runner"
-                     android:label="cts framework tests"/>
+                     android:label="cts framework tests">
+        <meta-data
+            android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
 
 </manifest>
diff --git a/tests/core/libcore/conscrypt/AndroidManifest.xml b/tests/core/libcore/conscrypt/AndroidManifest.xml
index 6517a0b..b299793 100644
--- a/tests/core/libcore/conscrypt/AndroidManifest.xml
+++ b/tests/core/libcore/conscrypt/AndroidManifest.xml
@@ -22,8 +22,11 @@
         <uses-library android:name="android.test.runner" />
     </application>
 
-    <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
                      android:targetPackage="android.core.tests.runner"
-                     android:label="cts framework tests"/>
+                     android:label="cts framework tests">
+        <meta-data android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
 
 </manifest>
diff --git a/tests/core/libcore/dalvik/AndroidManifest.xml b/tests/core/libcore/dalvik/AndroidManifest.xml
index 6def32c..ca34678 100644
--- a/tests/core/libcore/dalvik/AndroidManifest.xml
+++ b/tests/core/libcore/dalvik/AndroidManifest.xml
@@ -22,8 +22,12 @@
         <uses-library android:name="android.test.runner" />
     </application>
 
-    <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
                      android:targetPackage="android.core.tests.runner"
-                     android:label="cts framework tests"/>
+                     android:label="cts framework tests">
+        <meta-data
+            android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
 
 </manifest>
diff --git a/tests/core/libcore/harmony_annotation/AndroidManifest.xml b/tests/core/libcore/harmony_annotation/AndroidManifest.xml
index 0c59b1b..c83ecf2 100644
--- a/tests/core/libcore/harmony_annotation/AndroidManifest.xml
+++ b/tests/core/libcore/harmony_annotation/AndroidManifest.xml
@@ -22,8 +22,12 @@
         <uses-library android:name="android.test.runner" />
     </application>
 
-    <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
                      android:targetPackage="android.core.tests.runner"
-                     android:label="cts framework tests"/>
+                     android:label="cts framework tests">
+        <meta-data
+            android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
 
 </manifest>
diff --git a/tests/core/libcore/harmony_beans/AndroidManifest.xml b/tests/core/libcore/harmony_beans/AndroidManifest.xml
index b4932dd..b9b161c 100644
--- a/tests/core/libcore/harmony_beans/AndroidManifest.xml
+++ b/tests/core/libcore/harmony_beans/AndroidManifest.xml
@@ -22,8 +22,12 @@
         <uses-library android:name="android.test.runner" />
     </application>
 
-    <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
                      android:targetPackage="android.core.tests.runner"
-                     android:label="cts framework tests"/>
+                     android:label="cts framework tests">
+        <meta-data
+            android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
 
 </manifest>
diff --git a/tests/core/libcore/harmony_java_io/AndroidManifest.xml b/tests/core/libcore/harmony_java_io/AndroidManifest.xml
index 65d64ab..e69d4b4 100644
--- a/tests/core/libcore/harmony_java_io/AndroidManifest.xml
+++ b/tests/core/libcore/harmony_java_io/AndroidManifest.xml
@@ -22,8 +22,12 @@
         <uses-library android:name="android.test.runner" />
     </application>
 
-    <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
                      android:targetPackage="android.core.tests.runner"
-                     android:label="cts framework tests"/>
+                     android:label="cts framework tests">
+        <meta-data
+            android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
 
 </manifest>
diff --git a/tests/core/libcore/harmony_java_lang/AndroidManifest.xml b/tests/core/libcore/harmony_java_lang/AndroidManifest.xml
index a5e499a..1a5a748 100644
--- a/tests/core/libcore/harmony_java_lang/AndroidManifest.xml
+++ b/tests/core/libcore/harmony_java_lang/AndroidManifest.xml
@@ -22,8 +22,11 @@
         <uses-library android:name="android.test.runner" />
     </application>
 
-    <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
                      android:targetPackage="android.core.tests.runner"
-                     android:label="cts framework tests"/>
+                     android:label="cts framework tests">
+        <meta-data android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
 
 </manifest>
diff --git a/tests/core/libcore/harmony_java_math/AndroidManifest.xml b/tests/core/libcore/harmony_java_math/AndroidManifest.xml
index f8cd224..4cdf654 100644
--- a/tests/core/libcore/harmony_java_math/AndroidManifest.xml
+++ b/tests/core/libcore/harmony_java_math/AndroidManifest.xml
@@ -22,8 +22,11 @@
         <uses-library android:name="android.test.runner" />
     </application>
 
-    <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
                      android:targetPackage="android.core.tests.runner"
-                     android:label="cts framework tests"/>
+                     android:label="cts framework tests">
+        <meta-data android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
 
 </manifest>
diff --git a/tests/core/libcore/harmony_java_net/AndroidManifest.xml b/tests/core/libcore/harmony_java_net/AndroidManifest.xml
index 3c9fd63..db14fa9 100644
--- a/tests/core/libcore/harmony_java_net/AndroidManifest.xml
+++ b/tests/core/libcore/harmony_java_net/AndroidManifest.xml
@@ -22,8 +22,11 @@
         <uses-library android:name="android.test.runner" />
     </application>
 
-    <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
                      android:targetPackage="android.core.tests.runner"
-                     android:label="cts framework tests"/>
+                     android:label="cts framework tests">
+        <meta-data android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
 
 </manifest>
diff --git a/tests/core/libcore/harmony_java_nio/AndroidManifest.xml b/tests/core/libcore/harmony_java_nio/AndroidManifest.xml
index 27166d4..0221ebb 100644
--- a/tests/core/libcore/harmony_java_nio/AndroidManifest.xml
+++ b/tests/core/libcore/harmony_java_nio/AndroidManifest.xml
@@ -22,8 +22,11 @@
         <uses-library android:name="android.test.runner" />
     </application>
 
-    <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
                      android:targetPackage="android.core.tests.runner"
-                     android:label="cts framework tests"/>
+                     android:label="cts framework tests">
+        <meta-data android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
 
 </manifest>
diff --git a/tests/core/libcore/harmony_java_text/AndroidManifest.xml b/tests/core/libcore/harmony_java_text/AndroidManifest.xml
index 0b6beed..6818053 100644
--- a/tests/core/libcore/harmony_java_text/AndroidManifest.xml
+++ b/tests/core/libcore/harmony_java_text/AndroidManifest.xml
@@ -22,8 +22,11 @@
         <uses-library android:name="android.test.runner" />
     </application>
 
-    <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
                      android:targetPackage="android.core.tests.runner"
-                     android:label="cts framework tests"/>
+                     android:label="cts framework tests">
+        <meta-data android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
 
 </manifest>
diff --git a/tests/core/libcore/harmony_java_util/AndroidManifest.xml b/tests/core/libcore/harmony_java_util/AndroidManifest.xml
index 72fa3ef..e36468e 100644
--- a/tests/core/libcore/harmony_java_util/AndroidManifest.xml
+++ b/tests/core/libcore/harmony_java_util/AndroidManifest.xml
@@ -22,8 +22,11 @@
         <uses-library android:name="android.test.runner" />
     </application>
 
-    <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
                      android:targetPackage="android.core.tests.runner"
-                     android:label="cts framework tests"/>
+                     android:label="cts framework tests">
+        <meta-data android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
 
 </manifest>
diff --git a/tests/core/libcore/harmony_javax_security/AndroidManifest.xml b/tests/core/libcore/harmony_javax_security/AndroidManifest.xml
index b7b35f2..c927855 100644
--- a/tests/core/libcore/harmony_javax_security/AndroidManifest.xml
+++ b/tests/core/libcore/harmony_javax_security/AndroidManifest.xml
@@ -22,8 +22,11 @@
         <uses-library android:name="android.test.runner" />
     </application>
 
-    <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
                      android:targetPackage="android.core.tests.runner"
-                     android:label="cts framework tests"/>
+                     android:label="cts framework tests">
+        <meta-data android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
 
 </manifest>
diff --git a/tests/core/libcore/harmony_logging/AndroidManifest.xml b/tests/core/libcore/harmony_logging/AndroidManifest.xml
index 94ee60e..8a669e2 100644
--- a/tests/core/libcore/harmony_logging/AndroidManifest.xml
+++ b/tests/core/libcore/harmony_logging/AndroidManifest.xml
@@ -22,8 +22,11 @@
         <uses-library android:name="android.test.runner" />
     </application>
 
-    <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
                      android:targetPackage="android.core.tests.runner"
-                     android:label="cts framework tests"/>
+                     android:label="cts framework tests">
+        <meta-data android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
 
 </manifest>
diff --git a/tests/core/libcore/harmony_prefs/AndroidManifest.xml b/tests/core/libcore/harmony_prefs/AndroidManifest.xml
index f8fdea2..ebcb4ef 100644
--- a/tests/core/libcore/harmony_prefs/AndroidManifest.xml
+++ b/tests/core/libcore/harmony_prefs/AndroidManifest.xml
@@ -22,8 +22,11 @@
         <uses-library android:name="android.test.runner" />
     </application>
 
-    <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
                      android:targetPackage="android.core.tests.runner"
-                     android:label="cts framework tests"/>
+                     android:label="cts framework tests">
+        <meta-data android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
 
 </manifest>
diff --git a/tests/core/libcore/harmony_sql/AndroidManifest.xml b/tests/core/libcore/harmony_sql/AndroidManifest.xml
index c6c31b2..7cd86da 100644
--- a/tests/core/libcore/harmony_sql/AndroidManifest.xml
+++ b/tests/core/libcore/harmony_sql/AndroidManifest.xml
@@ -22,8 +22,11 @@
         <uses-library android:name="android.test.runner" />
     </application>
 
-    <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
                      android:targetPackage="android.core.tests.runner"
-                     android:label="cts framework tests"/>
+                     android:label="cts framework tests">
+        <meta-data android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
 
 </manifest>
diff --git a/tests/core/libcore/jsr166/AndroidManifest.xml b/tests/core/libcore/jsr166/AndroidManifest.xml
index 3a0150e..fb4a648 100644
--- a/tests/core/libcore/jsr166/AndroidManifest.xml
+++ b/tests/core/libcore/jsr166/AndroidManifest.xml
@@ -22,8 +22,11 @@
         <uses-library android:name="android.test.runner" />
     </application>
 
-    <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
                      android:targetPackage="android.core.tests.runner"
-                     android:label="cts framework tests"/>
+                     android:label="cts framework tests">
+        <meta-data android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
 
 </manifest>
diff --git a/tests/core/libcore/libcore/AndroidManifest.xml b/tests/core/libcore/libcore/AndroidManifest.xml
index e4a5d1e..67a3023 100644
--- a/tests/core/libcore/libcore/AndroidManifest.xml
+++ b/tests/core/libcore/libcore/AndroidManifest.xml
@@ -22,8 +22,11 @@
         <uses-library android:name="android.test.runner" />
     </application>
 
-    <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
                      android:targetPackage="android.core.tests.runner"
-                     android:label="cts framework tests"/>
+                     android:label="cts framework tests">
+        <meta-data android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
 
 </manifest>
diff --git a/tests/core/libcore/okhttp/Android.mk b/tests/core/libcore/okhttp/Android.mk
new file mode 100644
index 0000000..4fe50ff
--- /dev/null
+++ b/tests/core/libcore/okhttp/Android.mk
@@ -0,0 +1,24 @@
+# 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)
+
+ifeq ($(BUILD_CTSCORE_PACKAGE),)
+    $(error BUILD_CTSCORE_PACKAGE must be defined)
+endif
+
+include $(CLEAR_VARS)
+LOCAL_PACKAGE_NAME := android.core.tests.libcore.package.okhttp
+LOCAL_STATIC_JAVA_LIBRARIES := okhttp-nojarjar junit4-target bouncycastle-nojarjar okhttp-tests-nojarjar
+include $(BUILD_CTSCORE_PACKAGE)
diff --git a/tests/core/libcore/okhttp/AndroidManifest.xml b/tests/core/libcore/okhttp/AndroidManifest.xml
new file mode 100644
index 0000000..f69bc83
--- /dev/null
+++ b/tests/core/libcore/okhttp/AndroidManifest.xml
@@ -0,0 +1,32 @@
+<?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="android.core.tests.libcore.package.okhttp">
+    <uses-permission android:name="android.permission.INTERNET" />
+    <application>
+        <uses-library android:name="android.test.runner" />
+    </application>
+
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+                     android:targetPackage="android.core.tests.runner"
+                     android:label="cts framework tests">
+        <meta-data android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
+
+</manifest>
diff --git a/tests/core/libcore/org/AndroidManifest.xml b/tests/core/libcore/org/AndroidManifest.xml
index d5b77bd..d705f65 100644
--- a/tests/core/libcore/org/AndroidManifest.xml
+++ b/tests/core/libcore/org/AndroidManifest.xml
@@ -22,8 +22,11 @@
         <uses-library android:name="android.test.runner" />
     </application>
 
-    <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
                      android:targetPackage="android.core.tests.runner"
-                     android:label="cts framework tests"/>
+                     android:label="cts framework tests">
+        <meta-data android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
 
 </manifest>
diff --git a/tests/core/libcore/sun/AndroidManifest.xml b/tests/core/libcore/sun/AndroidManifest.xml
index cc1a853..9888af3 100644
--- a/tests/core/libcore/sun/AndroidManifest.xml
+++ b/tests/core/libcore/sun/AndroidManifest.xml
@@ -22,8 +22,11 @@
         <uses-library android:name="android.test.runner" />
     </application>
 
-    <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
                      android:targetPackage="android.core.tests.runner"
-                     android:label="cts framework tests"/>
+                     android:label="cts framework tests">
+        <meta-data android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
 
 </manifest>
diff --git a/tests/core/libcore/tests/AndroidManifest.xml b/tests/core/libcore/tests/AndroidManifest.xml
index 02f8b4a..f7dab9c 100644
--- a/tests/core/libcore/tests/AndroidManifest.xml
+++ b/tests/core/libcore/tests/AndroidManifest.xml
@@ -22,8 +22,11 @@
         <uses-library android:name="android.test.runner" />
     </application>
 
-    <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
                      android:targetPackage="android.core.tests.runner"
-                     android:label="cts framework tests"/>
+                     android:label="cts framework tests">
+        <meta-data android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
 
 </manifest>
diff --git a/tests/core/runner/Android.mk b/tests/core/runner/Android.mk
index fb548fc..649b3b4 100644
--- a/tests/core/runner/Android.mk
+++ b/tests/core/runner/Android.mk
@@ -27,6 +27,6 @@
 
 LOCAL_PACKAGE_NAME := android.core.tests.runner
 
-LOCAL_STATIC_JAVA_LIBRARIES := core-tests
+LOCAL_STATIC_JAVA_LIBRARIES := core-tests android-support-test
 
 include $(BUILD_CTSCORE_PACKAGE)
diff --git a/tests/core/runner/AndroidManifest.xml b/tests/core/runner/AndroidManifest.xml
index e179710..05d210b 100644
--- a/tests/core/runner/AndroidManifest.xml
+++ b/tests/core/runner/AndroidManifest.xml
@@ -22,7 +22,7 @@
         <uses-library android:name="android.test.runner" />
     </application>
 
-    <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
                      android:targetPackage="android.core.tests.runner"
                      android:label="cts framework tests"/>
 
diff --git a/tests/core/runner/src/android/test/InstrumentationCtsTestRunner.java b/tests/core/runner/src/android/test/InstrumentationCtsTestRunner.java
deleted file mode 100644
index 96aeb51..0000000
--- a/tests/core/runner/src/android/test/InstrumentationCtsTestRunner.java
+++ /dev/null
@@ -1,238 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.test;
-
-import com.android.internal.util.Predicate;
-import com.android.internal.util.Predicates;
-
-import dalvik.annotation.BrokenTest;
-import dalvik.annotation.SideEffect;
-
-import android.app.KeyguardManager;
-import android.content.Context;
-import android.content.pm.PackageManager;
-import android.os.Bundle;
-import android.test.suitebuilder.TestMethod;
-import android.test.suitebuilder.annotation.HasAnnotation;
-import android.util.Log;
-
-import java.io.File;
-import java.lang.reflect.Field;
-import java.lang.reflect.Modifier;
-import java.net.Authenticator;
-import java.net.CookieHandler;
-import java.net.ResponseCache;
-import java.util.List;
-import java.util.Locale;
-import java.util.TimeZone;
-import javax.net.ssl.HostnameVerifier;
-import javax.net.ssl.HttpsURLConnection;
-import javax.net.ssl.SSLSocketFactory;
-
-import junit.framework.AssertionFailedError;
-import junit.framework.Test;
-import junit.framework.TestCase;
-import junit.framework.TestListener;
-
-/**
- * This test runner extends the default InstrumentationTestRunner. It overrides
- * the {@code onCreate(Bundle)} method and sets the system properties necessary
- * for many core tests to run. This is needed because there are some core tests
- * that need writing access to the file system. We also need to set the harness
- * Thread's context ClassLoader. Otherwise some classes and resources will not
- * be found. Finally, we add a means to free memory allocated by a TestCase
- * after its execution.
- *
- * @hide
- */
-public class InstrumentationCtsTestRunner extends InstrumentationTestRunner {
-
-    private static final String TAG = "InstrumentationCtsTestRunner";
-
-    /**
-     * True if (and only if) we are running in single-test mode (as opposed to
-     * batch mode).
-     */
-    private boolean mSingleTest = false;
-
-    private TestEnvironment mEnvironment;
-
-    @Override
-    public void onCreate(Bundle arguments) {
-        // We might want to move this to /sdcard, if is is mounted/writable.
-        File cacheDir = getTargetContext().getCacheDir();
-        System.setProperty("java.io.tmpdir", cacheDir.getAbsolutePath());
-
-        mEnvironment = new TestEnvironment();
-
-        if (arguments != null) {
-            String classArg = arguments.getString(ARGUMENT_TEST_CLASS);
-            mSingleTest = classArg != null && classArg.contains("#");
-        }
-
-        // attempt to disable keyguard,  if current test has permission to do so
-        // TODO: move this to a better place, such as InstrumentationTestRunner ?
-        if (getContext().checkCallingOrSelfPermission(android.Manifest.permission.DISABLE_KEYGUARD)
-                == PackageManager.PERMISSION_GRANTED) {
-            Log.i(TAG, "Disabling keyguard");
-            KeyguardManager keyguardManager =
-                (KeyguardManager) getContext().getSystemService(Context.KEYGUARD_SERVICE);
-            keyguardManager.newKeyguardLock("cts").disableKeyguard();
-        } else {
-            Log.i(TAG, "Test lacks permission to disable keyguard. " +
-                    "UI based tests may fail if keyguard is up");
-        }
-
-        super.onCreate(arguments);
-    }
-
-    @Override
-    protected AndroidTestRunner getAndroidTestRunner() {
-        AndroidTestRunner runner = super.getAndroidTestRunner();
-
-        runner.addTestListener(new TestListener() {
-            /**
-             * The last test class we executed code from.
-             */
-            private Class<?> lastClass;
-
-            @Override
-            public void startTest(Test test) {
-                if (test.getClass() != lastClass) {
-                    lastClass = test.getClass();
-                    printMemory(test.getClass());
-                }
-
-                Thread.currentThread().setContextClassLoader(
-                        test.getClass().getClassLoader());
-
-                mEnvironment.reset();
-            }
-
-            @Override
-            public void endTest(Test test) {
-                if (test instanceof TestCase) {
-                    cleanup((TestCase)test);
-                }
-            }
-
-            @Override
-            public void addError(Test test, Throwable t) {
-                // This space intentionally left blank.
-            }
-
-            @Override
-            public void addFailure(Test test, AssertionFailedError t) {
-                // This space intentionally left blank.
-            }
-
-            /**
-             * Dumps some memory info.
-             */
-            private void printMemory(Class<? extends Test> testClass) {
-                Runtime runtime = Runtime.getRuntime();
-
-                long total = runtime.totalMemory();
-                long free = runtime.freeMemory();
-                long used = total - free;
-
-                Log.d(TAG, "Total memory  : " + total);
-                Log.d(TAG, "Used memory   : " + used);
-                Log.d(TAG, "Free memory   : " + free);
-                Log.d(TAG, "Now executing : " + testClass.getName());
-            }
-
-            /**
-             * Nulls all non-static reference fields in the given test class.
-             * This method helps us with those test classes that don't have an
-             * explicit tearDown() method. Normally the garbage collector should
-             * take care of everything, but since JUnit keeps references to all
-             * test cases, a little help might be a good idea.
-             */
-            private void cleanup(TestCase test) {
-                Class<?> clazz = test.getClass();
-
-                while (clazz != TestCase.class) {
-                    Field[] fields = clazz.getDeclaredFields();
-                    for (int i = 0; i < fields.length; i++) {
-                        Field f = fields[i];
-                        if (!f.getType().isPrimitive() &&
-                                !Modifier.isStatic(f.getModifiers())) {
-                            try {
-                                f.setAccessible(true);
-                                f.set(test, null);
-                            } catch (Exception ignored) {
-                                // Nothing we can do about it.
-                            }
-                        }
-                    }
-
-                    clazz = clazz.getSuperclass();
-                }
-            }
-
-        });
-
-        return runner;
-    }
-
-    // http://code.google.com/p/vogar/source/browse/trunk/src/vogar/target/TestEnvironment.java
-    static class TestEnvironment {
-        private final Locale mDefaultLocale;
-        private final TimeZone mDefaultTimeZone;
-        private final String mJavaIoTmpDir;
-        private final HostnameVerifier mHostnameVerifier;
-        private final SSLSocketFactory mSslSocketFactory;
-
-        TestEnvironment() {
-            mDefaultLocale = Locale.getDefault();
-            mDefaultTimeZone = TimeZone.getDefault();
-            mJavaIoTmpDir = System.getProperty("java.io.tmpdir");
-            mHostnameVerifier = HttpsURLConnection.getDefaultHostnameVerifier();
-            mSslSocketFactory = HttpsURLConnection.getDefaultSSLSocketFactory();
-        }
-
-        void reset() {
-            System.setProperties(null);
-            System.setProperty("java.io.tmpdir", mJavaIoTmpDir);
-            Locale.setDefault(mDefaultLocale);
-            TimeZone.setDefault(mDefaultTimeZone);
-            Authenticator.setDefault(null);
-            CookieHandler.setDefault(null);
-            ResponseCache.setDefault(null);
-            HttpsURLConnection.setDefaultHostnameVerifier(mHostnameVerifier);
-            HttpsURLConnection.setDefaultSSLSocketFactory(mSslSocketFactory);
-        }
-    }
-
-    @Override
-    List<Predicate<TestMethod>> getBuilderRequirements() {
-        List<Predicate<TestMethod>> builderRequirements =
-                super.getBuilderRequirements();
-
-        Predicate<TestMethod> brokenTestPredicate =
-                Predicates.not(new HasAnnotation(BrokenTest.class));
-        builderRequirements.add(brokenTestPredicate);
-
-        if (!mSingleTest) {
-            Predicate<TestMethod> sideEffectPredicate =
-                    Predicates.not(new HasAnnotation(SideEffect.class));
-            builderRequirements.add(sideEffectPredicate);
-        }
-        return builderRequirements;
-    }
-}
diff --git a/tests/core/runner/src/com/android/cts/runner/CtsTestRunListener.java b/tests/core/runner/src/com/android/cts/runner/CtsTestRunListener.java
new file mode 100644
index 0000000..5196df1
--- /dev/null
+++ b/tests/core/runner/src/com/android/cts/runner/CtsTestRunListener.java
@@ -0,0 +1,177 @@
+/*
+ * 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.runner;
+
+import android.app.Instrumentation;
+import android.app.KeyguardManager;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.support.test.internal.runner.listener.InstrumentationRunListener;
+import android.util.Log;
+
+import junit.framework.TestCase;
+
+import org.junit.runner.Description;
+import org.junit.runner.notification.RunListener;
+
+import java.io.File;
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+import java.net.Authenticator;
+import java.net.CookieHandler;
+import java.net.ResponseCache;
+import java.util.Locale;
+import java.util.TimeZone;
+
+import javax.net.ssl.HostnameVerifier;
+import javax.net.ssl.HttpsURLConnection;
+import javax.net.ssl.SSLSocketFactory;
+
+/**
+ * A {@link RunListener} for CTS. Sets the system properties necessary for many
+ * core tests to run. This is needed because there are some core tests that need
+ * writing access to the file system.
+ * Finally, we add a means to free memory allocated by a TestCase after its
+ * execution.
+ */
+public class CtsTestRunListener extends InstrumentationRunListener {
+
+    private static final String TAG = "CtsTestRunListener";
+
+    private TestEnvironment mEnvironment;
+    private Class<?> lastClass;
+
+    @Override
+    public void testRunStarted(Description description) throws Exception {
+        mEnvironment = new TestEnvironment();
+
+        // We might want to move this to /sdcard, if is is mounted/writable.
+        File cacheDir = getInstrumentation().getTargetContext().getCacheDir();
+        System.setProperty("java.io.tmpdir", cacheDir.getAbsolutePath());
+
+        // attempt to disable keyguard, if current test has permission to do so
+        // TODO: move this to a better place, such as InstrumentationTestRunner
+        // ?
+        if (getInstrumentation().getContext().checkCallingOrSelfPermission(
+                android.Manifest.permission.DISABLE_KEYGUARD)
+                == PackageManager.PERMISSION_GRANTED) {
+            Log.i(TAG, "Disabling keyguard");
+            KeyguardManager keyguardManager =
+                    (KeyguardManager) getInstrumentation().getContext().getSystemService(
+                            Context.KEYGUARD_SERVICE);
+            keyguardManager.newKeyguardLock("cts").disableKeyguard();
+        } else {
+            Log.i(TAG, "Test lacks permission to disable keyguard. " +
+                    "UI based tests may fail if keyguard is up");
+        }
+    }
+
+    @Override
+    public void testStarted(Description description) throws Exception {
+        if (description.getTestClass() != lastClass) {
+            lastClass = description.getTestClass();
+            printMemory(description.getTestClass());
+        }
+
+        mEnvironment.reset();
+    }
+
+    @Override
+    public void testFinished(Description description) {
+        // no way to implement this in JUnit4...
+        // offending test cases that need this logic should probably be cleaned
+        // up individually
+        // if (test instanceof TestCase) {
+        // cleanup((TestCase) test);
+        // }
+    }
+
+    /**
+     * Dumps some memory info.
+     */
+    private void printMemory(Class<?> testClass) {
+        Runtime runtime = Runtime.getRuntime();
+
+        long total = runtime.totalMemory();
+        long free = runtime.freeMemory();
+        long used = total - free;
+
+        Log.d(TAG, "Total memory  : " + total);
+        Log.d(TAG, "Used memory   : " + used);
+        Log.d(TAG, "Free memory   : " + free);
+        Log.d(TAG, "Now executing : " + testClass.getName());
+    }
+
+    /**
+     * Nulls all non-static reference fields in the given test class. This
+     * method helps us with those test classes that don't have an explicit
+     * tearDown() method. Normally the garbage collector should take care of
+     * everything, but since JUnit keeps references to all test cases, a little
+     * help might be a good idea.
+     */
+    private void cleanup(TestCase test) {
+        Class<?> clazz = test.getClass();
+
+        while (clazz != TestCase.class) {
+            Field[] fields = clazz.getDeclaredFields();
+            for (int i = 0; i < fields.length; i++) {
+                Field f = fields[i];
+                if (!f.getType().isPrimitive() &&
+                        !Modifier.isStatic(f.getModifiers())) {
+                    try {
+                        f.setAccessible(true);
+                        f.set(test, null);
+                    } catch (Exception ignored) {
+                        // Nothing we can do about it.
+                    }
+                }
+            }
+
+            clazz = clazz.getSuperclass();
+        }
+    }
+
+    // http://code.google.com/p/vogar/source/browse/trunk/src/vogar/target/TestEnvironment.java
+    static class TestEnvironment {
+        private final Locale mDefaultLocale;
+        private final TimeZone mDefaultTimeZone;
+        private final String mJavaIoTmpDir;
+        private final HostnameVerifier mHostnameVerifier;
+        private final SSLSocketFactory mSslSocketFactory;
+
+        TestEnvironment() {
+            mDefaultLocale = Locale.getDefault();
+            mDefaultTimeZone = TimeZone.getDefault();
+            mJavaIoTmpDir = System.getProperty("java.io.tmpdir");
+            mHostnameVerifier = HttpsURLConnection.getDefaultHostnameVerifier();
+            mSslSocketFactory = HttpsURLConnection.getDefaultSSLSocketFactory();
+        }
+
+        void reset() {
+            System.setProperties(null);
+            System.setProperty("java.io.tmpdir", mJavaIoTmpDir);
+            Locale.setDefault(mDefaultLocale);
+            TimeZone.setDefault(mDefaultTimeZone);
+            Authenticator.setDefault(null);
+            CookieHandler.setDefault(null);
+            ResponseCache.setDefault(null);
+            HttpsURLConnection.setDefaultHostnameVerifier(mHostnameVerifier);
+            HttpsURLConnection.setDefaultSSLSocketFactory(mSslSocketFactory);
+        }
+    }
+
+}
diff --git a/tests/deviceadmin/Android.mk b/tests/deviceadmin/Android.mk
index bcc23fc..9ab9cb8 100644
--- a/tests/deviceadmin/Android.mk
+++ b/tests/deviceadmin/Android.mk
@@ -20,7 +20,7 @@
 
 LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
 
-LOCAL_JAVA_LIBRARIES := android.test.runner guava
+LOCAL_JAVA_LIBRARIES := guava
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
diff --git a/tests/deviceadmin/AndroidManifest.xml b/tests/deviceadmin/AndroidManifest.xml
index 2395d99..f70a677 100644
--- a/tests/deviceadmin/AndroidManifest.xml
+++ b/tests/deviceadmin/AndroidManifest.xml
@@ -107,7 +107,7 @@
 
     </application>
 
-    <instrumentation android:name="android.test.InstrumentationTestRunner"
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
             android:targetPackage="com.android.cts.admin"
             android:label="Tests for the device admin APIs."/>
 </manifest>
diff --git a/tests/expectations/knownfailures.txt b/tests/expectations/knownfailures.txt
index adefd76..d55acfe 100644
--- a/tests/expectations/knownfailures.txt
+++ b/tests/expectations/knownfailures.txt
@@ -1,188 +1,32 @@
 [
 {
-  name: "android.openglperf.cts.GlVboPerfTest#testVboWithVaryingIndexBufferNumbers",
-  bug: 6950385
-},
-{
-  name: "android.holo.cts.HoloTest",
-  bug: 8148617
-},
-{
-  name: "android.holo.cts.HoloHostTest",
-  bug: 15343612
-},
-{
-  name: "android.nativeopengl.EGLCleanupTest#TestCorrect",
-  name: "android.nativeopengl.EGLCreateContextTest#BadAttributeFails",
-  bug: 11652564
-},
-{
-  name: "android.hardware.camera2.cts.ImageReaderTest",
-  name: "android.hardware.camera2.cts.CameraCharacteristicsTest",
-  name: "android.hardware.camera2.cts.CameraCaptureResultTest",
-  name: "android.hardware.camera2.cts.CameraDeviceTest",
-  name: "android.hardware.camera2.cts.CameraManagerTest",
-  bug: 11141002
-},
-{
-  name: "com.android.cts.opengl.primitive.GLPrimitiveBenchmark#testFullPipelineOffscreen",
-  name: "com.android.cts.opengl.primitive.GLPrimitiveBenchmark#testPixelOutputOffscreen",
-  bug: 11238219
-},
-{
-  name: "android.hardware.cts.SensorIntegrationTests#testSensorsWithSeveralClients",
-  name: "android.hardware.cts.SensorIntegrationTests#testSensorsMovingRates",
-  bug: 11352697
-},
-{
-  name: "android.app.cts.DownloadManagerTest#testDownloadManager",
-  name: "android.app.cts.DownloadManagerTest#testDownloadManagerDestination",
-  name: "android.app.cts.DownloadManagerTest#testDownloadManagerDestinationExtension",
-  name: "android.app.cts.DownloadManagerTest#testMinimumDownload",
-  name: "android.content.cts.ContentResolverSyncTestCase#testCallMultipleAccounts",
-  name: "android.content.cts.ContentResolverSyncTestCase#testCancelSync",
-  name: "android.content.cts.ContentResolverSyncTestCase#testRequestSync",
-  bug: 14657953
-},
-{
-  name: "android.app.cts.DialogTest#testTouchEvent",
-  bug: 15455341
-},
-{
-  name: "android.database.sqlite.cts.SQLiteQueryBuilderTest#testSetProjectionMap",
-  bug: 12475524
-},
-{
-  name: "android.keystore.cts.KeyChainTest#testIsBoundKeyAlgorithm_RequiredAlgorithmsSupported",
-  bug: 15314696
-},
-{
-  name: "android.location.cts.LocationManagerTest",
-  name: "android.location.cts.LocationProviderTest",
-  bug: 15748237
-},
-{
-  name: "android.media.cts.AudioEffectTest",
-  bug: 15081808
-},
-{
-  name: "android.media.cts.AudioManagerTest#testMusicActive",
-  name: "android.media.cts.AudioManagerTest#testVolume",
-  bug: 15319269
-},
-{
-  name: "android.media.cts.AudioTrackTest#testGetTimestamp",
-  bug: 15320704
-},
-{
-  name: "android.media.cts.BassBoostTest",
-  name: "android.media.cts.EnvReverbTest",
-  name: "android.media.cts.EqualizerTest",
-  bug: 15163233
-},
-{
-  name: "android.media.cts.DecoderTest",
-  bug: 15163143
-},
-{
-  name: "android.media.cts.MediaPlayerFlakyNetworkTest",
-  bug: 15350295
-},
-{
-  name: "android.media.cts.MediaPlayerTest",
-  bug: 15321806
-},
-{
-  name: "android.media.cts.PresetReverbTest",
-  bug: 15338282
-},
-{
-  name: "android.media.cts.RingtoneManagerTest",
-  bug: 15343572
-},
-{
-  name: "android.media.cts.SoundPoolAacTest",
-  name: "android.media.cts.SoundPoolOggTest",
-  bug: 15350147
-},
-{
-  name: "android.media.cts.StreamingMediaPlayerTest",
-  bug: 15321207
-},
-{
-  name: "android.media.cts.VirtualizerTest",
-  bug: 15381765
-},
-{
-  name: "android.mediastress.cts.H263QcifLongPlayerTest",
-  name: "android.mediastress.cts.H264R480x360AacShortPlayerTest",
-  name: "android.mediastress.cts.Vp8R480x360LongPlayerTest",
-  bug: 15081769
-},
-{
-  name: "android.net.cts.ConnectivityManagerTest#testGetActiveNetworkInfo",
-  bug: 15087784
-},
-{
-  name: "android.media.cts.MediaCodecListTest#testIsAVCBaselineProfileSupported",
-  name: "android.media.cts.MediaCodecListTest#testIsH263BaselineProfileSupported",
-  name: "android.media.cts.MediaCodecListTest#testIsM4VSimpleProfileSupported",
-  name: "android.media.cts.MediaCodecListTest#testRequiredMediaCodecList",
-  bug: 15433903
-},
-{
-  name: "com.android.cts.uiautomatortest.CtsUiAutomatorTest",
-  bug: 15093828
-},
-{
-  name: "android.media.cts.RingtoneTest#testRingtone",
-  bug: 15343572
-},
-{
-  name: "android.media.cts.VisualizerTest",
-  bug: 15381765
-},
-{
-  name: "android.mediastress.cts.H263QcifShortPlayerTest",
-  name: "android.mediastress.cts.Vp8R480x360ShortPlayerTest",
-  bug: 15081769
-},
-{
-  name: "android.speech.tts.cts.TextToSpeechTest",
-  bug: 15082733
-},
-{
-  name: "android.net.cts.DnsTest#testDnsWorks",
-  name: "android.net.cts.SSLCertificateSocketFactoryTest",
-  name: "android.net.wifi.cts.NsdManagerTest#testAndroidTestCaseSetupProperly",
-  bug: 15004618
-},
-{
-  name: "android.media.cts.AudioRecord_BufferSizeTest#testGetMinBufferSize",
-  bug: 15319578
-},
-{
-  name: "android.media.cts.AudioRecordTest#testAudioRecordOP",
-  bug: 15340791
-},
-{
-  name: "android.text.cts.BoringLayoutTest#testScale",
-  bug: 15134518
-},
-{
-  name: "android.media.cts.MediaRecorderTest#testOnErrorListener",
-  name: "android.media.cts.MediaRecorderTest#testOnInfoListener",
-  name: "android.media.cts.MediaRecorderTest#testRecorderAudio",
-  name: "android.media.cts.MediaRecorderTest#testRecordingAudioInRawFormats",
-  name: "android.media.cts.MediaRecorderTest#testSetMaxDuration",
-  bug: 15106730
-},
-{
-  name: "android.widget.cts.TextViewTest#testTextAttr",
-  bug: 15131296
-},
-{
-  name: "android.permission.cts.NoSystemFunctionPermissionTest#testSetWallpaper",
-  bug: 15383108
+  description: "Not all jdwp features are currently supported. These tests will fail",
+  names: [
+    "org.apache.harmony.jpda.tests.jdwp.DebuggerOnDemand.OnthrowDebuggerLaunchTest#testDebuggerLaunch001",
+    "org.apache.harmony.jpda.tests.jdwp.DebuggerOnDemand.OnthrowDebuggerLaunchTest#testDebuggerLaunch002",
+    "org.apache.harmony.jpda.tests.jdwp.DebuggerOnDemand.OnthrowDebuggerLaunchTest#testDebuggerLaunch003",
+    "org.apache.harmony.jpda.tests.jdwp.DebuggerOnDemand.OnthrowDebuggerLaunchTest#testDebuggerLaunch004",
+    "org.apache.harmony.jpda.tests.jdwp.DebuggerOnDemand.OnthrowLaunchDebugger001#testDebugger002",
+    "org.apache.harmony.jpda.tests.jdwp.DebuggerOnDemand.OnthrowLaunchDebugger002#testDebugger",
+    "org.apache.harmony.jpda.tests.jdwp.Events.ClassUnloadTest#testClassUnloadEvent",
+    "org.apache.harmony.jpda.tests.jdwp.Events.MonitorContendedEnterTest#testMonitorContendedEnterForClassMatch",
+    "org.apache.harmony.jpda.tests.jdwp.Events.MonitorContendedEnteredTest#testMonitorContendedEnteredForClassMatch",
+    "org.apache.harmony.jpda.tests.jdwp.Events.MonitorWaitTest#testMonitorWaitForClassExclude",
+    "org.apache.harmony.jpda.tests.jdwp.Events.MonitorWaitTest#testMonitorWaitForClassMatchExact",
+    "org.apache.harmony.jpda.tests.jdwp.Events.MonitorWaitTest#testMonitorWaitForClassMatchFirst",
+    "org.apache.harmony.jpda.tests.jdwp.Events.MonitorWaitTest#testMonitorWaitForClassMatchSecond",
+    "org.apache.harmony.jpda.tests.jdwp.Events.MonitorWaitTest#testMonitorWaitForClassOnly",
+    "org.apache.harmony.jpda.tests.jdwp.Events.MonitorWaitedTest#testMonitorWaitedForClassExclude",
+    "org.apache.harmony.jpda.tests.jdwp.Events.MonitorWaitedTest#testMonitorWaitedForClassMatchExact",
+    "org.apache.harmony.jpda.tests.jdwp.Events.MonitorWaitedTest#testMonitorWaitedForClassMatchFirst",
+    "org.apache.harmony.jpda.tests.jdwp.Events.MonitorWaitedTest#testMonitorWaitedForClassMatchSecond",
+    "org.apache.harmony.jpda.tests.jdwp.Events.MonitorWaitedTest#testMonitorWaitedForClassOnly",
+    "org.apache.harmony.jpda.tests.jdwp.ReferenceType.ClassFileVersionTest#testClassFileVersion001",
+    "org.apache.harmony.jpda.tests.jdwp.ReferenceType.NestedTypesTest#testNestedTypes001",
+    "org.apache.harmony.jpda.tests.jdwp.ThreadReference.StopTest#testStop001",
+    "org.apache.harmony.jpda.tests.jdwp.VirtualMachine.HoldEventsTest#testHoldEvents001",
+    "org.apache.harmony.jpda.tests.jdwp.VirtualMachine.ReleaseEventsTest#testReleaseEvents001"
+  ],
+  bug: 16720689
 }
 ]
diff --git a/tests/jni/android_os_cts_CpuFeatures.cpp b/tests/jni/android_os_cts_CpuFeatures.cpp
index 5276257..6b5950c 100644
--- a/tests/jni/android_os_cts_CpuFeatures.cpp
+++ b/tests/jni/android_os_cts_CpuFeatures.cpp
@@ -17,6 +17,7 @@
 #include <cpu-features.h>
 #include <jni.h>
 #include <string.h>
+#include <sys/auxv.h>
 
 jboolean android_os_cts_CpuFeatures_isArmCpu(JNIEnv* env, jobject thiz)
 {
@@ -60,6 +61,11 @@
     return cpuFamily == ANDROID_CPU_FAMILY_X86_64;
 }
 
+jint android_os_cts_CpuFeatures_getHwCaps(JNIEnv*, jobject)
+{
+    return (jint)getauxval(AT_HWCAP);
+}
+
 static JNINativeMethod gMethods[] = {
     {  "isArmCpu", "()Z",
             (void *) android_os_cts_CpuFeatures_isArmCpu  },
@@ -75,6 +81,8 @@
             (void *) android_os_cts_CpuFeatures_isMips64Cpu  },
     {  "isX86_64Cpu", "()Z",
             (void *) android_os_cts_CpuFeatures_isX86_64Cpu  },
+    {  "getHwCaps", "()I",
+            (void *) android_os_cts_CpuFeatures_getHwCaps  },
 };
 
 int register_android_os_cts_CpuFeatures(JNIEnv* env)
diff --git a/tests/jni/android_os_cts_OSFeatures.cpp b/tests/jni/android_os_cts_OSFeatures.cpp
index 4ee8454..5f3ee4e 100644
--- a/tests/jni/android_os_cts_OSFeatures.cpp
+++ b/tests/jni/android_os_cts_OSFeatures.cpp
@@ -16,6 +16,17 @@
  */
 #include <jni.h>
 #include <sys/prctl.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <linux/filter.h>
+#include <linux/seccomp.h>
+
+#include <sys/utsname.h>
+#include <sys/types.h>
+#include <sys/wait.h>
 
 jint android_os_cts_OSFeatures_getNoNewPrivs(JNIEnv* env, jobject thiz)
 {
@@ -27,11 +38,82 @@
     return prctl(PR_CAPBSET_READ, i, 0, 0, 0);
 }
 
+#define DENY BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_KILL)
+
+static void test_seccomp() {
+    if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) < 0) {
+        _exit(0);
+    }
+
+    struct sock_filter filter[] = { DENY };
+    struct sock_fprog prog;
+    memset(&prog, 0, sizeof(prog));
+    prog.len = sizeof(filter) / sizeof(filter[0]);
+    prog.filter = filter;
+
+    if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog) < 0) {
+        _exit(0);
+    }
+
+    while(1) {
+        _exit(0);  // should crash with SIGSYS
+    }
+}
+
+jboolean android_os_cts_OSFeatures_hasSeccompSupport(JNIEnv* env, jobject)
+{
+    pid_t pid = fork();
+    if (pid == -1) {
+        jclass cls = env->FindClass("java/lang/RuntimeException");
+        env->ThrowNew(cls, "fork failed");
+        return false;
+    }
+    if (pid == 0) {
+        // child
+        test_seccomp();
+        _exit(0);
+    }
+
+    int status;
+    TEMP_FAILURE_RETRY(waitpid(pid, &status, 0));
+    return WIFSIGNALED(status) && (WTERMSIG(status) == SIGSYS);
+}
+
+jboolean android_os_cts_OSFeatures_needsSeccompSupport(JNIEnv*, jobject)
+{
+#if !defined(__arm__) && !defined(__i386__) && !defined(__x86_64__)
+    // Seccomp support is only available for ARM, x86, x86_64.
+    return false;
+#endif
+
+    int major;
+    int minor;
+    struct utsname uts;
+    if (uname(&uts) == -1) {
+        return false;
+    }
+
+    if (sscanf(uts.release, "%d.%d", &major, &minor) != 2) {
+        return false;
+    }
+
+    // Kernels before 3.8 don't have seccomp
+    if ((major < 3) || ((major == 3) && (minor < 8))) {
+        return false;
+    }
+
+    return true;
+}
+
 static JNINativeMethod gMethods[] = {
     {  "getNoNewPrivs", "()I",
             (void *) android_os_cts_OSFeatures_getNoNewPrivs  },
     {  "prctlCapBsetRead", "(I)I",
             (void *) android_os_cts_OSFeatures_prctlCapBsetRead },
+    {  "hasSeccompSupport", "()Z",
+            (void *) android_os_cts_OSFeatures_hasSeccompSupport  },
+    {  "needsSeccompSupport", "()Z",
+            (void *) android_os_cts_OSFeatures_needsSeccompSupport  }
 };
 
 int register_android_os_cts_OSFeatures(JNIEnv* env)
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..1c568a1
--- /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, null)) {
+            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 completionLatch = 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);
+                                }
+                                completionLatch.countDown();
+                            }
+                }, UserHandle.USER_CURRENT);
+
+                try {
+                    completionLatch.await();
+                } catch (InterruptedException ie) {
+                    /* ignore */
+                }
+
+                return success.get();
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+        }
+    }
+}
diff --git a/tests/res/drawable-nodpi/vector_icon_clip_path_1_golden.png b/tests/res/drawable-nodpi/vector_icon_clip_path_1_golden.png
new file mode 100644
index 0000000..be487d1
--- /dev/null
+++ b/tests/res/drawable-nodpi/vector_icon_clip_path_1_golden.png
Binary files differ
diff --git a/tests/res/drawable-nodpi/vector_icon_create_golden.png b/tests/res/drawable-nodpi/vector_icon_create_golden.png
new file mode 100644
index 0000000..943fce5
--- /dev/null
+++ b/tests/res/drawable-nodpi/vector_icon_create_golden.png
Binary files differ
diff --git a/tests/res/drawable-nodpi/vector_icon_delete_golden.png b/tests/res/drawable-nodpi/vector_icon_delete_golden.png
new file mode 100644
index 0000000..b46363e
--- /dev/null
+++ b/tests/res/drawable-nodpi/vector_icon_delete_golden.png
Binary files differ
diff --git a/tests/res/drawable-nodpi/vector_icon_heart_golden.png b/tests/res/drawable-nodpi/vector_icon_heart_golden.png
new file mode 100644
index 0000000..7450751
--- /dev/null
+++ b/tests/res/drawable-nodpi/vector_icon_heart_golden.png
Binary files differ
diff --git a/tests/res/drawable-nodpi/vector_icon_random_path_1_golden.png b/tests/res/drawable-nodpi/vector_icon_random_path_1_golden.png
new file mode 100644
index 0000000..91776a9
--- /dev/null
+++ b/tests/res/drawable-nodpi/vector_icon_random_path_1_golden.png
Binary files differ
diff --git a/tests/res/drawable-nodpi/vector_icon_random_path_2_golden.png b/tests/res/drawable-nodpi/vector_icon_random_path_2_golden.png
new file mode 100644
index 0000000..9af40a3
--- /dev/null
+++ b/tests/res/drawable-nodpi/vector_icon_random_path_2_golden.png
Binary files differ
diff --git a/tests/res/drawable-nodpi/vector_icon_render_order_1_golden.png b/tests/res/drawable-nodpi/vector_icon_render_order_1_golden.png
new file mode 100644
index 0000000..ea3be94
--- /dev/null
+++ b/tests/res/drawable-nodpi/vector_icon_render_order_1_golden.png
Binary files differ
diff --git a/tests/res/drawable-nodpi/vector_icon_render_order_2_golden.png b/tests/res/drawable-nodpi/vector_icon_render_order_2_golden.png
new file mode 100644
index 0000000..f317901
--- /dev/null
+++ b/tests/res/drawable-nodpi/vector_icon_render_order_2_golden.png
Binary files differ
diff --git a/tests/res/drawable-nodpi/vector_icon_repeated_a_1_golden.png b/tests/res/drawable-nodpi/vector_icon_repeated_a_1_golden.png
new file mode 100644
index 0000000..b3acfe7
--- /dev/null
+++ b/tests/res/drawable-nodpi/vector_icon_repeated_a_1_golden.png
Binary files differ
diff --git a/tests/res/drawable-nodpi/vector_icon_repeated_a_2_golden.png b/tests/res/drawable-nodpi/vector_icon_repeated_a_2_golden.png
new file mode 100644
index 0000000..bbc84b9
--- /dev/null
+++ b/tests/res/drawable-nodpi/vector_icon_repeated_a_2_golden.png
Binary files differ
diff --git a/tests/res/drawable-nodpi/vector_icon_repeated_cq_golden.png b/tests/res/drawable-nodpi/vector_icon_repeated_cq_golden.png
new file mode 100644
index 0000000..8d73cfd
--- /dev/null
+++ b/tests/res/drawable-nodpi/vector_icon_repeated_cq_golden.png
Binary files differ
diff --git a/tests/res/drawable-nodpi/vector_icon_repeated_st_golden.png b/tests/res/drawable-nodpi/vector_icon_repeated_st_golden.png
new file mode 100644
index 0000000..6094a9a
--- /dev/null
+++ b/tests/res/drawable-nodpi/vector_icon_repeated_st_golden.png
Binary files differ
diff --git a/tests/res/drawable-nodpi/vector_icon_schedule_golden.png b/tests/res/drawable-nodpi/vector_icon_schedule_golden.png
new file mode 100644
index 0000000..9822bc2
--- /dev/null
+++ b/tests/res/drawable-nodpi/vector_icon_schedule_golden.png
Binary files differ
diff --git a/tests/res/drawable-nodpi/vector_icon_settings_golden.png b/tests/res/drawable-nodpi/vector_icon_settings_golden.png
new file mode 100644
index 0000000..d12b142
--- /dev/null
+++ b/tests/res/drawable-nodpi/vector_icon_settings_golden.png
Binary files differ
diff --git a/tests/res/drawable-nodpi/vector_icon_stroke_1_golden.png b/tests/res/drawable-nodpi/vector_icon_stroke_1_golden.png
new file mode 100644
index 0000000..c57ad20
--- /dev/null
+++ b/tests/res/drawable-nodpi/vector_icon_stroke_1_golden.png
Binary files differ
diff --git a/tests/res/drawable-nodpi/vector_icon_stroke_2_golden.png b/tests/res/drawable-nodpi/vector_icon_stroke_2_golden.png
new file mode 100644
index 0000000..aee8ff5
--- /dev/null
+++ b/tests/res/drawable-nodpi/vector_icon_stroke_2_golden.png
Binary files differ
diff --git a/tests/res/drawable-nodpi/vector_icon_stroke_3_golden.png b/tests/res/drawable-nodpi/vector_icon_stroke_3_golden.png
new file mode 100644
index 0000000..1212fb3
--- /dev/null
+++ b/tests/res/drawable-nodpi/vector_icon_stroke_3_golden.png
Binary files differ
diff --git a/tests/res/drawable-nodpi/vector_icon_transformation_1_golden.png b/tests/res/drawable-nodpi/vector_icon_transformation_1_golden.png
new file mode 100644
index 0000000..baf418d
--- /dev/null
+++ b/tests/res/drawable-nodpi/vector_icon_transformation_1_golden.png
Binary files differ
diff --git a/tests/res/drawable-nodpi/vector_icon_transformation_2_golden.png b/tests/res/drawable-nodpi/vector_icon_transformation_2_golden.png
new file mode 100644
index 0000000..e0e14f3
--- /dev/null
+++ b/tests/res/drawable-nodpi/vector_icon_transformation_2_golden.png
Binary files differ
diff --git a/tests/res/drawable-nodpi/vector_icon_transformation_3_golden.png b/tests/res/drawable-nodpi/vector_icon_transformation_3_golden.png
new file mode 100644
index 0000000..b6798c2
--- /dev/null
+++ b/tests/res/drawable-nodpi/vector_icon_transformation_3_golden.png
Binary files differ
diff --git a/tests/res/drawable-nodpi/vector_icon_transformation_4_golden.png b/tests/res/drawable-nodpi/vector_icon_transformation_4_golden.png
new file mode 100644
index 0000000..a5d4d33
--- /dev/null
+++ b/tests/res/drawable-nodpi/vector_icon_transformation_4_golden.png
Binary files differ
diff --git a/tests/res/drawable-nodpi/vector_icon_transformation_5_golden.png b/tests/res/drawable-nodpi/vector_icon_transformation_5_golden.png
new file mode 100644
index 0000000..0d8ded1
--- /dev/null
+++ b/tests/res/drawable-nodpi/vector_icon_transformation_5_golden.png
Binary files differ
diff --git a/tests/res/drawable-nodpi/vector_icon_transformation_6_golden.png b/tests/res/drawable-nodpi/vector_icon_transformation_6_golden.png
new file mode 100644
index 0000000..64d07fa
--- /dev/null
+++ b/tests/res/drawable-nodpi/vector_icon_transformation_6_golden.png
Binary files differ
diff --git a/tests/res/drawable/alpha.png b/tests/res/drawable/alpha.png
new file mode 100644
index 0000000..8a88548
--- /dev/null
+++ b/tests/res/drawable/alpha.png
Binary files differ
diff --git a/tests/res/drawable/bitmapdrawable_theme.xml b/tests/res/drawable/bitmapdrawable_theme.xml
new file mode 100644
index 0000000..6df36b3
--- /dev/null
+++ b/tests/res/drawable/bitmapdrawable_theme.xml
@@ -0,0 +1,26 @@
+<?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.
+-->
+
+<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
+    android:antialias="?attr/themeBoolean"
+    android:autoMirrored="?attr/themeBoolean"
+    android:dither="?attr/themeBoolean"
+    android:filter="?attr/themeBoolean"
+    android:gravity="?attr/themeGravity"
+    android:mipMap="?attr/themeBoolean"
+    android:src="?attr/themeBitmap"
+    android:tileMode="?attr/themeTileMode" />
diff --git a/tests/res/drawable/colordrawable_theme.xml b/tests/res/drawable/colordrawable_theme.xml
new file mode 100644
index 0000000..00c6fe7
--- /dev/null
+++ b/tests/res/drawable/colordrawable_theme.xml
@@ -0,0 +1,19 @@
+<?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.
+-->
+
+<color xmlns:android="http://schemas.android.com/apk/res/android"
+    android:color="?attr/themeColor" />
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/drawable/gradientdrawable_radius_base.xml b/tests/res/drawable/gradientdrawable_radius_base.xml
new file mode 100644
index 0000000..ecd50f8
--- /dev/null
+++ b/tests/res/drawable/gradientdrawable_radius_base.xml
@@ -0,0 +1,39 @@
+<?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.
+-->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android" >
+
+    <gradient
+        android:centerColor="#ffff0000"
+        android:endColor="#0000ffff"
+        android:gradientRadius="50%"
+        android:startColor="#ffffffff"
+        android:type="radial" />
+
+    <corners android:radius="8px" />
+
+    <padding
+        android:bottom="10px"
+        android:left="4px"
+        android:right="6px"
+        android:top="2px" />
+
+    <size
+        android:height="50px"
+        android:width="50px" />
+
+</shape>
\ No newline at end of file
diff --git a/tests/res/drawable/gradientdrawable_radius_parent.xml b/tests/res/drawable/gradientdrawable_radius_parent.xml
new file mode 100644
index 0000000..73d116a
--- /dev/null
+++ b/tests/res/drawable/gradientdrawable_radius_parent.xml
@@ -0,0 +1,39 @@
+<?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.
+-->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android" >
+
+    <gradient
+        android:centerColor="#ffff0000"
+        android:endColor="#0000ffff"
+        android:gradientRadius="50%p"
+        android:startColor="#ffffffff"
+        android:type="radial" />
+
+    <corners android:radius="8px" />
+
+    <padding
+        android:bottom="10px"
+        android:left="4px"
+        android:right="6px"
+        android:top="2px" />
+
+    <size
+        android:height="50px"
+        android:width="50px" />
+
+</shape>
\ No newline at end of file
diff --git a/tests/res/drawable/gradientdrawable_theme.xml b/tests/res/drawable/gradientdrawable_theme.xml
new file mode 100644
index 0000000..68cec62
--- /dev/null
+++ b/tests/res/drawable/gradientdrawable_theme.xml
@@ -0,0 +1,50 @@
+<?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.
+-->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android" >
+
+    <corners
+        android:bottomLeftRadius="?attr/themeDimension"
+        android:bottomRightRadius="?attr/themeDimension"
+        android:topLeftRadius="?attr/themeDimension"
+        android:topRightRadius="?attr/themeDimension" />
+
+    <gradient
+        android:angle="?attr/themeAngle"
+        android:centerColor="?attr/themeColor"
+        android:centerX="?attr/themeFloat"
+        android:centerY="?attr/themeFloat"
+        android:endColor="?attr/themeColor"
+        android:gradientRadius="?attr/themeFloat"
+        android:startColor="?attr/themeColor"
+        android:useLevel="?attr/themeBoolean" />
+
+    <padding
+        android:bottom="?attr/themeDimension"
+        android:left="?attr/themeDimension"
+        android:right="?attr/themeDimension"
+        android:top="?attr/themeDimension" />
+
+    <size
+        android:height="?attr/themeDimension"
+        android:width="?attr/themeDimension" />
+
+    <solid android:color="?attr/themeColor" />
+
+    <stroke android:color="?attr/themeColor" />
+
+</shape>
diff --git a/tests/res/drawable/layerdrawable_theme.xml b/tests/res/drawable/layerdrawable_theme.xml
new file mode 100644
index 0000000..2a678ff
--- /dev/null
+++ b/tests/res/drawable/layerdrawable_theme.xml
@@ -0,0 +1,29 @@
+<?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.
+-->
+
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android"
+    android:autoMirrored="?attr/themeBoolean" >
+
+    <item android:drawable="@drawable/bitmapdrawable_theme"/>
+    <item>
+        <nine-patch
+            android:autoMirrored="?attr/themeBoolean"
+            android:dither="?attr/themeBoolean"
+            android:src="?attr/themeNinePatch" />
+    </item>
+
+</layer-list>
\ No newline at end of file
diff --git a/tests/res/drawable/ninepatchdrawable_theme.xml b/tests/res/drawable/ninepatchdrawable_theme.xml
new file mode 100644
index 0000000..bb031a5
--- /dev/null
+++ b/tests/res/drawable/ninepatchdrawable_theme.xml
@@ -0,0 +1,21 @@
+<?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.
+-->
+
+<nine-patch xmlns:android="http://schemas.android.com/apk/res/android"
+    android:autoMirrored="?attr/themeBoolean"
+    android:dither="?attr/themeBoolean"
+    android:src="?attr/themeNinePatch" />
diff --git a/tests/res/drawable/rippledrawable_theme.xml b/tests/res/drawable/rippledrawable_theme.xml
new file mode 100644
index 0000000..a49b820
--- /dev/null
+++ b/tests/res/drawable/rippledrawable_theme.xml
@@ -0,0 +1,19 @@
+<?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.
+-->
+
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+    android:color="?attr/themeColor" />
diff --git a/tests/res/drawable/statelist_testimage.jpg b/tests/res/drawable/statelist_testimage.jpg
new file mode 100644
index 0000000..754df0c
--- /dev/null
+++ b/tests/res/drawable/statelist_testimage.jpg
Binary files differ
diff --git a/tests/res/drawable/statelistdrawable.xml b/tests/res/drawable/statelistdrawable.xml
index 9d9aa3b..b867904 100644
--- a/tests/res/drawable/statelistdrawable.xml
+++ b/tests/res/drawable/statelistdrawable.xml
@@ -16,7 +16,7 @@
  -->
 
 <selector xmlns:android="http://schemas.android.com/apk/res/android">
-   <item android:state_focused="true" android:drawable="@drawable/testimage"/>
-   <item android:state_enabled="false" android:drawable="@drawable/testimage"/>
+   <item android:state_focused="true" android:drawable="@drawable/statelist_testimage"/>
+   <item android:state_enabled="false" android:drawable="@drawable/statelist_testimage"/>
 </selector>
 
diff --git a/tests/res/drawable/vector_icon_clip_path_1.xml b/tests/res/drawable/vector_icon_clip_path_1.xml
new file mode 100644
index 0000000..c2ab429
--- /dev/null
+++ b/tests/res/drawable/vector_icon_clip_path_1.xml
@@ -0,0 +1,71 @@
+<!--
+ 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:height="64dp"
+        android:width="64dp"
+        android:viewportHeight="12.25"
+        android:viewportWidth="7.30625" >
+
+    <group
+        android:pivotX="3.65"
+        android:pivotY="6.125"
+        android:rotation="-30" >
+        <clip-path
+            android:name="clip1"
+            android:pathData="
+                M 0, 6.125
+                l 7.3, 0
+                l 0, 12.25
+                l -7.3, 0
+                z" />
+    </group>
+    <group>
+        <path
+            android:name="one"
+            android:fillColor="#ff88ff"
+            android:pathData="M 1.215625,9.5l 1.9375,0.0 0.0-6.671875 -2.109375,0.421875 0.0-1.078125
+                l 2.09375-0.421875 1.1874998,0.0 0.0,7.75 1.9375,0.0 0.0,1.0
+                l -5.046875,0.0 0.0-1.0Z" />
+    </group>
+    <group
+        android:pivotX="3.65"
+        android:pivotY="6.125"
+        android:rotation="-30" >
+        <clip-path
+            android:name="clip2"
+            android:pathData="
+                M 0, 0
+                l 7.3, 0
+                l 0, 6.125
+                l -7.3, 0
+                z" />
+    </group>
+    <group>
+        <path
+            android:name="two"
+            android:fillColor="#ff88ff"
+            android:pathData="M 2.534375,9.6875l 4.140625,0.0 0.0,1.0 -5.5625,0.0 0.0-1.0q 0.671875-0.6875 1.828125-1.859375
+                        q 1.1718752-1.1875 1.4687502-1.53125 0.578125-0.625 0.796875-1.0625
+                        q 0.234375-0.453125 0.234375-0.875 0.0-0.703125 -0.5-1.140625
+                        q -0.484375-0.4375 -1.2656252-0.4375 -0.5625,0.0 -1.1875,0.1875
+                        q -0.609375,0.1875 -1.3125,0.59375l 0.0-1.203125q 0.71875-0.28125 1.328125-0.421875
+                        q 0.625-0.15625 1.140625-0.15625 1.3593752,0.0 2.1718752,0.6875
+                        q 0.8125,0.671875 0.8125,1.8125 0.0,0.53125 -0.203125,1.015625
+                        q -0.203125,0.484375 -0.734375,1.140625 -0.15625,0.171875 -0.9375,0.984375
+                        q -0.78125024,0.8125 -2.2187502,2.265625Z" />
+    </group>
+
+</vector>
\ No newline at end of file
diff --git a/tests/res/drawable/vector_icon_create.xml b/tests/res/drawable/vector_icon_create.xml
new file mode 100644
index 0000000..7db4ad5
--- /dev/null
+++ b/tests/res/drawable/vector_icon_create.xml
@@ -0,0 +1,29 @@
+<?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.
+ */
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:height="64dp"
+        android:width="64dp"
+        android:viewportHeight="24"
+        android:viewportWidth="24" >
+
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M3.0,17.25L3.0,21.0l3.75,0.0L17.813995,9.936001l-3.75-3.75L3.0,17.25zM20.707,7.0429993c0.391-0.391 0.391-1.023 0.0-1.414l-2.336-2.336c-0.391-0.391-1.023-0.391 -1.414,0.0l-1.832,1.832l3.75,3.75L20.707,7.0429993z" />
+
+</vector>
\ No newline at end of file
diff --git a/tests/res/drawable/vector_icon_delete.xml b/tests/res/drawable/vector_icon_delete.xml
new file mode 100644
index 0000000..8d9c21c
--- /dev/null
+++ b/tests/res/drawable/vector_icon_delete.xml
@@ -0,0 +1,29 @@
+<?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.
+ */
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:height="64dp"
+        android:width="64dp"
+        android:viewportHeight="24"
+        android:viewportWidth="24" >
+
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M6.0,19.0c0.0,1.104 0.896,2.0 2.0,2.0l8.0,0.0c1.104,0.0 2.0-0.896 2.0-2.0l0.0-12.0L6.0,7.0L6.0,19.0zM18.0,4.0l-2.5,0.0l-1.0-1.0l-5.0,0.0l-1.0,1.0L6.0,4.0C5.4469986,4.0 5.0,4.4469986 5.0,5.0l0.0,1.0l14.0,0.0l0.0-1.0C19.0,4.4469986 18.552002,4.0 18.0,4.0z" />
+
+</vector>
\ No newline at end of file
diff --git a/tests/res/drawable/vector_icon_heart.xml b/tests/res/drawable/vector_icon_heart.xml
new file mode 100644
index 0000000..ff55fe5
--- /dev/null
+++ b/tests/res/drawable/vector_icon_heart.xml
@@ -0,0 +1,29 @@
+<?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.
+ */
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:height="64dp"
+        android:width="64dp"
+        android:viewportHeight="24"
+        android:viewportWidth="24" >
+
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M16.0,5.0c-1.955,0.0 -3.83,1.268 -4.5,3.0c-0.67-1.732 -2.547-3.0 -4.5-3.0C4.4570007,5.0 2.5,6.931999 2.5,9.5c0.0,3.529 3.793,6.258 9.0,11.5c5.207-5.242 9.0-7.971 9.0-11.5C20.5,6.931999 18.543,5.0 16.0,5.0z" />
+
+</vector>
\ No newline at end of file
diff --git a/tests/res/drawable/vector_icon_random_path_1.xml b/tests/res/drawable/vector_icon_random_path_1.xml
new file mode 100644
index 0000000..5c55294
--- /dev/null
+++ b/tests/res/drawable/vector_icon_random_path_1.xml
@@ -0,0 +1,49 @@
+<!--
+ 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:height="64dp"
+        android:width="64dp"
+        android:viewportHeight="128"
+        android:viewportWidth="128" >
+
+    <path
+        android:fillColor="#FF00FF00"
+        android:pathData="
+                 m 0.0 0.0
+                 c 58.357853 57.648304 47.260395 2.2044754 3.0 3.0
+                 s 61.29288 10.748665 6.0 6.0
+                 s 0.12015152 45.193787 9.0 9.0
+                 s 32.573513 46.862522 12.0 12.0
+                 C 52.051823 62.050003 14.197739 51.99994 15.0 15.0
+                 S 58.365482 51.877937 18.0 18.0
+                 S 26.692455 3.9604378 21.0 21.0
+                 S 21.433464 52.17514 24.0 24.0
+                 M 27.0 27.0
+                 s 0.77630234 20.606667 30.0 30.0
+                 M 33.0 33.0
+                 S 31.06879 21.506374 36.0 36.0
+                 m 39.0 39.0
+                 s 11.699013 23.684185 42.0 42.0
+                 m 45.0 45.0
+                 S 3.7642136 38.589584 48.0 48.0
+                 Q 27.203026 53.329338 51.0 51.0
+                 s 39.229023 15.1781845 54.0 54.0
+                 Q 47.946877 23.706299 57.0 57.0
+                 S 45.63452 56.15198 60.0 60.0 "
+        android:strokeColor="#FF0000FF"
+        android:strokeWidth="1" />
+
+</vector>
\ No newline at end of file
diff --git a/tests/res/drawable/vector_icon_random_path_2.xml b/tests/res/drawable/vector_icon_random_path_2.xml
new file mode 100644
index 0000000..95e0a54
--- /dev/null
+++ b/tests/res/drawable/vector_icon_random_path_2.xml
@@ -0,0 +1,49 @@
+<!--
+ 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:height="64dp"
+        android:width="64dp"
+        android:viewportHeight="128"
+        android:viewportWidth="128" >
+
+    <path
+        android:fillColor="#FF00FF00"
+        android:pathData="
+                 m 0.0 0.0
+                 q 4.7088394 36.956432 3.0 3.0
+                 s 29.470345 16.754963 6.0 6.0
+                 q 20.278355 7.4670525 9.0 9.0
+                 S 30.897224 17.732414 12.0 12.0
+                 T 15.0 15.0
+                 s 63.47204 45.67142 18.0 18.0
+                 T 21.0 21.0
+                 S 0.3184204 24.808247 24.0 24.0
+                 t 27.0 27.0
+                 s 39.02275 38.261158 30.0 30.0
+                 t 33.0 33.0
+                 S 50.709816 16.067192 36.0 36.0
+                 a 62.50911 7.7131805 51.932335 0 0 39.0 39.0
+                 s 5.155651 15.749123 42.0 42.0
+                 a 51.87415 40.30564 49.804344 0 0 45.0 45.0
+                 S 16.16534 62.55986 48.0 48.0
+                 A 39.90161 43.904438 41.642593 1 0 51.0 51.0
+                 s 46.258068 32.12831 54.0 54.0
+                 A 22.962704 55.05604 42.912285 1 1 57.0 57.0
+                 S 36.47731 54.216763 60.0 60.0 "
+        android:strokeColor="#FF0000FF"
+        android:strokeWidth="1" />
+
+</vector>
\ No newline at end of file
diff --git a/tests/res/drawable/vector_icon_render_order_1.xml b/tests/res/drawable/vector_icon_render_order_1.xml
new file mode 100644
index 0000000..8639096
--- /dev/null
+++ b/tests/res/drawable/vector_icon_render_order_1.xml
@@ -0,0 +1,89 @@
+<!--
+ 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:height="64dp"
+        android:width="64dp"
+        android:viewportHeight="400"
+        android:viewportWidth="400" >
+
+    <group
+        android:name="FirstLevelGroup"
+        android:alpha="0.9"
+        android:translateX="100.0"
+        android:translateY="0.0" >
+        <path
+            android:fillColor="#FFFF0000"
+            android:pathData="@string/rectangle200" />
+
+        <group
+            android:name="SecondLevelGroup1"
+            android:alpha="0.9"
+            android:translateX="-100.0"
+            android:translateY="50.0" >
+            <path
+                android:fillColor="#FF00FF00"
+                android:pathData="@string/rectangle200" />
+
+            <group
+                android:name="ThridLevelGroup1"
+                android:alpha="0.9"
+                android:translateX="-100.0"
+                android:translateY="50.0" >
+                <path
+                    android:fillColor="#FF0000FF"
+                    android:pathData="@string/rectangle200" />
+            </group>
+            <group
+                android:name="ThridLevelGroup2"
+                android:alpha="0.8"
+                android:translateX="100.0"
+                android:translateY="50.0" >
+                <path
+                    android:fillColor="#FF000000"
+                    android:pathData="@string/rectangle200" />
+            </group>
+        </group>
+        <group
+            android:name="SecondLevelGroup2"
+            android:alpha="0.8"
+            android:translateX="100.0"
+            android:translateY="50.0" >
+            <path
+                android:fillColor="#FF0000FF"
+                android:pathData="@string/rectangle200" />
+
+            <group
+                android:name="ThridLevelGroup3"
+                android:alpha="0.9"
+                android:translateX="-100.0"
+                android:translateY="50.0" >
+                <path
+                    android:fillColor="#FFFF0000"
+                    android:pathData="@string/rectangle200" />
+            </group>
+            <group
+                android:name="ThridLevelGroup4"
+                android:alpha="0.8"
+                android:translateX="100.0"
+                android:translateY="50.0" >
+                <path
+                    android:fillColor="#FF00FF00"
+                    android:pathData="@string/rectangle200" />
+            </group>
+        </group>
+    </group>
+
+</vector>
\ No newline at end of file
diff --git a/tests/res/drawable/vector_icon_render_order_2.xml b/tests/res/drawable/vector_icon_render_order_2.xml
new file mode 100644
index 0000000..069a531
--- /dev/null
+++ b/tests/res/drawable/vector_icon_render_order_2.xml
@@ -0,0 +1,89 @@
+<!--
+ 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:height="64dp"
+        android:width="64dp"
+        android:viewportHeight="400"
+        android:viewportWidth="400" >
+
+    <group
+        android:name="FirstLevelGroup"
+        android:alpha="0.9"
+        android:translateX="100.0"
+        android:translateY="0.0" >
+        <group
+            android:name="SecondLevelGroup1"
+            android:alpha="0.9"
+            android:translateX="-100.0"
+            android:translateY="50.0" >
+            <path
+                android:fillColor="#FF00FF00"
+                android:pathData="@string/rectangle200" />
+
+            <group
+                android:name="ThridLevelGroup1"
+                android:alpha="0.9"
+                android:translateX="-100.0"
+                android:translateY="50.0" >
+                <path
+                    android:fillColor="#FF0000FF"
+                    android:pathData="@string/rectangle200" />
+            </group>
+            <group
+                android:name="ThridLevelGroup2"
+                android:alpha="0.8"
+                android:translateX="100.0"
+                android:translateY="50.0" >
+                <path
+                    android:fillColor="#FF000000"
+                    android:pathData="@string/rectangle200" />
+            </group>
+        </group>
+        <group
+            android:name="SecondLevelGroup2"
+            android:alpha="0.8"
+            android:translateX="100.0"
+            android:translateY="50.0" >
+            <path
+                android:fillColor="#FF0000FF"
+                android:pathData="@string/rectangle200" />
+
+            <group
+                android:name="ThridLevelGroup3"
+                android:alpha="0.9"
+                android:translateX="-100.0"
+                android:translateY="50.0" >
+                <path
+                    android:fillColor="#FFFF0000"
+                    android:pathData="@string/rectangle200" />
+            </group>
+            <group
+                android:name="ThridLevelGroup4"
+                android:alpha="0.8"
+                android:translateX="100.0"
+                android:translateY="50.0" >
+                <path
+                    android:fillColor="#FF00FF00"
+                    android:pathData="@string/rectangle200" />
+            </group>
+        </group>
+
+        <path
+            android:fillColor="#FFFF0000"
+            android:pathData="@string/rectangle200" />
+    </group>
+
+</vector>
\ No newline at end of file
diff --git a/tests/res/drawable/vector_icon_repeated_a_1.xml b/tests/res/drawable/vector_icon_repeated_a_1.xml
new file mode 100644
index 0000000..e27464b
--- /dev/null
+++ b/tests/res/drawable/vector_icon_repeated_a_1.xml
@@ -0,0 +1,43 @@
+<!--
+ 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:height="64dp"
+        android:width="64dp"
+        android:viewportHeight="128"
+        android:viewportWidth="128" >
+
+    <path
+        android:fillColor="#FF00FF00"
+        android:pathData="m 45.712063 19.109837
+                H 24.509682
+                a 59.3415 26.877445 22.398209 1 1 3.3506432 1.6524277
+                a 34.922844 36.72583 13.569004 0 0 24.409462 20.931156
+                a 43.47134 32.61542 52.534607 1 0 7.187504 61.509724
+                A 30.621132 41.44202 50.885685 0 0 23.235489 26.638653
+                A 7.251148 15.767811 44.704533 1 1 19.989803 21.33052
+                A 55.645584 46.20288 19.40316 0 1 32.881298 53.410923
+                c 30.649612 4.8525085 21.96682 1.3304634 17.300182 14.747681
+                a 9.375069 44.365055 57.169727 0 0 56.01326 52.59596
+                A 50.071907 37.331825 56.301754 1 0 14.676102 62.04976
+                C 36.531925 4.6217957 47.59332 54.793385 13.562473 13.753647
+                A 2.3695297 42.578487 54.250687 0 1 33.1337 41.511288
+                a 39.4827 38.844944 54.52335 1 1 13.549484 46.81581
+                c 56.943657 51.96854 27.938824 61.148792 24.168636 46.642727
+                "
+        android:strokeColor="#FF0000FF"
+        android:strokeWidth="1" />
+
+</vector>
\ No newline at end of file
diff --git a/tests/res/drawable/vector_icon_repeated_a_2.xml b/tests/res/drawable/vector_icon_repeated_a_2.xml
new file mode 100644
index 0000000..924ba1b
--- /dev/null
+++ b/tests/res/drawable/vector_icon_repeated_a_2.xml
@@ -0,0 +1,45 @@
+<!--
+ 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:height="64dp"
+        android:width="64dp"
+        android:viewportHeight="128"
+        android:viewportWidth="128" >
+
+    <path
+        android:fillColor="#FF00FF00"
+        android:pathData="m 45.712063 19.109837
+                H 24.509682
+                A 37.689938 2.3916092 17.462616 1 0 24.958328 48.110596
+                q 45.248383 30.396336 5.777027 3.4086685
+                a 30.966236 62.67946 50.532032 1 0 29.213684 60.63014
+                L 56.16764 8.342098
+                Q 61.172253 1.4613304 4.4721107 38.287144
+                A 6.284897 22.991482 47.409508 1 1 44.10166 60.998764
+                t 36.36881 55.68292
+                a 51.938667 35.22107 22.272938 1 1 28.572739 60.848858
+                A 19.610851 11.569599 51.407906 1 1 56.82705 24.386292
+                T 36.918854 59.542286
+                a 33.191364 10.553429 53.047726 1 0 54.874985 7.409252
+                s 30.186714 42.154182 59.73551 35.50219
+                A 47.9379 5.776497 28.307701 1 1 3.3323975 30.113499
+                a 22.462494 28.096004 55.76455 0 0 25.58981 30.816948
+                S 43.91107 54.679676 19.540264 0.34284973
+                "
+        android:strokeColor="#FF0000FF"
+        android:strokeWidth="1" />
+
+</vector>
\ No newline at end of file
diff --git a/tests/res/drawable/vector_icon_repeated_cq.xml b/tests/res/drawable/vector_icon_repeated_cq.xml
new file mode 100644
index 0000000..e0848f0
--- /dev/null
+++ b/tests/res/drawable/vector_icon_repeated_cq.xml
@@ -0,0 +1,42 @@
+<!--
+ 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:height="64dp"
+        android:width="64dp"
+        android:viewportHeight="128"
+        android:viewportWidth="128" >
+
+    <path
+        android:fillColor="#FF00FF00"
+        android:pathData="m 30.81895 41.37989
+                v 31.00579
+                c 24.291603 52.03364 40.6086 24.840137 29.56704 6.5204926
+                45.133224 22.913471 33.052887 21.727486 33.369 61.60278
+                9.647232 22.098152 48.939598 47.470215 53.653687 62.32235
+                C 2.0560722 1.4615479 7.0928993 26.005287 40.137558 36.75628
+                11.246731 32.178127 59.367462 60.34823 57.254383 37.357815
+                47.75605 11.424667 3.3105545 51.886635 56.63027 17.12133
+                q 28.37534 32.85535 25.85654 33.57151
+                10.356537 51.850616 54.085087 35.653175
+                12.530029 52.87991 17.44696 11.780586
+                Q 2.585228 51.92801 60.000664 56.79912
+                54.18275 51.500694 9.375679 23.836113
+                60.35329 59.026245 31.058632 35.14934
+                "
+        android:strokeColor="#FF0000FF"
+        android:strokeWidth="1" />
+
+</vector>
\ No newline at end of file
diff --git a/tests/res/drawable/vector_icon_repeated_st.xml b/tests/res/drawable/vector_icon_repeated_st.xml
new file mode 100644
index 0000000..b104349
--- /dev/null
+++ b/tests/res/drawable/vector_icon_repeated_st.xml
@@ -0,0 +1,42 @@
+<!--
+ 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:height="64dp"
+        android:width="64dp"
+        android:viewportHeight="128"
+        android:viewportWidth="128" >
+
+    <path
+        android:fillColor="#FF00FF00"
+        android:pathData="m 20.20005 8.139153
+                h 10.053165
+                s 14.2943 49.612846 35.520653 54.904068
+                50.1405 17.044182 5.470337 40.180553
+                3.125019 34.221123 53.212563 32.862965
+                S 35.985264 35.74349 0.15337753 59.27337
+                2.2951508 44.56783 51.089413 29.829689
+                8.5599785 22.649555 4.3914986 28.139206
+                t 11.932453 44.041077
+                62.629326 7.40921
+                23.302986 54.116184
+                T 43.560753 63.370514
+                40.156204 17.60786
+                40.12051 60.803394
+                "
+        android:strokeColor="#FF0000FF"
+        android:strokeWidth="1" />
+
+</vector>
\ No newline at end of file
diff --git a/tests/res/drawable/vector_icon_schedule.xml b/tests/res/drawable/vector_icon_schedule.xml
new file mode 100644
index 0000000..64d19e8
--- /dev/null
+++ b/tests/res/drawable/vector_icon_schedule.xml
@@ -0,0 +1,32 @@
+<?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.
+ */
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:height="64dp"
+        android:width="64dp"
+        android:viewportHeight="24"
+        android:viewportWidth="24" >
+
+    <path
+        android:fillColor="#E6000000"
+        android:pathData="M11.994999,2.0C6.4679985,2.0 2.0,6.4780006 2.0,12.0s4.468,10.0 9.995,10.0S22.0,17.522 22.0,12.0S17.521,2.0 11.994999,2.0zM12.0,20.0c-4.42,0.0 -8.0-3.582-8.0-8.0s3.58-8.0 8.0-8.0s8.0,3.582 8.0,8.0S16.419998,20.0 12.0,20.0z" />
+    <path
+        android:fillColor="#E6000000"
+        android:pathData="M12.5,6.0l-1.5,0.0 0.0,7.0 5.3029995,3.1819992 0.75-1.249999-4.5529995-2.7320004z" />
+
+</vector>
\ No newline at end of file
diff --git a/tests/res/drawable/vector_icon_settings.xml b/tests/res/drawable/vector_icon_settings.xml
new file mode 100644
index 0000000..13d7f05
--- /dev/null
+++ b/tests/res/drawable/vector_icon_settings.xml
@@ -0,0 +1,29 @@
+<?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.
+ */
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:height="64dp"
+        android:width="64dp"
+        android:viewportHeight="24"
+        android:viewportWidth="24" >
+
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M19.429,12.975998c0.042-0.32 0.07-0.645 0.07-0.976s-0.029-0.655-0.07-0.976l2.113-1.654c0.188-0.151 0.243-0.422 0.118-0.639l-2.0-3.463c-0.125-0.217-0.386-0.304-0.612-0.218l-2.49,1.004c-0.516-0.396-1.081-0.731-1.69-0.984l-0.375-2.648C14.456,2.1829987 14.25,2.0 14.0,2.0l-4.0,0.0C9.75,2.0 9.544,2.1829987 9.506,2.422001L9.131,5.0699997C8.521,5.322998 7.957,5.6570015 7.44,6.054001L4.952,5.0509987C4.726,4.965 4.464,5.052002 4.34,5.269001l-2.0,3.463C2.2150002,8.947998 2.27,9.219002 2.4580002,9.369999l2.112,1.653C4.528,11.344002 4.5,11.668999 4.5,12.0s0.029,0.656 0.071,0.977L2.4580002,14.630001c-0.188,0.151-0.243,0.422-0.118,0.639l2.0,3.463c0.125,0.217 0.386,0.304 0.612,0.218l2.489-1.004c0.516,0.396 1.081,0.731 1.69,0.984l0.375,2.648C9.544,21.817001 9.75,22.0 10.0,22.0l4.0,0.0c0.25,0.0 0.456-0.183 0.494-0.422l0.375-2.648c0.609-0.253 1.174-0.588 1.689-0.984l2.49,1.004c0.226,0.086 0.487-0.001 0.612-0.218l2.0-3.463c0.125-0.217 0.07-0.487-0.118-0.639L19.429,12.975998zM12.0,16.0c-2.21,0.0-4.0-1.791-4.0-4.0c0.0-2.21 1.79-4.0 4.0-4.0c2.208,0.0 4.0,1.79 4.0,4.0C16.0,14.209 14.208,16.0 12.0,16.0z" />
+
+</vector>
\ No newline at end of file
diff --git a/tests/res/drawable/vector_icon_stroke_1.xml b/tests/res/drawable/vector_icon_stroke_1.xml
new file mode 100644
index 0000000..9b766c0
--- /dev/null
+++ b/tests/res/drawable/vector_icon_stroke_1.xml
@@ -0,0 +1,45 @@
+<!--
+ 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:height="64dp"
+    android:viewportHeight="200"
+    android:viewportWidth="200"
+    android:width="64dp" >
+
+    <group>
+        <path
+            android:name="background1"
+            android:fillColor="#FF000000"
+            android:pathData="M 0,0 l 100,0 l 0, 100 l -100, 0 z" />
+        <path
+            android:name="background2"
+            android:fillColor="#FF000000"
+            android:pathData="M 100,100 l 100,0 l 0, 100 l -100, 0 z" />
+    </group>
+    <group
+        android:translateX="50"
+        android:translateY="50" >
+        <path
+            android:name="twoLines"
+            android:pathData="M 100,20 l 0 80 l -30 -80"
+            android:strokeColor="#FF00FF00"
+            android:strokeLineCap="butt"
+            android:strokeLineJoin="miter"
+            android:strokeMiterLimit="6"
+            android:strokeWidth="20" />
+    </group>
+
+</vector>
\ No newline at end of file
diff --git a/tests/res/drawable/vector_icon_stroke_2.xml b/tests/res/drawable/vector_icon_stroke_2.xml
new file mode 100644
index 0000000..b0f0cee
--- /dev/null
+++ b/tests/res/drawable/vector_icon_stroke_2.xml
@@ -0,0 +1,45 @@
+<!--
+ 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:height="64dp"
+    android:viewportHeight="200"
+    android:viewportWidth="200"
+    android:width="64dp" >
+
+    <group>
+        <path
+            android:name="background1"
+            android:fillColor="#FF000000"
+            android:pathData="M 0,0 l 100,0 l 0, 100 l -100, 0 z" />
+        <path
+            android:name="background2"
+            android:fillColor="#FF000000"
+            android:pathData="M 100,100 l 100,0 l 0, 100 l -100, 0 z" />
+    </group>
+    <group
+        android:translateX="50"
+        android:translateY="50" >
+        <path
+            android:name="twoLines"
+            android:pathData="M 100,20 l 0 80 l -30 -80"
+            android:strokeColor="#FF00FF00"
+            android:strokeLineCap="round"
+            android:strokeLineJoin="round"
+            android:strokeMiterLimit="10"
+            android:strokeWidth="20" />
+    </group>
+
+</vector>
\ No newline at end of file
diff --git a/tests/res/drawable/vector_icon_stroke_3.xml b/tests/res/drawable/vector_icon_stroke_3.xml
new file mode 100644
index 0000000..cd7bb16
--- /dev/null
+++ b/tests/res/drawable/vector_icon_stroke_3.xml
@@ -0,0 +1,45 @@
+<!--
+ 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:height="64dp"
+    android:viewportHeight="200"
+    android:viewportWidth="200"
+    android:width="64dp" >
+
+    <group>
+        <path
+            android:name="background1"
+            android:fillColor="#FF000000"
+            android:pathData="M 0,0 l 100,0 l 0, 100 l -100, 0 z" />
+        <path
+            android:name="background2"
+            android:fillColor="#FF000000"
+            android:pathData="M 100,100 l 100,0 l 0, 100 l -100, 0 z" />
+    </group>
+    <group
+        android:translateX="50"
+        android:translateY="50" >
+        <path
+            android:name="twoLines"
+            android:pathData="M 100,20 l 0 80 l -30 -80"
+            android:strokeColor="#FF00FF00"
+            android:strokeLineCap="square"
+            android:strokeLineJoin="bevel"
+            android:strokeMiterLimit="10"
+            android:strokeWidth="20" />
+    </group>
+
+</vector>
\ No newline at end of file
diff --git a/tests/res/drawable/vector_icon_transformation_1.xml b/tests/res/drawable/vector_icon_transformation_1.xml
new file mode 100644
index 0000000..f6623d0
--- /dev/null
+++ b/tests/res/drawable/vector_icon_transformation_1.xml
@@ -0,0 +1,38 @@
+<!--
+ 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:height="64dp"
+        android:width="64dp"
+        android:viewportHeight="500"
+        android:viewportWidth="800" >
+
+    <group
+        android:pivotX="90"
+        android:pivotY="100"
+        android:rotation="20">
+        <path
+            android:name="pie2"
+            android:pathData="M200,350 l 50,-25
+           a25,12 -30 0,1 100,-50 l 50,-25
+           a25,25 -30 0,1 100,-50 l 50,-25
+           a25,37 -30 0,1 100,-50 l 50,-25
+           a25,50 -30 0,1 100,-50 l 50,-25"
+           android:fillColor="#00000000"
+            android:strokeColor="#FF00FF00"
+            android:strokeWidth="10" />
+    </group>
+
+</vector>
diff --git a/tests/res/drawable/vector_icon_transformation_2.xml b/tests/res/drawable/vector_icon_transformation_2.xml
new file mode 100644
index 0000000..87da0bb
--- /dev/null
+++ b/tests/res/drawable/vector_icon_transformation_2.xml
@@ -0,0 +1,48 @@
+<!--
+ 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:height="64dp"
+        android:width="64dp"
+        android:viewportHeight="200"
+        android:viewportWidth="200" >
+
+    <group>
+        <path
+            android:name="background1"
+            android:pathData="M 0,0 l 100,0 l 0, 100 l -100, 0 z"
+            android:fillColor="#FF000000"/>
+        <path
+            android:name="background2"
+            android:pathData="M 100,100 l 100,0 l 0, 100 l -100, 0 z"
+            android:fillColor="#FF000000"/>
+    </group>
+    <group
+        android:pivotX="100"
+        android:pivotY="100"
+        android:rotation="90"
+        android:scaleX="0.75"
+        android:scaleY="0.5"
+        android:translateX="0.0"
+        android:translateY="100.0">
+        <path
+            android:name="twoLines"
+            android:pathData="M 100,10 v 90 M 10,100 h 90"
+            android:fillColor="#00000000"
+            android:strokeColor="#FF00FF00"
+            android:strokeWidth="10" />
+    </group>
+
+</vector>
\ No newline at end of file
diff --git a/tests/res/drawable/vector_icon_transformation_3.xml b/tests/res/drawable/vector_icon_transformation_3.xml
new file mode 100644
index 0000000..fc30af3
--- /dev/null
+++ b/tests/res/drawable/vector_icon_transformation_3.xml
@@ -0,0 +1,48 @@
+<!--
+ 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:height="64dp"
+        android:width="64dp"
+        android:viewportHeight="200"
+        android:viewportWidth="200" >
+
+    <group>
+        <path
+            android:name="background1"
+            android:pathData="M 0,0 l 100,0 l 0, 100 l -100, 0 z"
+            android:fillColor="#FF000000"/>
+        <path
+            android:name="background2"
+            android:pathData="M 100,100 l 100,0 l 0, 100 l -100, 0 z"
+            android:fillColor="#FF000000"/>
+    </group>
+    <group
+        android:pivotX="0"
+        android:pivotY="0"
+        android:rotation="90"
+        android:scaleX="0.75"
+        android:scaleY="0.5"
+        android:translateX="100.0"
+        android:translateY="100.0">
+        <path
+            android:name="twoLines"
+            android:pathData="M 100,10 v 90 M 10,100 h 90"
+            android:fillColor="#00000000"
+            android:strokeColor="#FF00FF00"
+            android:strokeWidth="10" />
+    </group>
+
+</vector>
\ No newline at end of file
diff --git a/tests/res/drawable/vector_icon_transformation_4.xml b/tests/res/drawable/vector_icon_transformation_4.xml
new file mode 100644
index 0000000..5b40d0d
--- /dev/null
+++ b/tests/res/drawable/vector_icon_transformation_4.xml
@@ -0,0 +1,68 @@
+<!--
+ 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:height="64dp"
+        android:width="64dp"
+        android:viewportHeight="400"
+        android:viewportWidth="400" >
+
+    <group android:name="backgroundGroup" >
+        <path
+            android:name="background1"
+            android:fillColor="#80000000"
+            android:pathData="M 0,0 l 200,0 l 0, 200 l -200, 0 z" />
+        <path
+            android:name="background2"
+            android:fillColor="#80000000"
+            android:pathData="M 200,200 l 200,0 l 0, 200 l -200, 0 z" />
+    </group>
+    <group
+        android:name="translateToCenterGroup"
+        android:translateX="50.0"
+        android:translateY="90.0" >
+        <path
+            android:name="twoLines"
+            android:pathData="M 0,0 v 100 M 0,0 h 100"
+            android:strokeColor="#FFFF0000"
+            android:strokeWidth="20" />
+
+        <group
+            android:name="rotationGroup"
+            android:pivotX="0.0"
+            android:pivotY="0.0"
+            android:rotation="-45.0" >
+            <path
+                android:name="twoLines1"
+                android:pathData="M 0,0 v 100 M 0,0 h 100"
+                android:strokeColor="#FF00FF00"
+                android:strokeWidth="20" />
+
+            <group
+                android:name="translateGroup"
+                android:translateX="130.0"
+                android:translateY="160.0" >
+                <group android:name="scaleGroup" >
+                    <path
+                        android:name="twoLines2"
+                        android:pathData="M 0,0 v 100 M 0,0 h 100"
+                        android:strokeColor="#FF0000FF"
+                        android:strokeWidth="20" />
+                </group>
+            </group>
+        </group>
+    </group>
+
+</vector>
\ No newline at end of file
diff --git a/tests/res/drawable/vector_icon_transformation_5.xml b/tests/res/drawable/vector_icon_transformation_5.xml
new file mode 100644
index 0000000..4a27754
--- /dev/null
+++ b/tests/res/drawable/vector_icon_transformation_5.xml
@@ -0,0 +1,81 @@
+<!--
+ 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:height="64dp"
+        android:width="64dp"
+        android:viewportHeight="400"
+        android:viewportWidth="400" >
+
+    <group android:name="backgroundGroup" >
+        <path
+            android:name="background1"
+            android:fillColor="#80000000"
+            android:pathData="M 0,0 l 200,0 l 0, 200 l -200, 0 z" />
+        <path
+            android:name="background2"
+            android:fillColor="#80000000"
+            android:pathData="M 200,200 l 200,0 l 0, 200 l -200, 0 z" />
+    </group>
+    <group
+        android:name="translateToCenterGroup"
+        android:translateX="50.0"
+        android:translateY="90.0" >
+        <path
+            android:name="twoLines"
+            android:pathData="M 0,0 v 150 M 0,0 h 150"
+            android:strokeColor="#FFFF0000"
+            android:strokeWidth="20" />
+
+        <group
+            android:name="rotationGroup"
+            android:pivotX="0.0"
+            android:pivotY="0.0"
+            android:rotation="-45.0" >
+            <path
+                android:name="twoLines1"
+                android:pathData="M 0,0 v 100 M 0,0 h 100"
+                android:strokeColor="#FF00FF00"
+                android:strokeWidth="20" />
+
+            <group
+                android:name="translateGroup"
+                android:translateX="130.0"
+                android:translateY="160.0" >
+                <group android:name="scaleGroup" >
+                    <path
+                        android:name="twoLines3"
+                        android:pathData="M 0,0 v 100 M 0,0 h 100"
+                        android:strokeColor="#FF0000FF"
+                        android:strokeWidth="20" />
+                </group>
+            </group>
+
+            <group
+                android:name="translateGroupHalf"
+                android:translateX="65.0"
+                android:translateY="80.0" >
+                <group android:name="scaleGroup" >
+                    <path
+                        android:name="twoLines2"
+                        android:pathData="M 0,0 v 100 M 0,0 h 100"
+                        android:strokeColor="#FF0000FF"
+                        android:strokeWidth="20" />
+                </group>
+            </group>
+        </group>
+    </group>
+
+</vector>
\ No newline at end of file
diff --git a/tests/res/drawable/vector_icon_transformation_6.xml b/tests/res/drawable/vector_icon_transformation_6.xml
new file mode 100644
index 0000000..5e60c4a
--- /dev/null
+++ b/tests/res/drawable/vector_icon_transformation_6.xml
@@ -0,0 +1,86 @@
+<!--
+ 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:height="64dp"
+        android:width="64dp"
+        android:viewportHeight="400"
+        android:viewportWidth="400" >
+
+    <group android:name="backgroundGroup"
+        android:alpha = "0.5" >
+        <path
+            android:name="background1"
+            android:fillColor="#FF000000"
+            android:pathData="M 0,0 l 200,0 l 0, 200 l -200, 0 z" />
+        <path
+            android:name="background2"
+            android:fillColor="#FF000000"
+            android:pathData="M 200,200 l 200,0 l 0, 200 l -200, 0 z" />
+    </group>
+    <group
+        android:name="translateToCenterGroup"
+        android:translateX="50.0"
+        android:translateY="90.0"
+        android:alpha = "0.5" >
+        <path
+            android:name="twoLines"
+            android:pathData="M 0,0 v 100 M 0,0 h 100"
+            android:strokeColor="#FFFF0000"
+            android:strokeWidth="20" />
+
+        <group
+            android:name="rotationGroup"
+            android:pivotX="0.0"
+            android:pivotY="0.0"
+            android:rotation="-45.0"
+            android:alpha = "0.5" >
+            <path
+                android:name="twoLines1"
+                android:pathData="M 0,0 v 100 M 0,0 h 100"
+                android:strokeColor="#FF00FF00"
+                android:strokeWidth="20" />
+
+            <group
+                android:name="translateGroup"
+                android:translateX="130.0"
+                android:translateY="160.0"
+                android:alpha = "0.5">
+                <group android:name="scaleGroup" >
+                    <path
+                        android:name="twoLines3"
+                        android:pathData="M 0,0 v 100 M 0,0 h 100"
+                        android:strokeColor="#FF0000FF"
+                        android:strokeWidth="20" />
+                </group>
+            </group>
+
+            <group
+                android:name="translateGroupHalf"
+                android:translateX="65.0"
+                android:translateY="80.0"
+                android:alpha = "0.5">
+                <group android:name="scaleGroup" >
+                    <path
+                        android:name="twoLines2"
+                        android:pathData="M 0,0 v 100 M 0,0 h 100"
+                        android:strokeColor="#FF0000FF"
+                        android:strokeWidth="20" />
+                </group>
+            </group>
+        </group>
+    </group>
+
+</vector>
\ No newline at end of file
diff --git a/tests/res/layout/framelayout_layout.xml b/tests/res/layout/framelayout_layout.xml
index c6d1a83..78b7b47 100644
--- a/tests/res/layout/framelayout_layout.xml
+++ b/tests/res/layout/framelayout_layout.xml
@@ -48,4 +48,11 @@
 
     </FrameLayout>
 
+    <FrameLayout
+        android:id="@+id/foreground_tint"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:foregroundTint="@android:color/white"
+        android:foregroundTintMode="src_over" />
+
 </LinearLayout>
diff --git a/tests/res/layout/imageview_layout.xml b/tests/res/layout/imageview_layout.xml
index d1e17fa..e56a9c9 100644
--- a/tests/res/layout/imageview_layout.xml
+++ b/tests/res/layout/imageview_layout.xml
@@ -26,5 +26,13 @@
         android:id="@+id/imageview"
         android:layout_width="320px"
         android:layout_height="240px"/>
+
+    <ImageView
+        android:id="@+id/image_tint"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:tint="@android:color/white"
+        android:tintMode="src_over" />
+
 </LinearLayout>
 
diff --git a/tests/res/layout/inflater_layout_tags.xml b/tests/res/layout/inflater_layout_tags.xml
new file mode 100644
index 0000000..dc3eb29
--- /dev/null
+++ b/tests/res/layout/inflater_layout_tags.xml
@@ -0,0 +1,39 @@
+<?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:id="@+id/viewlayout_root"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical" >
+
+    <tag
+        android:id="@+id/tag_viewlayout_root"
+        android:value="@string/tag1" />
+
+    <View
+        android:id="@+id/mock_view"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content" >
+
+        <tag
+            android:id="@+id/tag_mock_view"
+            android:value="@string/tag2" />
+    </View>
+
+</LinearLayout>
\ No newline at end of file
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..2d2a578
--- /dev/null
+++ b/tests/res/layout/inflater_override_theme_layout.xml
@@ -0,0 +1,46 @@
+<?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>
diff --git a/tests/res/layout/multi_view.xml b/tests/res/layout/multi_view.xml
new file mode 100644
index 0000000..4f335d3
--- /dev/null
+++ b/tests/res/layout/multi_view.xml
@@ -0,0 +1,42 @@
+<?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">
+
+    <TextureView
+        android:id="@+id/texture_view_1"
+        android:layout_width="160dp"
+        android:layout_height="120dp"/>
+
+    <TextureView
+        android:id="@+id/texture_view_2"
+        android:layout_width="160dp"
+        android:layout_height="120dp"/>
+    <SurfaceView
+        android:id="@+id/surface_view_1"
+        android:layout_width="160dp"
+        android:layout_height="120dp"/>
+
+    <SurfaceView
+        android:id="@+id/surface_view_2"
+        android:layout_width="160dp"
+        android:layout_height="120dp"/>
+
+</LinearLayout>
diff --git a/tests/res/layout/progressbar_layout.xml b/tests/res/layout/progressbar_layout.xml
new file mode 100644
index 0000000..a1786b8
--- /dev/null
+++ b/tests/res/layout/progressbar_layout.xml
@@ -0,0 +1,41 @@
+<?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:layout_width="match_parent"
+    android:layout_height="match_parent">
+
+    <ProgressBar
+        android:id="@+id/progress_tint"
+        android:progressTint="@android:color/white"
+        android:progressTintMode="src_over"
+        android:progressBackgroundTint="@android:color/white"
+        android:progressBackgroundTintMode="src_over"
+        android:secondaryProgressTint="@android:color/white"
+        android:secondaryProgressTintMode="src_over"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        style="@android:style/Widget.ProgressBar.Horizontal" />
+
+    <ProgressBar
+        android:id="@+id/indeterminate_tint"
+        android:indeterminateTint="@android:color/white"
+        android:indeterminateTintMode="src_over"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        style="@android:style/Widget.ProgressBar.Large" />
+
+</LinearLayout>
diff --git a/tests/res/layout/seekbar.xml b/tests/res/layout/seekbar.xml
deleted file mode 100644
index c6f5246..0000000
--- a/tests/res/layout/seekbar.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent">
-
-     <SeekBar android:id="@+id/seekBar"
-         android:layout_width="match_parent"
-         android:layout_height="wrap_content"
-         android:max="100"
-         android:progress="50"
-         android:secondaryProgress="75" />
-
-</LinearLayout>
diff --git a/tests/res/layout/seekbar_layout.xml b/tests/res/layout/seekbar_layout.xml
new file mode 100644
index 0000000..5c311fd
--- /dev/null
+++ b/tests/res/layout/seekbar_layout.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+
+     <SeekBar android:id="@+id/seekBar"
+         android:layout_width="match_parent"
+         android:layout_height="wrap_content"
+         android:max="100"
+         android:progress="50"
+         android:secondaryProgress="75" />
+
+    <SeekBar
+        android:id="@+id/thumb_tint"
+        android:thumbTint="@android:color/white"
+        android:thumbTintMode="src_over"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content" />
+
+</LinearLayout>
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/layout/togglebutton_layout.xml b/tests/res/layout/togglebutton_layout.xml
index bb8a07d..a6c08e1 100644
--- a/tests/res/layout/togglebutton_layout.xml
+++ b/tests/res/layout/togglebutton_layout.xml
@@ -33,6 +33,12 @@
             android:layout_width="wrap_content"
             android:layout_height="wrap_content" />
 
+        <ToggleButton android:id="@+id/button_tint"
+            android:buttonTint="@android:color/white"
+            android:buttonTintMode="src_over"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content" />
+
     </LinearLayout>
 
 </ScrollView>
diff --git a/tests/res/layout/view_layout.xml b/tests/res/layout/view_layout.xml
index 40974f0..fa817dc 100644
--- a/tests/res/layout/view_layout.xml
+++ b/tests/res/layout/view_layout.xml
@@ -93,4 +93,11 @@
             android:paddingEnd="8px"
             android:background="@drawable/no_padding" />
 
+    <View
+        android:id="@+id/background_tint"
+        android:backgroundTint="@android:color/white"
+        android:backgroundTintMode="src_over"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content" />
+
 </LinearLayout>
diff --git a/tests/res/raw/testmp3_2.mp3 b/tests/res/raw/testmp3_2.mp3
new file mode 100644
index 0000000..6a70c69
--- /dev/null
+++ b/tests/res/raw/testmp3_2.mp3
Binary files differ
diff --git a/tests/res/values/attrs.xml b/tests/res/values/attrs.xml
index 9793893..4c3d9db 100644
--- a/tests/res/values/attrs.xml
+++ b/tests/res/values/attrs.xml
@@ -125,4 +125,21 @@
         <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"/>
+
+    <!-- Drawable theming attributes -->
+    <attr name="themeBoolean" />
+    <attr name="themeColor" />
+    <attr name="themeFloat" />
+    <attr name="themeInteger" />
+    <attr name="themeDimension" />
+    <attr name="themeDrawable" />
+    <attr name="themeBitmap" />
+    <attr name="themeNinePatch" />
+    <attr name="themeGravity" />
+    <attr name="themeTileMode" />
+    <attr name="themeAngle" />
 </resources>
diff --git a/tests/res/values/strings.xml b/tests/res/values/strings.xml
index 0ab3dd6..2cd2284 100644
--- a/tests/res/values/strings.xml
+++ b/tests/res/values/strings.xml
@@ -152,6 +152,8 @@
    <string name="version_v3">base</string>
    <string name="authenticator_label">Android CTS</string>
    <string name="search_label">Android CTS</string>
+   <string name="tag1">tag 1</string>
+   <string name="tag2">tag 2</string>
 
    <string name="button">Button</string>
    <string name="holo_test">Holo Test</string>
@@ -167,4 +169,5 @@
 New devices have a much larger screen which actually enables long strings to be displayed
 with no fading. I have made this string longer to fix this case. If you are correcting this
 text, I would love to see the kind of devices you guys now use! Guys, maybe some devices need longer string! </string>
+    <string name="rectangle200">"M 0,0 l 200,0 l 0, 200 l -200, 0 z"</string>
 </resources>
diff --git a/tests/res/values/styles.xml b/tests/res/values/styles.xml
index e0110cc..20c80f8 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,6 +138,33 @@
         <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>
+    
+    <style name="Theme_ThemedDrawableTest">
+        <item name="themeBoolean">true</item>
+        <item name="themeColor">@android:color/black</item>
+        <item name="themeFloat">1.0</item>
+        <item name="themeAngle">45.0</item>
+        <item name="themeInteger">1</item>
+        <item name="themeDimension">1px</item>
+        <item name="themeDrawable">@drawable/icon_black</item>
+        <item name="themeBitmap">@drawable/icon_black</item>
+        <item name="themeNinePatch">@drawable/ninepatch_0</item>
+        <item name="themeGravity">48</item>
+        <item name="themeTileMode">2</item>
+    </style>
+
     <style name="Theme_NoSwipeDismiss">
         <item name="android:windowSwipeToDismiss">false</item>
     </style>
diff --git a/tests/sample/Android.mk b/tests/sample/Android.mk
index e1a9408..5e8f571 100755
--- a/tests/sample/Android.mk
+++ b/tests/sample/Android.mk
@@ -22,9 +22,6 @@
 # When built, explicitly put it in the data partition.
 LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
 
-# All tests should include android.test.runner.
-LOCAL_JAVA_LIBRARIES := android.test.runner
-
 LOCAL_STATIC_JAVA_LIBRARIES := ctsdeviceutil ctstestrunner
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
diff --git a/tests/sample/AndroidManifest.xml b/tests/sample/AndroidManifest.xml
index ae58f0a..f07ebbe 100755
--- a/tests/sample/AndroidManifest.xml
+++ b/tests/sample/AndroidManifest.xml
@@ -31,9 +31,12 @@
 
     <!--  self-instrumenting test package. -->
     <instrumentation
-        android:name="android.test.InstrumentationCtsTestRunner"
+        android:name="android.support.test.runner.AndroidJUnitRunner"
         android:label="CTS sample tests"
-        android:targetPackage="android.sample.cts" />
-
+        android:targetPackage="android.sample.cts" >
+        <meta-data
+            android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
 </manifest>
 
diff --git a/tests/src/android/hardware/camera2/cts/Camera2MultiViewStubActivity.java b/tests/src/android/hardware/camera2/cts/Camera2MultiViewStubActivity.java
new file mode 100644
index 0000000..28781d3
--- /dev/null
+++ b/tests/src/android/hardware/camera2/cts/Camera2MultiViewStubActivity.java
@@ -0,0 +1,59 @@
+/*
+ * 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.util.Log;
+import android.view.SurfaceView;
+import android.view.TextureView;
+import android.view.WindowManager;
+
+import com.android.cts.stub.R;
+
+public class Camera2MultiViewStubActivity extends Activity {
+    private final static String TAG = "Camera2MultiViewStubActivity";
+    private TextureView[] mTextureView = new TextureView[2];
+    private SurfaceView[] mSurfaceView = new SurfaceView[2];
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.multi_view);
+        mTextureView[0] = (TextureView) findViewById(R.id.texture_view_1);
+        mTextureView[1] = (TextureView) findViewById(R.id.texture_view_2);
+        mSurfaceView[0] = (SurfaceView) findViewById(R.id.surface_view_1);
+        mSurfaceView[1] = (SurfaceView) findViewById(R.id.surface_view_2);
+
+        //Make sure screen is on when this activity window is visible to the user.
+        getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
+    }
+
+    public TextureView getTextureView(int index) {
+        if (index < 0 || index > 1) {
+            throw new IllegalArgumentException("Texture view index must be 0 or 1");
+        }
+        return mTextureView[index];
+    }
+
+    public SurfaceView getSurfaceView(int index) {
+        if (index < 0 || index > 1) {
+            throw new IllegalArgumentException("Surface view index must be 0 or 1");
+        }
+        return mSurfaceView[index];
+    }
+}
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/hardware/camera2/cts/common.rs b/tests/src/android/hardware/camera2/cts/common.rs
new file mode 100644
index 0000000..4c134b3
--- /dev/null
+++ b/tests/src/android/hardware/camera2/cts/common.rs
@@ -0,0 +1,45 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_HARDWARE_CAMERA2_CTS_COMMON_RS
+#define ANDROID_HARDWARE_CAMERA2_CTS_COMMON_RS
+
+#pragma version(1)
+#pragma rs java_package_name(android.hardware.camera2.cts)
+#pragma rs_fp_relaxed
+
+typedef uchar3 yuvx_444; // interleaved YUV. (y,u,v) per pixel. use .x/.y/.z to read
+typedef uchar3 yuvf_420; // flexible YUV (4:2:0). use rsGetElementAtYuv to read.
+
+#define convert_yuvx_444 convert_uchar3
+#define convert_yuvf_420 __error_cant_output_flexible_yuv__
+
+#define rsGetElementAt_yuvx_444 rsGetElementAt_uchar3
+#define rsGetElementAt_yuvf_420 __error_cant_output_flexible_yuv__
+
+#define RS_KERNEL __attribute__((kernel))
+
+#ifndef LOG_DEBUG
+#define LOG_DEBUG 0
+#endif
+
+#if LOG_DEBUG
+#define LOGD(string, expr) rsDebug((string), (expr))
+#else
+#define LOGD(string, expr) if (0) rsDebug((string), (expr))
+#endif
+
+#endif // header guard
diff --git a/tests/src/android/hardware/camera2/cts/crop_yuvf_420_to_yuvx_444.rs b/tests/src/android/hardware/camera2/cts/crop_yuvf_420_to_yuvx_444.rs
new file mode 100644
index 0000000..f930f58
--- /dev/null
+++ b/tests/src/android/hardware/camera2/cts/crop_yuvf_420_to_yuvx_444.rs
@@ -0,0 +1,37 @@
+/*
+ * 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.
+ */
+
+#include "common.rs"
+
+// Must be YUV 420 888 (flexible YUV)
+rs_allocation mInput;
+
+// Input globals
+uint32_t src_x;
+uint32_t src_y;
+
+// Crop each pixel from mInput
+yuvx_444 RS_KERNEL crop(uint32_t x, uint32_t y) {
+
+    uchar py = rsGetElementAtYuv_uchar_Y(mInput, x + src_x, y + src_y);
+    uchar pu = rsGetElementAtYuv_uchar_U(mInput, x + src_x, y + src_y);
+    uchar pv = rsGetElementAtYuv_uchar_V(mInput, x + src_x, y + src_y);
+
+    yuvx_444 yuv = { py, pu, pv };
+
+    return yuv;
+}
+
diff --git a/tests/src/android/hardware/camera2/cts/means_yuvx_444_1d_to_single.rs b/tests/src/android/hardware/camera2/cts/means_yuvx_444_1d_to_single.rs
new file mode 100644
index 0000000..052052f
--- /dev/null
+++ b/tests/src/android/hardware/camera2/cts/means_yuvx_444_1d_to_single.rs
@@ -0,0 +1,54 @@
+/*
+ * 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.
+ */
+
+#include "common.rs"
+
+// #define LOG_DEBUG 1
+
+// Must be yuvx_444 (interleaved yuv)
+rs_allocation mInput;
+
+uint32_t width;
+float inv_width; // must be set to 1/w
+
+// Average 1D array -> Single element
+// Input: mInput must be yuvx_444
+yuvx_444 RS_KERNEL means_yuvx_444(void) {
+
+    uint3 sum  = { 0, 0, 0 };
+
+    LOGD("width", width);
+
+    for (uint32_t i = 0; i < width; ++i) {
+        yuvx_444 elem = rsGetElementAt_yuvx_444(mInput, i);
+
+        LOGD("elem", elem);
+        LOGD("i", i);
+
+        sum.x += elem.x;
+        sum.y += elem.y;
+        sum.z += elem.z;
+    }
+
+    sum.x *= inv_width; // multiply by 1/w
+    sum.y *= inv_width; // multiply by 1/w
+    sum.z *= inv_width; // multiply by 1/w
+
+    yuvx_444 avg = convert_yuvx_444(sum);
+
+    return avg;
+}
+
diff --git a/tests/src/android/hardware/camera2/cts/means_yuvx_444_2d_to_1d.rs b/tests/src/android/hardware/camera2/cts/means_yuvx_444_2d_to_1d.rs
new file mode 100644
index 0000000..7dc4e0d
--- /dev/null
+++ b/tests/src/android/hardware/camera2/cts/means_yuvx_444_2d_to_1d.rs
@@ -0,0 +1,86 @@
+/*
+ * 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.
+ */
+
+#include "common.rs"
+
+// #define LOG_DEBUG 1
+
+// Must be YUV 420 888 (flexible YUV)
+rs_allocation mInput;
+
+uint32_t width;
+float inv_width; // 1/w
+uint32_t src_x; // x-offset from mInput
+uint32_t src_y; // y-offset from mInput
+
+// Average a 2D array -> 1D array (by each row)
+// Input: mInput must be yuvf_420
+yuvx_444 RS_KERNEL means_yuvf_420(uint32_t x) {
+
+    LOGD("x", x);
+
+    uint3 sum = { 0, 0, 0 };
+
+    for (uint32_t i = 0; i < width; ++i) {
+        uchar py = rsGetElementAtYuv_uchar_Y(mInput, src_x + i, src_y + x);
+        uchar pu = rsGetElementAtYuv_uchar_U(mInput, src_x + i, src_y + x);
+        uchar pv = rsGetElementAtYuv_uchar_V(mInput, src_x + i, src_y + x);
+
+        yuvx_444 elem = { py, pu, pv };
+
+        LOGD("elem", elem);
+
+        sum.x += elem.x;
+        sum.y += elem.y;
+        sum.z += elem.z;
+    }
+
+    sum.x *= inv_width; // multiply by 1/w
+    sum.y *= inv_width; // multiply by 1/w
+    sum.z *= inv_width; // multiply by 1/w
+
+    yuvx_444 avg = convert_yuvx_444(sum);
+
+    return avg;
+}
+
+// Average a 2D array -> 1D array (by each row)
+// Input: mInput must be yuvx_444
+yuvx_444 RS_KERNEL means_yuvx_444(uint32_t x) {
+
+    LOGD("x", x);
+
+    uint3 sum = { 0, 0, 0 };
+
+    for (uint32_t i = 0; i < width; ++i) {
+        yuvx_444 elem = rsGetElementAt_yuvx_444(mInput, src_x + i, src_y + x);
+
+        LOGD("elem", elem);
+
+        sum.x += elem.x;
+        sum.y += elem.y;
+        sum.z += elem.z;
+    }
+
+    sum.x *= inv_width; // multiply by 1/w
+    sum.y *= inv_width; // multiply by 1/w
+    sum.z *= inv_width; // multiply by 1/w
+
+    yuvx_444 avg = convert_yuvx_444(sum);
+
+    return avg;
+}
+
diff --git a/tests/src/android/opengl/cts/OpenGlEsVersionStubActivity.java b/tests/src/android/opengl/cts/OpenGlEsVersionStubActivity.java
index 488c8bd..2b96c5d 100644
--- a/tests/src/android/opengl/cts/OpenGlEsVersionStubActivity.java
+++ b/tests/src/android/opengl/cts/OpenGlEsVersionStubActivity.java
@@ -18,8 +18,11 @@
 
 import android.app.Activity;
 import android.content.Intent;
+import android.opengl.GLES31;
+import android.opengl.GLES31Ext;
 import android.opengl.GLSurfaceView;
 import android.os.Bundle;
+import android.util.Log;
 
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
@@ -32,6 +35,7 @@
  * OpenGL ES is supported and returns what the GL version string reports.
  */
 public class OpenGlEsVersionStubActivity extends Activity {
+    private static String TAG = "OpenGlEsVersionStubActivity";
 
     private static final String EGL_CONTEXT_CLIENT_VERSION = "eglContextClientVersion";
 
@@ -41,6 +45,12 @@
     /** Version string reported by glGetString. */
     private String mVersionString;
 
+    /** Extensions string reported by glGetString. */
+    private String mExtensionsString;
+
+    /** Whether GL_ANDROID_extension_pack_es31a is correctly supported. */
+    private boolean mAepEs31Support = false;
+
     /** Latch that is unlocked when the activity is done finding the version. */
     private CountDownLatch mSurfaceCreatedLatch = new CountDownLatch(1);
 
@@ -73,12 +83,108 @@
         }
     }
 
+    public String getExtensionsString() throws InterruptedException {
+        mSurfaceCreatedLatch.await(TIMEOUT_SECONDS, TimeUnit.SECONDS);
+        synchronized (this) {
+            return mExtensionsString;
+        }
+    }
+
+    public boolean getAepEs31Support() throws InterruptedException {
+        mSurfaceCreatedLatch.await(TIMEOUT_SECONDS, TimeUnit.SECONDS);
+        synchronized (this) {
+            return mAepEs31Support;
+        }
+    }
+
+    public static boolean hasExtension(String extensions, String name) {
+        int start = extensions.indexOf(name);
+        while (start >= 0) {
+            // check that we didn't find a prefix of a longer extension name
+            int end = start + name.length();
+            if (end == extensions.length() || extensions.charAt(end) == ' ') {
+                return true;
+            }
+            start = extensions.indexOf(name, end);
+        }
+        return false;
+    }
+
     private class Renderer implements GLSurfaceView.Renderer {
+        /**
+         * These shaders test at least one feature of each of the underlying extension, to verify
+         * that enabling GL_ANDROID_extension_pack_es31a correctly enables all of them.
+         */
+        private final String mAepEs31VertexShader =
+                "#version 310 es\n" +
+                "#extension GL_ANDROID_extension_pack_es31a : require\n" +
+                "void main() {\n" +
+                "  gl_Position = vec4(1, 0, 0, 1);\n" +
+                "}\n";
+
+        private final String mAepEs31TessellationControlShader =
+                "#version 310 es\n" +
+                "#extension GL_ANDROID_extension_pack_es31a : require\n" +
+                "layout(vertices = 3) out;\n" +  // GL_EXT_tessellation_shader
+                "void main() {\n" +
+                "  gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;\n" +  // GL_EXT_shader_io_blocks
+                "  if (gl_InvocationID == 0) {\n" +
+                "    gl_BoundingBoxEXT[0] = gl_in[0].gl_Position;\n" +  // GL_EXT_primitive_bounding_box
+                "    gl_BoundingBoxEXT[1] = gl_in[1].gl_Position;\n" +
+                "  }\n" +
+                "}\n";
+
+        private final String mAepEs31TessellationEvaluationShader =
+                "#version 310 es\n" +
+                "#extension GL_ANDROID_extension_pack_es31a : require\n" +
+                "layout(triangles, equal_spacing, cw) in;\n" +
+                "void main() {\n" +
+                "  gl_Position = gl_in[0].gl_Position * gl_TessCoord.x +\n" +
+                "      gl_in[1].gl_Position * gl_TessCoord.y +\n" +
+                "      gl_in[2].gl_Position * gl_TessCoord.z;\n" +
+                "}\n";
+
+        private final String mAepEs31GeometryShader =
+                "#version 310 es\n" +
+                "#extension GL_ANDROID_extension_pack_es31a : require\n" +
+                "layout(triangles) in;\n" +  // GL_EXT_geometry_shader
+                "layout(triangle_strip, max_vertices = 3) out;\n" +
+                "sample out vec4 perSampleColor;\n" +
+                "void main() {\n" +
+                "  for (int i = 0; i < gl_in.length(); ++i) {\n" +
+                "    gl_Position = gl_in[i].gl_Position;\n" +
+                "    perSampleColor = gl_in[i].gl_Position;\n" +
+                "    EmitVertex();\n" +
+                "  }\n" +
+                "}\n";
+
+        private final String mAepEs31FragmentShader =
+                "#version 310 es\n" +
+                "#extension GL_ANDROID_extension_pack_es31a : require\n" +
+                "precision mediump float;\n" +
+                "layout(blend_support_all_equations) out;\n" +  // GL_KHR_blend_equation_advanced
+                "sample in vec4 perSampleColor;\n" +  // GL_OES_shader_multisample_interpolation
+                "layout(r32ui) coherent uniform mediump uimage2D image;\n" +
+                "uniform mediump sampler2DMSArray mySamplerMSArray;\n" +  // GL_OES_texture_storage_multisample_2d_array
+                "uniform mediump samplerBuffer mySamplerBuffer;\n" +  // GL_EXT_texture_buffer
+                "uniform mediump samplerCubeArray mySamplerCubeArray;\n" +  // GL_EXT_texture_cube_map_array
+                "out vec4 color;\n" +
+                "void main() {\n" +
+                "  imageAtomicAdd(image, ivec2(1, 1), 1u);\n" +  // GL_OES_shader_image_atomic
+                "  vec4 color = vec4(gl_SamplePosition.x, 0, 0, 1);\n" +  // GL_OES_sample_variables
+                "  vec4 color2 = texelFetch(mySamplerMSArray, ivec3(1, 1, 1), 3);\n" +
+                "  vec4 color3 = texelFetch(mySamplerBuffer, 3);\n" +
+                "  vec4 color4 = texture(mySamplerCubeArray, vec4(1, 1, 1, 1));\n" +
+                "  color = fma(color + color2, color3 + color4, perSampleColor);" +  // GL_EXT_gpu_shader5
+                "}\n";
 
         public void onSurfaceCreated(GL10 gl, EGLConfig config) {
             synchronized (OpenGlEsVersionStubActivity.this) {
                 try {
                     mVersionString = gl.glGetString(GL10.GL_VERSION);
+                    mExtensionsString = gl.glGetString(GL10.GL_EXTENSIONS);
+                    if (hasExtension(mExtensionsString, "ANDROID_extension_pack_es31a"))
+                        mAepEs31Support = checkAepEs31Support();
                 } finally {
                     mSurfaceCreatedLatch.countDown();
                 }
@@ -90,5 +196,106 @@
 
         public void onDrawFrame(GL10 gl) {
         }
+
+        private boolean compileShaderAndAttach(int program, int shaderType, String source) {
+            int shader = GLES31.glCreateShader(shaderType);
+            if (shader == 0) {
+                Log.e(TAG, "Unable to create shaders of type " + shaderType);
+                return false;
+            }
+            GLES31.glShaderSource(shader, source);
+            GLES31.glCompileShader(shader);
+            int[] compiled = new int[1];
+            GLES31.glGetShaderiv(shader, GLES31.GL_COMPILE_STATUS, compiled, 0);
+            if (compiled[0] == 0) {
+                Log.e(TAG, "Unable to compile shader " + shaderType + ":");
+                Log.e(TAG, GLES31.glGetShaderInfoLog(shader));
+                GLES31.glDeleteShader(shader);
+                return false;
+            }
+            GLES31.glAttachShader(program, shader);
+            GLES31.glDeleteShader(shader);
+            return true;
+        }
+
+        private boolean checkAepEs31Support() {
+            final String requiredList[] = {
+                "EXT_copy_image",
+                "EXT_draw_buffers_indexed",
+                "EXT_geometry_shader",
+                "EXT_gpu_shader5",
+                "EXT_primitive_bounding_box",
+                "EXT_shader_io_blocks",
+                "EXT_tessellation_shader",
+                "EXT_texture_border_clamp",
+                "EXT_texture_buffer",
+                "EXT_texture_cube_map_array",
+                "EXT_texture_sRGB_decode",
+                "KHR_blend_equation_advanced",
+                "KHR_debug",
+                "KHR_texture_compression_astc_ldr",
+                "OES_sample_shading",
+                "OES_sample_variables",
+                "OES_shader_image_atomic",
+                "OES_shader_multisample_interpolation",
+                "OES_texture_stencil8",
+                "OES_texture_storage_multisample_2d_array"
+            };
+
+            for (int i = 0; i < requiredList.length; ++i) {
+                if (!hasExtension(mExtensionsString, requiredList[i])) {
+                    Log.e(TAG,"ANDROID_extension_pack_es31a is present but extension " +
+                            requiredList[i] + " is missing");
+                    return false;
+                }
+            }
+
+            int[] value = new int[1];
+            GLES31.glGetIntegerv(GLES31.GL_MAX_FRAGMENT_ATOMIC_COUNTER_BUFFERS, value, 0);
+            if (value[0] < 1) {
+                Log.e(TAG, "ANDROID_extension_pack_es31a is present, but the " +
+                        "GL_MAX_FRAGMENT_ATOMIC_COUNTER_BUFFERS value is " + value[0] + " < 1");
+                return false;
+            }
+            GLES31.glGetIntegerv(GLES31.GL_MAX_FRAGMENT_ATOMIC_COUNTERS, value, 0);
+            if (value[0] < 8) {
+                Log.e(TAG, "ANDROID_extension_pack_es31a is present, but the " +
+                        "GL_MAX_FRAGMENT_ATOMIC_COUNTERS value is " + value[0] + " < 8");
+                return false;
+            }
+            GLES31.glGetIntegerv(GLES31.GL_MAX_FRAGMENT_IMAGE_UNIFORMS, value, 0);
+            if (value[0] < 4) {
+                Log.e(TAG, "ANDROID_extension_pack_es31a is present, but the " +
+                        "GL_MAX_FRAGMENT_IMAGE_UNIFORMS value is " + value[0] + " < 4");
+                return false;
+            }
+            GLES31.glGetIntegerv(GLES31.GL_MAX_FRAGMENT_SHADER_STORAGE_BLOCKS, value, 0);
+            if (value[0] < 4) {
+                Log.e(TAG, "ANDROID_extension_pack_es31a is present, but the " +
+                        "GL_MAX_FRAGMENT_SHADER_STORAGE_BLOCKS value is " + value[0] + " < 4");
+                return false;
+            }
+
+            int program = GLES31.glCreateProgram();
+            try {
+                if (!compileShaderAndAttach(program, GLES31.GL_VERTEX_SHADER, mAepEs31VertexShader) ||
+                    !compileShaderAndAttach(program, GLES31Ext.GL_TESS_CONTROL_SHADER_EXT, mAepEs31TessellationControlShader) ||
+                    !compileShaderAndAttach(program, GLES31Ext.GL_TESS_EVALUATION_SHADER_EXT, mAepEs31TessellationEvaluationShader) ||
+                    !compileShaderAndAttach(program, GLES31Ext.GL_GEOMETRY_SHADER_EXT, mAepEs31GeometryShader) ||
+                    !compileShaderAndAttach(program, GLES31.GL_FRAGMENT_SHADER, mAepEs31FragmentShader))
+                    return false;
+
+                GLES31.glLinkProgram(program);
+                GLES31.glGetProgramiv(program, GLES31.GL_LINK_STATUS, value, 0);
+                if (value[0] == 0) {
+                    Log.e(TAG, "Unable to link program :");
+                    Log.e(TAG, GLES31.glGetProgramInfoLog(program));
+                    return false;
+                }
+            } finally {
+                GLES31.glDeleteProgram(program);
+            }
+            return true;
+        }
     }
 }
diff --git a/tests/src/android/os/cts/CpuFeatures.java b/tests/src/android/os/cts/CpuFeatures.java
index b767da2..fa6628b 100644
--- a/tests/src/android/os/cts/CpuFeatures.java
+++ b/tests/src/android/os/cts/CpuFeatures.java
@@ -16,6 +16,8 @@
 
 package android.os.cts;
 
+import android.os.Build;
+
 public class CpuFeatures {
 
     public static final String ARMEABI_V7 = "armeabi-v7a";
@@ -26,6 +28,18 @@
 
     public static final  String X86ABI = "x86";
 
+    public static final int HWCAP_VFP = (1 << 6);
+
+    public static final int HWCAP_NEON = (1 << 12);
+
+    public static final int HWCAP_VFPv3 = (1 << 13);
+
+    public static final int HWCAP_VFPv4 = (1 << 16);
+
+    public static final int HWCAP_IDIVA = (1 << 17);
+
+    public static final int HWCAP_IDIVT = (1 << 18);
+
     static {
         System.loadLibrary("cts_jni");
     }
@@ -43,4 +57,20 @@
     public static native boolean isMips64Cpu();
 
     public static native boolean isX86_64Cpu();
+
+    public static native int getHwCaps();
+
+    public static boolean isArm64CpuIn32BitMode() {
+        if (!isArmCpu()) {
+            return false;
+        }
+
+        for (String abi : Build.SUPPORTED_64_BIT_ABIS) {
+            if (abi.equals("arm64-v8a")) {
+                return true;
+            }
+        }
+
+        return false;
+    }
 }
diff --git a/tests/src/android/os/cts/OSFeatures.java b/tests/src/android/os/cts/OSFeatures.java
index fd30f58..59556db 100644
--- a/tests/src/android/os/cts/OSFeatures.java
+++ b/tests/src/android/os/cts/OSFeatures.java
@@ -23,4 +23,6 @@
 
     public static native int getNoNewPrivs();
     public static native int prctlCapBsetRead(int i);
+    public static native boolean hasSeccompSupport();
+    public static native boolean needsSeccompSupport();
 }
diff --git a/tests/src/android/renderscript/cts/AtomicTest.rs b/tests/src/android/renderscript/cts/AtomicTest.rs
new file mode 100644
index 0000000..0aee2bd
--- /dev/null
+++ b/tests/src/android/renderscript/cts/AtomicTest.rs
@@ -0,0 +1,126 @@
+/*
+ * 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.
+ */
+
+#include "shared.rsh"
+#include "rs_atomic.rsh"
+
+volatile int32_t gISum;
+volatile uint32_t gUSum;
+
+
+void __attribute__((kernel)) test_Inc(int32_t v) {
+    rsAtomicInc(&gISum);
+}
+void __attribute__((kernel)) test_uInc(uint32_t v) {
+    rsAtomicInc(&gUSum);
+}
+
+void __attribute__((kernel)) test_Dec(int32_t v) {
+    rsAtomicDec(&gISum);
+}
+void __attribute__((kernel)) test_uDec(uint32_t v) {
+    rsAtomicDec(&gUSum);
+}
+
+#define TEST_OP(op)                                     \
+void __attribute__((kernel)) test_##op(int32_t v) {     \
+    rsAtomic##op(&gISum, v);                            \
+}                                                       \
+void __attribute__((kernel)) test_u##op(uint32_t v) {   \
+    rsAtomic##op(&gUSum, v);                            \
+}
+
+TEST_OP(Add)
+TEST_OP(Sub)
+TEST_OP(And)
+TEST_OP(Or)
+TEST_OP(Xor)
+TEST_OP(Min)
+TEST_OP(Max)
+
+// the folowing functions copy the global to an allocation
+// to allow the calling code to read it back.
+void getValueS(rs_allocation v) {
+    rsSetElementAt_int(v, gISum, 0);
+}
+
+void getValueU(rs_allocation v) {
+    rsSetElementAt_uint(v, gUSum, 0);
+}
+
+void computeReference_Min(rs_allocation a, rs_allocation result) {
+    uint32_t dimX = rsAllocationGetDimX(a);
+    uint32_t dimY = rsAllocationGetDimY(a);
+    for (uint32_t y = 0; y < dimY; y++) {
+        for (uint32_t x = 0; x < dimX; x++) {
+            int v = rsGetElementAt_int(a, x, y);
+            gISum = min(gISum, v);
+        }
+    }
+    rsSetElementAt_int(result, gISum, 0);
+}
+
+void computeReference_uMin(rs_allocation a, rs_allocation result) {
+    uint32_t dimX = rsAllocationGetDimX(a);
+    uint32_t dimY = rsAllocationGetDimY(a);
+    for (uint32_t y = 0; y < dimY; y++) {
+        for (uint32_t x = 0; x < dimX; x++) {
+            uint v = rsGetElementAt_uint(a, x, y);
+            gUSum = min(gUSum, v);
+        }
+    }
+    rsSetElementAt_uint(result, gUSum, 0);
+}
+
+void computeReference_Max(rs_allocation a, rs_allocation result) {
+    uint32_t dimX = rsAllocationGetDimX(a);
+    uint32_t dimY = rsAllocationGetDimY(a);
+    for (uint32_t y = 0; y < dimY; y++) {
+        for (uint32_t x = 0; x < dimX; x++) {
+            int v = rsGetElementAt_int(a, x, y);
+            gISum = max(gISum, v);
+        }
+    }
+    rsSetElementAt_int(result, gISum, 0);
+}
+
+void computeReference_uMax(rs_allocation a, rs_allocation result) {
+    uint32_t dimX = rsAllocationGetDimX(a);
+    uint32_t dimY = rsAllocationGetDimY(a);
+    for (uint32_t y = 0; y < dimY; y++) {
+        for (uint32_t x = 0; x < dimX; x++) {
+            uint v = rsGetElementAt_uint(a, x, y);
+            gUSum = max(gUSum, v);
+        }
+    }
+    rsSetElementAt_uint(result, gUSum, 0);
+}
+
+
+void __attribute__((kernel)) test_Cas(int32_t v) {
+    int tmp = gISum;
+    while (rsAtomicCas(&gISum, tmp, tmp + 1) != tmp) {
+        tmp = gISum;
+    }
+}
+void __attribute__((kernel)) test_uCas(uint32_t v) {
+    uint tmp = gUSum;
+    while (rsAtomicCas(&gUSum, tmp, tmp + 1) != tmp) {
+        tmp = gUSum;
+    }
+}
+
+
diff --git a/tests/src/android/renderscript/cts/TestClamp.rs b/tests/src/android/renderscript/cts/TestClamp.rs
index bc0b379..9bb5c87 100644
--- a/tests/src/android/renderscript/cts/TestClamp.rs
+++ b/tests/src/android/renderscript/cts/TestClamp.rs
@@ -208,6 +208,54 @@
     return clamp(inValue, inMinValue, inMaxValue);
 }
 
+long __attribute__((kernel)) testClampLongLongLongLong(long inValue, unsigned int x) {
+    long inMinValue = rsGetElementAt_long(gAllocInMinValue, x);
+    long inMaxValue = rsGetElementAt_long(gAllocInMaxValue, x);
+    return clamp(inValue, inMinValue, inMaxValue);
+}
+
+long2 __attribute__((kernel)) testClampLong2Long2Long2Long2(long2 inValue, unsigned int x) {
+    long2 inMinValue = rsGetElementAt_long2(gAllocInMinValue, x);
+    long2 inMaxValue = rsGetElementAt_long2(gAllocInMaxValue, x);
+    return clamp(inValue, inMinValue, inMaxValue);
+}
+
+long3 __attribute__((kernel)) testClampLong3Long3Long3Long3(long3 inValue, unsigned int x) {
+    long3 inMinValue = rsGetElementAt_long3(gAllocInMinValue, x);
+    long3 inMaxValue = rsGetElementAt_long3(gAllocInMaxValue, x);
+    return clamp(inValue, inMinValue, inMaxValue);
+}
+
+long4 __attribute__((kernel)) testClampLong4Long4Long4Long4(long4 inValue, unsigned int x) {
+    long4 inMinValue = rsGetElementAt_long4(gAllocInMinValue, x);
+    long4 inMaxValue = rsGetElementAt_long4(gAllocInMaxValue, x);
+    return clamp(inValue, inMinValue, inMaxValue);
+}
+
+ulong __attribute__((kernel)) testClampUlongUlongUlongUlong(ulong inValue, unsigned int x) {
+    ulong inMinValue = rsGetElementAt_ulong(gAllocInMinValue, x);
+    ulong inMaxValue = rsGetElementAt_ulong(gAllocInMaxValue, x);
+    return clamp(inValue, inMinValue, inMaxValue);
+}
+
+ulong2 __attribute__((kernel)) testClampUlong2Ulong2Ulong2Ulong2(ulong2 inValue, unsigned int x) {
+    ulong2 inMinValue = rsGetElementAt_ulong2(gAllocInMinValue, x);
+    ulong2 inMaxValue = rsGetElementAt_ulong2(gAllocInMaxValue, x);
+    return clamp(inValue, inMinValue, inMaxValue);
+}
+
+ulong3 __attribute__((kernel)) testClampUlong3Ulong3Ulong3Ulong3(ulong3 inValue, unsigned int x) {
+    ulong3 inMinValue = rsGetElementAt_ulong3(gAllocInMinValue, x);
+    ulong3 inMaxValue = rsGetElementAt_ulong3(gAllocInMaxValue, x);
+    return clamp(inValue, inMinValue, inMaxValue);
+}
+
+ulong4 __attribute__((kernel)) testClampUlong4Ulong4Ulong4Ulong4(ulong4 inValue, unsigned int x) {
+    ulong4 inMinValue = rsGetElementAt_ulong4(gAllocInMinValue, x);
+    ulong4 inMaxValue = rsGetElementAt_ulong4(gAllocInMaxValue, x);
+    return clamp(inValue, inMinValue, inMaxValue);
+}
+
 char2 __attribute__((kernel)) testClampChar2CharCharChar2(char2 inValue, unsigned int x) {
     char inMinValue = rsGetElementAt_char(gAllocInMinValue, x);
     char inMaxValue = rsGetElementAt_char(gAllocInMaxValue, x);
@@ -315,3 +363,39 @@
     uint inMaxValue = rsGetElementAt_uint(gAllocInMaxValue, x);
     return clamp(inValue, inMinValue, inMaxValue);
 }
+
+long2 __attribute__((kernel)) testClampLong2LongLongLong2(long2 inValue, unsigned int x) {
+    long inMinValue = rsGetElementAt_long(gAllocInMinValue, x);
+    long inMaxValue = rsGetElementAt_long(gAllocInMaxValue, x);
+    return clamp(inValue, inMinValue, inMaxValue);
+}
+
+long3 __attribute__((kernel)) testClampLong3LongLongLong3(long3 inValue, unsigned int x) {
+    long inMinValue = rsGetElementAt_long(gAllocInMinValue, x);
+    long inMaxValue = rsGetElementAt_long(gAllocInMaxValue, x);
+    return clamp(inValue, inMinValue, inMaxValue);
+}
+
+long4 __attribute__((kernel)) testClampLong4LongLongLong4(long4 inValue, unsigned int x) {
+    long inMinValue = rsGetElementAt_long(gAllocInMinValue, x);
+    long inMaxValue = rsGetElementAt_long(gAllocInMaxValue, x);
+    return clamp(inValue, inMinValue, inMaxValue);
+}
+
+ulong2 __attribute__((kernel)) testClampUlong2UlongUlongUlong2(ulong2 inValue, unsigned int x) {
+    ulong inMinValue = rsGetElementAt_ulong(gAllocInMinValue, x);
+    ulong inMaxValue = rsGetElementAt_ulong(gAllocInMaxValue, x);
+    return clamp(inValue, inMinValue, inMaxValue);
+}
+
+ulong3 __attribute__((kernel)) testClampUlong3UlongUlongUlong3(ulong3 inValue, unsigned int x) {
+    ulong inMinValue = rsGetElementAt_ulong(gAllocInMinValue, x);
+    ulong inMaxValue = rsGetElementAt_ulong(gAllocInMaxValue, x);
+    return clamp(inValue, inMinValue, inMaxValue);
+}
+
+ulong4 __attribute__((kernel)) testClampUlong4UlongUlongUlong4(ulong4 inValue, unsigned int x) {
+    ulong inMinValue = rsGetElementAt_ulong(gAllocInMinValue, x);
+    ulong inMaxValue = rsGetElementAt_ulong(gAllocInMaxValue, x);
+    return clamp(inValue, inMinValue, inMaxValue);
+}
diff --git a/tests/src/android/renderscript/cts/TestConvert.rs b/tests/src/android/renderscript/cts/TestConvert.rs
index 85f8c94..7c94d5e 100644
--- a/tests/src/android/renderscript/cts/TestConvert.rs
+++ b/tests/src/android/renderscript/cts/TestConvert.rs
@@ -607,3 +607,615 @@
 uint4 __attribute__((kernel)) testConvertUint4Uint4Uint4(uint4 inV) {
     return convert_uint4(inV);
 }
+
+double2 __attribute__((kernel)) testConvertDouble2Double2Double2(double2 inV) {
+    return convert_double2(inV);
+}
+
+double3 __attribute__((kernel)) testConvertDouble3Double3Double3(double3 inV) {
+    return convert_double3(inV);
+}
+
+double4 __attribute__((kernel)) testConvertDouble4Double4Double4(double4 inV) {
+    return convert_double4(inV);
+}
+
+double2 __attribute__((kernel)) testConvertDouble2Long2Double2(long2 inV) {
+    return convert_double2(inV);
+}
+
+double3 __attribute__((kernel)) testConvertDouble3Long3Double3(long3 inV) {
+    return convert_double3(inV);
+}
+
+double4 __attribute__((kernel)) testConvertDouble4Long4Double4(long4 inV) {
+    return convert_double4(inV);
+}
+
+double2 __attribute__((kernel)) testConvertDouble2Ulong2Double2(ulong2 inV) {
+    return convert_double2(inV);
+}
+
+double3 __attribute__((kernel)) testConvertDouble3Ulong3Double3(ulong3 inV) {
+    return convert_double3(inV);
+}
+
+double4 __attribute__((kernel)) testConvertDouble4Ulong4Double4(ulong4 inV) {
+    return convert_double4(inV);
+}
+
+long2 __attribute__((kernel)) testConvertLong2Double2Long2(double2 inV) {
+    return convert_long2(inV);
+}
+
+long3 __attribute__((kernel)) testConvertLong3Double3Long3(double3 inV) {
+    return convert_long3(inV);
+}
+
+long4 __attribute__((kernel)) testConvertLong4Double4Long4(double4 inV) {
+    return convert_long4(inV);
+}
+
+long2 __attribute__((kernel)) testConvertLong2Long2Long2(long2 inV) {
+    return convert_long2(inV);
+}
+
+long3 __attribute__((kernel)) testConvertLong3Long3Long3(long3 inV) {
+    return convert_long3(inV);
+}
+
+long4 __attribute__((kernel)) testConvertLong4Long4Long4(long4 inV) {
+    return convert_long4(inV);
+}
+
+long2 __attribute__((kernel)) testConvertLong2Ulong2Long2(ulong2 inV) {
+    return convert_long2(inV);
+}
+
+long3 __attribute__((kernel)) testConvertLong3Ulong3Long3(ulong3 inV) {
+    return convert_long3(inV);
+}
+
+long4 __attribute__((kernel)) testConvertLong4Ulong4Long4(ulong4 inV) {
+    return convert_long4(inV);
+}
+
+ulong2 __attribute__((kernel)) testConvertUlong2Double2Ulong2(double2 inV) {
+    return convert_ulong2(inV);
+}
+
+ulong3 __attribute__((kernel)) testConvertUlong3Double3Ulong3(double3 inV) {
+    return convert_ulong3(inV);
+}
+
+ulong4 __attribute__((kernel)) testConvertUlong4Double4Ulong4(double4 inV) {
+    return convert_ulong4(inV);
+}
+
+ulong2 __attribute__((kernel)) testConvertUlong2Long2Ulong2(long2 inV) {
+    return convert_ulong2(inV);
+}
+
+ulong3 __attribute__((kernel)) testConvertUlong3Long3Ulong3(long3 inV) {
+    return convert_ulong3(inV);
+}
+
+ulong4 __attribute__((kernel)) testConvertUlong4Long4Ulong4(long4 inV) {
+    return convert_ulong4(inV);
+}
+
+ulong2 __attribute__((kernel)) testConvertUlong2Ulong2Ulong2(ulong2 inV) {
+    return convert_ulong2(inV);
+}
+
+ulong3 __attribute__((kernel)) testConvertUlong3Ulong3Ulong3(ulong3 inV) {
+    return convert_ulong3(inV);
+}
+
+ulong4 __attribute__((kernel)) testConvertUlong4Ulong4Ulong4(ulong4 inV) {
+    return convert_ulong4(inV);
+}
+
+float2 __attribute__((kernel)) testConvertFloat2Double2Float2(double2 inV) {
+    return convert_float2(inV);
+}
+
+float3 __attribute__((kernel)) testConvertFloat3Double3Float3(double3 inV) {
+    return convert_float3(inV);
+}
+
+float4 __attribute__((kernel)) testConvertFloat4Double4Float4(double4 inV) {
+    return convert_float4(inV);
+}
+
+float2 __attribute__((kernel)) testConvertFloat2Long2Float2(long2 inV) {
+    return convert_float2(inV);
+}
+
+float3 __attribute__((kernel)) testConvertFloat3Long3Float3(long3 inV) {
+    return convert_float3(inV);
+}
+
+float4 __attribute__((kernel)) testConvertFloat4Long4Float4(long4 inV) {
+    return convert_float4(inV);
+}
+
+float2 __attribute__((kernel)) testConvertFloat2Ulong2Float2(ulong2 inV) {
+    return convert_float2(inV);
+}
+
+float3 __attribute__((kernel)) testConvertFloat3Ulong3Float3(ulong3 inV) {
+    return convert_float3(inV);
+}
+
+float4 __attribute__((kernel)) testConvertFloat4Ulong4Float4(ulong4 inV) {
+    return convert_float4(inV);
+}
+
+char2 __attribute__((kernel)) testConvertChar2Double2Char2(double2 inV) {
+    return convert_char2(inV);
+}
+
+char3 __attribute__((kernel)) testConvertChar3Double3Char3(double3 inV) {
+    return convert_char3(inV);
+}
+
+char4 __attribute__((kernel)) testConvertChar4Double4Char4(double4 inV) {
+    return convert_char4(inV);
+}
+
+char2 __attribute__((kernel)) testConvertChar2Long2Char2(long2 inV) {
+    return convert_char2(inV);
+}
+
+char3 __attribute__((kernel)) testConvertChar3Long3Char3(long3 inV) {
+    return convert_char3(inV);
+}
+
+char4 __attribute__((kernel)) testConvertChar4Long4Char4(long4 inV) {
+    return convert_char4(inV);
+}
+
+char2 __attribute__((kernel)) testConvertChar2Ulong2Char2(ulong2 inV) {
+    return convert_char2(inV);
+}
+
+char3 __attribute__((kernel)) testConvertChar3Ulong3Char3(ulong3 inV) {
+    return convert_char3(inV);
+}
+
+char4 __attribute__((kernel)) testConvertChar4Ulong4Char4(ulong4 inV) {
+    return convert_char4(inV);
+}
+
+uchar2 __attribute__((kernel)) testConvertUchar2Double2Uchar2(double2 inV) {
+    return convert_uchar2(inV);
+}
+
+uchar3 __attribute__((kernel)) testConvertUchar3Double3Uchar3(double3 inV) {
+    return convert_uchar3(inV);
+}
+
+uchar4 __attribute__((kernel)) testConvertUchar4Double4Uchar4(double4 inV) {
+    return convert_uchar4(inV);
+}
+
+uchar2 __attribute__((kernel)) testConvertUchar2Long2Uchar2(long2 inV) {
+    return convert_uchar2(inV);
+}
+
+uchar3 __attribute__((kernel)) testConvertUchar3Long3Uchar3(long3 inV) {
+    return convert_uchar3(inV);
+}
+
+uchar4 __attribute__((kernel)) testConvertUchar4Long4Uchar4(long4 inV) {
+    return convert_uchar4(inV);
+}
+
+uchar2 __attribute__((kernel)) testConvertUchar2Ulong2Uchar2(ulong2 inV) {
+    return convert_uchar2(inV);
+}
+
+uchar3 __attribute__((kernel)) testConvertUchar3Ulong3Uchar3(ulong3 inV) {
+    return convert_uchar3(inV);
+}
+
+uchar4 __attribute__((kernel)) testConvertUchar4Ulong4Uchar4(ulong4 inV) {
+    return convert_uchar4(inV);
+}
+
+short2 __attribute__((kernel)) testConvertShort2Double2Short2(double2 inV) {
+    return convert_short2(inV);
+}
+
+short3 __attribute__((kernel)) testConvertShort3Double3Short3(double3 inV) {
+    return convert_short3(inV);
+}
+
+short4 __attribute__((kernel)) testConvertShort4Double4Short4(double4 inV) {
+    return convert_short4(inV);
+}
+
+short2 __attribute__((kernel)) testConvertShort2Long2Short2(long2 inV) {
+    return convert_short2(inV);
+}
+
+short3 __attribute__((kernel)) testConvertShort3Long3Short3(long3 inV) {
+    return convert_short3(inV);
+}
+
+short4 __attribute__((kernel)) testConvertShort4Long4Short4(long4 inV) {
+    return convert_short4(inV);
+}
+
+short2 __attribute__((kernel)) testConvertShort2Ulong2Short2(ulong2 inV) {
+    return convert_short2(inV);
+}
+
+short3 __attribute__((kernel)) testConvertShort3Ulong3Short3(ulong3 inV) {
+    return convert_short3(inV);
+}
+
+short4 __attribute__((kernel)) testConvertShort4Ulong4Short4(ulong4 inV) {
+    return convert_short4(inV);
+}
+
+ushort2 __attribute__((kernel)) testConvertUshort2Double2Ushort2(double2 inV) {
+    return convert_ushort2(inV);
+}
+
+ushort3 __attribute__((kernel)) testConvertUshort3Double3Ushort3(double3 inV) {
+    return convert_ushort3(inV);
+}
+
+ushort4 __attribute__((kernel)) testConvertUshort4Double4Ushort4(double4 inV) {
+    return convert_ushort4(inV);
+}
+
+ushort2 __attribute__((kernel)) testConvertUshort2Long2Ushort2(long2 inV) {
+    return convert_ushort2(inV);
+}
+
+ushort3 __attribute__((kernel)) testConvertUshort3Long3Ushort3(long3 inV) {
+    return convert_ushort3(inV);
+}
+
+ushort4 __attribute__((kernel)) testConvertUshort4Long4Ushort4(long4 inV) {
+    return convert_ushort4(inV);
+}
+
+ushort2 __attribute__((kernel)) testConvertUshort2Ulong2Ushort2(ulong2 inV) {
+    return convert_ushort2(inV);
+}
+
+ushort3 __attribute__((kernel)) testConvertUshort3Ulong3Ushort3(ulong3 inV) {
+    return convert_ushort3(inV);
+}
+
+ushort4 __attribute__((kernel)) testConvertUshort4Ulong4Ushort4(ulong4 inV) {
+    return convert_ushort4(inV);
+}
+
+int2 __attribute__((kernel)) testConvertInt2Double2Int2(double2 inV) {
+    return convert_int2(inV);
+}
+
+int3 __attribute__((kernel)) testConvertInt3Double3Int3(double3 inV) {
+    return convert_int3(inV);
+}
+
+int4 __attribute__((kernel)) testConvertInt4Double4Int4(double4 inV) {
+    return convert_int4(inV);
+}
+
+int2 __attribute__((kernel)) testConvertInt2Long2Int2(long2 inV) {
+    return convert_int2(inV);
+}
+
+int3 __attribute__((kernel)) testConvertInt3Long3Int3(long3 inV) {
+    return convert_int3(inV);
+}
+
+int4 __attribute__((kernel)) testConvertInt4Long4Int4(long4 inV) {
+    return convert_int4(inV);
+}
+
+int2 __attribute__((kernel)) testConvertInt2Ulong2Int2(ulong2 inV) {
+    return convert_int2(inV);
+}
+
+int3 __attribute__((kernel)) testConvertInt3Ulong3Int3(ulong3 inV) {
+    return convert_int3(inV);
+}
+
+int4 __attribute__((kernel)) testConvertInt4Ulong4Int4(ulong4 inV) {
+    return convert_int4(inV);
+}
+
+uint2 __attribute__((kernel)) testConvertUint2Double2Uint2(double2 inV) {
+    return convert_uint2(inV);
+}
+
+uint3 __attribute__((kernel)) testConvertUint3Double3Uint3(double3 inV) {
+    return convert_uint3(inV);
+}
+
+uint4 __attribute__((kernel)) testConvertUint4Double4Uint4(double4 inV) {
+    return convert_uint4(inV);
+}
+
+uint2 __attribute__((kernel)) testConvertUint2Long2Uint2(long2 inV) {
+    return convert_uint2(inV);
+}
+
+uint3 __attribute__((kernel)) testConvertUint3Long3Uint3(long3 inV) {
+    return convert_uint3(inV);
+}
+
+uint4 __attribute__((kernel)) testConvertUint4Long4Uint4(long4 inV) {
+    return convert_uint4(inV);
+}
+
+uint2 __attribute__((kernel)) testConvertUint2Ulong2Uint2(ulong2 inV) {
+    return convert_uint2(inV);
+}
+
+uint3 __attribute__((kernel)) testConvertUint3Ulong3Uint3(ulong3 inV) {
+    return convert_uint3(inV);
+}
+
+uint4 __attribute__((kernel)) testConvertUint4Ulong4Uint4(ulong4 inV) {
+    return convert_uint4(inV);
+}
+
+double2 __attribute__((kernel)) testConvertDouble2Float2Double2(float2 inV) {
+    return convert_double2(inV);
+}
+
+double3 __attribute__((kernel)) testConvertDouble3Float3Double3(float3 inV) {
+    return convert_double3(inV);
+}
+
+double4 __attribute__((kernel)) testConvertDouble4Float4Double4(float4 inV) {
+    return convert_double4(inV);
+}
+
+double2 __attribute__((kernel)) testConvertDouble2Char2Double2(char2 inV) {
+    return convert_double2(inV);
+}
+
+double3 __attribute__((kernel)) testConvertDouble3Char3Double3(char3 inV) {
+    return convert_double3(inV);
+}
+
+double4 __attribute__((kernel)) testConvertDouble4Char4Double4(char4 inV) {
+    return convert_double4(inV);
+}
+
+double2 __attribute__((kernel)) testConvertDouble2Uchar2Double2(uchar2 inV) {
+    return convert_double2(inV);
+}
+
+double3 __attribute__((kernel)) testConvertDouble3Uchar3Double3(uchar3 inV) {
+    return convert_double3(inV);
+}
+
+double4 __attribute__((kernel)) testConvertDouble4Uchar4Double4(uchar4 inV) {
+    return convert_double4(inV);
+}
+
+double2 __attribute__((kernel)) testConvertDouble2Short2Double2(short2 inV) {
+    return convert_double2(inV);
+}
+
+double3 __attribute__((kernel)) testConvertDouble3Short3Double3(short3 inV) {
+    return convert_double3(inV);
+}
+
+double4 __attribute__((kernel)) testConvertDouble4Short4Double4(short4 inV) {
+    return convert_double4(inV);
+}
+
+double2 __attribute__((kernel)) testConvertDouble2Ushort2Double2(ushort2 inV) {
+    return convert_double2(inV);
+}
+
+double3 __attribute__((kernel)) testConvertDouble3Ushort3Double3(ushort3 inV) {
+    return convert_double3(inV);
+}
+
+double4 __attribute__((kernel)) testConvertDouble4Ushort4Double4(ushort4 inV) {
+    return convert_double4(inV);
+}
+
+double2 __attribute__((kernel)) testConvertDouble2Int2Double2(int2 inV) {
+    return convert_double2(inV);
+}
+
+double3 __attribute__((kernel)) testConvertDouble3Int3Double3(int3 inV) {
+    return convert_double3(inV);
+}
+
+double4 __attribute__((kernel)) testConvertDouble4Int4Double4(int4 inV) {
+    return convert_double4(inV);
+}
+
+double2 __attribute__((kernel)) testConvertDouble2Uint2Double2(uint2 inV) {
+    return convert_double2(inV);
+}
+
+double3 __attribute__((kernel)) testConvertDouble3Uint3Double3(uint3 inV) {
+    return convert_double3(inV);
+}
+
+double4 __attribute__((kernel)) testConvertDouble4Uint4Double4(uint4 inV) {
+    return convert_double4(inV);
+}
+
+long2 __attribute__((kernel)) testConvertLong2Float2Long2(float2 inV) {
+    return convert_long2(inV);
+}
+
+long3 __attribute__((kernel)) testConvertLong3Float3Long3(float3 inV) {
+    return convert_long3(inV);
+}
+
+long4 __attribute__((kernel)) testConvertLong4Float4Long4(float4 inV) {
+    return convert_long4(inV);
+}
+
+long2 __attribute__((kernel)) testConvertLong2Char2Long2(char2 inV) {
+    return convert_long2(inV);
+}
+
+long3 __attribute__((kernel)) testConvertLong3Char3Long3(char3 inV) {
+    return convert_long3(inV);
+}
+
+long4 __attribute__((kernel)) testConvertLong4Char4Long4(char4 inV) {
+    return convert_long4(inV);
+}
+
+long2 __attribute__((kernel)) testConvertLong2Uchar2Long2(uchar2 inV) {
+    return convert_long2(inV);
+}
+
+long3 __attribute__((kernel)) testConvertLong3Uchar3Long3(uchar3 inV) {
+    return convert_long3(inV);
+}
+
+long4 __attribute__((kernel)) testConvertLong4Uchar4Long4(uchar4 inV) {
+    return convert_long4(inV);
+}
+
+long2 __attribute__((kernel)) testConvertLong2Short2Long2(short2 inV) {
+    return convert_long2(inV);
+}
+
+long3 __attribute__((kernel)) testConvertLong3Short3Long3(short3 inV) {
+    return convert_long3(inV);
+}
+
+long4 __attribute__((kernel)) testConvertLong4Short4Long4(short4 inV) {
+    return convert_long4(inV);
+}
+
+long2 __attribute__((kernel)) testConvertLong2Ushort2Long2(ushort2 inV) {
+    return convert_long2(inV);
+}
+
+long3 __attribute__((kernel)) testConvertLong3Ushort3Long3(ushort3 inV) {
+    return convert_long3(inV);
+}
+
+long4 __attribute__((kernel)) testConvertLong4Ushort4Long4(ushort4 inV) {
+    return convert_long4(inV);
+}
+
+long2 __attribute__((kernel)) testConvertLong2Int2Long2(int2 inV) {
+    return convert_long2(inV);
+}
+
+long3 __attribute__((kernel)) testConvertLong3Int3Long3(int3 inV) {
+    return convert_long3(inV);
+}
+
+long4 __attribute__((kernel)) testConvertLong4Int4Long4(int4 inV) {
+    return convert_long4(inV);
+}
+
+long2 __attribute__((kernel)) testConvertLong2Uint2Long2(uint2 inV) {
+    return convert_long2(inV);
+}
+
+long3 __attribute__((kernel)) testConvertLong3Uint3Long3(uint3 inV) {
+    return convert_long3(inV);
+}
+
+long4 __attribute__((kernel)) testConvertLong4Uint4Long4(uint4 inV) {
+    return convert_long4(inV);
+}
+
+ulong2 __attribute__((kernel)) testConvertUlong2Float2Ulong2(float2 inV) {
+    return convert_ulong2(inV);
+}
+
+ulong3 __attribute__((kernel)) testConvertUlong3Float3Ulong3(float3 inV) {
+    return convert_ulong3(inV);
+}
+
+ulong4 __attribute__((kernel)) testConvertUlong4Float4Ulong4(float4 inV) {
+    return convert_ulong4(inV);
+}
+
+ulong2 __attribute__((kernel)) testConvertUlong2Char2Ulong2(char2 inV) {
+    return convert_ulong2(inV);
+}
+
+ulong3 __attribute__((kernel)) testConvertUlong3Char3Ulong3(char3 inV) {
+    return convert_ulong3(inV);
+}
+
+ulong4 __attribute__((kernel)) testConvertUlong4Char4Ulong4(char4 inV) {
+    return convert_ulong4(inV);
+}
+
+ulong2 __attribute__((kernel)) testConvertUlong2Uchar2Ulong2(uchar2 inV) {
+    return convert_ulong2(inV);
+}
+
+ulong3 __attribute__((kernel)) testConvertUlong3Uchar3Ulong3(uchar3 inV) {
+    return convert_ulong3(inV);
+}
+
+ulong4 __attribute__((kernel)) testConvertUlong4Uchar4Ulong4(uchar4 inV) {
+    return convert_ulong4(inV);
+}
+
+ulong2 __attribute__((kernel)) testConvertUlong2Short2Ulong2(short2 inV) {
+    return convert_ulong2(inV);
+}
+
+ulong3 __attribute__((kernel)) testConvertUlong3Short3Ulong3(short3 inV) {
+    return convert_ulong3(inV);
+}
+
+ulong4 __attribute__((kernel)) testConvertUlong4Short4Ulong4(short4 inV) {
+    return convert_ulong4(inV);
+}
+
+ulong2 __attribute__((kernel)) testConvertUlong2Ushort2Ulong2(ushort2 inV) {
+    return convert_ulong2(inV);
+}
+
+ulong3 __attribute__((kernel)) testConvertUlong3Ushort3Ulong3(ushort3 inV) {
+    return convert_ulong3(inV);
+}
+
+ulong4 __attribute__((kernel)) testConvertUlong4Ushort4Ulong4(ushort4 inV) {
+    return convert_ulong4(inV);
+}
+
+ulong2 __attribute__((kernel)) testConvertUlong2Int2Ulong2(int2 inV) {
+    return convert_ulong2(inV);
+}
+
+ulong3 __attribute__((kernel)) testConvertUlong3Int3Ulong3(int3 inV) {
+    return convert_ulong3(inV);
+}
+
+ulong4 __attribute__((kernel)) testConvertUlong4Int4Ulong4(int4 inV) {
+    return convert_ulong4(inV);
+}
+
+ulong2 __attribute__((kernel)) testConvertUlong2Uint2Ulong2(uint2 inV) {
+    return convert_ulong2(inV);
+}
+
+ulong3 __attribute__((kernel)) testConvertUlong3Uint3Ulong3(uint3 inV) {
+    return convert_ulong3(inV);
+}
+
+ulong4 __attribute__((kernel)) testConvertUlong4Uint4Ulong4(uint4 inV) {
+    return convert_ulong4(inV);
+}
diff --git a/tests/src/android/renderscript/cts/TestMax.rs b/tests/src/android/renderscript/cts/TestMax.rs
index 87d8040..dff7927 100644
--- a/tests/src/android/renderscript/cts/TestMax.rs
+++ b/tests/src/android/renderscript/cts/TestMax.rs
@@ -47,117 +47,157 @@
     return max(inV1, inV2);
 }
 
-uchar __attribute__((kernel)) testMaxUcharUcharUchar(uchar inV1, unsigned int x) {
-    uchar inV2 = rsGetElementAt_uchar(gAllocInV2, x);
-    return max(inV1, inV2);
-}
-
-short __attribute__((kernel)) testMaxShortShortShort(short inV1, unsigned int x) {
-    short inV2 = rsGetElementAt_short(gAllocInV2, x);
-    return max(inV1, inV2);
-}
-
-ushort __attribute__((kernel)) testMaxUshortUshortUshort(ushort inV1, unsigned int x) {
-    ushort inV2 = rsGetElementAt_ushort(gAllocInV2, x);
-    return max(inV1, inV2);
-}
-
-int __attribute__((kernel)) testMaxIntIntInt(int inV1, unsigned int x) {
-    int inV2 = rsGetElementAt_int(gAllocInV2, x);
-    return max(inV1, inV2);
-}
-
-uint __attribute__((kernel)) testMaxUintUintUint(uint inV1, unsigned int x) {
-    uint inV2 = rsGetElementAt_uint(gAllocInV2, x);
-    return max(inV1, inV2);
-}
-
 char2 __attribute__((kernel)) testMaxChar2Char2Char2(char2 inV1, unsigned int x) {
     char2 inV2 = rsGetElementAt_char2(gAllocInV2, x);
     return max(inV1, inV2);
 }
 
-uchar2 __attribute__((kernel)) testMaxUchar2Uchar2Uchar2(uchar2 inV1, unsigned int x) {
-    uchar2 inV2 = rsGetElementAt_uchar2(gAllocInV2, x);
-    return max(inV1, inV2);
-}
-
-short2 __attribute__((kernel)) testMaxShort2Short2Short2(short2 inV1, unsigned int x) {
-    short2 inV2 = rsGetElementAt_short2(gAllocInV2, x);
-    return max(inV1, inV2);
-}
-
-ushort2 __attribute__((kernel)) testMaxUshort2Ushort2Ushort2(ushort2 inV1, unsigned int x) {
-    ushort2 inV2 = rsGetElementAt_ushort2(gAllocInV2, x);
-    return max(inV1, inV2);
-}
-
-int2 __attribute__((kernel)) testMaxInt2Int2Int2(int2 inV1, unsigned int x) {
-    int2 inV2 = rsGetElementAt_int2(gAllocInV2, x);
-    return max(inV1, inV2);
-}
-
-uint2 __attribute__((kernel)) testMaxUint2Uint2Uint2(uint2 inV1, unsigned int x) {
-    uint2 inV2 = rsGetElementAt_uint2(gAllocInV2, x);
-    return max(inV1, inV2);
-}
-
 char3 __attribute__((kernel)) testMaxChar3Char3Char3(char3 inV1, unsigned int x) {
     char3 inV2 = rsGetElementAt_char3(gAllocInV2, x);
     return max(inV1, inV2);
 }
 
-uchar3 __attribute__((kernel)) testMaxUchar3Uchar3Uchar3(uchar3 inV1, unsigned int x) {
-    uchar3 inV2 = rsGetElementAt_uchar3(gAllocInV2, x);
-    return max(inV1, inV2);
-}
-
-short3 __attribute__((kernel)) testMaxShort3Short3Short3(short3 inV1, unsigned int x) {
-    short3 inV2 = rsGetElementAt_short3(gAllocInV2, x);
-    return max(inV1, inV2);
-}
-
-ushort3 __attribute__((kernel)) testMaxUshort3Ushort3Ushort3(ushort3 inV1, unsigned int x) {
-    ushort3 inV2 = rsGetElementAt_ushort3(gAllocInV2, x);
-    return max(inV1, inV2);
-}
-
-int3 __attribute__((kernel)) testMaxInt3Int3Int3(int3 inV1, unsigned int x) {
-    int3 inV2 = rsGetElementAt_int3(gAllocInV2, x);
-    return max(inV1, inV2);
-}
-
-uint3 __attribute__((kernel)) testMaxUint3Uint3Uint3(uint3 inV1, unsigned int x) {
-    uint3 inV2 = rsGetElementAt_uint3(gAllocInV2, x);
-    return max(inV1, inV2);
-}
-
 char4 __attribute__((kernel)) testMaxChar4Char4Char4(char4 inV1, unsigned int x) {
     char4 inV2 = rsGetElementAt_char4(gAllocInV2, x);
     return max(inV1, inV2);
 }
 
+uchar __attribute__((kernel)) testMaxUcharUcharUchar(uchar inV1, unsigned int x) {
+    uchar inV2 = rsGetElementAt_uchar(gAllocInV2, x);
+    return max(inV1, inV2);
+}
+
+uchar2 __attribute__((kernel)) testMaxUchar2Uchar2Uchar2(uchar2 inV1, unsigned int x) {
+    uchar2 inV2 = rsGetElementAt_uchar2(gAllocInV2, x);
+    return max(inV1, inV2);
+}
+
+uchar3 __attribute__((kernel)) testMaxUchar3Uchar3Uchar3(uchar3 inV1, unsigned int x) {
+    uchar3 inV2 = rsGetElementAt_uchar3(gAllocInV2, x);
+    return max(inV1, inV2);
+}
+
 uchar4 __attribute__((kernel)) testMaxUchar4Uchar4Uchar4(uchar4 inV1, unsigned int x) {
     uchar4 inV2 = rsGetElementAt_uchar4(gAllocInV2, x);
     return max(inV1, inV2);
 }
 
+short __attribute__((kernel)) testMaxShortShortShort(short inV1, unsigned int x) {
+    short inV2 = rsGetElementAt_short(gAllocInV2, x);
+    return max(inV1, inV2);
+}
+
+short2 __attribute__((kernel)) testMaxShort2Short2Short2(short2 inV1, unsigned int x) {
+    short2 inV2 = rsGetElementAt_short2(gAllocInV2, x);
+    return max(inV1, inV2);
+}
+
+short3 __attribute__((kernel)) testMaxShort3Short3Short3(short3 inV1, unsigned int x) {
+    short3 inV2 = rsGetElementAt_short3(gAllocInV2, x);
+    return max(inV1, inV2);
+}
+
 short4 __attribute__((kernel)) testMaxShort4Short4Short4(short4 inV1, unsigned int x) {
     short4 inV2 = rsGetElementAt_short4(gAllocInV2, x);
     return max(inV1, inV2);
 }
 
+ushort __attribute__((kernel)) testMaxUshortUshortUshort(ushort inV1, unsigned int x) {
+    ushort inV2 = rsGetElementAt_ushort(gAllocInV2, x);
+    return max(inV1, inV2);
+}
+
+ushort2 __attribute__((kernel)) testMaxUshort2Ushort2Ushort2(ushort2 inV1, unsigned int x) {
+    ushort2 inV2 = rsGetElementAt_ushort2(gAllocInV2, x);
+    return max(inV1, inV2);
+}
+
+ushort3 __attribute__((kernel)) testMaxUshort3Ushort3Ushort3(ushort3 inV1, unsigned int x) {
+    ushort3 inV2 = rsGetElementAt_ushort3(gAllocInV2, x);
+    return max(inV1, inV2);
+}
+
 ushort4 __attribute__((kernel)) testMaxUshort4Ushort4Ushort4(ushort4 inV1, unsigned int x) {
     ushort4 inV2 = rsGetElementAt_ushort4(gAllocInV2, x);
     return max(inV1, inV2);
 }
 
+int __attribute__((kernel)) testMaxIntIntInt(int inV1, unsigned int x) {
+    int inV2 = rsGetElementAt_int(gAllocInV2, x);
+    return max(inV1, inV2);
+}
+
+int2 __attribute__((kernel)) testMaxInt2Int2Int2(int2 inV1, unsigned int x) {
+    int2 inV2 = rsGetElementAt_int2(gAllocInV2, x);
+    return max(inV1, inV2);
+}
+
+int3 __attribute__((kernel)) testMaxInt3Int3Int3(int3 inV1, unsigned int x) {
+    int3 inV2 = rsGetElementAt_int3(gAllocInV2, x);
+    return max(inV1, inV2);
+}
+
 int4 __attribute__((kernel)) testMaxInt4Int4Int4(int4 inV1, unsigned int x) {
     int4 inV2 = rsGetElementAt_int4(gAllocInV2, x);
     return max(inV1, inV2);
 }
 
+uint __attribute__((kernel)) testMaxUintUintUint(uint inV1, unsigned int x) {
+    uint inV2 = rsGetElementAt_uint(gAllocInV2, x);
+    return max(inV1, inV2);
+}
+
+uint2 __attribute__((kernel)) testMaxUint2Uint2Uint2(uint2 inV1, unsigned int x) {
+    uint2 inV2 = rsGetElementAt_uint2(gAllocInV2, x);
+    return max(inV1, inV2);
+}
+
+uint3 __attribute__((kernel)) testMaxUint3Uint3Uint3(uint3 inV1, unsigned int x) {
+    uint3 inV2 = rsGetElementAt_uint3(gAllocInV2, x);
+    return max(inV1, inV2);
+}
+
 uint4 __attribute__((kernel)) testMaxUint4Uint4Uint4(uint4 inV1, unsigned int x) {
     uint4 inV2 = rsGetElementAt_uint4(gAllocInV2, x);
     return max(inV1, inV2);
 }
+
+long __attribute__((kernel)) testMaxLongLongLong(long inV1, unsigned int x) {
+    long inV2 = rsGetElementAt_long(gAllocInV2, x);
+    return max(inV1, inV2);
+}
+
+long2 __attribute__((kernel)) testMaxLong2Long2Long2(long2 inV1, unsigned int x) {
+    long2 inV2 = rsGetElementAt_long2(gAllocInV2, x);
+    return max(inV1, inV2);
+}
+
+long3 __attribute__((kernel)) testMaxLong3Long3Long3(long3 inV1, unsigned int x) {
+    long3 inV2 = rsGetElementAt_long3(gAllocInV2, x);
+    return max(inV1, inV2);
+}
+
+long4 __attribute__((kernel)) testMaxLong4Long4Long4(long4 inV1, unsigned int x) {
+    long4 inV2 = rsGetElementAt_long4(gAllocInV2, x);
+    return max(inV1, inV2);
+}
+
+ulong __attribute__((kernel)) testMaxUlongUlongUlong(ulong inV1, unsigned int x) {
+    ulong inV2 = rsGetElementAt_ulong(gAllocInV2, x);
+    return max(inV1, inV2);
+}
+
+ulong2 __attribute__((kernel)) testMaxUlong2Ulong2Ulong2(ulong2 inV1, unsigned int x) {
+    ulong2 inV2 = rsGetElementAt_ulong2(gAllocInV2, x);
+    return max(inV1, inV2);
+}
+
+ulong3 __attribute__((kernel)) testMaxUlong3Ulong3Ulong3(ulong3 inV1, unsigned int x) {
+    ulong3 inV2 = rsGetElementAt_ulong3(gAllocInV2, x);
+    return max(inV1, inV2);
+}
+
+ulong4 __attribute__((kernel)) testMaxUlong4Ulong4Ulong4(ulong4 inV1, unsigned int x) {
+    ulong4 inV2 = rsGetElementAt_ulong4(gAllocInV2, x);
+    return max(inV1, inV2);
+}
diff --git a/tests/src/android/renderscript/cts/TestMin.rs b/tests/src/android/renderscript/cts/TestMin.rs
index 26301ff..29a9aeb 100644
--- a/tests/src/android/renderscript/cts/TestMin.rs
+++ b/tests/src/android/renderscript/cts/TestMin.rs
@@ -47,117 +47,157 @@
     return min(inV1, inV2);
 }
 
-uchar __attribute__((kernel)) testMinUcharUcharUchar(uchar inV1, unsigned int x) {
-    uchar inV2 = rsGetElementAt_uchar(gAllocInV2, x);
-    return min(inV1, inV2);
-}
-
-short __attribute__((kernel)) testMinShortShortShort(short inV1, unsigned int x) {
-    short inV2 = rsGetElementAt_short(gAllocInV2, x);
-    return min(inV1, inV2);
-}
-
-ushort __attribute__((kernel)) testMinUshortUshortUshort(ushort inV1, unsigned int x) {
-    ushort inV2 = rsGetElementAt_ushort(gAllocInV2, x);
-    return min(inV1, inV2);
-}
-
-int __attribute__((kernel)) testMinIntIntInt(int inV1, unsigned int x) {
-    int inV2 = rsGetElementAt_int(gAllocInV2, x);
-    return min(inV1, inV2);
-}
-
-uint __attribute__((kernel)) testMinUintUintUint(uint inV1, unsigned int x) {
-    uint inV2 = rsGetElementAt_uint(gAllocInV2, x);
-    return min(inV1, inV2);
-}
-
 char2 __attribute__((kernel)) testMinChar2Char2Char2(char2 inV1, unsigned int x) {
     char2 inV2 = rsGetElementAt_char2(gAllocInV2, x);
     return min(inV1, inV2);
 }
 
-uchar2 __attribute__((kernel)) testMinUchar2Uchar2Uchar2(uchar2 inV1, unsigned int x) {
-    uchar2 inV2 = rsGetElementAt_uchar2(gAllocInV2, x);
-    return min(inV1, inV2);
-}
-
-short2 __attribute__((kernel)) testMinShort2Short2Short2(short2 inV1, unsigned int x) {
-    short2 inV2 = rsGetElementAt_short2(gAllocInV2, x);
-    return min(inV1, inV2);
-}
-
-ushort2 __attribute__((kernel)) testMinUshort2Ushort2Ushort2(ushort2 inV1, unsigned int x) {
-    ushort2 inV2 = rsGetElementAt_ushort2(gAllocInV2, x);
-    return min(inV1, inV2);
-}
-
-int2 __attribute__((kernel)) testMinInt2Int2Int2(int2 inV1, unsigned int x) {
-    int2 inV2 = rsGetElementAt_int2(gAllocInV2, x);
-    return min(inV1, inV2);
-}
-
-uint2 __attribute__((kernel)) testMinUint2Uint2Uint2(uint2 inV1, unsigned int x) {
-    uint2 inV2 = rsGetElementAt_uint2(gAllocInV2, x);
-    return min(inV1, inV2);
-}
-
 char3 __attribute__((kernel)) testMinChar3Char3Char3(char3 inV1, unsigned int x) {
     char3 inV2 = rsGetElementAt_char3(gAllocInV2, x);
     return min(inV1, inV2);
 }
 
-uchar3 __attribute__((kernel)) testMinUchar3Uchar3Uchar3(uchar3 inV1, unsigned int x) {
-    uchar3 inV2 = rsGetElementAt_uchar3(gAllocInV2, x);
-    return min(inV1, inV2);
-}
-
-short3 __attribute__((kernel)) testMinShort3Short3Short3(short3 inV1, unsigned int x) {
-    short3 inV2 = rsGetElementAt_short3(gAllocInV2, x);
-    return min(inV1, inV2);
-}
-
-ushort3 __attribute__((kernel)) testMinUshort3Ushort3Ushort3(ushort3 inV1, unsigned int x) {
-    ushort3 inV2 = rsGetElementAt_ushort3(gAllocInV2, x);
-    return min(inV1, inV2);
-}
-
-int3 __attribute__((kernel)) testMinInt3Int3Int3(int3 inV1, unsigned int x) {
-    int3 inV2 = rsGetElementAt_int3(gAllocInV2, x);
-    return min(inV1, inV2);
-}
-
-uint3 __attribute__((kernel)) testMinUint3Uint3Uint3(uint3 inV1, unsigned int x) {
-    uint3 inV2 = rsGetElementAt_uint3(gAllocInV2, x);
-    return min(inV1, inV2);
-}
-
 char4 __attribute__((kernel)) testMinChar4Char4Char4(char4 inV1, unsigned int x) {
     char4 inV2 = rsGetElementAt_char4(gAllocInV2, x);
     return min(inV1, inV2);
 }
 
+uchar __attribute__((kernel)) testMinUcharUcharUchar(uchar inV1, unsigned int x) {
+    uchar inV2 = rsGetElementAt_uchar(gAllocInV2, x);
+    return min(inV1, inV2);
+}
+
+uchar2 __attribute__((kernel)) testMinUchar2Uchar2Uchar2(uchar2 inV1, unsigned int x) {
+    uchar2 inV2 = rsGetElementAt_uchar2(gAllocInV2, x);
+    return min(inV1, inV2);
+}
+
+uchar3 __attribute__((kernel)) testMinUchar3Uchar3Uchar3(uchar3 inV1, unsigned int x) {
+    uchar3 inV2 = rsGetElementAt_uchar3(gAllocInV2, x);
+    return min(inV1, inV2);
+}
+
 uchar4 __attribute__((kernel)) testMinUchar4Uchar4Uchar4(uchar4 inV1, unsigned int x) {
     uchar4 inV2 = rsGetElementAt_uchar4(gAllocInV2, x);
     return min(inV1, inV2);
 }
 
+short __attribute__((kernel)) testMinShortShortShort(short inV1, unsigned int x) {
+    short inV2 = rsGetElementAt_short(gAllocInV2, x);
+    return min(inV1, inV2);
+}
+
+short2 __attribute__((kernel)) testMinShort2Short2Short2(short2 inV1, unsigned int x) {
+    short2 inV2 = rsGetElementAt_short2(gAllocInV2, x);
+    return min(inV1, inV2);
+}
+
+short3 __attribute__((kernel)) testMinShort3Short3Short3(short3 inV1, unsigned int x) {
+    short3 inV2 = rsGetElementAt_short3(gAllocInV2, x);
+    return min(inV1, inV2);
+}
+
 short4 __attribute__((kernel)) testMinShort4Short4Short4(short4 inV1, unsigned int x) {
     short4 inV2 = rsGetElementAt_short4(gAllocInV2, x);
     return min(inV1, inV2);
 }
 
+ushort __attribute__((kernel)) testMinUshortUshortUshort(ushort inV1, unsigned int x) {
+    ushort inV2 = rsGetElementAt_ushort(gAllocInV2, x);
+    return min(inV1, inV2);
+}
+
+ushort2 __attribute__((kernel)) testMinUshort2Ushort2Ushort2(ushort2 inV1, unsigned int x) {
+    ushort2 inV2 = rsGetElementAt_ushort2(gAllocInV2, x);
+    return min(inV1, inV2);
+}
+
+ushort3 __attribute__((kernel)) testMinUshort3Ushort3Ushort3(ushort3 inV1, unsigned int x) {
+    ushort3 inV2 = rsGetElementAt_ushort3(gAllocInV2, x);
+    return min(inV1, inV2);
+}
+
 ushort4 __attribute__((kernel)) testMinUshort4Ushort4Ushort4(ushort4 inV1, unsigned int x) {
     ushort4 inV2 = rsGetElementAt_ushort4(gAllocInV2, x);
     return min(inV1, inV2);
 }
 
+int __attribute__((kernel)) testMinIntIntInt(int inV1, unsigned int x) {
+    int inV2 = rsGetElementAt_int(gAllocInV2, x);
+    return min(inV1, inV2);
+}
+
+int2 __attribute__((kernel)) testMinInt2Int2Int2(int2 inV1, unsigned int x) {
+    int2 inV2 = rsGetElementAt_int2(gAllocInV2, x);
+    return min(inV1, inV2);
+}
+
+int3 __attribute__((kernel)) testMinInt3Int3Int3(int3 inV1, unsigned int x) {
+    int3 inV2 = rsGetElementAt_int3(gAllocInV2, x);
+    return min(inV1, inV2);
+}
+
 int4 __attribute__((kernel)) testMinInt4Int4Int4(int4 inV1, unsigned int x) {
     int4 inV2 = rsGetElementAt_int4(gAllocInV2, x);
     return min(inV1, inV2);
 }
 
+uint __attribute__((kernel)) testMinUintUintUint(uint inV1, unsigned int x) {
+    uint inV2 = rsGetElementAt_uint(gAllocInV2, x);
+    return min(inV1, inV2);
+}
+
+uint2 __attribute__((kernel)) testMinUint2Uint2Uint2(uint2 inV1, unsigned int x) {
+    uint2 inV2 = rsGetElementAt_uint2(gAllocInV2, x);
+    return min(inV1, inV2);
+}
+
+uint3 __attribute__((kernel)) testMinUint3Uint3Uint3(uint3 inV1, unsigned int x) {
+    uint3 inV2 = rsGetElementAt_uint3(gAllocInV2, x);
+    return min(inV1, inV2);
+}
+
 uint4 __attribute__((kernel)) testMinUint4Uint4Uint4(uint4 inV1, unsigned int x) {
     uint4 inV2 = rsGetElementAt_uint4(gAllocInV2, x);
     return min(inV1, inV2);
 }
+
+long __attribute__((kernel)) testMinLongLongLong(long inV1, unsigned int x) {
+    long inV2 = rsGetElementAt_long(gAllocInV2, x);
+    return min(inV1, inV2);
+}
+
+long2 __attribute__((kernel)) testMinLong2Long2Long2(long2 inV1, unsigned int x) {
+    long2 inV2 = rsGetElementAt_long2(gAllocInV2, x);
+    return min(inV1, inV2);
+}
+
+long3 __attribute__((kernel)) testMinLong3Long3Long3(long3 inV1, unsigned int x) {
+    long3 inV2 = rsGetElementAt_long3(gAllocInV2, x);
+    return min(inV1, inV2);
+}
+
+long4 __attribute__((kernel)) testMinLong4Long4Long4(long4 inV1, unsigned int x) {
+    long4 inV2 = rsGetElementAt_long4(gAllocInV2, x);
+    return min(inV1, inV2);
+}
+
+ulong __attribute__((kernel)) testMinUlongUlongUlong(ulong inV1, unsigned int x) {
+    ulong inV2 = rsGetElementAt_ulong(gAllocInV2, x);
+    return min(inV1, inV2);
+}
+
+ulong2 __attribute__((kernel)) testMinUlong2Ulong2Ulong2(ulong2 inV1, unsigned int x) {
+    ulong2 inV2 = rsGetElementAt_ulong2(gAllocInV2, x);
+    return min(inV1, inV2);
+}
+
+ulong3 __attribute__((kernel)) testMinUlong3Ulong3Ulong3(ulong3 inV1, unsigned int x) {
+    ulong3 inV2 = rsGetElementAt_ulong3(gAllocInV2, x);
+    return min(inV1, inV2);
+}
+
+ulong4 __attribute__((kernel)) testMinUlong4Ulong4Ulong4(ulong4 inV1, unsigned int x) {
+    ulong4 inV2 = rsGetElementAt_ulong4(gAllocInV2, x);
+    return min(inV1, inV2);
+}
diff --git a/tests/src/android/renderscript/cts/TestNativeAcos.rs b/tests/src/android/renderscript/cts/TestNativeAcos.rs
new file mode 100644
index 0000000..2419c0e
--- /dev/null
+++ b/tests/src/android/renderscript/cts/TestNativeAcos.rs
@@ -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.
+ */
+
+#pragma version(1)
+#pragma rs java_package_name(android.renderscript.cts)
+
+// Don't edit this file!  It is auto-generated by frameworks/rs/api/gen_runtime.
+
+
+float __attribute__((kernel)) testNativeAcosFloatFloat(float inV) {
+    return native_acos(inV);
+}
+
+float2 __attribute__((kernel)) testNativeAcosFloat2Float2(float2 inV) {
+    return native_acos(inV);
+}
+
+float3 __attribute__((kernel)) testNativeAcosFloat3Float3(float3 inV) {
+    return native_acos(inV);
+}
+
+float4 __attribute__((kernel)) testNativeAcosFloat4Float4(float4 inV) {
+    return native_acos(inV);
+}
diff --git a/tests/src/android/renderscript/cts/TestNativeAcosRelaxed.rs b/tests/src/android/renderscript/cts/TestNativeAcosRelaxed.rs
new file mode 100644
index 0000000..01a9de2
--- /dev/null
+++ b/tests/src/android/renderscript/cts/TestNativeAcosRelaxed.rs
@@ -0,0 +1,20 @@
+/*
+ * 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.
+ */
+
+#include "TestNativeAcos.rs"
+#pragma rs_fp_relaxed
+// Don't edit this file!  It is auto-generated by frameworks/rs/api/gen_runtime.
+
diff --git a/tests/src/android/renderscript/cts/TestNativeAcosh.rs b/tests/src/android/renderscript/cts/TestNativeAcosh.rs
new file mode 100644
index 0000000..370f4ed
--- /dev/null
+++ b/tests/src/android/renderscript/cts/TestNativeAcosh.rs
@@ -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.
+ */
+
+#pragma version(1)
+#pragma rs java_package_name(android.renderscript.cts)
+
+// Don't edit this file!  It is auto-generated by frameworks/rs/api/gen_runtime.
+
+
+float __attribute__((kernel)) testNativeAcoshFloatFloat(float in) {
+    return native_acosh(in);
+}
+
+float2 __attribute__((kernel)) testNativeAcoshFloat2Float2(float2 in) {
+    return native_acosh(in);
+}
+
+float3 __attribute__((kernel)) testNativeAcoshFloat3Float3(float3 in) {
+    return native_acosh(in);
+}
+
+float4 __attribute__((kernel)) testNativeAcoshFloat4Float4(float4 in) {
+    return native_acosh(in);
+}
diff --git a/tests/src/android/renderscript/cts/TestNativeAcoshRelaxed.rs b/tests/src/android/renderscript/cts/TestNativeAcoshRelaxed.rs
new file mode 100644
index 0000000..9ea7b88
--- /dev/null
+++ b/tests/src/android/renderscript/cts/TestNativeAcoshRelaxed.rs
@@ -0,0 +1,20 @@
+/*
+ * 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.
+ */
+
+#include "TestNativeAcosh.rs"
+#pragma rs_fp_relaxed
+// Don't edit this file!  It is auto-generated by frameworks/rs/api/gen_runtime.
+
diff --git a/tests/src/android/renderscript/cts/TestNativeAcospi.rs b/tests/src/android/renderscript/cts/TestNativeAcospi.rs
new file mode 100644
index 0000000..d9d4ae5
--- /dev/null
+++ b/tests/src/android/renderscript/cts/TestNativeAcospi.rs
@@ -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.
+ */
+
+#pragma version(1)
+#pragma rs java_package_name(android.renderscript.cts)
+
+// Don't edit this file!  It is auto-generated by frameworks/rs/api/gen_runtime.
+
+
+float __attribute__((kernel)) testNativeAcospiFloatFloat(float inV) {
+    return native_acospi(inV);
+}
+
+float2 __attribute__((kernel)) testNativeAcospiFloat2Float2(float2 inV) {
+    return native_acospi(inV);
+}
+
+float3 __attribute__((kernel)) testNativeAcospiFloat3Float3(float3 inV) {
+    return native_acospi(inV);
+}
+
+float4 __attribute__((kernel)) testNativeAcospiFloat4Float4(float4 inV) {
+    return native_acospi(inV);
+}
diff --git a/tests/src/android/renderscript/cts/TestNativeAcospiRelaxed.rs b/tests/src/android/renderscript/cts/TestNativeAcospiRelaxed.rs
new file mode 100644
index 0000000..ff6025e
--- /dev/null
+++ b/tests/src/android/renderscript/cts/TestNativeAcospiRelaxed.rs
@@ -0,0 +1,20 @@
+/*
+ * 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.
+ */
+
+#include "TestNativeAcospi.rs"
+#pragma rs_fp_relaxed
+// Don't edit this file!  It is auto-generated by frameworks/rs/api/gen_runtime.
+
diff --git a/tests/src/android/renderscript/cts/TestNativeAsin.rs b/tests/src/android/renderscript/cts/TestNativeAsin.rs
new file mode 100644
index 0000000..28b8173
--- /dev/null
+++ b/tests/src/android/renderscript/cts/TestNativeAsin.rs
@@ -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.
+ */
+
+#pragma version(1)
+#pragma rs java_package_name(android.renderscript.cts)
+
+// Don't edit this file!  It is auto-generated by frameworks/rs/api/gen_runtime.
+
+
+float __attribute__((kernel)) testNativeAsinFloatFloat(float inV) {
+    return native_asin(inV);
+}
+
+float2 __attribute__((kernel)) testNativeAsinFloat2Float2(float2 inV) {
+    return native_asin(inV);
+}
+
+float3 __attribute__((kernel)) testNativeAsinFloat3Float3(float3 inV) {
+    return native_asin(inV);
+}
+
+float4 __attribute__((kernel)) testNativeAsinFloat4Float4(float4 inV) {
+    return native_asin(inV);
+}
diff --git a/tests/src/android/renderscript/cts/TestNativeAsinRelaxed.rs b/tests/src/android/renderscript/cts/TestNativeAsinRelaxed.rs
new file mode 100644
index 0000000..9d04b24
--- /dev/null
+++ b/tests/src/android/renderscript/cts/TestNativeAsinRelaxed.rs
@@ -0,0 +1,20 @@
+/*
+ * 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.
+ */
+
+#include "TestNativeAsin.rs"
+#pragma rs_fp_relaxed
+// Don't edit this file!  It is auto-generated by frameworks/rs/api/gen_runtime.
+
diff --git a/tests/src/android/renderscript/cts/TestNativeAsinh.rs b/tests/src/android/renderscript/cts/TestNativeAsinh.rs
new file mode 100644
index 0000000..2c40a7a
--- /dev/null
+++ b/tests/src/android/renderscript/cts/TestNativeAsinh.rs
@@ -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.
+ */
+
+#pragma version(1)
+#pragma rs java_package_name(android.renderscript.cts)
+
+// Don't edit this file!  It is auto-generated by frameworks/rs/api/gen_runtime.
+
+
+float __attribute__((kernel)) testNativeAsinhFloatFloat(float in) {
+    return native_asinh(in);
+}
+
+float2 __attribute__((kernel)) testNativeAsinhFloat2Float2(float2 in) {
+    return native_asinh(in);
+}
+
+float3 __attribute__((kernel)) testNativeAsinhFloat3Float3(float3 in) {
+    return native_asinh(in);
+}
+
+float4 __attribute__((kernel)) testNativeAsinhFloat4Float4(float4 in) {
+    return native_asinh(in);
+}
diff --git a/tests/src/android/renderscript/cts/TestNativeAsinhRelaxed.rs b/tests/src/android/renderscript/cts/TestNativeAsinhRelaxed.rs
new file mode 100644
index 0000000..1155084
--- /dev/null
+++ b/tests/src/android/renderscript/cts/TestNativeAsinhRelaxed.rs
@@ -0,0 +1,20 @@
+/*
+ * 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.
+ */
+
+#include "TestNativeAsinh.rs"
+#pragma rs_fp_relaxed
+// Don't edit this file!  It is auto-generated by frameworks/rs/api/gen_runtime.
+
diff --git a/tests/src/android/renderscript/cts/TestNativeAsinpi.rs b/tests/src/android/renderscript/cts/TestNativeAsinpi.rs
new file mode 100644
index 0000000..f08c041
--- /dev/null
+++ b/tests/src/android/renderscript/cts/TestNativeAsinpi.rs
@@ -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.
+ */
+
+#pragma version(1)
+#pragma rs java_package_name(android.renderscript.cts)
+
+// Don't edit this file!  It is auto-generated by frameworks/rs/api/gen_runtime.
+
+
+float __attribute__((kernel)) testNativeAsinpiFloatFloat(float inV) {
+    return native_asinpi(inV);
+}
+
+float2 __attribute__((kernel)) testNativeAsinpiFloat2Float2(float2 inV) {
+    return native_asinpi(inV);
+}
+
+float3 __attribute__((kernel)) testNativeAsinpiFloat3Float3(float3 inV) {
+    return native_asinpi(inV);
+}
+
+float4 __attribute__((kernel)) testNativeAsinpiFloat4Float4(float4 inV) {
+    return native_asinpi(inV);
+}
diff --git a/tests/src/android/renderscript/cts/TestNativeAsinpiRelaxed.rs b/tests/src/android/renderscript/cts/TestNativeAsinpiRelaxed.rs
new file mode 100644
index 0000000..83a97d3
--- /dev/null
+++ b/tests/src/android/renderscript/cts/TestNativeAsinpiRelaxed.rs
@@ -0,0 +1,20 @@
+/*
+ * 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.
+ */
+
+#include "TestNativeAsinpi.rs"
+#pragma rs_fp_relaxed
+// Don't edit this file!  It is auto-generated by frameworks/rs/api/gen_runtime.
+
diff --git a/tests/src/android/renderscript/cts/TestNativeAtan.rs b/tests/src/android/renderscript/cts/TestNativeAtan.rs
new file mode 100644
index 0000000..f15e5fd
--- /dev/null
+++ b/tests/src/android/renderscript/cts/TestNativeAtan.rs
@@ -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.
+ */
+
+#pragma version(1)
+#pragma rs java_package_name(android.renderscript.cts)
+
+// Don't edit this file!  It is auto-generated by frameworks/rs/api/gen_runtime.
+
+
+float __attribute__((kernel)) testNativeAtanFloatFloat(float inV) {
+    return native_atan(inV);
+}
+
+float2 __attribute__((kernel)) testNativeAtanFloat2Float2(float2 inV) {
+    return native_atan(inV);
+}
+
+float3 __attribute__((kernel)) testNativeAtanFloat3Float3(float3 inV) {
+    return native_atan(inV);
+}
+
+float4 __attribute__((kernel)) testNativeAtanFloat4Float4(float4 inV) {
+    return native_atan(inV);
+}
diff --git a/tests/src/android/renderscript/cts/TestNativeAtan2.rs b/tests/src/android/renderscript/cts/TestNativeAtan2.rs
new file mode 100644
index 0000000..3ab222c
--- /dev/null
+++ b/tests/src/android/renderscript/cts/TestNativeAtan2.rs
@@ -0,0 +1,42 @@
+/*
+ * 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.
+ */
+
+#pragma version(1)
+#pragma rs java_package_name(android.renderscript.cts)
+
+// Don't edit this file!  It is auto-generated by frameworks/rs/api/gen_runtime.
+
+rs_allocation gAllocInX;
+
+float __attribute__((kernel)) testNativeAtan2FloatFloatFloat(float inY, unsigned int x) {
+    float inX = rsGetElementAt_float(gAllocInX, x);
+    return native_atan2(inY, inX);
+}
+
+float2 __attribute__((kernel)) testNativeAtan2Float2Float2Float2(float2 inY, unsigned int x) {
+    float2 inX = rsGetElementAt_float2(gAllocInX, x);
+    return native_atan2(inY, inX);
+}
+
+float3 __attribute__((kernel)) testNativeAtan2Float3Float3Float3(float3 inY, unsigned int x) {
+    float3 inX = rsGetElementAt_float3(gAllocInX, x);
+    return native_atan2(inY, inX);
+}
+
+float4 __attribute__((kernel)) testNativeAtan2Float4Float4Float4(float4 inY, unsigned int x) {
+    float4 inX = rsGetElementAt_float4(gAllocInX, x);
+    return native_atan2(inY, inX);
+}
diff --git a/tests/src/android/renderscript/cts/TestNativeAtan2Relaxed.rs b/tests/src/android/renderscript/cts/TestNativeAtan2Relaxed.rs
new file mode 100644
index 0000000..40e8b3e
--- /dev/null
+++ b/tests/src/android/renderscript/cts/TestNativeAtan2Relaxed.rs
@@ -0,0 +1,20 @@
+/*
+ * 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.
+ */
+
+#include "TestNativeAtan2.rs"
+#pragma rs_fp_relaxed
+// Don't edit this file!  It is auto-generated by frameworks/rs/api/gen_runtime.
+
diff --git a/tests/src/android/renderscript/cts/TestNativeAtan2pi.rs b/tests/src/android/renderscript/cts/TestNativeAtan2pi.rs
new file mode 100644
index 0000000..ea3e3f9
--- /dev/null
+++ b/tests/src/android/renderscript/cts/TestNativeAtan2pi.rs
@@ -0,0 +1,42 @@
+/*
+ * 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.
+ */
+
+#pragma version(1)
+#pragma rs java_package_name(android.renderscript.cts)
+
+// Don't edit this file!  It is auto-generated by frameworks/rs/api/gen_runtime.
+
+rs_allocation gAllocInX;
+
+float __attribute__((kernel)) testNativeAtan2piFloatFloatFloat(float inY, unsigned int x) {
+    float inX = rsGetElementAt_float(gAllocInX, x);
+    return native_atan2pi(inY, inX);
+}
+
+float2 __attribute__((kernel)) testNativeAtan2piFloat2Float2Float2(float2 inY, unsigned int x) {
+    float2 inX = rsGetElementAt_float2(gAllocInX, x);
+    return native_atan2pi(inY, inX);
+}
+
+float3 __attribute__((kernel)) testNativeAtan2piFloat3Float3Float3(float3 inY, unsigned int x) {
+    float3 inX = rsGetElementAt_float3(gAllocInX, x);
+    return native_atan2pi(inY, inX);
+}
+
+float4 __attribute__((kernel)) testNativeAtan2piFloat4Float4Float4(float4 inY, unsigned int x) {
+    float4 inX = rsGetElementAt_float4(gAllocInX, x);
+    return native_atan2pi(inY, inX);
+}
diff --git a/tests/src/android/renderscript/cts/TestNativeAtan2piRelaxed.rs b/tests/src/android/renderscript/cts/TestNativeAtan2piRelaxed.rs
new file mode 100644
index 0000000..e301936
--- /dev/null
+++ b/tests/src/android/renderscript/cts/TestNativeAtan2piRelaxed.rs
@@ -0,0 +1,20 @@
+/*
+ * 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.
+ */
+
+#include "TestNativeAtan2pi.rs"
+#pragma rs_fp_relaxed
+// Don't edit this file!  It is auto-generated by frameworks/rs/api/gen_runtime.
+
diff --git a/tests/src/android/renderscript/cts/TestNativeAtanRelaxed.rs b/tests/src/android/renderscript/cts/TestNativeAtanRelaxed.rs
new file mode 100644
index 0000000..083f168
--- /dev/null
+++ b/tests/src/android/renderscript/cts/TestNativeAtanRelaxed.rs
@@ -0,0 +1,20 @@
+/*
+ * 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.
+ */
+
+#include "TestNativeAtan.rs"
+#pragma rs_fp_relaxed
+// Don't edit this file!  It is auto-generated by frameworks/rs/api/gen_runtime.
+
diff --git a/tests/src/android/renderscript/cts/TestNativeAtanh.rs b/tests/src/android/renderscript/cts/TestNativeAtanh.rs
new file mode 100644
index 0000000..eba5fd6
--- /dev/null
+++ b/tests/src/android/renderscript/cts/TestNativeAtanh.rs
@@ -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.
+ */
+
+#pragma version(1)
+#pragma rs java_package_name(android.renderscript.cts)
+
+// Don't edit this file!  It is auto-generated by frameworks/rs/api/gen_runtime.
+
+
+float __attribute__((kernel)) testNativeAtanhFloatFloat(float in) {
+    return native_atanh(in);
+}
+
+float2 __attribute__((kernel)) testNativeAtanhFloat2Float2(float2 in) {
+    return native_atanh(in);
+}
+
+float3 __attribute__((kernel)) testNativeAtanhFloat3Float3(float3 in) {
+    return native_atanh(in);
+}
+
+float4 __attribute__((kernel)) testNativeAtanhFloat4Float4(float4 in) {
+    return native_atanh(in);
+}
diff --git a/tests/src/android/renderscript/cts/TestNativeAtanhRelaxed.rs b/tests/src/android/renderscript/cts/TestNativeAtanhRelaxed.rs
new file mode 100644
index 0000000..40442c7
--- /dev/null
+++ b/tests/src/android/renderscript/cts/TestNativeAtanhRelaxed.rs
@@ -0,0 +1,20 @@
+/*
+ * 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.
+ */
+
+#include "TestNativeAtanh.rs"
+#pragma rs_fp_relaxed
+// Don't edit this file!  It is auto-generated by frameworks/rs/api/gen_runtime.
+
diff --git a/tests/src/android/renderscript/cts/TestNativeAtanpi.rs b/tests/src/android/renderscript/cts/TestNativeAtanpi.rs
new file mode 100644
index 0000000..19d5262
--- /dev/null
+++ b/tests/src/android/renderscript/cts/TestNativeAtanpi.rs
@@ -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.
+ */
+
+#pragma version(1)
+#pragma rs java_package_name(android.renderscript.cts)
+
+// Don't edit this file!  It is auto-generated by frameworks/rs/api/gen_runtime.
+
+
+float __attribute__((kernel)) testNativeAtanpiFloatFloat(float inV) {
+    return native_atanpi(inV);
+}
+
+float2 __attribute__((kernel)) testNativeAtanpiFloat2Float2(float2 inV) {
+    return native_atanpi(inV);
+}
+
+float3 __attribute__((kernel)) testNativeAtanpiFloat3Float3(float3 inV) {
+    return native_atanpi(inV);
+}
+
+float4 __attribute__((kernel)) testNativeAtanpiFloat4Float4(float4 inV) {
+    return native_atanpi(inV);
+}
diff --git a/tests/src/android/renderscript/cts/TestNativeAtanpiRelaxed.rs b/tests/src/android/renderscript/cts/TestNativeAtanpiRelaxed.rs
new file mode 100644
index 0000000..7758862
--- /dev/null
+++ b/tests/src/android/renderscript/cts/TestNativeAtanpiRelaxed.rs
@@ -0,0 +1,20 @@
+/*
+ * 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.
+ */
+
+#include "TestNativeAtanpi.rs"
+#pragma rs_fp_relaxed
+// Don't edit this file!  It is auto-generated by frameworks/rs/api/gen_runtime.
+
diff --git a/tests/src/android/renderscript/cts/TestNativeCbrt.rs b/tests/src/android/renderscript/cts/TestNativeCbrt.rs
new file mode 100644
index 0000000..828214b
--- /dev/null
+++ b/tests/src/android/renderscript/cts/TestNativeCbrt.rs
@@ -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.
+ */
+
+#pragma version(1)
+#pragma rs java_package_name(android.renderscript.cts)
+
+// Don't edit this file!  It is auto-generated by frameworks/rs/api/gen_runtime.
+
+
+float __attribute__((kernel)) testNativeCbrtFloatFloat(float in) {
+    return native_cbrt(in);
+}
+
+float2 __attribute__((kernel)) testNativeCbrtFloat2Float2(float2 in) {
+    return native_cbrt(in);
+}
+
+float3 __attribute__((kernel)) testNativeCbrtFloat3Float3(float3 in) {
+    return native_cbrt(in);
+}
+
+float4 __attribute__((kernel)) testNativeCbrtFloat4Float4(float4 in) {
+    return native_cbrt(in);
+}
diff --git a/tests/src/android/renderscript/cts/TestNativeCbrtRelaxed.rs b/tests/src/android/renderscript/cts/TestNativeCbrtRelaxed.rs
new file mode 100644
index 0000000..2a49b9e
--- /dev/null
+++ b/tests/src/android/renderscript/cts/TestNativeCbrtRelaxed.rs
@@ -0,0 +1,20 @@
+/*
+ * 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.
+ */
+
+#include "TestNativeCbrt.rs"
+#pragma rs_fp_relaxed
+// Don't edit this file!  It is auto-generated by frameworks/rs/api/gen_runtime.
+
diff --git a/tests/src/android/renderscript/cts/TestNativeCos.rs b/tests/src/android/renderscript/cts/TestNativeCos.rs
new file mode 100644
index 0000000..212c12e
--- /dev/null
+++ b/tests/src/android/renderscript/cts/TestNativeCos.rs
@@ -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.
+ */
+
+#pragma version(1)
+#pragma rs java_package_name(android.renderscript.cts)
+
+// Don't edit this file!  It is auto-generated by frameworks/rs/api/gen_runtime.
+
+
+float __attribute__((kernel)) testNativeCosFloatFloat(float in) {
+    return native_cos(in);
+}
+
+float2 __attribute__((kernel)) testNativeCosFloat2Float2(float2 in) {
+    return native_cos(in);
+}
+
+float3 __attribute__((kernel)) testNativeCosFloat3Float3(float3 in) {
+    return native_cos(in);
+}
+
+float4 __attribute__((kernel)) testNativeCosFloat4Float4(float4 in) {
+    return native_cos(in);
+}
diff --git a/tests/src/android/renderscript/cts/TestNativeCosRelaxed.rs b/tests/src/android/renderscript/cts/TestNativeCosRelaxed.rs
new file mode 100644
index 0000000..66f3fef
--- /dev/null
+++ b/tests/src/android/renderscript/cts/TestNativeCosRelaxed.rs
@@ -0,0 +1,20 @@
+/*
+ * 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.
+ */
+
+#include "TestNativeCos.rs"
+#pragma rs_fp_relaxed
+// Don't edit this file!  It is auto-generated by frameworks/rs/api/gen_runtime.
+
diff --git a/tests/src/android/renderscript/cts/TestNativeCosh.rs b/tests/src/android/renderscript/cts/TestNativeCosh.rs
new file mode 100644
index 0000000..f3635d8
--- /dev/null
+++ b/tests/src/android/renderscript/cts/TestNativeCosh.rs
@@ -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.
+ */
+
+#pragma version(1)
+#pragma rs java_package_name(android.renderscript.cts)
+
+// Don't edit this file!  It is auto-generated by frameworks/rs/api/gen_runtime.
+
+
+float __attribute__((kernel)) testNativeCoshFloatFloat(float in) {
+    return native_cosh(in);
+}
+
+float2 __attribute__((kernel)) testNativeCoshFloat2Float2(float2 in) {
+    return native_cosh(in);
+}
+
+float3 __attribute__((kernel)) testNativeCoshFloat3Float3(float3 in) {
+    return native_cosh(in);
+}
+
+float4 __attribute__((kernel)) testNativeCoshFloat4Float4(float4 in) {
+    return native_cosh(in);
+}
diff --git a/tests/src/android/renderscript/cts/TestNativeCoshRelaxed.rs b/tests/src/android/renderscript/cts/TestNativeCoshRelaxed.rs
new file mode 100644
index 0000000..138b5c2
--- /dev/null
+++ b/tests/src/android/renderscript/cts/TestNativeCoshRelaxed.rs
@@ -0,0 +1,20 @@
+/*
+ * 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.
+ */
+
+#include "TestNativeCosh.rs"
+#pragma rs_fp_relaxed
+// Don't edit this file!  It is auto-generated by frameworks/rs/api/gen_runtime.
+
diff --git a/tests/src/android/renderscript/cts/TestNativeCospi.rs b/tests/src/android/renderscript/cts/TestNativeCospi.rs
new file mode 100644
index 0000000..471403f
--- /dev/null
+++ b/tests/src/android/renderscript/cts/TestNativeCospi.rs
@@ -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.
+ */
+
+#pragma version(1)
+#pragma rs java_package_name(android.renderscript.cts)
+
+// Don't edit this file!  It is auto-generated by frameworks/rs/api/gen_runtime.
+
+
+float __attribute__((kernel)) testNativeCospiFloatFloat(float in) {
+    return native_cospi(in);
+}
+
+float2 __attribute__((kernel)) testNativeCospiFloat2Float2(float2 in) {
+    return native_cospi(in);
+}
+
+float3 __attribute__((kernel)) testNativeCospiFloat3Float3(float3 in) {
+    return native_cospi(in);
+}
+
+float4 __attribute__((kernel)) testNativeCospiFloat4Float4(float4 in) {
+    return native_cospi(in);
+}
diff --git a/tests/src/android/renderscript/cts/TestNativeCospiRelaxed.rs b/tests/src/android/renderscript/cts/TestNativeCospiRelaxed.rs
new file mode 100644
index 0000000..f72429e
--- /dev/null
+++ b/tests/src/android/renderscript/cts/TestNativeCospiRelaxed.rs
@@ -0,0 +1,20 @@
+/*
+ * 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.
+ */
+
+#include "TestNativeCospi.rs"
+#pragma rs_fp_relaxed
+// Don't edit this file!  It is auto-generated by frameworks/rs/api/gen_runtime.
+
diff --git a/tests/src/android/renderscript/cts/TestNativeDistance.rs b/tests/src/android/renderscript/cts/TestNativeDistance.rs
new file mode 100644
index 0000000..d9c215c
--- /dev/null
+++ b/tests/src/android/renderscript/cts/TestNativeDistance.rs
@@ -0,0 +1,42 @@
+/*
+ * 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.
+ */
+
+#pragma version(1)
+#pragma rs java_package_name(android.renderscript.cts)
+
+// Don't edit this file!  It is auto-generated by frameworks/rs/api/gen_runtime.
+
+rs_allocation gAllocInRhs;
+
+float __attribute__((kernel)) testNativeDistanceFloatFloatFloat(float inLhs, unsigned int x) {
+    float inRhs = rsGetElementAt_float(gAllocInRhs, x);
+    return native_distance(inLhs, inRhs);
+}
+
+float __attribute__((kernel)) testNativeDistanceFloat2Float2Float(float2 inLhs, unsigned int x) {
+    float2 inRhs = rsGetElementAt_float2(gAllocInRhs, x);
+    return native_distance(inLhs, inRhs);
+}
+
+float __attribute__((kernel)) testNativeDistanceFloat3Float3Float(float3 inLhs, unsigned int x) {
+    float3 inRhs = rsGetElementAt_float3(gAllocInRhs, x);
+    return native_distance(inLhs, inRhs);
+}
+
+float __attribute__((kernel)) testNativeDistanceFloat4Float4Float(float4 inLhs, unsigned int x) {
+    float4 inRhs = rsGetElementAt_float4(gAllocInRhs, x);
+    return native_distance(inLhs, inRhs);
+}
diff --git a/tests/src/android/renderscript/cts/TestNativeDistanceRelaxed.rs b/tests/src/android/renderscript/cts/TestNativeDistanceRelaxed.rs
new file mode 100644
index 0000000..167b79a
--- /dev/null
+++ b/tests/src/android/renderscript/cts/TestNativeDistanceRelaxed.rs
@@ -0,0 +1,20 @@
+/*
+ * 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.
+ */
+
+#include "TestNativeDistance.rs"
+#pragma rs_fp_relaxed
+// Don't edit this file!  It is auto-generated by frameworks/rs/api/gen_runtime.
+
diff --git a/tests/src/android/renderscript/cts/TestNativeDivide.rs b/tests/src/android/renderscript/cts/TestNativeDivide.rs
new file mode 100644
index 0000000..ee36b50
--- /dev/null
+++ b/tests/src/android/renderscript/cts/TestNativeDivide.rs
@@ -0,0 +1,42 @@
+/*
+ * 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.
+ */
+
+#pragma version(1)
+#pragma rs java_package_name(android.renderscript.cts)
+
+// Don't edit this file!  It is auto-generated by frameworks/rs/api/gen_runtime.
+
+rs_allocation gAllocInRhs;
+
+float __attribute__((kernel)) testNativeDivideFloatFloatFloat(float inLhs, unsigned int x) {
+    float inRhs = rsGetElementAt_float(gAllocInRhs, x);
+    return native_divide(inLhs, inRhs);
+}
+
+float2 __attribute__((kernel)) testNativeDivideFloat2Float2Float2(float2 inLhs, unsigned int x) {
+    float2 inRhs = rsGetElementAt_float2(gAllocInRhs, x);
+    return native_divide(inLhs, inRhs);
+}
+
+float3 __attribute__((kernel)) testNativeDivideFloat3Float3Float3(float3 inLhs, unsigned int x) {
+    float3 inRhs = rsGetElementAt_float3(gAllocInRhs, x);
+    return native_divide(inLhs, inRhs);
+}
+
+float4 __attribute__((kernel)) testNativeDivideFloat4Float4Float4(float4 inLhs, unsigned int x) {
+    float4 inRhs = rsGetElementAt_float4(gAllocInRhs, x);
+    return native_divide(inLhs, inRhs);
+}
diff --git a/tests/src/android/renderscript/cts/TestNativeDivideRelaxed.rs b/tests/src/android/renderscript/cts/TestNativeDivideRelaxed.rs
new file mode 100644
index 0000000..b6d5004
--- /dev/null
+++ b/tests/src/android/renderscript/cts/TestNativeDivideRelaxed.rs
@@ -0,0 +1,20 @@
+/*
+ * 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.
+ */
+
+#include "TestNativeDivide.rs"
+#pragma rs_fp_relaxed
+// Don't edit this file!  It is auto-generated by frameworks/rs/api/gen_runtime.
+
diff --git a/tests/src/android/renderscript/cts/TestNativeExpm1.rs b/tests/src/android/renderscript/cts/TestNativeExpm1.rs
new file mode 100644
index 0000000..6d265d6
--- /dev/null
+++ b/tests/src/android/renderscript/cts/TestNativeExpm1.rs
@@ -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.
+ */
+
+#pragma version(1)
+#pragma rs java_package_name(android.renderscript.cts)
+
+// Don't edit this file!  It is auto-generated by frameworks/rs/api/gen_runtime.
+
+
+float __attribute__((kernel)) testNativeExpm1FloatFloat(float in) {
+    return native_expm1(in);
+}
+
+float2 __attribute__((kernel)) testNativeExpm1Float2Float2(float2 in) {
+    return native_expm1(in);
+}
+
+float3 __attribute__((kernel)) testNativeExpm1Float3Float3(float3 in) {
+    return native_expm1(in);
+}
+
+float4 __attribute__((kernel)) testNativeExpm1Float4Float4(float4 in) {
+    return native_expm1(in);
+}
diff --git a/tests/src/android/renderscript/cts/TestNativeExpm1Relaxed.rs b/tests/src/android/renderscript/cts/TestNativeExpm1Relaxed.rs
new file mode 100644
index 0000000..a74a0d1
--- /dev/null
+++ b/tests/src/android/renderscript/cts/TestNativeExpm1Relaxed.rs
@@ -0,0 +1,20 @@
+/*
+ * 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.
+ */
+
+#include "TestNativeExpm1.rs"
+#pragma rs_fp_relaxed
+// Don't edit this file!  It is auto-generated by frameworks/rs/api/gen_runtime.
+
diff --git a/tests/src/android/renderscript/cts/TestNativeHypot.rs b/tests/src/android/renderscript/cts/TestNativeHypot.rs
new file mode 100644
index 0000000..b4e8a7c
--- /dev/null
+++ b/tests/src/android/renderscript/cts/TestNativeHypot.rs
@@ -0,0 +1,42 @@
+/*
+ * 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.
+ */
+
+#pragma version(1)
+#pragma rs java_package_name(android.renderscript.cts)
+
+// Don't edit this file!  It is auto-generated by frameworks/rs/api/gen_runtime.
+
+rs_allocation gAllocInY;
+
+float __attribute__((kernel)) testNativeHypotFloatFloatFloat(float inX, unsigned int x) {
+    float inY = rsGetElementAt_float(gAllocInY, x);
+    return native_hypot(inX, inY);
+}
+
+float2 __attribute__((kernel)) testNativeHypotFloat2Float2Float2(float2 inX, unsigned int x) {
+    float2 inY = rsGetElementAt_float2(gAllocInY, x);
+    return native_hypot(inX, inY);
+}
+
+float3 __attribute__((kernel)) testNativeHypotFloat3Float3Float3(float3 inX, unsigned int x) {
+    float3 inY = rsGetElementAt_float3(gAllocInY, x);
+    return native_hypot(inX, inY);
+}
+
+float4 __attribute__((kernel)) testNativeHypotFloat4Float4Float4(float4 inX, unsigned int x) {
+    float4 inY = rsGetElementAt_float4(gAllocInY, x);
+    return native_hypot(inX, inY);
+}
diff --git a/tests/src/android/renderscript/cts/TestNativeHypotRelaxed.rs b/tests/src/android/renderscript/cts/TestNativeHypotRelaxed.rs
new file mode 100644
index 0000000..79aa107
--- /dev/null
+++ b/tests/src/android/renderscript/cts/TestNativeHypotRelaxed.rs
@@ -0,0 +1,20 @@
+/*
+ * 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.
+ */
+
+#include "TestNativeHypot.rs"
+#pragma rs_fp_relaxed
+// Don't edit this file!  It is auto-generated by frameworks/rs/api/gen_runtime.
+
diff --git a/tests/src/android/renderscript/cts/TestNativeLength.rs b/tests/src/android/renderscript/cts/TestNativeLength.rs
new file mode 100644
index 0000000..ae30d2d
--- /dev/null
+++ b/tests/src/android/renderscript/cts/TestNativeLength.rs
@@ -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.
+ */
+
+#pragma version(1)
+#pragma rs java_package_name(android.renderscript.cts)
+
+// Don't edit this file!  It is auto-generated by frameworks/rs/api/gen_runtime.
+
+
+float __attribute__((kernel)) testNativeLengthFloatFloat(float inV) {
+    return native_length(inV);
+}
+
+float __attribute__((kernel)) testNativeLengthFloat2Float(float2 inV) {
+    return native_length(inV);
+}
+
+float __attribute__((kernel)) testNativeLengthFloat3Float(float3 inV) {
+    return native_length(inV);
+}
+
+float __attribute__((kernel)) testNativeLengthFloat4Float(float4 inV) {
+    return native_length(inV);
+}
diff --git a/tests/src/android/renderscript/cts/TestNativeLengthRelaxed.rs b/tests/src/android/renderscript/cts/TestNativeLengthRelaxed.rs
new file mode 100644
index 0000000..cb03c1e
--- /dev/null
+++ b/tests/src/android/renderscript/cts/TestNativeLengthRelaxed.rs
@@ -0,0 +1,20 @@
+/*
+ * 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.
+ */
+
+#include "TestNativeLength.rs"
+#pragma rs_fp_relaxed
+// Don't edit this file!  It is auto-generated by frameworks/rs/api/gen_runtime.
+
diff --git a/tests/src/android/renderscript/cts/TestNativeLog1p.rs b/tests/src/android/renderscript/cts/TestNativeLog1p.rs
new file mode 100644
index 0000000..8d9cdd2
--- /dev/null
+++ b/tests/src/android/renderscript/cts/TestNativeLog1p.rs
@@ -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.
+ */
+
+#pragma version(1)
+#pragma rs java_package_name(android.renderscript.cts)
+
+// Don't edit this file!  It is auto-generated by frameworks/rs/api/gen_runtime.
+
+
+float __attribute__((kernel)) testNativeLog1pFloatFloat(float in) {
+    return native_log1p(in);
+}
+
+float2 __attribute__((kernel)) testNativeLog1pFloat2Float2(float2 in) {
+    return native_log1p(in);
+}
+
+float3 __attribute__((kernel)) testNativeLog1pFloat3Float3(float3 in) {
+    return native_log1p(in);
+}
+
+float4 __attribute__((kernel)) testNativeLog1pFloat4Float4(float4 in) {
+    return native_log1p(in);
+}
diff --git a/tests/src/android/renderscript/cts/TestNativeLog1pRelaxed.rs b/tests/src/android/renderscript/cts/TestNativeLog1pRelaxed.rs
new file mode 100644
index 0000000..3411c6e
--- /dev/null
+++ b/tests/src/android/renderscript/cts/TestNativeLog1pRelaxed.rs
@@ -0,0 +1,20 @@
+/*
+ * 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.
+ */
+
+#include "TestNativeLog1p.rs"
+#pragma rs_fp_relaxed
+// Don't edit this file!  It is auto-generated by frameworks/rs/api/gen_runtime.
+
diff --git a/tests/src/android/renderscript/cts/TestNativeNormalize.rs b/tests/src/android/renderscript/cts/TestNativeNormalize.rs
new file mode 100644
index 0000000..89e0b52
--- /dev/null
+++ b/tests/src/android/renderscript/cts/TestNativeNormalize.rs
@@ -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.
+ */
+
+#pragma version(1)
+#pragma rs java_package_name(android.renderscript.cts)
+
+// Don't edit this file!  It is auto-generated by frameworks/rs/api/gen_runtime.
+
+
+float __attribute__((kernel)) testNativeNormalizeFloatFloat(float inV) {
+    return native_normalize(inV);
+}
+
+float2 __attribute__((kernel)) testNativeNormalizeFloat2Float2(float2 inV) {
+    return native_normalize(inV);
+}
+
+float3 __attribute__((kernel)) testNativeNormalizeFloat3Float3(float3 inV) {
+    return native_normalize(inV);
+}
+
+float4 __attribute__((kernel)) testNativeNormalizeFloat4Float4(float4 inV) {
+    return native_normalize(inV);
+}
diff --git a/tests/src/android/renderscript/cts/TestNativeNormalizeRelaxed.rs b/tests/src/android/renderscript/cts/TestNativeNormalizeRelaxed.rs
new file mode 100644
index 0000000..9050ff3
--- /dev/null
+++ b/tests/src/android/renderscript/cts/TestNativeNormalizeRelaxed.rs
@@ -0,0 +1,20 @@
+/*
+ * 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.
+ */
+
+#include "TestNativeNormalize.rs"
+#pragma rs_fp_relaxed
+// Don't edit this file!  It is auto-generated by frameworks/rs/api/gen_runtime.
+
diff --git a/tests/src/android/renderscript/cts/TestNativeRecip.rs b/tests/src/android/renderscript/cts/TestNativeRecip.rs
new file mode 100644
index 0000000..01220dc
--- /dev/null
+++ b/tests/src/android/renderscript/cts/TestNativeRecip.rs
@@ -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.
+ */
+
+#pragma version(1)
+#pragma rs java_package_name(android.renderscript.cts)
+
+// Don't edit this file!  It is auto-generated by frameworks/rs/api/gen_runtime.
+
+
+float __attribute__((kernel)) testNativeRecipFloatFloat(float inV) {
+    return native_recip(inV);
+}
+
+float2 __attribute__((kernel)) testNativeRecipFloat2Float2(float2 inV) {
+    return native_recip(inV);
+}
+
+float3 __attribute__((kernel)) testNativeRecipFloat3Float3(float3 inV) {
+    return native_recip(inV);
+}
+
+float4 __attribute__((kernel)) testNativeRecipFloat4Float4(float4 inV) {
+    return native_recip(inV);
+}
diff --git a/tests/src/android/renderscript/cts/TestNativeRecipRelaxed.rs b/tests/src/android/renderscript/cts/TestNativeRecipRelaxed.rs
new file mode 100644
index 0000000..f2cbc83
--- /dev/null
+++ b/tests/src/android/renderscript/cts/TestNativeRecipRelaxed.rs
@@ -0,0 +1,20 @@
+/*
+ * 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.
+ */
+
+#include "TestNativeRecip.rs"
+#pragma rs_fp_relaxed
+// Don't edit this file!  It is auto-generated by frameworks/rs/api/gen_runtime.
+
diff --git a/tests/src/android/renderscript/cts/TestNativeRootn.rs b/tests/src/android/renderscript/cts/TestNativeRootn.rs
new file mode 100644
index 0000000..1aeb2b2
--- /dev/null
+++ b/tests/src/android/renderscript/cts/TestNativeRootn.rs
@@ -0,0 +1,42 @@
+/*
+ * 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.
+ */
+
+#pragma version(1)
+#pragma rs java_package_name(android.renderscript.cts)
+
+// Don't edit this file!  It is auto-generated by frameworks/rs/api/gen_runtime.
+
+rs_allocation gAllocInN;
+
+float __attribute__((kernel)) testNativeRootnFloatIntFloat(float inV, unsigned int x) {
+    int inN = rsGetElementAt_int(gAllocInN, x);
+    return native_rootn(inV, inN);
+}
+
+float2 __attribute__((kernel)) testNativeRootnFloat2Int2Float2(float2 inV, unsigned int x) {
+    int2 inN = rsGetElementAt_int2(gAllocInN, x);
+    return native_rootn(inV, inN);
+}
+
+float3 __attribute__((kernel)) testNativeRootnFloat3Int3Float3(float3 inV, unsigned int x) {
+    int3 inN = rsGetElementAt_int3(gAllocInN, x);
+    return native_rootn(inV, inN);
+}
+
+float4 __attribute__((kernel)) testNativeRootnFloat4Int4Float4(float4 inV, unsigned int x) {
+    int4 inN = rsGetElementAt_int4(gAllocInN, x);
+    return native_rootn(inV, inN);
+}
diff --git a/tests/src/android/renderscript/cts/TestNativeRootnRelaxed.rs b/tests/src/android/renderscript/cts/TestNativeRootnRelaxed.rs
new file mode 100644
index 0000000..3b05992
--- /dev/null
+++ b/tests/src/android/renderscript/cts/TestNativeRootnRelaxed.rs
@@ -0,0 +1,20 @@
+/*
+ * 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.
+ */
+
+#include "TestNativeRootn.rs"
+#pragma rs_fp_relaxed
+// Don't edit this file!  It is auto-generated by frameworks/rs/api/gen_runtime.
+
diff --git a/tests/src/android/renderscript/cts/TestNativeRsqrt.rs b/tests/src/android/renderscript/cts/TestNativeRsqrt.rs
new file mode 100644
index 0000000..af291cb
--- /dev/null
+++ b/tests/src/android/renderscript/cts/TestNativeRsqrt.rs
@@ -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.
+ */
+
+#pragma version(1)
+#pragma rs java_package_name(android.renderscript.cts)
+
+// Don't edit this file!  It is auto-generated by frameworks/rs/api/gen_runtime.
+
+
+float __attribute__((kernel)) testNativeRsqrtFloatFloat(float in) {
+    return native_rsqrt(in);
+}
+
+float2 __attribute__((kernel)) testNativeRsqrtFloat2Float2(float2 in) {
+    return native_rsqrt(in);
+}
+
+float3 __attribute__((kernel)) testNativeRsqrtFloat3Float3(float3 in) {
+    return native_rsqrt(in);
+}
+
+float4 __attribute__((kernel)) testNativeRsqrtFloat4Float4(float4 in) {
+    return native_rsqrt(in);
+}
diff --git a/tests/src/android/renderscript/cts/TestNativeRsqrtRelaxed.rs b/tests/src/android/renderscript/cts/TestNativeRsqrtRelaxed.rs
new file mode 100644
index 0000000..c18cc22
--- /dev/null
+++ b/tests/src/android/renderscript/cts/TestNativeRsqrtRelaxed.rs
@@ -0,0 +1,20 @@
+/*
+ * 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.
+ */
+
+#include "TestNativeRsqrt.rs"
+#pragma rs_fp_relaxed
+// Don't edit this file!  It is auto-generated by frameworks/rs/api/gen_runtime.
+
diff --git a/tests/src/android/renderscript/cts/TestNativeSin.rs b/tests/src/android/renderscript/cts/TestNativeSin.rs
new file mode 100644
index 0000000..1af8929
--- /dev/null
+++ b/tests/src/android/renderscript/cts/TestNativeSin.rs
@@ -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.
+ */
+
+#pragma version(1)
+#pragma rs java_package_name(android.renderscript.cts)
+
+// Don't edit this file!  It is auto-generated by frameworks/rs/api/gen_runtime.
+
+
+float __attribute__((kernel)) testNativeSinFloatFloat(float in) {
+    return native_sin(in);
+}
+
+float2 __attribute__((kernel)) testNativeSinFloat2Float2(float2 in) {
+    return native_sin(in);
+}
+
+float3 __attribute__((kernel)) testNativeSinFloat3Float3(float3 in) {
+    return native_sin(in);
+}
+
+float4 __attribute__((kernel)) testNativeSinFloat4Float4(float4 in) {
+    return native_sin(in);
+}
diff --git a/tests/src/android/renderscript/cts/TestNativeSinRelaxed.rs b/tests/src/android/renderscript/cts/TestNativeSinRelaxed.rs
new file mode 100644
index 0000000..e3dbf27
--- /dev/null
+++ b/tests/src/android/renderscript/cts/TestNativeSinRelaxed.rs
@@ -0,0 +1,20 @@
+/*
+ * 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.
+ */
+
+#include "TestNativeSin.rs"
+#pragma rs_fp_relaxed
+// Don't edit this file!  It is auto-generated by frameworks/rs/api/gen_runtime.
+
diff --git a/tests/src/android/renderscript/cts/TestNativeSincos.rs b/tests/src/android/renderscript/cts/TestNativeSincos.rs
new file mode 100644
index 0000000..8148c67
--- /dev/null
+++ b/tests/src/android/renderscript/cts/TestNativeSincos.rs
@@ -0,0 +1,50 @@
+/*
+ * 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.
+ */
+
+#pragma version(1)
+#pragma rs java_package_name(android.renderscript.cts)
+
+// Don't edit this file!  It is auto-generated by frameworks/rs/api/gen_runtime.
+
+rs_allocation gAllocOutCosptr;
+
+float __attribute__((kernel)) testNativeSincosFloatFloatFloat(float inV, unsigned int x) {
+    float outCosptr = 0;
+    float out = native_sincos(inV, &outCosptr);
+    rsSetElementAt_float(gAllocOutCosptr, outCosptr, x);
+    return out;
+}
+
+float2 __attribute__((kernel)) testNativeSincosFloat2Float2Float2(float2 inV, unsigned int x) {
+    float2 outCosptr = 0;
+    float2 out = native_sincos(inV, &outCosptr);
+    rsSetElementAt_float2(gAllocOutCosptr, outCosptr, x);
+    return out;
+}
+
+float3 __attribute__((kernel)) testNativeSincosFloat3Float3Float3(float3 inV, unsigned int x) {
+    float3 outCosptr = 0;
+    float3 out = native_sincos(inV, &outCosptr);
+    rsSetElementAt_float3(gAllocOutCosptr, outCosptr, x);
+    return out;
+}
+
+float4 __attribute__((kernel)) testNativeSincosFloat4Float4Float4(float4 inV, unsigned int x) {
+    float4 outCosptr = 0;
+    float4 out = native_sincos(inV, &outCosptr);
+    rsSetElementAt_float4(gAllocOutCosptr, outCosptr, x);
+    return out;
+}
diff --git a/tests/src/android/renderscript/cts/TestNativeSincosRelaxed.rs b/tests/src/android/renderscript/cts/TestNativeSincosRelaxed.rs
new file mode 100644
index 0000000..175e265
--- /dev/null
+++ b/tests/src/android/renderscript/cts/TestNativeSincosRelaxed.rs
@@ -0,0 +1,20 @@
+/*
+ * 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.
+ */
+
+#include "TestNativeSincos.rs"
+#pragma rs_fp_relaxed
+// Don't edit this file!  It is auto-generated by frameworks/rs/api/gen_runtime.
+
diff --git a/tests/src/android/renderscript/cts/TestNativeSinh.rs b/tests/src/android/renderscript/cts/TestNativeSinh.rs
new file mode 100644
index 0000000..20728d5
--- /dev/null
+++ b/tests/src/android/renderscript/cts/TestNativeSinh.rs
@@ -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.
+ */
+
+#pragma version(1)
+#pragma rs java_package_name(android.renderscript.cts)
+
+// Don't edit this file!  It is auto-generated by frameworks/rs/api/gen_runtime.
+
+
+float __attribute__((kernel)) testNativeSinhFloatFloat(float in) {
+    return native_sinh(in);
+}
+
+float2 __attribute__((kernel)) testNativeSinhFloat2Float2(float2 in) {
+    return native_sinh(in);
+}
+
+float3 __attribute__((kernel)) testNativeSinhFloat3Float3(float3 in) {
+    return native_sinh(in);
+}
+
+float4 __attribute__((kernel)) testNativeSinhFloat4Float4(float4 in) {
+    return native_sinh(in);
+}
diff --git a/tests/src/android/renderscript/cts/TestNativeSinhRelaxed.rs b/tests/src/android/renderscript/cts/TestNativeSinhRelaxed.rs
new file mode 100644
index 0000000..7ba4ce8
--- /dev/null
+++ b/tests/src/android/renderscript/cts/TestNativeSinhRelaxed.rs
@@ -0,0 +1,20 @@
+/*
+ * 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.
+ */
+
+#include "TestNativeSinh.rs"
+#pragma rs_fp_relaxed
+// Don't edit this file!  It is auto-generated by frameworks/rs/api/gen_runtime.
+
diff --git a/tests/src/android/renderscript/cts/TestNativeSinpi.rs b/tests/src/android/renderscript/cts/TestNativeSinpi.rs
new file mode 100644
index 0000000..1b17471
--- /dev/null
+++ b/tests/src/android/renderscript/cts/TestNativeSinpi.rs
@@ -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.
+ */
+
+#pragma version(1)
+#pragma rs java_package_name(android.renderscript.cts)
+
+// Don't edit this file!  It is auto-generated by frameworks/rs/api/gen_runtime.
+
+
+float __attribute__((kernel)) testNativeSinpiFloatFloat(float in) {
+    return native_sinpi(in);
+}
+
+float2 __attribute__((kernel)) testNativeSinpiFloat2Float2(float2 in) {
+    return native_sinpi(in);
+}
+
+float3 __attribute__((kernel)) testNativeSinpiFloat3Float3(float3 in) {
+    return native_sinpi(in);
+}
+
+float4 __attribute__((kernel)) testNativeSinpiFloat4Float4(float4 in) {
+    return native_sinpi(in);
+}
diff --git a/tests/src/android/renderscript/cts/TestNativeSinpiRelaxed.rs b/tests/src/android/renderscript/cts/TestNativeSinpiRelaxed.rs
new file mode 100644
index 0000000..2bc871f
--- /dev/null
+++ b/tests/src/android/renderscript/cts/TestNativeSinpiRelaxed.rs
@@ -0,0 +1,20 @@
+/*
+ * 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.
+ */
+
+#include "TestNativeSinpi.rs"
+#pragma rs_fp_relaxed
+// Don't edit this file!  It is auto-generated by frameworks/rs/api/gen_runtime.
+
diff --git a/tests/src/android/renderscript/cts/TestNativeSqrt.rs b/tests/src/android/renderscript/cts/TestNativeSqrt.rs
new file mode 100644
index 0000000..2ed1cc8
--- /dev/null
+++ b/tests/src/android/renderscript/cts/TestNativeSqrt.rs
@@ -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.
+ */
+
+#pragma version(1)
+#pragma rs java_package_name(android.renderscript.cts)
+
+// Don't edit this file!  It is auto-generated by frameworks/rs/api/gen_runtime.
+
+
+float __attribute__((kernel)) testNativeSqrtFloatFloat(float in) {
+    return native_sqrt(in);
+}
+
+float2 __attribute__((kernel)) testNativeSqrtFloat2Float2(float2 in) {
+    return native_sqrt(in);
+}
+
+float3 __attribute__((kernel)) testNativeSqrtFloat3Float3(float3 in) {
+    return native_sqrt(in);
+}
+
+float4 __attribute__((kernel)) testNativeSqrtFloat4Float4(float4 in) {
+    return native_sqrt(in);
+}
diff --git a/tests/src/android/renderscript/cts/TestNativeSqrtRelaxed.rs b/tests/src/android/renderscript/cts/TestNativeSqrtRelaxed.rs
new file mode 100644
index 0000000..aa39435
--- /dev/null
+++ b/tests/src/android/renderscript/cts/TestNativeSqrtRelaxed.rs
@@ -0,0 +1,20 @@
+/*
+ * 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.
+ */
+
+#include "TestNativeSqrt.rs"
+#pragma rs_fp_relaxed
+// Don't edit this file!  It is auto-generated by frameworks/rs/api/gen_runtime.
+
diff --git a/tests/src/android/renderscript/cts/TestNativeTan.rs b/tests/src/android/renderscript/cts/TestNativeTan.rs
new file mode 100644
index 0000000..000bc74
--- /dev/null
+++ b/tests/src/android/renderscript/cts/TestNativeTan.rs
@@ -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.
+ */
+
+#pragma version(1)
+#pragma rs java_package_name(android.renderscript.cts)
+
+// Don't edit this file!  It is auto-generated by frameworks/rs/api/gen_runtime.
+
+
+float __attribute__((kernel)) testNativeTanFloatFloat(float in) {
+    return native_tan(in);
+}
+
+float2 __attribute__((kernel)) testNativeTanFloat2Float2(float2 in) {
+    return native_tan(in);
+}
+
+float3 __attribute__((kernel)) testNativeTanFloat3Float3(float3 in) {
+    return native_tan(in);
+}
+
+float4 __attribute__((kernel)) testNativeTanFloat4Float4(float4 in) {
+    return native_tan(in);
+}
diff --git a/tests/src/android/renderscript/cts/TestNativeTanRelaxed.rs b/tests/src/android/renderscript/cts/TestNativeTanRelaxed.rs
new file mode 100644
index 0000000..9ec6688
--- /dev/null
+++ b/tests/src/android/renderscript/cts/TestNativeTanRelaxed.rs
@@ -0,0 +1,20 @@
+/*
+ * 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.
+ */
+
+#include "TestNativeTan.rs"
+#pragma rs_fp_relaxed
+// Don't edit this file!  It is auto-generated by frameworks/rs/api/gen_runtime.
+
diff --git a/tests/src/android/renderscript/cts/TestNativeTanh.rs b/tests/src/android/renderscript/cts/TestNativeTanh.rs
new file mode 100644
index 0000000..a7bfcfd
--- /dev/null
+++ b/tests/src/android/renderscript/cts/TestNativeTanh.rs
@@ -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.
+ */
+
+#pragma version(1)
+#pragma rs java_package_name(android.renderscript.cts)
+
+// Don't edit this file!  It is auto-generated by frameworks/rs/api/gen_runtime.
+
+
+float __attribute__((kernel)) testNativeTanhFloatFloat(float in) {
+    return native_tanh(in);
+}
+
+float2 __attribute__((kernel)) testNativeTanhFloat2Float2(float2 in) {
+    return native_tanh(in);
+}
+
+float3 __attribute__((kernel)) testNativeTanhFloat3Float3(float3 in) {
+    return native_tanh(in);
+}
+
+float4 __attribute__((kernel)) testNativeTanhFloat4Float4(float4 in) {
+    return native_tanh(in);
+}
diff --git a/tests/src/android/renderscript/cts/TestNativeTanhRelaxed.rs b/tests/src/android/renderscript/cts/TestNativeTanhRelaxed.rs
new file mode 100644
index 0000000..27c9275
--- /dev/null
+++ b/tests/src/android/renderscript/cts/TestNativeTanhRelaxed.rs
@@ -0,0 +1,20 @@
+/*
+ * 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.
+ */
+
+#include "TestNativeTanh.rs"
+#pragma rs_fp_relaxed
+// Don't edit this file!  It is auto-generated by frameworks/rs/api/gen_runtime.
+
diff --git a/tests/src/android/renderscript/cts/TestNativeTanpi.rs b/tests/src/android/renderscript/cts/TestNativeTanpi.rs
new file mode 100644
index 0000000..ae1f633
--- /dev/null
+++ b/tests/src/android/renderscript/cts/TestNativeTanpi.rs
@@ -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.
+ */
+
+#pragma version(1)
+#pragma rs java_package_name(android.renderscript.cts)
+
+// Don't edit this file!  It is auto-generated by frameworks/rs/api/gen_runtime.
+
+
+float __attribute__((kernel)) testNativeTanpiFloatFloat(float in) {
+    return native_tanpi(in);
+}
+
+float2 __attribute__((kernel)) testNativeTanpiFloat2Float2(float2 in) {
+    return native_tanpi(in);
+}
+
+float3 __attribute__((kernel)) testNativeTanpiFloat3Float3(float3 in) {
+    return native_tanpi(in);
+}
+
+float4 __attribute__((kernel)) testNativeTanpiFloat4Float4(float4 in) {
+    return native_tanpi(in);
+}
diff --git a/tests/src/android/renderscript/cts/TestNativeTanpiRelaxed.rs b/tests/src/android/renderscript/cts/TestNativeTanpiRelaxed.rs
new file mode 100644
index 0000000..921fa3c
--- /dev/null
+++ b/tests/src/android/renderscript/cts/TestNativeTanpiRelaxed.rs
@@ -0,0 +1,20 @@
+/*
+ * 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.
+ */
+
+#include "TestNativeTanpi.rs"
+#pragma rs_fp_relaxed
+// Don't edit this file!  It is auto-generated by frameworks/rs/api/gen_runtime.
+
diff --git a/tests/src/android/renderscript/cts/TestStep.rs b/tests/src/android/renderscript/cts/TestStep.rs
index 41f8462..b815d52 100644
--- a/tests/src/android/renderscript/cts/TestStep.rs
+++ b/tests/src/android/renderscript/cts/TestStep.rs
@@ -55,3 +55,18 @@
     float inV = rsGetElementAt_float(gAllocInV, x);
     return step(inEdge, inV);
 }
+
+float2 __attribute__((kernel)) testStepFloatFloat2Float2(float inEdge, unsigned int x) {
+    float2 inV = rsGetElementAt_float2(gAllocInV, x);
+    return step(inEdge, inV);
+}
+
+float3 __attribute__((kernel)) testStepFloatFloat3Float3(float inEdge, unsigned int x) {
+    float3 inV = rsGetElementAt_float3(gAllocInV, x);
+    return step(inEdge, inV);
+}
+
+float4 __attribute__((kernel)) testStepFloatFloat4Float4(float inEdge, unsigned int x) {
+    float4 inV = rsGetElementAt_float4(gAllocInV, x);
+    return step(inEdge, inV);
+}
diff --git a/tests/src/android/webkit/cts/WebViewOnUiThread.java b/tests/src/android/webkit/cts/WebViewOnUiThread.java
index f638ba8..4f8e06e 100644
--- a/tests/src/android/webkit/cts/WebViewOnUiThread.java
+++ b/tests/src/android/webkit/cts/WebViewOnUiThread.java
@@ -31,6 +31,7 @@
 import android.view.ViewGroup;
 import android.view.ViewParent;
 import android.webkit.DownloadListener;
+import android.webkit.CookieManager;
 import android.webkit.ValueCallback;
 import android.webkit.WebBackForwardList;
 import android.webkit.WebChromeClient;
@@ -340,6 +341,15 @@
         });
     }
 
+    public void clearClientCertPreferences(final Runnable onCleared) {
+        runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                WebView.clearClientCertPreferences(onCleared);
+            }
+        });
+    }
+
     public void resumeTimers() {
         runOnUiThread(new Runnable() {
             @Override
@@ -387,6 +397,7 @@
      * onNewPicture and onProgressChange to reach 100.
      * Test fails if the load timeout elapses.
      * @param url The URL to load.
+     * @param extraHeaders The additional headers to be used in the HTTP request.
      */
     public void loadUrlAndWaitForCompletion(final String url,
             final Map<String, String> extraHeaders) {
@@ -416,6 +427,15 @@
         });
     }
 
+    public void postUrlAndWaitForCompletion(final String url, final byte[] postData) {
+        callAndWait(new Runnable() {
+            @Override
+            public void run() {
+                mWebView.postUrl(url, postData);
+            }
+        });
+    }
+
     public void loadDataAndWaitForCompletion(final String data,
             final String mimeType, final String encoding) {
         callAndWait(new Runnable() {
@@ -694,6 +714,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
@@ -718,6 +747,24 @@
         });
     }
 
+    public void setAcceptThirdPartyCookies(final boolean accept) {
+        runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                CookieManager.getInstance().setAcceptThirdPartyCookies(mWebView, accept);
+            }
+        });
+    }
+
+    public boolean acceptThirdPartyCookies() {
+        return getValue(new ValueGetter<Boolean>() {
+            @Override
+            public Boolean capture() {
+                return CookieManager.getInstance().acceptThirdPartyCookies(mWebView);
+            }
+        });
+    }
+
     /**
      * Helper for running code on the UI thread where an exception is
      * a test failure. If this is already the UI thread then it runs
@@ -747,7 +794,7 @@
         return mWebView;
     }
 
-    private <T> T getValue(ValueGetter<T> getter) {
+    private<T> T getValue(ValueGetter<T> getter) {
         runOnUiThread(getter);
         return getter.getValue();
     }
diff --git a/tests/src/android/widget/cts/SeekBarStubActivity.java b/tests/src/android/widget/cts/SeekBarStubActivity.java
index 84274dc..81d3d41 100644
--- a/tests/src/android/widget/cts/SeekBarStubActivity.java
+++ b/tests/src/android/widget/cts/SeekBarStubActivity.java
@@ -33,7 +33,7 @@
     @Override
     public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
-        setContentView(R.layout.seekbar);
+        setContentView(R.layout.seekbar_layout);
 
         View v = findViewById(R.id.seekBar);
         v.setEnabled(true);
diff --git a/tests/systemAppTest/test/Android.mk b/tests/systemAppTest/test/Android.mk
index 3354886..9be491c 100644
--- a/tests/systemAppTest/test/Android.mk
+++ b/tests/systemAppTest/test/Android.mk
@@ -24,8 +24,6 @@
 
 LOCAL_MODULE_PATH := $(TARGET_OUT_APPS_PRIVILEGED)
 
-LOCAL_JAVA_LIBRARIES := android.test.runner
-
 LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner ctsdeviceutil
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
diff --git a/tests/tests/acceleration/Android.mk b/tests/tests/acceleration/Android.mk
index 30a1f51..d417371 100644
--- a/tests/tests/acceleration/Android.mk
+++ b/tests/tests/acceleration/Android.mk
@@ -20,8 +20,6 @@
 
 LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
 
-LOCAL_JAVA_LIBRARIES := android.test.runner
-
 LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
diff --git a/tests/tests/acceleration/AndroidManifest.xml b/tests/tests/acceleration/AndroidManifest.xml
index d08827e..0dd2722 100644
--- a/tests/tests/acceleration/AndroidManifest.xml
+++ b/tests/tests/acceleration/AndroidManifest.xml
@@ -24,8 +24,11 @@
       <uses-library android:name="android.test.runner" />
   </application>
 
-  <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
+  <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
                    android:targetPackage="com.android.cts.acceleration.stub"
-                   android:label="Tests for the Hardware Acceleration APIs." />
+                   android:label="Tests for the Hardware Acceleration APIs." >
+        <meta-data android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
 
 </manifest>
diff --git a/tests/tests/accessibility/Android.mk b/tests/tests/accessibility/Android.mk
index abd6f4b..9f98b16 100644
--- a/tests/tests/accessibility/Android.mk
+++ b/tests/tests/accessibility/Android.mk
@@ -20,8 +20,6 @@
 
 LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
 
-LOCAL_JAVA_LIBRARIES := android.test.runner
-
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
 LOCAL_PACKAGE_NAME := CtsAccessibilityTestCases
diff --git a/tests/tests/accessibility/AndroidManifest.xml b/tests/tests/accessibility/AndroidManifest.xml
index 53b9cc3..319fb49 100644
--- a/tests/tests/accessibility/AndroidManifest.xml
+++ b/tests/tests/accessibility/AndroidManifest.xml
@@ -24,8 +24,11 @@
       <uses-library android:name="android.test.runner"/>
   </application>
 
-  <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
+  <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
                    android:targetPackage="android.view.cts.accessibility"
-                   android:label="Tests for the accessibility APIs."/>
+                   android:label="Tests for the accessibility APIs.">
+        <meta-data android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
 
 </manifest>
diff --git a/tests/tests/accessibility/res/values/ids.xml b/tests/tests/accessibility/res/values/ids.xml
new file mode 100644
index 0000000..23edb2b
--- /dev/null
+++ b/tests/tests/accessibility/res/values/ids.xml
@@ -0,0 +1,18 @@
+<!-- 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>
+    <item type="id" name="foo_custom_action" />
+</resources>
diff --git a/tests/tests/accessibility/src/android/view/accessibility/cts/AccessibilityNodeInfoTest.java b/tests/tests/accessibility/src/android/view/accessibility/cts/AccessibilityNodeInfoTest.java
index ae7cc9b..7925d15 100644
--- a/tests/tests/accessibility/src/android/view/accessibility/cts/AccessibilityNodeInfoTest.java
+++ b/tests/tests/accessibility/src/android/view/accessibility/cts/AccessibilityNodeInfoTest.java
@@ -23,6 +23,11 @@
 import android.view.View;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.accessibility.AccessibilityNodeInfo;
+import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
+import android.view.cts.accessibility.R;
+
+import java.util.ArrayList;
+import java.util.List;
 
 /**
  * Class for testing {@link AccessibilityNodeInfo}.
@@ -94,6 +99,70 @@
     }
 
     /**
+     * Tests whether accessibility actions are properly added.
+     */
+    @SmallTest
+    public void testAddActions() {
+        List<AccessibilityAction> customActions = new ArrayList<AccessibilityAction>();
+        customActions.add(new AccessibilityAction(AccessibilityNodeInfo.ACTION_FOCUS, "Foo"));
+        customActions.add(new AccessibilityAction(R.id.foo_custom_action, "Foo"));
+
+        AccessibilityNodeInfo info = AccessibilityNodeInfo.obtain();
+        info.addAction(AccessibilityNodeInfo.ACTION_FOCUS);
+        info.addAction(AccessibilityNodeInfo.ACTION_CLEAR_FOCUS);
+        for (AccessibilityAction customAction : customActions) {
+            info.addAction(customAction);
+        }
+
+        assertSame(info.getActions(), (AccessibilityNodeInfo.ACTION_FOCUS
+                | AccessibilityNodeInfo.ACTION_CLEAR_FOCUS));
+
+        List<AccessibilityAction> allActions = new ArrayList<AccessibilityAction>();
+        allActions.add(AccessibilityAction.ACTION_CLEAR_FOCUS);
+        allActions.addAll(customActions);
+        assertEquals(info.getActionList(), allActions);
+    }
+
+    /**
+     * Tests whether we catch addition of an action with invalid id.
+     */
+    @SmallTest
+    public void testCreateInvalidActionId() {
+        try {
+            new AccessibilityAction(3, null);
+        } catch (IllegalArgumentException iae) {
+            /* expected */
+        }
+    }
+
+    /**
+     * Tests whether accessibility actions are properly removed.
+     */
+    @SmallTest
+    public void testRemoveActions() {
+        AccessibilityNodeInfo info = AccessibilityNodeInfo.obtain();
+
+        info.addAction(AccessibilityNodeInfo.ACTION_FOCUS);
+        assertSame(info.getActions(), AccessibilityNodeInfo.ACTION_FOCUS);
+
+        info.removeAction(AccessibilityNodeInfo.ACTION_FOCUS);
+        assertSame(info.getActions(), 0);
+        assertTrue(info.getActionList().isEmpty());
+
+        AccessibilityAction customFocus = new AccessibilityAction(
+                AccessibilityNodeInfo.ACTION_FOCUS, "Foo");
+        info.addAction(AccessibilityNodeInfo.ACTION_FOCUS);
+        info.addAction(customFocus);
+        assertSame(info.getActionList().size(), 1);
+        assertEquals(info.getActionList().get(0), customFocus);
+        assertSame(info.getActions(), AccessibilityNodeInfo.ACTION_FOCUS);
+
+        info.removeAction(customFocus);
+        assertSame(info.getActions(), 0);
+        assertTrue(info.getActionList().isEmpty());
+    }
+
+    /**
      * Fully populates the {@link AccessibilityNodeInfo} to marshal.
      *
      * @param info The node info to populate.
@@ -119,7 +188,10 @@
         info.setPassword(true);
         info.setScrollable(true);
         info.setSelected(true);
+        info.addAction(AccessibilityNodeInfo.ACTION_FOCUS);
         info.addAction(AccessibilityNodeInfo.ACTION_CLEAR_FOCUS);
+        info.addAction(new AccessibilityAction(AccessibilityNodeInfo.ACTION_FOCUS, "Foo"));
+        info.addAction(new AccessibilityAction(R.id.foo_custom_action, "Foo"));
         info.setAccessibilityFocused(true);
         info.setMovementGranularities(AccessibilityNodeInfo.MOVEMENT_GRANULARITY_LINE);
         info.setLabeledBy(new View(getContext()));
@@ -171,6 +243,8 @@
                 receivedInfo.isSelected());
         assertSame("actions has incorrect value", expectedInfo.getActions(),
                 receivedInfo.getActions());
+        assertEquals("actionsSet has incorrect value", expectedInfo.getActionList(),
+                receivedInfo.getActionList());
         assertSame("childCount has incorrect value", expectedInfo.getChildCount(),
                 receivedInfo.getChildCount());
         assertSame("childCount has incorrect value", expectedInfo.getChildCount(),
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/accessibilityservice/Android.mk b/tests/tests/accessibilityservice/Android.mk
index 73cd288..b27dbcc 100644
--- a/tests/tests/accessibilityservice/Android.mk
+++ b/tests/tests/accessibilityservice/Android.mk
@@ -18,12 +18,12 @@
 
 LOCAL_MODULE_TAGS := optional
 
-LOCAL_JAVA_LIBRARIES := android.test.runner
-
 LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
 LOCAL_PACKAGE_NAME := CtsAccessibilityServiceTestCases
 
+LOCAL_SDK_VERSION := current
+
 include $(BUILD_CTS_PACKAGE)
diff --git a/tests/tests/accessibilityservice/AndroidManifest.xml b/tests/tests/accessibilityservice/AndroidManifest.xml
index ed94f9c..4039193 100644
--- a/tests/tests/accessibilityservice/AndroidManifest.xml
+++ b/tests/tests/accessibilityservice/AndroidManifest.xml
@@ -43,8 +43,11 @@
 
   </application>
 
-  <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
+  <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
                    android:targetPackage="com.android.cts.accessibilityservice"
-                   android:label="Tests for the accessibility APIs."/>
+                   android:label="Tests for the accessibility APIs.">
+        <meta-data android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
 
 </manifest>
diff --git a/tests/tests/accessibilityservice/res/values/ids.xml b/tests/tests/accessibilityservice/res/values/ids.xml
new file mode 100644
index 0000000..23edb2b
--- /dev/null
+++ b/tests/tests/accessibilityservice/res/values/ids.xml
@@ -0,0 +1,18 @@
+<!-- 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>
+    <item type="id" name="foo_custom_action" />
+</resources>
diff --git a/tests/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityEndToEndTest.java b/tests/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityEndToEndTest.java
index f0f0dd7..39b116a 100644
--- a/tests/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityEndToEndTest.java
+++ b/tests/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityEndToEndTest.java
@@ -46,10 +46,8 @@
 
     /**
      * Creates a new instance for testing {@link AccessibilityEndToEndActivity}.
-     *
-     * @throws Exception If any error occurs.
      */
-    public AccessibilityEndToEndTest() throws Exception {
+    public AccessibilityEndToEndTest() {
         super(AccessibilityEndToEndActivity.class);
     }
 
diff --git a/tests/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityWindowQueryActivity.java b/tests/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityWindowQueryActivity.java
index b0cedf6..aa66a45 100644
--- a/tests/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityWindowQueryActivity.java
+++ b/tests/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityWindowQueryActivity.java
@@ -17,13 +17,15 @@
 import android.os.Bundle;
 import android.view.View;
 
+import android.view.accessibility.AccessibilityNodeInfo;
+import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
 import com.android.cts.accessibilityservice.R;
 
 /**
  * Activity for testing the accessibility APIs for querying of
  * the screen content. These APIs allow exploring the screen and
  * requesting an action to be performed on a given view from an
- * AccessiiblityService.
+ * AccessibilityService.
  */
 public class AccessibilityWindowQueryActivity extends AccessibilityTestActivity {
 
@@ -42,5 +44,21 @@
                 return true;
             }
         });
+
+        findViewById(R.id.button5).setAccessibilityDelegate(new View.AccessibilityDelegate() {
+            @Override
+            public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) {
+                super.onInitializeAccessibilityNodeInfo(host, info);
+                info.addAction(new AccessibilityAction(R.id.foo_custom_action, "Foo"));
+            }
+
+            @Override
+            public boolean performAccessibilityAction(View host, int action, Bundle args) {
+                if (action == R.id.foo_custom_action) {
+                    return true;
+                }
+                return super.performAccessibilityAction(host, action, args);
+            }
+        });
     }
 }
diff --git a/tests/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityWindowQueryTest.java b/tests/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityWindowQueryTest.java
index 492b99b..33aa0d2 100644
--- a/tests/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityWindowQueryTest.java
+++ b/tests/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityWindowQueryTest.java
@@ -30,6 +30,8 @@
 import android.test.suitebuilder.annotation.MediumTest;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.accessibility.AccessibilityNodeInfo;
+import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
+import android.view.accessibility.AccessibilityWindowInfo;
 
 import com.android.cts.accessibilityservice.R;
 
@@ -41,11 +43,13 @@
 /**
  * Test cases for testing the accessibility APIs for querying of the screen content.
  * These APIs allow exploring the screen and requesting an action to be performed
- * on a given view from an AccessiiblityService.
+ * on a given view from an AccessibilityService.
  */
 public class AccessibilityWindowQueryTest
         extends AccessibilityActivityTestCase<AccessibilityWindowQueryActivity> {
 
+    private static final long TIMEOUT_WINDOW_STATE_IDLE = 500;
+
     public AccessibilityWindowQueryTest() {
         super(AccessibilityWindowQueryActivity.class);
     }
@@ -69,62 +73,326 @@
 
     @MediumTest
     public void testTraverseWindow() throws Exception {
-        try {
-            AccessibilityServiceInfo info = getInstrumentation().getUiAutomation().getServiceInfo();
-            info.flags |= AccessibilityServiceInfo.FLAG_INCLUDE_NOT_IMPORTANT_VIEWS;
-            getInstrumentation().getUiAutomation().setServiceInfo(info);
+        verifyNodesInAppWindow(getInstrumentation().getUiAutomation().getRootInActiveWindow());
+    }
 
-            // make list of expected nodes
-            List<String> classNameAndTextList = new ArrayList<String>();
-            classNameAndTextList.add("android.widget.LinearLayout");
-            classNameAndTextList.add("android.widget.LinearLayout");
-            classNameAndTextList.add("android.widget.LinearLayout");
-            classNameAndTextList.add("android.widget.LinearLayout");
-            classNameAndTextList.add("android.widget.ButtonB1");
-            classNameAndTextList.add("android.widget.ButtonB2");
-            classNameAndTextList.add("android.widget.ButtonB3");
-            classNameAndTextList.add("android.widget.ButtonB4");
-            classNameAndTextList.add("android.widget.ButtonB5");
-            classNameAndTextList.add("android.widget.ButtonB6");
-            classNameAndTextList.add("android.widget.ButtonB7");
-            classNameAndTextList.add("android.widget.ButtonB8");
-            classNameAndTextList.add("android.widget.ButtonB9");
+    @MediumTest
+    public void testNoWindowsAccessIfFlagNotSet() throws Exception {
+        // Make sure the windows cannot be accessed.
+        UiAutomation uiAutomation = getInstrumentation().getUiAutomation();
+        assertTrue(uiAutomation.getWindows().isEmpty());
 
-            String contentViewIdResName = "com.android.cts.accessibilityservice:id/added_content";
-            boolean verifyContent = false;
+        // Find a button to click on.
+        final AccessibilityNodeInfo button1 = uiAutomation.getRootInActiveWindow()
+                .findAccessibilityNodeInfosByViewId(
+                        "com.android.cts.accessibilityservice:id/button1").get(0);
 
-            Queue<AccessibilityNodeInfo> fringe = new LinkedList<AccessibilityNodeInfo>();
-            fringe.add(getInstrumentation().getUiAutomation().getRootInActiveWindow());
+        // Argh...
+        final List<AccessibilityEvent> events = new ArrayList<AccessibilityEvent>();
 
-            // do a BFS traversal and check nodes
-            while (!fringe.isEmpty()) {
-                AccessibilityNodeInfo current = fringe.poll();
-
-                if (!verifyContent
-                        && contentViewIdResName.equals(current.getViewIdResourceName())) {
-                    verifyContent = true;
-                }
-
-                if (verifyContent) {
-                    CharSequence text = current.getText();
-                    String receivedClassNameAndText = current.getClassName().toString()
-                            + ((text != null) ? text.toString() : "");
-                    String expectedClassNameAndText = classNameAndTextList.remove(0);
-
-                    assertEquals("Did not get the expected node info",
-                            expectedClassNameAndText, receivedClassNameAndText);
-                }
-
-                final int childCount = current.getChildCount();
-                for (int i = 0; i < childCount; i++) {
-                    AccessibilityNodeInfo child = current.getChild(i);
-                    fringe.add(child);
-                }
+        // Click the button.
+        uiAutomation.executeAndWaitForEvent(new Runnable() {
+            @Override
+            public void run() {
+                button1.performAction(AccessibilityNodeInfo.ACTION_CLICK);
             }
+        },
+        new UiAutomation.AccessibilityEventFilter() {
+            @Override
+            public boolean accept(AccessibilityEvent event) {
+                if (event.getEventType() == AccessibilityEvent.TYPE_VIEW_CLICKED) {
+                    events.add(event);
+                    return true;
+                }
+                return false;
+            }
+        },
+        TIMEOUT_ASYNC_PROCESSING);
+
+        // Make sure the source window cannot be accessed.
+        AccessibilityEvent event = events.get(0);
+        assertNull(event.getSource().getWindow());
+    }
+
+    @MediumTest
+    public void testTraverseAllWindows() throws Exception {
+        setAccessInteractiveWindowsFlag();
+        try {
+            final UiAutomation uiAutomation = getInstrumentation().getUiAutomation();
+
+            getInstrumentation().getUiAutomation().waitForIdle(
+                    TIMEOUT_WINDOW_STATE_IDLE,
+                    TIMEOUT_ASYNC_PROCESSING);
+
+            List<AccessibilityWindowInfo> windows = uiAutomation.getWindows();
+
+            Rect boundsInScreen = new Rect();
+
+            // Verify the navigation bar window.
+            AccessibilityWindowInfo navBarWindow = windows.get(0);
+            navBarWindow.getBoundsInScreen(boundsInScreen);
+            assertFalse(boundsInScreen.isEmpty()); // Varies on screen size, just emptiness check.
+            assertSame(navBarWindow.getType(), AccessibilityWindowInfo.TYPE_SYSTEM);
+            assertFalse(navBarWindow.isFocused());
+            assertFalse(navBarWindow.isActive());
+            assertNull(navBarWindow.getParent());
+            assertSame(0, navBarWindow.getChildCount());
+            assertNotNull(navBarWindow.getRoot());
+
+            // Verify the status bar window.
+            AccessibilityWindowInfo statusBarWindow = windows.get(1);
+            statusBarWindow.getBoundsInScreen(boundsInScreen);
+            assertFalse(boundsInScreen.isEmpty()); // Varies on screen size, just emptiness check.
+            assertSame(statusBarWindow.getType(), AccessibilityWindowInfo.TYPE_SYSTEM);
+            assertFalse(statusBarWindow.isFocused());
+            assertFalse(statusBarWindow.isActive());
+            assertNull(statusBarWindow.getParent());
+            assertSame(0, statusBarWindow.getChildCount());
+            assertNotNull(statusBarWindow.getRoot());
+
+            // Verify the application window.
+            AccessibilityWindowInfo appWindow = windows.get(2);
+            appWindow.getBoundsInScreen(boundsInScreen);
+            assertFalse(boundsInScreen.isEmpty()); // Varies on screen size, just emptiness check.
+            assertSame(appWindow.getType(), AccessibilityWindowInfo.TYPE_APPLICATION);
+            assertTrue(appWindow.isFocused());
+            assertTrue(appWindow.isActive());
+            assertNull(appWindow.getParent());
+            assertSame(0, appWindow.getChildCount());
+            assertNotNull(appWindow.getRoot());
+
+            verifyNodesInAppWindow(appWindow.getRoot());
         } finally {
-            AccessibilityServiceInfo info = getInstrumentation().getUiAutomation().getServiceInfo();
-            info.flags &= ~AccessibilityServiceInfo.FLAG_INCLUDE_NOT_IMPORTANT_VIEWS;
-            getInstrumentation().getUiAutomation().setServiceInfo(info);
+            clearAccessInteractiveWindowsFlag();
+        }
+    }
+
+    @MediumTest
+    public void testTraverseWindowFromEvent() throws Exception {
+        setAccessInteractiveWindowsFlag();
+        try {
+            UiAutomation uiAutomation = getInstrumentation().getUiAutomation();
+
+            // Find a button to click on.
+            final AccessibilityNodeInfo button1 = uiAutomation.getRootInActiveWindow()
+                    .findAccessibilityNodeInfosByViewId(
+                            "com.android.cts.accessibilityservice:id/button1").get(0);
+
+            // Argh...
+            final List<AccessibilityEvent> events = new ArrayList<AccessibilityEvent>();
+
+            // Click the button.
+            uiAutomation.executeAndWaitForEvent(new Runnable() {
+                @Override
+                public void run() {
+                    button1.performAction(AccessibilityNodeInfo.ACTION_CLICK);
+                }
+            },
+            new UiAutomation.AccessibilityEventFilter() {
+                @Override
+                public boolean accept(AccessibilityEvent event) {
+                    if (event.getEventType() == AccessibilityEvent.TYPE_VIEW_CLICKED) {
+                        events.add(event);
+                        return true;
+                    }
+                    return false;
+                }
+            },
+            TIMEOUT_ASYNC_PROCESSING);
+
+            // Get the source window.
+            AccessibilityEvent event = events.get(0);
+            AccessibilityWindowInfo window = event.getSource().getWindow();
+
+            // Verify the application window.
+            Rect boundsInScreen = new Rect();
+            window.getBoundsInScreen(boundsInScreen);
+            assertFalse(boundsInScreen.isEmpty()); // Varies on screen size, so just emptiness check.
+            assertSame(window.getType(), AccessibilityWindowInfo.TYPE_APPLICATION);
+            assertTrue(window.isFocused());
+            assertTrue(window.isActive());
+            assertNull(window.getParent());
+            assertSame(0, window.getChildCount());
+            assertNotNull(window.getRoot());
+
+            // Verify the window content.
+            verifyNodesInAppWindow(window.getRoot());
+        } finally {
+            clearAccessInteractiveWindowsFlag();
+        }
+    }
+
+    @MediumTest
+    public void testInteractWithAppWindow() throws Exception {
+        setAccessInteractiveWindowsFlag();
+        try {
+            UiAutomation uiAutomation = getInstrumentation().getUiAutomation();
+
+            // Find a button to click on.
+            final AccessibilityNodeInfo button1 = uiAutomation.getRootInActiveWindow()
+                    .findAccessibilityNodeInfosByViewId(
+                            "com.android.cts.accessibilityservice:id/button1").get(0);
+
+            // Argh...
+            final List<AccessibilityEvent> events = new ArrayList<AccessibilityEvent>();
+
+            // Click the button.
+            uiAutomation.executeAndWaitForEvent(new Runnable() {
+                @Override
+                public void run() {
+                    button1.performAction(AccessibilityNodeInfo.ACTION_CLICK);
+                }
+            },
+            new UiAutomation.AccessibilityEventFilter() {
+                @Override
+                public boolean accept(AccessibilityEvent event) {
+                    if (event.getEventType() == AccessibilityEvent.TYPE_VIEW_CLICKED) {
+                        events.add(event);
+                        return true;
+                    }
+                    return false;
+                }
+            },
+            TIMEOUT_ASYNC_PROCESSING);
+
+            // Get the source window.
+            AccessibilityEvent event = events.get(0);
+            AccessibilityWindowInfo window = event.getSource().getWindow();
+
+            // Find a another button from the event's window.
+            final AccessibilityNodeInfo button2 = window.getRoot()
+                    .findAccessibilityNodeInfosByViewId(
+                            "com.android.cts.accessibilityservice:id/button2").get(0);
+
+            // Click the second button.
+            uiAutomation.executeAndWaitForEvent(new Runnable() {
+                @Override
+                public void run() {
+                    button2.performAction(AccessibilityNodeInfo.ACTION_CLICK);
+                }
+            },
+            new UiAutomation.AccessibilityEventFilter() {
+                @Override
+                public boolean accept(AccessibilityEvent event) {
+                    return event.getEventType() == AccessibilityEvent.TYPE_VIEW_CLICKED;
+                }
+            },
+            TIMEOUT_ASYNC_PROCESSING);
+        } finally {
+            clearAccessInteractiveWindowsFlag();
+        }
+    }
+
+    @MediumTest
+    public void testInteractWithNavBarWindow() throws Exception {
+        setAccessInteractiveWindowsFlag();
+        try {
+            UiAutomation uiAutomation = getInstrumentation().getUiAutomation();
+            AccessibilityWindowInfo window = uiAutomation.getWindows().get(0);
+            assertTrue(window.getRoot().performAction(
+                    AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS));
+        } finally {
+            clearAccessInteractiveWindowsFlag();
+        }
+    }
+
+    @MediumTest
+    public void testInteractWithStatusBarWindow() throws Exception {
+        setAccessInteractiveWindowsFlag();
+        try {
+            UiAutomation uiAutomation = getInstrumentation().getUiAutomation();
+            AccessibilityWindowInfo window = uiAutomation.getWindows().get(1);
+            assertTrue(window.getRoot().performAction(
+                    AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS));
+        } finally {
+            clearAccessInteractiveWindowsFlag();
+        }
+    }
+
+    @MediumTest
+    public void testSingleAccessibilityFocusAcrossWindows() throws Exception {
+        setAccessInteractiveWindowsFlag();
+        try {
+            UiAutomation uiAutomation = getInstrumentation().getUiAutomation();
+
+            uiAutomation.waitForIdle(TIMEOUT_WINDOW_STATE_IDLE, TIMEOUT_ASYNC_PROCESSING);
+
+            List<AccessibilityWindowInfo> windows = uiAutomation.getWindows();
+
+            AccessibilityNodeInfo firstWindowRoot = windows.get(0).getRoot();
+            AccessibilityNodeInfo secondWindowRoot = windows.get(1).getRoot();
+            AccessibilityNodeInfo thirdWindowRoot = windows.get(2).getRoot();
+
+
+            // Set focus in the first window.
+            assertTrue(firstWindowRoot.performAction(
+                    AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS));
+
+            // Wait for things to settle.
+            uiAutomation.waitForIdle(TIMEOUT_WINDOW_STATE_IDLE, TIMEOUT_ASYNC_PROCESSING);
+
+            // Make sure there only one accessibility focus.
+            assertEquals(uiAutomation.findFocus(
+                    AccessibilityNodeInfo.FOCUS_ACCESSIBILITY), firstWindowRoot);
+            assertEquals(firstWindowRoot.findFocus(
+                    AccessibilityNodeInfo.FOCUS_ACCESSIBILITY), firstWindowRoot);
+            assertNull(secondWindowRoot.findFocus(
+                    AccessibilityNodeInfo.FOCUS_ACCESSIBILITY));
+            assertNull(thirdWindowRoot.findFocus(
+                    AccessibilityNodeInfo.FOCUS_ACCESSIBILITY));
+
+
+            // Set focus in the second window.
+            assertTrue(secondWindowRoot.performAction(
+                    AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS));
+
+            // Wait for things to settle.
+            uiAutomation.waitForIdle(TIMEOUT_WINDOW_STATE_IDLE, TIMEOUT_ASYNC_PROCESSING);
+
+            // Make sure there only one accessibility focus.
+            assertEquals(uiAutomation.findFocus(
+                    AccessibilityNodeInfo.FOCUS_ACCESSIBILITY), secondWindowRoot);
+            assertEquals(secondWindowRoot.findFocus(
+                    AccessibilityNodeInfo.FOCUS_ACCESSIBILITY), secondWindowRoot);
+            assertNull(firstWindowRoot.findFocus(
+                    AccessibilityNodeInfo.FOCUS_ACCESSIBILITY));
+            assertNull(thirdWindowRoot.findFocus(
+                    AccessibilityNodeInfo.FOCUS_ACCESSIBILITY));
+
+
+            // Set focus in the third window.
+            assertTrue(thirdWindowRoot.performAction(
+                    AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS));
+
+            // Wait for things to settle.
+            uiAutomation.waitForIdle(TIMEOUT_WINDOW_STATE_IDLE, TIMEOUT_ASYNC_PROCESSING);
+
+            // Make sure there only one accessibility focus.
+            assertEquals(uiAutomation.findFocus(
+                    AccessibilityNodeInfo.FOCUS_ACCESSIBILITY), thirdWindowRoot);
+            assertEquals(thirdWindowRoot.findFocus(
+                    AccessibilityNodeInfo.FOCUS_ACCESSIBILITY), thirdWindowRoot);
+            assertNull(firstWindowRoot.findFocus(
+                    AccessibilityNodeInfo.FOCUS_ACCESSIBILITY));
+            assertNull(secondWindowRoot.findFocus(
+                    AccessibilityNodeInfo.FOCUS_ACCESSIBILITY));
+
+
+            // Clear focus.
+            assertTrue(thirdWindowRoot.performAction(
+                    AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS));
+
+            // Make sure there is not accessibility focus.
+            assertNull(uiAutomation.findFocus(
+                    AccessibilityNodeInfo.FOCUS_ACCESSIBILITY));
+            assertNull(firstWindowRoot.findFocus(
+                    AccessibilityNodeInfo.FOCUS_ACCESSIBILITY));
+            assertNull(secondWindowRoot.findFocus(
+                    AccessibilityNodeInfo.FOCUS_ACCESSIBILITY));
+            assertNull(thirdWindowRoot.findFocus(
+                    AccessibilityNodeInfo.FOCUS_ACCESSIBILITY));
+        } finally {
+            clearAccessInteractiveWindowsFlag();
         }
     }
 
@@ -267,6 +535,28 @@
         assertNotNull(expected);
     }
 
+
+    @MediumTest
+    public void testPerformCustomAction() throws Exception {
+        // find a view and make sure it is not selected
+        AccessibilityNodeInfo button = getInstrumentation().getUiAutomation()
+                .getRootInActiveWindow().findAccessibilityNodeInfosByText(
+                        getString(R.string.button5)).get(0);
+
+        // find the custom action and perform it
+        List<AccessibilityAction> actions = button.getActionList();
+        final int actionCount = actions.size();
+        for (int i = 0; i < actionCount; i++) {
+            AccessibilityAction action = actions.get(i);
+            if (action.getId() == R.id.foo_custom_action) {
+                assertSame(action.getLabel(), "Foo");
+                // perform the action
+                assertTrue(button.performAction(action.getId()));
+                return;
+            }
+        }
+    }
+
     @MediumTest
     public void testGetEventSource() throws Exception {
         // find a view and make sure it is not focused
@@ -397,6 +687,23 @@
     }
 
     @MediumTest
+    public void testPerformGlobalActionPowerDialog() throws Exception {
+        // Check whether the action succeeded.
+        assertTrue(getInstrumentation().getUiAutomation().performGlobalAction(
+                AccessibilityService.GLOBAL_ACTION_POWER_DIALOG));
+
+        // Sleep a bit so the UI is settles.
+        waitForIdle();
+
+        // Clean up.
+        getInstrumentation().getUiAutomation().performGlobalAction(
+                AccessibilityService.GLOBAL_ACTION_BACK);
+
+        // Sleep a bit so the UI is settles.
+        waitForIdle();
+    }
+
+    @MediumTest
     public void testObjectContract() throws Exception {
         try {
             AccessibilityServiceInfo info = getInstrumentation().getUiAutomation().getServiceInfo();
@@ -426,6 +733,82 @@
         }
     }
 
+    private void setAccessInteractiveWindowsFlag () {
+        UiAutomation uiAutomation = getInstrumentation().getUiAutomation();
+        AccessibilityServiceInfo info = uiAutomation.getServiceInfo();
+        info.flags |= AccessibilityServiceInfo.FLAG_RETRIEVE_INTERACTIVE_WINDOWS;
+        uiAutomation.setServiceInfo(info);
+    }
+
+    private void clearAccessInteractiveWindowsFlag () {
+        UiAutomation uiAutomation = getInstrumentation().getUiAutomation();
+        AccessibilityServiceInfo info = uiAutomation.getServiceInfo();
+        info.flags &= ~AccessibilityServiceInfo.FLAG_RETRIEVE_INTERACTIVE_WINDOWS;
+        uiAutomation.setServiceInfo(info);
+    }
+
+    private void verifyNodesInAppWindow(AccessibilityNodeInfo root) throws Exception {
+        try {
+            AccessibilityServiceInfo info = getInstrumentation().getUiAutomation().getServiceInfo();
+            info.flags |= AccessibilityServiceInfo.FLAG_INCLUDE_NOT_IMPORTANT_VIEWS;
+            getInstrumentation().getUiAutomation().setServiceInfo(info);
+
+            root.refresh();
+
+            // make list of expected nodes
+            List<String> classNameAndTextList = new ArrayList<String>();
+            classNameAndTextList.add("android.widget.LinearLayout");
+            classNameAndTextList.add("android.widget.LinearLayout");
+            classNameAndTextList.add("android.widget.LinearLayout");
+            classNameAndTextList.add("android.widget.LinearLayout");
+            classNameAndTextList.add("android.widget.ButtonB1");
+            classNameAndTextList.add("android.widget.ButtonB2");
+            classNameAndTextList.add("android.widget.ButtonB3");
+            classNameAndTextList.add("android.widget.ButtonB4");
+            classNameAndTextList.add("android.widget.ButtonB5");
+            classNameAndTextList.add("android.widget.ButtonB6");
+            classNameAndTextList.add("android.widget.ButtonB7");
+            classNameAndTextList.add("android.widget.ButtonB8");
+            classNameAndTextList.add("android.widget.ButtonB9");
+
+            String contentViewIdResName = "com.android.cts.accessibilityservice:id/added_content";
+            boolean verifyContent = false;
+
+            Queue<AccessibilityNodeInfo> fringe = new LinkedList<AccessibilityNodeInfo>();
+            fringe.add(root);
+
+            // do a BFS traversal and check nodes
+            while (!fringe.isEmpty()) {
+                AccessibilityNodeInfo current = fringe.poll();
+
+                if (!verifyContent
+                        && contentViewIdResName.equals(current.getViewIdResourceName())) {
+                    verifyContent = true;
+                }
+
+                if (verifyContent) {
+                    CharSequence text = current.getText();
+                    String receivedClassNameAndText = current.getClassName().toString()
+                            + ((text != null) ? text.toString() : "");
+                    String expectedClassNameAndText = classNameAndTextList.remove(0);
+
+                    assertEquals("Did not get the expected node info",
+                            expectedClassNameAndText, receivedClassNameAndText);
+                }
+
+                final int childCount = current.getChildCount();
+                for (int i = 0; i < childCount; i++) {
+                    AccessibilityNodeInfo child = current.getChild(i);
+                    fringe.add(child);
+                }
+            }
+        } finally {
+            AccessibilityServiceInfo info = getInstrumentation().getUiAutomation().getServiceInfo();
+            info.flags &= ~AccessibilityServiceInfo.FLAG_INCLUDE_NOT_IMPORTANT_VIEWS;
+            getInstrumentation().getUiAutomation().setServiceInfo(info);
+        }
+    }
+
     @Override
     protected void scrubClass(Class<?> testCaseClass) {
         /* intentionally do not scrub */
diff --git a/tests/tests/accounts/Android.mk b/tests/tests/accounts/Android.mk
index e4536d4..39dbfb1 100644
--- a/tests/tests/accounts/Android.mk
+++ b/tests/tests/accounts/Android.mk
@@ -21,12 +21,12 @@
 # and when built explicitly put it in the data partition
 LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
 
-LOCAL_JAVA_LIBRARIES := android.test.runner
-
 LOCAL_STATIC_JAVA_LIBRARIES += android-common ctstestrunner
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
 LOCAL_PACKAGE_NAME := CtsAccountManagerTestCases
 
+LOCAL_SDK_VERSION := current
+
 include $(BUILD_CTS_PACKAGE)
diff --git a/tests/tests/accounts/AndroidManifest.xml b/tests/tests/accounts/AndroidManifest.xml
index 6020636..cf7f7d8 100644
--- a/tests/tests/accounts/AndroidManifest.xml
+++ b/tests/tests/accounts/AndroidManifest.xml
@@ -39,9 +39,12 @@
         </service>
     </application>
 
-    <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
                      android:targetPackage="android.accounts.cts"
-                     android:label="CTS tests for android.accounts"/>
+                     android:label="CTS tests for android.accounts">
+        <meta-data android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
 
 </manifest>
 
diff --git a/tests/tests/accounts/src/android/accounts/cts/AccountManagerTest.java b/tests/tests/accounts/src/android/accounts/cts/AccountManagerTest.java
index 5315cec..f443d30 100644
--- a/tests/tests/accounts/src/android/accounts/cts/AccountManagerTest.java
+++ b/tests/tests/accounts/src/android/accounts/cts/AccountManagerTest.java
@@ -46,6 +46,7 @@
 public class AccountManagerTest extends AndroidTestCase {
 
     public static final String ACCOUNT_NAME = "android.accounts.cts.account.name";
+    public static final String ACCOUNT_NEW_NAME = "android.accounts.cts.account.name.rename";
     public static final String ACCOUNT_NAME_OTHER = "android.accounts.cts.account.name.other";
 
     public static final String ACCOUNT_TYPE = "android.accounts.cts.account.type";
@@ -196,6 +197,16 @@
         return resultBundle;
     }
 
+    private Account renameAccount(AccountManager am, Account account, String newName)
+            throws OperationCanceledException, AuthenticatorException, IOException {
+        AccountManagerFuture<Account> futureAccount = am.renameAccount(
+                account, newName, null /* callback */, null /* handler */);
+        Account renamedAccount = futureAccount.getResult();
+        assertTrue(futureAccount.isDone());
+        assertNotNull(renamedAccount);
+        return renamedAccount;
+    }
+
     private boolean removeAccount(AccountManager am, Account account,
             AccountManagerCallback<Boolean> callback) throws IOException, AuthenticatorException,
                 OperationCanceledException {
@@ -345,35 +356,78 @@
     }
 
     /**
-     * Test addAccountExplicitly() and removeAccount()
+     * Test addAccountExplicitly(), renameAccount() and removeAccount().
      */
     public void testAddAccountExplicitlyAndRemoveAccount() throws IOException,
             AuthenticatorException, OperationCanceledException {
 
-        final int accountsCount = getAccountsCount();
+        final int expectedAccountsCount = getAccountsCount();
 
         addAccountExplicitly(ACCOUNT, ACCOUNT_PASSWORD, null /* userData */);
 
         // Assert that we have one more account
         Account[] accounts = am.getAccounts();
         assertNotNull(accounts);
-        assertEquals(1 + accountsCount, accounts.length);
+        assertEquals(1 + expectedAccountsCount, accounts.length);
         assertTrue(isAccountPresent(am.getAccounts(), ACCOUNT));
-
         // Need to clean up
         assertTrue(removeAccount(am, ACCOUNT, null /* callback */));
 
         // and verify that we go back to the initial state
         accounts = am.getAccounts();
         assertNotNull(accounts);
-        assertEquals(accountsCount, accounts.length);
+        assertEquals(expectedAccountsCount, accounts.length);
+    }
+
+    /**
+     * Test setUserData() and getUserData().
+     */
+    public void testAccountRenameAndGetPreviousName()
+            throws OperationCanceledException, AuthenticatorException, IOException {
+        // Add a first account
+        boolean result = am.addAccountExplicitly(ACCOUNT,
+                                ACCOUNT_PASSWORD,
+                                USERDATA_BUNDLE);
+        assertTrue(result);
+
+        // Prior to a renmae, the previous name should be null.
+        String nullName = am.getPreviousName(ACCOUNT);
+        assertNull(nullName);
+
+        final int expectedAccountsCount = getAccountsCount();
+
+        Account renamedAccount = renameAccount(am, ACCOUNT, ACCOUNT_NEW_NAME);
+
+        /*
+         *  Make sure that the resultant renamed account has the correct name
+         *  and is associated with the correct account type.
+         */
+        assertEquals(ACCOUNT_NEW_NAME, renamedAccount.name);
+        assertEquals(ACCOUNT.type, renamedAccount.type);
+
+        // Make sure the total number of accounts is the same.
+        Account[] accounts = am.getAccounts();
+        assertEquals(expectedAccountsCount, accounts.length);
+
+        // Make sure the old account isn't present.
+        assertFalse(isAccountPresent(am.getAccounts(), ACCOUNT));
+
+        // But that the new one is.
+        assertTrue(isAccountPresent(am.getAccounts(), renamedAccount));
+
+        // Check that the UserData is still present.
+        assertEquals(USERDATA_VALUE_1, am.getUserData(renamedAccount, USERDATA_NAME_1));
+
+        assertEquals(ACCOUNT.name, am.getPreviousName(renamedAccount));
+
+       // Need to clean up
+        assertTrue(removeAccount(am, renamedAccount, null /* callback */));
     }
 
     /**
      * Test getAccounts() and getAccountsByType()
      */
-    public void testGetAccountsAndGetAccountsByType() throws IOException, AuthenticatorException,
-            OperationCanceledException {
+    public void testGetAccountsAndGetAccountsByType() {
 
         assertEquals(false, isAccountPresent(am.getAccounts(), ACCOUNT));
         assertEquals(false, isAccountPresent(am.getAccounts(), ACCOUNT_SAME_TYPE));
diff --git a/tests/tests/admin/Android.mk b/tests/tests/admin/Android.mk
index c3645cc..7a5ae34 100644
--- a/tests/tests/admin/Android.mk
+++ b/tests/tests/admin/Android.mk
@@ -20,8 +20,6 @@
 
 LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
 
-LOCAL_JAVA_LIBRARIES := android.test.runner
-
 LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner mockito-target
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
diff --git a/tests/tests/admin/AndroidManifest.xml b/tests/tests/admin/AndroidManifest.xml
index 7ce29aa..bbd7918 100644
--- a/tests/tests/admin/AndroidManifest.xml
+++ b/tests/tests/admin/AndroidManifest.xml
@@ -26,8 +26,11 @@
 
   </application>
 
-  <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
+  <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
                    android:targetPackage="android.deviceadmin.cts"
-                   android:label="Tests for the admin APIs."/>
+                   android:label="Tests for the admin APIs.">
+        <meta-data android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
 
 </manifest>
diff --git a/tests/tests/admin/src/android/admin/cts/DevicePolicyManagerTest.java b/tests/tests/admin/src/android/admin/cts/DevicePolicyManagerTest.java
index e953305..5d83717 100644
--- a/tests/tests/admin/src/android/admin/cts/DevicePolicyManagerTest.java
+++ b/tests/tests/admin/src/android/admin/cts/DevicePolicyManagerTest.java
@@ -19,7 +19,9 @@
 import android.app.admin.DevicePolicyManager;
 import android.content.ComponentName;
 import android.content.Context;
+import android.content.Intent;
 import android.content.pm.PackageManager;
+import android.provider.Settings;
 import android.test.AndroidTestCase;
 import android.util.Log;
 
@@ -220,4 +222,198 @@
         assertFalse(mDevicePolicyManager.resetPassword("abcd", 0));
         assertTrue(mDevicePolicyManager.resetPassword("abcd123", 0));
     }
+
+    public void testCreateUser_failIfNotDeviceOwner() {
+        if (!mDeviceAdmin) {
+            Log.w(TAG, "Skipping testCreateUser_failIfNotDeviceOwner");
+            return;
+        }
+        try {
+            mDevicePolicyManager.createUser(mComponent, "user name");
+            fail("did not throw expected SecurityException");
+        } catch (SecurityException e) {
+            assertDeviceOwnerMessage(e.getMessage());
+        }
+    }
+
+    public void testRemoveUser_failIfNotDeviceOwner() {
+        if (!mDeviceAdmin) {
+            Log.w(TAG, "Skipping testRemoveUser_failIfNotDeviceOwner");
+            return;
+        }
+        try {
+            mDevicePolicyManager.removeUser(mComponent, null);
+            fail("did not throw expected SecurityException");
+        } catch (SecurityException e) {
+            assertDeviceOwnerMessage(e.getMessage());
+        }
+    }
+
+    public void testSetApplicationHidden_failIfNotDeviceOrProfileOwner() {
+        if (!mDeviceAdmin) {
+            Log.w(TAG, "Skipping testSetApplicationHidden_failIfNotDeviceOrProfileOwner");
+            return;
+        }
+        try {
+            mDevicePolicyManager.setApplicationHidden(mComponent, "com.google.anything", true);
+            fail("did not throw expected SecurityException");
+        } catch (SecurityException e) {
+            assertProfileOwnerMessage(e.getMessage());
+        }
+    }
+
+    public void testSetApplicationsHidden_failIfNotDeviceOrProfileOwner() {
+        if (!mDeviceAdmin) {
+            Log.w(TAG, "Skipping testSetApplicationsHidden_failIfNotDeviceOrProfileOwner");
+            return;
+        }
+        try {
+            Intent pdfViewIntent = new Intent(Intent.ACTION_VIEW).setType("application/pdf");
+            mDevicePolicyManager.setApplicationsHidden(mComponent, pdfViewIntent, true);
+            fail("did not throw expected SecurityException");
+        } catch (SecurityException e) {
+            assertProfileOwnerMessage(e.getMessage());
+        }
+    }
+
+    public void testIsApplicationHidden_failIfNotDeviceOrProfileOwner() {
+        if (!mDeviceAdmin) {
+            Log.w(TAG, "Skipping testIsApplicationHidden_failIfNotDeviceOrProfileOwner");
+            return;
+        }
+        try {
+            mDevicePolicyManager.isApplicationHidden(mComponent, "com.google.anything");
+            fail("did not throw expected SecurityException");
+        } catch (SecurityException e) {
+            assertProfileOwnerMessage(e.getMessage());
+        }
+    }
+
+    public void testSetGlobalSetting_failIfNotDeviceOwner() {
+        if (!mDeviceAdmin) {
+            Log.w(TAG, "Skipping testSetGlobalSetting_failIfNotDeviceOwner");
+            return;
+        }
+        try {
+            mDevicePolicyManager.setGlobalSetting(mComponent,
+                    Settings.Global.WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON, "1");
+            fail("did not throw expected SecurityException");
+        } catch (SecurityException e) {
+            assertDeviceOwnerMessage(e.getMessage());
+        }
+    }
+
+    public void testSetSecureSetting_failIfNotDeviceOrProfileOwner() {
+        if (!mDeviceAdmin) {
+            Log.w(TAG, "Skipping testSetSecureSetting_failIfNotDeviceOrProfileOwner");
+            return;
+        }
+        try {
+            mDevicePolicyManager.setSecureSetting(mComponent,
+                    Settings.Secure.INSTALL_NON_MARKET_APPS, "1");
+            fail("did not throw expected SecurityException");
+        } catch (SecurityException e) {
+            assertProfileOwnerMessage(e.getMessage());
+        }
+    }
+
+    public void testSetMasterVolumeMuted_failIfNotDeviceOrProfileOwner() {
+        if (!mDeviceAdmin) {
+            Log.w(TAG, "Skipping testSetMasterVolumeMuted_failIfNotDeviceOrProfileOwner");
+            return;
+        }
+        try {
+            mDevicePolicyManager.setMasterVolumeMuted(mComponent, true);
+            fail("did not throw expected SecurityException");
+        } catch (SecurityException e) {
+            assertProfileOwnerMessage(e.getMessage());
+        }
+    }
+
+    public void testIsMasterVolumeMuted_failIfNotDeviceOrProfileOwner() {
+        if (!mDeviceAdmin) {
+            Log.w(TAG, "Skipping testSetMasterVolumeMuted_failIfNotDeviceOrProfileOwner");
+            return;
+        }
+        try {
+            mDevicePolicyManager.isMasterVolumeMuted(mComponent);
+            fail("did not throw expected SecurityException");
+        } catch (SecurityException e) {
+            assertProfileOwnerMessage(e.getMessage());
+        }
+    }
+
+    public void testSetRecommendedGlobalProxy_failIfNotDeviceOwner() {
+        if (!mDeviceAdmin) {
+            Log.w(TAG, "Skipping testSetRecommendedGlobalProxy_failIfNotDeviceOwner");
+            return;
+        }
+        try {
+            mDevicePolicyManager.setRecommendedGlobalProxy(mComponent, null);
+            fail("did not throw expected SecurityException");
+        } catch (SecurityException e) {
+            assertDeviceOwnerMessage(e.getMessage());
+        }
+    }
+
+    public void testSetLockTaskPackages_failIfNotDeviceOwner() {
+        if (!mDeviceAdmin) {
+            Log.w(TAG, "Skipping testSetLockTaskPackages_failIfNotDeviceOwner");
+            return;
+        }
+        try {
+            mDevicePolicyManager.setLockTaskPackages(new String[] {"package"});
+            fail("did not throw expected SecurityException");
+        } catch (SecurityException e) {
+        }
+    }
+
+    public void testClearDeviceOwnerApp_failIfNotDeviceOwner() {
+        if (!mDeviceAdmin) {
+            Log.w(TAG, "Skipping testClearDeviceOwnerApp_failIfNotDeviceOwner");
+            return;
+        }
+        try {
+            mDevicePolicyManager.clearDeviceOwnerApp("android.deviceadmin.cts");
+            fail("did not throw expected SecurityException");
+        } catch (SecurityException e) {
+            assertDeviceOwnerMessage(e.getMessage());
+        }
+    }
+
+    public void testSwitchUser_failIfNotDeviceOwner() {
+        if (!mDeviceAdmin) {
+            Log.w(TAG, "Skipping testSwitchUser_failIfNotDeviceOwner");
+            return;
+        }
+        try {
+            mDevicePolicyManager.switchUser(mComponent, null);
+            fail("did not throw expected SecurityException");
+        } catch (SecurityException e) {
+            assertDeviceOwnerMessage(e.getMessage());
+        }
+    }
+
+    public void testCreateAndInitializeUser_failIfNotDeviceOwner() {
+        if (!mDeviceAdmin) {
+            Log.w(TAG, "Skipping testCreateAndInitializeUser_failIfNotDeviceOwner");
+            return;
+        }
+        try {
+            mDevicePolicyManager.createAndInitializeUser(mComponent, "name", "admin name",
+                        mComponent, null);
+            fail("did not throw expected SecurityException");
+        } catch (SecurityException e) {
+            assertDeviceOwnerMessage(e.getMessage());
+        }
+    }
+
+    private void assertDeviceOwnerMessage(String message) {
+        assertTrue("message is: "+ message, message.contains("does not own the device")
+                || message.contains("can only be called by the device owner"));
+    }
+
+    private void assertProfileOwnerMessage(String message) {
+        assertTrue(message.contains("does not own the profile"));
+    }
 }
diff --git a/tests/tests/animation/Android.mk b/tests/tests/animation/Android.mk
index a83bb65..3d8daf7 100644
--- a/tests/tests/animation/Android.mk
+++ b/tests/tests/animation/Android.mk
@@ -24,9 +24,6 @@
 # When built, explicitly put it in the data partition.
 LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
 
-# All tests should include android.test.runner.
-LOCAL_JAVA_LIBRARIES := android.test.runner
-
 LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
diff --git a/tests/tests/animation/AndroidManifest.xml b/tests/tests/animation/AndroidManifest.xml
index 2212643..fdc5ad9 100644
--- a/tests/tests/animation/AndroidManifest.xml
+++ b/tests/tests/animation/AndroidManifest.xml
@@ -27,8 +27,11 @@
         <uses-library android:name="android.test.runner" />
     </application>
 
-    <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
                      android:targetPackage="com.android.cts.animation"
-                     android:label="CTS tests for android.animation package"/>
+                     android:label="CTS tests for android.animation package">
+        <meta-data android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
 </manifest>
 
diff --git a/tests/tests/animation/src/android/animation/cts/AnimationActivity.java b/tests/tests/animation/src/android/animation/cts/AnimationActivity.java
index 555ab15..225a97e 100644
--- a/tests/tests/animation/src/android/animation/cts/AnimationActivity.java
+++ b/tests/tests/animation/src/android/animation/cts/AnimationActivity.java
@@ -145,6 +145,11 @@
         view.animateBall();
     }
 
+    public void startSingleAnimation(Animator animator) {
+        animator.setTarget(view.newBall);
+        animator.start();
+    }
+
     public void startAnimation(ValueAnimator valueAnimator){
         view.bounceYAnimator = valueAnimator;
         view.startColorAnimator();
diff --git a/tests/tests/animation/src/android/animation/cts/PropertyValuesHolderTest.java b/tests/tests/animation/src/android/animation/cts/PropertyValuesHolderTest.java
index 26db933..200ebce 100644
--- a/tests/tests/animation/src/android/animation/cts/PropertyValuesHolderTest.java
+++ b/tests/tests/animation/src/android/animation/cts/PropertyValuesHolderTest.java
@@ -16,15 +16,21 @@
 package android.animation.cts;
 
 import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
 import android.animation.ArgbEvaluator;
+import android.animation.Keyframe;
 import android.animation.ObjectAnimator;
 import android.animation.PropertyValuesHolder;
 import android.animation.ValueAnimator;
+import android.graphics.drawable.ShapeDrawable;
 import android.test.ActivityInstrumentationTestCase2;
 import android.util.Property;
 import android.view.View;
 import android.view.animation.AccelerateInterpolator;
 
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
 public class PropertyValuesHolderTest extends
         ActivityInstrumentationTestCase2<AnimationActivity> {
     private AnimationActivity mActivity;
@@ -86,6 +92,113 @@
         assertResults(yArray, mStartY, mEndY);
     }
 
+    private ObjectAnimator createAnimator(Keyframe... keyframes) {
+        PropertyValuesHolder pVHolder = PropertyValuesHolder.ofKeyframe(mProperty, keyframes);
+        ObjectAnimator objAnimator = ObjectAnimator.ofPropertyValuesHolder(mObject,pVHolder);
+        objAnimator.setDuration(mDuration);
+        objAnimator.setInterpolator(new AccelerateInterpolator());
+        return objAnimator;
+    }
+
+    private void waitUntilFinished(ObjectAnimator objectAnimator, long timeoutMilliseconds)
+            throws InterruptedException {
+        final CountDownLatch latch = new CountDownLatch(1);
+        objectAnimator.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                super.onAnimationEnd(animation);
+                latch.countDown();
+            }
+        });
+        latch.await(timeoutMilliseconds, TimeUnit.MILLISECONDS);
+        getInstrumentation().waitForIdleSync();
+    }
+
+    private void setTarget(final Animator animator, final Object target) throws Throwable {
+        runTestOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                animator.setTarget(target);
+            }
+        });
+    }
+
+    private void startSingleAnimation(final Animator animator) throws Throwable {
+        runTestOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                mActivity.startSingleAnimation(animator);
+            }
+        });
+    }
+
+    public void testResetValues() throws Throwable {
+        final float initialY = mActivity.view.newBall.getY();
+        Keyframe emptyKeyframe1 = Keyframe.ofFloat(.0f);
+        ObjectAnimator objAnimator1 = createAnimator(emptyKeyframe1, Keyframe.ofFloat(1f, 100f));
+        startSingleAnimation(objAnimator1);
+        assertTrue("Keyframe should be assigned a value", emptyKeyframe1.hasValue());
+        assertEquals("Keyframe should get the value from the target", emptyKeyframe1.getValue(),
+                initialY);
+        waitUntilFinished(objAnimator1, mDuration * 2);
+        assertEquals(100f, mActivity.view.newBall.getY());
+        startSingleAnimation(objAnimator1);
+        waitUntilFinished(objAnimator1, mDuration * 2);
+
+        // run another ObjectAnimator that will move the Y value to something else
+        Keyframe emptyKeyframe2 = Keyframe.ofFloat(.0f);
+        ObjectAnimator objAnimator2 = createAnimator(emptyKeyframe2, Keyframe.ofFloat(1f, 200f));
+        startSingleAnimation(objAnimator2);
+        assertTrue("Keyframe should be assigned a value", emptyKeyframe2.hasValue());
+        assertEquals("Keyframe should get the value from the target", emptyKeyframe2.getValue(), 100f);
+        waitUntilFinished(objAnimator2, mDuration * 2);
+        assertEquals(200f, mActivity.view.newBall.getY());
+
+        // re-run first object animator. since its target did not change, it should have the same
+        // start value for kf1
+        startSingleAnimation(objAnimator1);
+        assertEquals(emptyKeyframe1.getValue(), initialY);
+        waitUntilFinished(objAnimator1, mDuration * 2);
+
+        Keyframe fullKeyframe = Keyframe.ofFloat(.0f, 333f);
+        ObjectAnimator objAnimator3 = createAnimator(fullKeyframe, Keyframe.ofFloat(1f, 500f));
+        startSingleAnimation(objAnimator3);
+        assertEquals("When keyframe has value, should not be assigned from the target object",
+                fullKeyframe.getValue(), 333f);
+        waitUntilFinished(objAnimator3, mDuration * 2);
+
+        // now, null out the target of the first animator
+        float updatedY = mActivity.view.newBall.getY();
+        setTarget(objAnimator1, null);
+        startSingleAnimation(objAnimator1);
+        assertTrue("Keyframe should get a value", emptyKeyframe1.hasValue());
+        assertEquals("Keyframe should get the updated Y value", emptyKeyframe1.getValue(), updatedY);
+        waitUntilFinished(objAnimator1, mDuration * 2);
+        assertEquals("Animation should run as expected", 100f, mActivity.view.newBall.getY());
+
+        // now, reset the target of the fully defined animation.
+        setTarget(objAnimator3, null);
+        startSingleAnimation(objAnimator3);
+        assertEquals("When keyframe is fully defined, its value should not change when target is"
+                + " reset", fullKeyframe.getValue(), 333f);
+        waitUntilFinished(objAnimator3, mDuration * 2);
+
+        // run the other one to change Y value
+        startSingleAnimation(objAnimator2);
+        waitUntilFinished(objAnimator2, mDuration * 2);
+        // now, set another target w/ the same View type. it should still reset
+        ShapeHolder view = new ShapeHolder(new ShapeDrawable());
+        updatedY = mActivity.view.newBall.getY();
+        setTarget(objAnimator1, view);
+        startSingleAnimation(objAnimator1);
+        assertTrue("Keyframe should get a value when target is set to another view of the same"
+                + " class", emptyKeyframe1.hasValue());
+        assertEquals("Keyframe should get the updated Y value when target is set to another view"
+                + " of the same class", emptyKeyframe1.getValue(), updatedY);
+        waitUntilFinished(objAnimator1, mDuration * 2);
+        assertEquals("Animation should run as expected", 100f, mActivity.view.newBall.getY());
+    }
+
     public void testOffloat() throws Throwable {
         float[] values = {mStartY, mEndY};
         PropertyValuesHolder pVHolder = PropertyValuesHolder.ofFloat(mProperty, values);
diff --git a/tests/tests/app/AndroidManifest.xml b/tests/tests/app/AndroidManifest.xml
index acfc3c8..134df64 100644
--- a/tests/tests/app/AndroidManifest.xml
+++ b/tests/tests/app/AndroidManifest.xml
@@ -23,9 +23,12 @@
         <uses-library android:name="android.test.runner" />
     </application>
 
-    <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
                      android:targetPackage="com.android.cts.stub"
-                     android:label="CTS tests of android.app"/>
+                     android:label="CTS tests of android.app">
+        <meta-data android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
 
 </manifest>
 
diff --git a/tests/tests/app/src/android/app/cts/InstrumentationTest.java b/tests/tests/app/src/android/app/cts/InstrumentationTest.java
index 30cdd5f..16a4ee1 100644
--- a/tests/tests/app/src/android/app/cts/InstrumentationTest.java
+++ b/tests/tests/app/src/android/app/cts/InstrumentationTest.java
@@ -667,6 +667,24 @@
             @Override
             public void takeInputQueue(InputQueue.Callback queue) {
             }
+
+            @Override
+            public void setStatusBarColor(int color) {
+            }
+
+            @Override
+            public int getStatusBarColor() {
+                return 0;
+            }
+
+            @Override
+            public void setNavigationBarColor(int color) {
+            }
+
+            @Override
+            public int getNavigationBarColor() {
+                return 0;
+            }
         }
     }
 
diff --git a/tests/tests/app/src/android/app/cts/ServiceTest.java b/tests/tests/app/src/android/app/cts/ServiceTest.java
index f9a81e6..675b7ae 100644
--- a/tests/tests/app/src/android/app/cts/ServiceTest.java
+++ b/tests/tests/app/src/android/app/cts/ServiceTest.java
@@ -16,8 +16,6 @@
 
 package android.app.cts;
 
-
-import android.app.Service;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
@@ -51,9 +49,11 @@
     private IBinder mStateReceiver;
 
     private static class EmptyConnection implements ServiceConnection {
+        @Override
         public void onServiceConnected(ComponentName name, IBinder service) {
         }
 
+        @Override
         public void onServiceDisconnected(ComponentName name) {
         }
     }
@@ -74,6 +74,7 @@
             mMonitor = v;
         }
 
+        @Override
         public void onServiceConnected(ComponentName name, IBinder service) {
             if (mSetReporter) {
                 Parcel data = Parcel.obtain();
@@ -109,6 +110,7 @@
             }
         }
 
+        @Override
         public void onServiceDisconnected(ComponentName name) {
             if (mMonitor) {
                 if (mExpectedServiceState == STATE_DESTROY) {
@@ -150,15 +152,6 @@
         waitForResultOrThrow(DELAY, "service to be destroyed");
     }
 
-    private void startExpectNoPermission(Intent service) {
-        try {
-            mContext.startService(service);
-            fail("Expected security exception when starting " + service);
-        } catch (SecurityException e) {
-            // expected
-        }
-    }
-
     /**
      * test the service lifecycle, a service can be used in two ways:
      * 1  It can be started and allowed to run until someone stops it or it stops itself.
@@ -294,18 +287,6 @@
         waitForResultOrThrow(DELAY, "disconnecting from service");
     }
 
-    private void bindExpectNoPermission(Intent service) {
-        TestConnection conn = new TestConnection(false, false);
-        try {
-            mContext.bindService(service, conn, Context.BIND_AUTO_CREATE);
-            fail("Expected security exception when binding " + service);
-        } catch (SecurityException e) {
-            // expected
-        } finally {
-            mContext.unbindService(conn);
-        }
-    }
-
     @Override
     protected void setUp() throws Exception {
         super.setUp();
@@ -313,8 +294,10 @@
         mLocalService = new Intent(mContext, LocalService.class);
         mLocalDeniedService = new Intent(mContext, LocalDeniedService.class);
         mLocalGrantedService = new Intent(mContext, LocalGrantedService.class);
-        mLocalService_ApplicationHasPermission = new Intent(LocalService.SERVICE_LOCAL_GRANTED);
-        mLocalService_ApplicationDoesNotHavePermission = new Intent(LocalService.SERVICE_LOCAL_DENIED);
+        mLocalService_ApplicationHasPermission = new Intent(
+                LocalService.SERVICE_LOCAL_GRANTED, null /*uri*/, mContext, LocalService.class);
+        mLocalService_ApplicationDoesNotHavePermission = new Intent(
+                LocalService.SERVICE_LOCAL_DENIED, null /*uri*/, mContext, LocalService.class);
         mStateReceiver = new MockBinder();
     }
 
@@ -392,7 +375,8 @@
     }
 
     public void testLocalStartAction() throws Exception {
-        startExpectResult(new Intent(LocalService.SERVICE_LOCAL));
+        startExpectResult(new Intent(
+                LocalService.SERVICE_LOCAL, null /*uri*/, mContext, LocalService.class));
     }
 
     public void testLocalBindClass() throws Exception {
@@ -401,7 +385,8 @@
 
     @MediumTest
     public void testLocalBindAction() throws Exception {
-        bindExpectResult(new Intent(LocalService.SERVICE_LOCAL));
+        bindExpectResult(new Intent(
+                LocalService.SERVICE_LOCAL, null /*uri*/, mContext, LocalService.class));
     }
 
     @MediumTest
@@ -411,7 +396,8 @@
 
     @MediumTest
     public void testLocalBindAutoAction() throws Exception {
-        bindAutoExpectResult(new Intent(LocalService.SERVICE_LOCAL));
+        bindAutoExpectResult(new Intent(
+                LocalService.SERVICE_LOCAL, null /*uri*/, mContext, LocalService.class));
     }
 
     @MediumTest
diff --git a/tests/tests/appwidget/Android.mk b/tests/tests/appwidget/Android.mk
new file mode 100644
index 0000000..8641d53
--- /dev/null
+++ b/tests/tests/appwidget/Android.mk
@@ -0,0 +1,29 @@
+# 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_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_PACKAGE_NAME := CtsAppWidgetTestCases
+
+LOCAL_STATIC_JAVA_LIBRARIES := mockito-target ctstestrunner
+
+include $(BUILD_CTS_PACKAGE)
diff --git a/tests/tests/appwidget/AndroidManifest.xml b/tests/tests/appwidget/AndroidManifest.xml
new file mode 100644
index 0000000..4d9b61d
--- /dev/null
+++ b/tests/tests/appwidget/AndroidManifest.xml
@@ -0,0 +1,54 @@
+<?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="android.cts.appwidget">
+
+  <application>
+      <uses-library android:name="android.test.runner"/>
+
+      <receiver android:name="android.appwidget.cts.provider.FirstAppWidgetProvider" >
+          <intent-filter>
+              <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
+          </intent-filter>
+          <meta-data android:name="android.appwidget.provider"
+              android:resource="@xml/first_appwidget_info" />
+      </receiver>
+
+      <receiver android:name="android.appwidget.cts.provider.SecondAppWidgetProvider" >
+          <intent-filter>
+              <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
+          </intent-filter>
+          <meta-data android:name="android.appwidget.provider"
+              android:resource="@xml/second_appwidget_info" />
+      </receiver>
+
+      <service android:name="android.appwidget.cts.service.MyAppWidgetService"
+          android:permission="android.permission.BIND_REMOTEVIEWS">
+      </service>
+
+  </application>
+
+  <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+      android:targetPackage="android.cts.appwidget"
+      android:label="Tests for the app widget APIs.">
+      <meta-data android:name="listener"
+          android:value="com.android.cts.runner.CtsTestRunListener" />
+  </instrumentation>
+
+</manifest>
diff --git a/tests/tests/appwidget/res/drawable/first_android_icon.png b/tests/tests/appwidget/res/drawable/first_android_icon.png
new file mode 100644
index 0000000..4ba97a5
--- /dev/null
+++ b/tests/tests/appwidget/res/drawable/first_android_icon.png
Binary files differ
diff --git a/tests/tests/appwidget/res/drawable/second_android_icon.png b/tests/tests/appwidget/res/drawable/second_android_icon.png
new file mode 100644
index 0000000..4ba97a5
--- /dev/null
+++ b/tests/tests/appwidget/res/drawable/second_android_icon.png
Binary files differ
diff --git a/tests/tests/appwidget/res/layout/collection_widget_item_layout.xml b/tests/tests/appwidget/res/layout/collection_widget_item_layout.xml
new file mode 100644
index 0000000..50f49c1
--- /dev/null
+++ b/tests/tests/appwidget/res/layout/collection_widget_item_layout.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.
+-->
+
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/text_view"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content">
+</TextView>
diff --git a/tests/tests/appwidget/res/layout/collection_widget_layout.xml b/tests/tests/appwidget/res/layout/collection_widget_layout.xml
new file mode 100644
index 0000000..0489bca
--- /dev/null
+++ b/tests/tests/appwidget/res/layout/collection_widget_layout.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.
+-->
+
+<StackView xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/stack_view"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+</StackView>
diff --git a/tests/tests/appwidget/res/layout/first_initial_keyguard_layout.xml b/tests/tests/appwidget/res/layout/first_initial_keyguard_layout.xml
new file mode 100644
index 0000000..bebec9f
--- /dev/null
+++ b/tests/tests/appwidget/res/layout/first_initial_keyguard_layout.xml
@@ -0,0 +1,21 @@
+<?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.
+-->
+
+<Button xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content">
+</Button>
diff --git a/tests/tests/appwidget/res/layout/first_initial_layout.xml b/tests/tests/appwidget/res/layout/first_initial_layout.xml
new file mode 100644
index 0000000..bebec9f
--- /dev/null
+++ b/tests/tests/appwidget/res/layout/first_initial_layout.xml
@@ -0,0 +1,21 @@
+<?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.
+-->
+
+<Button xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content">
+</Button>
diff --git a/tests/tests/appwidget/res/layout/second_initial_keyguard_layout.xml b/tests/tests/appwidget/res/layout/second_initial_keyguard_layout.xml
new file mode 100644
index 0000000..bebec9f
--- /dev/null
+++ b/tests/tests/appwidget/res/layout/second_initial_keyguard_layout.xml
@@ -0,0 +1,21 @@
+<?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.
+-->
+
+<Button xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content">
+</Button>
diff --git a/tests/tests/appwidget/res/layout/second_initial_layout.xml b/tests/tests/appwidget/res/layout/second_initial_layout.xml
new file mode 100644
index 0000000..bebec9f
--- /dev/null
+++ b/tests/tests/appwidget/res/layout/second_initial_layout.xml
@@ -0,0 +1,21 @@
+<?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.
+-->
+
+<Button xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content">
+</Button>
diff --git a/tests/tests/appwidget/res/values/constants.xml b/tests/tests/appwidget/res/values/constants.xml
new file mode 100644
index 0000000..375dae3
--- /dev/null
+++ b/tests/tests/appwidget/res/values/constants.xml
@@ -0,0 +1,51 @@
+<?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>
+
+    <!-- First widget provider -->
+
+    <dimen name="first_min_appwidget_size">40dp</dimen>
+
+    <dimen name="first_min_resize_appwidget_size">60dp</dimen>
+
+    <integer name="first_update_period_millis">86400000</integer>
+
+    <integer name="first_resize_mode">3</integer>
+
+    <integer name="first_widget_category">3</integer>
+
+    <item type="id" name="first_auto_advance_view_id"/>
+
+    <!-- Second widget provider -->
+
+    <dimen name="second_min_appwidget_size">50dp</dimen>
+
+    <dimen name="second_min_resize_appwidget_size">70dp</dimen>
+
+    <integer name="second_update_period_millis">86500000</integer>
+
+    <integer name="second_resize_mode">1</integer>
+
+    <integer name="second_widget_category">1</integer>
+
+    <item type="id" name="second_auto_advance_view_id"/>
+
+    <string name="foo">Foo</string>
+
+</resources>
diff --git a/tests/tests/appwidget/res/xml/first_appwidget_info.xml b/tests/tests/appwidget/res/xml/first_appwidget_info.xml
new file mode 100644
index 0000000..d7c1028
--- /dev/null
+++ b/tests/tests/appwidget/res/xml/first_appwidget_info.xml
@@ -0,0 +1,31 @@
+<?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.
+-->
+<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
+    android:minWidth="@dimen/first_min_appwidget_size"
+    android:minHeight="@dimen/first_min_appwidget_size"
+    android:minResizeWidth="@dimen/first_min_resize_appwidget_size"
+    android:minResizeHeight="@dimen/first_min_resize_appwidget_size"
+    android:updatePeriodMillis="@integer/first_update_period_millis"
+    android:configure="android.appwidget.cts.provider.FirstAppWidgetConfigureActivity"
+    android:resizeMode="horizontal|vertical"
+    android:widgetCategory="home_screen|keyguard"
+    android:initialLayout="@layout/first_initial_layout"
+    android:initialKeyguardLayout="@layout/first_initial_keyguard_layout"
+    android:previewImage="@drawable/first_android_icon"
+    android:autoAdvanceViewId="@id/first_auto_advance_view_id">
+</appwidget-provider>
diff --git a/tests/tests/appwidget/res/xml/second_appwidget_info.xml b/tests/tests/appwidget/res/xml/second_appwidget_info.xml
new file mode 100644
index 0000000..d192b10
--- /dev/null
+++ b/tests/tests/appwidget/res/xml/second_appwidget_info.xml
@@ -0,0 +1,31 @@
+<?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.
+-->
+<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
+    android:minWidth="@dimen/second_min_appwidget_size"
+    android:minHeight="@dimen/second_min_appwidget_size"
+    android:minResizeWidth="@dimen/second_min_resize_appwidget_size"
+    android:minResizeHeight="@dimen/second_min_resize_appwidget_size"
+    android:updatePeriodMillis="@integer/second_update_period_millis"
+    android:configure="android.appwidget.cts.provider.SecondAppWidgetConfigureActivity"
+    android:resizeMode="horizontal"
+    android:widgetCategory="home_screen"
+    android:initialLayout="@layout/second_initial_layout"
+    android:initialKeyguardLayout="@layout/second_initial_keyguard_layout"
+    android:previewImage="@drawable/second_android_icon"
+    android:autoAdvanceViewId="@id/second_auto_advance_view_id">
+</appwidget-provider>
diff --git a/tests/tests/appwidget/src/android/appwidget/cts/AppWidgetTest.java b/tests/tests/appwidget/src/android/appwidget/cts/AppWidgetTest.java
new file mode 100644
index 0000000..654fb0d
--- /dev/null
+++ b/tests/tests/appwidget/src/android/appwidget/cts/AppWidgetTest.java
@@ -0,0 +1,1387 @@
+/*
+ * 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.appwidget.cts;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.argThat;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Matchers.same;
+import static org.mockito.Mockito.atLeastOnce;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.inOrder;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.appwidget.AppWidgetHost;
+import android.appwidget.AppWidgetHostView;
+import android.appwidget.AppWidgetManager;
+import android.appwidget.AppWidgetProviderInfo;
+import android.appwidget.cts.provider.AppWidgetProviderCallbacks;
+import android.appwidget.cts.provider.FirstAppWidgetProvider;
+import android.appwidget.cts.provider.SecondAppWidgetProvider;
+import android.appwidget.cts.service.MyAppWidgetService;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.UserInfo;
+import android.cts.appwidget.R;
+import android.graphics.drawable.Drawable;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.ParcelFileDescriptor;
+import android.os.Process;
+import android.os.SystemClock;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.test.InstrumentationTestCase;
+import android.text.TextUtils;
+import android.util.DisplayMetrics;
+import android.widget.RemoteViews;
+import android.widget.RemoteViewsService.RemoteViewsFactory;
+import libcore.io.IoUtils;
+import libcore.io.Streams;
+import org.hamcrest.BaseMatcher;
+import org.hamcrest.Description;
+import org.mockito.InOrder;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicInteger;
+
+public class AppWidgetTest extends InstrumentationTestCase {
+
+    private static final long OPERATION_TIMEOUT = 20 * 1000; // 20 sec
+
+    private static final String FIRST_APP_WIDGET_CONFIGURE_ACTIVITY =
+            "android.appwidget.cts.provider.FirstAppWidgetConfigureActivity";
+
+    private static final String SECOND_APP_WIDGET_CONFIGURE_ACTIVITY =
+            "android.appwidget.cts.provider.SecondAppWidgetConfigureActivity";
+
+    private final Object mLock = new Object();
+
+    @Override
+    public void setUp() throws Exception {
+        // 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());
+    }
+
+    private static final String GRANT_BIND_APP_WIDGET_PERMISSION_COMMAND =
+            "appwidget grantbind --package android.cts.appwidget --user 0";
+
+    private static final String REVOKE_BIND_APP_WIDGET_PERMISSION_COMMAND =
+            "appwidget revokebind --package android.cts.appwidget --user 0";
+
+    public void testGetAppInstalledProvidersForCurrentUserLegacy() throws Exception {
+        // By default we should get only providers for the current user.
+        List<AppWidgetProviderInfo> providers = getAppWidgetManager().getInstalledProviders();
+
+        // Make sure we have our two providers in the list.
+        assertExpectedInstalledProviders(providers);
+    }
+
+    public void testGetAppInstalledProvidersForCurrentUserNewCurrentProfile() throws Exception {
+        // We ask only for providers for the current user.
+        List<AppWidgetProviderInfo> providers = getAppWidgetManager()
+                .getInstalledProvidersForProfile(Process.myUserHandle());
+
+        // Make sure we have our two providers in the list.
+        assertExpectedInstalledProviders(providers);
+    }
+
+    public void testGetAppInstalledProvidersForCurrentUserNewAllProfiles() throws Exception {
+        // We ask only for providers for all current user's profiles
+        UserManager userManager = (UserManager) getInstrumentation()
+                .getTargetContext().getSystemService(Context.USER_SERVICE);
+
+        List<AppWidgetProviderInfo> allProviders = new ArrayList<>();
+
+        List<UserHandle> profiles = userManager.getUserProfiles();
+        final int profileCount = profiles.size();
+        for (int i = 0; i < profileCount; i++) {
+            UserHandle profile = profiles.get(i);
+            List<AppWidgetProviderInfo> providers = getAppWidgetManager()
+                    .getInstalledProvidersForProfile(profile);
+            allProviders.addAll(providers);
+        }
+
+        // Make sure we have our two providers in the list.
+        assertExpectedInstalledProviders(allProviders);
+    }
+
+    public void testBindAppWidget() throws Exception {
+        // Create a host and start listening.
+        AppWidgetHost host = new AppWidgetHost(getInstrumentation().getTargetContext(), 0);
+        host.startListening();
+
+        // Allocate an app widget id to bind.
+        final int appWidgetId = host.allocateAppWidgetId();
+
+        // Grab a provider we defined to be bound.
+        AppWidgetProviderInfo provider = getFirstAppWidgetProviderInfo();
+
+        // Bind the widget.
+        boolean widgetBound = getAppWidgetManager().bindAppWidgetIdIfAllowed(appWidgetId,
+                provider.getProfile(), provider.provider, null);
+        assertFalse(widgetBound);
+
+        // Well, app do not have this permission unless explicitly granted
+        // by the user. Now we will pretent for the user and grant it.
+        grantBindAppWidgetPermission();
+
+        try {
+            // Bind the widget as we have a permission for that.
+            widgetBound = getAppWidgetManager().bindAppWidgetIdIfAllowed(
+                    appWidgetId, provider.getProfile(), provider.provider, null);
+            assertTrue(widgetBound);
+
+            // Deallocate the app widget id.
+            host.deleteAppWidgetId(appWidgetId);
+        } finally {
+            // Clean up.
+            host.deleteHost();
+            revokeBindAppWidgetPermission();
+        }
+    }
+
+    public void testAppWidgetProviderCallbacks() throws Exception {
+        final AtomicInteger disabledCallCounter = new AtomicInteger();
+
+        // Set a mock to intercept provider callbacks.
+        AppWidgetProviderCallbacks callbacks = mock(AppWidgetProviderCallbacks.class);
+        doAnswer(new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                synchronized (mLock) {
+                    disabledCallCounter.incrementAndGet();
+                    mLock.notifyAll();
+                }
+                return null;
+            }
+        }).when(callbacks).onDeleted(any(Context.class), any(int[].class));
+        FirstAppWidgetProvider.setCallbacks(callbacks);
+
+        final int firstAppWidgetId;
+        final int secondAppWidgetId;
+
+        final Bundle firstOptions;
+        final Bundle secondOptions;
+
+        // Create a host and start listening.
+        AppWidgetHost host = spy(new AppWidgetHost(getInstrumentation().getTargetContext(), 0));
+        host.startListening();
+
+        // We want to bind a widget.
+        grantBindAppWidgetPermission();
+        try {
+            // Allocate the first widget id to bind.
+            firstAppWidgetId = host.allocateAppWidgetId();
+
+            // Grab a provider we defined to be bound.
+            AppWidgetProviderInfo provider = getFirstAppWidgetProviderInfo();
+
+            // Bind the first widget.
+            getAppWidgetManager().bindAppWidgetIdIfAllowed(firstAppWidgetId,
+                    provider.getProfile(), provider.provider, null);
+
+            // Update the first widget options.
+            firstOptions = new Bundle();
+            firstOptions.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH, 1);
+            firstOptions.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT, 2);
+            firstOptions.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH, 3);
+            firstOptions.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT, 4);
+            getAppWidgetManager().updateAppWidgetOptions(firstAppWidgetId, firstOptions);
+
+            // Allocate the second app widget id to bind.
+            secondAppWidgetId = host.allocateAppWidgetId();
+
+            // Bind the second widget.
+            getAppWidgetManager().bindAppWidgetIdIfAllowed(secondAppWidgetId,
+                    provider.getProfile(), provider.provider, null);
+
+            // Update the second widget options.
+            secondOptions = new Bundle();
+            secondOptions.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH, 5);
+            secondOptions.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT, 6);
+            secondOptions.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH, 7);
+            secondOptions.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT, 8);
+            getAppWidgetManager().updateAppWidgetOptions(secondAppWidgetId, secondOptions);
+
+            // Delete the first widget.
+            host.deleteAppWidgetId(firstAppWidgetId);
+
+            // Delete the second widget.
+            host.deleteAppWidgetId(secondAppWidgetId);
+
+            waitForCallCount(disabledCallCounter, 1);
+
+            // Make sure the provider callbacks are correct.
+            InOrder inOrder = inOrder(callbacks);
+
+            inOrder.verify(callbacks).onEnabled(any(Context.class));
+            inOrder.verify(callbacks).onUpdate(any(Context.class),
+                    any(AppWidgetManager.class), eq(new int[] {firstAppWidgetId}));
+            inOrder.verify(callbacks).onAppWidgetOptionsChanged(any(Context.class),
+                    any(AppWidgetManager.class), same(firstAppWidgetId), argThat(
+                            new OptionsMatcher(firstOptions)));
+            inOrder.verify(callbacks).onUpdate(any(Context.class),
+                    any(AppWidgetManager.class), eq(new int[] {secondAppWidgetId}));
+            inOrder.verify(callbacks).onAppWidgetOptionsChanged(any(Context.class),
+                    any(AppWidgetManager.class), same(secondAppWidgetId), argThat(
+                            new OptionsMatcher(secondOptions)));
+            inOrder.verify(callbacks).onDeleted(any(Context.class),
+                    argThat(new WidgetIdsMatcher(new int[]{firstAppWidgetId})));
+            inOrder.verify(callbacks).onDeleted(any(Context.class),
+                    argThat(new WidgetIdsMatcher(new int[]{secondAppWidgetId})));
+            inOrder.verify(callbacks).onDisabled(any(Context.class));
+        } finally {
+            // Clean up.
+            FirstAppWidgetProvider.setCallbacks(null);
+            revokeBindAppWidgetPermission();
+        }
+    }
+
+    public void testTwoAppWidgetProviderCallbacks() throws Exception {
+        final AtomicInteger disabledCallCounter = new AtomicInteger();
+
+        // Set a mock to intercept first provider callbacks.
+        AppWidgetProviderCallbacks firstCallbacks = mock(AppWidgetProviderCallbacks.class);
+        FirstAppWidgetProvider.setCallbacks(firstCallbacks);
+
+        // Set a mock to intercept second provider callbacks.
+        AppWidgetProviderCallbacks secondCallbacks = mock(AppWidgetProviderCallbacks.class);
+        doAnswer(new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                synchronized (mLock) {
+                    disabledCallCounter.incrementAndGet();
+                    mLock.notifyAll();
+                }
+                return null;
+            }
+        }).when(firstCallbacks).onDisabled(any(Context.class));
+        SecondAppWidgetProvider.setCallbacks(secondCallbacks);
+
+        final int firstAppWidgetId;
+        final int secondAppWidgetId;
+
+        // Create a host and start listening.
+        AppWidgetHost host = spy(new AppWidgetHost(
+                getInstrumentation().getTargetContext(), 0));
+        host.startListening();
+
+        // We want to bind widgets.
+        grantBindAppWidgetPermission();
+        try {
+            // Allocate the first widget id to bind.
+            firstAppWidgetId = host.allocateAppWidgetId();
+
+            // Allocate the second widget id to bind.
+            secondAppWidgetId = host.allocateAppWidgetId();
+
+            // Grab the first provider we defined to be bound.
+            AppWidgetProviderInfo firstProvider = getFirstAppWidgetProviderInfo();
+
+            // Bind the first widget.
+            getAppWidgetManager().bindAppWidgetIdIfAllowed(firstAppWidgetId,
+                    firstProvider.getProfile(), firstProvider.provider, null);
+
+            // Grab the second provider we defined to be bound.
+            AppWidgetProviderInfo secondProvider = getSecondAppWidgetProviderInfo();
+
+            // Bind the second widget.
+            getAppWidgetManager().bindAppWidgetIdIfAllowed(secondAppWidgetId,
+                    secondProvider.getProfile(), secondProvider.provider, null);
+
+            // Delete the first widget.
+            host.deleteAppWidgetId(firstAppWidgetId);
+
+            // Delete the second widget.
+            host.deleteAppWidgetId(secondAppWidgetId);
+
+            // Wait for all callbacks to settle.
+            waitForCallCount(disabledCallCounter, 1);
+
+            // Make sure the first provider callbacks are correct.
+            InOrder firstInOrder = inOrder(firstCallbacks);
+            firstInOrder.verify(firstCallbacks).onEnabled(any(Context.class));
+            firstInOrder.verify(firstCallbacks).onUpdate(any(Context.class),
+                    any(AppWidgetManager.class), eq(new int[]{firstAppWidgetId}));
+            firstInOrder.verify(firstCallbacks).onDeleted(any(Context.class),
+                    argThat(new WidgetIdsMatcher(new int[]{firstAppWidgetId})));
+            firstInOrder.verify(firstCallbacks).onDisabled(any(Context.class));
+
+            // Make sure the second provider callbacks are correct.
+            InOrder secondInOrder = inOrder(secondCallbacks);
+            secondInOrder.verify(secondCallbacks).onEnabled(any(Context.class));
+            secondInOrder.verify(secondCallbacks).onUpdate(any(Context.class),
+                    any(AppWidgetManager.class), eq(new int[]{secondAppWidgetId}));
+            secondInOrder.verify(secondCallbacks).onDeleted(any(Context.class),
+                    argThat(new WidgetIdsMatcher(new int[] {secondAppWidgetId})));
+            secondInOrder.verify(secondCallbacks).onDisabled(any(Context.class));
+        } finally {
+            // Clean up.
+            FirstAppWidgetProvider.setCallbacks(null);
+            SecondAppWidgetProvider.setCallbacks(null);
+            revokeBindAppWidgetPermission();
+        }
+    }
+
+    public void testGetAppWidgetIds() throws Exception {
+        // We want to bind widgets.
+        grantBindAppWidgetPermission();
+
+        // Create a host and start listening.
+        AppWidgetHost host = new AppWidgetHost(
+                getInstrumentation().getTargetContext(), 0);
+        host.startListening();
+
+        try {
+            // Grab the provider we defined to be bound.
+            AppWidgetProviderInfo provider = getFirstAppWidgetProviderInfo();
+
+            // Initially we have no widgets.
+            int[] widgetsIds = getAppWidgetManager().getAppWidgetIds(provider.provider);
+            assertTrue(widgetsIds.length == 0);
+
+            // Allocate the first widget id to bind.
+            final int firstAppWidgetId = host.allocateAppWidgetId();
+
+            // Bind the first widget.
+            getAppWidgetManager().bindAppWidgetIdIfAllowed(firstAppWidgetId,
+                    provider.getProfile(), provider.provider, null);
+
+            // Allocate the second widget id to bind.
+            final int secondAppWidgetId = host.allocateAppWidgetId();
+
+            // Bind the second widget.
+            getAppWidgetManager().bindAppWidgetIdIfAllowed(secondAppWidgetId,
+                    provider.getProfile(), provider.provider, null);
+
+            // Now we have two widgets,
+            widgetsIds = getAppWidgetManager().getAppWidgetIds(provider.provider);
+            assertTrue(Arrays.equals(widgetsIds, new int[]{firstAppWidgetId, secondAppWidgetId}));
+        } finally {
+            // Clean up.
+            host.deleteHost();
+            revokeBindAppWidgetPermission();
+        }
+    }
+
+    public void testGetAppWidgetInfo() throws Exception {
+        // We want to bind widgets.
+        grantBindAppWidgetPermission();
+
+        // Create a host and start listening.
+        AppWidgetHost host = new AppWidgetHost(
+                getInstrumentation().getTargetContext(), 0);
+        host.startListening();
+
+        try {
+            // Allocate an widget id to bind.
+            final int appWidgetId = host.allocateAppWidgetId();
+
+            // The widget is not bound, so no info.
+            AppWidgetProviderInfo foundProvider = getAppWidgetManager()
+                    .getAppWidgetInfo(appWidgetId);
+            assertNull(foundProvider);
+
+            // Grab the provider we defined to be bound.
+            AppWidgetProviderInfo provider = getFirstAppWidgetProviderInfo();
+
+            // Bind the app widget.
+            getAppWidgetManager().bindAppWidgetIdIfAllowed(appWidgetId,
+                    provider.getProfile(), provider.provider, null);
+
+            // The widget is bound, so the provider info should be there.
+            foundProvider = getAppWidgetManager().getAppWidgetInfo(appWidgetId);
+            assertEquals(provider.provider, foundProvider.provider);
+            assertEquals(provider.getProfile(), foundProvider.getProfile());
+
+            Context context = getInstrumentation().getTargetContext();
+
+            // Let us make sure the provider info is sane.
+            String label = foundProvider.loadLabel(context.getPackageManager());
+            assertTrue(!TextUtils.isEmpty(label));
+
+            Drawable icon = foundProvider.loadIcon(context, DisplayMetrics.DENSITY_DEFAULT);
+            assertNotNull(icon);
+
+            Drawable previewImage = foundProvider.loadPreviewImage(context, 0);
+            assertNotNull(previewImage);
+        } finally {
+            // Clean up.
+            host.deleteHost();
+            revokeBindAppWidgetPermission();
+        }
+    }
+
+    public void testGetAppWidgetOptions() throws Exception {
+        // We want to bind widgets.
+        grantBindAppWidgetPermission();
+
+        // Create a host and start listening.
+        AppWidgetHost host = new AppWidgetHost(
+                getInstrumentation().getTargetContext(), 0);
+        host.startListening();
+
+        try {
+            // Grab the provider we defined to be bound.
+            AppWidgetProviderInfo provider = getFirstAppWidgetProviderInfo();
+
+            // Allocate an widget id to bind.
+            final int appWidgetId = host.allocateAppWidgetId();
+
+            // Initially we have no options.
+            Bundle foundOptions = getAppWidgetManager().getAppWidgetOptions(appWidgetId);
+            assertTrue(foundOptions.isEmpty());
+
+            // We want to set the options when binding.
+            Bundle setOptions = new Bundle();
+            setOptions.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH, 1);
+            setOptions.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT, 2);
+            setOptions.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH, 3);
+            setOptions.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT, 4);
+
+            // Bind the app widget.
+            getAppWidgetManager().bindAppWidgetIdIfAllowed(appWidgetId,
+                    provider.getProfile(), provider.provider, setOptions);
+
+            // Make sure we get the options used when binding.
+            foundOptions = getAppWidgetManager().getAppWidgetOptions(appWidgetId);
+            assertTrue(equalOptions(setOptions, foundOptions));
+        } finally {
+            // Clean up.
+            host.deleteHost();
+            revokeBindAppWidgetPermission();
+        }
+    }
+
+    public void testDeleteHost() throws Exception {
+        // We want to bind widgets.
+        grantBindAppWidgetPermission();
+
+        // Create a host and start listening.
+        AppWidgetHost host = new AppWidgetHost(
+                getInstrumentation().getTargetContext(), 0);
+        host.startListening();
+
+        try {
+            // Allocate an widget id to bind.
+            final int appWidgetId = host.allocateAppWidgetId();
+
+            // Grab the provider we defined to be bound.
+            AppWidgetProviderInfo provider = getFirstAppWidgetProviderInfo();
+
+            // Bind the app widget.
+            getAppWidgetManager().bindAppWidgetIdIfAllowed(appWidgetId,
+                    provider.getProfile(), provider.provider, null);
+
+            // The widget should be there.
+            int[] widgetIds = getAppWidgetManager().getAppWidgetIds(provider.provider);
+            assertTrue(Arrays.equals(widgetIds, new int[]{appWidgetId}));
+
+            // Delete the host.
+            host.deleteHost();
+
+            // The host is gone and with it the widgets.
+            widgetIds = getAppWidgetManager().getAppWidgetIds(provider.provider);
+            assertTrue(widgetIds.length == 0);
+        } finally {
+            // Clean up.
+            revokeBindAppWidgetPermission();
+        }
+    }
+
+    public void testDeleteHosts() throws Exception {
+        // We want to bind widgets.
+        grantBindAppWidgetPermission();
+
+        // Create the first host and start listening.
+        AppWidgetHost firstHost = new AppWidgetHost(
+                getInstrumentation().getTargetContext(), 0);
+        firstHost.startListening();
+
+        // Create the second host and start listening.
+        AppWidgetHost secondHost = new AppWidgetHost(
+                getInstrumentation().getTargetContext(), 1);
+        secondHost.startListening();
+
+        try {
+            // Grab the provider we defined to be bound.
+            AppWidgetProviderInfo provider = getFirstAppWidgetProviderInfo();
+
+            // Allocate the first widget id to bind.
+            final int firstAppWidgetId = firstHost.allocateAppWidgetId();
+
+            // Bind the first app widget.
+            getAppWidgetManager().bindAppWidgetIdIfAllowed(firstAppWidgetId,
+                    provider.getProfile(), provider.provider, null);
+
+            // Allocate the second widget id to bind.
+            final int secondAppWidgetId = secondHost.allocateAppWidgetId();
+
+            // Bind the second app widget.
+            getAppWidgetManager().bindAppWidgetIdIfAllowed(secondAppWidgetId,
+                    provider.getProfile(), provider.provider, null);
+
+            // The widgets should be there.
+            int[] widgetIds = getAppWidgetManager().getAppWidgetIds(provider.provider);
+            assertTrue(Arrays.equals(widgetIds, new int[]{firstAppWidgetId, secondAppWidgetId}));
+
+            // Delete all hosts.
+            AppWidgetHost.deleteAllHosts();
+
+            // The hosts are gone and with it the widgets.
+            widgetIds = getAppWidgetManager().getAppWidgetIds(provider.provider);
+            assertTrue(widgetIds.length == 0);
+        } finally {
+            // Clean up.
+            revokeBindAppWidgetPermission();
+        }
+    }
+
+    public void testOnProvidersChanged() throws Exception {
+        // We want to bind widgets.
+        grantBindAppWidgetPermission();
+
+        final AtomicInteger onProvidersChangedCallCounter = new AtomicInteger();
+
+        // Create a host and start listening.
+        AppWidgetHost host = new AppWidgetHost(
+                getInstrumentation().getTargetContext(), 0) {
+            @Override
+            public void onProvidersChanged() {
+                synchronized (mLock) {
+                    onProvidersChangedCallCounter.incrementAndGet();
+                    mLock.notifyAll();
+                }
+            }
+        };
+        host.startListening();
+
+        try {
+            // Grab the provider we defined to be bound.
+            AppWidgetProviderInfo firstLookupProvider = getFirstAppWidgetProviderInfo();
+
+            // Allocate a widget id to bind.
+            final int appWidgetId = host.allocateAppWidgetId();
+
+            // Bind the first app widget.
+            getAppWidgetManager().bindAppWidgetIdIfAllowed(appWidgetId,
+                    firstLookupProvider.getProfile(), firstLookupProvider.provider, null);
+
+            // Disable the provider we just bound to.
+            PackageManager packageManager = getInstrumentation().getTargetContext()
+                    .getApplicationContext().getPackageManager();
+            packageManager.setComponentEnabledSetting(firstLookupProvider.provider,
+                    PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
+                    PackageManager.DONT_KILL_APP);
+
+            // Wait for the package change to propagate.
+            waitForCallCount(onProvidersChangedCallCounter, 1);
+
+            // The provider should not be present anymore.
+            AppWidgetProviderInfo secondLookupProvider = getFirstAppWidgetProviderInfo();
+            assertNull(secondLookupProvider);
+
+            // Enable the provider we disbaled.
+            packageManager.setComponentEnabledSetting(firstLookupProvider.provider,
+                    PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
+                    PackageManager.DONT_KILL_APP);
+
+            // Wait for the package change to propagate.
+            waitForCallCount(onProvidersChangedCallCounter, 2);
+        } finally {
+            // Clean up.
+            host.deleteHost();
+            revokeBindAppWidgetPermission();
+        }
+    }
+
+    public void testUpdateAppWidgetViaComponentName() throws Exception {
+        // We want to bind widgets.
+        grantBindAppWidgetPermission();
+
+        final AtomicInteger updateAppWidgetCallCount = new AtomicInteger();
+
+        // Create a host and start listening.
+        AppWidgetHost host = new AppWidgetHost(
+                getInstrumentation().getTargetContext(), 0) {
+            @Override
+            protected AppWidgetHostView onCreateView(Context context, int appWidgetId,
+                    AppWidgetProviderInfo appWidget) {
+                return new MyAppWidgetHostView(context);
+            }
+        };
+        host.startListening();
+
+        try {
+            // Grab the provider to be bound.
+            AppWidgetProviderInfo provider = getFirstAppWidgetProviderInfo();
+
+            // Allocate the first widget id to bind.
+            final int firstAppWidgetId = host.allocateAppWidgetId();
+
+            // Bind the first app widget.
+            getAppWidgetManager().bindAppWidgetIdIfAllowed(firstAppWidgetId,
+                    provider.getProfile(), provider.provider, null);
+
+            // Create the first host view.
+            MyAppWidgetHostView firstHostView = (MyAppWidgetHostView) host.createView(
+                    getInstrumentation().getContext(), firstAppWidgetId, provider);
+            MyAppWidgetHostView.OnUpdateAppWidgetListener firstAppHostViewListener =
+                    mock(MyAppWidgetHostView.OnUpdateAppWidgetListener.class);
+            firstHostView.setOnUpdateAppWidgetListener(firstAppHostViewListener);
+
+            // Allocate the second widget id to bind.
+            final int secondAppWidgetId = host.allocateAppWidgetId();
+
+            // Bind the second app widget.
+            getAppWidgetManager().bindAppWidgetIdIfAllowed(secondAppWidgetId,
+                    provider.getProfile(), provider.provider, null);
+
+            // Create the second host view.
+            MyAppWidgetHostView secondHostView = (MyAppWidgetHostView) host.createView(
+                    getInstrumentation().getContext(), secondAppWidgetId, provider);
+            MyAppWidgetHostView.OnUpdateAppWidgetListener secondAppHostViewListener =
+                    mock(MyAppWidgetHostView.OnUpdateAppWidgetListener.class);
+            doAnswer(new Answer<Void>() {
+                @Override
+                public Void answer(InvocationOnMock invocation) throws Throwable {
+                    synchronized (mLock) {
+                        updateAppWidgetCallCount.incrementAndGet();
+                        mLock.notifyAll();
+                    }
+                    return null;
+                }
+            }).when(secondAppHostViewListener).onUpdateAppWidget(any(RemoteViews.class));
+            secondHostView.setOnUpdateAppWidgetListener(secondAppHostViewListener);
+
+            // Update all app widgets.
+            final RemoteViews content = new RemoteViews(
+                    getInstrumentation().getContext().getPackageName(),
+                    R.layout.second_initial_layout);
+            getAppWidgetManager().updateAppWidget(provider.provider, content);
+
+            waitForCallCount(updateAppWidgetCallCount, 1);
+
+            // Verify the expected callbacks.
+            InOrder firstInOrder = inOrder(firstAppHostViewListener);
+            firstInOrder.verify(firstAppHostViewListener).onUpdateAppWidget(argThat(
+                    new RemoteViewsMatcher(content.getLayoutId(),
+                            provider.provider.getPackageName())));
+
+            InOrder secondInOrder = inOrder(secondAppHostViewListener);
+            secondInOrder.verify(secondAppHostViewListener).onUpdateAppWidget(argThat(
+                    new RemoteViewsMatcher(content.getLayoutId(),
+                            provider.provider.getPackageName())));
+        } finally {
+            // Clean up.
+            host.deleteHost();
+            revokeBindAppWidgetPermission();
+        }
+    }
+
+    public void testUpdateAppWidgetViaWidgetId() throws Exception {
+        // We want to bind widgets.
+        grantBindAppWidgetPermission();
+
+        final AtomicInteger updateAppWidgetCallCount = new AtomicInteger();
+
+        // Create a host and start listening.
+        AppWidgetHost host = new AppWidgetHost(
+                getInstrumentation().getTargetContext(), 0) {
+            @Override
+            protected AppWidgetHostView onCreateView(Context context, int appWidgetId,
+                    AppWidgetProviderInfo appWidget) {
+                return new MyAppWidgetHostView(context);
+            }
+        };
+        host.startListening();
+
+        try {
+            // Grab the provider to be bound.
+            AppWidgetProviderInfo provider = getFirstAppWidgetProviderInfo();
+
+            // Allocate the first widget id to bind.
+            final int firstAppWidgetId = host.allocateAppWidgetId();
+
+            // Bind the first app widget.
+            getAppWidgetManager().bindAppWidgetIdIfAllowed(firstAppWidgetId,
+                    provider.getProfile(), provider.provider, null);
+
+            // Create the first host view.
+            MyAppWidgetHostView hostView = (MyAppWidgetHostView) host.createView(
+                    getInstrumentation().getContext(), firstAppWidgetId, provider);
+            MyAppWidgetHostView.OnUpdateAppWidgetListener appHostViewListener =
+                    mock(MyAppWidgetHostView.OnUpdateAppWidgetListener.class);
+            doAnswer(new Answer<Void>() {
+                @Override
+                public Void answer(InvocationOnMock invocation) throws Throwable {
+                    synchronized (mLock) {
+                        updateAppWidgetCallCount.incrementAndGet();
+                        mLock.notifyAll();
+                    }
+                    return null;
+                }
+            }).when(appHostViewListener).onUpdateAppWidget(any(RemoteViews.class));
+            hostView.setOnUpdateAppWidgetListener(appHostViewListener);
+
+            // Update all app widgets.
+            RemoteViews content = new RemoteViews(
+                    getInstrumentation().getContext().getPackageName(),
+                    R.layout.second_initial_layout);
+            getAppWidgetManager().updateAppWidget(firstAppWidgetId, content);
+
+            waitForCallCount(updateAppWidgetCallCount, 1);
+
+            // Verify the expected callbacks.
+            InOrder inOrder = inOrder(appHostViewListener);
+            inOrder.verify(appHostViewListener).onUpdateAppWidget(argThat(
+                    new RemoteViewsMatcher(content.getLayoutId(),
+                            provider.provider.getPackageName())
+            ));
+        } finally {
+            // Clean up.
+            host.deleteHost();
+            revokeBindAppWidgetPermission();
+        }
+    }
+
+    public void testUpdateAppWidgetViaWidgetIds() throws Exception {
+        // We want to bind widgets.
+        grantBindAppWidgetPermission();
+
+        final AtomicInteger onUpdateAppWidgetCallCount = new AtomicInteger();
+
+        // Create a host and start listening.
+        AppWidgetHost host = new AppWidgetHost(
+                getInstrumentation().getTargetContext(), 0) {
+            @Override
+            protected AppWidgetHostView onCreateView(Context context, int appWidgetId,
+                    AppWidgetProviderInfo appWidget) {
+                return new MyAppWidgetHostView(context);
+            }
+        };
+        host.startListening();
+
+        try {
+            // Grab the provider to be bound.
+            AppWidgetProviderInfo provider = getFirstAppWidgetProviderInfo();
+
+            // Allocate the first widget id to bind.
+            final int firstAppWidgetId = host.allocateAppWidgetId();
+
+            // Bind the first app widget.
+            getAppWidgetManager().bindAppWidgetIdIfAllowed(firstAppWidgetId,
+                    provider.getProfile(), provider.provider, null);
+
+            // Create the first host view.
+            MyAppWidgetHostView firstHostView = (MyAppWidgetHostView) host.createView(
+                    getInstrumentation().getContext(), firstAppWidgetId, provider);
+            MyAppWidgetHostView.OnUpdateAppWidgetListener firstAppHostViewListener =
+                    mock(MyAppWidgetHostView.OnUpdateAppWidgetListener.class);
+            firstHostView.setOnUpdateAppWidgetListener(firstAppHostViewListener);
+
+            // Allocate the second widget id to bind.
+            final int secondAppWidgetId = host.allocateAppWidgetId();
+
+            // Bind the second app widget.
+            getAppWidgetManager().bindAppWidgetIdIfAllowed(secondAppWidgetId,
+                    provider.getProfile(), provider.provider, null);
+
+            // Create the second host view.
+            MyAppWidgetHostView secondHostView = (MyAppWidgetHostView) host.createView(
+                    getInstrumentation().getContext(), secondAppWidgetId, provider);
+            MyAppWidgetHostView.OnUpdateAppWidgetListener secondAppHostViewListener =
+                    mock(MyAppWidgetHostView.OnUpdateAppWidgetListener.class);
+            doAnswer(new Answer<Void>() {
+                @Override
+                public Void answer(InvocationOnMock invocation) throws Throwable {
+                    synchronized (mLock) {
+                        onUpdateAppWidgetCallCount.incrementAndGet();
+                        mLock.notifyAll();
+                    }
+                    return null;
+                }
+            }).when(secondAppHostViewListener).onUpdateAppWidget(any(RemoteViews.class));
+            secondHostView.setOnUpdateAppWidgetListener(secondAppHostViewListener);
+
+            // Update all app widgets.
+            RemoteViews content = new RemoteViews(
+                    getInstrumentation().getContext().getPackageName(),
+                    R.layout.second_initial_layout);
+            getAppWidgetManager().updateAppWidget(new int[] {firstAppWidgetId,
+                    secondAppWidgetId}, content);
+
+            waitForCallCount(onUpdateAppWidgetCallCount, 1);
+
+            // Verify the expected callbacks.
+            InOrder firstInOrder = inOrder(firstAppHostViewListener);
+            firstInOrder.verify(firstAppHostViewListener).onUpdateAppWidget(
+                    argThat(new RemoteViewsMatcher(content.getLayoutId(),
+                            provider.provider.getPackageName())));
+
+            InOrder secondInOrder = inOrder(secondAppHostViewListener);
+            secondInOrder.verify(secondAppHostViewListener).onUpdateAppWidget(
+                    argThat(new RemoteViewsMatcher(content.getLayoutId(),
+                            provider.provider.getPackageName()))
+            );
+        } finally {
+            // Clean up.
+            host.deleteHost();
+            revokeBindAppWidgetPermission();
+        }
+    }
+
+    public void testPartiallyUpdateAppWidgetViaWidgetId() throws Exception {
+        // We want to bind widgets.
+        grantBindAppWidgetPermission();
+
+        final AtomicInteger updateAppWidgetCallCount = new AtomicInteger();
+
+        // Create a host and start listening.
+        AppWidgetHost host = new AppWidgetHost(
+                getInstrumentation().getTargetContext(), 0) {
+            @Override
+            protected AppWidgetHostView onCreateView(Context context, int appWidgetId,
+                    AppWidgetProviderInfo appWidget) {
+                return new MyAppWidgetHostView(context);
+            }
+        };
+
+        host.startListening();
+
+        try {
+            // Grab the provider to be bound.
+            AppWidgetProviderInfo provider = getFirstAppWidgetProviderInfo();
+
+            // Allocate the first widget id to bind.
+            final int firstAppWidgetId = host.allocateAppWidgetId();
+
+            // Bind the first app widget.
+            getAppWidgetManager().bindAppWidgetIdIfAllowed(firstAppWidgetId,
+                    provider.getProfile(), provider.provider, null);
+
+            // Create the first host view.
+            MyAppWidgetHostView hostView = (MyAppWidgetHostView) host.createView(
+                    getInstrumentation().getContext(), firstAppWidgetId, provider);
+            MyAppWidgetHostView.OnUpdateAppWidgetListener appHostViewListener =
+                    mock(MyAppWidgetHostView.OnUpdateAppWidgetListener.class);
+            doAnswer(new Answer<Void>() {
+                @Override
+                public Void answer(InvocationOnMock invocation) throws Throwable {
+                    synchronized (mLock) {
+                        updateAppWidgetCallCount.incrementAndGet();
+                        mLock.notifyAll();
+                    }
+                    return null;
+                }
+            }).when(appHostViewListener).onUpdateAppWidget(any(RemoteViews.class));
+            hostView.setOnUpdateAppWidgetListener(appHostViewListener);
+
+            // Set the content for all app widgets.
+            RemoteViews content = new RemoteViews(
+                    getInstrumentation().getContext().getPackageName(),
+                    R.layout.first_initial_layout);
+            getAppWidgetManager().updateAppWidget(firstAppWidgetId, content);
+
+            waitForCallCount(updateAppWidgetCallCount, 1);
+
+            // Partially update the content for all app widgets (pretent we changed somehting).
+            getAppWidgetManager().partiallyUpdateAppWidget(firstAppWidgetId, content);
+
+            waitForCallCount(updateAppWidgetCallCount, 2);
+
+            // Verify the expected callbacks.
+            InOrder inOrder = inOrder(appHostViewListener);
+            inOrder.verify(appHostViewListener, times(2)).onUpdateAppWidget(
+                    argThat(new RemoteViewsMatcher(content.getLayoutId(),
+                            provider.provider.getPackageName())));
+        } finally {
+            // Clean up.
+            host.deleteHost();
+            revokeBindAppWidgetPermission();
+        }
+    }
+
+    public void testPartiallyUpdateAppWidgetViaWidgetIds() throws Exception {
+        // We want to bind widgets.
+        grantBindAppWidgetPermission();
+
+        final AtomicInteger firstAppWidgetCallCounter = new AtomicInteger();
+        final AtomicInteger secondAppWidgetCallCounter = new AtomicInteger();
+
+        // Create a host and start listening.
+        AppWidgetHost host = new AppWidgetHost(
+                getInstrumentation().getTargetContext(), 0) {
+            @Override
+            protected AppWidgetHostView onCreateView(Context context, int appWidgetId,
+                    AppWidgetProviderInfo appWidget) {
+                return new MyAppWidgetHostView(context);
+            }
+        };
+        host.startListening();
+
+        try {
+            // Grab the provider to be bound.
+            AppWidgetProviderInfo provider = getFirstAppWidgetProviderInfo();
+
+            // Allocate the first widget id to bind.
+            final int firstAppWidgetId = host.allocateAppWidgetId();
+
+            // Bind the first app widget.
+            getAppWidgetManager().bindAppWidgetIdIfAllowed(firstAppWidgetId,
+                    provider.getProfile(), provider.provider, null);
+
+            // Create the first host view.
+            MyAppWidgetHostView firstHostView = (MyAppWidgetHostView) host.createView(
+                    getInstrumentation().getContext(), firstAppWidgetId, provider);
+            MyAppWidgetHostView.OnUpdateAppWidgetListener firstAppHostViewListener =
+                    mock(MyAppWidgetHostView.OnUpdateAppWidgetListener.class);
+            doAnswer(new Answer<Void>() {
+                @Override
+                public Void answer(InvocationOnMock invocation) throws Throwable {
+                    synchronized (mLock) {
+                        firstAppWidgetCallCounter.incrementAndGet();
+                        mLock.notifyAll();
+                    }
+                    return null;
+                }
+            }).when(firstAppHostViewListener).onUpdateAppWidget(any(RemoteViews.class));
+            firstHostView.setOnUpdateAppWidgetListener(firstAppHostViewListener);
+
+            // Allocate the second widget id to bind.
+            final int secondAppWidgetId = host.allocateAppWidgetId();
+
+            // Bind the second app widget.
+            getAppWidgetManager().bindAppWidgetIdIfAllowed(secondAppWidgetId,
+                    provider.getProfile(), provider.provider, null);
+
+            // Create the second host view.
+            MyAppWidgetHostView secondHostView = (MyAppWidgetHostView) host.createView(
+                    getInstrumentation().getContext(), secondAppWidgetId, provider);
+            MyAppWidgetHostView.OnUpdateAppWidgetListener secondAppHostViewListener =
+                    mock(MyAppWidgetHostView.OnUpdateAppWidgetListener.class);
+            doAnswer(new Answer<Void>() {
+                @Override
+                public Void answer(InvocationOnMock invocation) throws Throwable {
+                    synchronized (mLock) {
+                        secondAppWidgetCallCounter.incrementAndGet();
+                        mLock.notifyAll();
+                    }
+                    return null;
+                }
+            }).when(secondAppHostViewListener).onUpdateAppWidget(any(RemoteViews.class));
+            secondHostView.setOnUpdateAppWidgetListener(secondAppHostViewListener);
+
+            // Set the content for all app widgets.
+            RemoteViews content = new RemoteViews(
+                    getInstrumentation().getContext().getPackageName(),
+                    R.layout.first_initial_layout);
+            getAppWidgetManager().updateAppWidget(new int[]{firstAppWidgetId,
+                    secondAppWidgetId}, content);
+
+            waitForCallCount(firstAppWidgetCallCounter, 1);
+            waitForCallCount(secondAppWidgetCallCounter, 1);
+
+            // Partially update the content for all app widgets (pretend we changed somehting).
+            getAppWidgetManager().partiallyUpdateAppWidget(new int[] {firstAppWidgetId,
+                    secondAppWidgetId}, content);
+
+            waitForCallCount(firstAppWidgetCallCounter, 2);
+            waitForCallCount(secondAppWidgetCallCounter, 2);
+
+            // Verify the expected callbacks.
+            InOrder firstInOrder = inOrder(firstAppHostViewListener);
+            firstInOrder.verify(firstAppHostViewListener, times(2)).onUpdateAppWidget(
+                    argThat(new RemoteViewsMatcher(content.getLayoutId(),
+                            provider.provider.getPackageName())));
+
+            InOrder secondInOrder = inOrder(secondAppHostViewListener);
+            secondInOrder.verify(secondAppHostViewListener, times(2)).onUpdateAppWidget(
+                    argThat(new RemoteViewsMatcher(content.getLayoutId(),
+                            provider.provider.getPackageName())));
+        } finally {
+            // Clean up.
+            host.deleteHost();
+            revokeBindAppWidgetPermission();
+        }
+    }
+
+    public void testCollectionWidgets() throws Exception {
+        // We want to bind widgets.
+        grantBindAppWidgetPermission();
+
+        final AtomicInteger getViewCounter = new AtomicInteger();
+        final Context context = getInstrumentation().getTargetContext();
+
+        // Create a host and start listening.
+        AppWidgetHost host = new AppWidgetHost(context, 0);
+        host.startListening();
+
+        try {
+            // Configure the provider behavior.
+            AppWidgetProviderCallbacks callbacks = mock(AppWidgetProviderCallbacks.class);
+            doAnswer(new Answer<Void>() {
+                @Override
+                public Void answer(InvocationOnMock invocation) throws Throwable {
+                    final int appWidgetId = ((int[]) invocation.getArguments()[2])[0];
+
+                    Intent intent = new Intent(context, MyAppWidgetService.class);
+                    intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
+                    intent.setData(Uri.parse(intent.toUri(Intent.URI_INTENT_SCHEME)));
+
+                    RemoteViews removeViews = new RemoteViews(context.getPackageName(),
+                            R.layout.collection_widget_layout);
+                    removeViews.setRemoteAdapter(R.id.stack_view, intent);
+
+                    getAppWidgetManager().updateAppWidget(appWidgetId, removeViews);
+
+                    return null;
+                }
+            }).when(callbacks).onUpdate(any(Context.class), any(AppWidgetManager.class),
+                    any(int[].class));
+            FirstAppWidgetProvider.setCallbacks(callbacks);
+
+            // Grab the provider to be bound.
+            AppWidgetProviderInfo provider = getFirstAppWidgetProviderInfo();
+
+            // Allocate a widget id to bind.
+            final int appWidgetId = host.allocateAppWidgetId();
+
+            // Bind the app widget.
+            getAppWidgetManager().bindAppWidgetIdIfAllowed(appWidgetId,
+                    provider.getProfile(), provider.provider, null);
+
+            // Configure the app widget service behavior.
+            RemoteViewsFactory factory = mock(RemoteViewsFactory.class);
+            doAnswer(new Answer<Integer>() {
+                @Override
+                public Integer answer(InvocationOnMock invocation) throws Throwable {
+                    return 1;
+                }
+            }).when(factory).getCount();
+            doAnswer(new Answer<RemoteViews>() {
+                @Override
+                public RemoteViews answer(InvocationOnMock invocation) throws Throwable {
+                    RemoteViews remoteViews = new RemoteViews(context.getPackageName(),
+                            R.layout.collection_widget_item_layout);
+                    remoteViews.setTextViewText(R.id.text_view, context.getText(R.string.foo));
+                    synchronized (mLock) {
+                        getViewCounter.incrementAndGet();
+                    }
+                    return remoteViews;
+                }
+            }).when(factory).getViewAt(any(int.class));
+            doAnswer(new Answer<Integer>() {
+                @Override
+                public Integer answer(InvocationOnMock invocation) throws Throwable {
+                    return 1;
+                }
+            }).when(factory).getViewTypeCount();
+            MyAppWidgetService.setFactory(factory);
+
+            host.createView(context, appWidgetId, provider);
+
+            // Wait for the interactions to occur.
+            waitForCallCount(getViewCounter, 1);
+
+            // Verify the interactions.
+            verify(factory, atLeastOnce()).hasStableIds();
+            verify(factory, atLeastOnce()).getViewTypeCount();
+            verify(factory, atLeastOnce()).getCount();
+            verify(factory, atLeastOnce()).getLoadingView();
+            verify(factory, atLeastOnce()).getViewAt(same(0));
+        } finally {
+            // Clean up.
+            FirstAppWidgetProvider.setCallbacks(null);
+            host.deleteHost();
+            revokeBindAppWidgetPermission();
+        }
+    }
+
+    private void waitForCallCount(AtomicInteger counter, int expectedCount) {
+        synchronized (mLock) {
+            final long startTimeMillis = SystemClock.uptimeMillis();
+            while (counter.get() < expectedCount) {
+                final long elapsedTimeMillis = SystemClock.uptimeMillis() - startTimeMillis;
+                final long remainingTimeMillis = OPERATION_TIMEOUT - elapsedTimeMillis;
+                if (remainingTimeMillis <= 0) {
+                    fail("Did not get expected call to onUpdateAppWidget");
+                }
+                try {
+                    mLock.wait(remainingTimeMillis);
+                } catch (InterruptedException ie) {
+                        /* ignore */
+                }
+            }
+        }
+    }
+
+    @SuppressWarnings("deprecation")
+    private void assertExpectedInstalledProviders(List<AppWidgetProviderInfo> providers) {
+        boolean firstProviderVerified = false;
+        boolean secondProviderVerified = false;
+
+        ComponentName firstComponentName = new ComponentName(
+                getInstrumentation().getTargetContext().getPackageName(),
+                FirstAppWidgetProvider.class.getName());
+
+        ComponentName secondComponentName = new ComponentName(
+                getInstrumentation().getTargetContext().getPackageName(),
+                SecondAppWidgetProvider.class.getName());
+
+        final int providerCount = providers.size();
+        for (int i = 0; i < providerCount; i++) {
+            AppWidgetProviderInfo provider = providers.get(i);
+
+            if (firstComponentName.equals(provider.provider)
+                    && android.os.Process.myUserHandle().equals(provider.getProfile())) {
+                assertEquals(getNormalizedDimensionResource(R.dimen.first_min_appwidget_size),
+                        provider.minWidth);
+                assertEquals(getNormalizedDimensionResource(R.dimen.first_min_appwidget_size),
+                        provider.minHeight);
+                assertEquals(getNormalizedDimensionResource(
+                        R.dimen.first_min_resize_appwidget_size), provider.minResizeWidth);
+                assertEquals(getNormalizedDimensionResource(
+                        R.dimen.first_min_resize_appwidget_size), provider.minResizeHeight);
+                assertEquals(getIntResource(R.integer.first_update_period_millis),
+                        provider.updatePeriodMillis);
+                assertEquals(getInstrumentation().getTargetContext().getPackageName(),
+                        provider.configure.getPackageName());
+                assertEquals(FIRST_APP_WIDGET_CONFIGURE_ACTIVITY,
+                        provider.configure.getClassName());
+                assertEquals(getIntResource(R.integer.first_resize_mode),
+                        provider.resizeMode);
+                assertEquals(getIntResource(R.integer.first_widget_category),
+                        provider.widgetCategory);
+                assertEquals(R.layout.first_initial_layout,
+                        provider.initialLayout);
+                assertEquals(R.layout.first_initial_keyguard_layout,
+                        provider.initialKeyguardLayout);
+                assertEquals(R.drawable.first_android_icon,
+                        provider.previewImage);
+                assertEquals(R.id.first_auto_advance_view_id,
+                        provider.autoAdvanceViewId);
+                firstProviderVerified = true;
+            } else if (secondComponentName.equals(provider.provider)
+                    && android.os.Process.myUserHandle().equals(provider.getProfile())) {
+                assertEquals(getNormalizedDimensionResource(R.dimen.second_min_appwidget_size),
+                        provider.minWidth);
+                assertEquals(getNormalizedDimensionResource(R.dimen.second_min_appwidget_size),
+                        provider.minHeight);
+                assertEquals(getNormalizedDimensionResource(
+                        R.dimen.second_min_resize_appwidget_size), provider.minResizeWidth);
+                assertEquals(getNormalizedDimensionResource(
+                        R.dimen.second_min_resize_appwidget_size), provider.minResizeHeight);
+                assertEquals(getIntResource(R.integer.second_update_period_millis),
+                        provider.updatePeriodMillis);
+                assertEquals(getInstrumentation().getTargetContext().getPackageName(),
+                        provider.configure.getPackageName());
+                assertEquals(SECOND_APP_WIDGET_CONFIGURE_ACTIVITY,
+                        provider.configure.getClassName());
+                assertEquals(getIntResource(R.integer.second_resize_mode),
+                        provider.resizeMode);
+                assertEquals(getIntResource(R.integer.second_widget_category),
+                        provider.widgetCategory);
+                assertEquals(R.layout.second_initial_layout,
+                        provider.initialLayout);
+                assertEquals(R.layout.second_initial_keyguard_layout,
+                        provider.initialKeyguardLayout);
+                assertEquals(R.drawable.second_android_icon,
+                        provider.previewImage);
+                assertEquals(R.id.second_auto_advance_view_id,
+                        provider.autoAdvanceViewId);
+                secondProviderVerified = true;
+            }
+        }
+
+        assertTrue(firstProviderVerified && secondProviderVerified);
+    }
+
+    private void grantBindAppWidgetPermission() {
+        executeShellCommandIgnoreOutput(GRANT_BIND_APP_WIDGET_PERMISSION_COMMAND);
+    }
+
+    private void revokeBindAppWidgetPermission() {
+        executeShellCommandIgnoreOutput(REVOKE_BIND_APP_WIDGET_PERMISSION_COMMAND);
+    }
+
+    private void executeShellCommandIgnoreOutput(String command) {
+        ParcelFileDescriptor pfd = getInstrumentation().getUiAutomation()
+                .executeShellCommand(command);
+        try {
+            Streams.readFully(new FileInputStream(pfd.getFileDescriptor()));
+        } catch (IOException ioe) {
+            IoUtils.closeQuietly(pfd);
+        }
+    }
+
+    private AppWidgetProviderInfo getFirstAppWidgetProviderInfo() {
+        ComponentName firstComponentName = new ComponentName(
+                getInstrumentation().getTargetContext().getPackageName(),
+                FirstAppWidgetProvider.class.getName());
+
+        return getProviderInfo(firstComponentName);
+    }
+
+    private AppWidgetProviderInfo getSecondAppWidgetProviderInfo() {
+        ComponentName secondComponentName = new ComponentName(
+                getInstrumentation().getTargetContext().getPackageName(),
+                SecondAppWidgetProvider.class.getName());
+
+        return getProviderInfo(secondComponentName);
+    }
+
+    private AppWidgetProviderInfo getProviderInfo(ComponentName componentName) {
+        List<AppWidgetProviderInfo> providers = getAppWidgetManager().getInstalledProviders();
+
+        final int providerCount = providers.size();
+        for (int i = 0; i < providerCount; i++) {
+            AppWidgetProviderInfo provider = providers.get(i);
+            if (componentName.equals(provider.provider)
+                    && Process.myUserHandle().equals(provider.getProfile())) {
+                return provider;
+
+            }
+        }
+
+        return null;
+    }
+
+    private int getNormalizedDimensionResource(int resId) {
+        return getInstrumentation().getTargetContext().getResources()
+                .getDimensionPixelSize(resId);
+    }
+
+    private int getIntResource(int resId) {
+        return getInstrumentation().getTargetContext().getResources().getInteger(resId);
+    }
+
+    private AppWidgetManager getAppWidgetManager() {
+        return (AppWidgetManager) getInstrumentation().getTargetContext()
+                .getSystemService(Context.APPWIDGET_SERVICE);
+    }
+
+    private static boolean equalOptions(Bundle first, Bundle second) {
+        return first.getInt(AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH)
+                       == second.getInt(AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH)
+                && first.getInt(AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT)
+                       == second.getInt(AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT)
+                && first.getInt(AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH)
+                        == second.getInt(AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH)
+                && first.getInt(AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT)
+                        == second.getInt(AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT);
+    }
+
+    private static final class OptionsMatcher extends BaseMatcher<Bundle> {
+        private Bundle mOptions;
+
+        public OptionsMatcher(Bundle options) {
+            mOptions = options;
+        }
+
+        @Override
+        public boolean matches(Object item) {
+            Bundle options = (Bundle) item;
+            return equalOptions(mOptions, options);
+        }
+
+        @Override
+        public void describeTo(Description description) {
+            /* do nothing */
+        }
+    }
+
+    private static final class WidgetIdsMatcher extends BaseMatcher<int[]> {
+        private final int[] mWidgetIds;
+
+        public WidgetIdsMatcher(int[] widgetIds) {
+            mWidgetIds = widgetIds;
+        }
+
+        @Override
+        public boolean matches(Object item) {
+            final int[] widgetIds = (int[]) item;
+            return Arrays.equals(widgetIds, mWidgetIds);
+        }
+
+        @Override
+        public void describeTo(Description description) {
+            /* do nothing */
+        }
+    }
+
+    private static final class RemoteViewsMatcher extends BaseMatcher<RemoteViews> {
+        private final int mLayoutId;
+        private final String mPackageName;
+
+        public RemoteViewsMatcher(int layoutId, String packageName) {
+            mLayoutId = layoutId;
+            mPackageName = packageName;
+        }
+
+        @Override
+        public boolean matches(Object item) {
+            final RemoteViews remoteViews = (RemoteViews) item;
+            return remoteViews != null && remoteViews.getLayoutId() == mLayoutId
+                    && remoteViews.getPackage().equals(mPackageName);
+        }
+
+        @Override
+        public void describeTo(Description description) {
+            /* do nothing */
+        }
+    }
+
+    private static class MyAppWidgetHostView extends AppWidgetHostView {
+        private OnUpdateAppWidgetListener mOnUpdateAppWidgetListener;
+
+
+        public interface OnUpdateAppWidgetListener {
+            public void onUpdateAppWidget(RemoteViews remoteViews);
+        }
+
+        private MyAppWidgetHostView(Context context) {
+            super(context);
+        }
+
+        public void setOnUpdateAppWidgetListener(OnUpdateAppWidgetListener listener) {
+            mOnUpdateAppWidgetListener = listener;
+        }
+
+        @Override
+        public void updateAppWidget(RemoteViews remoteViews) {
+            super.updateAppWidget(remoteViews);
+            if (mOnUpdateAppWidgetListener != null) {
+                mOnUpdateAppWidgetListener.onUpdateAppWidget(remoteViews);
+            }
+        }
+    }
+}
diff --git a/tests/tests/appwidget/src/android/appwidget/cts/provider/AppWidgetProviderCallbacks.java b/tests/tests/appwidget/src/android/appwidget/cts/provider/AppWidgetProviderCallbacks.java
new file mode 100644
index 0000000..0657a42
--- /dev/null
+++ b/tests/tests/appwidget/src/android/appwidget/cts/provider/AppWidgetProviderCallbacks.java
@@ -0,0 +1,49 @@
+/*
+ * 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.appwidget.cts.provider;
+
+import android.appwidget.AppWidgetManager;
+import android.appwidget.AppWidgetProvider;
+import android.content.Context;
+import android.os.Bundle;
+
+public abstract class AppWidgetProviderCallbacks {
+
+    private AppWidgetProvider mProvider;
+
+    public final AppWidgetProvider getProvider() {
+        return mProvider;
+    }
+
+    public final void setProvider(AppWidgetProvider provider) {
+        mProvider = provider;
+    }
+
+    public abstract void onUpdate(Context context, AppWidgetManager appWidgetManager,
+             int[] appWidgetIds);
+
+    public abstract void onAppWidgetOptionsChanged(Context context,
+            AppWidgetManager appWidgetManager, int appWidgetId, Bundle newOptions);
+
+    public abstract void onDeleted(Context context, int[] appWidgetIds);
+
+    public abstract void onEnabled(Context context);
+
+    public abstract void onDisabled(Context context);
+
+    public abstract void onRestored(Context context, int[] oldWidgetIds, int[] newWidgetIds);
+}
diff --git a/tests/tests/appwidget/src/android/appwidget/cts/provider/FirstAppWidgetProvider.java b/tests/tests/appwidget/src/android/appwidget/cts/provider/FirstAppWidgetProvider.java
new file mode 100644
index 0000000..7396ca2
--- /dev/null
+++ b/tests/tests/appwidget/src/android/appwidget/cts/provider/FirstAppWidgetProvider.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.appwidget.cts.provider;
+
+public final class FirstAppWidgetProvider extends StubbableAppWidgetProvider {
+    private static final Object sLock = new Object();
+
+    private static AppWidgetProviderCallbacks sCallbacks;
+
+    public static void setCallbacks(AppWidgetProviderCallbacks callbacks) {
+        synchronized (sLock) {
+            sCallbacks = callbacks;
+        }
+    }
+
+    @Override
+    protected AppWidgetProviderCallbacks getCallbacks() {
+        synchronized (sLock) {
+            if (sCallbacks != null) {
+                sCallbacks.setProvider(this);
+            }
+            return sCallbacks;
+        }
+    }
+}
diff --git a/tests/tests/appwidget/src/android/appwidget/cts/provider/SecondAppWidgetProvider.java b/tests/tests/appwidget/src/android/appwidget/cts/provider/SecondAppWidgetProvider.java
new file mode 100644
index 0000000..223f8f0
--- /dev/null
+++ b/tests/tests/appwidget/src/android/appwidget/cts/provider/SecondAppWidgetProvider.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.appwidget.cts.provider;
+
+public final class SecondAppWidgetProvider extends StubbableAppWidgetProvider {
+    private static final Object sLock = new Object();
+
+    private static AppWidgetProviderCallbacks sCallbacks;
+
+    public static void setCallbacks(AppWidgetProviderCallbacks callbacks) {
+        synchronized (sLock) {
+            sCallbacks = callbacks;
+        }
+    }
+
+    @Override
+    protected AppWidgetProviderCallbacks getCallbacks() {
+        synchronized (sLock) {
+            if (sCallbacks != null) {
+                sCallbacks.setProvider(this);
+            }
+            return sCallbacks;
+        }
+    }
+}
diff --git a/tests/tests/appwidget/src/android/appwidget/cts/provider/StubbableAppWidgetProvider.java b/tests/tests/appwidget/src/android/appwidget/cts/provider/StubbableAppWidgetProvider.java
new file mode 100644
index 0000000..9023902
--- /dev/null
+++ b/tests/tests/appwidget/src/android/appwidget/cts/provider/StubbableAppWidgetProvider.java
@@ -0,0 +1,75 @@
+/*
+ * 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.appwidget.cts.provider;
+
+import android.appwidget.AppWidgetManager;
+import android.appwidget.AppWidgetProvider;
+import android.content.Context;
+import android.os.Bundle;
+
+public abstract class StubbableAppWidgetProvider extends AppWidgetProvider {
+    @Override
+    public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
+        AppWidgetProviderCallbacks callbacks = getCallbacks();
+        if (callbacks != null) {
+            callbacks.onUpdate(context, appWidgetManager, appWidgetIds);
+        }
+    }
+
+    @Override
+    public void onAppWidgetOptionsChanged(Context context, AppWidgetManager appWidgetManager,
+            int appWidgetId, Bundle newOptions) {
+        AppWidgetProviderCallbacks callbacks = getCallbacks();
+        if (callbacks != null) {
+            callbacks.onAppWidgetOptionsChanged(context, appWidgetManager, appWidgetId, newOptions);
+        }
+    }
+
+    @Override
+    public void onDeleted(Context context, int[] appWidgetIds) {
+        AppWidgetProviderCallbacks callbacks = getCallbacks();
+        if (callbacks != null) {
+            callbacks.onDeleted(context, appWidgetIds);
+        }
+    }
+
+    @Override
+    public void onEnabled(Context context) {
+        AppWidgetProviderCallbacks callbacks = getCallbacks();
+        if (callbacks != null) {
+            callbacks.onEnabled(context);
+        }
+    }
+
+    @Override
+    public void onDisabled(Context context) {
+        AppWidgetProviderCallbacks callbacks = getCallbacks();
+        if (callbacks != null) {
+            callbacks.onDisabled(context);
+        }
+    }
+
+    @Override
+    public void onRestored(Context context, int[] oldWidgetIds, int[] newWidgetIds) {
+        AppWidgetProviderCallbacks callbacks = getCallbacks();
+        if (callbacks != null) {
+            super.onRestored(context, oldWidgetIds, newWidgetIds);
+        }
+    }
+
+    protected abstract AppWidgetProviderCallbacks getCallbacks();
+}
diff --git a/tests/tests/appwidget/src/android/appwidget/cts/service/MyAppWidgetService.java b/tests/tests/appwidget/src/android/appwidget/cts/service/MyAppWidgetService.java
new file mode 100644
index 0000000..313855b
--- /dev/null
+++ b/tests/tests/appwidget/src/android/appwidget/cts/service/MyAppWidgetService.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.appwidget.cts.service;
+
+import android.content.Intent;
+import android.widget.RemoteViewsService;
+
+public class MyAppWidgetService extends RemoteViewsService {
+    private static final Object sLock = new Object();
+
+    private static RemoteViewsFactory sFactory;
+
+    public static void setFactory(RemoteViewsFactory factory) {
+        synchronized (sLock) {
+            sFactory = factory;
+        }
+    }
+
+    @Override
+    public RemoteViewsFactory onGetViewFactory(Intent intent) {
+        synchronized (sLock) {
+            return sFactory;
+        }
+    }
+}
diff --git a/tests/tests/bluetooth/Android.mk b/tests/tests/bluetooth/Android.mk
index 701730d..4f837fc 100644
--- a/tests/tests/bluetooth/Android.mk
+++ b/tests/tests/bluetooth/Android.mk
@@ -18,16 +18,12 @@
 
 LOCAL_PACKAGE_NAME := CtsBluetoothTestCases
 
-
 # Don't include this package in any target.
 LOCAL_MODULE_TAGS := optional
 
 # When built, explicitly put it in the data partition.
 LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
 
-# All tests should include android.test.runner.
-LOCAL_JAVA_LIBRARIES := android.test.runner
-
 LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
diff --git a/tests/tests/bluetooth/AndroidManifest.xml b/tests/tests/bluetooth/AndroidManifest.xml
index 9caa267..c9ad122 100644
--- a/tests/tests/bluetooth/AndroidManifest.xml
+++ b/tests/tests/bluetooth/AndroidManifest.xml
@@ -26,9 +26,12 @@
     </application>
 
     <!-- This is a self-instrumenting test package. -->
-    <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
                      android:targetPackage="com.android.cts.bluetooth"
-                     android:label="CTS tests of bluetooth component"/>
+                     android:label="CTS tests of bluetooth component">
+        <meta-data android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
 
 </manifest>
 
diff --git a/tests/tests/calendarcommon/Android.mk b/tests/tests/calendarcommon/Android.mk
index c825c32..fa4b6c5 100644
--- a/tests/tests/calendarcommon/Android.mk
+++ b/tests/tests/calendarcommon/Android.mk
@@ -25,9 +25,6 @@
 # When built, explicitly put it in the data partition.
 LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
 
-# All tests should include android.test.runner.
-LOCAL_JAVA_LIBRARIES := android.test.runner
-
 LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
diff --git a/tests/tests/calendarcommon/AndroidManifest.xml b/tests/tests/calendarcommon/AndroidManifest.xml
index dc95af5..17520a3 100644
--- a/tests/tests/calendarcommon/AndroidManifest.xml
+++ b/tests/tests/calendarcommon/AndroidManifest.xml
@@ -23,9 +23,12 @@
     </application>
 
     <!-- This is a self-instrumenting test package. -->
-    <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
                      android:targetPackage="com.android.cts.calendarcommon2"
-                     android:label="CTS tests of calendarcommon"/>
+                     android:label="CTS tests of calendarcommon">
+        <meta-data android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
 
     <uses-sdk android:minSdkVersion="15" android:targetSdkVersion="15"></uses-sdk>
 
diff --git a/tests/tests/calendarcommon/src/android/calendarcommon2/cts/Calendarcommon2Test.java b/tests/tests/calendarcommon/src/android/calendarcommon2/cts/Calendarcommon2Test.java
index a17e3b8..24a04a5 100644
--- a/tests/tests/calendarcommon/src/android/calendarcommon2/cts/Calendarcommon2Test.java
+++ b/tests/tests/calendarcommon/src/android/calendarcommon2/cts/Calendarcommon2Test.java
@@ -16,7 +16,6 @@
 
 package android.calendarcommon2.cts;
 
-import android.test.InstrumentationCtsTestRunner;
 import android.test.InstrumentationTestCase;
 import android.test.suitebuilder.annotation.MediumTest;
 import com.android.calendarcommon2.RecurrenceSet;
diff --git a/tests/tests/content/AndroidManifest.xml b/tests/tests/content/AndroidManifest.xml
index 5af05eb..8d57e49 100644
--- a/tests/tests/content/AndroidManifest.xml
+++ b/tests/tests/content/AndroidManifest.xml
@@ -39,8 +39,11 @@
         <uses-library android:name="android.test.runner" />
     </application>
 
-    <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
                      android:targetPackage="com.android.cts.stub"
-                     android:label="CTS tests of android.content"/>
+                     android:label="CTS tests of android.content">
+        <meta-data android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
 </manifest>
 
diff --git a/tests/tests/database/AndroidManifest.xml b/tests/tests/database/AndroidManifest.xml
index 602f783..fefcc1f 100644
--- a/tests/tests/database/AndroidManifest.xml
+++ b/tests/tests/database/AndroidManifest.xml
@@ -23,9 +23,12 @@
         <uses-library android:name="android.test.runner" />
     </application>
 
-    <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
                      android:targetPackage="com.android.cts.database"
-                     android:label="CTS tests of android.database"/>
+                     android:label="CTS tests of android.database">
+        <meta-data android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
 
 </manifest>
 
diff --git a/tests/tests/display/Android.mk b/tests/tests/display/Android.mk
index ec5b40d..a48a8e3 100644
--- a/tests/tests/display/Android.mk
+++ b/tests/tests/display/Android.mk
@@ -21,8 +21,6 @@
 # and when built explicitly put it in the data partition
 LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
 
-LOCAL_JAVA_LIBRARIES := android.test.runner
-
 LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
diff --git a/tests/tests/display/AndroidManifest.xml b/tests/tests/display/AndroidManifest.xml
index d1386d1..0b24754 100644
--- a/tests/tests/display/AndroidManifest.xml
+++ b/tests/tests/display/AndroidManifest.xml
@@ -24,9 +24,13 @@
         <uses-library android:name="android.test.runner" />
     </application>
 
-    <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
                      android:targetPackage="com.android.cts.display"
-                     android:label="CTS tests of android.view.display"/>
+                     android:label="CTS tests of android.view.display">
+        <meta-data
+            android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
 
 </manifest>
 
diff --git a/tests/tests/dpi/Android.mk b/tests/tests/dpi/Android.mk
index a9dbcc3..fde990b 100644
--- a/tests/tests/dpi/Android.mk
+++ b/tests/tests/dpi/Android.mk
@@ -17,8 +17,6 @@
 
 include $(CLEAR_VARS)
 
-LOCAL_JAVA_LIBRARIES := android.test.runner
-
 LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
@@ -44,8 +42,8 @@
 
 LOCAL_MODULE_TAGS := optional
 
-LOCAL_JAVA_LIBRARIES := android.test.runner
-
 LOCAL_MODULE := android.cts.dpi
 
+LOCAL_SDK_VERSION := current
+
 include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/tests/tests/dpi/AndroidManifest.xml b/tests/tests/dpi/AndroidManifest.xml
index bacfe4a..0197056 100644
--- a/tests/tests/dpi/AndroidManifest.xml
+++ b/tests/tests/dpi/AndroidManifest.xml
@@ -26,7 +26,10 @@
                 android:configChanges="orientation" />
     </application>
 
-    <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
                      android:targetPackage="com.android.cts.dpi"
-                     android:label="CTS tests for DPI"/>
+                     android:label="CTS tests for DPI">
+        <meta-data android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
 </manifest>
diff --git a/tests/tests/dpi2/Android.mk b/tests/tests/dpi2/Android.mk
index 92ba992..03a687d 100644
--- a/tests/tests/dpi2/Android.mk
+++ b/tests/tests/dpi2/Android.mk
@@ -17,7 +17,6 @@
 
 include $(CLEAR_VARS)
 
-LOCAL_JAVA_LIBRARIES := android.test.runner
 # We use the DefaultManifestAttributesTest from the android.cts.dpi package.
 LOCAL_STATIC_JAVA_LIBRARIES := android.cts.dpi ctstestrunner
 
@@ -29,8 +28,6 @@
 
 LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
 
-# We would set LOCAL_SDK_VERSION := 3 here, but the build system
-# doesn't currently support setting LOCAL_SDK_VERSION to anything but
-# current.
+LOCAL_SDK_VERSION := current
 
 include $(BUILD_CTS_PACKAGE)
diff --git a/tests/tests/dpi2/AndroidManifest.xml b/tests/tests/dpi2/AndroidManifest.xml
index 0364b10..6dbdc23 100644
--- a/tests/tests/dpi2/AndroidManifest.xml
+++ b/tests/tests/dpi2/AndroidManifest.xml
@@ -27,7 +27,10 @@
          properly for the screen size attributes. -->
     <uses-sdk android:targetSdkVersion="3" />
 
-    <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
                      android:targetPackage="com.android.cts.dpi2"
-                     android:label="CTS tests for DPI"/>
+                     android:label="CTS tests for DPI">
+        <meta-data android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
 </manifest>
diff --git a/tests/tests/dreams/Android.mk b/tests/tests/dreams/Android.mk
index c630d8a..eca3d83 100644
--- a/tests/tests/dreams/Android.mk
+++ b/tests/tests/dreams/Android.mk
@@ -24,14 +24,11 @@
 # When built, explicitly put it in the data partition.
 LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
 
-# All tests should include android.test.runner.
-LOCAL_JAVA_LIBRARIES := android.test.runner
-
 LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
-# Need access to ServiceManager
+# Need access to ServiceManager - see b/13307221
 #LOCAL_SDK_VERSION := current
 
 include $(BUILD_CTS_PACKAGE)
diff --git a/tests/tests/dreams/AndroidManifest.xml b/tests/tests/dreams/AndroidManifest.xml
index fb3e564..b395a4f 100644
--- a/tests/tests/dreams/AndroidManifest.xml
+++ b/tests/tests/dreams/AndroidManifest.xml
@@ -22,9 +22,12 @@
     </application>
 
     <!-- This is a self-instrumenting test package. -->
-    <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
                      android:targetPackage="com.android.cts.dreams"
-                     android:label="CTS tests for the android.service.dreams package"/>
+                     android:label="CTS tests for the android.service.dreams package">
+        <meta-data android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
 
 </manifest>
 
diff --git a/tests/tests/drm/Android.mk b/tests/tests/drm/Android.mk
index 8b76cd8..9404f4b 100644
--- a/tests/tests/drm/Android.mk
+++ b/tests/tests/drm/Android.mk
@@ -21,8 +21,6 @@
 # and when built explicitly put it in the data partition
 LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
 
-LOCAL_JAVA_LIBRARIES := android.test.runner
-
 LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
@@ -35,8 +33,7 @@
 	libctsdrm_jni \
 	libdrmtestplugin
 
-# uncomment when dalvik.annotation.Test* are removed or part of SDK
-#LOCAL_SDK_VERSION := current
+LOCAL_SDK_VERSION := current
 
 include $(BUILD_CTS_PACKAGE)
 
diff --git a/tests/tests/drm/AndroidManifest.xml b/tests/tests/drm/AndroidManifest.xml
index 1fc8968..dd70f02 100644
--- a/tests/tests/drm/AndroidManifest.xml
+++ b/tests/tests/drm/AndroidManifest.xml
@@ -22,9 +22,12 @@
         <uses-library android:name="android.test.runner" />
     </application>
 
-    <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
                      android:targetPackage="com.android.cts.stub"
-                     android:label="CTS tests of android.drm"/>
+                     android:label="CTS tests of android.drm">
+        <meta-data android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
 
 </manifest>
 
diff --git a/tests/tests/effect/Android.mk b/tests/tests/effect/Android.mk
index 9e27769..6a9778e 100644
--- a/tests/tests/effect/Android.mk
+++ b/tests/tests/effect/Android.mk
@@ -24,9 +24,6 @@
 # When built, explicitly put it in the data partition.
 LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
 
-# All tests should include android.test.runner.
-LOCAL_JAVA_LIBRARIES := android.test.runner
-
 LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
diff --git a/tests/tests/effect/AndroidManifest.xml b/tests/tests/effect/AndroidManifest.xml
index 1a346ae..481be14 100644
--- a/tests/tests/effect/AndroidManifest.xml
+++ b/tests/tests/effect/AndroidManifest.xml
@@ -23,9 +23,12 @@
     </application>
 
     <!-- This is a self-instrumenting test package. -->
-    <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
                      android:targetPackage="com.android.cts.effect"
-                     android:label="CTS tests of android.media.effect component"/>
+                     android:label="CTS tests of android.media.effect component">
+        <meta-data android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
 
 </manifest>
 
diff --git a/tests/tests/gesture/Android.mk b/tests/tests/gesture/Android.mk
index 5d44cfc..4a97931 100755
--- a/tests/tests/gesture/Android.mk
+++ b/tests/tests/gesture/Android.mk
@@ -21,8 +21,6 @@
 # and when built explicitly put it in the data partition
 LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
 
-LOCAL_JAVA_LIBRARIES := android.test.runner
-
 LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
diff --git a/tests/tests/gesture/AndroidManifest.xml b/tests/tests/gesture/AndroidManifest.xml
index 39e2b90..b288cd2 100755
--- a/tests/tests/gesture/AndroidManifest.xml
+++ b/tests/tests/gesture/AndroidManifest.xml
@@ -24,9 +24,12 @@
     </application>
 
     <!--  self-instrumenting test package. -->
-    <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
                      android:targetPackage="com.android.cts.gesture"
-                     android:label="CTS tests of android.gesture"/>
+                     android:label="CTS tests of android.gesture">
+        <meta-data android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
 
 </manifest>
 
diff --git a/tests/tests/graphics/Android.mk b/tests/tests/graphics/Android.mk
index 811267a..b65bb1b 100644
--- a/tests/tests/graphics/Android.mk
+++ b/tests/tests/graphics/Android.mk
@@ -16,12 +16,7 @@
 
 include $(CLEAR_VARS)
 
-# don't include this package in any target
-LOCAL_MODULE_TAGS := optional
-# and when built explicitly put it in the data partition
-LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
-
-LOCAL_JAVA_LIBRARIES := android.test.runner
+LOCAL_MODULE_TAGS := tests
 
 LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner
 
@@ -31,7 +26,6 @@
 
 LOCAL_INSTRUMENTATION_FOR := CtsTestStubs
 
-# uncomment when dalvik.annotation.Test* are removed or part of SDK
-#LOCAL_SDK_VERSION := current
+LOCAL_SDK_VERSION := current
 
 include $(BUILD_CTS_PACKAGE)
diff --git a/tests/tests/graphics/AndroidManifest.xml b/tests/tests/graphics/AndroidManifest.xml
index c052a15..0371093 100644
--- a/tests/tests/graphics/AndroidManifest.xml
+++ b/tests/tests/graphics/AndroidManifest.xml
@@ -23,9 +23,12 @@
         <uses-library android:name="android.test.runner" />
     </application>
 
-    <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
                      android:targetPackage="com.android.cts.stub"
-                     android:label="CTS tests of android.graphics"/>
+                     android:label="CTS tests of android.graphics">
+        <meta-data android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
 
 </manifest>
 
diff --git a/tests/tests/graphics/src/android/graphics/cts/BitmapFactoryTest.java b/tests/tests/graphics/src/android/graphics/cts/BitmapFactoryTest.java
index a80d2e2..edb8d73 100644
--- a/tests/tests/graphics/src/android/graphics/cts/BitmapFactoryTest.java
+++ b/tests/tests/graphics/src/android/graphics/cts/BitmapFactoryTest.java
@@ -39,7 +39,6 @@
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
-import java.io.OutputStream;
 import java.io.RandomAccessFile;
 
 public class BitmapFactoryTest extends InstrumentationTestCase {
@@ -69,6 +68,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),
@@ -197,11 +200,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.
@@ -210,7 +217,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());
         }
     }
 
@@ -237,6 +283,50 @@
         assertEquals(START_WIDTH, b.getWidth());
     }
 
+    public void testDecodeFileDescriptor3() throws IOException {
+        // Arbitrary offsets to use. If the offset of the FD matches the offset of the image,
+        // decoding should succeed, but if they do not match, decoding should fail.
+        long ACTUAL_OFFSETS[] = new long[] { 0, 17 };
+        for (int RES_ID : RES_IDS) {
+            for (int j = 0; j < ACTUAL_OFFSETS.length; ++j) {
+                // FIXME: The purgeable test should attempt to purge the memory
+                // to force a re-decode.
+                for (boolean TEST_PURGEABLE : new boolean[] { false, true }) {
+                    BitmapFactory.Options opts = new BitmapFactory.Options();
+                    opts.inPurgeable = TEST_PURGEABLE;
+                    opts.inInputShareable = TEST_PURGEABLE;
+
+                    long actualOffset = ACTUAL_OFFSETS[j];
+                    String path = obtainPath(RES_ID, actualOffset);
+                    RandomAccessFile file = new RandomAccessFile(path, "r");
+                    FileDescriptor fd = file.getFD();
+                    assertTrue(fd.valid());
+
+                    // Set the offset to ACTUAL_OFFSET
+                    file.seek(actualOffset);
+                    assertEquals(file.getFilePointer(), actualOffset);
+
+                    // Now decode. This should be successful and leave the offset
+                    // unchanged.
+                    Bitmap b = BitmapFactory.decodeFileDescriptor(fd, null, opts);
+                    assertNotNull(b);
+                    assertEquals(file.getFilePointer(), actualOffset);
+
+                    // Now use the other offset. It should fail to decode, and
+                    // the offset should remain unchanged.
+                    long otherOffset = ACTUAL_OFFSETS[(j + 1) % ACTUAL_OFFSETS.length];
+                    assertFalse(otherOffset == actualOffset);
+                    file.seek(otherOffset);
+                    assertEquals(file.getFilePointer(), otherOffset);
+
+                    b = BitmapFactory.decodeFileDescriptor(fd, null, opts);
+                    assertNull(b);
+                    assertEquals(file.getFilePointer(), otherOffset);
+                }
+            }
+        }
+    }
+
     public void testDecodeFile1() throws IOException {
         Bitmap b = BitmapFactory.decodeFile(obtainPath(), mOpt1);
         assertNotNull(b);
@@ -275,6 +365,50 @@
         assertTrue(pass.isMutable());
     }
 
+    /**
+     * Create bitmap sized to load unscaled resources: start, pass, and alpha
+     */
+    private Bitmap createBitmapForReuse(int pixelCount) {
+        Bitmap bitmap = Bitmap.createBitmap(pixelCount, 1, Config.ARGB_8888);
+        bitmap.eraseColor(Color.BLACK);
+        bitmap.setHasAlpha(false);
+        return bitmap;
+    }
+
+    /**
+     * Decode resource with ResId into reuse bitmap without scaling, verifying expected hasAlpha
+     */
+    private void decodeResourceWithReuse(Bitmap reuse, int resId, boolean hasAlpha) {
+        BitmapFactory.Options options = new BitmapFactory.Options();
+        options.inMutable = true;
+        options.inSampleSize = 1;
+        options.inScaled = false;
+        options.inBitmap = reuse;
+        Bitmap output = BitmapFactory.decodeResource(mRes, resId, options);
+        assertSame(reuse, output);
+        assertEquals(output.hasAlpha(), hasAlpha);
+    }
+
+    public void testDecodeReuseHasAlpha() throws IOException {
+        final int bitmapSize = 31; // size in pixels of start, pass, and alpha resources
+        final int pixelCount = bitmapSize * bitmapSize;
+
+        // Test reuse, hasAlpha false and true
+        Bitmap bitmap = createBitmapForReuse(pixelCount);
+        decodeResourceWithReuse(bitmap, R.drawable.start, false);
+        decodeResourceWithReuse(bitmap, R.drawable.alpha, true);
+
+        // Test pre-reconfigure, hasAlpha false and true
+        bitmap = createBitmapForReuse(pixelCount);
+        bitmap.reconfigure(bitmapSize, bitmapSize, Config.ARGB_8888);
+        bitmap.setHasAlpha(true);
+        decodeResourceWithReuse(bitmap, R.drawable.start, false);
+
+        bitmap = createBitmapForReuse(pixelCount);
+        bitmap.reconfigure(bitmapSize, bitmapSize, Config.ARGB_8888);
+        decodeResourceWithReuse(bitmap, R.drawable.alpha, true);
+    }
+
     public void testDecodeReuseFormats() throws IOException {
         // reuse should support all image formats
         for (int i = 0; i < RES_IDS.length; ++i) {
@@ -464,17 +598,32 @@
     }
 
     private String obtainPath() throws IOException {
+        return obtainPath(R.drawable.start, 0);
+    }
+
+    /*
+     * Create a new file and return a path to it.
+     * @param resId Original file. It will be copied into the new file.
+     * @param offset Number of zeroes to write to the new file before the
+     *               copied file. This allows testing decodeFileDescriptor
+     *               with an offset. Must be less than or equal to 1024
+     */
+    private String obtainPath(int resId, long offset) throws IOException {
         File dir = getInstrumentation().getTargetContext().getFilesDir();
         dir.mkdirs();
+        // The suffix does not necessarily represent theactual file type.
         File file = new File(dir, "test.jpg");
         if (!file.createNewFile()) {
             if (!file.exists()) {
                 fail("Failed to create new File!");
             }
         }
-        InputStream is = obtainInputStream();
+        InputStream is = obtainInputStream(resId);
         FileOutputStream fOutput = new FileOutputStream(file);
         byte[] dataBuffer = new byte[1024];
+        // Write a bunch of zeroes before the image.
+        assertTrue(offset <= 1024);
+        fOutput.write(dataBuffer, 0, (int) offset);
         int readLength = 0;
         while ((readLength = is.read(dataBuffer)) != -1) {
             fOutput.write(dataBuffer, 0, readLength);
@@ -488,7 +637,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();
 
@@ -501,30 +650,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/cts/BitmapTest.java b/tests/tests/graphics/src/android/graphics/cts/BitmapTest.java
index 997560d..487d9bf 100644
--- a/tests/tests/graphics/src/android/graphics/cts/BitmapTest.java
+++ b/tests/tests/graphics/src/android/graphics/cts/BitmapTest.java
@@ -513,6 +513,102 @@
         }
     }
 
+    // Used by testAlphaAndPremul. FIXME: Should we also test Index8? That would require decoding a
+    // Bitmap, since one cannot be created directly. It will also have a Config of null, since it
+    // has no Java equivalent.
+    private static Config[] CONFIGS = new Config[] { Config.ALPHA_8, Config.ARGB_4444,
+            Config.ARGB_8888, Config.RGB_565 };
+
+    // test that reconfigure, setHasAlpha, and setPremultiplied behave as expected with
+    // respect to alpha and premultiplied.
+    public void testAlphaAndPremul() {
+        boolean falseTrue[] = new boolean[] { false, true };
+        for (Config fromConfig : CONFIGS) {
+            for (Config toConfig : CONFIGS) {
+                for (boolean hasAlpha : falseTrue) {
+                    for (boolean isPremul : falseTrue) {
+                        Bitmap bitmap = Bitmap.createBitmap(10, 10, fromConfig);
+
+                        // 4444 is deprecated, and will convert to 8888. No need to
+                        // attempt a reconfigure, which will be tested when fromConfig
+                        // is 8888.
+                        if (fromConfig == Config.ARGB_4444) {
+                            assertEquals(bitmap.getConfig(), Config.ARGB_8888);
+                            break;
+                        }
+
+                        bitmap.setHasAlpha(hasAlpha);
+                        bitmap.setPremultiplied(isPremul);
+
+                        checkAlphaAndPremul(bitmap, hasAlpha, isPremul, false);
+
+                        // reconfigure to a smaller size so the function will still succeed when
+                        // going to a Config that requires more bits.
+                        bitmap.reconfigure(1, 1, toConfig);
+                        if (toConfig == Config.ARGB_4444) {
+                            assertEquals(bitmap.getConfig(), Config.ARGB_8888);
+                        } else {
+                            assertEquals(bitmap.getConfig(), toConfig);
+                        }
+
+                        // Check that the alpha and premultiplied state has not changed (unless
+                        // we expected it to).
+                        checkAlphaAndPremul(bitmap, hasAlpha, isPremul, fromConfig == Config.RGB_565);
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     *  Assert that bitmap returns the appropriate values for hasAlpha() and isPremultiplied().
+     *  @param bitmap Bitmap to check.
+     *  @param expectedAlpha Expected return value from bitmap.hasAlpha(). Note that this is based
+     *          on what was set, but may be different from the actual return value depending on the
+     *          Config and convertedFrom565.
+     *  @param expectedPremul Expected return value from bitmap.isPremultiplied(). Similar to
+     *          expectedAlpha, this is based on what was set, but may be different from the actual
+     *          return value depending on the Config.
+     *  @param convertedFrom565 Whether bitmap was converted to its current Config by being
+     *          reconfigured from RGB_565. If true, and bitmap is now a Config that supports alpha,
+     *          hasAlpha() is expected to be true even if expectedAlpha is false.
+     */
+    private void checkAlphaAndPremul(Bitmap bitmap, boolean expectedAlpha, boolean expectedPremul,
+            boolean convertedFrom565) {
+        switch (bitmap.getConfig()) {
+            case ARGB_4444:
+                // This shouldn't happen, since we don't allow creating or converting
+                // to 4444.
+                assertFalse(true);
+                break;
+            case RGB_565:
+                assertFalse(bitmap.hasAlpha());
+                assertFalse(bitmap.isPremultiplied());
+                break;
+            case ALPHA_8:
+                // ALPHA_8 behaves mostly the same as 8888, except for premultiplied. Fall through.
+            case ARGB_8888:
+                // Since 565 is necessarily opaque, we revert to hasAlpha when switching to a type
+                // that can have alpha.
+                if (convertedFrom565) {
+                    assertTrue(bitmap.hasAlpha());
+                } else {
+                    assertEquals(bitmap.hasAlpha(), expectedAlpha);
+                }
+
+                if (bitmap.hasAlpha()) {
+                    // ALPHA_8's premultiplied status is undefined.
+                    if (bitmap.getConfig() != Config.ALPHA_8) {
+                        assertEquals(bitmap.isPremultiplied(), expectedPremul);
+                    }
+                } else {
+                    // Opaque bitmap is never considered premultiplied.
+                    assertFalse(bitmap.isPremultiplied());
+                }
+                break;
+        }
+    }
+
     public void testSetConfig() {
         mBitmap = Bitmap.createBitmap(100, 200, Bitmap.Config.RGB_565);
         int alloc = mBitmap.getAllocationByteCount();
diff --git a/tests/tests/graphics/src/android/graphics/cts/CanvasTest.java b/tests/tests/graphics/src/android/graphics/cts/CanvasTest.java
index 4102957..76eeee3 100644
--- a/tests/tests/graphics/src/android/graphics/cts/CanvasTest.java
+++ b/tests/tests/graphics/src/android/graphics/cts/CanvasTest.java
@@ -43,6 +43,7 @@
 
 import com.android.cts.stub.R;
 
+import java.util.Vector;
 
 public class CanvasTest extends InstrumentationTestCase {
     private final static int PAINT_COLOR = 0xff00ff00;
@@ -267,6 +268,86 @@
         }
     }
 
+    public void testSaveFlags1() {
+        int[] flags = {
+            Canvas.MATRIX_SAVE_FLAG,
+        };
+        verifySaveFlagsSequence(flags);
+    }
+
+    public void testSaveFlags2() {
+        int[] flags = {
+            Canvas.CLIP_SAVE_FLAG,
+        };
+        verifySaveFlagsSequence(flags);
+    }
+
+    public void testSaveFlags3() {
+        int[] flags = {
+            Canvas.ALL_SAVE_FLAG,
+            Canvas.MATRIX_SAVE_FLAG,
+            Canvas.MATRIX_SAVE_FLAG,
+        };
+        verifySaveFlagsSequence(flags);
+    }
+
+    public void testSaveFlags4() {
+        int[] flags = {
+            Canvas.ALL_SAVE_FLAG,
+            Canvas.CLIP_SAVE_FLAG,
+            Canvas.CLIP_SAVE_FLAG,
+        };
+        verifySaveFlagsSequence(flags);
+    }
+
+    public void testSaveFlags5() {
+        int[] flags = {
+            Canvas.MATRIX_SAVE_FLAG,
+            Canvas.MATRIX_SAVE_FLAG,
+            Canvas.CLIP_SAVE_FLAG,
+            Canvas.CLIP_SAVE_FLAG,
+            Canvas.MATRIX_SAVE_FLAG,
+            Canvas.MATRIX_SAVE_FLAG,
+        };
+        verifySaveFlagsSequence(flags);
+    }
+
+    public void testSaveFlags6() {
+        int[] flags = {
+            Canvas.CLIP_SAVE_FLAG,
+            Canvas.CLIP_SAVE_FLAG,
+            Canvas.MATRIX_SAVE_FLAG,
+            Canvas.MATRIX_SAVE_FLAG,
+            Canvas.CLIP_SAVE_FLAG,
+            Canvas.CLIP_SAVE_FLAG,
+        };
+        verifySaveFlagsSequence(flags);
+    }
+
+    public void testSaveFlags7() {
+        int[] flags = {
+            Canvas.MATRIX_SAVE_FLAG,
+            Canvas.MATRIX_SAVE_FLAG,
+            Canvas.ALL_SAVE_FLAG,
+            Canvas.ALL_SAVE_FLAG,
+            Canvas.CLIP_SAVE_FLAG,
+            Canvas.CLIP_SAVE_FLAG,
+        };
+        verifySaveFlagsSequence(flags);
+    }
+
+    public void testSaveFlags8() {
+        int[] flags = {
+            Canvas.MATRIX_SAVE_FLAG,
+            Canvas.CLIP_SAVE_FLAG,
+            Canvas.ALL_SAVE_FLAG,
+            Canvas.MATRIX_SAVE_FLAG,
+            Canvas.CLIP_SAVE_FLAG,
+            Canvas.ALL_SAVE_FLAG,
+        };
+        verifySaveFlagsSequence(flags);
+    }
+
     public void testSaveLayer1() {
         final Paint p = new Paint();
         final RectF rF = new RectF(0, 10, 31, 0);
@@ -906,6 +987,30 @@
         assertFalse(mCanvas.clipRegion(r, Op.XOR));
     }
 
+    public void testClipRegion3() {
+        assertTrue(mCanvas.clipRegion(new Region(0, 0, 10, 10)));
+        final Rect clip = mCanvas.getClipBounds();
+        assertEquals(0, clip.left);
+        assertEquals(0, clip.top);
+        assertEquals(10, clip.right);
+        assertEquals(10, clip.bottom);
+    }
+
+    public void testClipRegion4() {
+        mCanvas.translate(10, 10);
+        mCanvas.scale(2, 2);
+
+        final Matrix beforeMatrix = mCanvas.getMatrix();
+        assertTrue(mCanvas.clipRegion(new Region(0, 0, 10, 10)));
+        assertEquals(beforeMatrix, mCanvas.getMatrix());
+
+        Rect clip = mCanvas.getClipBounds();
+        assertEquals(-5, clip.left);
+        assertEquals(-5, clip.top);
+        assertEquals(0, clip.right);
+        assertEquals(0, clip.bottom);
+    }
+
     public void testGetDrawFilter() {
         assertNull(mCanvas.getDrawFilter());
         final DrawFilter dF = new DrawFilter();
@@ -1727,4 +1832,49 @@
         assertEquals(0.0f, values[7]);
         assertEquals(1.0f, values[8]);
     }
+
+    private RectF getDeviceClip() {
+        final RectF clip = new RectF(mCanvas.getClipBounds());
+        mCanvas.getMatrix().mapRect(clip);
+        return clip;
+    }
+
+    // Loops through the passed flags, applying each in order with successive calls
+    // to save, verifying the clip and matrix values when restoring.
+    private void verifySaveFlagsSequence(int[] saveFlags) {
+        final Vector<RectF> clips = new Vector<RectF>();
+        final Vector<Matrix> matrices = new Vector<Matrix>();
+
+        assertTrue(BITMAP_WIDTH > saveFlags.length);
+        assertTrue(BITMAP_HEIGHT > saveFlags.length);
+
+        for (int i = 0; i < saveFlags.length; ++i) {
+            clips.add(getDeviceClip());
+            matrices.add(mCanvas.getMatrix());
+            mCanvas.save(saveFlags[i]);
+
+            mCanvas.translate(1, 1);
+            mCanvas.clipRect(0, 0, BITMAP_WIDTH - i - 1, BITMAP_HEIGHT - i - 1);
+
+            if (i  > 0) {
+                // We are mutating the state on each iteration.
+                assertFalse(clips.elementAt(i).equals(clips.elementAt(i - 1)));
+                assertFalse(matrices.elementAt(i).equals(matrices.elementAt(i - 1)));
+            }
+        }
+
+        for (int i = saveFlags.length - 1; i >= 0; --i) {
+            // If clip/matrix flags are not set, the associated state should be preserved.
+            if ((saveFlags[i] & Canvas.CLIP_SAVE_FLAG) == 0) {
+                clips.elementAt(i).set(getDeviceClip());
+            }
+            if ((saveFlags[i] & Canvas.MATRIX_SAVE_FLAG) == 0) {
+                matrices.elementAt(i).set(mCanvas.getMatrix());
+            }
+
+            mCanvas.restore();
+            assertEquals(clips.elementAt(i), getDeviceClip());
+            assertEquals(matrices.elementAt(i), mCanvas.getMatrix());
+        }
+    }
 }
diff --git a/tests/tests/graphics/src/android/graphics/cts/LayerRasterizerTest.java b/tests/tests/graphics/src/android/graphics/cts/LayerRasterizerTest.java
index 4309ac5..289ee77 100644
--- a/tests/tests/graphics/src/android/graphics/cts/LayerRasterizerTest.java
+++ b/tests/tests/graphics/src/android/graphics/cts/LayerRasterizerTest.java
@@ -17,32 +17,44 @@
 package android.graphics.cts;
 
 import junit.framework.TestCase;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
 import android.graphics.LayerRasterizer;
 import android.graphics.Paint;
+import android.graphics.Rasterizer;
 
 public class LayerRasterizerTest extends TestCase {
+    private final static int BITMAP_WIDTH = 16;
+    private final static int BITMAP_HEIGHT = 16;
+
+    private void exerciseRasterizer(Rasterizer rasterizer) {
+        Bitmap bm = Bitmap.createBitmap(BITMAP_WIDTH, BITMAP_HEIGHT, Bitmap.Config.ARGB_8888);
+        Canvas canvas = new Canvas(bm);
+        Paint paint = new Paint();
+
+        // just want to confirm that we don't crash or throw an exception
+        paint.setRasterizer(rasterizer);
+        canvas.drawCircle(BITMAP_WIDTH/2, BITMAP_WIDTH/2, BITMAP_WIDTH/2, paint);
+    }
 
     public void testConstructor() {
-
-        // new the LayerRasterizer instance
-        new LayerRasterizer();
+        exerciseRasterizer(new LayerRasterizer());
     }
 
     public void testAddLayer1() {
-        // new the LayerRasterizer instance
         LayerRasterizer layerRasterizer = new LayerRasterizer();
         Paint p = new Paint();
         layerRasterizer.addLayer(p);
-        // this function called a native function and this test just make sure
-        // it doesn't throw out any exception.
+        exerciseRasterizer(layerRasterizer);
     }
 
     public void testAddLayer2() {
-        // new the LayerRasterizer instance
         LayerRasterizer layerRasterizer = new LayerRasterizer();
         layerRasterizer.addLayer(new Paint(), 1.0f, 1.0f);
-        // this function called a native function and this test just make sure
-        // it doesn't throw out any exception.
+        exerciseRasterizer(layerRasterizer);
+        // explicitly add another layer and draw again
+        layerRasterizer.addLayer(new Paint(), 2.0f, 2.0f);
+        exerciseRasterizer(layerRasterizer);
     }
 
 }
diff --git a/tests/tests/graphics/src/android/graphics/cts/PaintFlagsDrawFilterTest.java b/tests/tests/graphics/src/android/graphics/cts/PaintFlagsDrawFilterTest.java
index 31dbf16..ee3ec7c 100644
--- a/tests/tests/graphics/src/android/graphics/cts/PaintFlagsDrawFilterTest.java
+++ b/tests/tests/graphics/src/android/graphics/cts/PaintFlagsDrawFilterTest.java
@@ -107,4 +107,82 @@
         // underline is under the text or at least at the bottom of it
         assertTrue(rect.top >= TEXT_Y);
     }
+
+    // Tests that FILTER_BITMAP_FLAG is handled properly.
+    public void testPaintFlagsDrawFilter2() {
+        // Create a bitmap with alternating black and white pixels.
+        int kWidth = 5;
+        int kHeight = 5;
+        int colors[] = new int [] { Color.WHITE, Color.BLACK };
+        int k = 0;
+        Bitmap grid = Bitmap.createBitmap(kWidth, kHeight, Config.ARGB_8888);
+        for (int i = 0; i < kWidth; ++i) {
+            for (int j = 0; j < kHeight; ++j) {
+                grid.setPixel(i, j, colors[k]);
+                k = (k + 1) % 2;
+            }
+        }
+
+        // Setup a scaled canvas for drawing the bitmap, with and without FILTER_BITMAP_FLAG set.
+        // When the flag is set, there will be gray pixels. When the flag is not set, all pixels
+        // will be either black or white.
+        int kScale = 5;
+        Bitmap dst = Bitmap.createBitmap(kWidth * kScale, kHeight * kScale, Config.ARGB_8888);
+        Canvas canvas = new Canvas(dst);
+        canvas.scale(kScale, kScale);
+
+        // Drawn without FILTER_BITMAP_FLAG, all pixels will be black or white.
+        Paint simplePaint = new Paint();
+        canvas.drawBitmap(grid, 0, 0, simplePaint);
+
+        assertContainsOnlyBlackAndWhite(dst);
+
+        // Drawn with FILTER_BITMAP_FLAG, some pixels will be somewhere in between.
+        Paint filterBitmapPaint = new Paint(Paint.FILTER_BITMAP_FLAG);
+        canvas.drawBitmap(grid, 0, 0, filterBitmapPaint);
+
+        assertContainsNonBW(dst);
+
+        // Drawing with a paint that FILTER_BITMAP_FLAG set and a DrawFilter that removes
+        // FILTER_BITMAP_FLAG should remove the effect of the flag, resulting in all pixels being
+        // either black or white.
+        canvas.setDrawFilter(new PaintFlagsDrawFilter(Paint.FILTER_BITMAP_FLAG, 0));
+        canvas.drawBitmap(grid, 0, 0, filterBitmapPaint);
+
+        assertContainsOnlyBlackAndWhite(dst);
+
+        // Likewise, drawing with a DrawFilter that sets FILTER_BITMAP_FLAG should filter,
+        // resulting in gray pixels.
+        canvas.setDrawFilter(new PaintFlagsDrawFilter(0, Paint.FILTER_BITMAP_FLAG));
+        canvas.drawBitmap(grid, 0, 0, simplePaint);
+
+        assertContainsNonBW(dst);
+    }
+
+    // Assert that at least one pixel is neither black nor white. This is used to verify that
+    // filtering was done, since the original bitmap only contained black and white pixels.
+    private void assertContainsNonBW(Bitmap bitmap) {
+        for (int i = 0; i < bitmap.getWidth(); ++i) {
+            for (int j = 0; j < bitmap.getHeight(); ++j) {
+                int color = bitmap.getPixel(i, j);
+                if (color != Color.BLACK && color != Color.WHITE) {
+                    // Filtering must have been done.
+                    return;
+                }
+            }
+        }
+        // Filtering did not happen.
+        assertTrue(false);
+    }
+
+    // Assert that every pixel is either black or white. Used to verify that no filtering was
+    // done, since the original bitmap contained only black and white pixels.
+    private void assertContainsOnlyBlackAndWhite(Bitmap bitmap) {
+        for (int i = 0; i < bitmap.getWidth(); ++i) {
+            for (int j = 0; j < bitmap.getHeight(); ++j) {
+                int color = bitmap.getPixel(i, j);
+                assertTrue(color == Color.BLACK || color == Color.WHITE);
+            }
+        }
+    }
 }
diff --git a/tests/tests/graphics/src/android/graphics/cts/PaintTest.java b/tests/tests/graphics/src/android/graphics/cts/PaintTest.java
index f4904fc..1f709d3 100644
--- a/tests/tests/graphics/src/android/graphics/cts/PaintTest.java
+++ b/tests/tests/graphics/src/android/graphics/cts/PaintTest.java
@@ -112,6 +112,30 @@
         // Reverse measure substring in the middle and restrict width to the last 2 characters.
         assertBreakText(text, textChars, textSpan, 2, 5, false, widths[3] + widths[4],
                 2, widths[3] + widths[4]);
+
+        // a single Emoji (U+1f601)
+        String emoji = "\ud83d\ude01";
+        char[] emojiChars = emoji.toCharArray();
+        SpannedString emojiSpan = new SpannedString(emoji);
+
+        float[] emojiWidths = new float[emoji.length()];
+        assertEquals(emoji.length(), p.getTextWidths(emoji, emojiWidths));
+
+        // Measure substring with a cluster
+        assertBreakText(emoji, emojiChars, emojiSpan, 0, 2, true, 0,
+                0, 0);
+
+        // Measure substring with a cluster
+        assertBreakText(emoji, emojiChars, emojiSpan, 0, 2, true, emojiWidths[0],
+                2, emojiWidths[0]);
+
+        // Reverse measure substring with a cluster
+        assertBreakText(emoji, emojiChars, emojiSpan, 0, 2, false, 0,
+                0, 0);
+
+        // Measure substring with a cluster
+        assertBreakText(emoji, emojiChars, emojiSpan, 0, 2, false, emojiWidths[0],
+                2, emojiWidths[0]);
     }
 
     private void assertBreakText(String text, char[] textChars, SpannedString textSpan,
diff --git a/tests/tests/graphics/src/android/graphics/cts/PictureTest.java b/tests/tests/graphics/src/android/graphics/cts/PictureTest.java
index 81f053d..912c5a7 100644
--- a/tests/tests/graphics/src/android/graphics/cts/PictureTest.java
+++ b/tests/tests/graphics/src/android/graphics/cts/PictureTest.java
@@ -24,18 +24,90 @@
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
 import android.graphics.Color;
+import android.graphics.Matrix;
 import android.graphics.Paint;
 import android.graphics.Picture;
+import android.graphics.Rect;
 import android.graphics.Paint.Style;
-
+import android.graphics.Region.Op;
 
 public class PictureTest extends TestCase {
 
     private static final int TEST_WIDTH = 4; // must be >= 2
     private static final int TEST_HEIGHT = 3; // must >= 2
 
-    public void testPicture() throws Exception {
+    private final Rect mClipRect = new Rect(0, 0, 2, 2);
 
+    // This method tests out some edge cases w.r.t. Picture creation.
+    // In particular, this test verifies that, in the following situations,
+    // the created picture (effectively) has balanced saves and restores:
+    //   - copy constructed picture from actively recording picture
+    //   - writeToStream/createFromStream created picture from actively recording picture
+    //   - actively recording picture after draw call
+    public void testSaveRestoreBalance() throws Exception {
+        Picture original = new Picture();
+        Canvas canvas = original.beginRecording(TEST_WIDTH, TEST_HEIGHT);
+        assertNotNull(canvas);
+        createImbalance(canvas);
+
+        int expectedSaveCount = canvas.getSaveCount();
+
+        Picture copy = new Picture(original);
+        checkBalance(copy);
+
+        assertEquals(expectedSaveCount, canvas.getSaveCount());
+
+        ByteArrayOutputStream bout = new ByteArrayOutputStream();
+        original.writeToStream(bout);
+
+        assertEquals(expectedSaveCount, canvas.getSaveCount());
+
+        Picture serialized = Picture.createFromStream(new ByteArrayInputStream(bout.toByteArray()));
+        // The serialization/deserialization process will balance the saves and restores
+        checkBalance(serialized);
+
+        assertEquals(expectedSaveCount, canvas.getSaveCount());
+
+        Bitmap bitmap = Bitmap.createBitmap(TEST_WIDTH, TEST_HEIGHT, Bitmap.Config.ARGB_8888);
+        Canvas drawDest = new Canvas(bitmap);
+        original.draw(drawDest);
+        checkBalance(original);
+    }
+
+    // Add an extra save with a transform and clip
+    private void createImbalance(Canvas canvas) {
+        canvas.save();
+        canvas.clipRect(mClipRect, Op.REPLACE);
+        canvas.translate(1.0f, 1.0f);
+        Paint paint = new Paint();
+        paint.setColor(Color.GREEN);
+        canvas.drawRect(0, 0, 10, 10, paint);
+    }
+
+    private void checkBalance(Picture picture) {
+        Bitmap bitmap = Bitmap.createBitmap(TEST_WIDTH, TEST_HEIGHT, Bitmap.Config.ARGB_8888);
+        Canvas canvas = new Canvas(bitmap);
+
+        int beforeSaveCount = canvas.getSaveCount();
+
+        final Matrix beforeMatrix = canvas.getMatrix();
+
+        Rect beforeClip = new Rect();
+        assertTrue(canvas.getClipBounds(beforeClip));
+
+        canvas.drawPicture(picture);
+
+        assertEquals(beforeSaveCount, canvas.getSaveCount());
+
+        assertTrue(beforeMatrix.equals(canvas.getMatrix()));
+
+        Rect afterClip = new Rect();
+
+        assertTrue(canvas.getClipBounds(afterClip));
+        assertEquals(beforeClip, afterClip);
+    }
+
+    public void testPicture() throws Exception {
         Picture picture = new Picture();
         ByteArrayOutputStream bout = new ByteArrayOutputStream();
 
diff --git a/tests/tests/graphics/src/android/graphics/drawable/cts/BitmapDrawableTest.java b/tests/tests/graphics/src/android/graphics/drawable/cts/BitmapDrawableTest.java
index c84510d..1bb6f6d 100644
--- a/tests/tests/graphics/src/android/graphics/drawable/cts/BitmapDrawableTest.java
+++ b/tests/tests/graphics/src/android/graphics/drawable/cts/BitmapDrawableTest.java
@@ -18,19 +18,21 @@
 
 import com.android.cts.stub.R;
 
-
 import org.xmlpull.v1.XmlPullParserException;
 
 import android.content.Context;
+import android.content.res.ColorStateList;
 import android.content.res.Resources;
 import android.content.res.XmlResourceParser;
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
+import android.graphics.Color;
 import android.graphics.ColorFilter;
 import android.graphics.Paint;
 import android.graphics.PixelFormat;
 import android.graphics.Shader;
 import android.graphics.Bitmap.Config;
+import android.graphics.PorterDuff.Mode;
 import android.graphics.Shader.TileMode;
 import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable.ConstantState;
@@ -240,6 +242,18 @@
         assertNull(bitmapDrawable.getPaint().getColorFilter());
     }
 
+    public void testSetTint() {
+        final InputStream source = mContext.getResources().openRawResource(R.raw.testimage);
+        final BitmapDrawable d = new BitmapDrawable(source);
+
+        d.setTint(Color.BLACK);
+        d.setTintMode(Mode.SRC_OVER);
+        assertEquals("Nine-patch is tinted", Color.BLACK, DrawableTestingUtils.getPixel(d, 0, 0));
+
+        d.setTintList(null);
+        d.setTintMode(null);
+    }
+
     public void testGetOpacity() {
         BitmapDrawable bitmapDrawable = new BitmapDrawable();
         assertEquals(Gravity.FILL, bitmapDrawable.getGravity());
@@ -343,6 +357,7 @@
         attrs = DrawableTestUtils.getAttributeSet(
                 mContext.getResources().getXml(R.xml.bitmapdrawable), "bitmap_wrongsrc");
         try {
+            bitmapDrawable = new BitmapDrawable();
             bitmapDrawable.inflate(mContext.getResources(), parser, attrs);
             fail("Should throw XmlPullParserException if the bitmap source can't be decoded.");
         } catch (XmlPullParserException e) {
@@ -351,20 +366,23 @@
         attrs = DrawableTestUtils.getAttributeSet(
                 mContext.getResources().getXml(R.xml.bitmapdrawable), "bitmap_nosrc");
         try {
+            bitmapDrawable = new BitmapDrawable();
             bitmapDrawable.inflate(mContext.getResources(), parser, attrs);
-            fail("Should throw XmlPullParserException if the bitmap src doesn't be defined.");
+            fail("Should throw XmlPullParserException if the bitmap src is not defined.");
         } catch (XmlPullParserException e) {
         }
 
         attrs = DrawableTestUtils.getAttributeSet(
                 mContext.getResources().getXml(R.xml.bitmapdrawable), "bitmap_allattrs");
         try {
+            bitmapDrawable = new BitmapDrawable();
             bitmapDrawable.inflate(null, parser, attrs);
             fail("Should throw NullPointerException if resource is null");
         } catch (NullPointerException e) {
         }
 
         try {
+            bitmapDrawable = new BitmapDrawable();
             bitmapDrawable.inflate(mContext.getResources(), parser, null);
             fail("Should throw NullPointerException if attribute set is null");
         } catch (NullPointerException e) {
diff --git a/tests/tests/graphics/src/android/graphics/drawable/cts/ColorDrawableTest.java b/tests/tests/graphics/src/android/graphics/drawable/cts/ColorDrawableTest.java
index 1267885..18d07ce 100644
--- a/tests/tests/graphics/src/android/graphics/drawable/cts/ColorDrawableTest.java
+++ b/tests/tests/graphics/src/android/graphics/drawable/cts/ColorDrawableTest.java
@@ -16,22 +16,21 @@
 
 package android.graphics.drawable.cts;
 
-import com.android.cts.stub.R;
-
-
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-
-import android.content.res.Resources;
+import android.content.res.ColorStateList;
 import android.content.res.XmlResourceParser;
-import android.graphics.Canvas;
-import android.graphics.ColorFilter;
+import android.graphics.Color;
 import android.graphics.PixelFormat;
+import android.graphics.PorterDuff.Mode;
 import android.graphics.drawable.ColorDrawable;
 import android.test.AndroidTestCase;
 import android.util.AttributeSet;
 import android.util.Xml;
 
+import com.android.cts.stub.R;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
 import java.io.IOException;
 
 public class ColorDrawableTest extends AndroidTestCase {
@@ -120,9 +119,20 @@
     }
 
     public void testSetColorFilter() {
-        final ColorDrawable colorDrawable = new ColorDrawable();
+        final ColorDrawable d = new ColorDrawable(Color.WHITE);
+        assertEquals(Color.WHITE, DrawableTestingUtils.getPixel(d, 0, 0));
 
-        // setColorFilter(ColorFilter) is a non-operation function.
-        colorDrawable.setColorFilter(null);
+        d.setColorFilter(Color.BLACK, Mode.SRC_OVER);
+        assertEquals(Color.BLACK, DrawableTestingUtils.getPixel(d, 0, 0));
+        
+    }
+
+    public void testSetTint() {
+        final ColorDrawable d = new ColorDrawable(Color.WHITE);
+        assertEquals(Color.WHITE, DrawableTestingUtils.getPixel(d, 0, 0));
+
+        d.setTint(Color.BLACK);
+        d.setTintMode(Mode.SRC_OVER);
+        assertEquals(Color.BLACK, DrawableTestingUtils.getPixel(d, 0, 0));
     }
 }
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..036c756 100644
--- a/tests/tests/graphics/src/android/graphics/drawable/cts/DrawableContainerTest.java
+++ b/tests/tests/graphics/src/android/graphics/drawable/cts/DrawableContainerTest.java
@@ -20,9 +20,12 @@
 
 import java.util.Arrays;
 
+import android.content.res.ColorStateList;
 import android.graphics.Canvas;
+import android.graphics.Color;
 import android.graphics.ColorFilter;
 import android.graphics.PixelFormat;
+import android.graphics.PorterDuff.Mode;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.DrawableContainer;
@@ -186,10 +189,10 @@
         assertConstantStateNotSet();
         assertNull(mDrawableContainer.getCurrent());
 
+        mDrawableContainer.setConstantState(mDrawableContainerState);
         mDrawableContainer.setColorFilter(null);
         mDrawableContainer.setColorFilter(new ColorFilter());
 
-        mDrawableContainer.setConstantState(mDrawableContainerState);
         MockDrawable dr = new MockDrawable();
         addAndSelectDrawable(dr);
 
@@ -204,6 +207,25 @@
         assertTrue(dr.hasSetColorFilterCalled());
     }
 
+    public void testSetTint() {
+        assertConstantStateNotSet();
+        assertNull(mDrawableContainer.getCurrent());
+
+        mDrawableContainer.setConstantState(mDrawableContainerState);
+        mDrawableContainer.setTint(Color.BLACK);
+        mDrawableContainer.setTintMode(Mode.SRC_OVER);
+
+        MockDrawable dr = new MockDrawable();
+        addAndSelectDrawable(dr);
+
+        assertEquals("Initial tint propagates", Mode.SRC_OVER, dr.getTintMode());
+
+        dr.reset();
+        mDrawableContainer.setTintList(null);
+        mDrawableContainer.setTintMode(null);
+        assertTrue("setImageTintList() propagates", dr.hasSetTintCalled());
+    }
+
     public void testOnBoundsChange() {
         assertConstantStateNotSet();
         assertNull(mDrawableContainer.getCurrent());
@@ -753,35 +775,29 @@
 
     private class MockDrawable extends Drawable {
         private boolean mHasCalledDraw;
-
         private boolean mHasCalledSetAlpha;
-
         private boolean mHasCalledSetColorFilter;
-
         private boolean mHasCalledSetDither;
-
+        private boolean mHasCalledSetTint;
         private boolean mHasCalledOnBoundsChanged;
-
         private boolean mHasCalledOnStateChanged;
-
         private boolean mHasCalledOnLevelChanged;
-
         private boolean mHasCalledMutate;
 
-        private boolean mIsStatful;
+        private boolean mIsStateful;
 
         private Rect mPadding;
 
         private int mIntrinsicHeight;
-
         private int mIntrinsicWidth;
 
         private int mMinimumHeight;
-
         private int mMinimumWidth;
 
         private int mOpacity;
 
+        private Mode mTintMode;
+
         @Override
         public int getOpacity() {
             return mOpacity;
@@ -789,11 +805,15 @@
 
         @Override
         public boolean isStateful() {
-            return mIsStatful;
+            return mIsStateful;
         }
 
         public void setStateful(boolean isStateful) {
-            mIsStatful = isStateful;
+            mIsStateful = isStateful;
+        }
+
+        public Mode getTintMode() {
+            return mTintMode;
         }
 
         public void setPadding(Rect rect) {
@@ -832,11 +852,18 @@
             return mIntrinsicWidth;
         }
 
+        @Override
         public Drawable mutate() {
             mHasCalledMutate = true;
             return this;
         }
 
+        @Override
+        public void setTintMode(Mode tintMode) {
+            mTintMode = tintMode;
+            mHasCalledSetTint = true;
+        }
+
         public void setMinimumHeight(int h) {
             mMinimumHeight = h;
         }
@@ -873,6 +900,10 @@
             return mHasCalledSetDither;
         }
 
+        public boolean hasSetTintCalled() {
+            return mHasCalledSetTint;
+        }
+
         public boolean hasOnBoundsChangedCalled() {
             return mHasCalledOnBoundsChanged;
         }
diff --git a/tests/tests/graphics/src/android/graphics/drawable/cts/DrawableTestingUtils.java b/tests/tests/graphics/src/android/graphics/drawable/cts/DrawableTestingUtils.java
new file mode 100644
index 0000000..d560906
--- /dev/null
+++ b/tests/tests/graphics/src/android/graphics/drawable/cts/DrawableTestingUtils.java
@@ -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.
+ */
+
+package android.graphics.drawable.cts;
+
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Bitmap.Config;
+import android.graphics.drawable.Drawable;
+
+public class DrawableTestingUtils {
+    public static int getPixel(Drawable d, int x, int y) {
+        final int w = Math.max(d.getIntrinsicWidth(), x + 1);
+        final int h = Math.max(d.getIntrinsicHeight(), y + 1);
+        final Bitmap b = Bitmap.createBitmap(w, h, Config.ARGB_8888);
+        final Canvas c = new Canvas(b);
+        d.setBounds(0, 0, w, h);
+        d.draw(c);
+
+        final int pixel = b.getPixel(x, y);
+        b.recycle();
+        return pixel;
+    }
+}
diff --git a/tests/tests/graphics/src/android/graphics/drawable/cts/GradientDrawableTest.java b/tests/tests/graphics/src/android/graphics/drawable/cts/GradientDrawableTest.java
index f2d7b82..75639c2 100644
--- a/tests/tests/graphics/src/android/graphics/drawable/cts/GradientDrawableTest.java
+++ b/tests/tests/graphics/src/android/graphics/drawable/cts/GradientDrawableTest.java
@@ -18,11 +18,11 @@
 
 import com.android.cts.stub.R;
 
-
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
 
 import android.content.res.Resources;
+import android.content.res.Resources.Theme;
 import android.graphics.Canvas;
 import android.graphics.ColorFilter;
 import android.graphics.PixelFormat;
@@ -248,6 +248,26 @@
         }
     }
 
+    public void testInflateGradientRadius() throws XmlPullParserException, IOException {
+        Rect parentBounds = new Rect(0, 0, 100, 100);
+        Resources resources = mContext.getResources();
+
+        GradientDrawable gradientDrawable;
+        float radius;
+
+        gradientDrawable = (GradientDrawable) resources.getDrawable(
+                R.drawable.gradientdrawable_radius_base);
+        gradientDrawable.setBounds(parentBounds);
+        radius = gradientDrawable.getGradientRadius();
+        assertEquals(25.0f, radius, 0.0f);
+
+        gradientDrawable = (GradientDrawable) resources.getDrawable(
+                R.drawable.gradientdrawable_radius_parent);
+        gradientDrawable.setBounds(parentBounds);
+        radius = gradientDrawable.getGradientRadius();
+        assertEquals(50.0f, radius, 0.0f);
+    }
+
     public void testGetIntrinsicWidth() {
         GradientDrawable gradientDrawable = new GradientDrawable();
         gradientDrawable.setSize(6, 4);
diff --git a/tests/tests/graphics/src/android/graphics/drawable/cts/LayerDrawableTest.java b/tests/tests/graphics/src/android/graphics/drawable/cts/LayerDrawableTest.java
index 4e4648f..86772cc 100644
--- a/tests/tests/graphics/src/android/graphics/drawable/cts/LayerDrawableTest.java
+++ b/tests/tests/graphics/src/android/graphics/drawable/cts/LayerDrawableTest.java
@@ -301,6 +301,7 @@
 
         cb.reset();
         layerDrawable.scheduleDrawable(new BitmapDrawable(), new Runnable() {
+            @Override
             public void run() {
             }
         }, 1000L);
@@ -324,6 +325,7 @@
 
         cb.reset();
         layerDrawable.unscheduleDrawable(new BitmapDrawable(), new Runnable() {
+            @Override
             public void run() {
             }
         });
@@ -340,14 +342,17 @@
         private boolean mCalledSchedule;
         private boolean mCalledUnschedule;
 
+        @Override
         public void invalidateDrawable(Drawable who) {
             mCalledInvalidate = true;
         }
 
+        @Override
         public void scheduleDrawable(Drawable who, Runnable what, long when) {
             mCalledSchedule = true;
         }
 
+        @Override
         public void unscheduleDrawable(Drawable who, Runnable what) {
             mCalledUnschedule = true;
         }
@@ -542,7 +547,7 @@
         LayerDrawable layerDrawable = new LayerDrawable(array);
         assertFalse(layerDrawable.isStateful());
 
-        array = new Drawable[] { new BitmapDrawable(), new MockDrawable() };
+        array = new Drawable[] { new BitmapDrawable(), new MockDrawable(false) };
         layerDrawable = new LayerDrawable(array);
         assertFalse(layerDrawable.isStateful());
 
@@ -552,8 +557,8 @@
     }
 
     public void testOnStateChange() {
-        MockDrawable mockDrawable1 = new MockDrawable();
-        MockDrawable mockDrawable2 = new MockDrawable();
+        MockDrawable mockDrawable1 = new MockDrawable(true);
+        MockDrawable mockDrawable2 = new MockDrawable(true);
         Drawable[] array = new Drawable[] { mockDrawable1, mockDrawable2 };
         MockLayerDrawable layerDrawable = new MockLayerDrawable(array);
 
@@ -730,10 +735,20 @@
 
         private boolean mCalledDraw = false;
 
+        private boolean mIsStateful = false;
+
         private int mOpacity = PixelFormat.OPAQUE;
 
         Rect mPadding = null;
 
+        public MockDrawable() {
+            this(false);
+        }
+
+        public MockDrawable(boolean isStateful) {
+            mIsStateful = isStateful;
+        }
+
         @Override
         public void draw(Canvas canvas) {
             mCalledDraw = true;
@@ -813,10 +828,16 @@
             return true;
         }
 
+        @Override
+        public boolean isStateful() {
+            return mIsStateful;
+        }
+
         public boolean hasCalledSetState() {
             return mCalledSetState;
         }
 
+        @Override
         public boolean setState(final int[] stateSet) {
             increasePadding();
             mCalledSetState = true;
diff --git a/tests/tests/graphics/src/android/graphics/drawable/cts/NinePatchDrawableTest.java b/tests/tests/graphics/src/android/graphics/drawable/cts/NinePatchDrawableTest.java
index b23c7fa..10ca311 100644
--- a/tests/tests/graphics/src/android/graphics/drawable/cts/NinePatchDrawableTest.java
+++ b/tests/tests/graphics/src/android/graphics/drawable/cts/NinePatchDrawableTest.java
@@ -18,11 +18,10 @@
 
 import com.android.cts.stub.R;
 
-import dalvik.annotation.KnownFailure;
-
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
 
+import android.content.res.ColorStateList;
 import android.content.res.Resources;
 import android.content.res.XmlResourceParser;
 import android.graphics.Bitmap;
@@ -36,8 +35,11 @@
 import android.graphics.Rect;
 import android.graphics.Region;
 import android.graphics.Bitmap.Config;
+import android.graphics.PorterDuff.Mode;
 import android.graphics.drawable.NinePatchDrawable;
+import android.graphics.drawable.ShapeDrawable;
 import android.graphics.drawable.Drawable.ConstantState;
+import android.graphics.drawable.shapes.RectShape;
 import android.test.InstrumentationTestCase;
 import android.util.AttributeSet;
 import android.util.Xml;
@@ -180,6 +182,16 @@
         assertNull(mNinePatchDrawable.getPaint().getColorFilter());
     }
 
+    public void testSetTint() {
+        mNinePatchDrawable.setTint(Color.BLACK);
+        mNinePatchDrawable.setTintMode(Mode.SRC_OVER);
+        assertEquals("Nine-patch is tinted", Color.BLACK,
+                DrawableTestingUtils.getPixel(mNinePatchDrawable, 0, 0));
+
+        mNinePatchDrawable.setTintList(null);
+        mNinePatchDrawable.setTintMode(null);
+    }
+
     public void testSetDither() {
         mNinePatchDrawable.setDither(false);
         assertFalse(mNinePatchDrawable.getPaint().isDither());
@@ -239,9 +251,9 @@
         assertEquals(9, mNinePatchDrawable.getMinimumHeight());
     }
 
-    @KnownFailure("Bug 2834281 - Bitmap#hasAlpha seems to return true for "
-        + "images without alpha.")
-    public void testGetOpacity() {
+    // Known failure: Bug 2834281 - Bitmap#hasAlpha seems to return true for
+    // images without alpha
+    public void suppress_testGetOpacity() {
         assertEquals(PixelFormat.OPAQUE, mNinePatchDrawable.getOpacity());
 
         mNinePatchDrawable = getNinePatchDrawable(R.drawable.ninepatch_1);
diff --git a/tests/tests/graphics/src/android/graphics/drawable/cts/ShapeDrawableTest.java b/tests/tests/graphics/src/android/graphics/drawable/cts/ShapeDrawableTest.java
index d92b8cb..3475757 100644
--- a/tests/tests/graphics/src/android/graphics/drawable/cts/ShapeDrawableTest.java
+++ b/tests/tests/graphics/src/android/graphics/drawable/cts/ShapeDrawableTest.java
@@ -16,21 +16,18 @@
 
 package android.graphics.drawable.cts;
 
-import com.android.cts.stub.R;
-
-
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-
+import android.content.res.ColorStateList;
 import android.content.res.Resources;
 import android.graphics.Canvas;
+import android.graphics.Color;
 import android.graphics.ColorFilter;
 import android.graphics.Paint;
 import android.graphics.PixelFormat;
+import android.graphics.PorterDuff.Mode;
 import android.graphics.Rect;
 import android.graphics.Shader;
-import android.graphics.drawable.ShapeDrawable;
 import android.graphics.drawable.Drawable.ConstantState;
+import android.graphics.drawable.ShapeDrawable;
 import android.graphics.drawable.ShapeDrawable.ShaderFactory;
 import android.graphics.drawable.shapes.RectShape;
 import android.graphics.drawable.shapes.Shape;
@@ -38,6 +35,11 @@
 import android.util.AttributeSet;
 import android.util.Xml;
 
+import com.android.cts.stub.R;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
 import java.io.IOException;
 
 public class ShapeDrawableTest extends AndroidTestCase {
@@ -314,6 +316,13 @@
         assertNull(shapeDrawable.getPaint().getColorFilter());
     }
 
+    public void testSetTint() {
+        final ShapeDrawable d = new ShapeDrawable(new RectShape());
+        d.setTint(Color.BLACK);
+        d.setTintMode(Mode.SRC_OVER);
+        assertEquals("Shape is tinted", Color.BLACK, DrawableTestingUtils.getPixel(d, 0, 0));
+    }
+
     public void testSetDither() {
         ShapeDrawable shapeDrawable = new ShapeDrawable();
 
diff --git a/tests/tests/graphics/src/android/graphics/drawable/cts/ThemedDrawableTest.java b/tests/tests/graphics/src/android/graphics/drawable/cts/ThemedDrawableTest.java
new file mode 100644
index 0000000..b56377e
--- /dev/null
+++ b/tests/tests/graphics/src/android/graphics/drawable/cts/ThemedDrawableTest.java
@@ -0,0 +1,149 @@
+/*
+ * 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.graphics.drawable.cts;
+
+import android.annotation.TargetApi;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.graphics.Color;
+import android.graphics.Rect;
+import android.graphics.Shader.TileMode;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.GradientDrawable;
+import android.graphics.drawable.LayerDrawable;
+import android.graphics.drawable.NinePatchDrawable;
+import android.graphics.drawable.RippleDrawable;
+import android.test.AndroidTestCase;
+import android.util.SparseIntArray;
+import android.view.ContextThemeWrapper;
+import android.view.Gravity;
+
+import com.android.cts.stub.R;
+
+@TargetApi(19)
+public class ThemedDrawableTest extends AndroidTestCase {
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+
+        mContext.setTheme(R.style.Theme_ThemedDrawableTest);
+    }
+
+    public void testBitmapDrawable() {
+        BitmapDrawable d = (BitmapDrawable) mContext.getDrawable(R.drawable.bitmapdrawable_theme);
+
+        internalTestBitmapDrawable(d);
+    }
+
+    private void internalTestBitmapDrawable(BitmapDrawable d) {
+        assertEquals(true, d.hasAntiAlias());
+        assertEquals(true, d.isAutoMirrored());
+        // assertEquals(true, d.hasDither());
+        // assertEquals(true, d.hasFilter());
+        assertEquals(Gravity.TOP, d.getGravity());
+        assertEquals(true, d.hasMipMap());
+        assertNotNull(d.getBitmap());
+        assertEquals(TileMode.MIRROR, d.getTileModeX());
+        assertEquals(TileMode.MIRROR, d.getTileModeY());
+    }
+
+    public void testColorDrawable() {
+        ColorDrawable d = (ColorDrawable) mContext.getDrawable(R.drawable.colordrawable_theme);
+
+        assertEquals(Color.BLACK, d.getColor());
+    }
+
+    public void testGradientDrawable() {
+        GradientDrawable d = (GradientDrawable) mContext.getDrawable(
+                R.drawable.gradientdrawable_theme);
+
+        // Corners
+        // assertEquals(1, d.getCornerRadius(0));
+        // assertEquals(1, d.getCornerRadius(1));
+        // assertEquals(1, d.getCornerRadius(2));
+        // assertEquals(1, d.getCornerRadius(3));
+
+        // Gradient
+        // int[] colors = d.getColors(null);
+        // for (int i = 0; i < color.length; i++) {
+        // assertEquals(Color.BLACK, colors[i]);
+        // }
+        // assertEquals(1.0f, d.getGradientAngle());
+        // assertEquals(1.0, d.getGradientCenterX());
+        // assertEquals(1.0, d.getGradientCenterY());
+        // assertEquals(1.0, d.getGradientRadius());
+        // assertEquals(false, d.getUseLevel());
+
+        // Padding
+        Rect padding = new Rect();
+        assertTrue(d.getPadding(padding));
+        assertEquals(1, padding.left);
+        assertEquals(1, padding.top);
+        assertEquals(1, padding.bottom);
+        assertEquals(1, padding.right);
+
+        // Size
+        assertEquals(1, d.getIntrinsicHeight());
+        assertEquals(1, d.getIntrinsicWidth());
+
+        // Solid
+        // assertEquals(true, d.hasSolidColor());
+        // assertEquals(Color.BLACK, d.getColor());
+
+        // Stroke
+        // assertEquals(1.0, d.getStrokeWidth());
+        // assertEquals(Color.BLACK, d.getStrokeColor());
+        // assertEquals(1.0, d.getStrokeDashWidth());
+        // assertEquals(1.0, d.getStrokeDashGap());
+    }
+
+    public void testNinePatchDrawable() {
+        NinePatchDrawable d = (NinePatchDrawable) mContext.getDrawable(
+                R.drawable.ninepatchdrawable_theme);
+
+        internalTestNinePatchDrawable(d);
+    }
+
+    private void internalTestNinePatchDrawable(NinePatchDrawable d) {
+        assertEquals(true, d.isAutoMirrored());
+        // assertEquals(true, d.hasDither());
+        // assertNotNull(d.getNinePatch());
+    }
+
+    public void testRippleDrawable() {
+        RippleDrawable d = (RippleDrawable) mContext.getDrawable(
+                R.drawable.rippledrawable_theme);
+
+        // assertEquals(Color.BLACK, d.getColor());
+    }
+
+    public void testLayerDrawable() {
+        LayerDrawable d = (LayerDrawable) mContext.getDrawable(R.drawable.layerdrawable_theme);
+
+        // Layer autoMirror values are set to the parent's autoMirror value, so
+        // make sure the container is using the expected value.
+        assertEquals(true, d.isAutoMirrored());
+
+        BitmapDrawable bitmapDrawable  = (BitmapDrawable) d.getDrawable(0);
+        internalTestBitmapDrawable(bitmapDrawable);
+
+        NinePatchDrawable ninePatchDrawable = (NinePatchDrawable) d.getDrawable(1);
+        internalTestNinePatchDrawable(ninePatchDrawable);
+    }
+}
diff --git a/tests/tests/graphics/src/android/graphics/drawable/cts/VectorDrawableTest.java b/tests/tests/graphics/src/android/graphics/drawable/cts/VectorDrawableTest.java
new file mode 100644
index 0000000..76ff73d
--- /dev/null
+++ b/tests/tests/graphics/src/android/graphics/drawable/cts/VectorDrawableTest.java
@@ -0,0 +1,286 @@
+/*
+ * 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.graphics.drawable.cts;
+
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.drawable.VectorDrawable;
+import android.graphics.drawable.Drawable.ConstantState;
+import android.test.AndroidTestCase;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.util.Xml;
+
+import com.android.cts.stub.R;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+
+public class VectorDrawableTest extends AndroidTestCase {
+    private static final String LOGTAG = VectorDrawableTest.class.getSimpleName();
+    private int[] mIconResIds = new int[] {
+            R.drawable.vector_icon_create,
+            R.drawable.vector_icon_delete,
+            R.drawable.vector_icon_heart,
+            R.drawable.vector_icon_schedule,
+            R.drawable.vector_icon_settings,
+            R.drawable.vector_icon_random_path_1,
+            R.drawable.vector_icon_random_path_2,
+            R.drawable.vector_icon_repeated_cq,
+            R.drawable.vector_icon_repeated_st,
+            R.drawable.vector_icon_repeated_a_1,
+            R.drawable.vector_icon_repeated_a_2,
+            R.drawable.vector_icon_clip_path_1,
+            R.drawable.vector_icon_transformation_1,
+            R.drawable.vector_icon_transformation_2,
+            R.drawable.vector_icon_transformation_3,
+            R.drawable.vector_icon_transformation_4,
+            R.drawable.vector_icon_transformation_5,
+            R.drawable.vector_icon_transformation_6,
+            R.drawable.vector_icon_render_order_1,
+            R.drawable.vector_icon_render_order_2,
+            R.drawable.vector_icon_stroke_1,
+            R.drawable.vector_icon_stroke_2,
+            R.drawable.vector_icon_stroke_3,
+    };
+
+    private int[] mGoldenImages = new int[] {
+            R.drawable.vector_icon_create_golden,
+            R.drawable.vector_icon_delete_golden,
+            R.drawable.vector_icon_heart_golden,
+            R.drawable.vector_icon_schedule_golden,
+            R.drawable.vector_icon_settings_golden,
+            R.drawable.vector_icon_random_path_1_golden,
+            R.drawable.vector_icon_random_path_2_golden,
+            R.drawable.vector_icon_repeated_cq_golden,
+            R.drawable.vector_icon_repeated_st_golden,
+            R.drawable.vector_icon_repeated_a_1_golden,
+            R.drawable.vector_icon_repeated_a_2_golden,
+            R.drawable.vector_icon_clip_path_1_golden,
+            R.drawable.vector_icon_transformation_1_golden,
+            R.drawable.vector_icon_transformation_2_golden,
+            R.drawable.vector_icon_transformation_3_golden,
+            R.drawable.vector_icon_transformation_4_golden,
+            R.drawable.vector_icon_transformation_5_golden,
+            R.drawable.vector_icon_transformation_6_golden,
+            R.drawable.vector_icon_render_order_1_golden,
+            R.drawable.vector_icon_render_order_2_golden,
+            R.drawable.vector_icon_stroke_1_golden,
+            R.drawable.vector_icon_stroke_2_golden,
+            R.drawable.vector_icon_stroke_3_golden,
+    };
+
+    private static final int IMAGE_WIDTH = 64;
+    private static final int IMAGE_HEIGHT = 64;
+    // A small value is actually making sure that the values are matching
+    // exactly with the golden image.
+    // We can increase the threshold if the Skia is drawing with some variance
+    // on different devices. So far, the tests show they are matching correctly.
+    private static final float PIXEL_ERROR_THRESHOLD = 0.00001f;
+
+    private static final boolean DBG_DUMP_PNG = false;
+
+    private Resources mResources;
+    private VectorDrawable mVectorDrawable;
+    private Bitmap mBitmap;
+    private Canvas mCanvas;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        final int width = IMAGE_WIDTH;
+        final int height = IMAGE_HEIGHT;
+
+        mBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
+        mCanvas = new Canvas(mBitmap);
+        mVectorDrawable = new VectorDrawable();
+        mVectorDrawable.setBounds(0, 0, width, height);
+
+        mResources = mContext.getResources();
+    }
+
+    public void testSimpleVectorDrawables() throws Exception {
+        verifyVectorDrawables(mIconResIds, mGoldenImages, 0);
+    }
+
+    private void verifyVectorDrawables(int[] resIds, int[] goldenImages, float fraction) throws Exception {
+        for (int i = 0; i < resIds.length; i++) {
+            // Setup VectorDrawable from xml file and draw into the bitmap.
+            XmlPullParser parser = mResources.getXml(resIds[i]);
+            AttributeSet attrs = Xml.asAttributeSet(parser);
+
+            int type;
+            while ((type=parser.next()) != XmlPullParser.START_TAG &&
+                    type != XmlPullParser.END_DOCUMENT) {
+                // Empty loop
+            }
+
+            if (type != XmlPullParser.START_TAG) {
+                throw new XmlPullParserException("No start tag found");
+            }
+
+            mVectorDrawable.inflate(mResources, parser, attrs);
+
+            mBitmap.eraseColor(0);
+            mVectorDrawable.draw(mCanvas);
+
+            if (DBG_DUMP_PNG) {
+                saveVectorDrawableIntoPNG(mBitmap, resIds, i);
+            } else {
+                // Start to compare
+                Bitmap golden = BitmapFactory.decodeResource(mResources, goldenImages[i]);
+                compareImages(mBitmap, golden, mResources.getString(resIds[i]));
+            }
+        }
+    }
+
+    // This is only for debugging or golden image (re)generation purpose.
+    private void saveVectorDrawableIntoPNG(Bitmap bitmap, int[] resIds, int index) throws IOException {
+        // Save the image to the disk.
+        FileOutputStream out = null;
+        try {
+            String outputFolder = "/sdcard/temp/";
+            File folder = new File(outputFolder);
+            if (!folder.exists()) {
+                folder.mkdir();
+            }
+            String originalFilePath = mResources.getString(resIds[index]);
+            File originalFile = new File(originalFilePath);
+            String fileFullName = originalFile.getName();
+            String fileTitle = fileFullName.substring(0, fileFullName.lastIndexOf("."));
+            String outputFilename = outputFolder + fileTitle + "_golden.png";
+            File outputFile = new File(outputFilename);
+            if (!outputFile.exists()) {
+                outputFile.createNewFile();
+            }
+
+            out = new FileOutputStream(outputFile, false);
+            bitmap.compress(Bitmap.CompressFormat.PNG, 100, out);
+            Log.v(LOGTAG, "Write test No." + index + " to file successfully.");
+        } catch (Exception e) {
+            e.printStackTrace();
+        } finally {
+            if (out != null) {
+                out.close();
+            }
+        }
+    }
+
+    private void compareImages(Bitmap ideal, Bitmap given, String filename) {
+        int idealWidth = ideal.getWidth();
+        int idealHeight = ideal.getHeight();
+
+        assertTrue(idealWidth == given.getWidth());
+        assertTrue(idealHeight == given.getHeight());
+
+        int totalDiffPixelCount = 0;
+        float totalPixelCount = idealWidth * idealHeight;
+        for (int x = 0; x < idealWidth; x++) {
+            for (int y = 0; y < idealHeight; y++) {
+                int idealColor = ideal.getPixel(x, y);
+                int givenColor = given.getPixel(x, y);
+                if (idealColor == givenColor)
+                    continue;
+
+                float totalError = 0;
+                totalError += Math.abs(Color.red(idealColor) - Color.red(givenColor));
+                totalError += Math.abs(Color.green(idealColor) - Color.green(givenColor));
+                totalError += Math.abs(Color.blue(idealColor) - Color.blue(givenColor));
+                totalError += Math.abs(Color.alpha(idealColor) - Color.alpha(givenColor));
+
+                if ((totalError / 1024.0f) >= PIXEL_ERROR_THRESHOLD) {
+                    fail((filename + ": totalError is " + totalError));
+                }
+
+                totalDiffPixelCount++;
+            }
+        }
+        if ((totalDiffPixelCount / totalPixelCount) >= PIXEL_ERROR_THRESHOLD) {
+            fail((filename +": totalDiffPixelCount is " + totalDiffPixelCount));
+        }
+
+    }
+
+    public void testGetChangingConfigurations() {
+        VectorDrawable vectorDrawable = new VectorDrawable();
+        ConstantState constantState = vectorDrawable.getConstantState();
+
+        // default
+        assertEquals(0, constantState.getChangingConfigurations());
+        assertEquals(0, vectorDrawable.getChangingConfigurations());
+
+        // change the drawable's configuration does not affect the state's configuration
+        vectorDrawable.setChangingConfigurations(0xff);
+        assertEquals(0xff, vectorDrawable.getChangingConfigurations());
+        assertEquals(0, constantState.getChangingConfigurations());
+
+        // the state's configuration get refreshed
+        constantState = vectorDrawable.getConstantState();
+        assertEquals(0xff,  constantState.getChangingConfigurations());
+
+        // set a new configuration to drawable
+        vectorDrawable.setChangingConfigurations(0xff00);
+        assertEquals(0xff,  constantState.getChangingConfigurations());
+        assertEquals(0xffff,  vectorDrawable.getChangingConfigurations());
+    }
+
+    public void testGetConstantState() {
+        VectorDrawable vectorDrawable = new VectorDrawable();
+        ConstantState constantState = vectorDrawable.getConstantState();
+        assertNotNull(constantState);
+        assertEquals(0, constantState.getChangingConfigurations());
+
+        vectorDrawable.setChangingConfigurations(1);
+        constantState = vectorDrawable.getConstantState();
+        assertNotNull(constantState);
+        assertEquals(1, constantState.getChangingConfigurations());
+    }
+
+    public void testMutate() {
+        Resources resources = mContext.getResources();
+        // d1 will be mutated, while d2 / d3 will not.
+        VectorDrawable d1 = (VectorDrawable) resources.getDrawable(R.drawable.vector_icon_create);
+        VectorDrawable d2 = (VectorDrawable) resources.getDrawable(R.drawable.vector_icon_create);
+        VectorDrawable d3 = (VectorDrawable) resources.getDrawable(R.drawable.vector_icon_create);
+        int originalAlpha = d2.getAlpha();
+
+        d1.setAlpha(0x80);
+        assertEquals(0x80, d1.getAlpha());
+        assertEquals(0x80, d2.getAlpha());
+        assertEquals(0x80, d3.getAlpha());
+
+        d1.mutate();
+        d1.setAlpha(0x40);
+        assertEquals(0x40, d1.getAlpha());
+        assertEquals(0x80, d2.getAlpha());
+        assertEquals(0x80, d3.getAlpha());
+
+        d2.setAlpha(0x20);
+        assertEquals(0x40, d1.getAlpha());
+        assertEquals(0x20, d2.getAlpha());
+        assertEquals(0x20, d3.getAlpha());
+
+        d2.setAlpha(originalAlpha);
+    }
+}
diff --git a/tests/tests/graphics/src/android/opengl/cts/OpenGlEsVersionTest.java b/tests/tests/graphics/src/android/opengl/cts/OpenGlEsVersionTest.java
index 85159a8..e8ab59c 100644
--- a/tests/tests/graphics/src/android/opengl/cts/OpenGlEsVersionTest.java
+++ b/tests/tests/graphics/src/android/opengl/cts/OpenGlEsVersionTest.java
@@ -58,40 +58,89 @@
     }
 
     public void testOpenGlEsVersion() throws InterruptedException {
-        int detectedVersion = getDetectedVersion();
+        int detectedMajorVersion = getDetectedMajorVersion();
         int reportedVersion = getVersionFromActivityManager(mActivity);
 
-        assertEquals("Detected OpenGL ES major version " + detectedVersion
-                + " but Activity Manager is reporting " +  reportedVersion
-                + " (Check ro.opengles.version)", detectedVersion, reportedVersion);
+        assertEquals("Detected OpenGL ES major version " + detectedMajorVersion
+                + " but Activity Manager is reporting " +  getMajorVersion(reportedVersion)
+                + " (Check ro.opengles.version)",
+                detectedMajorVersion, getMajorVersion(reportedVersion));
         assertEquals("Reported OpenGL ES version from ActivityManager differs from PackageManager",
                 reportedVersion, getVersionFromPackageManager(mActivity));
 
         assertGlVersionString(1);
-        if (detectedVersion == 2) {
+        if (detectedMajorVersion == 2) {
             restartActivityWithClientVersion(2);
             assertGlVersionString(2);
-        } else if (detectedVersion == 3) {
+        } else if (detectedMajorVersion == 3) {
             restartActivityWithClientVersion(3);
             assertGlVersionString(3);
         }
     }
 
-    private static boolean hasExtension(String extensions, String name) {
-        int start = extensions.indexOf(name);
-        while (start >= 0) {
-            // check that we didn't find a prefix of a longer extension name
-            int end = start + name.length();
-            if (end == extensions.length() || extensions.charAt(end) == ' ') {
-                return true;
-            }
-            start = extensions.indexOf(name, end);
+    public void testRequiredExtensions() throws InterruptedException {
+        int reportedVersion = getVersionFromActivityManager(mActivity);
+        // We only have required extensions on ES3.1
+        if (getMajorVersion(reportedVersion) != 3 || getMinorVersion(reportedVersion) != 1)
+            return;
+
+        restartActivityWithClientVersion(3);
+
+        String extensions = mActivity.getExtensionsString();
+        final String requiredList[] = {
+            "EXT_texture_sRGB_decode",
+            "KHR_blend_equation_advanced",
+            "KHR_debug",
+            "OES_shader_image_atomic",
+            "OES_texture_stencil8",
+            "OES_texture_storage_multisample_2d_array"
+        };
+
+        for (int i = 0; i < requiredList.length; ++i) {
+            assertTrue("OpenGL ES version 3.1 is missing extension " + requiredList[i],
+                    hasExtension(extensions, requiredList[i]));
         }
-        return false;
+    }
+
+    public void testExtensionPack() throws InterruptedException {
+        // Requirements:
+        // 1. If the device claims support for the system feature, the extension must be available.
+        // 2. If the extension is available, it must be correct:
+        //    - ES 3.1 must be supported
+        //    - All included extensions must be available
+        //
+        // Supporting the extension but not claiming support for the system feature is allowed,
+        // just like the ES context version can be higher than the ro.opengles.version property.
+
+        int reportedVersion = getVersionFromActivityManager(mActivity);
+        boolean hasAepFeature = mActivity.getPackageManager().hasSystemFeature(
+                PackageManager.FEATURE_OPENGLES_EXTENSION_PACK);
+
+        if (getMajorVersion(reportedVersion) != 3 || getMinorVersion(reportedVersion) != 1) {
+            assertFalse("FEATURE_OPENGLES_EXTENSION_PACK is available without OpenGL ES 3.1",
+                    hasAepFeature);
+            return;
+        }
+
+        restartActivityWithClientVersion(3);
+
+        String extensions = mActivity.getExtensionsString();
+        if (!hasExtension(extensions, "ANDROID_extension_pack_es31a")) {
+            assertFalse("FEATURE_OPENGLES_EXTENSION_PACK is available but ANDROID_extension_pack_es31a isn't in the extension list",
+                    hasAepFeature);
+            return;
+        }
+
+        assertTrue("ANDROID_extension_pack_es31a is present, but support is incomplete",
+                mActivity.getAepEs31Support());
+    }
+
+    private static boolean hasExtension(String extensions, String name) {
+        return OpenGlEsVersionStubActivity.hasExtension(extensions, name);
     }
 
     /** @return OpenGL ES major version 1, 2, or 3 or some non-positive number for error */
-    private static int getDetectedVersion() {
+    private static int getDetectedMajorVersion() {
         /*
          * Get all the device configurations and check the EGL_RENDERABLE_TYPE attribute
          * to determine the highest ES version supported by any config. The
@@ -156,9 +205,9 @@
             (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
         ConfigurationInfo configInfo = activityManager.getDeviceConfigurationInfo();
         if (configInfo.reqGlEsVersion != ConfigurationInfo.GL_ES_VERSION_UNDEFINED) {
-            return getMajorVersion(configInfo.reqGlEsVersion);
+            return configInfo.reqGlEsVersion;
         } else {
-            return 1; // Lack of property means OpenGL ES version 1
+            return 1 << 16; // Lack of property means OpenGL ES version 1
         }
     }
 
@@ -170,9 +219,9 @@
                 // Null feature name means this feature is the open gl es version feature.
                 if (featureInfo.name == null) {
                     if (featureInfo.reqGlEsVersion != FeatureInfo.GL_ES_VERSION_UNDEFINED) {
-                        return getMajorVersion(featureInfo.reqGlEsVersion);
+                        return featureInfo.reqGlEsVersion;
                     } else {
-                        return 1; // Lack of property means OpenGL ES version 1
+                        return 1 << 16; // Lack of property means OpenGL ES version 1
                     }
                 }
             }
@@ -185,6 +234,11 @@
         return ((glEsVersion & 0xffff0000) >> 16);
     }
 
+    /** @see FeatureInfo#getGlEsVersion() */
+    private static int getMinorVersion(int glEsVersion) {
+        return glEsVersion & 0xffff;
+    }
+
     /**
      * Check that the version string has some form of "Open GL ES X.Y" in it where X is the major
      * version and Y must be some digit.
diff --git a/tests/tests/graphics2/Android.mk b/tests/tests/graphics2/Android.mk
index b3e7340..a3cdafa 100644
--- a/tests/tests/graphics2/Android.mk
+++ b/tests/tests/graphics2/Android.mk
@@ -21,8 +21,6 @@
 # and when built explicitly put it in the data partition
 LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
 
-LOCAL_JAVA_LIBRARIES := android.test.runner
-
 LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
diff --git a/tests/tests/graphics2/AndroidManifest.xml b/tests/tests/graphics2/AndroidManifest.xml
index 2392100..67557ad 100644
--- a/tests/tests/graphics2/AndroidManifest.xml
+++ b/tests/tests/graphics2/AndroidManifest.xml
@@ -24,7 +24,10 @@
 
     <instrumentation
         android:targetPackage="com.android.cts.graphics2"
-        android:name="android.test.InstrumentationCtsTestRunner" />
+        android:name="android.support.test.runner.AndroidJUnitRunner" >
+        <meta-data android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
 
     <application>
         <uses-library android:name="android.test.runner" />
diff --git a/tests/tests/hardware/Android.mk b/tests/tests/hardware/Android.mk
index 68684b5..12c2b77 100644
--- a/tests/tests/hardware/Android.mk
+++ b/tests/tests/hardware/Android.mk
@@ -38,8 +38,6 @@
 
 LOCAL_MODULE_TAGS := tests
 
-LOCAL_JAVA_LIBRARIES := android.test.runner
-
 LOCAL_STATIC_JAVA_LIBRARIES := ctsdeviceutil ctstestrunner mockito-target android-ex-camera2
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
diff --git a/tests/tests/hardware/AndroidManifest.xml b/tests/tests/hardware/AndroidManifest.xml
index 971d6c7..11ca9c0 100644
--- a/tests/tests/hardware/AndroidManifest.xml
+++ b/tests/tests/hardware/AndroidManifest.xml
@@ -23,9 +23,12 @@
         <uses-library android:name="android.test.runner" />
     </application>
 
-    <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
                      android:targetPackage="com.android.cts.stub"
-                     android:label="CTS tests of android.hardware"/>
+                     android:label="CTS tests of android.hardware">
+        <meta-data android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
 
 </manifest>
 
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/AllocationTest.java b/tests/tests/hardware/src/android/hardware/camera2/cts/AllocationTest.java
new file mode 100644
index 0000000..fb23a5f
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/AllocationTest.java
@@ -0,0 +1,860 @@
+/*
+ * 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 static android.graphics.ImageFormat.YUV_420_888;
+import static android.hardware.camera2.cts.helpers.Preconditions.*;
+import static android.hardware.camera2.cts.helpers.AssertHelpers.*;
+import static android.hardware.camera2.cts.CameraTestUtils.*;
+import static com.android.ex.camera2.blocking.BlockingStateListener.*;
+
+import android.content.Context;
+import android.graphics.ImageFormat;
+import android.graphics.RectF;
+import android.hardware.camera2.CameraAccessException;
+import android.hardware.camera2.CameraCaptureSession;
+import android.hardware.camera2.CameraCharacteristics;
+import android.hardware.camera2.CameraDevice;
+import android.hardware.camera2.CameraManager;
+import android.hardware.camera2.CameraMetadata;
+import android.hardware.camera2.CaptureRequest;
+import android.hardware.camera2.CaptureResult;
+import android.hardware.camera2.TotalCaptureResult;
+import android.hardware.camera2.params.ColorSpaceTransform;
+import android.hardware.camera2.params.RggbChannelVector;
+import android.hardware.camera2.params.StreamConfigurationMap;
+import android.util.Size;
+import android.hardware.camera2.cts.helpers.MaybeNull;
+import android.hardware.camera2.cts.helpers.StaticMetadata;
+import android.hardware.camera2.cts.rs.RenderScriptSingleton;
+import android.hardware.camera2.cts.rs.ScriptGraph;
+import android.hardware.camera2.cts.rs.ScriptYuvCrop;
+import android.hardware.camera2.cts.rs.ScriptYuvMeans1d;
+import android.hardware.camera2.cts.rs.ScriptYuvMeans2dTo1d;
+import android.hardware.camera2.cts.rs.ScriptYuvToRgb;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.renderscript.Allocation;
+import android.renderscript.Script.LaunchOptions;
+import android.test.AndroidTestCase;
+import android.util.Log;
+import android.util.Rational;
+import android.view.Surface;
+
+import com.android.ex.camera2.blocking.BlockingCameraManager.BlockingOpenException;
+import com.android.ex.camera2.blocking.BlockingStateListener;
+import com.android.ex.camera2.blocking.BlockingSessionListener;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Suite of tests for camera2 -> RenderScript APIs.
+ *
+ * <p>It uses CameraDevice as producer, camera sends the data to the surface provided by
+ * Allocation. Only the below format is tested:</p>
+ *
+ * <p>YUV_420_888: flexible YUV420, it is a mandatory format for camera.</p>
+ */
+public class AllocationTest extends AndroidTestCase {
+    private static final String TAG = "AllocationTest";
+    private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
+
+    private CameraManager mCameraManager;
+    private CameraDevice mCamera;
+    private CameraCaptureSession mSession;
+    private BlockingStateListener mCameraListener;
+    private BlockingSessionListener mSessionListener;
+
+    private String[] mCameraIds;
+
+    private Handler mHandler;
+    private HandlerThread mHandlerThread;
+
+    private CameraIterable mCameraIterable;
+    private SizeIterable mSizeIterable;
+    private ResultIterable mResultIterable;
+
+    @Override
+    public synchronized void setContext(Context context) {
+        super.setContext(context);
+        mCameraManager = (CameraManager) context.getSystemService(Context.CAMERA_SERVICE);
+        assertNotNull("Can't connect to camera manager!", mCameraManager);
+
+        RenderScriptSingleton.setContext(context);
+        // TODO: call clearContext
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mCameraIds = mCameraManager.getCameraIdList();
+        mHandlerThread = new HandlerThread("AllocationTest");
+        mHandlerThread.start();
+        mHandler = new Handler(mHandlerThread.getLooper());
+        mCameraListener = new BlockingStateListener();
+
+        mCameraIterable = new CameraIterable();
+        mSizeIterable = new SizeIterable();
+        mResultIterable = new ResultIterable();
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        MaybeNull.close(mCamera);
+
+        // TODO: Clean up RenderScript context in a static test run finished method.
+        // Or alternatively count the # of test methods that are in this test,
+        // once we reach that count, it's time to call the last tear down
+
+        mHandlerThread.quitSafely();
+        mHandler = null;
+        super.tearDown();
+    }
+
+    /**
+     * Update the request with a default manual request template.
+     *
+     * @param request A builder for a CaptureRequest
+     * @param sensitivity ISO gain units (e.g. 100)
+     * @param expTimeNs Exposure time in nanoseconds
+     */
+    private static void setManualCaptureRequest(CaptureRequest.Builder request, int sensitivity,
+            long expTimeNs) {
+        final Rational ONE = new Rational(1, 1);
+        final Rational ZERO = new Rational(0, 1);
+
+        if (VERBOSE) {
+            Log.v(TAG, String.format("Create manual capture request, sensitivity = %d, expTime = %f",
+                    sensitivity, expTimeNs / (1000.0 * 1000)));
+        }
+
+        request.set(CaptureRequest.CONTROL_MODE, CaptureRequest.CONTROL_MODE_OFF);
+        request.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_OFF);
+        request.set(CaptureRequest.CONTROL_AWB_MODE, CaptureRequest.CONTROL_AWB_MODE_OFF);
+        request.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_OFF);
+        request.set(CaptureRequest.CONTROL_EFFECT_MODE, CaptureRequest.CONTROL_EFFECT_MODE_OFF);
+        request.set(CaptureRequest.SENSOR_FRAME_DURATION, 0L);
+        request.set(CaptureRequest.SENSOR_SENSITIVITY, sensitivity);
+        request.set(CaptureRequest.SENSOR_EXPOSURE_TIME, expTimeNs);
+        request.set(CaptureRequest.COLOR_CORRECTION_MODE,
+                CaptureRequest.COLOR_CORRECTION_MODE_TRANSFORM_MATRIX);
+
+        // Identity transform
+        request.set(CaptureRequest.COLOR_CORRECTION_TRANSFORM,
+            new ColorSpaceTransform(new Rational[] {
+                ONE, ZERO, ZERO,
+                ZERO, ONE, ZERO,
+                ZERO, ZERO, ONE
+            }));
+
+        // Identity gains
+        request.set(CaptureRequest.COLOR_CORRECTION_GAINS,
+                new RggbChannelVector(1.0f, 1.0f, 1.0f, 1.0f ));
+        request.set(CaptureRequest.TONEMAP_MODE, CaptureRequest.TONEMAP_MODE_FAST);
+    }
+
+    /**
+     * Calculate the absolute crop window from a {@link Size},
+     * and configure {@link LaunchOptions} for it.
+     */
+    // TODO: split patch crop window and the application against a particular size into 2 classes
+    public static class Patch {
+        /**
+         * Create a new {@link Patch} from relative crop coordinates.
+         *
+         * <p>All float values must be normalized coordinates between [0, 1].</p>
+         *
+         * @param size Size of the original rectangle that is being cropped.
+         * @param xNorm The X coordinate defining the left side of the rectangle (in [0, 1]).
+         * @param yNorm The Y coordinate defining the top side of the rectangle (in [0, 1]).
+         * @param wNorm The width of the crop rectangle (normalized between [0, 1]).
+         * @param hNorm The height of the crop rectangle (normalized between [0, 1]).
+         *
+         * @throws NullPointerException if size was {@code null}.
+         * @throws AssertionError if any of the normalized coordinates were out of range
+         */
+        public Patch(Size size, float xNorm, float yNorm, float wNorm, float hNorm) {
+            checkNotNull("size", size);
+
+            assertInRange(xNorm, 0.0f, 1.0f);
+            assertInRange(yNorm, 0.0f, 1.0f);
+            assertInRange(wNorm, 0.0f, 1.0f);
+            assertInRange(hNorm, 0.0f, 1.0f);
+
+            wFull = size.getWidth();
+            hFull = size.getWidth();
+
+            xTile = (int)Math.ceil(xNorm * wFull);
+            yTile = (int)Math.ceil(yNorm * hFull);
+
+            wTile = (int)Math.ceil(wNorm * wFull);
+            hTile = (int)Math.ceil(hNorm * hFull);
+
+            mSourceSize = size;
+        }
+
+        /**
+         * Get the original size used to create this {@link Patch}.
+         *
+         * @return source size
+         */
+        public Size getSourceSize() {
+            return mSourceSize;
+        }
+
+        /**
+         * Get the cropped size after applying the normalized crop window.
+         *
+         * @return cropped size
+         */
+        public Size getSize() {
+            return new Size(wFull, hFull);
+        }
+
+        /**
+         * Get the {@link LaunchOptions} that can be used with a {@link android.renderscript.Script}
+         * to apply a kernel over a subset of an {@link Allocation}.
+         *
+         * @return launch options
+         */
+        public LaunchOptions getLaunchOptions() {
+            return (new LaunchOptions())
+                    .setX(xTile, xTile + wTile)
+                    .setY(yTile, yTile + hTile);
+        }
+
+        /**
+         * Get the cropped width after applying the normalized crop window.
+         *
+         * @return cropped width
+         */
+        public int getWidth() {
+            return wTile;
+        }
+
+        /**
+         * Get the cropped height after applying the normalized crop window.
+         *
+         * @return cropped height
+         */
+        public int getHeight() {
+            return hTile;
+        }
+
+        /**
+         * Convert to a {@link RectF} where each corner is represented by a
+         * normalized coordinate in between [0.0, 1.0] inclusive.
+         *
+         * @return a new rectangle
+         */
+        public RectF toRectF() {
+            return new RectF(
+                    xTile * 1.0f / wFull,
+                    yTile * 1.0f / hFull,
+                    (xTile + wTile) * 1.0f / wFull,
+                    (yTile + hTile) * 1.0f / hFull);
+        }
+
+        private final Size mSourceSize;
+        private final int wFull;
+        private final int hFull;
+        private final int xTile;
+        private final int yTile;
+        private final int wTile;
+        private final int hTile;
+    }
+
+    /**
+     * Convert a single YUV pixel (3 byte elements) to an RGB pixel.
+     *
+     * <p>The color channels must be in the following order:
+     * <ul><li>Y - 0th channel
+     * <li>U - 1st channel
+     * <li>V - 2nd channel
+     * </ul></p>
+     *
+     * <p>Each channel has data in the range 0-255.</p>
+     *
+     * <p>Output data is a 3-element pixel with each channel in the range of [0,1].
+     * Each channel is saturated to avoid over/underflow.</p>
+     *
+     * <p>The conversion is done using JFIF File Interchange Format's "Conversion to and from RGB":
+     * <ul>
+     * <li>R = Y + 1.042 (Cr - 128)
+     * <li>G = Y - 0.34414 (Cb - 128) - 0.71414 (Cr - 128)
+     * <li>B = Y + 1.772 (Cb - 128)
+     * </ul>
+     *
+     * Where Cr and Cb are aliases of V and U respectively.
+     * </p>
+     *
+     * @param yuvData An array of a YUV pixel (at least 3 bytes large)
+     *
+     * @return an RGB888 pixel with each channel in the range of [0,1]
+     */
+    private static float[] convertPixelYuvToRgb(byte[] yuvData) {
+        final int CHANNELS = 3; // yuv
+        final float COLOR_RANGE = 256f;
+
+        assertTrue("YUV pixel must be at least 3 bytes large", CHANNELS <= yuvData.length);
+
+        float[] rgb = new float[CHANNELS];
+
+        float y = yuvData[0] & 0xFF;  // Y channel
+        float cb = yuvData[1] & 0xFF; // U channel
+        float cr = yuvData[2] & 0xFF; // V channel
+
+        // convert YUV -> RGB (from JFIF's "Conversion to and from RGB" section)
+        float r = y + 1.402f * (cr - 128);
+        float g = y - 0.34414f * (cb - 128) - 0.71414f * (cr - 128);
+        float b = y + 1.772f * (cb - 128);
+
+        // normalize [0,255] -> [0,1]
+        rgb[0] = r / COLOR_RANGE;
+        rgb[1] = g / COLOR_RANGE;
+        rgb[2] = b / COLOR_RANGE;
+
+        // Clamp to range [0,1]
+        for (int i = 0; i < CHANNELS; ++i) {
+            rgb[i] = Math.max(0.0f, Math.min(1.0f, rgb[i]));
+        }
+
+        if (VERBOSE) {
+            Log.v(TAG, String.format("RGB calculated (r,g,b) = (%f, %f, %f)", rgb[0], rgb[1],
+                    rgb[2]));
+        }
+
+        return rgb;
+    }
+
+    /**
+     * Configure the camera with the target surface;
+     * create a capture request builder with {@code cameraTarget} as the sole surface target.
+     *
+     * <p>Outputs are configured with the new surface targets, and this function blocks until
+     * the camera has finished configuring.</p>
+     *
+     * <p>The capture request is created from the {@link CameraDevice#TEMPLATE_PREVIEW} template.
+     * No other keys are set.
+     * </p>
+     */
+    private CaptureRequest.Builder configureAndCreateRequestForSurface(Surface cameraTarget)
+            throws CameraAccessException {
+        List<Surface> outputSurfaces = new ArrayList<Surface>(/*capacity*/1);
+        assertNotNull("Failed to get Surface", cameraTarget);
+        outputSurfaces.add(cameraTarget);
+
+        mSessionListener = new BlockingSessionListener();
+        mCamera.createCaptureSession(outputSurfaces, mSessionListener, mHandler);
+        mSession = mSessionListener.waitAndGetSession(SESSION_CONFIGURE_TIMEOUT_MS);
+        CaptureRequest.Builder captureBuilder =
+                mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
+        assertNotNull("Fail to create captureRequest", captureBuilder);
+        captureBuilder.addTarget(cameraTarget);
+
+        if (VERBOSE) Log.v(TAG, "configureAndCreateRequestForSurface - done");
+
+        return captureBuilder;
+    }
+
+    /**
+     * Submit a single request to the camera, block until the buffer is available.
+     *
+     * <p>Upon return from this function, script has been executed against the latest buffer.
+     * </p>
+     */
+    private void captureSingleShotAndExecute(CaptureRequest request, ScriptGraph graph)
+            throws CameraAccessException {
+        checkNotNull("request", request);
+        checkNotNull("graph", graph);
+
+        mSession.capture(request, new CameraCaptureSession.CaptureListener() {
+            @Override
+            public void onCaptureCompleted(CameraCaptureSession session, CaptureRequest request,
+                    TotalCaptureResult result) {
+                if (VERBOSE) Log.v(TAG, "Capture completed");
+            }
+        }, mHandler);
+
+        if (VERBOSE) Log.v(TAG, "Waiting for single shot buffer");
+        graph.advanceInputWaiting();
+        if (VERBOSE) Log.v(TAG, "Got the buffer");
+        graph.execute();
+    }
+
+    private void stopCapture() throws CameraAccessException {
+        if (VERBOSE) Log.v(TAG, "Stopping capture and waiting for idle");
+        // Stop repeat, wait for captures to complete, and disconnect from surfaces
+        mSession.close();
+        mSessionListener.getStateWaiter().waitForState(BlockingSessionListener.SESSION_CLOSED,
+                SESSION_CLOSE_TIMEOUT_MS);
+        mSession = null;
+        mSessionListener = null;
+    }
+
+    /**
+     * Extremely dumb validator. Makes sure there is at least one non-zero RGB pixel value.
+     */
+    private void validateInputOutputNotZeroes(ScriptGraph scriptGraph, Size size) {
+        final int BPP = 8; // bits per pixel
+
+        int width = size.getWidth();
+        int height = size.getHeight();
+        /**
+         * Check the input allocation is sane.
+         * - Byte size matches what we expect.
+         * - The input is not all zeroes.
+         */
+
+        // Check that input data was updated first. If it wasn't, the rest of the test will fail.
+        byte[] data = scriptGraph.getInputData();
+        assertArrayNotAllZeroes("Input allocation data was not updated", data);
+
+        // Minimal required size to represent YUV 4:2:0 image
+        int packedSize =
+                width * height * ImageFormat.getBitsPerPixel(YUV_420_888) / BPP;
+        if (VERBOSE) Log.v(TAG, "Expected image size = " + packedSize);
+        int actualSize = data.length;
+        // Actual size may be larger due to strides or planes being non-contiguous
+        assertTrue(
+                String.format(
+                        "YUV 420 packed size (%d) should be at least as large as the actual size " +
+                        "(%d)", packedSize, actualSize), packedSize <= actualSize);
+        /**
+         * Check the output allocation by converting to RGBA.
+         * - Byte size matches what we expect
+         * - The output is not all zeroes
+         */
+        final int RGBA_CHANNELS = 4;
+
+        int actualSizeOut = scriptGraph.getOutputAllocation().getBytesSize();
+        int packedSizeOut = width * height * RGBA_CHANNELS;
+
+        byte[] dataOut = scriptGraph.getOutputData();
+        assertEquals("RGB mismatched byte[] and expected size",
+                packedSizeOut, dataOut.length);
+
+        if (VERBOSE) {
+            Log.v(TAG, "checkAllocationByConvertingToRgba - RGB data size " + dataOut.length);
+        }
+
+        assertArrayNotAllZeroes("RGBA data was not updated", dataOut);
+        // RGBA8888 stride should be equal to the width
+        assertEquals("RGBA 8888 mismatched byte[] and expected size", packedSizeOut, actualSizeOut);
+
+        if (VERBOSE) Log.v(TAG, "validating Buffer , size = " + actualSize);
+    }
+
+    public void testAllocationFromCameraFlexibleYuv() throws Exception {
+
+        /** number of frame (for streaming requests) to be verified. */
+        final int NUM_FRAME_VERIFIED = 1;
+
+        mCameraIterable.forEachCamera(new CameraBlock() {
+            @Override
+            public void run(CameraDevice camera) throws CameraAccessException {
+
+                // Iterate over each size in the camera
+                mSizeIterable.forEachSize(YUV_420_888, new SizeBlock() {
+                    @Override
+                    public void run(final Size size) throws CameraAccessException {
+                        // Create a script graph that converts YUV to RGB
+                        final ScriptGraph scriptGraph = ScriptGraph.create()
+                                .configureInputWithSurface(size, YUV_420_888)
+                                .chainScript(ScriptYuvToRgb.class)
+                                .buildGraph();
+
+                        if (VERBOSE) Log.v(TAG, "Prepared ScriptYuvToRgb for size " + size);
+
+                        // Run the graph against camera input and validate we get some input
+                        try {
+                            CaptureRequest request =
+                                    configureAndCreateRequestForSurface(scriptGraph.getInputSurface()).build();
+
+                            // Block until we get 1 result, then iterate over the result
+                            mResultIterable.forEachResultRepeating(
+                                    request, NUM_FRAME_VERIFIED, new ResultBlock() {
+                                @Override
+                                public void run(CaptureResult result) throws CameraAccessException {
+                                    scriptGraph.advanceInputWaiting();
+                                    scriptGraph.execute();
+                                    validateInputOutputNotZeroes(scriptGraph, size);
+                                    scriptGraph.advanceInputAndDrop();
+                                }
+                            });
+
+                            stopCapture();
+                        } finally {
+                            scriptGraph.close();
+                        }
+                    }
+                });
+            }
+        });
+    }
+
+    /**
+     * Take two shots and ensure per-frame-control with exposure/gain is working correctly.
+     *
+     * <p>Takes a shot with very low ISO and exposure time. Expect it to be black.</p>
+     *
+     * <p>Take a shot with very high ISO and exposure time. Expect it to be white.</p>
+     *
+     * @throws Exception
+     */
+    public void testBlackWhite() throws CameraAccessException {
+
+        /** low iso + low exposure (first shot) */
+        final float THRESHOLD_LOW = 0.025f;
+        /** high iso + high exposure (second shot) */
+        final float THRESHOLD_HIGH = 0.975f;
+
+        mCameraIterable.forEachCamera(/*fullHwLevel*/false, new CameraBlock() {
+            @Override
+            public void run(CameraDevice camera) throws CameraAccessException {
+                final StaticMetadata staticInfo =
+                        new StaticMetadata(mCameraManager.getCameraCharacteristics(camera.getId()));
+
+                // This test requires PFC and manual sensor control
+                if (!staticInfo.isCapabilitySupported(
+                        CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR) ||
+                        !staticInfo.isPerFrameControlSupported()) {
+                    return;
+                }
+
+                final Size maxSize = getMaxSize(
+                        getSupportedSizeForFormat(YUV_420_888, camera.getId(), mCameraManager));
+
+                ScriptGraph scriptGraph = createGraphForYuvCroppedMeans(maxSize);
+
+                CaptureRequest.Builder req =
+                        configureAndCreateRequestForSurface(scriptGraph.getInputSurface());
+
+                // Take a shot with very low ISO and exposure time. Expect it to be black.
+                int minimumSensitivity = staticInfo.getSensitivityMinimumOrDefault();
+                long minimumExposure = staticInfo.getExposureMinimumOrDefault();
+                setManualCaptureRequest(req, minimumSensitivity, minimumExposure);
+
+                CaptureRequest lowIsoExposureShot = req.build();
+                captureSingleShotAndExecute(lowIsoExposureShot, scriptGraph);
+
+                float[] blackMeans = convertPixelYuvToRgb(scriptGraph.getOutputData());
+
+                // Take a shot with very high ISO and exposure time. Expect it to be white.
+                int maximumSensitivity = staticInfo.getSensitivityMaximumOrDefault();
+                long maximumExposure = staticInfo.getExposureMaximumOrDefault();
+                setManualCaptureRequest(req, maximumSensitivity, maximumExposure);
+
+                CaptureRequest highIsoExposureShot = req.build();
+                captureSingleShotAndExecute(highIsoExposureShot, scriptGraph);
+
+                float[] whiteMeans = convertPixelYuvToRgb(scriptGraph.getOutputData());
+
+                // low iso + low exposure (first shot)
+                assertArrayWithinUpperBound("Black means too high", blackMeans, THRESHOLD_LOW);
+
+                // high iso + high exposure (second shot)
+                assertArrayWithinLowerBound("White means too low", whiteMeans, THRESHOLD_HIGH);
+            }
+        });
+    }
+
+    /**
+     * Test that the android.sensitivity.parameter is applied.
+     */
+    public void testParamSensitivity() throws CameraAccessException {
+        final float THRESHOLD_MAX_MIN_DIFF = 0.3f;
+        final float THRESHOLD_MAX_MIN_RATIO = 2.0f;
+        final int NUM_STEPS = 5;
+        final long EXPOSURE_TIME_NS = 2000000; // 2 seconds
+        final int RGB_CHANNELS = 3;
+
+        mCameraIterable.forEachCamera(/*fullHwLevel*/false, new CameraBlock() {
+
+
+            @Override
+            public void run(CameraDevice camera) throws CameraAccessException {
+                final StaticMetadata staticInfo =
+                        new StaticMetadata(mCameraManager.getCameraCharacteristics(camera.getId()));
+                // This test requires PFC and manual sensor control
+                if (!staticInfo.isCapabilitySupported(
+                        CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR) ||
+                        !staticInfo.isPerFrameControlSupported()) {
+                    return;
+                }
+
+                final List<float[]> rgbMeans = new ArrayList<float[]>();
+                final Size maxSize = getMaxSize(
+                        getSupportedSizeForFormat(YUV_420_888, camera.getId(), mCameraManager));
+
+                final int sensitivityMin = staticInfo.getSensitivityMinimumOrDefault();
+                final int sensitivityMax = staticInfo.getSensitivityMaximumOrDefault();
+
+                // List each sensitivity from min-max in NUM_STEPS increments
+                int[] sensitivities = new int[NUM_STEPS];
+                for (int i = 0; i < NUM_STEPS; ++i) {
+                    int delta = (sensitivityMax - sensitivityMin) / (NUM_STEPS - 1);
+                    sensitivities[i] = sensitivityMin + delta * i;
+                }
+
+                ScriptGraph scriptGraph = createGraphForYuvCroppedMeans(maxSize);
+
+                CaptureRequest.Builder req =
+                        configureAndCreateRequestForSurface(scriptGraph.getInputSurface());
+
+                // Take burst shots with increasing sensitivity one after other.
+                for (int i = 0; i < NUM_STEPS; ++i) {
+                    setManualCaptureRequest(req, sensitivities[i], EXPOSURE_TIME_NS);
+                    captureSingleShotAndExecute(req.build(), scriptGraph);
+                    float[] means = convertPixelYuvToRgb(scriptGraph.getOutputData());
+                    rgbMeans.add(means);
+
+                    if (VERBOSE) {
+                        Log.v(TAG, "testParamSensitivity - captured image " + i +
+                                " with RGB means: " + Arrays.toString(means));
+                    }
+                }
+
+                // Test that every consecutive image gets brighter.
+                for (int i = 0; i < rgbMeans.size() - 1; ++i) {
+                    float[] curMeans = rgbMeans.get(i);
+                    float[] nextMeans = rgbMeans.get(i+1);
+
+                    assertArrayNotGreater(
+                            String.format("Shot with sensitivity %d should not have higher " +
+                                    "average means than shot with sensitivity %d",
+                                    sensitivities[i], sensitivities[i+1]),
+                            curMeans, nextMeans);
+                }
+
+                // Test the min-max diff and ratios are within expected thresholds
+                float[] lastMeans = rgbMeans.get(NUM_STEPS - 1);
+                float[] firstMeans = rgbMeans.get(/*location*/0);
+                for (int i = 0; i < RGB_CHANNELS; ++i) {
+                    assertTrue(
+                            String.format("Sensitivity max-min diff too small (max=%f, min=%f)",
+                                    lastMeans[i], firstMeans[i]),
+                            lastMeans[i] - firstMeans[i] > THRESHOLD_MAX_MIN_DIFF);
+                    assertTrue(
+                            String.format("Sensitivity max-min ratio too small (max=%f, min=%f)",
+                                    lastMeans[i], firstMeans[i]),
+                            lastMeans[i] / firstMeans[i] > THRESHOLD_MAX_MIN_RATIO);
+                }
+            }
+        });
+
+    }
+
+    /**
+     * Common script graph for manual-capture based tests that determine the average pixel
+     * values of a cropped sub-region.
+     *
+     * <p>Processing chain:
+     *
+     * <pre>
+     * input:  YUV_420_888 surface
+     * output: mean YUV value of a central section of the image,
+     *         YUV 4:4:4 encoded as U8_3
+     * steps:
+     *      1) crop [0.45,0.45] - [0.55, 0.55]
+     *      2) average columns
+     *      3) average rows
+     * </pre>
+     * </p>
+     */
+    private static ScriptGraph createGraphForYuvCroppedMeans(final Size size) {
+        ScriptGraph scriptGraph = ScriptGraph.create()
+                .configureInputWithSurface(size, YUV_420_888)
+                .configureScript(ScriptYuvCrop.class)
+                    .set(ScriptYuvCrop.CROP_WINDOW,
+                            new Patch(size, /*x*/0.45f, /*y*/0.45f, /*w*/0.1f, /*h*/0.1f).toRectF())
+                    .buildScript()
+                .chainScript(ScriptYuvMeans2dTo1d.class)
+                .chainScript(ScriptYuvMeans1d.class)
+                // TODO: Make a script for YUV 444 -> RGB 888 conversion
+                .buildGraph();
+        return scriptGraph;
+    }
+
+    /*
+     * TODO: Refactor below code into separate classes and to not depend on AllocationTest
+     * inner variables.
+     *
+     * TODO: add javadocs to below methods
+     *
+     * TODO: Figure out if there's some elegant way to compose these forEaches together, so that
+     * the callers don't have to do a ton of nesting
+     */
+
+    interface CameraBlock {
+        void run(CameraDevice camera) throws CameraAccessException;
+    }
+
+    class CameraIterable {
+        public void forEachCamera(CameraBlock runnable)
+                throws CameraAccessException {
+            forEachCamera(/*fullHwLevel*/false, runnable);
+        }
+
+        public void forEachCamera(boolean fullHwLevel, CameraBlock runnable)
+                throws CameraAccessException {
+            assertNotNull("No camera manager", mCameraManager);
+            assertNotNull("No camera IDs", mCameraIds);
+
+            for (int i = 0; i < mCameraIds.length; i++) {
+                // Don't execute the runnable against non-FULL cameras if FULL is required
+                CameraCharacteristics properties =
+                        mCameraManager.getCameraCharacteristics(mCameraIds[i]);
+                StaticMetadata staticInfo = new StaticMetadata(properties);
+                if (fullHwLevel && !staticInfo.isHardwareLevelFull()) {
+                    Log.i(TAG, String.format(
+                            "Skipping this test for camera %s, needs FULL hw level",
+                            mCameraIds[i]));
+                    continue;
+                }
+
+                // Open camera and execute test
+                Log.i(TAG, "Testing Camera " + mCameraIds[i]);
+                try {
+                    openDevice(mCameraIds[i]);
+
+                    runnable.run(mCamera);
+                } finally {
+                    closeDevice(mCameraIds[i]);
+                }
+            }
+        }
+
+        private void openDevice(String cameraId) {
+            if (mCamera != null) {
+                throw new IllegalStateException("Already have open camera device");
+            }
+            try {
+                mCamera = openCamera(
+                    mCameraManager, cameraId, mCameraListener, mHandler);
+            } catch (CameraAccessException e) {
+                fail("Fail to open camera synchronously, " + Log.getStackTraceString(e));
+            } catch (BlockingOpenException e) {
+                fail("Fail to open camera asynchronously, " + Log.getStackTraceString(e));
+            }
+            mCameraListener.waitForState(STATE_OPENED, CAMERA_OPEN_TIMEOUT_MS);
+        }
+
+        private void closeDevice(String cameraId) {
+            if (mCamera != null) {
+                mCamera.close();
+                mCameraListener.waitForState(STATE_CLOSED, CAMERA_CLOSE_TIMEOUT_MS);
+                mCamera = null;
+            }
+        }
+    }
+
+    interface SizeBlock {
+        void run(Size size) throws CameraAccessException;
+    }
+
+    class SizeIterable {
+        public void forEachSize(int format, SizeBlock runnable) throws CameraAccessException {
+            assertNotNull("No camera opened", mCamera);
+            assertNotNull("No camera manager", mCameraManager);
+
+            CameraCharacteristics properties =
+                    mCameraManager.getCameraCharacteristics(mCamera.getId());
+
+            assertNotNull("Can't get camera properties!", properties);
+
+            StreamConfigurationMap config =
+                    properties.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
+            int[] availableOutputFormats = config.getOutputFormats();
+            assertArrayNotEmpty(availableOutputFormats,
+                    "availableOutputFormats should not be empty");
+            Arrays.sort(availableOutputFormats);
+            assertTrue("Can't find the format " + format + " in supported formats " +
+                    Arrays.toString(availableOutputFormats),
+                    Arrays.binarySearch(availableOutputFormats, format) >= 0);
+
+            Size[] availableSizes = getSupportedSizeForFormat(format, mCamera.getId(),
+                    mCameraManager);
+            assertArrayNotEmpty(availableSizes, "availableSizes should not be empty");
+
+            for (Size size : availableSizes) {
+
+                if (VERBOSE) {
+                    Log.v(TAG, "Testing size " + size.toString() +
+                            " for camera " + mCamera.getId());
+                }
+                runnable.run(size);
+            }
+        }
+    }
+
+    interface ResultBlock {
+        void run(CaptureResult result) throws CameraAccessException;
+    }
+
+    class ResultIterable {
+        public void forEachResultOnce(CaptureRequest request, ResultBlock block)
+                throws CameraAccessException {
+            forEachResult(request, /*count*/1, /*repeating*/false, block);
+        }
+
+        public void forEachResultRepeating(CaptureRequest request, int count, ResultBlock block)
+                throws CameraAccessException {
+            forEachResult(request, count, /*repeating*/true, block);
+        }
+
+        public void forEachResult(CaptureRequest request, int count, boolean repeating,
+                ResultBlock block) throws CameraAccessException {
+
+            // TODO: start capture, i.e. configureOutputs
+
+            SimpleCaptureListener listener = new SimpleCaptureListener();
+
+            if (!repeating) {
+                for (int i = 0; i < count; ++i) {
+                    mSession.capture(request, listener, mHandler);
+                }
+            } else {
+                mSession.setRepeatingRequest(request, listener, mHandler);
+            }
+
+            // Assume that the device is already IDLE.
+            mSessionListener.getStateWaiter().waitForState(BlockingSessionListener.SESSION_ACTIVE,
+                    CAMERA_ACTIVE_TIMEOUT_MS);
+
+            for (int i = 0; i < count; ++i) {
+                if (VERBOSE) {
+                    Log.v(TAG, String.format("Testing with result %d of %d for camera %s",
+                            i, count, mCamera.getId()));
+                }
+
+                CaptureResult result = listener.getCaptureResult(CAPTURE_RESULT_TIMEOUT_MS);
+                block.run(result);
+            }
+
+            if (repeating) {
+                mSession.stopRepeating();
+                mSessionListener.getStateWaiter().waitForState(
+                    BlockingSessionListener.SESSION_READY, CAMERA_IDLE_TIMEOUT_MS);
+            }
+
+            // TODO: Make a Configure decorator or some such for configureOutputs
+        }
+    }
+}
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/CameraCaptureResultTest.java b/tests/tests/hardware/src/android/hardware/camera2/cts/CameraCaptureResultTest.java
deleted file mode 100644
index 6a708e3..0000000
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/CameraCaptureResultTest.java
+++ /dev/null
@@ -1,336 +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.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;
-import android.hardware.camera2.CaptureFailure;
-import android.hardware.camera2.CaptureRequest;
-import android.hardware.camera2.CaptureResult;
-import android.hardware.camera2.Size;
-import static android.hardware.camera2.cts.CameraTestUtils.*;
-import android.media.ImageReader;
-import android.os.Handler;
-import android.os.HandlerThread;
-import android.test.AndroidTestCase;
-import android.util.Log;
-import android.view.Surface;
-
-import com.android.ex.camera2.blocking.BlockingStateListener;
-import static com.android.ex.camera2.blocking.BlockingStateListener.*;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.concurrent.LinkedBlockingQueue;
-import java.util.concurrent.TimeUnit;
-
-public class CameraCaptureResultTest extends AndroidTestCase {
-    private static final String TAG = "CameraCaptureResultTest";
-    private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
-
-    private CameraManager mCameraManager;
-    private HandlerThread mHandlerThread;
-    private Handler mHandler;
-    private ImageReader mImageReader;
-    private Surface mSurface;
-    private BlockingStateListener mCameraListener;
-
-    private static final int MAX_NUM_IMAGES = 5;
-    private static final int NUM_FRAMES_VERIFIED = 300;
-    private static final long WAIT_FOR_RESULT_TIMEOUT_MS = 3000;
-
-    // List that includes all public keys from CaptureResult
-    List<CameraMetadata.Key<?>> mAllKeys;
-
-    // List tracking the failed test keys.
-    List<CameraMetadata.Key<?>> mFailedKeys = new ArrayList<CameraMetadata.Key<?>>();
-
-    @Override
-    public void setContext(Context context) {
-        mAllKeys = getAllCaptureResultKeys();
-        super.setContext(context);
-    }
-
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
-        mCameraManager = (CameraManager) mContext.getSystemService(Context.CAMERA_SERVICE);
-        assertNotNull("Can't connect to camera manager", mCameraManager);
-        mHandlerThread = new HandlerThread(TAG);
-        mHandlerThread.start();
-        mHandler = new Handler(mHandlerThread.getLooper());
-        mCameraListener = new BlockingStateListener();
-        mFailedKeys.clear();
-    }
-
-    @Override
-    protected void tearDown() throws Exception {
-        mHandlerThread.quitSafely();
-        super.tearDown();
-    }
-
-    public void testCameraCaptureResultAllKeys() throws Exception {
-        /**
-         * Hardcode a key waiver list for the keys we want to skip the sanity check.
-         * FIXME: We need get ride of this list, see bug 11116270.
-         */
-        List<CameraMetadata.Key<?>> waiverkeys = new ArrayList<CameraMetadata.Key<?>>();
-        waiverkeys.add(CaptureResult.EDGE_MODE);
-        waiverkeys.add(CaptureResult.JPEG_GPS_COORDINATES);
-        waiverkeys.add(CaptureResult.JPEG_GPS_PROCESSING_METHOD);
-        waiverkeys.add(CaptureResult.JPEG_GPS_TIMESTAMP);
-        waiverkeys.add(CaptureResult.JPEG_ORIENTATION);
-        waiverkeys.add(CaptureResult.JPEG_QUALITY);
-        waiverkeys.add(CaptureResult.JPEG_THUMBNAIL_QUALITY);
-        waiverkeys.add(CaptureResult.JPEG_THUMBNAIL_SIZE);
-        waiverkeys.add(CaptureResult.SENSOR_TEMPERATURE);
-        waiverkeys.add(CaptureResult.TONEMAP_CURVE_BLUE);
-        waiverkeys.add(CaptureResult.TONEMAP_CURVE_GREEN);
-        waiverkeys.add(CaptureResult.TONEMAP_CURVE_RED);
-        waiverkeys.add(CaptureResult.TONEMAP_MODE);
-        waiverkeys.add(CaptureResult.STATISTICS_PREDICTED_COLOR_GAINS);
-        waiverkeys.add(CaptureResult.STATISTICS_PREDICTED_COLOR_TRANSFORM);
-        waiverkeys.add(CaptureResult.STATISTICS_SCENE_FLICKER);
-
-        String[] ids = mCameraManager.getCameraIdList();
-        for (int i = 0; i < ids.length; i++) {
-            CameraCharacteristics props = mCameraManager.getCameraCharacteristics(ids[i]);
-            assertNotNull("CameraCharacteristics shouldn't be null", props);
-            Integer hwLevel = props.get(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL);
-            if (hwLevel != CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL) {
-                continue;
-            }
-            // TODO: check for LIMITED keys
-
-            CameraDevice camera = null;
-            try {
-                Size[] sizes = CameraTestUtils.getSupportedSizeForFormat(
-                        ImageFormat.YUV_420_888, ids[i], mCameraManager);
-                CameraTestUtils.assertArrayNotEmpty(sizes, "Available sizes shouldn't be empty");
-                createDefaultSurface(sizes[0]);
-
-                if (VERBOSE) {
-                    Log.v(TAG, "Testing camera " + ids[i] + "for size " + sizes[0].toString());
-                }
-
-                camera = CameraTestUtils.openCamera(
-                        mCameraManager, ids[i], mCameraListener, mHandler);
-                assertNotNull(
-                        String.format("Failed to open camera device %s", ids[i]), camera);
-                mCameraListener.waitForState(STATE_UNCONFIGURED, CAMERA_OPEN_TIMEOUT_MS);
-
-                List<Surface> outputSurfaces = new ArrayList<Surface>(1);
-                outputSurfaces.add(mSurface);
-                camera.configureOutputs(outputSurfaces);
-                mCameraListener.waitForState(STATE_BUSY, CAMERA_BUSY_TIMEOUT_MS);
-                mCameraListener.waitForState(STATE_IDLE, CAMERA_IDLE_TIMEOUT_MS);
-
-                CaptureRequest.Builder requestBuilder =
-                        camera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
-                assertNotNull("Failed to create capture request", requestBuilder);
-                requestBuilder.addTarget(mSurface);
-
-                // Enable face detection if supported
-                byte[] faceModes = props.get(
-                        CameraCharacteristics.STATISTICS_INFO_AVAILABLE_FACE_DETECT_MODES);
-                assertNotNull("Available face detection modes shouldn't be null", faceModes);
-                for (int m = 0; m < faceModes.length; m++) {
-                    if (faceModes[m] == CameraMetadata.STATISTICS_FACE_DETECT_MODE_FULL) {
-                        if (VERBOSE) {
-                            Log.v(TAG, "testCameraCaptureResultAllKeys - " +
-                                    "setting facedetection mode to full");
-                        }
-                        requestBuilder.set(CaptureRequest.STATISTICS_FACE_DETECT_MODE,
-                                (int)faceModes[m]);
-                    }
-                }
-
-                // Enable lensShading mode, it should be supported by full mode device.
-                requestBuilder.set(CaptureRequest.STATISTICS_LENS_SHADING_MAP_MODE,
-                        CameraMetadata.STATISTICS_LENS_SHADING_MAP_MODE_ON);
-
-                SimpleCaptureListener captureListener = new SimpleCaptureListener();
-                camera.setRepeatingRequest(requestBuilder.build(), captureListener, mHandler);
-
-                for (int m = 0; m < NUM_FRAMES_VERIFIED; m++) {
-                    if(VERBOSE) {
-                        Log.v(TAG, "Testing frame " + m);
-                    }
-                    validateCaptureResult(
-                            captureListener.getCaptureResult(WAIT_FOR_RESULT_TIMEOUT_MS),
-                            waiverkeys);
-                }
-
-                // Stop repeat, wait for captures to complete, and disconnect from surfaces
-                camera.configureOutputs(/*outputs*/ null);
-                mCameraListener.waitForState(STATE_BUSY, CAMERA_BUSY_TIMEOUT_MS);
-                mCameraListener.waitForState(STATE_UNCONFIGURED, CAMERA_IDLE_TIMEOUT_MS);
-                // Camera has disconnected, clear out the reader
-                mSurface.release();
-                mImageReader.close();
-            } finally {
-                if (camera != null) {
-                    camera.close();
-                }
-            }
-
-        }
-    }
-
-    private void validateCaptureResult(CaptureResult result,
-            List<CameraMetadata.Key<?>> skippedKeys) throws Exception {
-        for (CameraMetadata.Key<?> key : mAllKeys) {
-            if (!skippedKeys.contains(key) && result.get(key) == null) {
-                mFailedKeys.add(key);
-            }
-        }
-
-        StringBuffer failedKeyNames = new StringBuffer("Below Keys have null values:\n");
-        for (CameraMetadata.Key<?> key : mFailedKeys) {
-            failedKeyNames.append(key.getName() + "\n");
-        }
-
-        assertTrue("Some keys have null values, " + failedKeyNames.toString(),
-                mFailedKeys.isEmpty());
-    }
-
-    private static class SimpleCaptureListener extends CameraDevice.CaptureListener {
-        LinkedBlockingQueue<CaptureResult> mQueue = new LinkedBlockingQueue<CaptureResult>();
-
-        @Override
-        public void onCaptureStarted(CameraDevice camera, CaptureRequest request, long timestamp)
-        {
-        }
-
-        @Override
-        public void onCaptureCompleted(CameraDevice camera, CaptureRequest request,
-                CaptureResult result) {
-            try {
-                mQueue.put(result);
-            } catch (InterruptedException e) {
-                throw new UnsupportedOperationException(
-                        "Can't handle InterruptedException in onCaptureCompleted");
-            }
-        }
-
-        @Override
-        public void onCaptureFailed(CameraDevice camera, CaptureRequest request,
-                CaptureFailure failure) {
-        }
-
-        @Override
-        public void onCaptureSequenceCompleted(CameraDevice camera, int sequenceId,
-                int frameNumber) {
-        }
-
-        public CaptureResult getCaptureResult(long timeout) throws InterruptedException {
-            CaptureResult result = mQueue.poll(timeout, TimeUnit.MILLISECONDS);
-            assertNotNull("Wait for a capture result timed out in " + timeout + "ms", result);
-            return result;
-        }
-    }
-
-    private void createDefaultSurface(Size sz) {
-        mImageReader =
-                ImageReader.newInstance(sz.getWidth(),
-                        sz.getHeight(),
-                        ImageFormat.YUV_420_888,
-                        MAX_NUM_IMAGES);
-        mImageReader.setOnImageAvailableListener(new ImageDropperListener(), mHandler);
-        mSurface = mImageReader.getSurface();
-    }
-
-    /**
-     * TODO: Use CameraCharacteristics.getAvailableCaptureResultKeys() once we can filter out
-     * @hide keys.
-     *
-     */
-
-    /*@O~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~
-     * The key entries below this point are generated from metadata
-     * definitions in /system/media/camera/docs. Do not modify by hand or
-     * modify the comment blocks at the start or end.
-     *~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~*/
-
-    private static List<CameraMetadata.Key<?>> getAllCaptureResultKeys() {
-        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_REGIONS);
-        resultKeys.add(CaptureResult.CONTROL_AF_MODE);
-        resultKeys.add(CaptureResult.CONTROL_AF_REGIONS);
-        resultKeys.add(CaptureResult.CONTROL_AWB_MODE);
-        resultKeys.add(CaptureResult.CONTROL_AWB_REGIONS);
-        resultKeys.add(CaptureResult.CONTROL_MODE);
-        resultKeys.add(CaptureResult.CONTROL_AE_STATE);
-        resultKeys.add(CaptureResult.CONTROL_AF_STATE);
-        resultKeys.add(CaptureResult.CONTROL_AWB_STATE);
-        resultKeys.add(CaptureResult.EDGE_MODE);
-        resultKeys.add(CaptureResult.FLASH_MODE);
-        resultKeys.add(CaptureResult.FLASH_STATE);
-        resultKeys.add(CaptureResult.JPEG_GPS_COORDINATES);
-        resultKeys.add(CaptureResult.JPEG_GPS_PROCESSING_METHOD);
-        resultKeys.add(CaptureResult.JPEG_GPS_TIMESTAMP);
-        resultKeys.add(CaptureResult.JPEG_ORIENTATION);
-        resultKeys.add(CaptureResult.JPEG_QUALITY);
-        resultKeys.add(CaptureResult.JPEG_THUMBNAIL_QUALITY);
-        resultKeys.add(CaptureResult.JPEG_THUMBNAIL_SIZE);
-        resultKeys.add(CaptureResult.LENS_APERTURE);
-        resultKeys.add(CaptureResult.LENS_FILTER_DENSITY);
-        resultKeys.add(CaptureResult.LENS_FOCAL_LENGTH);
-        resultKeys.add(CaptureResult.LENS_FOCUS_DISTANCE);
-        resultKeys.add(CaptureResult.LENS_OPTICAL_STABILIZATION_MODE);
-        resultKeys.add(CaptureResult.LENS_FOCUS_RANGE);
-        resultKeys.add(CaptureResult.LENS_STATE);
-        resultKeys.add(CaptureResult.NOISE_REDUCTION_MODE);
-        resultKeys.add(CaptureResult.REQUEST_FRAME_COUNT);
-        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.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);
-        resultKeys.add(CaptureResult.TONEMAP_CURVE_RED);
-        resultKeys.add(CaptureResult.TONEMAP_MODE);
-        resultKeys.add(CaptureResult.BLACK_LEVEL_LOCK);
-
-        // Add STATISTICS_FACES key separately here because it is not
-        // defined in metadata xml file.
-        resultKeys.add(CaptureResult.STATISTICS_FACES);
-
-        return resultKeys;
-    }
-
-    /*~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~
-     * End generated code
-     *~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~O@*/
-}
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..cc618ee 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/CameraCharacteristicsTest.java
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/CameraCharacteristicsTest.java
@@ -27,7 +27,7 @@
 import android.content.Context;
 import android.hardware.camera2.CameraCharacteristics;
 import android.hardware.camera2.CameraManager;
-import android.hardware.camera2.CameraMetadata.Key;
+import android.hardware.camera2.CameraCharacteristics.Key;
 import android.test.AndroidTestCase;
 import android.util.Log;
 
@@ -57,6 +57,28 @@
         super.tearDown();
     }
 
+    public void testCameraCharacteristicsAndroidColorCorrectionAvailableAberrationCorrectionModes() 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.colorCorrection.availableAberrationCorrectionModes",
+                        props.get(CameraCharacteristics.COLOR_CORRECTION_AVAILABLE_ABERRATION_CORRECTION_MODES));
+                List<Key<?>> allKeys = props.getKeys();
+                assertNotNull(String.format("Can't get camera characteristics keys from: ID %s",
+                        ids[i]), allKeys);
+                assertTrue("Key not in keys list: android.colorCorrection.availableAberrationCorrectionModes", allKeys.contains(
+                        CameraCharacteristics.COLOR_CORRECTION_AVAILABLE_ABERRATION_CORRECTION_MODES));
+
+            }
+
+        }
+    }
+
     public void testCameraCharacteristicsAndroidControlAeAvailableAntibandingModes() throws Exception {
         String[] ids = mCameraManager.getCameraIdList();
         for (int i = 0; i < ids.length; i++) {
@@ -68,10 +90,9 @@
 
                 assertNotNull("Invalid property: android.control.aeAvailableAntibandingModes",
                         props.get(CameraCharacteristics.CONTROL_AE_AVAILABLE_ANTIBANDING_MODES));
-
                 List<Key<?>> allKeys = props.getKeys();
                 assertNotNull(String.format("Can't get camera characteristics keys from: ID %s",
-                        ids[i], props));
+                        ids[i]), allKeys);
                 assertTrue("Key not in keys list: android.control.aeAvailableAntibandingModes", allKeys.contains(
                         CameraCharacteristics.CONTROL_AE_AVAILABLE_ANTIBANDING_MODES));
 
@@ -80,6 +101,28 @@
         }
     }
 
+    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]), allKeys);
+                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++) {
@@ -91,10 +134,9 @@
 
                 assertNotNull("Invalid property: android.control.aeAvailableTargetFpsRanges",
                         props.get(CameraCharacteristics.CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES));
-
                 List<Key<?>> allKeys = props.getKeys();
                 assertNotNull(String.format("Can't get camera characteristics keys from: ID %s",
-                        ids[i], props));
+                        ids[i]), allKeys);
                 assertTrue("Key not in keys list: android.control.aeAvailableTargetFpsRanges", allKeys.contains(
                         CameraCharacteristics.CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES));
 
@@ -114,10 +156,9 @@
 
                 assertNotNull("Invalid property: android.control.aeCompensationRange",
                         props.get(CameraCharacteristics.CONTROL_AE_COMPENSATION_RANGE));
-
                 List<Key<?>> allKeys = props.getKeys();
                 assertNotNull(String.format("Can't get camera characteristics keys from: ID %s",
-                        ids[i], props));
+                        ids[i]), allKeys);
                 assertTrue("Key not in keys list: android.control.aeCompensationRange", allKeys.contains(
                         CameraCharacteristics.CONTROL_AE_COMPENSATION_RANGE));
 
@@ -137,10 +178,9 @@
 
                 assertNotNull("Invalid property: android.control.aeCompensationStep",
                         props.get(CameraCharacteristics.CONTROL_AE_COMPENSATION_STEP));
-
                 List<Key<?>> allKeys = props.getKeys();
                 assertNotNull(String.format("Can't get camera characteristics keys from: ID %s",
-                        ids[i], props));
+                        ids[i]), allKeys);
                 assertTrue("Key not in keys list: android.control.aeCompensationStep", allKeys.contains(
                         CameraCharacteristics.CONTROL_AE_COMPENSATION_STEP));
 
@@ -160,10 +200,9 @@
 
                 assertNotNull("Invalid property: android.control.afAvailableModes",
                         props.get(CameraCharacteristics.CONTROL_AF_AVAILABLE_MODES));
-
                 List<Key<?>> allKeys = props.getKeys();
                 assertNotNull(String.format("Can't get camera characteristics keys from: ID %s",
-                        ids[i], props));
+                        ids[i]), allKeys);
                 assertTrue("Key not in keys list: android.control.afAvailableModes", allKeys.contains(
                         CameraCharacteristics.CONTROL_AF_AVAILABLE_MODES));
 
@@ -183,10 +222,9 @@
 
                 assertNotNull("Invalid property: android.control.availableEffects",
                         props.get(CameraCharacteristics.CONTROL_AVAILABLE_EFFECTS));
-
                 List<Key<?>> allKeys = props.getKeys();
                 assertNotNull(String.format("Can't get camera characteristics keys from: ID %s",
-                        ids[i], props));
+                        ids[i]), allKeys);
                 assertTrue("Key not in keys list: android.control.availableEffects", allKeys.contains(
                         CameraCharacteristics.CONTROL_AVAILABLE_EFFECTS));
 
@@ -206,10 +244,9 @@
 
                 assertNotNull("Invalid property: android.control.availableSceneModes",
                         props.get(CameraCharacteristics.CONTROL_AVAILABLE_SCENE_MODES));
-
                 List<Key<?>> allKeys = props.getKeys();
                 assertNotNull(String.format("Can't get camera characteristics keys from: ID %s",
-                        ids[i], props));
+                        ids[i]), allKeys);
                 assertTrue("Key not in keys list: android.control.availableSceneModes", allKeys.contains(
                         CameraCharacteristics.CONTROL_AVAILABLE_SCENE_MODES));
 
@@ -229,10 +266,9 @@
 
                 assertNotNull("Invalid property: android.control.availableVideoStabilizationModes",
                         props.get(CameraCharacteristics.CONTROL_AVAILABLE_VIDEO_STABILIZATION_MODES));
-
                 List<Key<?>> allKeys = props.getKeys();
                 assertNotNull(String.format("Can't get camera characteristics keys from: ID %s",
-                        ids[i], props));
+                        ids[i]), allKeys);
                 assertTrue("Key not in keys list: android.control.availableVideoStabilizationModes", allKeys.contains(
                         CameraCharacteristics.CONTROL_AVAILABLE_VIDEO_STABILIZATION_MODES));
 
@@ -252,10 +288,9 @@
 
                 assertNotNull("Invalid property: android.control.awbAvailableModes",
                         props.get(CameraCharacteristics.CONTROL_AWB_AVAILABLE_MODES));
-
                 List<Key<?>> allKeys = props.getKeys();
                 assertNotNull(String.format("Can't get camera characteristics keys from: ID %s",
-                        ids[i], props));
+                        ids[i]), allKeys);
                 assertTrue("Key not in keys list: android.control.awbAvailableModes", allKeys.contains(
                         CameraCharacteristics.CONTROL_AWB_AVAILABLE_MODES));
 
@@ -264,7 +299,7 @@
         }
     }
 
-    public void testCameraCharacteristicsAndroidControlMaxRegions() throws Exception {
+    public void testCameraCharacteristicsAndroidControlMaxRegionsAe() throws Exception {
         String[] ids = mCameraManager.getCameraIdList();
         for (int i = 0; i < ids.length; i++) {
             CameraCharacteristics props = mCameraManager.getCameraCharacteristics(ids[i]);
@@ -273,14 +308,79 @@
 
             {
 
-                assertNotNull("Invalid property: android.control.maxRegions",
-                        props.get(CameraCharacteristics.CONTROL_MAX_REGIONS));
-
+                assertNotNull("Invalid property: android.control.maxRegionsAe",
+                        props.get(CameraCharacteristics.CONTROL_MAX_REGIONS_AE));
                 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.maxRegions", allKeys.contains(
-                        CameraCharacteristics.CONTROL_MAX_REGIONS));
+                        ids[i]), allKeys);
+                assertTrue("Key not in keys list: android.control.maxRegionsAe", allKeys.contains(
+                        CameraCharacteristics.CONTROL_MAX_REGIONS_AE));
+
+            }
+
+        }
+    }
+
+    public void testCameraCharacteristicsAndroidControlMaxRegionsAwb() 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.maxRegionsAwb",
+                        props.get(CameraCharacteristics.CONTROL_MAX_REGIONS_AWB));
+                List<Key<?>> allKeys = props.getKeys();
+                assertNotNull(String.format("Can't get camera characteristics keys from: ID %s",
+                        ids[i]), allKeys);
+                assertTrue("Key not in keys list: android.control.maxRegionsAwb", allKeys.contains(
+                        CameraCharacteristics.CONTROL_MAX_REGIONS_AWB));
+
+            }
+
+        }
+    }
+
+    public void testCameraCharacteristicsAndroidControlMaxRegionsAf() 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.maxRegionsAf",
+                        props.get(CameraCharacteristics.CONTROL_MAX_REGIONS_AF));
+                List<Key<?>> allKeys = props.getKeys();
+                assertNotNull(String.format("Can't get camera characteristics keys from: ID %s",
+                        ids[i]), allKeys);
+                assertTrue("Key not in keys list: android.control.maxRegionsAf", allKeys.contains(
+                        CameraCharacteristics.CONTROL_MAX_REGIONS_AF));
+
+            }
+
+        }
+    }
+
+    public void testCameraCharacteristicsAndroidEdgeAvailableEdgeModes() 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.edge.availableEdgeModes",
+                        props.get(CameraCharacteristics.EDGE_AVAILABLE_EDGE_MODES));
+                List<Key<?>> allKeys = props.getKeys();
+                assertNotNull(String.format("Can't get camera characteristics keys from: ID %s",
+                        ids[i]), allKeys);
+                assertTrue("Key not in keys list: android.edge.availableEdgeModes", allKeys.contains(
+                        CameraCharacteristics.EDGE_AVAILABLE_EDGE_MODES));
 
             }
 
@@ -298,10 +398,9 @@
 
                 assertNotNull("Invalid property: android.flash.info.available",
                         props.get(CameraCharacteristics.FLASH_INFO_AVAILABLE));
-
                 List<Key<?>> allKeys = props.getKeys();
                 assertNotNull(String.format("Can't get camera characteristics keys from: ID %s",
-                        ids[i], props));
+                        ids[i]), allKeys);
                 assertTrue("Key not in keys list: android.flash.info.available", allKeys.contains(
                         CameraCharacteristics.FLASH_INFO_AVAILABLE));
 
@@ -310,6 +409,28 @@
         }
     }
 
+    public void testCameraCharacteristicsAndroidHotPixelAvailableHotPixelModes() 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.hotPixel.availableHotPixelModes",
+                        props.get(CameraCharacteristics.HOT_PIXEL_AVAILABLE_HOT_PIXEL_MODES));
+                List<Key<?>> allKeys = props.getKeys();
+                assertNotNull(String.format("Can't get camera characteristics keys from: ID %s",
+                        ids[i]), allKeys);
+                assertTrue("Key not in keys list: android.hotPixel.availableHotPixelModes", allKeys.contains(
+                        CameraCharacteristics.HOT_PIXEL_AVAILABLE_HOT_PIXEL_MODES));
+
+            }
+
+        }
+    }
+
     public void testCameraCharacteristicsAndroidJpegAvailableThumbnailSizes() throws Exception {
         String[] ids = mCameraManager.getCameraIdList();
         for (int i = 0; i < ids.length; i++) {
@@ -321,10 +442,9 @@
 
                 assertNotNull("Invalid property: android.jpeg.availableThumbnailSizes",
                         props.get(CameraCharacteristics.JPEG_AVAILABLE_THUMBNAIL_SIZES));
-
                 List<Key<?>> allKeys = props.getKeys();
                 assertNotNull(String.format("Can't get camera characteristics keys from: ID %s",
-                        ids[i], props));
+                        ids[i]), allKeys);
                 assertTrue("Key not in keys list: android.jpeg.availableThumbnailSizes", allKeys.contains(
                         CameraCharacteristics.JPEG_AVAILABLE_THUMBNAIL_SIZES));
 
@@ -344,10 +464,9 @@
 
                 assertNotNull("Invalid property: android.lens.facing",
                         props.get(CameraCharacteristics.LENS_FACING));
-
                 List<Key<?>> allKeys = props.getKeys();
                 assertNotNull(String.format("Can't get camera characteristics keys from: ID %s",
-                        ids[i], props));
+                        ids[i]), allKeys);
                 assertTrue("Key not in keys list: android.lens.facing", allKeys.contains(
                         CameraCharacteristics.LENS_FACING));
 
@@ -367,10 +486,9 @@
 
                 assertNotNull("Invalid property: android.lens.info.availableApertures",
                         props.get(CameraCharacteristics.LENS_INFO_AVAILABLE_APERTURES));
-
                 List<Key<?>> allKeys = props.getKeys();
                 assertNotNull(String.format("Can't get camera characteristics keys from: ID %s",
-                        ids[i], props));
+                        ids[i]), allKeys);
                 assertTrue("Key not in keys list: android.lens.info.availableApertures", allKeys.contains(
                         CameraCharacteristics.LENS_INFO_AVAILABLE_APERTURES));
 
@@ -390,10 +508,9 @@
 
                 assertNotNull("Invalid property: android.lens.info.availableFilterDensities",
                         props.get(CameraCharacteristics.LENS_INFO_AVAILABLE_FILTER_DENSITIES));
-
                 List<Key<?>> allKeys = props.getKeys();
                 assertNotNull(String.format("Can't get camera characteristics keys from: ID %s",
-                        ids[i], props));
+                        ids[i]), allKeys);
                 assertTrue("Key not in keys list: android.lens.info.availableFilterDensities", allKeys.contains(
                         CameraCharacteristics.LENS_INFO_AVAILABLE_FILTER_DENSITIES));
 
@@ -413,10 +530,9 @@
 
                 assertNotNull("Invalid property: android.lens.info.availableFocalLengths",
                         props.get(CameraCharacteristics.LENS_INFO_AVAILABLE_FOCAL_LENGTHS));
-
                 List<Key<?>> allKeys = props.getKeys();
                 assertNotNull(String.format("Can't get camera characteristics keys from: ID %s",
-                        ids[i], props));
+                        ids[i]), allKeys);
                 assertTrue("Key not in keys list: android.lens.info.availableFocalLengths", allKeys.contains(
                         CameraCharacteristics.LENS_INFO_AVAILABLE_FOCAL_LENGTHS));
 
@@ -436,10 +552,9 @@
 
                 assertNotNull("Invalid property: android.lens.info.availableOpticalStabilization",
                         props.get(CameraCharacteristics.LENS_INFO_AVAILABLE_OPTICAL_STABILIZATION));
-
                 List<Key<?>> allKeys = props.getKeys();
                 assertNotNull(String.format("Can't get camera characteristics keys from: ID %s",
-                        ids[i], props));
+                        ids[i]), allKeys);
                 assertTrue("Key not in keys list: android.lens.info.availableOpticalStabilization", allKeys.contains(
                         CameraCharacteristics.LENS_INFO_AVAILABLE_OPTICAL_STABILIZATION));
 
@@ -455,14 +570,17 @@
             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.lens.info.hyperfocalDistance",
                         props.get(CameraCharacteristics.LENS_INFO_HYPERFOCAL_DISTANCE));
-
                 List<Key<?>> allKeys = props.getKeys();
                 assertNotNull(String.format("Can't get camera characteristics keys from: ID %s",
-                        ids[i], props));
+                        ids[i]), allKeys);
                 assertTrue("Key not in keys list: android.lens.info.hyperfocalDistance", allKeys.contains(
                         CameraCharacteristics.LENS_INFO_HYPERFOCAL_DISTANCE));
 
@@ -478,14 +596,17 @@
             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.lens.info.minimumFocusDistance",
                         props.get(CameraCharacteristics.LENS_INFO_MINIMUM_FOCUS_DISTANCE));
-
                 List<Key<?>> allKeys = props.getKeys();
                 assertNotNull(String.format("Can't get camera characteristics keys from: ID %s",
-                        ids[i], props));
+                        ids[i]), allKeys);
                 assertTrue("Key not in keys list: android.lens.info.minimumFocusDistance", allKeys.contains(
                         CameraCharacteristics.LENS_INFO_MINIMUM_FOCUS_DISTANCE));
 
@@ -494,7 +615,7 @@
         }
     }
 
-    public void testCameraCharacteristicsAndroidLensInfoShadingMapSize() throws Exception {
+    public void testCameraCharacteristicsAndroidLensInfoFocusDistanceCalibration() throws Exception {
         String[] ids = mCameraManager.getCameraIdList();
         for (int i = 0; i < ids.length; i++) {
             CameraCharacteristics props = mCameraManager.getCameraCharacteristics(ids[i]);
@@ -503,21 +624,20 @@
 
             {
 
-                assertNotNull("Invalid property: android.lens.info.shadingMapSize",
-                        props.get(CameraCharacteristics.LENS_INFO_SHADING_MAP_SIZE));
-
+                assertNotNull("Invalid property: android.lens.info.focusDistanceCalibration",
+                        props.get(CameraCharacteristics.LENS_INFO_FOCUS_DISTANCE_CALIBRATION));
                 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.lens.info.shadingMapSize", allKeys.contains(
-                        CameraCharacteristics.LENS_INFO_SHADING_MAP_SIZE));
+                        ids[i]), allKeys);
+                assertTrue("Key not in keys list: android.lens.info.focusDistanceCalibration", allKeys.contains(
+                        CameraCharacteristics.LENS_INFO_FOCUS_DISTANCE_CALIBRATION));
 
             }
 
         }
     }
 
-    public void testCameraCharacteristicsAndroidRequestMaxNumOutputStreams() throws Exception {
+    public void testCameraCharacteristicsAndroidNoiseReductionAvailableNoiseReductionModes() throws Exception {
         String[] ids = mCameraManager.getCameraIdList();
         for (int i = 0; i < ids.length; i++) {
             CameraCharacteristics props = mCameraManager.getCameraCharacteristics(ids[i]);
@@ -526,21 +646,20 @@
 
             {
 
-                assertNotNull("Invalid property: android.request.maxNumOutputStreams",
-                        props.get(CameraCharacteristics.REQUEST_MAX_NUM_OUTPUT_STREAMS));
-
+                assertNotNull("Invalid property: android.noiseReduction.availableNoiseReductionModes",
+                        props.get(CameraCharacteristics.NOISE_REDUCTION_AVAILABLE_NOISE_REDUCTION_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.request.maxNumOutputStreams", allKeys.contains(
-                        CameraCharacteristics.REQUEST_MAX_NUM_OUTPUT_STREAMS));
+                        ids[i]), allKeys);
+                assertTrue("Key not in keys list: android.noiseReduction.availableNoiseReductionModes", allKeys.contains(
+                        CameraCharacteristics.NOISE_REDUCTION_AVAILABLE_NOISE_REDUCTION_MODES));
 
             }
 
         }
     }
 
-    public void testCameraCharacteristicsAndroidScalerAvailableFormats() throws Exception {
+    public void testCameraCharacteristicsAndroidRequestMaxNumOutputRaw() throws Exception {
         String[] ids = mCameraManager.getCameraIdList();
         for (int i = 0; i < ids.length; i++) {
             CameraCharacteristics props = mCameraManager.getCameraCharacteristics(ids[i]);
@@ -549,21 +668,20 @@
 
             {
 
-                assertNotNull("Invalid property: android.scaler.availableFormats",
-                        props.get(CameraCharacteristics.SCALER_AVAILABLE_FORMATS));
-
+                assertNotNull("Invalid property: android.request.maxNumOutputRaw",
+                        props.get(CameraCharacteristics.REQUEST_MAX_NUM_OUTPUT_RAW));
                 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.scaler.availableFormats", allKeys.contains(
-                        CameraCharacteristics.SCALER_AVAILABLE_FORMATS));
+                        ids[i]), allKeys);
+                assertTrue("Key not in keys list: android.request.maxNumOutputRaw", allKeys.contains(
+                        CameraCharacteristics.REQUEST_MAX_NUM_OUTPUT_RAW));
 
             }
 
         }
     }
 
-    public void testCameraCharacteristicsAndroidScalerAvailableJpegMinDurations() throws Exception {
+    public void testCameraCharacteristicsAndroidRequestMaxNumOutputProc() throws Exception {
         String[] ids = mCameraManager.getCameraIdList();
         for (int i = 0; i < ids.length; i++) {
             CameraCharacteristics props = mCameraManager.getCameraCharacteristics(ids[i]);
@@ -572,21 +690,20 @@
 
             {
 
-                assertNotNull("Invalid property: android.scaler.availableJpegMinDurations",
-                        props.get(CameraCharacteristics.SCALER_AVAILABLE_JPEG_MIN_DURATIONS));
-
+                assertNotNull("Invalid property: android.request.maxNumOutputProc",
+                        props.get(CameraCharacteristics.REQUEST_MAX_NUM_OUTPUT_PROC));
                 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.scaler.availableJpegMinDurations", allKeys.contains(
-                        CameraCharacteristics.SCALER_AVAILABLE_JPEG_MIN_DURATIONS));
+                        ids[i]), allKeys);
+                assertTrue("Key not in keys list: android.request.maxNumOutputProc", allKeys.contains(
+                        CameraCharacteristics.REQUEST_MAX_NUM_OUTPUT_PROC));
 
             }
 
         }
     }
 
-    public void testCameraCharacteristicsAndroidScalerAvailableJpegSizes() throws Exception {
+    public void testCameraCharacteristicsAndroidRequestMaxNumOutputProcStalling() throws Exception {
         String[] ids = mCameraManager.getCameraIdList();
         for (int i = 0; i < ids.length; i++) {
             CameraCharacteristics props = mCameraManager.getCameraCharacteristics(ids[i]);
@@ -595,14 +712,83 @@
 
             {
 
-                assertNotNull("Invalid property: android.scaler.availableJpegSizes",
-                        props.get(CameraCharacteristics.SCALER_AVAILABLE_JPEG_SIZES));
-
+                assertNotNull("Invalid property: android.request.maxNumOutputProcStalling",
+                        props.get(CameraCharacteristics.REQUEST_MAX_NUM_OUTPUT_PROC_STALLING));
                 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.scaler.availableJpegSizes", allKeys.contains(
-                        CameraCharacteristics.SCALER_AVAILABLE_JPEG_SIZES));
+                        ids[i]), allKeys);
+                assertTrue("Key not in keys list: android.request.maxNumOutputProcStalling", allKeys.contains(
+                        CameraCharacteristics.REQUEST_MAX_NUM_OUTPUT_PROC_STALLING));
+
+            }
+
+        }
+    }
+
+    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]), allKeys);
+                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);
+
+            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.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]), allKeys);
+                assertTrue("Key not in keys list: android.request.partialResultCount", allKeys.contains(
+                        CameraCharacteristics.REQUEST_PARTIAL_RESULT_COUNT));
+
+            }
+
+        }
+    }
+
+    public void testCameraCharacteristicsAndroidRequestAvailableCapabilities() 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.availableCapabilities",
+                        props.get(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES));
+                List<Key<?>> allKeys = props.getKeys();
+                assertNotNull(String.format("Can't get camera characteristics keys from: ID %s",
+                        ids[i]), allKeys);
+                assertTrue("Key not in keys list: android.request.availableCapabilities", allKeys.contains(
+                        CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES));
 
             }
 
@@ -620,10 +806,9 @@
 
                 assertNotNull("Invalid property: android.scaler.availableMaxDigitalZoom",
                         props.get(CameraCharacteristics.SCALER_AVAILABLE_MAX_DIGITAL_ZOOM));
-
                 List<Key<?>> allKeys = props.getKeys();
                 assertNotNull(String.format("Can't get camera characteristics keys from: ID %s",
-                        ids[i], props));
+                        ids[i]), allKeys);
                 assertTrue("Key not in keys list: android.scaler.availableMaxDigitalZoom", allKeys.contains(
                         CameraCharacteristics.SCALER_AVAILABLE_MAX_DIGITAL_ZOOM));
 
@@ -632,7 +817,7 @@
         }
     }
 
-    public void testCameraCharacteristicsAndroidScalerAvailableProcessedMinDurations() throws Exception {
+    public void testCameraCharacteristicsAndroidScalerStreamConfigurationMap() throws Exception {
         String[] ids = mCameraManager.getCameraIdList();
         for (int i = 0; i < ids.length; i++) {
             CameraCharacteristics props = mCameraManager.getCameraCharacteristics(ids[i]);
@@ -641,21 +826,20 @@
 
             {
 
-                assertNotNull("Invalid property: android.scaler.availableProcessedMinDurations",
-                        props.get(CameraCharacteristics.SCALER_AVAILABLE_PROCESSED_MIN_DURATIONS));
-
+                assertNotNull("Invalid property: android.scaler.streamConfigurationMap",
+                        props.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP));
                 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.scaler.availableProcessedMinDurations", allKeys.contains(
-                        CameraCharacteristics.SCALER_AVAILABLE_PROCESSED_MIN_DURATIONS));
+                        ids[i]), allKeys);
+                assertTrue("Key not in keys list: android.scaler.streamConfigurationMap", allKeys.contains(
+                        CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP));
 
             }
 
         }
     }
 
-    public void testCameraCharacteristicsAndroidScalerAvailableProcessedSizes() throws Exception {
+    public void testCameraCharacteristicsAndroidScalerCroppingType() throws Exception {
         String[] ids = mCameraManager.getCameraIdList();
         for (int i = 0; i < ids.length; i++) {
             CameraCharacteristics props = mCameraManager.getCameraCharacteristics(ids[i]);
@@ -664,21 +848,64 @@
 
             {
 
-                assertNotNull("Invalid property: android.scaler.availableProcessedSizes",
-                        props.get(CameraCharacteristics.SCALER_AVAILABLE_PROCESSED_SIZES));
-
+                assertNotNull("Invalid property: android.scaler.croppingType",
+                        props.get(CameraCharacteristics.SCALER_CROPPING_TYPE));
                 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.scaler.availableProcessedSizes", allKeys.contains(
-                        CameraCharacteristics.SCALER_AVAILABLE_PROCESSED_SIZES));
+                        ids[i]), allKeys);
+                assertTrue("Key not in keys list: android.scaler.croppingType", allKeys.contains(
+                        CameraCharacteristics.SCALER_CROPPING_TYPE));
 
             }
 
         }
     }
 
-    public void testCameraCharacteristicsAndroidSensorBaseGainFactor() throws Exception {
+    public void testCameraCharacteristicsAndroidSensorReferenceIlluminant1() 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.sensor.referenceIlluminant1",
+                        props.get(CameraCharacteristics.SENSOR_REFERENCE_ILLUMINANT1));
+                List<Key<?>> allKeys = props.getKeys();
+                assertNotNull(String.format("Can't get camera characteristics keys from: ID %s",
+                        ids[i]), allKeys);
+                assertTrue("Key not in keys list: android.sensor.referenceIlluminant1", allKeys.contains(
+                        CameraCharacteristics.SENSOR_REFERENCE_ILLUMINANT1));
+
+            }
+
+        }
+    }
+
+    public void testCameraCharacteristicsAndroidSensorReferenceIlluminant2() 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.sensor.referenceIlluminant2",
+                        props.get(CameraCharacteristics.SENSOR_REFERENCE_ILLUMINANT2));
+                List<Key<?>> allKeys = props.getKeys();
+                assertNotNull(String.format("Can't get camera characteristics keys from: ID %s",
+                        ids[i]), allKeys);
+                assertTrue("Key not in keys list: android.sensor.referenceIlluminant2", allKeys.contains(
+                        CameraCharacteristics.SENSOR_REFERENCE_ILLUMINANT2));
+
+            }
+
+        }
+    }
+
+    public void testCameraCharacteristicsAndroidSensorCalibrationTransform1() throws Exception {
         String[] ids = mCameraManager.getCameraIdList();
         for (int i = 0; i < ids.length; i++) {
             CameraCharacteristics props = mCameraManager.getCameraCharacteristics(ids[i]);
@@ -691,14 +918,169 @@
             if (hwLevel == CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL)
             {
 
-                assertNotNull("Invalid property: android.sensor.baseGainFactor",
-                        props.get(CameraCharacteristics.SENSOR_BASE_GAIN_FACTOR));
-
+                assertNotNull("Invalid property: android.sensor.calibrationTransform1",
+                        props.get(CameraCharacteristics.SENSOR_CALIBRATION_TRANSFORM1));
                 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.baseGainFactor", allKeys.contains(
-                        CameraCharacteristics.SENSOR_BASE_GAIN_FACTOR));
+                        ids[i]), allKeys);
+                assertTrue("Key not in keys list: android.sensor.calibrationTransform1", allKeys.contains(
+                        CameraCharacteristics.SENSOR_CALIBRATION_TRANSFORM1));
+
+            }
+
+        }
+    }
+
+    public void testCameraCharacteristicsAndroidSensorCalibrationTransform2() 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.calibrationTransform2",
+                        props.get(CameraCharacteristics.SENSOR_CALIBRATION_TRANSFORM2));
+                List<Key<?>> allKeys = props.getKeys();
+                assertNotNull(String.format("Can't get camera characteristics keys from: ID %s",
+                        ids[i]), allKeys);
+                assertTrue("Key not in keys list: android.sensor.calibrationTransform2", allKeys.contains(
+                        CameraCharacteristics.SENSOR_CALIBRATION_TRANSFORM2));
+
+            }
+
+        }
+    }
+
+    public void testCameraCharacteristicsAndroidSensorColorTransform1() 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.colorTransform1",
+                        props.get(CameraCharacteristics.SENSOR_COLOR_TRANSFORM1));
+                List<Key<?>> allKeys = props.getKeys();
+                assertNotNull(String.format("Can't get camera characteristics keys from: ID %s",
+                        ids[i]), allKeys);
+                assertTrue("Key not in keys list: android.sensor.colorTransform1", allKeys.contains(
+                        CameraCharacteristics.SENSOR_COLOR_TRANSFORM1));
+
+            }
+
+        }
+    }
+
+    public void testCameraCharacteristicsAndroidSensorColorTransform2() 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.colorTransform2",
+                        props.get(CameraCharacteristics.SENSOR_COLOR_TRANSFORM2));
+                List<Key<?>> allKeys = props.getKeys();
+                assertNotNull(String.format("Can't get camera characteristics keys from: ID %s",
+                        ids[i]), allKeys);
+                assertTrue("Key not in keys list: android.sensor.colorTransform2", allKeys.contains(
+                        CameraCharacteristics.SENSOR_COLOR_TRANSFORM2));
+
+            }
+
+        }
+    }
+
+    public void testCameraCharacteristicsAndroidSensorForwardMatrix1() 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.forwardMatrix1",
+                        props.get(CameraCharacteristics.SENSOR_FORWARD_MATRIX1));
+                List<Key<?>> allKeys = props.getKeys();
+                assertNotNull(String.format("Can't get camera characteristics keys from: ID %s",
+                        ids[i]), allKeys);
+                assertTrue("Key not in keys list: android.sensor.forwardMatrix1", allKeys.contains(
+                        CameraCharacteristics.SENSOR_FORWARD_MATRIX1));
+
+            }
+
+        }
+    }
+
+    public void testCameraCharacteristicsAndroidSensorForwardMatrix2() 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.forwardMatrix2",
+                        props.get(CameraCharacteristics.SENSOR_FORWARD_MATRIX2));
+                List<Key<?>> allKeys = props.getKeys();
+                assertNotNull(String.format("Can't get camera characteristics keys from: ID %s",
+                        ids[i]), allKeys);
+                assertTrue("Key not in keys list: android.sensor.forwardMatrix2", allKeys.contains(
+                        CameraCharacteristics.SENSOR_FORWARD_MATRIX2));
+
+            }
+
+        }
+    }
+
+    public void testCameraCharacteristicsAndroidSensorBlackLevelPattern() 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.blackLevelPattern",
+                        props.get(CameraCharacteristics.SENSOR_BLACK_LEVEL_PATTERN));
+                List<Key<?>> allKeys = props.getKeys();
+                assertNotNull(String.format("Can't get camera characteristics keys from: ID %s",
+                        ids[i]), allKeys);
+                assertTrue("Key not in keys list: android.sensor.blackLevelPattern", allKeys.contains(
+                        CameraCharacteristics.SENSOR_BLACK_LEVEL_PATTERN));
 
             }
 
@@ -720,10 +1102,9 @@
 
                 assertNotNull("Invalid property: android.sensor.maxAnalogSensitivity",
                         props.get(CameraCharacteristics.SENSOR_MAX_ANALOG_SENSITIVITY));
-
                 List<Key<?>> allKeys = props.getKeys();
                 assertNotNull(String.format("Can't get camera characteristics keys from: ID %s",
-                        ids[i], props));
+                        ids[i]), allKeys);
                 assertTrue("Key not in keys list: android.sensor.maxAnalogSensitivity", allKeys.contains(
                         CameraCharacteristics.SENSOR_MAX_ANALOG_SENSITIVITY));
 
@@ -743,10 +1124,9 @@
 
                 assertNotNull("Invalid property: android.sensor.orientation",
                         props.get(CameraCharacteristics.SENSOR_ORIENTATION));
-
                 List<Key<?>> allKeys = props.getKeys();
                 assertNotNull(String.format("Can't get camera characteristics keys from: ID %s",
-                        ids[i], props));
+                        ids[i]), allKeys);
                 assertTrue("Key not in keys list: android.sensor.orientation", allKeys.contains(
                         CameraCharacteristics.SENSOR_ORIENTATION));
 
@@ -755,6 +1135,32 @@
         }
     }
 
+    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]), allKeys);
+                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++) {
@@ -766,10 +1172,9 @@
 
                 assertNotNull("Invalid property: android.sensor.info.activeArraySize",
                         props.get(CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE));
-
                 List<Key<?>> allKeys = props.getKeys();
                 assertNotNull(String.format("Can't get camera characteristics keys from: ID %s",
-                        ids[i], props));
+                        ids[i]), allKeys);
                 assertTrue("Key not in keys list: android.sensor.info.activeArraySize", allKeys.contains(
                         CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE));
 
@@ -789,10 +1194,9 @@
 
                 assertNotNull("Invalid property: android.sensor.info.sensitivityRange",
                         props.get(CameraCharacteristics.SENSOR_INFO_SENSITIVITY_RANGE));
-
                 List<Key<?>> allKeys = props.getKeys();
                 assertNotNull(String.format("Can't get camera characteristics keys from: ID %s",
-                        ids[i], props));
+                        ids[i]), allKeys);
                 assertTrue("Key not in keys list: android.sensor.info.sensitivityRange", allKeys.contains(
                         CameraCharacteristics.SENSOR_INFO_SENSITIVITY_RANGE));
 
@@ -801,6 +1205,28 @@
         }
     }
 
+    public void testCameraCharacteristicsAndroidSensorInfoColorFilterArrangement() 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.sensor.info.colorFilterArrangement",
+                        props.get(CameraCharacteristics.SENSOR_INFO_COLOR_FILTER_ARRANGEMENT));
+                List<Key<?>> allKeys = props.getKeys();
+                assertNotNull(String.format("Can't get camera characteristics keys from: ID %s",
+                        ids[i]), allKeys);
+                assertTrue("Key not in keys list: android.sensor.info.colorFilterArrangement", allKeys.contains(
+                        CameraCharacteristics.SENSOR_INFO_COLOR_FILTER_ARRANGEMENT));
+
+            }
+
+        }
+    }
+
     public void testCameraCharacteristicsAndroidSensorInfoExposureTimeRange() throws Exception {
         String[] ids = mCameraManager.getCameraIdList();
         for (int i = 0; i < ids.length; i++) {
@@ -812,10 +1238,9 @@
 
                 assertNotNull("Invalid property: android.sensor.info.exposureTimeRange",
                         props.get(CameraCharacteristics.SENSOR_INFO_EXPOSURE_TIME_RANGE));
-
                 List<Key<?>> allKeys = props.getKeys();
                 assertNotNull(String.format("Can't get camera characteristics keys from: ID %s",
-                        ids[i], props));
+                        ids[i]), allKeys);
                 assertTrue("Key not in keys list: android.sensor.info.exposureTimeRange", allKeys.contains(
                         CameraCharacteristics.SENSOR_INFO_EXPOSURE_TIME_RANGE));
 
@@ -835,10 +1260,9 @@
 
                 assertNotNull("Invalid property: android.sensor.info.maxFrameDuration",
                         props.get(CameraCharacteristics.SENSOR_INFO_MAX_FRAME_DURATION));
-
                 List<Key<?>> allKeys = props.getKeys();
                 assertNotNull(String.format("Can't get camera characteristics keys from: ID %s",
-                        ids[i], props));
+                        ids[i]), allKeys);
                 assertTrue("Key not in keys list: android.sensor.info.maxFrameDuration", allKeys.contains(
                         CameraCharacteristics.SENSOR_INFO_MAX_FRAME_DURATION));
 
@@ -858,10 +1282,9 @@
 
                 assertNotNull("Invalid property: android.sensor.info.physicalSize",
                         props.get(CameraCharacteristics.SENSOR_INFO_PHYSICAL_SIZE));
-
                 List<Key<?>> allKeys = props.getKeys();
                 assertNotNull(String.format("Can't get camera characteristics keys from: ID %s",
-                        ids[i], props));
+                        ids[i]), allKeys);
                 assertTrue("Key not in keys list: android.sensor.info.physicalSize", allKeys.contains(
                         CameraCharacteristics.SENSOR_INFO_PHYSICAL_SIZE));
 
@@ -870,6 +1293,72 @@
         }
     }
 
+    public void testCameraCharacteristicsAndroidSensorInfoPixelArraySize() 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.sensor.info.pixelArraySize",
+                        props.get(CameraCharacteristics.SENSOR_INFO_PIXEL_ARRAY_SIZE));
+                List<Key<?>> allKeys = props.getKeys();
+                assertNotNull(String.format("Can't get camera characteristics keys from: ID %s",
+                        ids[i]), allKeys);
+                assertTrue("Key not in keys list: android.sensor.info.pixelArraySize", allKeys.contains(
+                        CameraCharacteristics.SENSOR_INFO_PIXEL_ARRAY_SIZE));
+
+            }
+
+        }
+    }
+
+    public void testCameraCharacteristicsAndroidSensorInfoWhiteLevel() 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.sensor.info.whiteLevel",
+                        props.get(CameraCharacteristics.SENSOR_INFO_WHITE_LEVEL));
+                List<Key<?>> allKeys = props.getKeys();
+                assertNotNull(String.format("Can't get camera characteristics keys from: ID %s",
+                        ids[i]), allKeys);
+                assertTrue("Key not in keys list: android.sensor.info.whiteLevel", allKeys.contains(
+                        CameraCharacteristics.SENSOR_INFO_WHITE_LEVEL));
+
+            }
+
+        }
+    }
+
+    public void testCameraCharacteristicsAndroidSensorInfoTimestampSource() 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.sensor.info.timestampSource",
+                        props.get(CameraCharacteristics.SENSOR_INFO_TIMESTAMP_SOURCE));
+                List<Key<?>> allKeys = props.getKeys();
+                assertNotNull(String.format("Can't get camera characteristics keys from: ID %s",
+                        ids[i]), allKeys);
+                assertTrue("Key not in keys list: android.sensor.info.timestampSource", allKeys.contains(
+                        CameraCharacteristics.SENSOR_INFO_TIMESTAMP_SOURCE));
+
+            }
+
+        }
+    }
+
     public void testCameraCharacteristicsAndroidStatisticsInfoAvailableFaceDetectModes() throws Exception {
         String[] ids = mCameraManager.getCameraIdList();
         for (int i = 0; i < ids.length; i++) {
@@ -881,10 +1370,9 @@
 
                 assertNotNull("Invalid property: android.statistics.info.availableFaceDetectModes",
                         props.get(CameraCharacteristics.STATISTICS_INFO_AVAILABLE_FACE_DETECT_MODES));
-
                 List<Key<?>> allKeys = props.getKeys();
                 assertNotNull(String.format("Can't get camera characteristics keys from: ID %s",
-                        ids[i], props));
+                        ids[i]), allKeys);
                 assertTrue("Key not in keys list: android.statistics.info.availableFaceDetectModes", allKeys.contains(
                         CameraCharacteristics.STATISTICS_INFO_AVAILABLE_FACE_DETECT_MODES));
 
@@ -904,10 +1392,9 @@
 
                 assertNotNull("Invalid property: android.statistics.info.maxFaceCount",
                         props.get(CameraCharacteristics.STATISTICS_INFO_MAX_FACE_COUNT));
-
                 List<Key<?>> allKeys = props.getKeys();
                 assertNotNull(String.format("Can't get camera characteristics keys from: ID %s",
-                        ids[i], props));
+                        ids[i]), allKeys);
                 assertTrue("Key not in keys list: android.statistics.info.maxFaceCount", allKeys.contains(
                         CameraCharacteristics.STATISTICS_INFO_MAX_FACE_COUNT));
 
@@ -916,6 +1403,28 @@
         }
     }
 
+    public void testCameraCharacteristicsAndroidStatisticsInfoAvailableHotPixelMapModes() 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.statistics.info.availableHotPixelMapModes",
+                        props.get(CameraCharacteristics.STATISTICS_INFO_AVAILABLE_HOT_PIXEL_MAP_MODES));
+                List<Key<?>> allKeys = props.getKeys();
+                assertNotNull(String.format("Can't get camera characteristics keys from: ID %s",
+                        ids[i]), allKeys);
+                assertTrue("Key not in keys list: android.statistics.info.availableHotPixelMapModes", allKeys.contains(
+                        CameraCharacteristics.STATISTICS_INFO_AVAILABLE_HOT_PIXEL_MAP_MODES));
+
+            }
+
+        }
+    }
+
     public void testCameraCharacteristicsAndroidTonemapMaxCurvePoints() throws Exception {
         String[] ids = mCameraManager.getCameraIdList();
         for (int i = 0; i < ids.length; i++) {
@@ -927,10 +1436,9 @@
 
                 assertNotNull("Invalid property: android.tonemap.maxCurvePoints",
                         props.get(CameraCharacteristics.TONEMAP_MAX_CURVE_POINTS));
-
                 List<Key<?>> allKeys = props.getKeys();
                 assertNotNull(String.format("Can't get camera characteristics keys from: ID %s",
-                        ids[i], props));
+                        ids[i]), allKeys);
                 assertTrue("Key not in keys list: android.tonemap.maxCurvePoints", allKeys.contains(
                         CameraCharacteristics.TONEMAP_MAX_CURVE_POINTS));
 
@@ -939,6 +1447,28 @@
         }
     }
 
+    public void testCameraCharacteristicsAndroidTonemapAvailableToneMapModes() 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.tonemap.availableToneMapModes",
+                        props.get(CameraCharacteristics.TONEMAP_AVAILABLE_TONE_MAP_MODES));
+                List<Key<?>> allKeys = props.getKeys();
+                assertNotNull(String.format("Can't get camera characteristics keys from: ID %s",
+                        ids[i]), allKeys);
+                assertTrue("Key not in keys list: android.tonemap.availableToneMapModes", allKeys.contains(
+                        CameraCharacteristics.TONEMAP_AVAILABLE_TONE_MAP_MODES));
+
+            }
+
+        }
+    }
+
     public void testCameraCharacteristicsAndroidInfoSupportedHardwareLevel() throws Exception {
         String[] ids = mCameraManager.getCameraIdList();
         for (int i = 0; i < ids.length; i++) {
@@ -950,10 +1480,9 @@
 
                 assertNotNull("Invalid property: android.info.supportedHardwareLevel",
                         props.get(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL));
-
                 List<Key<?>> allKeys = props.getKeys();
                 assertNotNull(String.format("Can't get camera characteristics keys from: ID %s",
-                        ids[i], props));
+                        ids[i]), allKeys);
                 assertTrue("Key not in keys list: android.info.supportedHardwareLevel", allKeys.contains(
                         CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL));
 
@@ -961,5 +1490,27 @@
 
         }
     }
+
+    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]), allKeys);
+                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..bd35387 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/CameraDeviceTest.java
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/CameraDeviceTest.java
@@ -18,65 +18,59 @@
 
 import static android.hardware.camera2.cts.CameraTestUtils.*;
 import static com.android.ex.camera2.blocking.BlockingStateListener.*;
+import static com.android.ex.camera2.blocking.BlockingSessionListener.*;
 import static org.mockito.Mockito.*;
+import static android.hardware.camera2.CaptureRequest.*;
 
 import android.content.Context;
 import android.graphics.ImageFormat;
+import android.hardware.camera2.CameraAccessException;
+import android.hardware.camera2.CameraCaptureSession;
 import android.hardware.camera2.CameraCharacteristics;
 import android.hardware.camera2.CameraDevice;
-import android.hardware.camera2.CameraManager;
-import android.hardware.camera2.CameraMetadata;
 import android.hardware.camera2.CaptureFailure;
 import android.hardware.camera2.CaptureRequest;
 import android.hardware.camera2.CaptureResult;
-import android.media.Image;
-import android.media.ImageReader;
+import android.hardware.camera2.TotalCaptureResult;
+import android.hardware.camera2.cts.testcases.Camera2AndroidTestCase;
 import android.os.Handler;
 import android.os.SystemClock;
-import android.test.AndroidTestCase;
 import android.util.Log;
+import android.util.Range;
 import android.view.Surface;
 
+import com.android.ex.camera2.blocking.BlockingSessionListener;
 import com.android.ex.camera2.blocking.BlockingStateListener;
+import com.android.ex.camera2.utils.StateWaiter;
+
 import org.mockito.ArgumentMatcher;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.List;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.TimeUnit;
 
 /**
  * <p>Basic test for CameraDevice APIs.</p>
  */
-public class CameraDeviceTest extends AndroidTestCase {
+public class CameraDeviceTest extends Camera2AndroidTestCase {
     private static final String TAG = "CameraDeviceTest";
     private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
-
-    private CameraManager mCameraManager;
-    private BlockingStateListener mCameraListener;
-    private CameraTestThread mLooperThread;
-    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;
-    private static final int CAPTURE_WAIT_TIMEOUT_MS = 2000;
     private static final int ERROR_LISTENER_WAIT_TIMEOUT_MS = 1000;
     private static final int REPEATING_CAPTURE_EXPECTED_RESULT_COUNT = 5;
-    // VGA size capture is required by CDD.
-    private static final int DEFAULT_CAPTURE_WIDTH = 640;
-    private static final int DEFAULT_CAPTURE_HEIGHT = 480;
     private static final int MAX_NUM_IMAGES = 5;
+    private static final int MIN_FPS_REQUIRED_FOR_STREAMING = 20;
 
-    private static int[] mTemplates = new int[] {
+    private CameraCaptureSession mSession;
+
+    private BlockingStateListener mCameraMockListener;
+    private int mLatestDeviceState = STATE_UNINITIALIZED;
+    private BlockingSessionListener mSessionMockListener;
+    private StateWaiter mSessionWaiter;
+    private int mLatestSessionState = -1; // uninitialized
+
+    private static int[] sTemplates = new int[] {
             CameraDevice.TEMPLATE_PREVIEW,
             CameraDevice.TEMPLATE_RECORD,
             CameraDevice.TEMPLATE_STILL_CAPTURE,
@@ -86,19 +80,21 @@
     @Override
     public void setContext(Context context) {
         super.setContext(context);
+
         /**
          * 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());
+        System.setProperty("dexmaker.dexcache", getContext().getCacheDir().toString());
+
         /**
-         * Create errorlistener in context scope, to catch asynchronous device error.
+         * Create error listener in context scope, to catch asynchronous device error.
          * Use spy object here since we want to use the SimpleDeviceListener callback
          * implementation (spy doesn't stub the functions unless we ask it to do so).
          */
-        mCameraListener = spy(new BlockingStateListener());
+        mCameraMockListener = spy(new BlockingStateListener());
     }
 
     @Override
@@ -110,113 +106,582 @@
          * fail the rest of the tests. This is especially needed when error
          * callback is fired too late.
          */
-        verify(mCameraListener, never())
+        verify(mCameraMockListener, never())
                 .onError(
                     any(CameraDevice.class),
                     anyInt());
-        verify(mCameraListener, never())
+        verify(mCameraMockListener, never())
                 .onDisconnected(
                     any(CameraDevice.class));
 
-        mCameraManager = (CameraManager)mContext.getSystemService(Context.CAMERA_SERVICE);
-        assertNotNull("Can't connect to camera manager", mCameraManager);
-        createDefaultSurface();
-        mLooperThread = new CameraTestThread();
-        mCallbackHandler = mLooperThread.start();
+        mCameraListener = mCameraMockListener;
+        createDefaultImageReader(DEFAULT_CAPTURE_SIZE, ImageFormat.YUV_420_888, MAX_NUM_IMAGES,
+                new ImageDropperListener());
     }
 
     @Override
     protected void tearDown() throws Exception {
-        mDummyThread.close();
-        mReader.close();
         super.tearDown();
     }
 
-    public void testCameraDeviceCreateCaptureBuilder() throws Exception {
-        String[] ids = mCameraManager.getCameraIdList();
-        for (int i = 0; i < ids.length; i++) {
-            CameraDevice camera = null;
-            try {
-                camera = CameraTestUtils.openCamera(mCameraManager, ids[i], mCallbackHandler);
-                assertNotNull(
-                        String.format("Failed to open camera device ID: %s", ids[i]), camera);
+    /**
+     * <p>
+     * Test camera capture request preview capture template.
+     * </p>
+     *
+     * <p>
+     * The request template returned by the camera device must include a
+     * necessary set of metadata keys, and their values must be set correctly.
+     * It mainly requires below settings:
+     * </p>
+     * <ul>
+     * <li>All 3A settings are auto.</li>
+     * <li>All sensor settings are not null.</li>
+     * <li>All ISP processing settings should be non-manual, and the camera
+     * device should make sure the stable frame rate is guaranteed for the given
+     * settings.</li>
+     * </ul>
+     */
+    public void testCameraDevicePreviewTemplate() throws Exception {
+        for (int i = 0; i < mCameraIds.length; i++) {
+            captureTemplateTestByCamera(mCameraIds[i], CameraDevice.TEMPLATE_PREVIEW);
+        }
 
+        // TODO: test the frame rate sustainability in preview use case test.
+    }
+
+    /**
+     * <p>
+     * Test camera capture request still capture template.
+     * </p>
+     *
+     * <p>
+     * The request template returned by the camera device must include a
+     * necessary set of metadata keys, and their values must be set correctly.
+     * It mainly requires below settings:
+     * </p>
+     * <ul>
+     * <li>All 3A settings are auto.</li>
+     * <li>All sensor settings are not null.</li>
+     * <li>All ISP processing settings should be non-manual, and the camera
+     * device should make sure the high quality takes priority to the stable
+     * frame rate for the given settings.</li>
+     * </ul>
+     */
+    public void testCameraDeviceStillTemplate() throws Exception {
+        for (int i = 0; i < mCameraIds.length; i++) {
+            captureTemplateTestByCamera(mCameraIds[i], CameraDevice.TEMPLATE_STILL_CAPTURE);
+        }
+    }
+
+    /**
+     * <p>
+     * Test camera capture video recording template.
+     * </p>
+     *
+     * <p>
+     * The request template returned by the camera device must include a
+     * necessary set of metadata keys, and their values must be set correctly.
+     * It has the similar requirement as preview, with one difference:
+     * </p>
+     * <ul>
+     * <li>Frame rate should be stable, for example, wide fps range like [7, 30]
+     * is a bad setting.</li>
+     */
+    public void testCameraDeviceRecordingTemplate() throws Exception {
+        for (int i = 0; i < mCameraIds.length; i++) {
+            captureTemplateTestByCamera(mCameraIds[i], CameraDevice.TEMPLATE_RECORD);
+        }
+
+        // TODO: test the frame rate sustainability in recording use case test.
+    }
+
+    /**
+     *<p>Test camera capture video snapshot template.</p>
+     *
+     * <p>The request template returned by the camera device must include a necessary set of
+     * metadata keys, and their values must be set correctly. It has the similar requirement
+     * as recording, with an additional requirement: the settings should maximize image quality
+     * without compromising stable frame rate.</p>
+     */
+    public void testCameraDeviceVideoSnapShotTemplate() throws Exception {
+        for (int i = 0; i < mCameraIds.length; i++) {
+            captureTemplateTestByCamera(mCameraIds[i], CameraDevice.TEMPLATE_VIDEO_SNAPSHOT);
+        }
+
+        // TODO: test the frame rate sustainability in video snapshot use case test.
+    }
+
+    /**
+     *<p>Test camera capture request zero shutter lag template.</p>
+     *
+     * <p>The request template returned by the camera device must include a necessary set of
+     * metadata keys, and their values must be set correctly. It has the similar requirement
+     * as preview, with an additional requirement: </p>
+     */
+    public void testCameraDeviceZSLTemplate() throws Exception {
+        for (int i = 0; i < mCameraIds.length; i++) {
+            captureTemplateTestByCamera(mCameraIds[i], CameraDevice.TEMPLATE_ZERO_SHUTTER_LAG);
+        }
+    }
+
+    /**
+     * <p>
+     * Test camera capture request manual template.
+     * </p>
+     *
+     * <p>
+     * The request template returned by the camera device must include a
+     * necessary set of metadata keys, and their values must be set correctly. It
+     * mainly requires below settings:
+     * </p>
+     * <ul>
+     * <li>All 3A settings are manual.</li>
+     * <li>ISP processing parameters are set to preview quality.</li>
+     * <li>The manual capture parameters (exposure, sensitivity, and so on) are
+     * set to reasonable defaults.</li>
+     * </ul>
+     */
+    public void testCameraDeviceManualTemplate() throws Exception {
+        for (int i = 0; i < mCameraIds.length; i++) {
+            captureTemplateTestByCamera(mCameraIds[i], CameraDevice.TEMPLATE_MANUAL);
+        }
+    }
+
+    public void testCameraDeviceCreateCaptureBuilder() throws Exception {
+        for (int i = 0; i < mCameraIds.length; i++) {
+            try {
+                openDevice(mCameraIds[i], mCameraMockListener);
                 /**
                  * Test: that each template type is supported, and that its required fields are
                  * present.
                  */
-                for (int j = 0; j < mTemplates.length; j++) {
-                    CaptureRequest.Builder capReq = camera.createCaptureRequest(mTemplates[j]);
+                for (int j = 0; j < sTemplates.length; j++) {
+                    // Skip video snapshots for LEGACY mode
+                    if (sTemplates[j] == CameraDevice.TEMPLATE_VIDEO_SNAPSHOT) {
+                        continue;
+                    }
+                    CaptureRequest.Builder capReq = mCamera.createCaptureRequest(sTemplates[j]);
                     assertNotNull("Failed to create capture request", capReq);
-                    assertNotNull("Missing field: SENSOR_EXPOSURE_TIME",
-                            capReq.get(CaptureRequest.SENSOR_EXPOSURE_TIME));
-                    assertNotNull("Missing field: SENSOR_SENSITIVITY",
-                            capReq.get(CaptureRequest.SENSOR_SENSITIVITY));
-
-                    // TODO: Add more tests to check more fields.
+                    if (mStaticInfo.areKeysAvailable(CaptureRequest.SENSOR_EXPOSURE_TIME)) {
+                        assertNotNull("Missing field: SENSOR_EXPOSURE_TIME",
+                                capReq.get(CaptureRequest.SENSOR_EXPOSURE_TIME));
+                    }
+                    if (mStaticInfo.areKeysAvailable(CaptureRequest.SENSOR_SENSITIVITY)) {
+                        assertNotNull("Missing field: SENSOR_SENSITIVITY",
+                                capReq.get(CaptureRequest.SENSOR_SENSITIVITY));
+                    }
                 }
             }
             finally {
-                if (camera != null) {
-                    camera.close();
+                try {
+                    closeSession();
+                } finally {
+                    closeDevice(mCameraIds[i], mCameraMockListener);
                 }
             }
         }
     }
 
     public void testCameraDeviceSetErrorListener() throws Exception {
-        String[] ids = mCameraManager.getCameraIdList();
-        for (int i = 0; i < ids.length; i++) {
-            CameraDevice camera = null;
+        for (int i = 0; i < mCameraIds.length; i++) {
             try {
-                camera = CameraTestUtils.openCamera(mCameraManager, ids[i],
-                        mCameraListener, mCallbackHandler);
-                assertNotNull(
-                        String.format("Failed to open camera device %s", ids[i]), camera);
-
+                openDevice(mCameraIds[i], mCameraMockListener);
                 /**
                  * Test: that the error listener can be set without problems.
                  * Also, wait some time to check if device doesn't run into error.
                  */
                 SystemClock.sleep(ERROR_LISTENER_WAIT_TIMEOUT_MS);
-                verify(mCameraListener, never())
+                verify(mCameraMockListener, never())
                         .onError(
                                 any(CameraDevice.class),
                                 anyInt());
             }
             finally {
-                if (camera != null) {
-                    camera.close();
+                try {
+                    closeSession();
+                } finally {
+                    closeDevice(mCameraIds[i], mCameraMockListener);
                 }
             }
         }
     }
 
     public void testCameraDeviceCapture() throws Exception {
-        runCaptureTest(false, false);
+        runCaptureTest(/*burst*/false, /*repeating*/false, /*abort*/false);
     }
 
     public void testCameraDeviceCaptureBurst() throws Exception {
-        runCaptureTest(true, false);
+        runCaptureTest(/*burst*/true, /*repeating*/false, /*abort*/false);
     }
 
     public void testCameraDeviceRepeatingRequest() throws Exception {
-        runCaptureTest(false, true);
+        runCaptureTest(/*burst*/false, /*repeating*/true, /*abort*/false);
     }
 
     public void testCameraDeviceRepeatingBurst() throws Exception {
-        runCaptureTest(true, true);
+        runCaptureTest(/*burst*/true, /*repeating*/true, /*abort*/false);
     }
 
-    private class IsCameraMetadataNotEmpty<T extends CameraMetadata>
-            extends ArgumentMatcher<T> {
+    /**
+     * Test {@link android.hardware.camera2.CameraCaptureSession#abortCaptures} API.
+     *
+     * <p>Abort is the fastest way to idle the camera device for reconfiguration with
+     * {@link android.hardware.camera2.CameraCaptureSession#abortCaptures}, at the cost of
+     * discarding in-progress work. Once the abort is complete, the idle callback will be called.
+     * </p>
+     */
+    public void testCameraDeviceAbort() throws Exception {
+        runCaptureTest(/*burst*/false, /*repeating*/true, /*abort*/true);
+        runCaptureTest(/*burst*/true, /*repeating*/true, /*abort*/true);
+        /**
+         * TODO: this is only basic test of abort. we probably should also test below cases:
+         *
+         * 1. Performance. Make sure abort is faster than stopRepeating, we can test each one a
+         * couple of times, then compare the average. Also, for abortCaptures() alone, we should
+         * make sure it doesn't take too long time (e.g. <100ms for full devices, <500ms for limited
+         * devices), after the abort, we should be able to get all results back very quickly.  This
+         * can be done in performance test.
+         *
+         * 2. Make sure all in-flight request comes back after abort, e.g. submit a couple of
+         * long exposure single captures, then abort, then check if we can get the pending
+         * request back quickly.
+         *
+         * 3. Also need check onCaptureSequenceCompleted for repeating burst after abortCaptures().
+         */
+    }
+
+    /**
+     * Test invalid capture (e.g. null or empty capture request).
+     */
+    public void testInvalidCapture() throws Exception {
+        for (int i = 0; i < mCameraIds.length; i++) {
+            try {
+                openDevice(mCameraIds[i], mCameraMockListener);
+                waitForDeviceState(STATE_OPENED, CAMERA_OPEN_TIMEOUT_MS);
+
+                prepareCapture();
+
+                invalidRequestCaptureTestByCamera();
+
+                closeSession();
+            }
+            finally {
+                try {
+
+                } finally {
+                    closeDevice(mCameraIds[i], mCameraMockListener);
+                }
+            }
+        }
+    }
+
+    /**
+     * Test to ensure that we can call camera2 API methods inside callbacks.
+     *
+     * Tests:
+     *  onOpened -> createCaptureSession, createCaptureRequest
+     *  onConfigured -> getDevice, abortCaptures,
+     *     createCaptureRequest, capture, setRepeatingRequest, stopRepeating
+     *  onCaptureCompleted -> createCaptureRequest, getDevice, abortCaptures,
+     *     capture, setRepeatingRequest, stopRepeating, session+device.close
+     */
+    public void testChainedOperation() throws Throwable {
+
+        // Set up single dummy target
+        createDefaultImageReader(DEFAULT_CAPTURE_SIZE, ImageFormat.YUV_420_888, MAX_NUM_IMAGES,
+                /*listener*/ null);
+        final ArrayList<Surface> outputs = new ArrayList<>();
+        outputs.add(mReaderSurface);
+
+        // A queue for the chained listeners to push results to
+        // A success Throwable indicates no errors; other Throwables detail a test failure;
+        // nulls indicate timeouts.
+        final Throwable success = new Throwable("Success");
+        final LinkedBlockingQueue<Throwable> results = new LinkedBlockingQueue<>();
+
+        // Define listeners
+        // A cascade of Device->Session->Capture listeners, each of which invokes at least one
+        // method on the camera device or session.
+
+        class ChainedCaptureListener extends CameraCaptureSession.CaptureListener {
+            public void onCaptureCompleted(CameraCaptureSession session, CaptureRequest request,
+                    TotalCaptureResult result) {
+                try {
+                    CaptureRequest.Builder request2 =
+                            session.getDevice().createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
+                    request2.addTarget(mReaderSurface);
+
+                    // Some calls to the camera for coverage
+                    session.abortCaptures();
+                    session.capture(request2.build(),
+                            /*listener*/ null, /*handler*/ null);
+                    session.setRepeatingRequest(request2.build(),
+                            /*listener*/ null, /*handler*/ null);
+                    session.stopRepeating();
+
+                    CameraDevice camera = session.getDevice();
+                    session.close();
+                    camera.close();
+
+                    results.offer(success);
+                } catch (Throwable t) {
+                    results.offer(t);
+                }
+            }
+
+            public void onCaptureFailed(CameraCaptureSession session, CaptureRequest request,
+                    CaptureFailure failure) {
+                try {
+                    CameraDevice camera = session.getDevice();
+                    session.close();
+                    camera.close();
+                    fail("onCaptureFailed invoked with failure reason: " + failure.getReason());
+                } catch (Throwable t) {
+                    results.offer(t);
+                }
+            }
+        }
+
+        class ChainedSessionListener extends CameraCaptureSession.StateListener {
+            private final ChainedCaptureListener mCaptureListener = new ChainedCaptureListener();
+
+            public void onConfigured(CameraCaptureSession session) {
+                try {
+                    CaptureRequest.Builder request =
+                            session.getDevice().createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
+                    request.addTarget(mReaderSurface);
+                    // Some calls to the camera for coverage
+                    session.getDevice();
+                    session.abortCaptures();
+                    // The important call for the next level of chaining
+                    session.capture(request.build(), mCaptureListener, mHandler);
+                    // Some more calls
+                    session.setRepeatingRequest(request.build(),
+                            /*listener*/ null, /*handler*/ null);
+                    session.stopRepeating();
+                    results.offer(success);
+                } catch (Throwable t) {
+                    results.offer(t);
+                }
+            }
+
+            public void onConfigureFailed(CameraCaptureSession session) {
+                try {
+                    CameraDevice camera = session.getDevice();
+                    session.close();
+                    camera.close();
+                    fail("onConfigureFailed was invoked");
+                } catch (Throwable t) {
+                    results.offer(t);
+                }
+            }
+        }
+
+        class ChainedCameraListener extends CameraDevice.StateListener {
+            private final ChainedSessionListener mSessionListener = new ChainedSessionListener();
+
+            public CameraDevice cameraDevice;
+
+            public void onOpened(CameraDevice camera) {
+
+                cameraDevice = camera;
+                try {
+                    // Some calls for coverage
+                    camera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
+                    // The important call for next level of chaining
+                    camera.createCaptureSession(outputs, mSessionListener, mHandler);
+                    results.offer(success);
+                } catch (Throwable t) {
+                    try {
+                        camera.close();
+                        results.offer(t);
+                    } catch (Throwable t2) {
+                        Log.e(TAG,
+                                "Second failure reached; discarding first exception with trace " +
+                                Log.getStackTraceString(t));
+                        results.offer(t2);
+                    }
+                }
+            }
+
+            public void onDisconnected(CameraDevice camera) {
+                try {
+                    camera.close();
+                    fail("onDisconnected invoked");
+                } catch (Throwable t) {
+                    results.offer(t);
+                }
+            }
+
+            public void onError(CameraDevice camera, int error) {
+                try {
+                    camera.close();
+                    fail("onError invoked with error code: " + error);
+                } catch (Throwable t) {
+                    results.offer(t);
+                }
+            }
+        }
+
+        // Actual test code
+
+        for (int i = 0; i < mCameraIds.length; i++) {
+            Throwable result;
+
+            // Start chained cascade
+            ChainedCameraListener cameraListener = new ChainedCameraListener();
+            mCameraManager.openCamera(mCameraIds[i], cameraListener, mHandler);
+
+            // Check if open succeeded
+            result = results.poll(CAMERA_OPEN_TIMEOUT_MS, TimeUnit.MILLISECONDS);
+            if (result != success) {
+                if (cameraListener.cameraDevice != null) cameraListener.cameraDevice.close();
+                if (result == null) {
+                    fail("Timeout waiting for camera open");
+                } else {
+                    throw result;
+                }
+            }
+
+            // Check if configure succeeded
+            result = results.poll(SESSION_CONFIGURE_TIMEOUT_MS, TimeUnit.MILLISECONDS);
+            if (result != success) {
+                if (cameraListener.cameraDevice != null) cameraListener.cameraDevice.close();
+                if (result == null) {
+                    fail("Timeout waiting for session configure");
+                } else {
+                    throw result;
+                }
+            }
+
+            // Check if capture succeeded
+            result = results.poll(CAPTURE_RESULT_TIMEOUT_MS, TimeUnit.MILLISECONDS);
+            if (result != success) {
+                if (cameraListener.cameraDevice != null) cameraListener.cameraDevice.close();
+                if (result == null) {
+                    fail("Timeout waiting for capture completion");
+                } else {
+                    throw result;
+                }
+            }
+        }
+    }
+
+    private void invalidRequestCaptureTestByCamera() throws Exception {
+        if (VERBOSE) Log.v(TAG, "invalidRequestCaptureTestByCamera");
+
+        List<CaptureRequest> emptyRequests = new ArrayList<CaptureRequest>();
+        CaptureRequest.Builder requestBuilder =
+                mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
+        CaptureRequest unConfiguredRequest = requestBuilder.build();
+        List<CaptureRequest> unConfiguredRequests = new ArrayList<CaptureRequest>();
+        unConfiguredRequests.add(unConfiguredRequest);
+
+        try {
+            // Test: CameraCaptureSession capture should throw IAE for null request.
+            mSession.capture(/*request*/null, /*listener*/null, mHandler);
+            mCollector.addMessage(
+                    "Session capture should throw IllegalArgumentException for null request");
+        } catch (IllegalArgumentException e) {
+            // Pass.
+        }
+
+        try {
+            // Test: CameraCaptureSession capture should throw IAE for request
+            // without surface configured.
+            mSession.capture(unConfiguredRequest, /*listener*/null, mHandler);
+            mCollector.addMessage("Session capture should throw " +
+                    "IllegalArgumentException for request without surface configured");
+        } catch (IllegalArgumentException e) {
+            // Pass.
+        }
+
+        try {
+            // Test: CameraCaptureSession setRepeatingRequest should throw IAE for null request.
+            mSession.setRepeatingRequest(/*request*/null, /*listener*/null, mHandler);
+            mCollector.addMessage("Session setRepeatingRequest should throw " +
+                    "IllegalArgumentException for null request");
+        } catch (IllegalArgumentException e) {
+            // Pass.
+        }
+
+        try {
+            // Test: CameraCaptureSession setRepeatingRequest should throw IAE for for request
+            // without surface configured.
+            mSession.setRepeatingRequest(unConfiguredRequest, /*listener*/null, mHandler);
+            mCollector.addMessage("Capture zero burst should throw IllegalArgumentException " +
+                    "for request without surface configured");
+        } catch (IllegalArgumentException e) {
+            // Pass.
+        }
+
+        try {
+            // Test: CameraCaptureSession captureBurst should throw IAE for null request list.
+            mSession.captureBurst(/*requests*/null, /*listener*/null, mHandler);
+            mCollector.addMessage("Session captureBurst should throw " +
+                    "IllegalArgumentException for null request list");
+        } catch (IllegalArgumentException e) {
+            // Pass.
+        }
+
+        try {
+            // Test: CameraCaptureSession captureBurst should throw IAE for empty request list.
+            mSession.captureBurst(emptyRequests, /*listener*/null, mHandler);
+            mCollector.addMessage("Session captureBurst should throw " +
+                    " IllegalArgumentException for empty request list");
+        } catch (IllegalArgumentException e) {
+            // Pass.
+        }
+
+        try {
+            // Test: CameraCaptureSession captureBurst should throw IAE for request
+            // without surface configured.
+            mSession.captureBurst(unConfiguredRequests, /*listener*/null, mHandler);
+            fail("Session captureBurst should throw IllegalArgumentException " +
+                    "for null request list");
+        } catch (IllegalArgumentException e) {
+            // Pass.
+        }
+
+        try {
+            // Test: CameraCaptureSession setRepeatingBurst should throw IAE for null request list.
+            mSession.setRepeatingBurst(/*requests*/null, /*listener*/null, mHandler);
+            mCollector.addMessage("Session setRepeatingBurst should throw " +
+                    "IllegalArgumentException for null request list");
+        } catch (IllegalArgumentException e) {
+            // Pass.
+        }
+
+        try {
+            // Test: CameraCaptureSession setRepeatingBurst should throw IAE for empty request list.
+            mSession.setRepeatingBurst(emptyRequests, /*listener*/null, mHandler);
+            mCollector.addMessage("Session setRepeatingBurst should throw " +
+                    "IllegalArgumentException for empty request list");
+        } catch (IllegalArgumentException e) {
+            // Pass.
+        }
+
+        try {
+            // Test: CameraCaptureSession setRepeatingBurst should throw IAE for request
+            // without surface configured.
+            mSession.setRepeatingBurst(unConfiguredRequests, /*listener*/null, mHandler);
+            mCollector.addMessage("Session setRepeatingBurst should throw " +
+                    "IllegalArgumentException for request without surface configured");
+        } catch (IllegalArgumentException e) {
+            // Pass.
+        }
+    }
+
+    private class IsCaptureResultNotEmpty
+            extends ArgumentMatcher<TotalCaptureResult> {
         @Override
         public boolean matches(Object obj) {
             /**
              * Do the simple verification here. Only verify the timestamp for now.
              * TODO: verify more required capture result metadata fields.
              */
-            CameraMetadata result = (CameraMetadata) obj;
+            TotalCaptureResult result = (TotalCaptureResult) obj;
             Long timeStamp = result.get(CaptureResult.SENSOR_TIMESTAMP);
             if (timeStamp != null && timeStamp.longValue() > 0L) {
                 return true;
@@ -225,31 +690,33 @@
         }
     }
 
-    private void runCaptureTest(boolean burst, boolean repeating) throws Exception {
-        String[] ids = mCameraManager.getCameraIdList();
-        for (int i = 0; i < ids.length; i++) {
-            CameraDevice camera = null;
+    /**
+     * Run capture test with different test configurations.
+     *
+     * @param burst If the test uses {@link CameraCaptureSession#captureBurst} or
+     * {@link CameraCaptureSession#setRepeatingBurst} to capture the burst.
+     * @param repeating If the test uses {@link CameraCaptureSession#setRepeatingBurst} or
+     * {@link CameraCaptureSession#setRepeatingRequest} for repeating capture.
+     * @param abort If the test uses {@link CameraCaptureSession#abortCaptures} to stop the
+     * repeating capture.  It has no effect if repeating is false.
+     */
+    private void runCaptureTest(boolean burst, boolean repeating, boolean abort) throws Exception {
+        for (int i = 0; i < mCameraIds.length; i++) {
             try {
-                camera = CameraTestUtils.openCamera(mCameraManager, ids[i],
-                        mCameraListener, mCallbackHandler);
-                assertNotNull(
-                        String.format("Failed to open camera device %s", ids[i]), camera);
-                waitForState(STATE_UNCONFIGURED, CAMERA_OPEN_TIMEOUT_MS);
+                openDevice(mCameraIds[i], mCameraMockListener);
+                waitForDeviceState(STATE_OPENED, CAMERA_OPEN_TIMEOUT_MS);
 
-                prepareCapture(camera);
+                prepareCapture();
 
                 if (!burst) {
                     // Test: that a single capture of each template type succeeds.
-                    for (int j = 0; j < mTemplates.length; j++) {
-                        captureSingleShot(camera, ids[i], mTemplates[j], repeating);
+                    for (int j = 0; j < sTemplates.length; j++) {
+                        captureSingleShot(mCameraIds[i], sTemplates[j], repeating, abort);
                     }
                 }
                 else {
-                    // Test: burst of zero shots
-                    captureBurstShot(camera, ids[i], mTemplates, 0, repeating);
-
                     // Test: burst of one shot
-                    captureBurstShot(camera, ids[i], mTemplates, 1, repeating);
+                    captureBurstShot(mCameraIds[i], sTemplates, 1, repeating, abort);
 
                     int[] templates = new int[] {
                             CameraDevice.TEMPLATE_STILL_CAPTURE,
@@ -260,181 +727,589 @@
                             };
 
                     // Test: burst of 5 shots of the same template type
-                    captureBurstShot(camera, ids[i], templates, templates.length, repeating);
+                    captureBurstShot(mCameraIds[i], templates, templates.length, repeating, abort);
 
                     // Test: burst of 5 shots of different template types
-                    captureBurstShot(camera, ids[i], mTemplates, mTemplates.length, repeating);
+                    captureBurstShot(
+                            mCameraIds[i], sTemplates, sTemplates.length, repeating, abort);
                 }
-                verify(mCameraListener, never())
+                verify(mCameraMockListener, never())
                         .onError(
                                 any(CameraDevice.class),
                                 anyInt());
             }
             finally {
-                if (camera != null) {
-                    camera.close();
+                try {
+                    closeSession();
+                } finally {
+                    closeDevice(mCameraIds[i], mCameraMockListener);
                 }
             }
         }
     }
 
     private void captureSingleShot(
-            CameraDevice camera,
             String id,
             int template,
-            boolean repeating) throws Exception {
+            boolean repeating, boolean abort) throws Exception {
 
         assertEquals("Bad initial state for preparing to capture",
-                mLatestState, STATE_IDLE);
+                mLatestSessionState, SESSION_READY);
 
-        CaptureRequest.Builder requestBuilder = camera.createCaptureRequest(template);
+        CaptureRequest.Builder requestBuilder = mCamera.createCaptureRequest(template);
         assertNotNull("Failed to create capture request", requestBuilder);
-        requestBuilder.addTarget(mSurface);
-        CameraDevice.CaptureListener mockCaptureListener =
-                mock(CameraDevice.CaptureListener.class);
+        requestBuilder.addTarget(mReaderSurface);
+        CameraCaptureSession.CaptureListener mockCaptureListener =
+                mock(CameraCaptureSession.CaptureListener.class);
 
         if (VERBOSE) {
             Log.v(TAG, String.format("Capturing shot for device %s, template %d",
                     id, template));
         }
-        if (!repeating) {
-            camera.capture(requestBuilder.build(), mockCaptureListener, mCallbackHandler);
-        }
-        else {
-            camera.setRepeatingRequest(requestBuilder.build(), mockCaptureListener,
-                    mCallbackHandler);
-        }
-        waitForState(STATE_ACTIVE, CAMERA_CONFIGURE_TIMEOUT_MS);
+
+        startCapture(requestBuilder.build(), repeating, mockCaptureListener, mHandler);
+        waitForSessionState(SESSION_ACTIVE, SESSION_ACTIVE_TIMEOUT_MS);
 
         int expectedCaptureResultCount = repeating ? REPEATING_CAPTURE_EXPECTED_RESULT_COUNT : 1;
-        verifyCaptureResults(camera, mockCaptureListener, expectedCaptureResultCount);
+        verifyCaptureResults(mockCaptureListener, expectedCaptureResultCount);
 
         if (repeating) {
-            camera.stopRepeating();
+            if (abort) {
+                mSession.abortCaptures();
+            } else {
+                mSession.stopRepeating();
+            }
         }
-        waitForState(STATE_IDLE, CAMERA_CONFIGURE_TIMEOUT_MS);
+        waitForSessionState(SESSION_READY, SESSION_READY_TIMEOUT_MS);
     }
 
     private void captureBurstShot(
-            CameraDevice camera,
             String id,
             int[] templates,
             int len,
-            boolean repeating) throws Exception {
+            boolean repeating,
+            boolean abort) throws Exception {
 
         assertEquals("Bad initial state for preparing to capture",
-                mLatestState, STATE_IDLE);
+                mLatestSessionState, SESSION_READY);
 
         assertTrue("Invalid args to capture function", len <= templates.length);
         List<CaptureRequest> requests = new ArrayList<CaptureRequest>();
         for (int i = 0; i < len; i++) {
-            CaptureRequest.Builder requestBuilder = camera.createCaptureRequest(templates[i]);
+            CaptureRequest.Builder requestBuilder = mCamera.createCaptureRequest(templates[i]);
             assertNotNull("Failed to create capture request", requestBuilder);
-            requestBuilder.addTarget(mSurface);
+            requestBuilder.addTarget(mReaderSurface);
             requests.add(requestBuilder.build());
         }
-        CameraDevice.CaptureListener mockCaptureListener =
-                mock(CameraDevice.CaptureListener.class);
+        CameraCaptureSession.CaptureListener mockCaptureListener =
+                mock(CameraCaptureSession.CaptureListener.class);
 
         if (VERBOSE) {
             Log.v(TAG, String.format("Capturing burst shot for device %s", id));
         }
 
         if (!repeating) {
-            camera.captureBurst(requests, mockCaptureListener, mCallbackHandler);
+            mSession.captureBurst(requests, mockCaptureListener, mHandler);
         }
         else {
-            camera.setRepeatingBurst(requests, mockCaptureListener, mCallbackHandler);
+            mSession.setRepeatingBurst(requests, mockCaptureListener, mHandler);
         }
-        waitForState(STATE_ACTIVE, CAMERA_CONFIGURE_TIMEOUT_MS);
+        waitForSessionState(SESSION_ACTIVE, SESSION_READY_TIMEOUT_MS);
 
         int expectedResultCount = len;
         if (repeating) {
             expectedResultCount *= REPEATING_CAPTURE_EXPECTED_RESULT_COUNT;
         }
 
-        verifyCaptureResults(camera, mockCaptureListener, expectedResultCount);
+        verifyCaptureResults(mockCaptureListener, expectedResultCount);
 
         if (repeating) {
-            camera.stopRepeating();
+            if (abort) {
+                mSession.abortCaptures();
+            } else {
+                mSession.stopRepeating();
+            }
         }
-        waitForState(STATE_IDLE, CAMERA_CONFIGURE_TIMEOUT_MS);
-    }
-
-    // Precondition: Device must be in known IDLE/UNCONFIGURED state (has been waited for)
-    private void prepareCapture(CameraDevice camera) throws Exception {
-        assertTrue("Bad initial state for preparing to capture",
-                mLatestState == STATE_IDLE || mLatestState == STATE_UNCONFIGURED);
-
-        List<Surface> outputSurfaces = new ArrayList<Surface>(1);
-        outputSurfaces.add(mSurface);
-        camera.configureOutputs(outputSurfaces);
-        waitForState(STATE_BUSY, CAMERA_BUSY_TIMEOUT_MS);
-        waitForState(STATE_IDLE, CAMERA_IDLE_TIMEOUT_MS);
+        waitForSessionState(SESSION_READY, SESSION_READY_TIMEOUT_MS);
     }
 
     /**
-     * Dummy listener that release the image immediately once it is available.
-     * It can be used for the case where we don't care the image data at all.
-     * TODO: move it to the CameraTestUtil class.
-     */
-    private class ImageDropperListener implements ImageReader.OnImageAvailableListener {
-        @Override
-        public void onImageAvailable(ImageReader reader) {
-            Image image = null;
-            try {
-                image = reader.acquireNextImage();
-            } finally {
-                if (image != null) {
-                    image.close();
-                }
-            }
+     * Precondition: Device must be in known OPENED state (has been waited for).
+     *
+     * <p>Creates a new capture session and waits until it is in the {@code SESSION_READY} state.
+     * </p>
+     *
+     * <p>Any existing capture session will be closed as a result of calling this.</p>
+     * */
+    private void prepareCapture() throws Exception {
+        if (VERBOSE) Log.v(TAG, "prepareCapture");
+
+        assertTrue("Bad initial state for preparing to capture",
+                mLatestDeviceState == STATE_OPENED);
+
+        if (mSession != null) {
+            if (VERBOSE) Log.v(TAG, "prepareCapture - closing existing session");
+            closeSession();
         }
+
+        // Create a new session listener each time, it's not reusable across cameras
+        mSessionMockListener = spy(new BlockingSessionListener());
+        mSessionWaiter = mSessionMockListener.getStateWaiter();
+
+        List<Surface> outputSurfaces = new ArrayList<>(Arrays.asList(mReaderSurface));
+        mCamera.createCaptureSession(outputSurfaces, mSessionMockListener, mHandler);
+
+        mSession = mSessionMockListener.waitAndGetSession(SESSION_CONFIGURE_TIMEOUT_MS);
+        waitForSessionState(SESSION_CONFIGURED, SESSION_CONFIGURE_TIMEOUT_MS);
+        waitForSessionState(SESSION_READY, SESSION_READY_TIMEOUT_MS);
+}
+
+    private void waitForDeviceState(int state, long timeoutMs) {
+        mCameraMockListener.waitForState(state, timeoutMs);
+        mLatestDeviceState = state;
     }
 
-    private void createDefaultSurface() throws Exception {
-        mReader =
-                ImageReader.newInstance(DEFAULT_CAPTURE_WIDTH,
-                        DEFAULT_CAPTURE_HEIGHT,
-                        ImageFormat.YUV_420_888,
-                        MAX_NUM_IMAGES);
-        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());
-    }
-
-    private void waitForState(int state, long timeout) {
-        mCameraListener.waitForState(state, timeout);
-        mLatestState = state;
+    private void waitForSessionState(int state, long timeoutMs) {
+        mSessionWaiter.waitForState(state, timeoutMs);
+        mLatestSessionState = state;
     }
 
     private void verifyCaptureResults(
-            CameraDevice camera,
-            CameraDevice.CaptureListener mockListener,
+            CameraCaptureSession.CaptureListener mockListener,
             int expectResultCount) {
         // Should receive expected number of capture results.
         verify(mockListener,
                 timeout(CAPTURE_WAIT_TIMEOUT_MS).atLeast(expectResultCount))
                         .onCaptureCompleted(
-                                eq(camera),
+                                eq(mSession),
                                 isA(CaptureRequest.class),
-                                argThat(new IsCameraMetadataNotEmpty<CaptureResult>()));
+                                argThat(new IsCaptureResultNotEmpty()));
         // Should not receive any capture failed callbacks.
         verify(mockListener, never())
                         .onCaptureFailed(
-                                eq(camera),
-                                argThat(new IsCameraMetadataNotEmpty<CaptureRequest>()),
+                                eq(mSession),
+                                isA(CaptureRequest.class),
                                 isA(CaptureFailure.class));
         // Should receive expected number of capture shutter calls
         verify(mockListener,
                 atLeast(expectResultCount))
                         .onCaptureStarted(
-                               eq(camera),
+                               eq(mSession),
                                isA(CaptureRequest.class),
                                anyLong());
-
     }
 
+    private void checkFpsRange(CaptureRequest.Builder request, int template,
+            CameraCharacteristics props) {
+        CaptureRequest.Key<Range<Integer>> fpsRangeKey = CONTROL_AE_TARGET_FPS_RANGE;
+        Range<Integer> fpsRange;
+        if ((fpsRange = mCollector.expectKeyValueNotNull(request, fpsRangeKey)) == null) {
+            return;
+        }
+
+        int minFps = fpsRange.getLower();
+        int maxFps = fpsRange.getUpper();
+        Range<Integer>[] availableFpsRange = props
+                .get(CameraCharacteristics.CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES);
+        boolean foundRange = false;
+        for (int i = 0; i < availableFpsRange.length; i += 1) {
+            if (minFps == availableFpsRange[i].getLower()
+                    && maxFps == availableFpsRange[i].getUpper()) {
+                foundRange = true;
+                break;
+            }
+        }
+        if (!foundRange) {
+            mCollector.addMessage(String.format("Unable to find the fps range (%d, %d)",
+                    minFps, maxFps));
+            return;
+        }
+
+
+        if (template != CameraDevice.TEMPLATE_MANUAL &&
+                template != CameraDevice.TEMPLATE_STILL_CAPTURE) {
+            if (maxFps < MIN_FPS_REQUIRED_FOR_STREAMING) {
+                mCollector.addMessage("Max fps should be at least "
+                        + MIN_FPS_REQUIRED_FOR_STREAMING);
+                return;
+            }
+
+            // Relax framerate constraints on legacy mode
+            if (mStaticInfo.isHardwareLevelLimitedOrBetter()) {
+                // Need give fixed frame rate for video recording template.
+                if (template == CameraDevice.TEMPLATE_RECORD) {
+                    if (maxFps != minFps) {
+                        mCollector.addMessage("Video recording frame rate should be fixed");
+                    }
+                }
+            }
+        }
+    }
+
+    private void checkAfMode(CaptureRequest.Builder request, int template,
+            CameraCharacteristics props) {
+        boolean hasFocuser = !props.getKeys().contains(CameraCharacteristics.
+                LENS_INFO_MINIMUM_FOCUS_DISTANCE) ||
+                props.get(CameraCharacteristics.LENS_INFO_MINIMUM_FOCUS_DISTANCE) > 0f;
+
+        if (!hasFocuser) {
+            return;
+        }
+
+        int targetAfMode = CaptureRequest.CONTROL_AF_MODE_AUTO;
+        int[] availableAfMode = props.get(CameraCharacteristics.CONTROL_AF_AVAILABLE_MODES);
+        if (template == CameraDevice.TEMPLATE_PREVIEW ||
+                template == CameraDevice.TEMPLATE_STILL_CAPTURE ||
+                template == CameraDevice.TEMPLATE_ZERO_SHUTTER_LAG) {
+            // Default to CONTINUOUS_PICTURE if it is available, otherwise AUTO.
+            for (int i = 0; i < availableAfMode.length; i++) {
+                if (availableAfMode[i] == CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE) {
+                    targetAfMode = CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE;
+                    break;
+                }
+            }
+        } else if (template == CameraDevice.TEMPLATE_RECORD ||
+                template == CameraDevice.TEMPLATE_VIDEO_SNAPSHOT) {
+            // Default to CONTINUOUS_VIDEO if it is available, otherwise AUTO.
+            for (int i = 0; i < availableAfMode.length; i++) {
+                if (availableAfMode[i] == CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_VIDEO) {
+                    targetAfMode = CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_VIDEO;
+                    break;
+                }
+            }
+        } else if (template == CameraDevice.TEMPLATE_MANUAL) {
+            targetAfMode = CaptureRequest.CONTROL_AF_MODE_OFF;
+        }
+
+        mCollector.expectKeyValueEquals(request, CONTROL_AF_MODE, targetAfMode);
+        if (mStaticInfo.areKeysAvailable(CaptureRequest.LENS_FOCUS_DISTANCE)) {
+            mCollector.expectKeyValueNotNull(request, LENS_FOCUS_DISTANCE);
+        }
+    }
+
+    /**
+     * <p>Check if the request settings are suitable for a given request template.</p>
+     *
+     * <p>This function doesn't fail the test immediately, it updates the
+     * test pass/fail status and appends the failure message to the error collector each key.</p>
+     *
+     * @param request The request to be checked.
+     * @param template The capture template targeted by this request.
+     * @param props The CameraCharacteristics this request is checked against with.
+     */
+    private void checkRequestForTemplate(CaptureRequest.Builder request, int template,
+            CameraCharacteristics props) {
+        // 3A settings--control.mode.
+        if (template != CameraDevice.TEMPLATE_MANUAL) {
+            mCollector.expectKeyValueEquals(request, CONTROL_MODE,
+                    CaptureRequest.CONTROL_MODE_AUTO);
+        }
+
+        // 3A settings--AE/AWB/AF.
+        int maxRegionsAe = props.get(CameraCharacteristics.CONTROL_MAX_REGIONS_AE);
+        int maxRegionsAwb = props.get(CameraCharacteristics.CONTROL_MAX_REGIONS_AWB);
+        int maxRegionsAf = props.get(CameraCharacteristics.CONTROL_MAX_REGIONS_AF);
+
+        checkAfMode(request, template, props);
+        checkFpsRange(request, template, props);
+
+        if (template == CameraDevice.TEMPLATE_MANUAL) {
+            mCollector.expectKeyValueEquals(request, CONTROL_MODE, CaptureRequest.CONTROL_MODE_OFF);
+            mCollector.expectKeyValueEquals(request, CONTROL_AE_MODE,
+                    CaptureRequest.CONTROL_AE_MODE_OFF);
+            mCollector.expectKeyValueEquals(request, CONTROL_AWB_MODE,
+                    CaptureRequest.CONTROL_AWB_MODE_OFF);
+        } else {
+            mCollector.expectKeyValueEquals(request, CONTROL_AE_MODE,
+                    CaptureRequest.CONTROL_AE_MODE_ON);
+            mCollector.expectKeyValueNotEquals(request, CONTROL_AE_ANTIBANDING_MODE,
+                    CaptureRequest.CONTROL_AE_ANTIBANDING_MODE_OFF);
+            mCollector.expectKeyValueEquals(request, CONTROL_AE_EXPOSURE_COMPENSATION, 0);
+            mCollector.expectKeyValueEquals(request, CONTROL_AE_LOCK, false);
+            mCollector.expectKeyValueEquals(request, CONTROL_AE_PRECAPTURE_TRIGGER,
+                    CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER_IDLE);
+
+            mCollector.expectKeyValueEquals(request, CONTROL_AF_TRIGGER,
+                    CaptureRequest.CONTROL_AF_TRIGGER_IDLE);
+
+            mCollector.expectKeyValueEquals(request, CONTROL_AWB_MODE,
+                    CaptureRequest.CONTROL_AWB_MODE_AUTO);
+            mCollector.expectKeyValueEquals(request, CONTROL_AWB_LOCK, false);
+
+            // Check 3A regions.
+            if (VERBOSE) {
+                Log.v(TAG, String.format("maxRegions is: {AE: %s, AWB: %s, AF: %s}",
+                        maxRegionsAe, maxRegionsAwb, maxRegionsAf));
+            }
+            if (maxRegionsAe > 0) {
+                mCollector.expectKeyValueNotNull(request, CONTROL_AE_REGIONS);
+            }
+            if (maxRegionsAwb > 0) {
+                mCollector.expectKeyValueNotNull(request, CONTROL_AWB_REGIONS);
+            }
+            if (maxRegionsAf > 0) {
+                mCollector.expectKeyValueNotNull(request, CONTROL_AF_REGIONS);
+            }
+        }
+
+        // Sensor settings.
+
+        mCollector.expectEquals("Lens aperture must be present in request if available apertures " +
+                        "are present in metadata, and vice-versa.",
+                mStaticInfo.areKeysAvailable(CameraCharacteristics.LENS_INFO_AVAILABLE_APERTURES),
+                mStaticInfo.areKeysAvailable(CaptureRequest.LENS_APERTURE));
+        if (mStaticInfo.areKeysAvailable(CameraCharacteristics.LENS_INFO_AVAILABLE_APERTURES)) {
+            float[] availableApertures =
+                    props.get(CameraCharacteristics.LENS_INFO_AVAILABLE_APERTURES);
+            if (availableApertures.length > 1) {
+                mCollector.expectKeyValueNotNull(request, LENS_APERTURE);
+            }
+        }
+
+        mCollector.expectEquals("Lens filter density must be present in request if available " +
+                        "filter densities are present in metadata, and vice-versa.",
+                mStaticInfo.areKeysAvailable(CameraCharacteristics.
+                        LENS_INFO_AVAILABLE_FILTER_DENSITIES),
+                mStaticInfo.areKeysAvailable(CaptureRequest.LENS_FILTER_DENSITY));
+        if (mStaticInfo.areKeysAvailable(CameraCharacteristics.
+                LENS_INFO_AVAILABLE_FILTER_DENSITIES)) {
+            float[] availableFilters =
+                    props.get(CameraCharacteristics.LENS_INFO_AVAILABLE_FILTER_DENSITIES);
+            if (availableFilters.length > 1) {
+                mCollector.expectKeyValueNotNull(request, LENS_FILTER_DENSITY);
+            }
+        }
+
+        float[] availableFocalLen =
+                props.get(CameraCharacteristics.LENS_INFO_AVAILABLE_FOCAL_LENGTHS);
+        if (availableFocalLen.length > 1) {
+            mCollector.expectKeyValueNotNull(request, LENS_FOCAL_LENGTH);
+        }
+
+        mCollector.expectEquals("Lens optical stabilization must be present in request if " +
+                        "available optical stabilizations are present in metadata, and vice-versa.",
+                mStaticInfo.areKeysAvailable(CameraCharacteristics.
+                        LENS_INFO_AVAILABLE_OPTICAL_STABILIZATION),
+                mStaticInfo.areKeysAvailable(CaptureRequest.LENS_OPTICAL_STABILIZATION_MODE));
+        if (mStaticInfo.areKeysAvailable(CameraCharacteristics.
+                LENS_INFO_AVAILABLE_OPTICAL_STABILIZATION)) {
+            int[] availableOIS =
+                    props.get(CameraCharacteristics.LENS_INFO_AVAILABLE_OPTICAL_STABILIZATION);
+            if (availableOIS.length > 1) {
+                mCollector.expectKeyValueNotNull(request, LENS_OPTICAL_STABILIZATION_MODE);
+            }
+        }
+
+        if (mStaticInfo.areKeysAvailable(BLACK_LEVEL_LOCK)) {
+            mCollector.expectKeyValueEquals(request, BLACK_LEVEL_LOCK, false);
+        }
+
+        if (mStaticInfo.areKeysAvailable(SENSOR_FRAME_DURATION)) {
+            mCollector.expectKeyValueNotNull(request, SENSOR_FRAME_DURATION);
+        }
+
+        if (mStaticInfo.areKeysAvailable(SENSOR_EXPOSURE_TIME)) {
+            mCollector.expectKeyValueNotNull(request, SENSOR_EXPOSURE_TIME);
+        }
+
+        if (mStaticInfo.areKeysAvailable(SENSOR_SENSITIVITY)) {
+            mCollector.expectKeyValueNotNull(request, SENSOR_SENSITIVITY);
+        }
+
+        // ISP-processing settings.
+        mCollector.expectKeyValueEquals(
+                request, STATISTICS_FACE_DETECT_MODE,
+                CaptureRequest.STATISTICS_FACE_DETECT_MODE_OFF);
+        mCollector.expectKeyValueEquals(request, FLASH_MODE, CaptureRequest.FLASH_MODE_OFF);
+
+        List<Integer> availableCaps = mStaticInfo.getAvailableCapabilitiesChecked();
+        if (mStaticInfo.areKeysAvailable(STATISTICS_LENS_SHADING_MAP_MODE)) {
+            // If the device doesn't support RAW, all template should have OFF as default.
+            if (!availableCaps.contains(REQUEST_AVAILABLE_CAPABILITIES_RAW)) {
+                mCollector.expectKeyValueEquals(
+                        request, STATISTICS_LENS_SHADING_MAP_MODE,
+                        CaptureRequest.STATISTICS_LENS_SHADING_MAP_MODE_OFF);
+            }
+        }
+
+        if (template == CameraDevice.TEMPLATE_STILL_CAPTURE) {
+            // Not enforce high quality here, as some devices may not effectively have high quality
+            // mode.
+            if (mStaticInfo.areKeysAvailable(COLOR_CORRECTION_MODE)) {
+                mCollector.expectKeyValueNotEquals(
+                        request, COLOR_CORRECTION_MODE,
+                        CaptureRequest.COLOR_CORRECTION_MODE_TRANSFORM_MATRIX);
+            }
+
+            mCollector.expectEquals("Edge mode must be present in request if " +
+                            "available edge modes are present in metadata, and vice-versa.",
+                    mStaticInfo.areKeysAvailable(CameraCharacteristics.
+                            EDGE_AVAILABLE_EDGE_MODES),
+                    mStaticInfo.areKeysAvailable(CaptureRequest.EDGE_MODE));
+            if (mStaticInfo.areKeysAvailable(EDGE_MODE)) {
+                List<Integer> availableEdgeModes =
+                        Arrays.asList(toObject(mStaticInfo.getAvailableEdgeModesChecked()));
+                if (availableEdgeModes.contains(CaptureRequest.EDGE_MODE_HIGH_QUALITY)) {
+                    mCollector.expectKeyValueEquals(request, EDGE_MODE,
+                            CaptureRequest.EDGE_MODE_HIGH_QUALITY);
+                } else if (availableEdgeModes.contains(CaptureRequest.EDGE_MODE_FAST)) {
+                    mCollector.expectKeyValueEquals(request, EDGE_MODE,
+                            CaptureRequest.EDGE_MODE_FAST);
+                } else {
+                    mCollector.expectKeyValueEquals(request, EDGE_MODE,
+                            CaptureRequest.EDGE_MODE_OFF);
+                }
+            }
+
+            mCollector.expectEquals("Noise reduction mode must be present in request if " +
+                            "available noise reductions are present in metadata, and vice-versa.",
+                    mStaticInfo.areKeysAvailable(CameraCharacteristics.
+                            NOISE_REDUCTION_AVAILABLE_NOISE_REDUCTION_MODES),
+                    mStaticInfo.areKeysAvailable(CaptureRequest.NOISE_REDUCTION_MODE));
+            if (mStaticInfo.areKeysAvailable(
+                    CameraCharacteristics.NOISE_REDUCTION_AVAILABLE_NOISE_REDUCTION_MODES)) {
+                List<Integer> availableNoiseReductionModes =
+                        Arrays.asList(toObject(mStaticInfo.getAvailableNoiseReductionModesChecked()));
+                if (availableNoiseReductionModes
+                        .contains(CaptureRequest.NOISE_REDUCTION_MODE_HIGH_QUALITY)) {
+                    mCollector.expectKeyValueEquals(
+                            request, NOISE_REDUCTION_MODE,
+                            CaptureRequest.NOISE_REDUCTION_MODE_HIGH_QUALITY);
+                } else if (availableNoiseReductionModes
+                        .contains(CaptureRequest.NOISE_REDUCTION_MODE_FAST)) {
+                    mCollector.expectKeyValueEquals(
+                            request, NOISE_REDUCTION_MODE, CaptureRequest.NOISE_REDUCTION_MODE_FAST);
+                } else {
+                    mCollector.expectKeyValueEquals(
+                            request, NOISE_REDUCTION_MODE, CaptureRequest.NOISE_REDUCTION_MODE_OFF);
+                }
+            }
+
+            mCollector.expectEquals("Tonemap mode must be present in request if " +
+                            "available tonemap modes are present in metadata, and vice-versa.",
+                    mStaticInfo.areKeysAvailable(CameraCharacteristics.
+                            TONEMAP_AVAILABLE_TONE_MAP_MODES),
+                    mStaticInfo.areKeysAvailable(CaptureRequest.TONEMAP_MODE));
+            if (mStaticInfo.areKeysAvailable(
+                    CameraCharacteristics.TONEMAP_AVAILABLE_TONE_MAP_MODES)) {
+                List<Integer> availableToneMapModes =
+                        Arrays.asList(toObject(mStaticInfo.getAvailableToneMapModesChecked()));
+                if (availableToneMapModes.contains(CaptureRequest.TONEMAP_MODE_HIGH_QUALITY)) {
+                    mCollector.expectKeyValueEquals(request, TONEMAP_MODE,
+                            CaptureRequest.TONEMAP_MODE_HIGH_QUALITY);
+                } else {
+                    mCollector.expectKeyValueEquals(request, TONEMAP_MODE,
+                            CaptureRequest.TONEMAP_MODE_FAST);
+                }
+            }
+
+            // Still capture template should have android.statistics.lensShadingMapMode ON when
+            // RAW capability is supported.
+            if (mStaticInfo.areKeysAvailable(STATISTICS_LENS_SHADING_MAP_MODE) &&
+                    availableCaps.contains(REQUEST_AVAILABLE_CAPABILITIES_RAW)) {
+                    mCollector.expectKeyValueEquals(request, STATISTICS_LENS_SHADING_MAP_MODE,
+                            STATISTICS_LENS_SHADING_MAP_MODE_ON);
+            }
+        } else {
+            if (mStaticInfo.areKeysAvailable(EDGE_MODE)) {
+                mCollector.expectKeyValueNotNull(request, EDGE_MODE);
+            }
+
+            if (mStaticInfo.areKeysAvailable(NOISE_REDUCTION_MODE)) {
+                mCollector.expectKeyValueNotNull(request, NOISE_REDUCTION_MODE);
+            }
+
+            if (mStaticInfo.areKeysAvailable(TONEMAP_MODE)) {
+                mCollector.expectKeyValueNotEquals(request, TONEMAP_MODE,
+                        CaptureRequest.TONEMAP_MODE_CONTRAST_CURVE);
+            }
+            if (mStaticInfo.areKeysAvailable(STATISTICS_LENS_SHADING_MAP_MODE)) {
+                mCollector.expectKeyValueNotNull(request, STATISTICS_LENS_SHADING_MAP_MODE);
+            }
+        }
+
+        mCollector.expectKeyValueEquals(request, CONTROL_CAPTURE_INTENT, template);
+
+        // TODO: use the list of keys from CameraCharacteristics to avoid expecting
+        //       keys which are not available by this CameraDevice.
+    }
+
+    private void captureTemplateTestByCamera(String cameraId, int template) throws Exception {
+        try {
+            openDevice(cameraId, mCameraMockListener);
+
+            assertTrue("Camera template " + template + " is out of range!",
+                    template >= CameraDevice.TEMPLATE_PREVIEW
+                            && template <= CameraDevice.TEMPLATE_MANUAL);
+
+            mCollector.setCameraId(cameraId);
+
+            try {
+                CaptureRequest.Builder request = mCamera.createCaptureRequest(template);
+                assertNotNull("Failed to create capture request for template " + template, request);
+
+                CameraCharacteristics props = mStaticInfo.getCharacteristics();
+                checkRequestForTemplate(request, template, props);
+            } catch (IllegalArgumentException e) {
+                if (template == CameraDevice.TEMPLATE_MANUAL &&
+                        !mStaticInfo.isCapabilitySupported(CameraCharacteristics.
+                                REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR)) {
+                    // OK
+                } else if (template == CameraDevice.TEMPLATE_ZERO_SHUTTER_LAG &&
+                        !mStaticInfo.isCapabilitySupported(CameraCharacteristics.
+                                REQUEST_AVAILABLE_CAPABILITIES_ZSL)) {
+                    // OK
+                } else if (template == CameraDevice.TEMPLATE_VIDEO_SNAPSHOT &&
+                        mStaticInfo.isHardwareLevelLegacy()) {
+                    // OK
+                } else {
+                    throw e; // rethrow
+                }
+            }
+        }
+        finally {
+            try {
+                closeSession();
+            } finally {
+                closeDevice(cameraId, mCameraMockListener);
+            }
+        }
+    }
+
+    /**
+     * Start capture with given {@link #CaptureRequest}.
+     *
+     * @param request The {@link #CaptureRequest} to be captured.
+     * @param repeating If the capture is single capture or repeating.
+     * @param listener The {@link #CaptureListener} camera device used to notify callbacks.
+     * @param handler The handler camera device used to post callbacks.
+     */
+    protected void startCapture(CaptureRequest request, boolean repeating,
+            CameraCaptureSession.CaptureListener listener, Handler handler)
+                    throws CameraAccessException {
+        if (VERBOSE) Log.v(TAG, "Starting capture from session");
+
+        if (repeating) {
+            mSession.setRepeatingRequest(request, listener, handler);
+        } else {
+            mSession.capture(request, listener, handler);
+        }
+    }
+
+    /**
+     * Close a {@link #CameraCaptureSession capture session}; blocking until
+     * the close finishes with a transition to {@link CameraCaptureSession.StateListener#onClosed}.
+     */
+    protected void closeSession() {
+        if (mSession == null) {
+            return;
+        }
+
+        mSession.close();
+        waitForSessionState(SESSION_CLOSED, SESSION_CLOSE_TIMEOUT_MS);
+        mSession = null;
+
+        mSessionMockListener = null;
+        mSessionWaiter = null;
+    }
 }
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..a8c9896 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/CameraManagerTest.java
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/CameraManagerTest.java
@@ -16,17 +16,32 @@
 
 package android.hardware.camera2.cts;
 
+import static org.mockito.Mockito.*;
+import static org.mockito.AdditionalMatchers.not;
+import static org.mockito.AdditionalMatchers.and;
+
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.hardware.camera2.CameraAccessException;
 import android.hardware.camera2.CameraCharacteristics;
 import android.hardware.camera2.CameraDevice;
+import android.hardware.camera2.CameraDevice.StateListener;
 import android.hardware.camera2.CameraManager;
+import android.hardware.camera2.cts.CameraTestUtils.MockStateListener;
+import android.hardware.camera2.cts.helpers.CameraErrorCollector;
 import android.os.Handler;
+import android.os.HandlerThread;
 import android.test.AndroidTestCase;
 import android.util.Log;
 
+import com.android.ex.camera2.blocking.BlockingStateListener;
+
+import org.mockito.ArgumentCaptor;
+import org.mockito.InOrder;
+
+import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.List;
 
 /**
  * <p>Basic test for CameraManager class.</p>
@@ -34,14 +49,15 @@
 public class CameraManagerTest extends AndroidTestCase {
     private static final String TAG = "CameraManagerTest";
     private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
-
     private static final int NUM_CAMERA_REOPENS = 10;
 
     private PackageManager mPackageManager;
     private CameraManager mCameraManager;
     private NoopCameraListener mListener;
-    private CameraTestThread mLooperThread;
+    private HandlerThread mHandlerThread;
     private Handler mHandler;
+    private BlockingStateListener mCameraListener;
+    private CameraErrorCollector mCollector;
 
     @Override
     public void setContext(Context context) {
@@ -57,16 +73,53 @@
     protected void setUp() throws Exception {
         super.setUp();
 
-        mLooperThread = new CameraTestThread();
-        mHandler = mLooperThread.start();
+        /**
+         * 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", getContext().getCacheDir().toString());
+
+        mCameraListener = spy(new BlockingStateListener());
+
+        mHandlerThread = new HandlerThread(TAG);
+        mHandlerThread.start();
+        mHandler = new Handler(mHandlerThread.getLooper());
+        mCollector = new CameraErrorCollector();
     }
 
     @Override
     protected void tearDown() throws Exception {
-        mLooperThread.close();
+        mHandlerThread.quitSafely();
         mHandler = null;
 
-        super.tearDown();
+        try {
+            mCollector.verify();
+        } catch (Throwable e) {
+            // When new Exception(e) is used, exception info will be printed twice.
+            throw new Exception(e.getMessage());
+        } finally {
+            super.tearDown();
+        }
+    }
+
+    /**
+     * Verifies that the reason is in the range of public-only codes.
+     */
+    private static int checkCameraAccessExceptionReason(CameraAccessException e) {
+        int reason = e.getReason();
+
+        switch (reason) {
+            case CameraAccessException.CAMERA_DISABLED:
+            case CameraAccessException.CAMERA_DISCONNECTED:
+            case CameraAccessException.CAMERA_ERROR:
+                return reason;
+        }
+
+        fail("Invalid CameraAccessException code: " + reason);
+
+        return -1; // unreachable
     }
 
     public void testCameraManagerGetDeviceIdList() throws Exception {
@@ -135,12 +188,12 @@
         for (int i = 0; i < ids.length; i++) {
             invalidId.append(ids[i]);
         }
+
         try {
-            CameraCharacteristics props = mCameraManager.getCameraCharacteristics(
+            mCameraManager.getCameraCharacteristics(
                 invalidId.toString());
             fail(String.format("Accepted invalid camera ID: %s", invalidId.toString()));
-        }
-        catch (IllegalArgumentException e) {
+        } catch (IllegalArgumentException e) {
             // This is the exception that should be thrown in this case.
         }
     }
@@ -150,84 +203,259 @@
         String[] ids = mCameraManager.getCameraIdList();
         for (int i = 0; i < ids.length; i++) {
             for (int j = 0; j < NUM_CAMERA_REOPENS; j++) {
-                CameraDevice camera = CameraTestUtils.openCamera(mCameraManager, ids[i], mHandler);
-                assertNotNull(
-                    String.format("Failed to open camera device ID: %s", ids[i]), camera);
-                camera.close();
+                CameraDevice camera = null;
+                try {
+                    MockStateListener mockListener = MockStateListener.mock();
+                    mCameraListener = new BlockingStateListener(mockListener);
+
+                    mCameraManager.openCamera(ids[i], mCameraListener, mHandler);
+
+                    // Block until unConfigured
+                    mCameraListener.waitForState(BlockingStateListener.STATE_OPENED,
+                            CameraTestUtils.CAMERA_IDLE_TIMEOUT_MS);
+
+                    // Ensure state transitions are in right order:
+                    // -- 1) Opened
+                    // Ensure no other state transitions have occurred:
+                    camera = verifyCameraStateOpened(ids[i], mockListener);
+                } finally {
+                    if (camera != null) {
+                        camera.close();
+                    }
+                }
             }
         }
     }
 
     /**
-     * Test: that all camera devices can be open at the same time, or the appropriate
-     * exception is thrown if this can't be done.
+     * Test: one or more camera devices can be open at the same time, or the right error state
+     * is set if this can't be done.
      */
     public void testCameraManagerOpenAllCameras() throws Exception {
         String[] ids = mCameraManager.getCameraIdList();
-        CameraDevice[] cameras = new CameraDevice[ids.length];
+        assertNotNull("Camera ids shouldn't be null", ids);
+
+        // Skip test if the device doesn't have multiple cameras.
+        if (ids.length <= 1) {
+            return;
+        }
+
+        List<CameraDevice> cameraList = new ArrayList<CameraDevice>();
+        List<MockStateListener> listenerList = new ArrayList<MockStateListener>();
+        List<BlockingStateListener> blockingListenerList = new ArrayList<BlockingStateListener>();
         try {
             for (int i = 0; i < ids.length; i++) {
-                try {
-                    cameras[i] = CameraTestUtils.openCamera(mCameraManager, ids[i], mHandler);
+                // Ignore state changes from other cameras
+                MockStateListener mockListener = MockStateListener.mock();
+                mCameraListener = new BlockingStateListener(mockListener);
 
-                    /**
-                     * If the camera can't be opened, should throw an exception, rather than
-                     * returning null.
-                     */
-                    assertNotNull(
-                        String.format("Failed to open camera device ID: %s", ids[i]),
-                        cameras[i]);
+                /**
+                 * Track whether or not we got a synchronous error from openCamera.
+                 *
+                 * A synchronous error must also be accompanied by an asynchronous
+                 * StateListener#onError callback.
+                 */
+                boolean expectingError = false;
+
+                String cameraId = ids[i];
+                try {
+                    mCameraManager.openCamera(cameraId, mCameraListener,
+                            mHandler);
+                } catch (CameraAccessException e) {
+                    if (checkCameraAccessExceptionReason(e) == CameraAccessException.CAMERA_ERROR) {
+                        expectingError = true;
+                    } else {
+                        // TODO: We should handle a Disabled camera by passing here and elsewhere
+                        fail("Camera must not be disconnected or disabled for this test" + ids[i]);
+                    }
                 }
-                catch (CameraAccessException e) {
-                    /**
-                     * This is the expected behavior if the camera can't be opened due to
-                     * limitations on how many devices can be open simultaneously.
-                     */
-                    assertEquals(
-                        String.format("Invalid exception reason: %s", e.getReason()),
-                        CameraAccessException.MAX_CAMERAS_IN_USE, e.getReason());
+
+                List<Integer> expectedStates = new ArrayList<Integer>();
+                expectedStates.add(BlockingStateListener.STATE_OPENED);
+                expectedStates.add(BlockingStateListener.STATE_ERROR);
+                int state = mCameraListener.waitForAnyOfStates(
+                        expectedStates, CameraTestUtils.CAMERA_IDLE_TIMEOUT_MS);
+
+                // It's possible that we got an asynchronous error transition only. This is ok.
+                if (expectingError) {
+                    assertEquals("Throwing a CAMERA_ERROR exception must be accompanied with a " +
+                            "StateListener#onError callback",
+                            BlockingStateListener.STATE_ERROR, state);
                 }
+
+                /**
+                 * Two situations are considered passing:
+                 * 1) The camera opened successfully.
+                 *     => No error must be set.
+                 * 2) The camera did not open because there were too many other cameras opened.
+                 *     => Only MAX_CAMERAS_IN_USE error must be set.
+                 *
+                 * Any other situation is considered a failure.
+                 *
+                 * For simplicity we treat disconnecting asynchronously as a failure, so
+                 * camera devices should not be physically unplugged during this test.
+                 */
+
+                CameraDevice camera;
+                if (state == BlockingStateListener.STATE_ERROR) {
+                    // Camera did not open because too many other cameras were opened
+                    // => onError called exactly once with a non-null camera
+                    assertTrue("At least one camera must be opened successfully",
+                            cameraList.size() > 0);
+
+                    ArgumentCaptor<CameraDevice> argument =
+                            ArgumentCaptor.forClass(CameraDevice.class);
+
+                    verify(mockListener)
+                            .onError(
+                                    argument.capture(),
+                                    eq(CameraDevice.StateListener.ERROR_MAX_CAMERAS_IN_USE));
+                    verifyNoMoreInteractions(mockListener);
+
+                    camera = argument.getValue();
+                    assertNotNull("Expected a non-null camera for the error transition for ID: "
+                            + ids[i], camera);
+                } else if (state == BlockingStateListener.STATE_OPENED) {
+                    // Camera opened successfully.
+                    // => onOpened called exactly once
+                    camera = verifyCameraStateOpened(cameraId,
+                            mockListener);
+                } else {
+                    fail("Unexpected state " + state);
+                    camera = null; // unreachable. but need this for java compiler
+                }
+
+                // Keep track of cameras so we can close it later
+                cameraList.add(camera);
+                listenerList.add(mockListener);
+                blockingListenerList.add(mCameraListener);
+            }
+        } finally {
+            for (CameraDevice camera : cameraList) {
+                camera.close();
+            }
+            for (BlockingStateListener blockingListener : blockingListenerList) {
+                blockingListener.waitForState(
+                        BlockingStateListener.STATE_CLOSED,
+                        CameraTestUtils.CAMERA_IDLE_TIMEOUT_MS);
             }
         }
-        finally {
-            for (int i = 0; i < ids.length; i++) {
-                if (cameras[i] != null) {
-                    cameras[i].close();
-                }
-            }
+
+        /*
+         * Ensure that no state transitions have bled through from one camera to another
+         * after closing the cameras.
+         */
+        int i = 0;
+        for (MockStateListener listener : listenerList) {
+            CameraDevice camera = cameraList.get(i);
+
+            verify(listener).onClosed(eq(camera));
+            verifyNoMoreInteractions(listener);
+            i++;
+            // Only a #close can happen on the camera since we were done with it.
+            // Also nothing else should've happened between the close and the open.
         }
     }
 
-    // Test: that opening the same device multiple times throws the right exception.
+    /**
+     * Verifies the camera in this listener was opened and then unconfigured exactly once.
+     *
+     * <p>This assumes that no other action to the camera has been done (e.g.
+     * it hasn't been configured, or closed, or disconnected). Verification is
+     * performed immediately without any timeouts.</p>
+     *
+     * <p>This checks that the state has previously changed first for opened and then unconfigured.
+     * Any other state transitions will fail. A test failure is thrown if verification fails.</p>
+     *
+     * @param cameraId Camera identifier
+     * @param listener Listener which was passed to {@link CameraManager#openCamera}
+     *
+     * @return The camera device (non-{@code null}).
+     */
+    private static CameraDevice verifyCameraStateOpened(String cameraId,
+            MockStateListener listener) {
+        ArgumentCaptor<CameraDevice> argument =
+                ArgumentCaptor.forClass(CameraDevice.class);
+        InOrder inOrder = inOrder(listener);
+
+        /**
+         * State transitions (in that order):
+         *  1) onOpened
+         *
+         * No other transitions must occur for successful #openCamera
+         */
+        inOrder.verify(listener)
+                .onOpened(argument.capture());
+
+        CameraDevice camera = argument.getValue();
+        assertNotNull(
+                String.format("Failed to open camera device ID: %s", cameraId),
+                camera);
+
+        // Do not use inOrder here since that would skip anything called before onOpened
+        verifyNoMoreInteractions(listener);
+
+        return camera;
+    }
+
+    /**
+     * Test: that opening the same device multiple times and make sure the right
+     * error state is set.
+     */
     public void testCameraManagerOpenCameraTwice() throws Exception {
         String[] ids = mCameraManager.getCameraIdList();
-        CameraDevice[] cameras = new CameraDevice[2];
-        if (ids.length > 0) {
+
+        // Test across every camera device.
+        for (int i = 0; i < ids.length; ++i) {
+            CameraDevice successCamera = null;
+            mCollector.setCameraId(ids[i]);
+
             try {
-                cameras[0] = CameraTestUtils.openCamera(mCameraManager, ids[0], mHandler);
-                assertNotNull(
-                    String.format("Failed to open camera device ID: %s", ids[0]),
-                    cameras[0]);
+                MockStateListener mockSuccessListener = MockStateListener.mock();
+                MockStateListener mockFailListener = MockStateListener.mock();
+
+                BlockingStateListener successListener =
+                        new BlockingStateListener(mockSuccessListener);
+                BlockingStateListener failListener =
+                        new BlockingStateListener(mockFailListener);
+
+                mCameraManager.openCamera(ids[i], successListener, mHandler);
+
                 try {
-                    cameras[1] = CameraTestUtils.openCamera(mCameraManager, ids[0], mHandler);
-                    fail(String.format("Opened the same camera device twice ID: %s",
-                        ids[0]));
+                    mCameraManager.openCamera(ids[i], failListener,
+                            mHandler);
+                } catch (CameraAccessException e) {
+                    // Optional (but common). Camera might fail asynchronously only.
+                    // Don't assert here, otherwise, all subsequent tests will fail because the
+                    // opened camera is never closed.
+                    mCollector.expectEquals(
+                            "If second camera open fails immediately, must be due to"
+                            + "camera being busy for ID: " + ids[i],
+                            CameraAccessException.CAMERA_ERROR, e.getReason());
                 }
-                catch (CameraAccessException e) {
-                    /**
-                     * This is the expected behavior if the camera device is attempted to
-                     * be opened more than once.
-                     */
-                    assertEquals(
-                        String.format("Invalid exception reason: %s", e.getReason()),
-                        CameraAccessException.CAMERA_IN_USE, e.getReason());
-                }
-            }
-            finally {
-                for (int i = 0; i < 2; i++) {
-                    if (cameras[i] != null) {
-                        cameras[i].close();
-                    }
+
+                successListener.waitForState(BlockingStateListener.STATE_OPENED,
+                        CameraTestUtils.CAMERA_IDLE_TIMEOUT_MS);
+                // Have to get the successCamera here, otherwise, it won't be
+                // closed if STATE_ERROR timeout exception occurs.
+                ArgumentCaptor<CameraDevice> argument =
+                        ArgumentCaptor.forClass(CameraDevice.class);
+                verify(mockSuccessListener, atLeastOnce()).onOpened(argument.capture());
+
+                failListener.waitForState(BlockingStateListener.STATE_ERROR,
+                        CameraTestUtils.CAMERA_IDLE_TIMEOUT_MS);
+
+                successCamera = verifyCameraStateOpened(
+                        ids[i], mockSuccessListener);
+
+                verify(mockFailListener)
+                        .onError(
+                                and(notNull(CameraDevice.class), not(eq(successCamera))),
+                                eq(StateListener.ERROR_CAMERA_IN_USE));
+                verifyNoMoreInteractions(mockFailListener);
+            } finally {
+                if (successCamera != null) {
+                    successCamera.close();
                 }
             }
         }
@@ -252,13 +480,12 @@
      * 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, mHandler);
+        mCameraManager.addAvailabilityListener(mListener, mHandler);
+        mCameraManager.removeAvailabilityListener(mListener);
+        mCameraManager.removeAvailabilityListener(mListener);
 
-        mCameraManager.removeAvailabilityListener(mListener);
-        mCameraManager.addAvailabilityListener(mListener, callbackHandler);
-        mCameraManager.addAvailabilityListener(mListener, callbackHandler);
-        mCameraManager.removeAvailabilityListener(mListener);
-        mCameraManager.removeAvailabilityListener(mListener);
+        // TODO: test the listener callbacks
     }
 }
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..bbfd7b5 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/CameraTestUtils.java
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/CameraTestUtils.java
@@ -16,43 +16,115 @@
 
 package android.hardware.camera2.cts;
 
+import static com.android.ex.camera2.blocking.BlockingStateListener.*;
+
+import android.graphics.BitmapFactory;
 import android.graphics.ImageFormat;
+import android.graphics.PointF;
+import android.graphics.Rect;
 import android.hardware.camera2.CameraAccessException;
+import android.hardware.camera2.CameraCaptureSession;
 import android.hardware.camera2.CameraDevice;
 import android.hardware.camera2.CameraManager;
-import android.hardware.camera2.CameraMetadata;
 import android.hardware.camera2.CameraCharacteristics;
-import android.hardware.camera2.Size;
+import android.hardware.camera2.CaptureFailure;
+import android.hardware.camera2.CaptureRequest;
+import android.hardware.camera2.CaptureResult;
+import android.hardware.camera2.TotalCaptureResult;
+import android.util.Size;
+import android.hardware.camera2.params.MeteringRectangle;
+import android.hardware.camera2.params.StreamConfigurationMap;
 import android.media.Image;
 import android.media.ImageReader;
 import android.media.Image.Plane;
 import android.os.Handler;
 import android.util.Log;
+import android.view.Surface;
 
 import com.android.ex.camera2.blocking.BlockingCameraManager;
 import com.android.ex.camera2.blocking.BlockingCameraManager.BlockingOpenException;
+import com.android.ex.camera2.blocking.BlockingSessionListener;
+import com.android.ex.camera2.blocking.BlockingStateListener;
+import com.android.ex.camera2.exceptions.TimeoutRuntimeException;
 
 import junit.framework.Assert;
 
+import org.mockito.Mockito;
+
 import java.io.FileOutputStream;
 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;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.TimeUnit;
 
 /**
  * A package private utility class for wrapping up the camera2 cts test common utility functions
  */
-class CameraTestUtils extends Assert {
+public class CameraTestUtils extends Assert {
     private static final String TAG = "CameraTestUtils";
     private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
-
+    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+    // 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 final int CAMERA_UNCONFIGURED_TIMEOUT_MS = 1000;
+    public static final int CAMERA_CONFIGURE_TIMEOUT_MS = 2000;
+    public static final int CAPTURE_RESULT_TIMEOUT_MS = 3000;
+    public static final int CAPTURE_IMAGE_TIMEOUT_MS = 3000;
 
+    public static final int SESSION_CONFIGURE_TIMEOUT_MS = 2000;
+    public static final int SESSION_CLOSE_TIMEOUT_MS = 2000;
+    public static final int SESSION_READY_TIMEOUT_MS = 2000;
+    public static final int SESSION_ACTIVE_TIMEOUT_MS = 1000;
+
+    public static final int MAX_READER_IMAGES = 5;
+
+    /**
+     * Create an {@link android.media.ImageReader} object and get the surface.
+     *
+     * @param size The size of this ImageReader to be created.
+     * @param format The format of this ImageReader to be created
+     * @param maxNumImages The max number of images that can be acquired simultaneously.
+     * @param listener The listener used by this ImageReader to notify callbacks.
+     * @param handler The handler to use for any listener callbacks.
+     */
+    public static ImageReader makeImageReader(Size size, int format, int maxNumImages,
+            ImageReader.OnImageAvailableListener listener, Handler handler) {
+        ImageReader reader =  ImageReader.newInstance(size.getWidth(), size.getHeight(), format,
+                maxNumImages);
+        reader.setOnImageAvailableListener(listener, handler);
+        if (VERBOSE) Log.v(TAG, "Created ImageReader size " + size);
+        return reader;
+    }
+
+    /**
+     * Close pending images and clean up an {@link android.media.ImageReader} object.
+     * @param reader an {@link android.media.ImageReader} to close.
+     */
+    public static void closeImageReader(ImageReader reader) {
+        if (reader != null) {
+            reader.close();
+        }
+    }
+
+    /**
+     * Dummy listener that release the image immediately once it is available.
+     *
+     * <p>
+     * It can be used for the case where we don't care the image data at all.
+     * </p>
+     */
     public static class ImageDropperListener implements ImageReader.OnImageAvailableListener {
         @Override
         public void onImageAvailable(ImageReader reader) {
@@ -68,15 +140,154 @@
     }
 
     /**
+     * Image listener that release the image immediately after validating the image
+     */
+    public static class ImageVerifierListener implements ImageReader.OnImageAvailableListener {
+        private Size mSize;
+        private int mFormat;
+
+        public ImageVerifierListener(Size sz, int format) {
+            mSize = sz;
+            mFormat = format;
+        }
+
+        @Override
+        public void onImageAvailable(ImageReader reader) {
+            Image image = null;
+            try {
+                image = reader.acquireNextImage();
+            } finally {
+                if (image != null) {
+                    validateImage(image, mSize.getWidth(), mSize.getHeight(), mFormat, null);
+                    image.close();
+                }
+            }
+        }
+    }
+
+    public static class SimpleImageReaderListener
+            implements ImageReader.OnImageAvailableListener {
+        private final LinkedBlockingQueue<Image> mQueue =
+                new LinkedBlockingQueue<Image>();
+
+        @Override
+        public void onImageAvailable(ImageReader reader) {
+            try {
+                mQueue.put(reader.acquireNextImage());
+            } catch (InterruptedException e) {
+                throw new UnsupportedOperationException(
+                        "Can't handle InterruptedException in onImageAvailable");
+            }
+        }
+
+        /**
+         * Get an image from the image reader.
+         *
+         * @param timeout Timeout value for the wait.
+         * @return The image from the image reader.
+         */
+        public Image getImage(long timeout) throws InterruptedException {
+            Image image = mQueue.poll(timeout, TimeUnit.MILLISECONDS);
+            assertNotNull("Wait for an image timed out in " + timeout + "ms", image);
+            return image;
+        }
+    }
+
+    public static class SimpleCaptureListener extends CameraCaptureSession.CaptureListener {
+        private final LinkedBlockingQueue<CaptureResult> mQueue =
+                new LinkedBlockingQueue<CaptureResult>();
+
+        @Override
+        public void onCaptureStarted(CameraCaptureSession session, CaptureRequest request, long timestamp)
+        {
+        }
+
+        @Override
+        public void onCaptureCompleted(CameraCaptureSession session, CaptureRequest request,
+                TotalCaptureResult result) {
+            try {
+                mQueue.put(result);
+            } catch (InterruptedException e) {
+                throw new UnsupportedOperationException(
+                        "Can't handle InterruptedException in onCaptureCompleted");
+            }
+        }
+
+        @Override
+        public void onCaptureFailed(CameraCaptureSession session, CaptureRequest request,
+                CaptureFailure failure) {
+        }
+
+        @Override
+        public void onCaptureSequenceCompleted(CameraCaptureSession session, int sequenceId,
+                long frameNumber) {
+        }
+
+        public CaptureResult getCaptureResult(long timeout) {
+            try {
+                CaptureResult result = mQueue.poll(timeout, TimeUnit.MILLISECONDS);
+                assertNotNull("Wait for a capture result timed out in " + timeout + "ms", result);
+                return result;
+            } catch (InterruptedException e) {
+                throw new UnsupportedOperationException("Unhandled interrupted exception", e);
+            }
+        }
+
+        /**
+         * Get the {@link #CaptureResult capture result} for a given
+         * {@link #CaptureRequest capture request}.
+         *
+         * @param myRequest The {@link #CaptureRequest capture request} whose
+         *            corresponding {@link #CaptureResult capture result} was
+         *            being waited for
+         * @param numResultsWait Number of frames to wait for the capture result
+         *            before timeout.
+         * @throws TimeoutRuntimeException If more than numResultsWait results are
+         *            seen before the result matching myRequest arrives, or each
+         *            individual wait for result times out after
+         *            {@value #CAPTURE_RESULT_TIMEOUT_MS}ms.
+         */
+        public CaptureResult getCaptureResultForRequest(CaptureRequest myRequest,
+                int numResultsWait) {
+            if (numResultsWait < 0) {
+                throw new IllegalArgumentException("numResultsWait must be no less than 0");
+            }
+
+            CaptureResult result;
+            int i = 0;
+            do {
+                result = getCaptureResult(CAPTURE_RESULT_TIMEOUT_MS);
+                if (result.getRequest().equals(myRequest)) {
+                    return result;
+                }
+            } while (i++ < numResultsWait);
+
+            throw new TimeoutRuntimeException("Unable to get the expected capture result after "
+                    + "waiting for " + numResultsWait + " results");
+        }
+
+        public boolean hasMoreResults()
+        {
+            return mQueue.isEmpty();
+        }
+    }
+
+    /**
      * Block until the camera is opened.
      *
      * <p>Don't use this to test #onDisconnected/#onError since this will throw
      * an AssertionError if it fails to open the camera device.</p>
      *
      * @return CameraDevice opened camera device
-     * @throws BlockingOpenException
      *
-     * @throws AssertionError if the camera fails to open (or times out)
+     * @throws IllegalArgumentException
+     *            If the handler is null, or if the handler's looper is current.
+     * @throws CameraAccessException
+     *            If open fails immediately.
+     * @throws BlockingOpenException
+     *            If open fails after blocking for some amount of time.
+     * @throws TimeoutRuntimeException
+     *            If opening times out. Typically unrecoverable.
      */
     public static CameraDevice openCamera(CameraManager manager, String cameraId,
             CameraDevice.StateListener listener, Handler handler) throws CameraAccessException,
@@ -101,9 +312,14 @@
      * <p>Don't use this to test #onDisconnected/#onError since this will throw
      * an AssertionError if it fails to open the camera device.</p>
      *
-     * @return CameraDevice opened camera device
-     *
-     * @throws AssertionError if the camera fails to open (or times out)
+     * @throws IllegalArgumentException
+     *            If the handler is null, or if the handler's looper is current.
+     * @throws CameraAccessException
+     *            If open fails immediately.
+     * @throws BlockingOpenException
+     *            If open fails after blocking for some amount of time.
+     * @throws TimeoutRuntimeException
+     *            If opening times out. Typically unrecoverable.
      */
     public static CameraDevice openCamera(CameraManager manager, String cameraId, Handler handler)
             throws CameraAccessException,
@@ -111,6 +327,23 @@
         return openCamera(manager, cameraId, /*listener*/null, handler);
     }
 
+    /**
+     * Configure a new camera session with output surfaces.
+     *
+     * @param camera The CameraDevice to be configured.
+     * @param outputSurfaces The surface list that used for camera output.
+     * @param listener The callback CameraDevice will notify when capture results are available.
+     */
+    public static CameraCaptureSession configureCameraSession(CameraDevice camera,
+            List<Surface> outputSurfaces,
+            CameraCaptureSession.StateListener listener, Handler handler)
+            throws CameraAccessException {
+        BlockingSessionListener sessionListener = new BlockingSessionListener(listener);
+        camera.createCaptureSession(outputSurfaces, sessionListener, handler);
+
+        return sessionListener.waitAndGetSession(SESSION_CONFIGURE_TIMEOUT_MS);
+    }
+
     public static <T> void assertArrayNotEmpty(T arr, String message) {
         assertTrue(message, arr != null && Array.getLength(arr) > 0);
     }
@@ -121,9 +354,7 @@
     public static void checkYuvFormat(int format) {
         if ((format != ImageFormat.YUV_420_888) &&
                 (format != ImageFormat.NV21) &&
-                (format != ImageFormat.YV12) &&
-                (format != ImageFormat.Y8) &&
-                (format != ImageFormat.Y16)) {
+                (format != ImageFormat.YV12)) {
             fail("Wrong formats: " + format);
         }
     }
@@ -170,8 +401,9 @@
         if (format == ImageFormat.JPEG) {
             buffer = planes[0].getBuffer();
             assertNotNull("Fail to get jpeg ByteBuffer", buffer);
-            data = new byte[buffer.capacity()];
+            data = new byte[buffer.remaining()];
             buffer.get(data);
+            buffer.rewind();
             return data;
         }
 
@@ -183,7 +415,6 @@
             buffer = planes[i].getBuffer();
             assertNotNull("Fail to get bytebuffer from plane", buffer);
             rowStride = planes[i].getRowStride();
-            assertTrue("rowStride should be no less than width", rowStride >= width);
             pixelStride = planes[i].getPixelStride();
             assertTrue("pixel stride " + pixelStride + " is invalid", pixelStride > 0);
             if (VERBOSE) {
@@ -216,6 +447,7 @@
                 }
             }
             if (VERBOSE) Log.v(TAG, "Finished reading data from plane " + i);
+            buffer.rewind();
         }
         return data;
     }
@@ -223,7 +455,7 @@
     /**
      * <p>Check android image format validity for an image, only support below formats:</p>
      *
-     * <p>YUV_420_888/NV21/YV12/Y8/Y16, can add more for future</p>
+     * <p>YUV_420_888/NV21/YV12, can add more for future</p>
      */
     public static void checkAndroidImageFormat(Image image) {
         int format = image.getFormat();
@@ -234,11 +466,8 @@
             case ImageFormat.YV12:
                 assertEquals("YUV420 format Images should have 3 planes", 3, planes.length);
                 break;
-            case ImageFormat.Y8:
-            case ImageFormat.Y16:
-                assertEquals("Y8/Y16 Image should have 1 plane", 1, planes.length);
-                break;
             case ImageFormat.JPEG:
+            case ImageFormat.RAW_SENSOR:
                 assertEquals("Jpeg Image should have one plane", 1, planes.length);
                 break;
             default:
@@ -263,31 +492,480 @@
         }
     }
 
+    /**
+     * Get the available output sizes for the user-defined {@code format}.
+     *
+     * <p>Note that implementation-defined/hidden formats are not supported.</p>
+     */
     public static Size[] getSupportedSizeForFormat(int format, String cameraId,
-            CameraManager cameraManager) throws Exception {
-        CameraMetadata.Key<Size[]> key = null;
+            CameraManager cameraManager) throws CameraAccessException {
         CameraCharacteristics properties = cameraManager.getCameraCharacteristics(cameraId);
         assertNotNull("Can't get camera characteristics!", properties);
         if (VERBOSE) {
             Log.v(TAG, "get camera characteristics for camera: " + cameraId);
         }
-        switch (format) {
-            case ImageFormat.JPEG:
-                key = CameraCharacteristics.SCALER_AVAILABLE_JPEG_SIZES;
-                break;
-            case ImageFormat.YUV_420_888:
-            case ImageFormat.YV12:
-            case ImageFormat.NV21:
-            case ImageFormat.Y8:
-            case ImageFormat.Y16:
-                key = CameraCharacteristics.SCALER_AVAILABLE_PROCESSED_SIZES;
-                break;
-            default:
-                throw new UnsupportedOperationException(
-                        String.format("Invalid format specified 0x%x", format));
-        }
-        Size[] availableSizes = properties.get(key);
+        StreamConfigurationMap configMap =
+                properties.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
+        Size[] availableSizes = configMap.getOutputSizes(format);
+        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.
+     *
+     * <p>If two the areas of two sizes are same, compare the widths.</p>
+     */
+    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();
+            if (left == right) {
+                left = lhs.getWidth();
+                right = rhs.getWidth();
+            }
+            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 CameraAccessException {
+        return getSortedSizesForFormat(cameraId, cameraManager, ImageFormat.YUV_420_888, bound);
+    }
+
+    /**
+     * Get a sorted list of sizes from a given size list.
+     *
+     * <p>
+     * The size is compare by area it covers, if the areas are same, then
+     * compare the widths.
+     * </p>
+     *
+     * @param sizeList The input size list to be sorted
+     * @param ascending True if the order is ascending, otherwise descending order
+     * @return The ordered list of sizes
+     */
+    static public List<Size> getAscendingOrderSizes(final List<Size> sizeList, boolean ascending) {
+        if (sizeList == null) {
+            throw new IllegalArgumentException("sizeList shouldn't be null");
+        }
+
+        Comparator<Size> comparator = new SizeComparator();
+        List<Size> sortedSizes = new ArrayList<Size>();
+        sortedSizes.addAll(sizeList);
+        Collections.sort(sortedSizes, comparator);
+        if (!ascending) {
+            Collections.reverse(sortedSizes);
+        }
+
+        return sortedSizes;
+    }
+
+    /**
+     * Get sorted (descending order) size list for given format. Remove the sizes larger than
+     * the bound. If the bound is null, don't do the size bound filtering.
+     */
+    static private List<Size> getSortedSizesForFormat(String cameraId,
+            CameraManager cameraManager, int format, Size bound) throws CameraAccessException {
+        Comparator<Size> comparator = new SizeComparator();
+        Size[] sizes = getSupportedSizeForFormat(format, cameraId, cameraManager);
+        List<Size> sortedSizes = null;
+        if (bound != null) {
+            sortedSizes = new ArrayList<Size>(/*capacity*/1);
+            for (Size sz : sizes) {
+                if (comparator.compare(sz, bound) <= 0) {
+                    sortedSizes.add(sz);
+                }
+            }
+        } else {
+            sortedSizes = Arrays.asList(sizes);
+        }
+        assertTrue("Supported size list should have at least one element",
+                sortedSizes.size() > 0);
+
+        Collections.sort(sortedSizes, comparator);
+        // Make it in descending order.
+        Collections.reverse(sortedSizes);
+        return sortedSizes;
+    }
+
+    /**
+     * Get supported video size list for a given camera device.
+     *
+     * <p>
+     * Filter out the sizes that are larger than the bound. If the bound is
+     * null, don't do the size bound filtering.
+     * </p>
+     */
+    static public List<Size> getSupportedVideoSizes(String cameraId,
+            CameraManager cameraManager, Size bound) throws CameraAccessException {
+        return getSortedSizesForFormat(cameraId, cameraManager, ImageFormat.YUV_420_888, bound);
+    }
+
+    /**
+     * Get supported video size list (descending order) for a given camera device.
+     *
+     * <p>
+     * Filter out the sizes that are larger than the bound. If the bound is
+     * null, don't do the size bound filtering.
+     * </p>
+     */
+    static public List<Size> getSupportedStillSizes(String cameraId,
+            CameraManager cameraManager, Size bound) throws CameraAccessException {
+        return getSortedSizesForFormat(cameraId, cameraManager, ImageFormat.JPEG, bound);
+    }
+
+    static public Size getMinPreviewSize(String cameraId, CameraManager cameraManager)
+            throws CameraAccessException {
+        List<Size> sizes = getSupportedPreviewSizes(cameraId, cameraManager, null);
+        return sizes.get(sizes.size() - 1);
+    }
+
+    /**
+     * Get max supported preview size for a camera device.
+     */
+    static public Size getMaxPreviewSize(String cameraId, CameraManager cameraManager)
+            throws CameraAccessException {
+        return getMaxPreviewSize(cameraId, cameraManager, /*bound*/null);
+    }
+
+    /**
+     * Get max preview size for a camera device in the supported sizes that are no larger
+     * than the bound.
+     */
+    static public Size getMaxPreviewSize(String cameraId, CameraManager cameraManager, Size bound)
+            throws CameraAccessException {
+        List<Size> sizes = getSupportedPreviewSizes(cameraId, cameraManager, bound);
+        return sizes.get(0);
+    }
+
+    /**
+     * Get the largest size by area.
+     *
+     * @param sizes an array of sizes, must have at least 1 element
+     *
+     * @return Largest Size
+     *
+     * @throws IllegalArgumentException if sizes was null or had 0 elements
+     */
+    public static Size getMaxSize(Size[] sizes) {
+        if (sizes == null || sizes.length == 0) {
+            throw new IllegalArgumentException("sizes was empty");
+        }
+
+        Size sz = sizes[0];
+        for (Size size : sizes) {
+            if (size.getWidth() * size.getHeight() > sz.getWidth() * sz.getHeight()) {
+                sz = size;
+            }
+        }
+
+        return sz;
+    }
+
+    /**
+     * Get object array from byte array.
+     *
+     * @param array Input byte array to be converted
+     * @return Byte object array converted from input byte array
+     */
+    public static Byte[] toObject(byte[] array) {
+        return convertPrimitiveArrayToObjectArray(array, Byte.class);
+    }
+
+    /**
+     * Get object array from int array.
+     *
+     * @param array Input int array to be converted
+     * @return Integer object array converted from input int array
+     */
+    public static Integer[] toObject(int[] array) {
+        return convertPrimitiveArrayToObjectArray(array, Integer.class);
+    }
+
+    /**
+     * Get object array from float array.
+     *
+     * @param array Input float array to be converted
+     * @return Float object array converted from input float array
+     */
+    public static Float[] toObject(float[] array) {
+        return convertPrimitiveArrayToObjectArray(array, Float.class);
+    }
+
+    /**
+     * Get object array from double array.
+     *
+     * @param array Input double array to be converted
+     * @return Double object array converted from input double array
+     */
+    public static Double[] toObject(double[] array) {
+        return convertPrimitiveArrayToObjectArray(array, Double.class);
+    }
+
+    /**
+     * Convert a primitive input array into its object array version (e.g. from int[] to Integer[]).
+     *
+     * @param array Input array object
+     * @param wrapperClass The boxed class it converts to
+     * @return Boxed version of primitive array
+     */
+    private static <T> T[] convertPrimitiveArrayToObjectArray(final Object array,
+            final Class<T> wrapperClass) {
+        // getLength does the null check and isArray check already.
+        int arrayLength = Array.getLength(array);
+        if (arrayLength == 0) {
+            throw new IllegalArgumentException("Input array shouldn't be empty");
+        }
+
+        @SuppressWarnings("unchecked")
+        final T[] result = (T[]) Array.newInstance(wrapperClass, arrayLength);
+        for (int i = 0; i < arrayLength; i++) {
+            Array.set(result, i, Array.get(array, i));
+        }
+        return result;
+    }
+
+    /**
+     * Validate image based on format and size.
+     * <p>
+     * Only RAW_SENSOR, YUV420_888 and JPEG formats are supported. Calling this
+     * method with other formats will cause a UnsupportedOperationException.
+     * </p>
+     *
+     * @param image The image to be validated.
+     * @param width The image width.
+     * @param height The image height.
+     * @param format The image format.
+     * @param filePath The debug dump file path, null if don't want to dump to
+     *            file.
+     * @throws UnsupportedOperationException if calling with format other than
+     *             RAW_SENSOR, YUV420_888 or JPEG.
+     */
+    public static void validateImage(Image image, int width, int height, int format,
+            String filePath) {
+        checkImage(image, width, height, format);
+
+        /**
+         * TODO: validate timestamp:
+         * 1. capture result timestamp against the image timestamp (need
+         * consider frame drops)
+         * 2. timestamps should be monotonically increasing for different requests
+         */
+        if(VERBOSE) Log.v(TAG, "validating Image");
+        byte[] data = getDataFromImage(image);
+        assertTrue("Invalid image data", data != null && data.length > 0);
+
+        switch (format) {
+            case ImageFormat.JPEG:
+                validateJpegData(data, width, height, filePath);
+                break;
+            case ImageFormat.YUV_420_888:
+                validateYuvData(data, width, height, format, image.getTimestamp(), filePath);
+                break;
+            case ImageFormat.RAW_SENSOR:
+                validateRaw16Data(data, width, height, format, image.getTimestamp(), filePath);
+                break;
+            default:
+                throw new UnsupportedOperationException("Unsupported format for validation: "
+                        + format);
+        }
+    }
+
+    /**
+     * Provide a mock for {@link CameraDevice.StateListener}.
+     *
+     * <p>Only useful because mockito can't mock {@link CameraDevice.StateListener} which is an
+     * abstract class.</p>
+     *
+     * <p>
+     * Use this instead of other classes when needing to verify interactions, since
+     * trying to spy on {@link BlockingStateListener} (or others) will cause unnecessary extra
+     * interactions which will cause false test failures.
+     * </p>
+     *
+     */
+    public static class MockStateListener extends CameraDevice.StateListener {
+
+        @Override
+        public void onOpened(CameraDevice camera) {
+        }
+
+        @Override
+        public void onDisconnected(CameraDevice camera) {
+        }
+
+        @Override
+        public void onError(CameraDevice camera, int error) {
+        }
+
+        private MockStateListener() {}
+
+        /**
+         * Create a Mockito-ready mocked StateListener.
+         */
+        public static MockStateListener mock() {
+            return Mockito.spy(new MockStateListener());
+        }
+    }
+
+    private static void validateJpegData(byte[] jpegData, int width, int height, String filePath) {
+        BitmapFactory.Options bmpOptions = new BitmapFactory.Options();
+        // DecodeBound mode: only parse the frame header to get width/height.
+        // it doesn't decode the pixel.
+        bmpOptions.inJustDecodeBounds = true;
+        BitmapFactory.decodeByteArray(jpegData, 0, jpegData.length, bmpOptions);
+        assertEquals(width, bmpOptions.outWidth);
+        assertEquals(height, bmpOptions.outHeight);
+
+        // Pixel decoding mode: decode whole image. check if the image data
+        // is decodable here.
+        assertNotNull("Decoding jpeg failed",
+                BitmapFactory.decodeByteArray(jpegData, 0, jpegData.length));
+        if (DEBUG && filePath != null) {
+            String fileName =
+                    filePath + "/" + width + "x" + height + ".jpeg";
+            dumpFile(fileName, jpegData);
+        }
+    }
+
+    private static void validateYuvData(byte[] yuvData, int width, int height, int format,
+            long ts, String filePath) {
+        checkYuvFormat(format);
+        if (VERBOSE) Log.v(TAG, "Validating YUV data");
+        int expectedSize = width * height * ImageFormat.getBitsPerPixel(format) / 8;
+        assertEquals("Yuv data doesn't match", expectedSize, yuvData.length);
+
+        // TODO: Can add data validation for test pattern.
+
+        if (DEBUG && filePath != null) {
+            String fileName =
+                    filePath + "/" + width + "x" + height + "_" + ts / 1e6 + ".yuv";
+            dumpFile(fileName, yuvData);
+        }
+    }
+
+    private static void validateRaw16Data(byte[] rawData, int width, int height, int format,
+            long ts, String filePath) {
+        if (VERBOSE) Log.v(TAG, "Validating raw data");
+        int expectedSize = width * height * ImageFormat.getBitsPerPixel(format) / 8;
+        assertEquals("Yuv data doesn't match", expectedSize, rawData.length);
+
+        // TODO: Can add data validation for test pattern.
+
+        if (DEBUG && filePath != null) {
+            String fileName =
+                    filePath + "/" + width + "x" + height + "_" + ts / 1e6 + ".raw16";
+            dumpFile(fileName, rawData);
+        }
+
+        return;
+    }
+
+    public static <T> T getValueNotNull(CaptureResult result, CaptureResult.Key<T> key) {
+        if (result == null) {
+            throw new IllegalArgumentException("Result must not be null");
+        }
+
+        T value = result.get(key);
+        assertNotNull("Value of Key " + key.getName() + "shouldn't be null", value);
+        return value;
+    }
+
+    /**
+     * Get a crop region for a given zoom factor and center position.
+     * <p>
+     * The center position is normalized position in range of [0, 1.0], where
+     * (0, 0) represents top left corner, (1.0. 1.0) represents bottom right
+     * corner. The center position could limit the effective minimal zoom
+     * factor, for example, if the center position is (0.75, 0.75), the
+     * effective minimal zoom position becomes 2.0. If the requested zoom factor
+     * is smaller than 2.0, a crop region with 2.0 zoom factor will be returned.
+     * </p>
+     * <p>
+     * The aspect ratio of the crop region is maintained the same as the aspect
+     * ratio of active array.
+     * </p>
+     *
+     * @param zoomFactor The zoom factor to generate the crop region, it must be
+     *            >= 1.0
+     * @param center The normalized zoom center point that is in the range of [0, 1].
+     * @param maxZoom The max zoom factor supported by this device.
+     * @param activeArray The active array size of this device.
+     * @return crop region for the given normalized center and zoom factor.
+     */
+    public static Rect getCropRegionForZoom(float zoomFactor, final PointF center,
+            final float maxZoom, final Rect activeArray) {
+        if (zoomFactor < 1.0) {
+            throw new IllegalArgumentException("zoom factor " + zoomFactor + " should be >= 1.0");
+        }
+        if (center.x > 1.0 || center.x < 0) {
+            throw new IllegalArgumentException("center.x " + center.x
+                    + " should be in range of [0, 1.0]");
+        }
+        if (center.y > 1.0 || center.y < 0) {
+            throw new IllegalArgumentException("center.y " + center.y
+                    + " should be in range of [0, 1.0]");
+        }
+        if (maxZoom < 1.0) {
+            throw new IllegalArgumentException("max zoom factor " + maxZoom + " should be >= 1.0");
+        }
+        if (activeArray == null) {
+            throw new IllegalArgumentException("activeArray must not be null");
+        }
+
+        float minCenterLength = Math.min(Math.min(center.x, 1.0f - center.x),
+                Math.min(center.y, 1.0f - center.y));
+        float minEffectiveZoom =  0.5f / minCenterLength;
+        if (minEffectiveZoom > maxZoom) {
+            throw new IllegalArgumentException("Requested center " + center.toString() +
+                    " has minimal zoomable factor " + minEffectiveZoom + ", which exceeds max"
+                            + " zoom factor " + maxZoom);
+        }
+
+        if (zoomFactor < minEffectiveZoom) {
+            Log.w(TAG, "Requested zoomFactor " + zoomFactor + " > minimal zoomable factor "
+                    + minEffectiveZoom + ". It will be overwritten by " + minEffectiveZoom);
+            zoomFactor = minEffectiveZoom;
+        }
+
+        int cropCenterX = (int)(activeArray.width() * center.x);
+        int cropCenterY = (int)(activeArray.height() * center.y);
+        int cropWidth = (int) (activeArray.width() / zoomFactor);
+        int cropHeight = (int) (activeArray.height() / zoomFactor);
+
+        return new Rect(
+                /*left*/cropCenterX - cropWidth / 2,
+                /*top*/cropCenterY - cropHeight / 2,
+                /*right*/ cropCenterX + cropWidth / 2 - 1,
+                /*bottom*/cropCenterY + cropHeight / 2 - 1);
+    }
+
+    /**
+     * Calculate output 3A region from the intersection of input 3A region and cropped region.
+     *
+     * @param requestRegions The input 3A regions
+     * @param cropRect The cropped region
+     * @return expected 3A regions output in capture result
+     */
+    public static MeteringRectangle[] getExpectedOutputRegion(
+            MeteringRectangle[] requestRegions, Rect cropRect){
+        MeteringRectangle[] resultRegions = new MeteringRectangle[requestRegions.length];
+        for (int i = 0; i < requestRegions.length; i++) {
+            Rect requestRect = requestRegions[i].getRect();
+            Rect resultRect = new Rect();
+            assertTrue("Input 3A region must intersect cropped region",
+                        resultRect.setIntersect(requestRect, cropRect));
+            resultRegions[i] = new MeteringRectangle(
+                    resultRect,
+                    requestRegions[i].getMeteringWeight());
+        }
+        return resultRegions;
+    }
+}
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/CaptureRequestTest.java b/tests/tests/hardware/src/android/hardware/camera2/cts/CaptureRequestTest.java
new file mode 100644
index 0000000..ec79151
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/CaptureRequestTest.java
@@ -0,0 +1,2188 @@
+/*
+ * 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 static android.hardware.camera2.cts.CameraTestUtils.*;
+import static android.hardware.camera2.CameraCharacteristics.*;
+
+import android.graphics.Point;
+import android.graphics.PointF;
+import android.graphics.Rect;
+import android.hardware.camera2.CameraCharacteristics;
+import android.hardware.camera2.CameraDevice;
+import android.hardware.camera2.CameraMetadata;
+import android.hardware.camera2.CaptureRequest;
+import android.hardware.camera2.CaptureResult;
+import android.hardware.camera2.cts.CameraTestUtils.SimpleCaptureListener;
+import android.hardware.camera2.cts.testcases.Camera2SurfaceViewTestCase;
+import android.hardware.camera2.params.ColorSpaceTransform;
+import android.hardware.camera2.params.Face;
+import android.hardware.camera2.params.LensShadingMap;
+import android.hardware.camera2.params.MeteringRectangle;
+import android.hardware.camera2.params.RggbChannelVector;
+import android.hardware.camera2.params.TonemapCurve;
+
+import android.util.Log;
+import android.util.Range;
+import android.util.Rational;
+import android.util.Size;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * <p>
+ * Basic test for camera CaptureRequest key controls.
+ * </p>
+ * <p>
+ * Several test categories are covered: manual sensor control, 3A control,
+ * manual ISP control and other per-frame control and synchronization.
+ * </p>
+ */
+public class CaptureRequestTest extends Camera2SurfaceViewTestCase {
+    private static final String TAG = "CaptureRequestTest";
+    private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
+    private static final int NUM_FRAMES_VERIFIED = 15;
+    private static final int NUM_FACE_DETECTION_FRAMES_VERIFIED = 60;
+    /** 30ms exposure time must be supported by full capability devices. */
+    private static final long DEFAULT_EXP_TIME_NS = 30000000L;
+    private static final int DEFAULT_SENSITIVITY = 100;
+    private static final int RGGB_COLOR_CHANNEL_COUNT = 4;
+    private static final int MAX_SHADING_MAP_SIZE = 64 * 64 * RGGB_COLOR_CHANNEL_COUNT;
+    private static final int MIN_SHADING_MAP_SIZE = 1 * 1 * RGGB_COLOR_CHANNEL_COUNT;
+    private static final long IGNORE_REQUESTED_EXPOSURE_TIME_CHECK = -1L;
+    private static final long EXPOSURE_TIME_BOUNDARY_50HZ_NS = 10000000L; // 10ms
+    private static final long EXPOSURE_TIME_BOUNDARY_60HZ_NS = 8333333L; // 8.3ms, Approximation.
+    private static final long EXPOSURE_TIME_ERROR_MARGIN_NS = 100000L; // 100us, Approximation.
+    private static final int SENSITIVITY_ERROR_MARGIN = 10; // 10
+    private static final int DEFAULT_NUM_EXPOSURE_TIME_STEPS = 3;
+    private static final int DEFAULT_NUM_SENSITIVITY_STEPS = 16;
+    private static final int DEFAULT_SENSITIVITY_STEP_SIZE = 100;
+    private static final int NUM_RESULTS_WAIT_TIMEOUT = 100;
+    private static final int NUM_FRAMES_WAITED_FOR_UNKNOWN_LATENCY = 8;
+    private static final int NUM_TEST_FOCUS_DISTANCES = 10;
+    // 5 percent error margin for calibrated device
+    private static final float FOCUS_DISTANCE_ERROR_PERCENT_CALIBRATED = 0.05f;
+    // 25 percent error margin for uncalibrated device
+    private static final float FOCUS_DISTANCE_ERROR_PERCENT_UNCALIBRATED = 0.25f;
+    // 10 percent error margin for approximate device
+    private static final float FOCUS_DISTANCE_ERROR_PERCENT_APPROXIMATE = 0.10f;
+    private static final int ANTI_FLICKERING_50HZ = 1;
+    private static final int ANTI_FLICKERING_60HZ = 2;
+
+    // 5 percent error margin for resulting crop regions
+    private static final float CROP_REGION_ERROR_PERCENT_DELTA = 0.05f;
+    // 1 percent error margin for centering the crop region
+    private static final float CROP_REGION_ERROR_PERCENT_CENTERED = 0.01f;
+
+    // Linear tone mapping curve example.
+    private static final float[] TONEMAP_CURVE_LINEAR = {0, 0, 1.0f, 1.0f};
+    // Standard sRGB tone mapping, per IEC 61966-2-1:1999, with 16 control points.
+    private static final float[] TONEMAP_CURVE_SRGB = {
+            0.0000f, 0.0000f, 0.0667f, 0.2864f, 0.1333f, 0.4007f, 0.2000f, 0.4845f,
+            0.2667f, 0.5532f, 0.3333f, 0.6125f, 0.4000f, 0.6652f, 0.4667f, 0.7130f,
+            0.5333f, 0.7569f, 0.6000f, 0.7977f, 0.6667f, 0.8360f, 0.7333f, 0.8721f,
+            0.8000f, 0.9063f, 0.8667f, 0.9389f, 0.9333f, 0.9701f, 1.0000f, 1.0000f
+    };
+    private final Rational ZERO_R = new Rational(0, 1);
+    private final Rational ONE_R = new Rational(1, 1);
+
+    private final int NUM_ALGORITHMS = 3; // AE, AWB and AF
+    private final int INDEX_ALGORITHM_AE = 0;
+    private final int INDEX_ALGORITHM_AWB = 1;
+    private final int INDEX_ALGORITHM_AF = 2;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        super.tearDown();
+    }
+
+    /**
+     * Test black level lock when exposure value change.
+     * <p>
+     * When {@link CaptureRequest#BLACK_LEVEL_LOCK} is true in a request, the
+     * camera device should lock the black level. When the exposure values are changed,
+     * the camera may require reset black level Since changes to certain capture
+     * parameters (such as exposure time) may require resetting of black level
+     * compensation. However, the black level must remain locked after exposure
+     * value changes (when requests have lock ON).
+     * </p>
+     */
+    public void testBlackLevelLock() throws Exception {
+        for (int i = 0; i < mCameraIds.length; i++) {
+            try {
+                openDevice(mCameraIds[i]);
+
+                if (!mStaticInfo.isCapabilitySupported(
+                        CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR)) {
+                    continue;
+                }
+
+                SimpleCaptureListener listener = new SimpleCaptureListener();
+                CaptureRequest.Builder requestBuilder =
+                        mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
+
+                // Start with default manual exposure time, with black level being locked.
+                requestBuilder.set(CaptureRequest.BLACK_LEVEL_LOCK, true);
+                changeExposure(requestBuilder, DEFAULT_EXP_TIME_NS, DEFAULT_SENSITIVITY);
+
+                Size previewSz =
+                        getMaxPreviewSize(mCamera.getId(), mCameraManager, PREVIEW_SIZE_BOUND);
+
+                startPreview(requestBuilder, previewSz, listener);
+                waitForSettingsApplied(listener, NUM_FRAMES_WAITED_FOR_UNKNOWN_LATENCY);
+                // No lock OFF state is allowed as the exposure is not changed.
+                verifyBlackLevelLockResults(listener, NUM_FRAMES_VERIFIED, /*maxLockOffCnt*/0);
+
+                // Double the exposure time and gain, with black level still being locked.
+                changeExposure(requestBuilder, DEFAULT_EXP_TIME_NS * 2, DEFAULT_SENSITIVITY * 2);
+                listener = new SimpleCaptureListener();
+                startPreview(requestBuilder, previewSz, listener);
+                waitForSettingsApplied(listener, NUM_FRAMES_WAITED_FOR_UNKNOWN_LATENCY);
+                // Allow at most one lock OFF state as the exposure is changed once.
+                verifyBlackLevelLockResults(listener, NUM_FRAMES_VERIFIED, /*maxLockOffCnt*/1);
+
+                stopPreview();
+            } finally {
+                closeDevice();
+            }
+        }
+    }
+
+    /**
+     * Basic lens shading map request test.
+     * <p>
+     * When {@link CaptureRequest#SHADING_MODE} is set to OFF, no lens shading correction will
+     * be applied by the camera device, and an identity lens shading map data
+     * will be provided if {@link CaptureRequest#STATISTICS_LENS_SHADING_MAP_MODE} is ON.
+     * </p>
+     * <p>
+     * When {@link CaptureRequest#SHADING_MODE} is set to other modes, lens shading correction
+     * will be applied by the camera device. The lens shading map data can be
+     * requested by setting {@link CaptureRequest#STATISTICS_LENS_SHADING_MAP_MODE} to ON.
+     * </p>
+     */
+    public void testLensShadingMap() throws Exception {
+        for (int i = 0; i < mCameraIds.length; i++) {
+            try {
+                openDevice(mCameraIds[i]);
+
+                if (!mStaticInfo.isManualLensShadingMapSupported()) {
+                    continue;
+                }
+
+                SimpleCaptureListener listener = new SimpleCaptureListener();
+                CaptureRequest.Builder requestBuilder =
+                        mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
+
+                // Shading map mode OFF, lensShadingMapMode ON, camera device
+                // should output unity maps.
+                requestBuilder.set(CaptureRequest.SHADING_MODE, SHADING_MODE_OFF);
+                requestBuilder.set(CaptureRequest.STATISTICS_LENS_SHADING_MAP_MODE,
+                        STATISTICS_LENS_SHADING_MAP_MODE_ON);
+
+                Size previewSz =
+                        getMaxPreviewSize(mCamera.getId(), mCameraManager, PREVIEW_SIZE_BOUND);
+
+                listener = new SimpleCaptureListener();
+                startPreview(requestBuilder, previewSz, listener);
+                waitForSettingsApplied(listener, NUM_FRAMES_WAITED_FOR_UNKNOWN_LATENCY);
+                verifyShadingMap(listener, NUM_FRAMES_VERIFIED, SHADING_MODE_OFF);
+
+                // Shading map mode FAST, lensShadingMapMode ON, camera device
+                // should output valid maps.
+                requestBuilder.set(CaptureRequest.SHADING_MODE, SHADING_MODE_FAST);
+
+                listener = new SimpleCaptureListener();
+                startPreview(requestBuilder, previewSz, listener);
+                waitForSettingsApplied(listener, NUM_FRAMES_WAITED_FOR_UNKNOWN_LATENCY);
+                // Allow at most one lock OFF state as the exposure is changed once.
+                verifyShadingMap(listener, NUM_FRAMES_VERIFIED, SHADING_MODE_FAST);
+
+                // Shading map mode HIGH_QUALITY, lensShadingMapMode ON, camera device
+                // should output valid maps.
+                requestBuilder.set(CaptureRequest.SHADING_MODE, SHADING_MODE_HIGH_QUALITY);
+
+                listener = new SimpleCaptureListener();
+                startPreview(requestBuilder, previewSz, listener);
+                waitForSettingsApplied(listener, NUM_FRAMES_WAITED_FOR_UNKNOWN_LATENCY);
+                verifyShadingMap(listener, NUM_FRAMES_VERIFIED, SHADING_MODE_HIGH_QUALITY);
+
+                stopPreview();
+            } finally {
+                closeDevice();
+            }
+        }
+    }
+
+    /**
+     * Test {@link CaptureRequest#CONTROL_AE_ANTIBANDING_MODE} control.
+     * <p>
+     * Test all available anti-banding modes, check if the exposure time adjustment is
+     * correct.
+     * </p>
+     */
+    public void testAntiBandingModes() throws Exception {
+        for (int i = 0; i < mCameraIds.length; i++) {
+            try {
+                openDevice(mCameraIds[i]);
+
+                // Without manual sensor control, exposure time cannot be verified
+                if (!mStaticInfo.isCapabilitySupported(
+                        CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR)) {
+                    return;
+                }
+
+                int[] modes = mStaticInfo.getAeAvailableAntiBandingModesChecked();
+
+                Size previewSz =
+                        getMaxPreviewSize(mCamera.getId(), mCameraManager, PREVIEW_SIZE_BOUND);
+
+                for (int mode : modes) {
+                    antiBandingTestByMode(previewSz, mode);
+                }
+            } finally {
+                closeDevice();
+            }
+        }
+
+    }
+
+    /**
+     * Test AE mode and lock.
+     *
+     * <p>
+     * For AE lock, when it is locked, exposure parameters shouldn't be changed.
+     * For AE modes, each mode should satisfy the per frame controls defined in
+     * API specifications.
+     * </p>
+     */
+    public void testAeModeAndLock() throws Exception {
+        for (int i = 0; i < mCameraIds.length; i++) {
+            try {
+                openDevice(mCameraIds[i]);
+
+                Size maxPreviewSz = mOrderedPreviewSizes.get(0); // Max preview size.
+
+                // Update preview surface with given size for all sub-tests.
+                updatePreviewSurface(maxPreviewSz);
+
+                // Test aeMode and lock
+                int[] aeModes = mStaticInfo.getAeAvailableModesChecked();
+                for (int mode : aeModes) {
+                    aeModeAndLockTestByMode(mode);
+                }
+            } finally {
+                closeDevice();
+            }
+        }
+    }
+
+    /** Test {@link CaptureRequest#FLASH_MODE} control.
+     * <p>
+     * For each {@link CaptureRequest#FLASH_MODE} mode, test the flash control
+     * and {@link CaptureResult#FLASH_STATE} result.
+     * </p>
+     */
+    public void testFlashControl() throws Exception {
+        for (int i = 0; i < mCameraIds.length; i++) {
+            try {
+                openDevice(mCameraIds[i]);
+
+                SimpleCaptureListener listener = new SimpleCaptureListener();
+                CaptureRequest.Builder requestBuilder =
+                        mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
+
+                Size maxPreviewSz = mOrderedPreviewSizes.get(0); // Max preview size.
+
+                startPreview(requestBuilder, maxPreviewSz, listener);
+
+                // Flash control can only be used when the AE mode is ON or OFF.
+                flashTestByAeMode(listener, CaptureRequest.CONTROL_AE_MODE_ON);
+
+                // LEGACY won't support AE mode OFF
+                if (mStaticInfo.isHardwareLevelLimitedOrBetter()) {
+                    flashTestByAeMode(listener, CaptureRequest.CONTROL_AE_MODE_OFF);
+                }
+
+                stopPreview();
+            } finally {
+                closeDevice();
+            }
+        }
+    }
+
+    /**
+     * Test face detection modes and results.
+     */
+    public void testFaceDetection() throws Exception {
+        for (int i = 0; i < mCameraIds.length; i++) {
+            try {
+                openDevice(mCameraIds[i]);
+
+                faceDetectionTestByCamera();
+            } finally {
+                closeDevice();
+            }
+        }
+    }
+
+    /**
+     * Test tone map modes and controls.
+     */
+    public void testToneMapControl() throws Exception {
+        for (int i = 0; i < mCameraIds.length; i++) {
+            try {
+                openDevice(mCameraIds[i]);
+
+                toneMapTestByCamera();
+            } finally {
+                closeDevice();
+            }
+        }
+    }
+
+    /**
+     * Test color correction modes and controls.
+     */
+    public void testColorCorrectionControl() throws Exception {
+        for (String id : mCameraIds) {
+            try {
+                openDevice(id);
+
+                colorCorrectionTestByCamera();
+            } finally {
+                closeDevice();
+            }
+        }
+    }
+
+    public void testEdgeModeControl() throws Exception {
+        for (String id : mCameraIds) {
+            try {
+                openDevice(id);
+                if (!mStaticInfo.getCharacteristics().getKeys().
+                        contains(CameraCharacteristics.EDGE_AVAILABLE_EDGE_MODES)) {
+                    Log.i(TAG, "Camera " + id + " doesn't support EDGE_MODE controls.");
+                    continue;
+                }
+
+                edgeModesTestByCamera();
+            } finally {
+                closeDevice();
+            }
+        }
+    }
+
+    /**
+     * Test focus distance control.
+     */
+    public void testFocusDistanceControl() throws Exception {
+        for (String id : mCameraIds) {
+            try {
+                openDevice(id);
+                if (!mStaticInfo.hasFocuser()) {
+                    Log.i(TAG, "Camera " + id + " has no focuser");
+                    continue;
+                }
+
+                focusDistanceTestByCamera();
+            } finally {
+                closeDevice();
+            }
+        }
+    }
+
+    public void testNoiseReductionModeControl() throws Exception {
+        for (String id : mCameraIds) {
+            try {
+                openDevice(id);
+                if (!mStaticInfo.getCharacteristics().getKeys().contains(
+                        CameraCharacteristics.NOISE_REDUCTION_AVAILABLE_NOISE_REDUCTION_MODES)) {
+                    Log.i(TAG, "Camera " + id + " doesn't support noise reduction mode");
+                    continue;
+                }
+
+                noiseReductionModeTestByCamera();
+            } finally {
+                closeDevice();
+            }
+        }
+    }
+
+    /**
+     * Test AWB lock control.
+     *
+     * <p>The color correction gain and transform shouldn't be changed when AWB is locked.</p>
+     */
+    public void testAwbModeAndLock() throws Exception {
+        for (String id : mCameraIds) {
+            try {
+                openDevice(id);
+
+                awbModeAndLockTestByCamera();
+            } finally {
+                closeDevice();
+            }
+        }
+    }
+
+    /**
+     * Test different AF modes.
+     */
+    public void testAfModes() throws Exception {
+        for (String id : mCameraIds) {
+            try {
+                openDevice(id);
+
+                afModeTestByCamera();
+            } finally {
+                closeDevice();
+            }
+        }
+    }
+
+    /**
+     * Test video and optical stabilizations.
+     */
+    public void testCameraStabilizations() throws Exception {
+        for (String id : mCameraIds) {
+            try {
+                openDevice(id);
+                List<Key<?>> keys = mStaticInfo.getCharacteristics().getKeys();
+                if (!(keys.contains(
+                        CameraCharacteristics.CONTROL_AVAILABLE_VIDEO_STABILIZATION_MODES) ||
+                        keys.contains(
+                                CameraCharacteristics.LENS_INFO_AVAILABLE_OPTICAL_STABILIZATION))) {
+                    Log.i(TAG, "Camera " + id + " doesn't support any stabilization modes");
+                    continue;
+                }
+
+                stabilizationTestByCamera();
+            } finally {
+                closeDevice();
+            }
+        }
+    }
+
+    /**
+     * Test digitalZoom (center wise and non-center wise), validate the returned crop regions.
+     * The max preview size is used for each camera.
+     */
+    public void testDigitalZoom() throws Exception {
+        for (String id : mCameraIds) {
+            try {
+                openDevice(id);
+
+                Size maxPreviewSize = mOrderedPreviewSizes.get(0);
+                digitalZoomTestByCamera(maxPreviewSize);
+            } finally {
+                closeDevice();
+            }
+        }
+    }
+
+    /**
+     * Test digital zoom and all preview size combinations.
+     * TODO: this and above test should all be moved to preview test class.
+     */
+    public void testDigitalZoomPreviewCombinations() throws Exception {
+        for (String id : mCameraIds) {
+            try {
+                openDevice(id);
+                if (!mStaticInfo.isPerFrameControlSupported()) {
+                    Log.i(TAG, "Camera " + id + "Doesn't support per frame control");
+                    continue;
+                }
+
+                digitalZoomPreviewCombinationTestByCamera();
+            } finally {
+                closeDevice();
+            }
+        }
+    }
+
+    /**
+     * Test scene mode controls.
+     */
+    public void testSceneModes() throws Exception {
+        for (String id : mCameraIds) {
+            try {
+                openDevice(id);
+
+                sceneModeTestByCamera();
+            } finally {
+                closeDevice();
+            }
+        }
+    }
+
+    /**
+     * Test effect mode controls.
+     */
+    public void testEffectModes() throws Exception {
+        for (String id : mCameraIds) {
+            try {
+                openDevice(id);
+
+                effectModeTestByCamera();
+            } finally {
+                closeDevice();
+            }
+        }
+    }
+
+    // TODO: add 3A state machine test.
+
+    private void noiseReductionModeTestByCamera() throws Exception {
+        Size maxPrevSize = mOrderedPreviewSizes.get(0);
+        CaptureRequest.Builder requestBuilder =
+                mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
+        int[] availableModes = mStaticInfo.getAvailableNoiseReductionModesChecked();
+        SimpleCaptureListener resultListener = new SimpleCaptureListener();
+        startPreview(requestBuilder, maxPrevSize, resultListener);
+
+        for (int mode : availableModes) {
+            requestBuilder.set(CaptureRequest.NOISE_REDUCTION_MODE, mode);
+            resultListener = new SimpleCaptureListener();
+            mSession.setRepeatingRequest(requestBuilder.build(), resultListener, mHandler);
+            waitForSettingsApplied(resultListener, NUM_FRAMES_WAITED_FOR_UNKNOWN_LATENCY);
+
+            verifyCaptureResultForKey(CaptureResult.NOISE_REDUCTION_MODE, mode,
+                    resultListener, NUM_FRAMES_VERIFIED);
+
+            // Test that OFF and FAST mode should not slow down the frame rate.
+            if (mode == CaptureRequest.NOISE_REDUCTION_MODE_OFF ||
+                    mode == CaptureRequest.NOISE_REDUCTION_MODE_FAST) {
+                verifyFpsNotSlowDown(requestBuilder, NUM_FRAMES_VERIFIED);
+            }
+        }
+
+        stopPreview();
+    }
+
+    private void focusDistanceTestByCamera() throws Exception {
+        Size maxPrevSize = mOrderedPreviewSizes.get(0);
+        float[] testDistances = getFocusDistanceTestValuesInOrder();
+        CaptureRequest.Builder requestBuilder =
+                mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
+        requestBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_OFF);
+        SimpleCaptureListener resultListener = new SimpleCaptureListener();
+        startPreview(requestBuilder, maxPrevSize, resultListener);
+
+        CaptureRequest request;
+        float[] resultDistances = new float[testDistances.length];
+        for (int i = 0; i < testDistances.length; i++) {
+            requestBuilder.set(CaptureRequest.LENS_FOCUS_DISTANCE, testDistances[i]);
+            request = requestBuilder.build();
+            resultListener = new SimpleCaptureListener();
+            mSession.setRepeatingRequest(request, resultListener, mHandler);
+            waitForSettingsApplied(resultListener, NUM_FRAMES_WAITED_FOR_UNKNOWN_LATENCY);
+            resultDistances[i] = verifyFocusDistanceControl(testDistances[i], request,
+                    resultListener);
+            if (VERBOSE) {
+                Log.v(TAG, "Capture request focus distance: " + testDistances[i] + " result: "
+                        + resultDistances[i]);
+            }
+        }
+
+        // Verify the monotonicity
+        mCollector.checkArrayMonotonicityAndNotAllEqual(CameraTestUtils.toObject(resultDistances),
+                /*ascendingOrder*/true);
+
+        if (mStaticInfo.getCharacteristics().getKeys().
+                contains(CameraCharacteristics.LENS_INFO_HYPERFOCAL_DISTANCE)) {
+
+            // Test hyperfocal distance optionally
+            float hyperFocalDistance = mStaticInfo.getHyperfocalDistanceChecked();
+            if (hyperFocalDistance > 0) {
+                requestBuilder.set(CaptureRequest.LENS_FOCUS_DISTANCE, hyperFocalDistance);
+                request = requestBuilder.build();
+                resultListener = new SimpleCaptureListener();
+                mSession.setRepeatingRequest(request, resultListener, mHandler);
+                waitForSettingsApplied(resultListener, NUM_FRAMES_WAITED_FOR_UNKNOWN_LATENCY);
+
+                // Then wait for the lens.state to be stationary.
+                waitForResultValue(resultListener, CaptureResult.LENS_STATE,
+                        CaptureResult.LENS_STATE_STATIONARY, NUM_RESULTS_WAIT_TIMEOUT);
+                // Need get reasonably accurate value.
+                CaptureResult result = resultListener.getCaptureResult(WAIT_FOR_RESULT_TIMEOUT_MS);
+                Float focusDistance = getValueNotNull(result, CaptureResult.LENS_FOCUS_DISTANCE);
+                float errorMargin = FOCUS_DISTANCE_ERROR_PERCENT_UNCALIBRATED;
+                int calibrationStatus = mStaticInfo.getFocusDistanceCalibrationChecked();
+                if (calibrationStatus ==
+                        CameraMetadata.LENS_INFO_FOCUS_DISTANCE_CALIBRATION_CALIBRATED) {
+                    errorMargin = FOCUS_DISTANCE_ERROR_PERCENT_CALIBRATED;
+                } else if (calibrationStatus ==
+                        CameraMetadata.LENS_INFO_FOCUS_DISTANCE_CALIBRATION_APPROXIMATE) {
+                    errorMargin = FOCUS_DISTANCE_ERROR_PERCENT_APPROXIMATE;
+                }
+                mCollector.expectInRange("Focus distance for hyper focal should be close enough to" +
+                                "requested value", focusDistance,
+                        hyperFocalDistance * (1.0f - errorMargin),
+                        hyperFocalDistance * (1.0f + errorMargin)
+                );
+            }
+        }
+    }
+
+    /**
+     * Verify focus distance control.
+     *
+     * @param distance The focus distance requested
+     * @param request The capture request to control the manual focus distance
+     * @param resultListener The capture listener to recieve capture result callbacks
+     * @return the result focus distance
+     */
+    private float verifyFocusDistanceControl(float distance, CaptureRequest request,
+            SimpleCaptureListener resultListener) {
+        // Need make sure the result corresponding to the request is back, then check.
+        CaptureResult result =
+                resultListener.getCaptureResultForRequest(request, NUM_RESULTS_WAIT_TIMEOUT);
+        // Then wait for the lens.state to be stationary.
+        waitForResultValue(resultListener, CaptureResult.LENS_STATE,
+                CaptureResult.LENS_STATE_STATIONARY, NUM_RESULTS_WAIT_TIMEOUT);
+        // Then check the focus distance.
+        result = resultListener.getCaptureResultForRequest(request, NUM_RESULTS_WAIT_TIMEOUT);
+        Float resultDistance = getValueNotNull(result, CaptureResult.LENS_FOCUS_DISTANCE);
+        if (mStaticInfo.getFocusDistanceCalibrationChecked() ==
+                CameraCharacteristics.LENS_INFO_FOCUS_DISTANCE_CALIBRATION_CALIBRATED) {
+            // TODO: what's more to test for CALIBRATED devices?
+        }
+
+        float minValue = 0;
+        float maxValue = mStaticInfo.getMinimumFocusDistanceChecked();
+        mCollector.expectInRange("Result focus distance is out of range",
+                resultDistance, minValue, maxValue);
+
+        return resultDistance;
+    }
+
+    /**
+     * Verify edge mode control results.
+     */
+    private void edgeModesTestByCamera() throws Exception {
+        Size maxPrevSize = mOrderedPreviewSizes.get(0);
+        int[] edgeModes = mStaticInfo.getAvailableEdgeModesChecked();
+        CaptureRequest.Builder requestBuilder =
+                mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
+        SimpleCaptureListener resultListener = new SimpleCaptureListener();
+        startPreview(requestBuilder, maxPrevSize, resultListener);
+
+        for (int mode : edgeModes) {
+            requestBuilder.set(CaptureRequest.EDGE_MODE, mode);
+            resultListener = new SimpleCaptureListener();
+            mSession.setRepeatingRequest(requestBuilder.build(), resultListener, mHandler);
+            waitForSettingsApplied(resultListener, NUM_FRAMES_WAITED_FOR_UNKNOWN_LATENCY);
+
+            verifyCaptureResultForKey(CaptureResult.EDGE_MODE, mode, resultListener,
+                    NUM_FRAMES_VERIFIED);
+
+            // Test that OFF and FAST mode should not slow down the frame rate.
+            if (mode == CaptureRequest.EDGE_MODE_OFF ||
+                    mode == CaptureRequest.EDGE_MODE_FAST) {
+                verifyFpsNotSlowDown(requestBuilder, NUM_FRAMES_VERIFIED);
+            }
+        }
+
+        stopPreview();
+    }
+
+    /**
+     * Test color correction controls.
+     *
+     * <p>Test different color correction modes. For TRANSFORM_MATRIX, only test
+     * the unit gain and identity transform.</p>
+     */
+    private void colorCorrectionTestByCamera() throws Exception {
+        CaptureRequest request;
+        CaptureResult result;
+        Size maxPreviewSz = mOrderedPreviewSizes.get(0); // Max preview size.
+        updatePreviewSurface(maxPreviewSz);
+        CaptureRequest.Builder manualRequestBuilder = createRequestForPreview();
+        CaptureRequest.Builder previewRequestBuilder = createRequestForPreview();
+        SimpleCaptureListener listener = new SimpleCaptureListener();
+
+        startPreview(previewRequestBuilder, maxPreviewSz, listener);
+
+        // Default preview result should give valid color correction metadata.
+        result = listener.getCaptureResult(WAIT_FOR_RESULT_TIMEOUT_MS);
+        validateColorCorrectionResult(result,
+                previewRequestBuilder.get(CaptureRequest.COLOR_CORRECTION_MODE));
+
+        // TRANSFORM_MATRIX mode
+        // Only test unit gain and identity transform
+        RggbChannelVector UNIT_GAIN = new RggbChannelVector(1.0f, 1.0f, 1.0f, 1.0f);
+
+        ColorSpaceTransform IDENTITY_TRANSFORM = new ColorSpaceTransform(
+            new Rational[] {
+                ONE_R, ZERO_R, ZERO_R,
+                ZERO_R, ONE_R, ZERO_R,
+                ZERO_R, ZERO_R, ONE_R
+            });
+
+        int colorCorrectionMode = CaptureRequest.COLOR_CORRECTION_MODE_TRANSFORM_MATRIX;
+        manualRequestBuilder.set(CaptureRequest.CONTROL_MODE, CaptureRequest.CONTROL_MODE_OFF);
+        manualRequestBuilder.set(CaptureRequest.COLOR_CORRECTION_MODE, colorCorrectionMode);
+        manualRequestBuilder.set(CaptureRequest.COLOR_CORRECTION_GAINS, UNIT_GAIN);
+        manualRequestBuilder.set(CaptureRequest.COLOR_CORRECTION_TRANSFORM, IDENTITY_TRANSFORM);
+        request = manualRequestBuilder.build();
+        mSession.capture(request, listener, mHandler);
+        result = listener.getCaptureResultForRequest(request, NUM_RESULTS_WAIT_TIMEOUT);
+        RggbChannelVector gains = result.get(CaptureResult.COLOR_CORRECTION_GAINS);
+        ColorSpaceTransform transform = result.get(CaptureResult.COLOR_CORRECTION_TRANSFORM);
+        validateColorCorrectionResult(result, colorCorrectionMode);
+        mCollector.expectEquals("control mode result/request mismatch",
+                CaptureResult.CONTROL_MODE_OFF, result.get(CaptureResult.CONTROL_MODE));
+        mCollector.expectEquals("Color correction gain result/request mismatch",
+                UNIT_GAIN, gains);
+        mCollector.expectEquals("Color correction gain result/request mismatch",
+                IDENTITY_TRANSFORM, transform);
+
+        // FAST mode
+        colorCorrectionMode = CaptureRequest.COLOR_CORRECTION_MODE_FAST;
+        manualRequestBuilder.set(CaptureRequest.CONTROL_MODE, CaptureRequest.CONTROL_MODE_AUTO);
+        manualRequestBuilder.set(CaptureRequest.COLOR_CORRECTION_MODE, colorCorrectionMode);
+        request = manualRequestBuilder.build();
+        mSession.capture(request, listener, mHandler);
+        result = listener.getCaptureResultForRequest(request, NUM_RESULTS_WAIT_TIMEOUT);
+        validateColorCorrectionResult(result, colorCorrectionMode);
+        mCollector.expectEquals("control mode result/request mismatch",
+                CaptureResult.CONTROL_MODE_AUTO, result.get(CaptureResult.CONTROL_MODE));
+
+        // HIGH_QUALITY mode
+        colorCorrectionMode = CaptureRequest.COLOR_CORRECTION_MODE_HIGH_QUALITY;
+        manualRequestBuilder.set(CaptureRequest.CONTROL_MODE, CaptureRequest.CONTROL_MODE_AUTO);
+        manualRequestBuilder.set(CaptureRequest.COLOR_CORRECTION_MODE, colorCorrectionMode);
+        request = manualRequestBuilder.build();
+        mSession.capture(request, listener, mHandler);
+        result = listener.getCaptureResultForRequest(request, NUM_RESULTS_WAIT_TIMEOUT);
+        validateColorCorrectionResult(result, colorCorrectionMode);
+        mCollector.expectEquals("control mode result/request mismatch",
+                CaptureResult.CONTROL_MODE_AUTO, result.get(CaptureResult.CONTROL_MODE));
+    }
+
+    private void validateColorCorrectionResult(CaptureResult result, int colorCorrectionMode) {
+        final RggbChannelVector ZERO_GAINS = new RggbChannelVector(0, 0, 0, 0);
+        final int TRANSFORM_SIZE = 9;
+        Rational[] zeroTransform = new Rational[TRANSFORM_SIZE];
+        Arrays.fill(zeroTransform, ZERO_R);
+        final ColorSpaceTransform ZERO_TRANSFORM = new ColorSpaceTransform(zeroTransform);
+
+        RggbChannelVector resultGain;
+        if ((resultGain = mCollector.expectKeyValueNotNull(result,
+                CaptureResult.COLOR_CORRECTION_GAINS)) != null) {
+            mCollector.expectKeyValueNotEquals(result,
+                    CaptureResult.COLOR_CORRECTION_GAINS, ZERO_GAINS);
+        }
+
+        ColorSpaceTransform resultTransform;
+        if ((resultTransform = mCollector.expectKeyValueNotNull(result,
+                CaptureResult.COLOR_CORRECTION_TRANSFORM)) != null) {
+            mCollector.expectKeyValueNotEquals(result,
+                    CaptureResult.COLOR_CORRECTION_TRANSFORM, ZERO_TRANSFORM);
+        }
+
+        mCollector.expectEquals("color correction mode result/request mismatch",
+                colorCorrectionMode, result.get(CaptureResult.COLOR_CORRECTION_MODE));
+    }
+
+    /**
+     * Test flash mode control by AE mode.
+     * <p>
+     * Only allow AE mode ON or OFF, because other AE mode could run into conflict with
+     * flash manual control. This function expects the camera to already have an active
+     * repeating request and be sending results to the listener.
+     * </p>
+     *
+     * @param listener The Capture listener that is used to wait for capture result
+     * @param aeMode The AE mode for flash to test with
+     */
+    private void flashTestByAeMode(SimpleCaptureListener listener, int aeMode) throws Exception {
+        CaptureResult result;
+        final int NUM_FLASH_REQUESTS_TESTED = 10;
+        CaptureRequest.Builder requestBuilder = createRequestForPreview();
+
+        if (aeMode == CaptureRequest.CONTROL_AE_MODE_ON) {
+            requestBuilder.set(CaptureRequest.CONTROL_AE_MODE, aeMode);
+        } else if (aeMode == CaptureRequest.CONTROL_AE_MODE_OFF) {
+            changeExposure(requestBuilder, DEFAULT_EXP_TIME_NS, DEFAULT_SENSITIVITY);
+        } else {
+            throw new IllegalArgumentException("This test only works when AE mode is ON or OFF");
+        }
+
+        mSession.setRepeatingRequest(requestBuilder.build(), listener, mHandler);
+        waitForSettingsApplied(listener, NUM_FRAMES_WAITED_FOR_UNKNOWN_LATENCY);
+
+        // For camera that doesn't have flash unit, flash state should always be UNAVAILABLE.
+        if (mStaticInfo.getFlashInfoChecked() == false) {
+            for (int i = 0; i < NUM_FLASH_REQUESTS_TESTED; i++) {
+                result = listener.getCaptureResult(CAPTURE_RESULT_TIMEOUT_MS);
+                mCollector.expectEquals("No flash unit available, flash state must be UNAVAILABLE"
+                        + "for AE mode " + aeMode, CaptureResult.FLASH_STATE_UNAVAILABLE,
+                        result.get(CaptureResult.FLASH_STATE));
+            }
+
+            return;
+        }
+
+        // Test flash SINGLE mode control. Wait for flash state to be READY first.
+        if (mStaticInfo.isHardwareLevelLimitedOrBetter()) {
+            waitForResultValue(listener, CaptureResult.FLASH_STATE, CaptureResult.FLASH_STATE_READY,
+                    NUM_RESULTS_WAIT_TIMEOUT);
+        } // else the settings were already waited on earlier
+
+        requestBuilder.set(CaptureRequest.FLASH_MODE, CaptureRequest.FLASH_MODE_SINGLE);
+        CaptureRequest flashSinglerequest = requestBuilder.build();
+
+        int flashModeSingleRequests = captureRequestsSynchronized(
+                flashSinglerequest, listener, mHandler);
+        waitForNumResults(listener, flashModeSingleRequests - 1);
+        result = listener.getCaptureResultForRequest(flashSinglerequest, NUM_RESULTS_WAIT_TIMEOUT);
+        // Result mode must be SINGLE, state must be FIRED.
+        mCollector.expectEquals("Flash mode result must be SINGLE",
+                CaptureResult.FLASH_MODE_SINGLE, result.get(CaptureResult.FLASH_MODE));
+        mCollector.expectEquals("Flash state result must be FIRED",
+                CaptureResult.FLASH_STATE_FIRED, result.get(CaptureResult.FLASH_STATE));
+
+        // Test flash TORCH mode control.
+        requestBuilder.set(CaptureRequest.FLASH_MODE, CaptureRequest.FLASH_MODE_TORCH);
+        CaptureRequest torchRequest = requestBuilder.build();
+
+        int flashModeTorchRequests = captureRequestsSynchronized(torchRequest,
+                NUM_FLASH_REQUESTS_TESTED, listener, mHandler);
+        waitForNumResults(listener, flashModeTorchRequests - NUM_FLASH_REQUESTS_TESTED);
+
+        // Verify the results
+        for (int i = 0; i < NUM_FLASH_REQUESTS_TESTED; i++) {
+            result = listener.getCaptureResultForRequest(torchRequest,
+                    NUM_RESULTS_WAIT_TIMEOUT);
+
+            // Result mode must be TORCH, state must be FIRED
+            mCollector.expectEquals("Flash mode result must be TORCH",
+                    CaptureResult.FLASH_MODE_TORCH, result.get(CaptureResult.FLASH_MODE));
+            mCollector.expectEquals("Flash state result must be FIRED",
+                    CaptureResult.FLASH_STATE_FIRED, result.get(CaptureResult.FLASH_STATE));
+        }
+
+        // Test flash OFF mode control
+        requestBuilder.set(CaptureRequest.FLASH_MODE, CaptureRequest.FLASH_MODE_OFF);
+        CaptureRequest flashOffrequest = requestBuilder.build();
+
+        int flashModeOffRequests = captureRequestsSynchronized(flashOffrequest, listener, mHandler);
+        waitForNumResults(listener, flashModeOffRequests - 1);
+        result = listener.getCaptureResultForRequest(flashOffrequest, NUM_RESULTS_WAIT_TIMEOUT);
+        mCollector.expectEquals("Flash mode result must be OFF", CaptureResult.FLASH_MODE_OFF,
+                result.get(CaptureResult.FLASH_MODE));
+    }
+
+    private void verifyAntiBandingMode(SimpleCaptureListener listener, int numFramesVerified,
+            int mode, boolean isAeManual, long requestExpTime) throws Exception {
+        // Skip the first a couple of frames as antibanding may not be fully up yet.
+        final int NUM_FRAMES_SKIPPED = 5;
+        for (int i = 0; i < NUM_FRAMES_SKIPPED; i++) {
+            listener.getCaptureResult(WAIT_FOR_RESULT_TIMEOUT_MS);
+        }
+
+        for (int i = 0; i < numFramesVerified; i++) {
+            CaptureResult result = listener.getCaptureResult(WAIT_FOR_RESULT_TIMEOUT_MS);
+            Long resultExpTime = result.get(CaptureResult.SENSOR_EXPOSURE_TIME);
+            assertNotNull("Exposure time shouldn't be null", resultExpTime);
+            Integer flicker = result.get(CaptureResult.STATISTICS_SCENE_FLICKER);
+            // Scene flicker result should be always available.
+            assertNotNull("Scene flicker must not be null", flicker);
+            assertTrue("Scene flicker is invalid", flicker >= STATISTICS_SCENE_FLICKER_NONE &&
+                    flicker <= STATISTICS_SCENE_FLICKER_60HZ);
+
+            if (isAeManual) {
+                // First, round down not up, second, need close enough.
+                validateExposureTime(requestExpTime, resultExpTime);
+                return;
+            }
+
+            long expectedExpTime = resultExpTime; // Default, no exposure adjustment.
+            if (mode == CONTROL_AE_ANTIBANDING_MODE_50HZ) {
+                // result exposure time must be adjusted by 50Hz illuminant source.
+                expectedExpTime =
+                        getAntiFlickeringExposureTime(ANTI_FLICKERING_50HZ, resultExpTime);
+            } else if (mode == CONTROL_AE_ANTIBANDING_MODE_60HZ) {
+                // result exposure time must be adjusted by 60Hz illuminant source.
+                expectedExpTime =
+                        getAntiFlickeringExposureTime(ANTI_FLICKERING_60HZ, resultExpTime);
+            } else if (mode == CONTROL_AE_ANTIBANDING_MODE_AUTO){
+                /**
+                 * Use STATISTICS_SCENE_FLICKER to tell the illuminant source
+                 * and do the exposure adjustment.
+                 */
+                expectedExpTime = resultExpTime;
+                if (flicker == STATISTICS_SCENE_FLICKER_60HZ) {
+                    expectedExpTime =
+                            getAntiFlickeringExposureTime(ANTI_FLICKERING_60HZ, resultExpTime);
+                } else if (flicker == STATISTICS_SCENE_FLICKER_50HZ) {
+                    expectedExpTime =
+                            getAntiFlickeringExposureTime(ANTI_FLICKERING_50HZ, resultExpTime);
+                }
+            }
+
+            if (Math.abs(resultExpTime - expectedExpTime) > EXPOSURE_TIME_ERROR_MARGIN_NS) {
+                mCollector.addMessage(String.format("Result exposure time %dns diverges too much"
+                        + " from expected exposure time %dns for mode %d when AE is auto",
+                        resultExpTime, expectedExpTime, mode));
+            }
+        }
+    }
+
+    private void antiBandingTestByMode(Size size, int mode)
+            throws Exception {
+        if(VERBOSE) {
+            Log.v(TAG, "Anti-banding test for mode " + mode + " for camera " + mCamera.getId());
+        }
+        CaptureRequest.Builder requestBuilder =
+                mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
+
+        requestBuilder.set(CaptureRequest.CONTROL_AE_ANTIBANDING_MODE, mode);
+
+        // Test auto AE mode anti-banding behavior
+        SimpleCaptureListener resultListener = new SimpleCaptureListener();
+        startPreview(requestBuilder, size, resultListener);
+        waitForSettingsApplied(resultListener, NUM_FRAMES_WAITED_FOR_UNKNOWN_LATENCY);
+        verifyAntiBandingMode(resultListener, NUM_FRAMES_VERIFIED, mode, /*isAeManual*/false,
+                IGNORE_REQUESTED_EXPOSURE_TIME_CHECK);
+
+        // Test manual AE mode anti-banding behavior
+        // 65ms, must be supported by full capability devices.
+        final long TEST_MANUAL_EXP_TIME_NS = 65000000L;
+        long manualExpTime = mStaticInfo.getExposureClampToRange(TEST_MANUAL_EXP_TIME_NS);
+        changeExposure(requestBuilder, manualExpTime);
+        resultListener = new SimpleCaptureListener();
+        startPreview(requestBuilder, size, resultListener);
+        waitForSettingsApplied(resultListener, NUM_FRAMES_WAITED_FOR_UNKNOWN_LATENCY);
+        verifyAntiBandingMode(resultListener, NUM_FRAMES_VERIFIED, mode, /*isAeManual*/true,
+                manualExpTime);
+
+        stopPreview();
+    }
+
+    /**
+     * Test the all available AE modes and AE lock.
+     * <p>
+     * For manual AE mode, test iterates through different sensitivities and
+     * exposure times, validate the result exposure time correctness. For
+     * CONTROL_AE_MODE_ON_ALWAYS_FLASH mode, the AE lock and flash are tested.
+     * For the rest of the AUTO mode, AE lock is tested.
+     * </p>
+     *
+     * @param mode
+     */
+    private void aeModeAndLockTestByMode(int mode)
+            throws Exception {
+        switch (mode) {
+            case CONTROL_AE_MODE_OFF:
+                if (mStaticInfo.isCapabilitySupported(
+                        CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR)) {
+                    // Test manual exposure control.
+                    aeManualControlTest();
+                } else {
+                    Log.w(TAG,
+                            "aeModeAndLockTestByMode - can't test AE mode OFF without " +
+                            "manual sensor control");
+                }
+                break;
+            case CONTROL_AE_MODE_ON:
+            case CONTROL_AE_MODE_ON_AUTO_FLASH:
+            case CONTROL_AE_MODE_ON_AUTO_FLASH_REDEYE:
+            case CONTROL_AE_MODE_ON_ALWAYS_FLASH:
+                // Test AE lock for above AUTO modes.
+                aeAutoModeTestLock(mode);
+                break;
+            default:
+                throw new UnsupportedOperationException("Unhandled AE mode " + mode);
+        }
+    }
+
+    /**
+     * Test AE auto modes.
+     * <p>
+     * Use single request rather than repeating request to test AE lock per frame control.
+     * </p>
+     */
+    private void aeAutoModeTestLock(int mode) throws Exception {
+        CaptureRequest.Builder requestBuilder =
+                mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
+        requestBuilder.set(CaptureRequest.CONTROL_AE_LOCK, false);
+        requestBuilder.set(CaptureRequest.CONTROL_AE_MODE, mode);
+        configurePreviewOutput(requestBuilder);
+
+        final int MAX_NUM_CAPTURES_DURING_LOCK = 5;
+        for (int i = 1; i <= MAX_NUM_CAPTURES_DURING_LOCK; i++) {
+            autoAeMultipleCapturesThenTestLock(requestBuilder, mode, i);
+        }
+    }
+
+    /**
+     * Issue multiple auto AE captures, then lock AE, validate the AE lock vs.
+     * the first capture result after the AE lock. The right AE lock behavior is:
+     * When it is locked, it locks to the current exposure value, and all subsequent
+     * request with lock ON will have the same exposure value locked.
+     */
+    private void autoAeMultipleCapturesThenTestLock(
+            CaptureRequest.Builder requestBuilder, int aeMode, int numCapturesDuringLock)
+            throws Exception {
+        if (numCapturesDuringLock < 1) {
+            throw new IllegalArgumentException("numCapturesBeforeLock must be no less than 1");
+        }
+        if (VERBOSE) {
+            Log.v(TAG, "Camera " + mCamera.getId() + ": Testing auto AE mode and lock for mode "
+                    + aeMode + " with " + numCapturesDuringLock + " captures before lock");
+        }
+
+        final int NUM_CAPTURES_BEFORE_LOCK = 2;
+        SimpleCaptureListener listener =  new SimpleCaptureListener();
+
+        CaptureResult[] resultsDuringLock = new CaptureResult[numCapturesDuringLock];
+
+        // Reset the AE lock to OFF, since we are reusing this builder many times
+        requestBuilder.set(CaptureRequest.CONTROL_AE_LOCK, false);
+
+        // Just send several captures with auto AE, lock off.
+        CaptureRequest request = requestBuilder.build();
+        for (int i = 0; i < NUM_CAPTURES_BEFORE_LOCK; i++) {
+            mSession.capture(request, listener, mHandler);
+        }
+        waitForNumResults(listener, NUM_CAPTURES_BEFORE_LOCK);
+
+        // Then fire several capture to lock the AE.
+        requestBuilder.set(CaptureRequest.CONTROL_AE_LOCK, true);
+
+        int requestCount = captureRequestsSynchronized(
+                requestBuilder.build(), numCapturesDuringLock, listener, mHandler);
+
+        int[] sensitivities = new int[numCapturesDuringLock];
+        long[] expTimes = new long[numCapturesDuringLock];
+        Arrays.fill(sensitivities, -1);
+        Arrays.fill(expTimes, -1L);
+
+        // Get the AE lock on result and validate the exposure values.
+        waitForNumResults(listener, requestCount - numCapturesDuringLock);
+        for (int i = 0; i < resultsDuringLock.length; i++) {
+            resultsDuringLock[i] = listener.getCaptureResult(WAIT_FOR_RESULT_TIMEOUT_MS);
+        }
+
+        for (int i = 0; i < numCapturesDuringLock; i++) {
+            mCollector.expectKeyValueEquals(
+                    resultsDuringLock[i], CaptureResult.CONTROL_AE_LOCK, true);
+        }
+
+        // Can't read manual sensor/exposure settings without manual sensor
+        if (mStaticInfo.isCapabilitySupported(
+                CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR)) {
+            int sensitivityLocked =
+                    getValueNotNull(resultsDuringLock[0], CaptureResult.SENSOR_SENSITIVITY);
+            long expTimeLocked =
+                    getValueNotNull(resultsDuringLock[0], CaptureResult.SENSOR_EXPOSURE_TIME);
+            for (int i = 1; i < resultsDuringLock.length; i++) {
+                mCollector.expectKeyValueEquals(
+                        resultsDuringLock[i], CaptureResult.SENSOR_EXPOSURE_TIME, expTimeLocked);
+                mCollector.expectKeyValueEquals(
+                        resultsDuringLock[i], CaptureResult.SENSOR_SENSITIVITY, sensitivityLocked);
+            }
+        }
+    }
+
+    /**
+     * Iterate through exposure times and sensitivities for manual AE control.
+     * <p>
+     * Use single request rather than repeating request to test manual exposure
+     * value change per frame control.
+     * </p>
+     */
+    private void aeManualControlTest()
+            throws Exception {
+        CaptureRequest.Builder requestBuilder =
+                mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
+
+        requestBuilder.set(CaptureRequest.CONTROL_AE_MODE, CONTROL_AE_MODE_OFF);
+        configurePreviewOutput(requestBuilder);
+        SimpleCaptureListener listener =  new SimpleCaptureListener();
+
+        long[] expTimes = getExposureTimeTestValues();
+        int[] sensitivities = getSensitivityTestValues();
+        // Submit single request at a time, then verify the result.
+        for (int i = 0; i < expTimes.length; i++) {
+            for (int j = 0; j < sensitivities.length; j++) {
+                if (VERBOSE) {
+                    Log.v(TAG, "Camera " + mCamera.getId() + ": Testing sensitivity "
+                            + sensitivities[j] + ", exposure time " + expTimes[i] + "ns");
+                }
+
+                changeExposure(requestBuilder, expTimes[i], sensitivities[j]);
+                mSession.capture(requestBuilder.build(), listener, mHandler);
+
+                CaptureResult result = listener.getCaptureResult(WAIT_FOR_RESULT_TIMEOUT_MS);
+                long resultExpTime = getValueNotNull(result, CaptureResult.SENSOR_EXPOSURE_TIME);
+                int resultSensitivity = getValueNotNull(result, CaptureResult.SENSOR_SENSITIVITY);
+                validateExposureTime(expTimes[i], resultExpTime);
+                validateSensitivity(sensitivities[j], resultSensitivity);
+                validateFrameDurationForCapture(result);
+            }
+        }
+        // TODO: Add another case to test where we can submit all requests, then wait for
+        // results, which will hide the pipeline latency. this is not only faster, but also
+        // test high speed per frame control and synchronization.
+    }
+
+
+    /**
+     * Verify black level lock control.
+     */
+    private void verifyBlackLevelLockResults(SimpleCaptureListener listener, int numFramesVerified,
+            int maxLockOffCnt) throws Exception {
+        int noLockCnt = 0;
+        for (int i = 0; i < numFramesVerified; i++) {
+            CaptureResult result = listener.getCaptureResult(WAIT_FOR_RESULT_TIMEOUT_MS);
+            Boolean blackLevelLock = result.get(CaptureResult.BLACK_LEVEL_LOCK);
+            assertNotNull("Black level lock result shouldn't be null", blackLevelLock);
+
+            // Count the lock == false result, which could possibly occur at most once.
+            if (blackLevelLock == false) {
+                noLockCnt++;
+            }
+
+            if(VERBOSE) {
+                Log.v(TAG, "Black level lock result: " + blackLevelLock);
+            }
+        }
+        assertTrue("Black level lock OFF occurs " + noLockCnt + " times,  expect at most "
+                + maxLockOffCnt + " for camera " + mCamera.getId(), noLockCnt <= maxLockOffCnt);
+    }
+
+    /**
+     * Verify shading map for different shading modes.
+     */
+    private void verifyShadingMap(SimpleCaptureListener listener, int numFramesVerified,
+            int shadingMode) throws Exception {
+
+        for (int i = 0; i < numFramesVerified; i++) {
+            CaptureResult result = listener.getCaptureResult(WAIT_FOR_RESULT_TIMEOUT_MS);
+            mCollector.expectEquals("Shading mode result doesn't match request",
+                    shadingMode, result.get(CaptureResult.SHADING_MODE));
+            LensShadingMap mapObj = result.get(
+                    CaptureResult.STATISTICS_LENS_SHADING_CORRECTION_MAP);
+            assertNotNull("Map object must not be null", mapObj);
+            int numElementsInMap = mapObj.getGainFactorCount();
+            float[] map = new float[numElementsInMap];
+            mapObj.copyGainFactors(map, /*offset*/0);
+            assertNotNull("Map must not be null", map);
+            assertFalse(String.format(
+                    "Map size %d should be less than %d", numElementsInMap, MAX_SHADING_MAP_SIZE),
+                    numElementsInMap >= MAX_SHADING_MAP_SIZE);
+            assertFalse(String.format("Map size %d should be no less than %d", numElementsInMap,
+                    MIN_SHADING_MAP_SIZE), numElementsInMap < MIN_SHADING_MAP_SIZE);
+
+            if (shadingMode == CaptureRequest.SHADING_MODE_FAST ||
+                    shadingMode == CaptureRequest.SHADING_MODE_HIGH_QUALITY) {
+                // shading mode is FAST or HIGH_QUALITY, expect to receive a map with all
+                // elements >= 1.0f
+
+                int badValueCnt = 0;
+                // Detect the bad values of the map data.
+                for (int j = 0; j < numElementsInMap; j++) {
+                    if (Float.isNaN(map[j]) || map[j] < 1.0f) {
+                        badValueCnt++;
+                    }
+                }
+                assertEquals("Number of value in the map is " + badValueCnt + " out of "
+                        + numElementsInMap, /*expected*/0, /*actual*/badValueCnt);
+            } else if (shadingMode == CaptureRequest.SHADING_MODE_OFF) {
+                float[] unityMap = new float[numElementsInMap];
+                Arrays.fill(unityMap, 1.0f);
+                // shading mode is OFF, expect to receive a unity map.
+                assertTrue("Result map " + Arrays.toString(map) + " must be an unity map",
+                        Arrays.equals(unityMap, map));
+            }
+        }
+    }
+
+    /**
+     * Test face detection for a camera.
+     */
+    private void faceDetectionTestByCamera() throws Exception {
+        int[] faceDetectModes = mStaticInfo.getAvailableFaceDetectModesChecked();
+
+        SimpleCaptureListener listener;
+        CaptureRequest.Builder requestBuilder =
+                mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
+
+        Size maxPreviewSz = mOrderedPreviewSizes.get(0); // Max preview size.
+        for (int mode : faceDetectModes) {
+            requestBuilder.set(CaptureRequest.STATISTICS_FACE_DETECT_MODE, mode);
+            if (VERBOSE) {
+                Log.v(TAG, "Start testing face detection mode " + mode);
+            }
+
+            // Create a new listener for each run to avoid the results from one run spill
+            // into another run.
+            listener = new SimpleCaptureListener();
+            startPreview(requestBuilder, maxPreviewSz, listener);
+            waitForSettingsApplied(listener, NUM_FRAMES_WAITED_FOR_UNKNOWN_LATENCY);
+            verifyFaceDetectionResults(listener, NUM_FACE_DETECTION_FRAMES_VERIFIED, mode);
+        }
+
+        stopPreview();
+    }
+
+    /**
+     * Verify face detection results for different face detection modes.
+     *
+     * @param listener The listener to get capture result
+     * @param numFramesVerified Number of results to be verified
+     * @param faceDetectionMode Face detection mode to be verified against
+     */
+    private void verifyFaceDetectionResults(SimpleCaptureListener listener, int numFramesVerified,
+            int faceDetectionMode) {
+        for (int i = 0; i < numFramesVerified; i++) {
+            CaptureResult result = listener.getCaptureResult(WAIT_FOR_RESULT_TIMEOUT_MS);
+            mCollector.expectEquals("Result face detection mode should match the request",
+                    faceDetectionMode, result.get(CaptureResult.STATISTICS_FACE_DETECT_MODE));
+
+            Face[] faces = result.get(CaptureResult.STATISTICS_FACES);
+            List<Integer> faceIds = new ArrayList<Integer>(faces.length);
+            List<Integer> faceScores = new ArrayList<Integer>(faces.length);
+            if (faceDetectionMode == CaptureResult.STATISTICS_FACE_DETECT_MODE_OFF) {
+                mCollector.expectEquals("Number of detection faces should always 0 for OFF mode",
+                        0, faces.length);
+            } else if (faceDetectionMode == CaptureResult.STATISTICS_FACE_DETECT_MODE_SIMPLE) {
+                for (Face face : faces) {
+                    mCollector.expectNotNull("Face rectangle shouldn't be null", face.getBounds());
+                    faceScores.add(face.getScore());
+                    mCollector.expectTrue("Face id is expected to be -1 for SIMPLE mode",
+                            face.getId() == Face.ID_UNSUPPORTED);
+                }
+            } else if (faceDetectionMode == CaptureResult.STATISTICS_FACE_DETECT_MODE_FULL) {
+                if (VERBOSE) {
+                    Log.v(TAG, "Number of faces detected: " + faces.length);
+                }
+
+                for (Face face : faces) {
+                    Rect faceBound;
+                    boolean faceRectAvailable =  mCollector.expectTrue("Face rectangle "
+                            + "shouldn't be null", face.getBounds() != null);
+                    if (!faceRectAvailable) {
+                        continue;
+                    }
+                    faceBound = face.getBounds();
+
+                    faceScores.add(face.getScore());
+                    faceIds.add(face.getId());
+
+                    mCollector.expectTrue("Face id is shouldn't be -1 for FULL mode",
+                            face.getId() != Face.ID_UNSUPPORTED);
+                    boolean leftEyeAvailable =
+                            mCollector.expectTrue("Left eye position shouldn't be null",
+                                    face.getLeftEyePosition() != null);
+                    boolean rightEyeAvailable =
+                            mCollector.expectTrue("Right eye position shouldn't be null",
+                                    face.getRightEyePosition() != null);
+                    boolean mouthAvailable =
+                            mCollector.expectTrue("Mouth position shouldn't be null",
+                            face.getMouthPosition() != null);
+                    // Eyes/mouth position should be inside of the face rect.
+                    if (leftEyeAvailable) {
+                        Point leftEye = face.getLeftEyePosition();
+                        mCollector.expectTrue("Left eye " + leftEye + "should be"
+                                + "inside of face rect " + faceBound,
+                                faceBound.contains(leftEye.x, leftEye.y));
+                    }
+                    if (rightEyeAvailable) {
+                        Point rightEye = face.getRightEyePosition();
+                        mCollector.expectTrue("Right eye " + rightEye + "should be"
+                                + "inside of face rect " + faceBound,
+                                faceBound.contains(rightEye.x, rightEye.y));
+                    }
+                    if (mouthAvailable) {
+                        Point mouth = face.getMouthPosition();
+                        mCollector.expectTrue("Mouth " + mouth +  " should be inside of"
+                                + " face rect " + faceBound,
+                                faceBound.contains(mouth.x, mouth.y));
+                    }
+                }
+            }
+            mCollector.expectValuesInRange("Face scores are invalid", faceIds,
+                    Face.SCORE_MIN, Face.SCORE_MAX);
+            mCollector.expectValuesUnique("Face ids are invalid", faceIds);
+        }
+    }
+
+    /**
+     * Test tone map mode and result by camera
+     */
+    private void toneMapTestByCamera() throws Exception {
+        if (!mStaticInfo.isManualToneMapSupported()) {
+            return;
+        }
+
+        SimpleCaptureListener listener;
+        CaptureRequest.Builder requestBuilder =
+                mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
+
+        Size maxPreviewSz = mOrderedPreviewSizes.get(0); // Max preview size.
+
+        int[] toneMapModes = mStaticInfo.getAvailableToneMapModesChecked();
+        for (int mode : toneMapModes) {
+            requestBuilder.set(CaptureRequest.TONEMAP_MODE, mode);
+            if (VERBOSE) {
+                Log.v(TAG, "Testing tonemap mode " + mode);
+            }
+
+            if (mode == CaptureRequest.TONEMAP_MODE_CONTRAST_CURVE) {
+                TonemapCurve tcLinear = new TonemapCurve(
+                        TONEMAP_CURVE_LINEAR, TONEMAP_CURVE_LINEAR, TONEMAP_CURVE_LINEAR);
+                requestBuilder.set(CaptureRequest.TONEMAP_CURVE, tcLinear);
+                // Create a new listener for each run to avoid the results from one run spill
+                // into another run.
+                listener = new SimpleCaptureListener();
+                startPreview(requestBuilder, maxPreviewSz, listener);
+                waitForSettingsApplied(listener, NUM_FRAMES_WAITED_FOR_UNKNOWN_LATENCY);
+                verifyToneMapModeResults(listener, NUM_FRAMES_VERIFIED, mode,
+                        TONEMAP_CURVE_LINEAR);
+
+                TonemapCurve tcSrgb = new TonemapCurve(
+                        TONEMAP_CURVE_SRGB, TONEMAP_CURVE_SRGB, TONEMAP_CURVE_SRGB);
+                requestBuilder.set(CaptureRequest.TONEMAP_CURVE, tcSrgb);
+                // Create a new listener for each run to avoid the results from one run spill
+                // into another run.
+                listener = new SimpleCaptureListener();
+                startPreview(requestBuilder, maxPreviewSz, listener);
+                waitForSettingsApplied(listener, NUM_FRAMES_WAITED_FOR_UNKNOWN_LATENCY);
+                verifyToneMapModeResults(listener, NUM_FRAMES_VERIFIED, mode,
+                        TONEMAP_CURVE_SRGB);
+            } else {
+                // Create a new listener for each run to avoid the results from one run spill
+                // into another run.
+                listener = new SimpleCaptureListener();
+                startPreview(requestBuilder, maxPreviewSz, listener);
+                waitForSettingsApplied(listener, NUM_FRAMES_WAITED_FOR_UNKNOWN_LATENCY);
+                verifyToneMapModeResults(listener, NUM_FRAMES_VERIFIED, mode,
+                        /*inputToneCurve*/null);
+            }
+        }
+
+        stopPreview();
+    }
+
+    /**
+     * Verify tonemap results.
+     * <p>
+     * Assumes R,G,B channels use the same tone curve
+     * </p>
+     *
+     * @param listener The capture listener used to get the capture results
+     * @param numFramesVerified Number of results to be verified
+     * @param tonemapMode Tonemap mode to verify
+     * @param inputToneCurve Tonemap curve used by all 3 channels, ignored when
+     * map mode is not CONTRAST_CURVE.
+     */
+    private void verifyToneMapModeResults(SimpleCaptureListener listener, int numFramesVerified,
+            int tonemapMode, float[] inputToneCurve) {
+        final int MIN_TONEMAP_CURVE_POINTS = 2;
+        final Float ZERO = new Float(0);
+        final Float ONE = new Float(1.0f);
+
+        int maxCurvePoints = mStaticInfo.getMaxTonemapCurvePointChecked();
+        for (int i = 0; i < numFramesVerified; i++) {
+            CaptureResult result = listener.getCaptureResult(WAIT_FOR_RESULT_TIMEOUT_MS);
+            mCollector.expectEquals("Capture result tonemap mode should match request", tonemapMode,
+                    result.get(CaptureResult.TONEMAP_MODE));
+            TonemapCurve tc = getValueNotNull(result, CaptureResult.TONEMAP_CURVE);
+            int pointCount = tc.getPointCount(TonemapCurve.CHANNEL_RED);
+            float[] mapRed = new float[pointCount * TonemapCurve.POINT_SIZE];
+            pointCount = tc.getPointCount(TonemapCurve.CHANNEL_GREEN);
+            float[] mapGreen = new float[pointCount * TonemapCurve.POINT_SIZE];
+            pointCount = tc.getPointCount(TonemapCurve.CHANNEL_BLUE);
+            float[] mapBlue = new float[pointCount * TonemapCurve.POINT_SIZE];
+            tc.copyColorCurve(TonemapCurve.CHANNEL_RED, mapRed, 0);
+            tc.copyColorCurve(TonemapCurve.CHANNEL_GREEN, mapGreen, 0);
+            tc.copyColorCurve(TonemapCurve.CHANNEL_BLUE, mapBlue, 0);
+            if (tonemapMode == CaptureResult.TONEMAP_MODE_CONTRAST_CURVE) {
+                /**
+                 * TODO: need figure out a good way to measure the difference
+                 * between request and result, as they may have different array
+                 * size.
+                 */
+            }
+
+            // Tonemap curve result availability and basic sanity check for all modes.
+            mCollector.expectValuesInRange("Tonemap curve red values are out of range",
+                    CameraTestUtils.toObject(mapRed), /*min*/ZERO, /*max*/ONE);
+            mCollector.expectInRange("Tonemap curve red length is out of range",
+                    mapRed.length, MIN_TONEMAP_CURVE_POINTS, maxCurvePoints * 2);
+            mCollector.expectValuesInRange("Tonemap curve green values are out of range",
+                    CameraTestUtils.toObject(mapGreen), /*min*/ZERO, /*max*/ONE);
+            mCollector.expectInRange("Tonemap curve green length is out of range",
+                    mapGreen.length, MIN_TONEMAP_CURVE_POINTS, maxCurvePoints * 2);
+            mCollector.expectValuesInRange("Tonemap curve blue values are out of range",
+                    CameraTestUtils.toObject(mapBlue), /*min*/ZERO, /*max*/ONE);
+            mCollector.expectInRange("Tonemap curve blue length is out of range",
+                    mapBlue.length, MIN_TONEMAP_CURVE_POINTS, maxCurvePoints * 2);
+        }
+    }
+
+    /**
+     * Test awb mode control.
+     * <p>
+     * Test each supported AWB mode, verify the AWB mode in capture result
+     * matches request. When AWB is locked, the color correction gains and
+     * transform should remain unchanged.
+     * </p>
+     */
+    private void awbModeAndLockTestByCamera() throws Exception {
+        int[] awbModes = mStaticInfo.getAwbAvailableModesChecked();
+        Size maxPreviewSize = mOrderedPreviewSizes.get(0);
+        CaptureRequest.Builder requestBuilder =
+                mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
+        startPreview(requestBuilder, maxPreviewSize, /*listener*/null);
+
+        for (int mode : awbModes) {
+            SimpleCaptureListener listener;
+            requestBuilder.set(CaptureRequest.CONTROL_AWB_MODE, mode);
+            listener = new SimpleCaptureListener();
+            mSession.setRepeatingRequest(requestBuilder.build(), listener, mHandler);
+            waitForSettingsApplied(listener, NUM_FRAMES_WAITED_FOR_UNKNOWN_LATENCY);
+
+            // Verify AWB mode in capture result.
+            verifyCaptureResultForKey(CaptureResult.CONTROL_AWB_MODE, mode, listener,
+                    NUM_FRAMES_VERIFIED);
+
+            // Verify color correction transform and gains stay unchanged after a lock.
+            requestBuilder.set(CaptureRequest.CONTROL_AWB_LOCK, true);
+            listener = new SimpleCaptureListener();
+            mSession.setRepeatingRequest(requestBuilder.build(), listener, mHandler);
+            waitForSettingsApplied(listener, NUM_FRAMES_WAITED_FOR_UNKNOWN_LATENCY);
+
+            if (mStaticInfo.areKeysAvailable(CaptureResult.CONTROL_AWB_STATE)) {
+                waitForResultValue(listener, CaptureResult.CONTROL_AWB_STATE,
+                        CaptureResult.CONTROL_AWB_STATE_LOCKED, NUM_RESULTS_WAIT_TIMEOUT);
+            }
+
+            verifyAwbCaptureResultUnchanged(listener, NUM_FRAMES_VERIFIED);
+        }
+    }
+
+    private void verifyAwbCaptureResultUnchanged(SimpleCaptureListener listener,
+            int numFramesVerified) {
+        // Skip check if cc gains/transform/mode are not available
+        if (!mStaticInfo.areKeysAvailable(
+                CaptureResult.COLOR_CORRECTION_GAINS,
+                CaptureResult.COLOR_CORRECTION_TRANSFORM,
+                CaptureResult.COLOR_CORRECTION_MODE)) {
+            return;
+        }
+
+        CaptureResult result = listener.getCaptureResult(WAIT_FOR_RESULT_TIMEOUT_MS);
+        RggbChannelVector lockedGains =
+                getValueNotNull(result, CaptureResult.COLOR_CORRECTION_GAINS);
+        ColorSpaceTransform lockedTransform =
+                getValueNotNull(result, CaptureResult.COLOR_CORRECTION_TRANSFORM);
+
+        for (int i = 0; i < numFramesVerified; i++) {
+            result = listener.getCaptureResult(WAIT_FOR_RESULT_TIMEOUT_MS);
+            // Color correction mode check is skipped here, as it is checked in colorCorrectionTest.
+            validateColorCorrectionResult(result, result.get(CaptureResult.COLOR_CORRECTION_MODE));
+
+            RggbChannelVector gains = getValueNotNull(result, CaptureResult.COLOR_CORRECTION_GAINS);
+            ColorSpaceTransform transform =
+                    getValueNotNull(result, CaptureResult.COLOR_CORRECTION_TRANSFORM);
+            mCollector.expectEquals("Color correction gains should remain unchanged after awb lock",
+                    lockedGains, gains);
+            mCollector.expectEquals("Color correction transform should remain unchanged after"
+                    + " awb lock", lockedTransform, transform);
+        }
+    }
+
+    /**
+     * Test AF mode control.
+     * <p>
+     * Test all supported AF modes, verify the AF mode in capture result matches
+     * request. When AF mode is one of the CONTROL_AF_MODE_CONTINUOUS_* mode,
+     * verify if the AF can converge to PASSIVE_FOCUSED or PASSIVE_UNFOCUSED
+     * state within certain amount of frames.
+     * </p>
+     */
+    private void afModeTestByCamera() throws Exception {
+        int[] afModes = mStaticInfo.getAfAvailableModesChecked();
+        Size maxPreviewSize = mOrderedPreviewSizes.get(0);
+        CaptureRequest.Builder requestBuilder =
+                mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
+        startPreview(requestBuilder, maxPreviewSize, /*listener*/null);
+
+        for (int mode : afModes) {
+            SimpleCaptureListener listener;
+            requestBuilder.set(CaptureRequest.CONTROL_AF_MODE, mode);
+            listener = new SimpleCaptureListener();
+            mSession.setRepeatingRequest(requestBuilder.build(), listener, mHandler);
+            waitForSettingsApplied(listener, NUM_FRAMES_WAITED_FOR_UNKNOWN_LATENCY);
+
+            // Verify AF mode in capture result.
+            verifyCaptureResultForKey(CaptureResult.CONTROL_AF_MODE, mode, listener,
+                    NUM_FRAMES_VERIFIED);
+
+            // Verify AF can finish a scan for CONTROL_AF_MODE_CONTINUOUS_* modes
+            if (mode == CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE ||
+                    mode == CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_VIDEO) {
+                List<Integer> afStateList = new ArrayList<Integer>();
+                afStateList.add(CaptureResult.CONTROL_AF_STATE_PASSIVE_FOCUSED);
+                afStateList.add(CaptureResult.CONTROL_AF_STATE_PASSIVE_UNFOCUSED);
+                waitForAnyResultValue(listener, CaptureResult.CONTROL_AF_STATE, afStateList,
+                        NUM_RESULTS_WAIT_TIMEOUT);
+            }
+        }
+    }
+
+    /**
+     * Test video and optical stabilizations if they are supported by a given camera.
+     */
+    private void stabilizationTestByCamera() throws Exception {
+        // video stabilization test.
+        List<Key<?>> keys = mStaticInfo.getCharacteristics().getKeys();
+
+        int[] videoStabModes = (keys.contains(CameraCharacteristics.
+                CONTROL_AVAILABLE_VIDEO_STABILIZATION_MODES)) ?
+                mStaticInfo.getAvailableVideoStabilizationModesChecked() : new int[0];
+        int[] opticalStabModes = (keys.contains(
+                CameraCharacteristics.LENS_INFO_AVAILABLE_OPTICAL_STABILIZATION)) ?
+                mStaticInfo.getAvailableOpticalStabilizationChecked() : new int[0];
+
+        Size maxPreviewSize = mOrderedPreviewSizes.get(0);
+        CaptureRequest.Builder requestBuilder =
+                mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
+        SimpleCaptureListener listener = new SimpleCaptureListener();
+        startPreview(requestBuilder, maxPreviewSize, listener);
+
+        for (int mode : videoStabModes) {
+            listener = new SimpleCaptureListener();
+            requestBuilder.set(CaptureRequest.CONTROL_VIDEO_STABILIZATION_MODE, mode);
+            mSession.setRepeatingRequest(requestBuilder.build(), listener, mHandler);
+            waitForSettingsApplied(listener, NUM_FRAMES_WAITED_FOR_UNKNOWN_LATENCY);
+            verifyCaptureResultForKey(CaptureResult.CONTROL_VIDEO_STABILIZATION_MODE, mode,
+                    listener, NUM_FRAMES_VERIFIED);
+        }
+
+        for (int mode : opticalStabModes) {
+            listener = new SimpleCaptureListener();
+            requestBuilder.set(CaptureRequest.LENS_OPTICAL_STABILIZATION_MODE, mode);
+            mSession.setRepeatingRequest(requestBuilder.build(), listener, mHandler);
+            waitForSettingsApplied(listener, NUM_FRAMES_WAITED_FOR_UNKNOWN_LATENCY);
+            verifyCaptureResultForKey(CaptureResult.LENS_OPTICAL_STABILIZATION_MODE, mode,
+                    listener, NUM_FRAMES_VERIFIED);
+        }
+
+        stopPreview();
+    }
+
+    private void digitalZoomTestByCamera(Size previewSize) throws Exception {
+        final int ZOOM_STEPS = 30;
+        final PointF[] TEST_ZOOM_CENTERS;
+
+        final int croppingType = mStaticInfo.getScalerCroppingTypeChecked();
+        if (croppingType ==
+                CameraCharacteristics.SCALER_CROPPING_TYPE_FREEFORM) {
+            TEST_ZOOM_CENTERS = new PointF[] {
+                new PointF(0.5f, 0.5f),   // Center point
+                new PointF(0.25f, 0.25f), // top left corner zoom, minimal zoom: 2x
+                new PointF(0.75f, 0.25f), // top right corner zoom, minimal zoom: 2x
+                new PointF(0.25f, 0.75f), // bottom left corner zoom, minimal zoom: 2x
+                new PointF(0.75f, 0.75f), // bottom right corner zoom, minimal zoom: 2x
+            };
+
+            if (VERBOSE) {
+                Log.v(TAG, "Testing zoom with CROPPING_TYPE = FREEFORM");
+            }
+        } else {
+            // CENTER_ONLY
+            TEST_ZOOM_CENTERS = new PointF[] {
+                    new PointF(0.5f, 0.5f),   // Center point
+            };
+
+            if (VERBOSE) {
+                Log.v(TAG, "Testing zoom with CROPPING_TYPE = CENTER_ONLY");
+            }
+        }
+
+        final float maxZoom = mStaticInfo.getAvailableMaxDigitalZoomChecked();
+        final Rect activeArraySize = mStaticInfo.getActiveArraySizeChecked();
+        Rect[] cropRegions = new Rect[ZOOM_STEPS];
+        MeteringRectangle[][] expectRegions = new MeteringRectangle[ZOOM_STEPS][];
+        CaptureRequest.Builder requestBuilder =
+                mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
+        SimpleCaptureListener listener = new SimpleCaptureListener();
+        startPreview(requestBuilder, previewSize, listener);
+        CaptureRequest[] requests = new CaptureRequest[ZOOM_STEPS];
+
+        // Set algorithm regions to full active region
+        // TODO: test more different 3A regions
+        final MeteringRectangle[] defaultMeteringRect = new MeteringRectangle[] {
+                new MeteringRectangle (
+                        /*x*/0, /*y*/0, activeArraySize.width(), activeArraySize.height(),
+                        /*meteringWeight*/1)
+        };
+
+        for (int algo = 0; algo < NUM_ALGORITHMS; algo++) {
+            update3aRegion(requestBuilder, algo,  defaultMeteringRect);
+        }
+
+        final int CAPTURE_SUBMIT_REPEAT;
+        {
+            int maxLatency = mStaticInfo.getSyncMaxLatency();
+            if (maxLatency == CameraMetadata.SYNC_MAX_LATENCY_UNKNOWN) {
+                CAPTURE_SUBMIT_REPEAT = NUM_FRAMES_WAITED_FOR_UNKNOWN_LATENCY + 1;
+            } else {
+                CAPTURE_SUBMIT_REPEAT = maxLatency + 1;
+            }
+        }
+
+        if (VERBOSE) {
+            Log.v(TAG, "Testing zoom with CAPTURE_SUBMIT_REPEAT = " + CAPTURE_SUBMIT_REPEAT);
+        }
+
+        for (PointF center : TEST_ZOOM_CENTERS) {
+            Rect previousCrop = null;
+
+            for (int i = 0; i < ZOOM_STEPS; i++) {
+                /*
+                 * Submit capture request
+                 */
+                float zoomFactor = (float) (1.0f + (maxZoom - 1.0) * i / ZOOM_STEPS);
+                cropRegions[i] = getCropRegionForZoom(zoomFactor, center, maxZoom, activeArraySize);
+                if (VERBOSE) {
+                    Log.v(TAG, "Testing Zoom for factor " + zoomFactor + " and center " +
+                            center + " The cropRegion is " + cropRegions[i] +
+                            " Preview size is " + previewSize);
+                }
+                requestBuilder.set(CaptureRequest.SCALER_CROP_REGION, cropRegions[i]);
+                requests[i] = requestBuilder.build();
+                for (int j = 0; j < CAPTURE_SUBMIT_REPEAT; ++j) {
+                    mSession.capture(requests[i], listener, mHandler);
+                }
+
+                /*
+                 * Validate capture result
+                 */
+                waitForNumResults(listener, CAPTURE_SUBMIT_REPEAT - 1); // Drop first few frames
+                CaptureResult result = listener.getCaptureResultForRequest(
+                        requests[i], NUM_RESULTS_WAIT_TIMEOUT);
+                Rect cropRegion = getValueNotNull(result, CaptureResult.SCALER_CROP_REGION);
+
+                /*
+                 * Validate resulting crop regions
+                 */
+                if (previousCrop != null) {
+                    Rect currentCrop = cropRegion;
+                    mCollector.expectTrue(String.format(
+                            "Crop region should shrink or stay the same " +
+                                    "(previous = %s, current = %s)",
+                                    previousCrop, currentCrop),
+                            previousCrop.equals(currentCrop) ||
+                                (previousCrop.width() > currentCrop.width() &&
+                                 previousCrop.height() > currentCrop.height()));
+                }
+
+                if (mStaticInfo.isHardwareLevelLimitedOrBetter()) {
+                    mCollector.expectRectsAreSimilar(
+                            "Request and result crop region should be similar",
+                            cropRegions[i], cropRegion, CROP_REGION_ERROR_PERCENT_DELTA);
+                }
+
+                if (croppingType == SCALER_CROPPING_TYPE_CENTER_ONLY) {
+                    mCollector.expectRectCentered(
+                            "Result crop region should be centered inside the active array",
+                            new Size(activeArraySize.width(), activeArraySize.height()),
+                            cropRegion, CROP_REGION_ERROR_PERCENT_CENTERED);
+                }
+
+                /*
+                 * Validate resulting metering regions
+                 */
+
+                // Use the actual reported crop region to calculate the resulting metering region
+                expectRegions[i] = getExpectedOutputRegion(
+                        /*requestRegion*/defaultMeteringRect,
+                        /*cropRect*/     cropRegion);
+
+                // Verify Output 3A region is intersection of input 3A region and crop region
+                for (int algo = 0; algo < NUM_ALGORITHMS; algo++) {
+                    validate3aRegion(result, algo, expectRegions[i]);
+                }
+
+                previousCrop = cropRegion;
+            }
+
+            if (maxZoom > 1.0f) {
+                mCollector.expectTrue(
+                        String.format("Most zoomed-in crop region should be smaller" +
+                                        "than active array w/h" +
+                                        "(last crop = %s, active array = %s)",
+                                        previousCrop, activeArraySize),
+                            (previousCrop.width() < activeArraySize.width() &&
+                             previousCrop.height() < activeArraySize.height()));
+            }
+        }
+
+        stopPreview();
+    }
+
+    private void digitalZoomPreviewCombinationTestByCamera() throws Exception {
+        for (Size size : mOrderedPreviewSizes) {
+            if (VERBOSE) {
+                Log.v(TAG, "Test preview size " + size.toString() + " digital zoom");
+            }
+
+            digitalZoomTestByCamera(size);
+        }
+    }
+
+    private void sceneModeTestByCamera() throws Exception {
+        int[] sceneModes = mStaticInfo.getAvailableSceneModesChecked();
+        Size maxPreviewSize = mOrderedPreviewSizes.get(0);
+        CaptureRequest.Builder requestBuilder =
+                mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
+        SimpleCaptureListener listener = new SimpleCaptureListener();
+        requestBuilder.set(CaptureRequest.CONTROL_MODE, CaptureRequest.CONTROL_MODE_USE_SCENE_MODE);
+        startPreview(requestBuilder, maxPreviewSize, listener);
+
+        for(int mode : sceneModes) {
+            requestBuilder.set(CaptureRequest.CONTROL_SCENE_MODE, mode);
+            listener = new SimpleCaptureListener();
+            mSession.setRepeatingRequest(requestBuilder.build(), listener, mHandler);
+            waitForSettingsApplied(listener, NUM_FRAMES_WAITED_FOR_UNKNOWN_LATENCY);
+
+            verifyCaptureResultForKey(CaptureResult.CONTROL_SCENE_MODE,
+                    mode, listener, NUM_FRAMES_VERIFIED);
+            // This also serves as purpose of showing preview for NUM_FRAMES_VERIFIED
+            verifyCaptureResultForKey(CaptureResult.CONTROL_MODE,
+                    CaptureRequest.CONTROL_MODE_USE_SCENE_MODE, listener, NUM_FRAMES_VERIFIED);
+        }
+    }
+
+    private void effectModeTestByCamera() throws Exception {
+        int[] effectModes = mStaticInfo.getAvailableEffectModesChecked();
+        Size maxPreviewSize = mOrderedPreviewSizes.get(0);
+        CaptureRequest.Builder requestBuilder =
+                mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
+        requestBuilder.set(CaptureRequest.CONTROL_MODE, CaptureRequest.CONTROL_MODE_AUTO);
+        SimpleCaptureListener listener = new SimpleCaptureListener();
+        startPreview(requestBuilder, maxPreviewSize, listener);
+
+        for(int mode : effectModes) {
+            requestBuilder.set(CaptureRequest.CONTROL_EFFECT_MODE, mode);
+            listener = new SimpleCaptureListener();
+            mSession.setRepeatingRequest(requestBuilder.build(), listener, mHandler);
+            waitForSettingsApplied(listener, NUM_FRAMES_WAITED_FOR_UNKNOWN_LATENCY);
+
+            verifyCaptureResultForKey(CaptureResult.CONTROL_EFFECT_MODE,
+                    mode, listener, NUM_FRAMES_VERIFIED);
+            // This also serves as purpose of showing preview for NUM_FRAMES_VERIFIED
+            verifyCaptureResultForKey(CaptureResult.CONTROL_MODE,
+                    CaptureRequest.CONTROL_MODE_AUTO, listener, NUM_FRAMES_VERIFIED);
+        }
+    }
+
+    //----------------------------------------------------------------
+    //---------Below are common functions for all tests.--------------
+    //----------------------------------------------------------------
+
+    /**
+     * Enable exposure manual control and change exposure and sensitivity and
+     * clamp the value into the supported range.
+     */
+    private void changeExposure(CaptureRequest.Builder requestBuilder,
+            long expTime, int sensitivity) {
+        // Check if the max analog sensitivity is available and no larger than max sensitivity.
+        // The max analog sensitivity is not actually used here. This is only an extra sanity check.
+        mStaticInfo.getMaxAnalogSensitivityChecked();
+
+        expTime = mStaticInfo.getExposureClampToRange(expTime);
+        sensitivity = mStaticInfo.getSensitivityClampToRange(sensitivity);
+
+        requestBuilder.set(CaptureRequest.CONTROL_AE_MODE, CONTROL_AE_MODE_OFF);
+        requestBuilder.set(CaptureRequest.SENSOR_EXPOSURE_TIME, expTime);
+        requestBuilder.set(CaptureRequest.SENSOR_SENSITIVITY, sensitivity);
+    }
+    /**
+     * Enable exposure manual control and change exposure time and
+     * clamp the value into the supported range.
+     *
+     * <p>The sensitivity is set to default value.</p>
+     */
+    private void changeExposure(CaptureRequest.Builder requestBuilder, long expTime) {
+        changeExposure(requestBuilder, expTime, DEFAULT_SENSITIVITY);
+    }
+
+    /**
+     * Get the exposure time array that contains multiple exposure time steps in
+     * the exposure time range.
+     */
+    private long[] getExposureTimeTestValues() {
+        long[] testValues = new long[DEFAULT_NUM_EXPOSURE_TIME_STEPS + 1];
+        long maxExpTime = mStaticInfo.getExposureMaximumOrDefault(DEFAULT_EXP_TIME_NS);
+        long minxExpTime = mStaticInfo.getExposureMinimumOrDefault(DEFAULT_EXP_TIME_NS);
+
+        long range = maxExpTime - minxExpTime;
+        double stepSize = range / (double)DEFAULT_NUM_EXPOSURE_TIME_STEPS;
+        for (int i = 0; i < testValues.length; i++) {
+            testValues[i] = minxExpTime + (long)(stepSize * i);
+            testValues[i] = mStaticInfo.getExposureClampToRange(testValues[i]);
+        }
+
+        return testValues;
+    }
+
+    /**
+     * Generate test focus distances in range of [0, minFocusDistance] in increasing order.
+     */
+    private float[] getFocusDistanceTestValuesInOrder() {
+        float[] testValues = new float[NUM_TEST_FOCUS_DISTANCES + 1];
+        float minValue = 0;
+        float maxValue = mStaticInfo.getMinimumFocusDistanceChecked();
+
+        float range = maxValue - minValue;
+        float stepSize = range / NUM_TEST_FOCUS_DISTANCES;
+        for (int i = 0; i < testValues.length; i++) {
+            testValues[i] = minValue + stepSize * i;
+        }
+
+        return testValues;
+    }
+
+    /**
+     * Get the sensitivity array that contains multiple sensitivity steps in the
+     * sensitivity range.
+     * <p>
+     * Sensitivity number of test values is determined by
+     * {@value #DEFAULT_SENSITIVITY_STEP_SIZE} and sensitivity range, and
+     * bounded by {@value #DEFAULT_NUM_SENSITIVITY_STEPS}.
+     * </p>
+     */
+    private int[] getSensitivityTestValues() {
+        int maxSensitivity = mStaticInfo.getSensitivityMaximumOrDefault(
+                DEFAULT_SENSITIVITY);
+        int minSensitivity = mStaticInfo.getSensitivityMinimumOrDefault(
+                DEFAULT_SENSITIVITY);
+
+        int range = maxSensitivity - minSensitivity;
+        int stepSize = DEFAULT_SENSITIVITY_STEP_SIZE;
+        int numSteps = range / stepSize;
+        // Bound the test steps to avoid supper long test.
+        if (numSteps > DEFAULT_NUM_SENSITIVITY_STEPS) {
+            numSteps = DEFAULT_NUM_SENSITIVITY_STEPS;
+            stepSize = range / numSteps;
+        }
+        int[] testValues = new int[numSteps + 1];
+        for (int i = 0; i < testValues.length; i++) {
+            testValues[i] = minSensitivity + stepSize * i;
+            testValues[i] = mStaticInfo.getSensitivityClampToRange(testValues[i]);
+        }
+
+        return testValues;
+    }
+
+    /**
+     * Validate the AE manual control exposure time.
+     *
+     * <p>Exposure should be close enough, and only round down if they are not equal.</p>
+     *
+     * @param request Request exposure time
+     * @param result Result exposure time
+     */
+    private void validateExposureTime(long request, long result) {
+        long expTimeDelta = request - result;
+        // First, round down not up, second, need close enough.
+        mCollector.expectTrue("Exposture time is invalid for AE manaul control test, request: "
+                + request + " result: " + result,
+                expTimeDelta < EXPOSURE_TIME_ERROR_MARGIN_NS && expTimeDelta >= 0);
+    }
+
+    /**
+     * Validate AE manual control sensitivity.
+     *
+     * @param request Request sensitivity
+     * @param result Result sensitivity
+     */
+    private void validateSensitivity(int request, int result) {
+        int sensitivityDelta = request - result;
+        // First, round down not up, second, need close enough.
+        mCollector.expectTrue("Sensitivity is invalid for AE manaul control test, request: "
+                + request + " result: " + result,
+                sensitivityDelta < SENSITIVITY_ERROR_MARGIN && sensitivityDelta >= 0);
+    }
+
+    /**
+     * Validate frame duration for a given capture.
+     *
+     * <p>Frame duration should be longer than exposure time.</p>
+     *
+     * @param result The capture result for a given capture
+     */
+    private void validateFrameDurationForCapture(CaptureResult result) {
+        long expTime = getValueNotNull(result, CaptureResult.SENSOR_EXPOSURE_TIME);
+        long frameDuration = getValueNotNull(result, CaptureResult.SENSOR_FRAME_DURATION);
+        if (VERBOSE) {
+            Log.v(TAG, "frame duration: " + frameDuration + " Exposure time: " + expTime);
+        }
+
+        mCollector.expectTrue(String.format("Frame duration (%d) should be longer than exposure"
+                + " time (%d) for a given capture", frameDuration, expTime),
+                frameDuration >= expTime);
+
+        validatePipelineDepth(result);
+    }
+
+    private <T> T getValueNotNull(CaptureResult result, CaptureResult.Key<T> key) {
+        T value = result.get(key);
+        assertNotNull("Value of Key " + key.getName() + " shouldn't be null", value);
+        return value;
+    }
+
+    /**
+     * Basic verification for the control mode capture result.
+     *
+     * @param key The capture result key to be verified against
+     * @param requestMode The request mode for this result
+     * @param listener The capture listener to get capture results
+     * @param numFramesVerified The number of capture results to be verified
+     */
+    private <T> void verifyCaptureResultForKey(CaptureResult.Key<T> key, T requestMode,
+            SimpleCaptureListener listener, int numFramesVerified) {
+        for (int i = 0; i < numFramesVerified; i++) {
+            CaptureResult result = listener.getCaptureResult(WAIT_FOR_RESULT_TIMEOUT_MS);
+            validatePipelineDepth(result);
+            T resultMode = getValueNotNull(result, key);
+            if (VERBOSE) {
+                Log.v(TAG, "Expect value: " + requestMode.toString() + " result value: "
+                        + resultMode.toString());
+            }
+            mCollector.expectEquals("Key " + key.getName() + " result should match request",
+                    requestMode, resultMode);
+        }
+    }
+
+    /**
+     * Verify if the fps is slow down for given input request with certain
+     * controls inside.
+     * <p>
+     * This method selects a max preview size for each fps range, and then
+     * configure the preview stream. Preview is started with the max preview
+     * size, and then verify if the result frame duration is in the frame
+     * duration range.
+     * </p>
+     *
+     * @param requestBuilder The request builder that contains post-processing
+     *            controls that could impact the output frame rate, such as
+     *            {@link CaptureRequest.NOISE_REDUCTION_MODE}. The value of
+     *            these controls must be set to some values such that the frame
+     *            rate is not slow down.
+     * @param numFramesVerified The number of frames to be verified
+     */
+    private void verifyFpsNotSlowDown(CaptureRequest.Builder requestBuilder,
+            int numFramesVerified)  throws Exception {
+        Range<Integer>[] fpsRanges = mStaticInfo.getAeAvailableTargetFpsRangesChecked();
+        boolean antiBandingOffIsSupported = mStaticInfo.isAntiBandingOffModeSupported();
+        Range<Integer> fpsRange;
+        SimpleCaptureListener resultListener;
+
+        for (int i = 0; i < fpsRanges.length; i += 1) {
+            fpsRange = fpsRanges[i];
+            Size previewSz = getMaxPreviewSizeForFpsRange(fpsRange);
+            // If unable to find a preview size, then log the failure, and skip this run.
+            if (!mCollector.expectTrue(String.format(
+                    "Unable to find a preview size supporting given fps range %s",
+                    fpsRange), previewSz != null)) {
+                continue;
+            }
+
+            if (VERBOSE) {
+                Log.v(TAG, String.format("Test fps range %s for preview size %s",
+                        fpsRange, previewSz.toString()));
+            }
+            requestBuilder.set(CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE, fpsRange);
+            // Turn off auto antibanding to avoid exposure time and frame duration interference
+            // from antibanding algorithm.
+            if (antiBandingOffIsSupported) {
+                requestBuilder.set(CaptureRequest.CONTROL_AE_ANTIBANDING_MODE,
+                        CaptureRequest.CONTROL_AE_ANTIBANDING_MODE_OFF);
+            } else {
+                // The device doesn't implement the OFF mode, test continues. It need make sure
+                // that the antibanding algorithm doesn't slow down the fps.
+                Log.i(TAG, "OFF antibanding mode is not supported, the camera device output must" +
+                        " not slow down the frame rate regardless of its current antibanding" +
+                        " mode");
+            }
+
+            resultListener = new SimpleCaptureListener();
+            startPreview(requestBuilder, previewSz, resultListener);
+            long[] frameDurationRange =
+                    new long[]{(long) (1e9 / fpsRange.getUpper()), (long) (1e9 / fpsRange.getLower())};
+            for (int j = 0; j < numFramesVerified; j++) {
+                CaptureResult result =
+                        resultListener.getCaptureResult(WAIT_FOR_RESULT_TIMEOUT_MS);
+                validatePipelineDepth(result);
+                long frameDuration = getValueNotNull(result, CaptureResult.SENSOR_FRAME_DURATION);
+                mCollector.expectInRange(
+                        "Frame duration must be in the range of " + Arrays.toString(frameDurationRange),
+                        frameDuration,
+                        (long) (frameDurationRange[0] * (1 - FRAME_DURATION_ERROR_MARGIN)),
+                        (long) (frameDurationRange[1] * (1 + FRAME_DURATION_ERROR_MARGIN)));
+            }
+        }
+
+        mSession.stopRepeating();
+    }
+
+    /**
+     * Validate the pipeline depth result.
+     *
+     * @param result The capture result to get pipeline depth data
+     */
+    private void validatePipelineDepth(CaptureResult result) {
+        final byte MIN_PIPELINE_DEPTH = 1;
+        byte maxPipelineDepth = mStaticInfo.getPipelineMaxDepthChecked();
+        Byte pipelineDepth = getValueNotNull(result, CaptureResult.REQUEST_PIPELINE_DEPTH);
+        mCollector.expectInRange(String.format("Pipeline depth must be in the range of [%d, %d]",
+                MIN_PIPELINE_DEPTH, maxPipelineDepth), pipelineDepth, MIN_PIPELINE_DEPTH,
+                maxPipelineDepth);
+    }
+
+    /**
+     * Calculate the anti-flickering corrected exposure time.
+     * <p>
+     * If the input exposure time is very short (shorter than flickering
+     * boundary), which indicate the scene is bright and very likely at outdoor
+     * environment, skip the correction, as it doesn't make much sense by doing so.
+     * </p>
+     * <p>
+     * For long exposure time (larger than the flickering boundary), find the
+     * exposure time that is closest to the flickering boundary.
+     * </p>
+     *
+     * @param flickeringMode The flickering mode
+     * @param exposureTime The input exposureTime to be corrected
+     * @return anti-flickering corrected exposure time
+     */
+    private long getAntiFlickeringExposureTime(int flickeringMode, long exposureTime) {
+        if (flickeringMode != ANTI_FLICKERING_50HZ && flickeringMode != ANTI_FLICKERING_60HZ) {
+            throw new IllegalArgumentException("Input anti-flickering mode must be 50 or 60Hz");
+        }
+        long flickeringBoundary = EXPOSURE_TIME_BOUNDARY_50HZ_NS;
+        if (flickeringMode == ANTI_FLICKERING_60HZ) {
+            flickeringBoundary = EXPOSURE_TIME_BOUNDARY_60HZ_NS;
+        }
+
+        if (exposureTime <= flickeringBoundary) {
+            return exposureTime;
+        }
+
+        // Find the closest anti-flickering corrected exposure time
+        long correctedExpTime = exposureTime + (flickeringBoundary / 2);
+        correctedExpTime = correctedExpTime - (correctedExpTime % flickeringBoundary);
+        return correctedExpTime;
+    }
+
+    /**
+     * Update one 3A region in capture request builder if that region is supported. Do nothing
+     * if the specified 3A region is not supported by camera device.
+     * @param requestBuilder The request to be updated
+     * @param algoIdx The index to the algorithm. (AE: 0, AWB: 1, AF: 2)
+     * @param regions The 3A regions to be set
+     */
+    private void update3aRegion(
+            CaptureRequest.Builder requestBuilder, int algoIdx, MeteringRectangle[] regions)
+    {
+        int maxRegions;
+        CaptureRequest.Key<MeteringRectangle[]> key;
+
+        if (regions == null || regions.length == 0) {
+            throw new IllegalArgumentException("Invalid input 3A region!");
+        }
+
+        switch (algoIdx) {
+            case INDEX_ALGORITHM_AE:
+                maxRegions = mStaticInfo.getAeMaxRegionsChecked();
+                key = CaptureRequest.CONTROL_AE_REGIONS;
+                break;
+            case INDEX_ALGORITHM_AWB:
+                maxRegions = mStaticInfo.getAwbMaxRegionsChecked();
+                key = CaptureRequest.CONTROL_AWB_REGIONS;
+                break;
+            case INDEX_ALGORITHM_AF:
+                maxRegions = mStaticInfo.getAfMaxRegionsChecked();
+                key = CaptureRequest.CONTROL_AF_REGIONS;
+                break;
+            default:
+                throw new IllegalArgumentException("Unknown 3A Algorithm!");
+        }
+
+        if (maxRegions >= regions.length) {
+            requestBuilder.set(key, regions);
+        }
+    }
+
+    /**
+     * Validate one 3A region in capture result equals to expected region if that region is
+     * supported. Do nothing if the specified 3A region is not supported by camera device.
+     * @param result The capture result to be validated
+     * @param algoIdx The index to the algorithm. (AE: 0, AWB: 1, AF: 2)
+     * @param expectRegions The 3A regions expected in capture result
+     */
+    private void validate3aRegion(
+            CaptureResult result, int algoIdx, MeteringRectangle[] expectRegions)
+    {
+        int maxRegions;
+        CaptureResult.Key<MeteringRectangle[]> key;
+        MeteringRectangle[] actualRegion;
+
+        switch (algoIdx) {
+            case INDEX_ALGORITHM_AE:
+                maxRegions = mStaticInfo.getAeMaxRegionsChecked();
+                key = CaptureResult.CONTROL_AE_REGIONS;
+                break;
+            case INDEX_ALGORITHM_AWB:
+                maxRegions = mStaticInfo.getAwbMaxRegionsChecked();
+                key = CaptureResult.CONTROL_AWB_REGIONS;
+                break;
+            case INDEX_ALGORITHM_AF:
+                maxRegions = mStaticInfo.getAfMaxRegionsChecked();
+                key = CaptureResult.CONTROL_AF_REGIONS;
+                break;
+            default:
+                throw new IllegalArgumentException("Unknown 3A Algorithm!");
+        }
+
+        if (maxRegions > 0)
+        {
+            actualRegion = getValueNotNull(result, key);
+            mCollector.expectEquals(
+                    "Expected 3A regions: " + Arrays.toString(expectRegions) +
+                    " does not match actual one: " + Arrays.toString(actualRegion),
+                    expectRegions, actualRegion);
+        }
+    }
+}
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/CaptureResultTest.java b/tests/tests/hardware/src/android/hardware/camera2/cts/CaptureResultTest.java
new file mode 100644
index 0000000..2ccbc3d
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/CaptureResultTest.java
@@ -0,0 +1,482 @@
+/*
+ * 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.content.Context;
+import android.graphics.ImageFormat;
+import android.hardware.camera2.CameraCaptureSession;
+import android.hardware.camera2.CameraCharacteristics;
+import android.hardware.camera2.CameraDevice;
+import android.hardware.camera2.CameraMetadata;
+import android.hardware.camera2.CaptureRequest;
+import android.hardware.camera2.CaptureResult;
+import android.hardware.camera2.TotalCaptureResult;
+import android.hardware.camera2.cts.helpers.CameraSessionUtils;
+import android.media.Image;
+import android.media.ImageReader;
+import android.os.SystemClock;
+import android.util.Pair;
+import android.util.Size;
+import android.hardware.camera2.cts.testcases.Camera2AndroidTestCase;
+
+import static android.hardware.camera2.cts.CameraTestUtils.*;
+import static android.hardware.camera2.cts.helpers.CameraSessionUtils.*;
+
+import android.util.Log;
+import android.view.Surface;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+public class CaptureResultTest extends Camera2AndroidTestCase {
+    private static final String TAG = "CaptureResultTest";
+    private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
+    private static final int MAX_NUM_IMAGES = MAX_READER_IMAGES;
+    private static final int NUM_FRAMES_VERIFIED = 30;
+    private static final long WAIT_FOR_RESULT_TIMEOUT_MS = 3000;
+
+    // List that includes all public keys from CaptureResult
+    List<CaptureResult.Key<?>> mAllKeys;
+
+    // List tracking the failed test keys.
+
+    @Override
+    public void setContext(Context context) {
+        mAllKeys = getAllCaptureResultKeys();
+        super.setContext(context);
+
+        /**
+         * 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", getContext().getCacheDir().toString());
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        super.tearDown();
+    }
+
+    /**
+     * <p>
+     * Basic non-null check test for multiple capture results.
+     * </p>
+     * <p>
+     * When capturing many frames, some camera devices may return some results that have null keys
+     * randomly, which is an API violation and could cause application crash randomly. This test
+     * runs a typical flexible yuv capture many times, and checks if there is any null entries in
+     * a capture result.
+     * </p>
+     */
+    public void testCameraCaptureResultAllKeys() throws Exception {
+        for (String id : mCameraIds) {
+            try {
+                openDevice(id);
+                // Create image reader and surface.
+                Size size = mOrderedPreviewSizes.get(0);
+                createDefaultImageReader(size, ImageFormat.YUV_420_888, MAX_NUM_IMAGES,
+                        new ImageDropperListener());
+
+                // Configure output streams.
+                List<Surface> outputSurfaces = new ArrayList<Surface>(1);
+                outputSurfaces.add(mReaderSurface);
+                createSession(outputSurfaces);
+
+                CaptureRequest.Builder requestBuilder =
+                        mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
+                assertNotNull("Failed to create capture request", requestBuilder);
+                requestBuilder.addTarget(mReaderSurface);
+
+                // Start capture
+                SimpleCaptureListener captureListener = new SimpleCaptureListener();
+                startCapture(requestBuilder.build(), /*repeating*/true, captureListener, mHandler);
+
+                // Get the waived keys for current camera device
+                List<CaptureResult.Key<?>> waiverkeys = getWaiverKeysForCamera();
+
+                // Verify results
+                validateCaptureResult(captureListener, waiverkeys, requestBuilder,
+                        NUM_FRAMES_VERIFIED);
+
+                stopCapture(/*fast*/false);
+            } finally {
+                closeDevice(id);
+                closeDefaultImageReader();
+            }
+        }
+    }
+
+    /**
+     * Check that the timestamps passed in the results, buffers, and capture callbacks match for
+     * a single request, and increase monotonically
+     */
+    public void testResultTimestamps() throws Exception {
+        for (String id : mCameraIds) {
+            ImageReader previewReader = null;
+            ImageReader jpegReader = null;
+
+            SimpleImageReaderListener jpegListener = new SimpleImageReaderListener();
+            SimpleImageReaderListener prevListener = new SimpleImageReaderListener();
+            try {
+                openDevice(id);
+
+                CaptureRequest.Builder previewBuilder =
+                        mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
+                CaptureRequest.Builder multiBuilder =
+                        mCamera.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
+
+                // Create image reader and surface.
+                Size previewSize = mOrderedPreviewSizes.get(0);
+                Size jpegSize = mOrderedStillSizes.get(0);
+
+                // Create ImageReaders.
+                previewReader = makeImageReader(previewSize, ImageFormat.YUV_420_888,
+                        MAX_NUM_IMAGES, prevListener, mHandler);
+                jpegReader = makeImageReader(jpegSize, ImageFormat.JPEG,
+                        MAX_NUM_IMAGES, jpegListener, mHandler);
+
+                // Configure output streams with preview and jpeg streams.
+                List<Surface> outputSurfaces = new ArrayList<>(Arrays.asList(
+                        previewReader.getSurface(), jpegReader.getSurface()));
+
+                SessionListener mockSessionListener = getMockSessionListener();
+
+                CameraCaptureSession session = configureAndVerifySession(mockSessionListener,
+                        mCamera, outputSurfaces, mHandler);
+
+                // Configure the requests.
+                previewBuilder.addTarget(previewReader.getSurface());
+                multiBuilder.addTarget(previewReader.getSurface());
+                multiBuilder.addTarget(jpegReader.getSurface());
+
+                CaptureListener mockCaptureListener = getMockCaptureListener();
+
+                // Capture targeting only preview
+                Pair<TotalCaptureResult, Long> result = captureAndVerifyResult(mockCaptureListener,
+                        session, previewBuilder.build(), mHandler);
+
+                // Check if all timestamps are the same
+                validateTimestamps("Result 1", result.first,
+                        prevListener.getImage(CAPTURE_IMAGE_TIMEOUT_MS), result.second);
+
+                // Capture targeting both jpeg and preview
+                Pair<TotalCaptureResult, Long> result2 = captureAndVerifyResult(mockCaptureListener,
+                        session, multiBuilder.build(), mHandler);
+
+                // Check if all timestamps are the same
+                validateTimestamps("Result 2 Preview", result2.first,
+                        prevListener.getImage(CAPTURE_IMAGE_TIMEOUT_MS), result2.second);
+                validateTimestamps("Result 2 Jpeg", result2.first,
+                        jpegListener.getImage(CAPTURE_IMAGE_TIMEOUT_MS), result2.second);
+
+                // Check if timestamps are increasing
+                mCollector.expectGreater("Timestamps must be increasing.", result.second,
+                        result2.second);
+
+                // Capture two preview frames
+                long startTime = SystemClock.elapsedRealtimeNanos();
+                Pair<TotalCaptureResult, Long> result3 = captureAndVerifyResult(mockCaptureListener,
+                        session, previewBuilder.build(), mHandler);
+                Pair<TotalCaptureResult, Long> result4 = captureAndVerifyResult(mockCaptureListener,
+                        session, previewBuilder.build(), mHandler);
+                long clockDiff = SystemClock.elapsedRealtimeNanos() - startTime;
+                long resultDiff = result4.second - result3.second;
+
+                // Check if all timestamps are the same
+                validateTimestamps("Result 3", result3.first,
+                        prevListener.getImage(CAPTURE_IMAGE_TIMEOUT_MS), result3.second);
+                validateTimestamps("Result 4", result4.first,
+                        prevListener.getImage(CAPTURE_IMAGE_TIMEOUT_MS), result4.second);
+
+                // Check that the timestamps monotonically increase at a reasonable rate
+                mCollector.expectGreaterOrEqual("Timestamps increase faster than system clock.",
+                        resultDiff, clockDiff);
+                mCollector.expectGreater("Timestamps must be increasing.", result3.second,
+                        result4.second);
+            } finally {
+                closeDevice(id);
+                closeImageReader(previewReader);
+                closeImageReader(jpegReader);
+            }
+        }
+    }
+
+    private void validateTimestamps(String msg, TotalCaptureResult result, Image resultImage,
+                                    long captureTime) {
+        mCollector.expectKeyValueEquals(result, CaptureResult.SENSOR_TIMESTAMP, captureTime);
+        mCollector.expectEquals(msg + ": Capture timestamp must be same as resultImage timestamp",
+                resultImage.getTimestamp(), captureTime);
+    }
+
+    private void validateCaptureResult(SimpleCaptureListener captureListener,
+            List<CaptureResult.Key<?>> skippedKeys, CaptureRequest.Builder requestBuilder,
+            int numFramesVerified) throws Exception {
+        CaptureResult result = null;
+        for (int i = 0; i < numFramesVerified; i++) {
+            String failMsg = "Failed capture result " + i + " test ";
+            result = captureListener.getCaptureResult(WAIT_FOR_RESULT_TIMEOUT_MS);
+
+            for (CaptureResult.Key<?> key : mAllKeys) {
+                if (!skippedKeys.contains(key)) {
+                    /**
+                     * Check the critical tags here.
+                     * TODO: Can use the same key for request and result when request/result
+                     * becomes symmetric (b/14059883). Then below check can be wrapped into
+                     * a generic function.
+                     */
+                    String msg = failMsg + "for key " + key.getName();
+                    if (key.equals(CaptureResult.CONTROL_AE_MODE)) {
+                        mCollector.expectEquals(msg,
+                                requestBuilder.get(CaptureRequest.CONTROL_AE_MODE),
+                                result.get(CaptureResult.CONTROL_AE_MODE));
+                    } else if (key.equals(CaptureResult.CONTROL_AF_MODE)) {
+                        mCollector.expectEquals(msg,
+                                requestBuilder.get(CaptureRequest.CONTROL_AF_MODE),
+                                result.get(CaptureResult.CONTROL_AF_MODE));
+                    } else if (key.equals(CaptureResult.CONTROL_AWB_MODE)) {
+                        mCollector.expectEquals(msg,
+                                requestBuilder.get(CaptureRequest.CONTROL_AWB_MODE),
+                                result.get(CaptureResult.CONTROL_AWB_MODE));
+                    } else if (key.equals(CaptureResult.CONTROL_MODE)) {
+                        mCollector.expectEquals(msg,
+                                requestBuilder.get(CaptureRequest.CONTROL_MODE),
+                                result.get(CaptureResult.CONTROL_MODE));
+                    } else if (key.equals(CaptureResult.STATISTICS_FACE_DETECT_MODE)) {
+                        mCollector.expectEquals(msg,
+                                requestBuilder.get(CaptureRequest.STATISTICS_FACE_DETECT_MODE),
+                                result.get(CaptureResult.STATISTICS_FACE_DETECT_MODE));
+                    } else if (key.equals(CaptureResult.NOISE_REDUCTION_MODE)) {
+                        mCollector.expectEquals(msg,
+                                requestBuilder.get(CaptureRequest.NOISE_REDUCTION_MODE),
+                                result.get(CaptureResult.NOISE_REDUCTION_MODE));
+                    } else if (key.equals(CaptureResult.NOISE_REDUCTION_MODE)) {
+                        mCollector.expectEquals(msg,
+                                requestBuilder.get(CaptureRequest.NOISE_REDUCTION_MODE),
+                                result.get(CaptureResult.NOISE_REDUCTION_MODE));
+                    } else if (key.equals(CaptureResult.REQUEST_PIPELINE_DEPTH)) {
+
+                    } else {
+                        // Only do non-null check for the rest of keys.
+                        mCollector.expectKeyValueNotNull(failMsg, result, key);
+                    }
+                }
+            }
+        }
+    }
+
+    /*
+     * Add waiver keys per camera device hardware level and capability.
+     *
+     * Must be called after camera device is opened.
+     */
+    private List<CaptureResult.Key<?>> getWaiverKeysForCamera() {
+        List<CaptureResult.Key<?>> waiverKeys = new ArrayList<>();
+
+        // Global waiver keys
+        waiverKeys.add(CaptureResult.JPEG_GPS_LOCATION);
+        waiverKeys.add(CaptureResult.JPEG_ORIENTATION);
+        waiverKeys.add(CaptureResult.JPEG_QUALITY);
+        waiverKeys.add(CaptureResult.JPEG_THUMBNAIL_QUALITY);
+        waiverKeys.add(CaptureResult.JPEG_THUMBNAIL_SIZE);
+
+        // Keys only present when corresponding control is on are being
+        // verified in its own functional test
+        // Only present when tone mapping mode is CONTRAST_CURVE
+        waiverKeys.add(CaptureResult.TONEMAP_CURVE);
+        // Only present when test pattern mode is SOLID_COLOR.
+        // TODO: verify this key in test pattern test later
+        waiverKeys.add(CaptureResult.SENSOR_TEST_PATTERN_DATA);
+        // Only present when STATISTICS_LENS_SHADING_MAP_MODE is ON
+        waiverKeys.add(CaptureResult.STATISTICS_LENS_SHADING_CORRECTION_MAP);
+        //  Only present when STATISTICS_INFO_AVAILABLE_HOT_PIXEL_MAP_MODES is ON
+        waiverKeys.add(CaptureResult.STATISTICS_HOT_PIXEL_MAP);
+        //  Only present when face detection is on
+        waiverKeys.add(CaptureResult.STATISTICS_FACES);
+
+        //Keys not required if RAW is not supported
+        if (!mStaticInfo.isCapabilitySupported(
+                CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_RAW)) {
+            waiverKeys.add(CaptureResult.SENSOR_NEUTRAL_COLOR_POINT);
+            waiverKeys.add(CaptureResult.SENSOR_GREEN_SPLIT);
+            waiverKeys.add(CaptureResult.SENSOR_NOISE_PROFILE);
+        }
+
+        if (mStaticInfo.isHardwareLevelFull()) {
+            return waiverKeys;
+        }
+
+        /*
+         * Hardware Level = LIMITED or LEGACY
+         */
+        // Key not present if certain control is not supported
+        if (!mStaticInfo.isManualColorCorrectionSupported()) {
+            waiverKeys.add(CaptureResult.COLOR_CORRECTION_GAINS);
+            waiverKeys.add(CaptureResult.COLOR_CORRECTION_MODE);
+            waiverKeys.add(CaptureResult.COLOR_CORRECTION_TRANSFORM);
+        }
+
+        if (!mStaticInfo.isManualColorAberrationControlSupported()) {
+            waiverKeys.add(CaptureResult.COLOR_CORRECTION_ABERRATION_CORRECTION_MODE);
+        }
+
+        if (!mStaticInfo.isManualToneMapSupported()) {
+            waiverKeys.add(CaptureResult.TONEMAP_MODE);
+        }
+
+        if (!mStaticInfo.isEdgeModeControlSupported()) {
+            waiverKeys.add(CaptureResult.EDGE_MODE);
+        }
+
+        if (!mStaticInfo.isHotPixelMapModeControlSupported()) {
+            waiverKeys.add(CaptureResult.HOT_PIXEL_MODE);
+        }
+
+        if (!mStaticInfo.isNoiseReductionModeControlSupported()) {
+            waiverKeys.add(CaptureResult.NOISE_REDUCTION_MODE);
+        }
+
+        //Keys not required if manual sensor control is not supported
+        if (!mStaticInfo.isCapabilitySupported(
+                CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR)) {
+            waiverKeys.add(CaptureResult.SENSOR_EXPOSURE_TIME);
+            waiverKeys.add(CaptureResult.SENSOR_FRAME_DURATION);
+            waiverKeys.add(CaptureResult.SENSOR_SENSITIVITY);
+            waiverKeys.add(CaptureResult.BLACK_LEVEL_LOCK);
+        }
+
+        if (mStaticInfo.isHardwareLevelLimited()) {
+            return waiverKeys;
+        }
+
+        /*
+         * Hardware Level = LEGACY
+         */
+        waiverKeys.add(CaptureResult.CONTROL_AE_PRECAPTURE_TRIGGER);
+        waiverKeys.add(CaptureResult.CONTROL_AE_STATE);
+        waiverKeys.add(CaptureResult.CONTROL_AWB_STATE);
+        waiverKeys.add(CaptureResult.CONTROL_AWB_REGIONS);
+        waiverKeys.add(CaptureResult.FLASH_STATE);
+        waiverKeys.add(CaptureResult.LENS_OPTICAL_STABILIZATION_MODE);
+        waiverKeys.add(CaptureResult.LENS_FOCUS_RANGE);
+        waiverKeys.add(CaptureResult.LENS_FOCUS_DISTANCE);
+        waiverKeys.add(CaptureResult.LENS_STATE);
+        waiverKeys.add(CaptureResult.LENS_APERTURE);
+        waiverKeys.add(CaptureResult.LENS_FILTER_DENSITY);
+        waiverKeys.add(CaptureResult.SENSOR_ROLLING_SHUTTER_SKEW);
+        waiverKeys.add(CaptureResult.SHADING_MODE);
+        waiverKeys.add(CaptureResult.STATISTICS_LENS_SHADING_MAP_MODE);
+        waiverKeys.add(CaptureResult.STATISTICS_SCENE_FLICKER);
+        waiverKeys.add(CaptureResult.STATISTICS_HOT_PIXEL_MAP_MODE);
+
+        return waiverKeys;
+    }
+
+    /**
+     * TODO: Use CameraCharacteristics.getAvailableCaptureResultKeys() once we can filter out
+     * @hide keys.
+     *
+     */
+
+    /*@O~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~
+     * The key entries below this point are generated from metadata
+     * definitions in /system/media/camera/docs. Do not modify by hand or
+     * modify the comment blocks at the start or end.
+     *~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~*/
+
+    private static List<CaptureResult.Key<?>> getAllCaptureResultKeys() {
+        ArrayList<CaptureResult.Key<?>> resultKeys = new ArrayList<CaptureResult.Key<?>>();
+        resultKeys.add(CaptureResult.COLOR_CORRECTION_MODE);
+        resultKeys.add(CaptureResult.COLOR_CORRECTION_TRANSFORM);
+        resultKeys.add(CaptureResult.COLOR_CORRECTION_GAINS);
+        resultKeys.add(CaptureResult.COLOR_CORRECTION_ABERRATION_CORRECTION_MODE);
+        resultKeys.add(CaptureResult.CONTROL_AE_ANTIBANDING_MODE);
+        resultKeys.add(CaptureResult.CONTROL_AE_EXPOSURE_COMPENSATION);
+        resultKeys.add(CaptureResult.CONTROL_AE_LOCK);
+        resultKeys.add(CaptureResult.CONTROL_AE_MODE);
+        resultKeys.add(CaptureResult.CONTROL_AE_REGIONS);
+        resultKeys.add(CaptureResult.CONTROL_AE_TARGET_FPS_RANGE);
+        resultKeys.add(CaptureResult.CONTROL_AE_PRECAPTURE_TRIGGER);
+        resultKeys.add(CaptureResult.CONTROL_AF_MODE);
+        resultKeys.add(CaptureResult.CONTROL_AF_REGIONS);
+        resultKeys.add(CaptureResult.CONTROL_AF_TRIGGER);
+        resultKeys.add(CaptureResult.CONTROL_AWB_LOCK);
+        resultKeys.add(CaptureResult.CONTROL_AWB_MODE);
+        resultKeys.add(CaptureResult.CONTROL_AWB_REGIONS);
+        resultKeys.add(CaptureResult.CONTROL_CAPTURE_INTENT);
+        resultKeys.add(CaptureResult.CONTROL_EFFECT_MODE);
+        resultKeys.add(CaptureResult.CONTROL_MODE);
+        resultKeys.add(CaptureResult.CONTROL_SCENE_MODE);
+        resultKeys.add(CaptureResult.CONTROL_VIDEO_STABILIZATION_MODE);
+        resultKeys.add(CaptureResult.CONTROL_AE_STATE);
+        resultKeys.add(CaptureResult.CONTROL_AF_STATE);
+        resultKeys.add(CaptureResult.CONTROL_AWB_STATE);
+        resultKeys.add(CaptureResult.EDGE_MODE);
+        resultKeys.add(CaptureResult.FLASH_MODE);
+        resultKeys.add(CaptureResult.FLASH_STATE);
+        resultKeys.add(CaptureResult.HOT_PIXEL_MODE);
+        resultKeys.add(CaptureResult.JPEG_GPS_LOCATION);
+        resultKeys.add(CaptureResult.JPEG_ORIENTATION);
+        resultKeys.add(CaptureResult.JPEG_QUALITY);
+        resultKeys.add(CaptureResult.JPEG_THUMBNAIL_QUALITY);
+        resultKeys.add(CaptureResult.JPEG_THUMBNAIL_SIZE);
+        resultKeys.add(CaptureResult.LENS_APERTURE);
+        resultKeys.add(CaptureResult.LENS_FILTER_DENSITY);
+        resultKeys.add(CaptureResult.LENS_FOCAL_LENGTH);
+        resultKeys.add(CaptureResult.LENS_FOCUS_DISTANCE);
+        resultKeys.add(CaptureResult.LENS_OPTICAL_STABILIZATION_MODE);
+        resultKeys.add(CaptureResult.LENS_FOCUS_RANGE);
+        resultKeys.add(CaptureResult.LENS_STATE);
+        resultKeys.add(CaptureResult.NOISE_REDUCTION_MODE);
+        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_NEUTRAL_COLOR_POINT);
+        resultKeys.add(CaptureResult.SENSOR_NOISE_PROFILE);
+        resultKeys.add(CaptureResult.SENSOR_GREEN_SPLIT);
+        resultKeys.add(CaptureResult.SENSOR_TEST_PATTERN_DATA);
+        resultKeys.add(CaptureResult.SENSOR_TEST_PATTERN_MODE);
+        resultKeys.add(CaptureResult.SENSOR_ROLLING_SHUTTER_SKEW);
+        resultKeys.add(CaptureResult.SHADING_MODE);
+        resultKeys.add(CaptureResult.STATISTICS_FACE_DETECT_MODE);
+        resultKeys.add(CaptureResult.STATISTICS_HOT_PIXEL_MAP_MODE);
+        resultKeys.add(CaptureResult.STATISTICS_FACES);
+        resultKeys.add(CaptureResult.STATISTICS_LENS_SHADING_CORRECTION_MAP);
+        resultKeys.add(CaptureResult.STATISTICS_SCENE_FLICKER);
+        resultKeys.add(CaptureResult.STATISTICS_HOT_PIXEL_MAP);
+        resultKeys.add(CaptureResult.STATISTICS_LENS_SHADING_MAP_MODE);
+        resultKeys.add(CaptureResult.TONEMAP_CURVE);
+        resultKeys.add(CaptureResult.TONEMAP_MODE);
+        resultKeys.add(CaptureResult.BLACK_LEVEL_LOCK);
+
+        return resultKeys;
+    }
+
+    /*~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~
+     * End generated code
+     *~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~O@*/
+}
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/DngCreatorTest.java b/tests/tests/hardware/src/android/hardware/camera2/cts/DngCreatorTest.java
new file mode 100644
index 0000000..136cd0d
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/DngCreatorTest.java
@@ -0,0 +1,204 @@
+/*
+ * 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.graphics.ImageFormat;
+import android.hardware.camera2.CameraCaptureSession;
+import android.hardware.camera2.CameraCharacteristics;
+import android.hardware.camera2.CameraDevice;
+import android.hardware.camera2.CaptureRequest;
+import android.hardware.camera2.CaptureResult;
+import android.hardware.camera2.DngCreator;
+import android.hardware.camera2.cts.helpers.StaticMetadata;
+import android.hardware.camera2.cts.testcases.Camera2AndroidTestCase;
+import android.media.Image;
+import android.media.ImageReader;
+import android.util.Log;
+import android.util.Pair;
+import android.util.Size;
+import android.view.Surface;
+
+import java.io.ByteArrayOutputStream;
+import java.io.FileOutputStream;
+import java.util.ArrayList;
+import java.util.List;
+
+import static android.hardware.camera2.cts.CameraTestUtils.configureCameraSession;
+
+/**
+ * Tests for the DngCreator API.
+ */
+public class DngCreatorTest extends Camera2AndroidTestCase {
+    private static final String TAG = "DngCreatorTest";
+    private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
+    private static final String DEBUG_DNG_FILE = "/raw16.dng";
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        super.tearDown();
+    }
+
+    /**
+     * Test basic raw capture and DNG saving functionality for each of the available cameras.
+     *
+     * <p>
+     * For each camera, capture a single RAW16 image at the first capture size reported for
+     * the raw format on that device, and save that image as a DNG file.  No further validation
+     * is done.
+     * </p>
+     *
+     * <p>
+     * Note: Enabling adb shell setprop log.tag.DngCreatorTest VERBOSE will also cause the
+     * raw image captured for the first reported camera device to be saved to an output file.
+     * </p>
+     */
+    public void testSingleImageBasic() throws Exception {
+        for (int i = 0; i < mCameraIds.length; i++) {
+            String deviceId = mCameraIds[i];
+            ImageReader captureReader = null;
+            FileOutputStream fileStream = null;
+            ByteArrayOutputStream outputStream = null;
+            try {
+                openDevice(deviceId);
+
+                Size[] targetCaptureSizes =
+                        mStaticInfo.getAvailableSizesForFormatChecked(ImageFormat.RAW_SENSOR,
+                                StaticMetadata.StreamDirection.Output);
+                if (targetCaptureSizes.length == 0) {
+                    if (VERBOSE) {
+                        Log.v(TAG, "Skipping testSingleImageBasic - " +
+                                "no raw output streams for camera " + deviceId);
+                    }
+                    continue;
+                }
+
+                Size s = targetCaptureSizes[0];
+
+                // Create capture image reader
+                CameraTestUtils.SimpleImageReaderListener captureListener
+                        = new CameraTestUtils.SimpleImageReaderListener();
+                captureReader = createImageReader(s, ImageFormat.RAW_SENSOR, 2,
+                        captureListener);
+                Pair<Image, CaptureResult> resultPair = captureSingleRawShot(s, captureReader, captureListener);
+                CameraCharacteristics characteristics = mStaticInfo.getCharacteristics();
+
+                // Test simple writeImage, no header checks
+                DngCreator dngCreator = new DngCreator(characteristics, resultPair.second);
+                outputStream = new ByteArrayOutputStream();
+                dngCreator.writeImage(outputStream, resultPair.first);
+
+                if (VERBOSE && i == 0) {
+                    // Write out captured DNG file for the first camera device if setprop is enabled
+                    fileStream = new FileOutputStream(DEBUG_FILE_NAME_BASE +
+                            DEBUG_DNG_FILE);
+                    fileStream.write(outputStream.toByteArray());
+                    fileStream.flush();
+                    fileStream.close();
+                    Log.v(TAG, "Test DNG file for camera " + deviceId + " saved to " +
+                            DEBUG_FILE_NAME_BASE + DEBUG_DNG_FILE);
+                }
+            } finally {
+                closeDevice(deviceId);
+                closeImageReader(captureReader);
+
+                if (outputStream != null) {
+                    outputStream.close();
+                }
+
+                if (fileStream != null) {
+                    fileStream.close();
+                }
+            }
+        }
+    }
+
+    // TODO: Further tests for DNG header validation.
+
+    /**
+     * Capture a single raw image.
+     *
+     * <p>Capture an raw image for a given size.</p>
+     *
+     * @param s The size of the raw image to capture.  Must be one of the available sizes for this
+     *          device.
+     * @return a pair containing the {@link Image} and {@link CaptureResult} used for this capture.
+     */
+    private Pair<Image, CaptureResult> captureSingleRawShot(Size s, ImageReader captureReader,
+            CameraTestUtils.SimpleImageReaderListener captureListener) throws Exception {
+        if (VERBOSE) {
+            Log.v(TAG, "captureSingleRawShot - Capturing raw image.");
+        }
+
+        Size maxYuvSz = mOrderedPreviewSizes.get(0);
+        Size[] targetCaptureSizes =
+                mStaticInfo.getAvailableSizesForFormatChecked(ImageFormat.RAW_SENSOR,
+                        StaticMetadata.StreamDirection.Output);
+
+        // Validate size
+        boolean validSize = false;
+        for (int i = 0; i < targetCaptureSizes.length; ++i) {
+            if (targetCaptureSizes[i].equals(s)) {
+                validSize = true;
+                break;
+            }
+        }
+        assertTrue("Capture size is supported.", validSize);
+        Surface captureSurface = captureReader.getSurface();
+
+        // Capture images.
+        List<Surface> outputSurfaces = new ArrayList<Surface>();
+        outputSurfaces.add(captureSurface);
+        CaptureRequest.Builder request = prepareCaptureRequestForSurfaces(outputSurfaces);
+        request.set(CaptureRequest.STATISTICS_LENS_SHADING_MAP_MODE,
+                CaptureRequest.STATISTICS_LENS_SHADING_MAP_MODE_ON);
+        CameraTestUtils.SimpleCaptureListener resultListener =
+                new CameraTestUtils.SimpleCaptureListener();
+
+        startCapture(request.build(), /*repeating*/false, resultListener, mHandler);
+
+        // Verify capture result and images
+        CaptureResult result = resultListener.getCaptureResult(CAPTURE_WAIT_TIMEOUT_MS);
+
+        Image captureImage = captureListener.getImage(CAPTURE_WAIT_TIMEOUT_MS);
+
+        CameraTestUtils.validateImage(captureImage, s.getWidth(), s.getHeight(),
+                ImageFormat.RAW_SENSOR, null);
+        // Stop capture, delete the streams.
+        stopCapture(/*fast*/false);
+
+        return new Pair<Image, CaptureResult>(captureImage, result);
+    }
+
+    private CaptureRequest.Builder prepareCaptureRequestForSurfaces(List<Surface> surfaces)
+            throws Exception {
+        createSession(surfaces);
+
+        CaptureRequest.Builder captureBuilder =
+                mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
+        assertNotNull("Fail to get captureRequest", captureBuilder);
+        for (Surface surface : surfaces) {
+            captureBuilder.addTarget(surface);
+        }
+
+        return captureBuilder;
+    }
+}
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/ExtendedCameraCharacteristicsTest.java b/tests/tests/hardware/src/android/hardware/camera2/cts/ExtendedCameraCharacteristicsTest.java
new file mode 100644
index 0000000..8d76f25
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/ExtendedCameraCharacteristicsTest.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.hardware.camera2.cts;
+
+import android.content.Context;
+import android.graphics.ImageFormat;
+import android.hardware.camera2.CameraCharacteristics;
+import android.hardware.camera2.CameraCharacteristics.Key;
+import android.hardware.camera2.CameraManager;
+import android.hardware.camera2.cts.helpers.CameraErrorCollector;
+import android.hardware.camera2.params.StreamConfigurationMap;
+import android.test.AndroidTestCase;
+import android.util.Log;
+import android.util.Size;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Objects;
+
+import static android.hardware.camera2.cts.helpers.AssertHelpers.*;
+
+/**
+ * Extended tests for static camera characteristics.
+ */
+public class ExtendedCameraCharacteristicsTest extends AndroidTestCase {
+    private static final String TAG = "ExChrsTest"; // must be short so next line doesn't throw
+    private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
+
+    private static final String PREFIX_ANDROID = "android";
+    private static final String PREFIX_VENDOR = "com";
+
+    private CameraManager mCameraManager;
+    private List<CameraCharacteristics> mCharacteristics;
+    private String[] mIds;
+    private CameraErrorCollector mCollector;
+
+    private static final Size VGA = new Size(640, 480);
+
+    /*
+     * HW Levels short hand
+     */
+    private static final int LEGACY = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY;
+    private static final int LIMITED = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED;
+    private static final int FULL = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL;
+
+    /*
+     * Capabilities short hand
+     */
+    private static final int NONE = -1;
+    private static final int BC =
+            CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE;
+    private static final int MANUAL_SENSOR =
+            CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR;
+    private static final int RAW =
+            CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_RAW;
+    private static final int ZSL =
+            CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_ZSL;
+
+    @Override
+    public void setContext(Context context) {
+        super.setContext(context);
+        mCameraManager = (CameraManager)context.getSystemService(Context.CAMERA_SERVICE);
+        assertNotNull("Can't connect to camera manager", mCameraManager);
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mIds = mCameraManager.getCameraIdList();
+        mCharacteristics = new ArrayList<>();
+        mCollector = new CameraErrorCollector();
+        for (int i = 0; i < mIds.length; i++) {
+            CameraCharacteristics props = mCameraManager.getCameraCharacteristics(mIds[i]);
+            assertNotNull(String.format("Can't get camera characteristics from: ID %s", mIds[i]),
+                    props);
+            mCharacteristics.add(props);
+        }
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        mCharacteristics = null;
+
+        try {
+            mCollector.verify();
+        } catch (Throwable e) {
+            // When new Exception(e) is used, exception info will be printed twice.
+            throw new Exception(e.getMessage());
+        } finally {
+            super.tearDown();
+        }
+    }
+
+    /**
+     * Test that the available stream configurations contain a few required formats and sizes.
+     */
+    public void testAvailableStreamConfigs() {
+        int counter = 0;
+        for (CameraCharacteristics c : mCharacteristics) {
+            StreamConfigurationMap config =
+                    c.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
+            assertNotNull(String.format("No stream configuration map found for: ID %s",
+                    mIds[counter]), config);
+            int[] outputFormats = config.getOutputFormats();
+
+            // Check required formats exist (JPEG, and YUV_420_888).
+            assertArrayContains(
+                    String.format("No valid YUV_420_888 preview formats found for: ID %s",
+                            mIds[counter]), outputFormats, ImageFormat.YUV_420_888);
+            assertArrayContains(String.format("No JPEG image format for: ID %s",
+                    mIds[counter]), outputFormats, ImageFormat.JPEG);
+
+            Size[] sizes = config.getOutputSizes(ImageFormat.YUV_420_888);
+            CameraTestUtils.assertArrayNotEmpty(sizes,
+                    String.format("No sizes for preview format %x for: ID %s",
+                            ImageFormat.YUV_420_888, mIds[counter]));
+
+            assertArrayContains(String.format(
+                            "Required VGA size not found for format %x for: ID %s",
+                            ImageFormat.YUV_420_888, mIds[counter]), sizes, VGA);
+
+            counter++;
+        }
+    }
+
+    /**
+     * Test {@link CameraCharacteristics#getKeys}
+     */
+    public void testKeys() {
+        int counter = 0;
+        for (CameraCharacteristics c : mCharacteristics) {
+            mCollector.setCameraId(mIds[counter]);
+
+            if (VERBOSE) {
+                Log.v(TAG, "testKeys - testing characteristics for camera " + mIds[counter]);
+            }
+
+            List<CameraCharacteristics.Key<?>> allKeys = c.getKeys();
+            assertNotNull("Camera characteristics keys must not be null", allKeys);
+            assertFalse("Camera characteristics keys must have at least 1 key",
+                    allKeys.isEmpty());
+
+            for (CameraCharacteristics.Key<?> key : allKeys) {
+                assertKeyPrefixValid(key.getName());
+
+                // All characteristics keys listed must never be null
+                mCollector.expectKeyValueNotNull(c, key);
+
+                // TODO: add a check that key must not be @hide
+            }
+
+            /*
+             * List of keys that must be present in camera characteristics (not null).
+             *
+             * Keys for LIMITED, FULL devices might be available despite lacking either
+             * the hardware level or the capability. This is *OK*. This only lists the
+             * *minimal* requirements for a key to be listed.
+             *
+             * LEGACY devices are a bit special since they map to api1 devices, so we know
+             * for a fact most keys are going to be illegal there so they should never be
+             * available.
+             *
+             * (TODO: Codegen this)
+             */
+            {
+                //                                           (Key Name)                                     (HW Level)  (Capabilities <Var-Arg>)
+                expectKeyAvailable(c, CameraCharacteristics.CONTROL_AE_AVAILABLE_ANTIBANDING_MODES          , LEGACY   ,   BC                   );
+                expectKeyAvailable(c, CameraCharacteristics.CONTROL_AE_AVAILABLE_MODES                      , LEGACY   ,   BC                   );
+                expectKeyAvailable(c, CameraCharacteristics.CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES          , LEGACY   ,   BC                   );
+                expectKeyAvailable(c, CameraCharacteristics.CONTROL_AE_COMPENSATION_RANGE                   , LEGACY   ,   BC                   );
+                expectKeyAvailable(c, CameraCharacteristics.CONTROL_AE_COMPENSATION_STEP                    , LEGACY   ,   BC                   );
+                expectKeyAvailable(c, CameraCharacteristics.CONTROL_AF_AVAILABLE_MODES                      , LEGACY   ,   BC                   );
+                expectKeyAvailable(c, CameraCharacteristics.CONTROL_AVAILABLE_EFFECTS                       , LEGACY   ,   BC                   );
+                expectKeyAvailable(c, CameraCharacteristics.CONTROL_AVAILABLE_SCENE_MODES                   , LEGACY   ,   BC                   );
+                expectKeyAvailable(c, CameraCharacteristics.CONTROL_AVAILABLE_VIDEO_STABILIZATION_MODES     , LEGACY   ,   BC                   );
+                expectKeyAvailable(c, CameraCharacteristics.CONTROL_AWB_AVAILABLE_MODES                     , LEGACY   ,   BC                   );
+                expectKeyAvailable(c, CameraCharacteristics.CONTROL_MAX_REGIONS_AE                          , LEGACY   ,   BC                   );
+                expectKeyAvailable(c, CameraCharacteristics.CONTROL_MAX_REGIONS_AF                          , LEGACY   ,   BC                   );
+                expectKeyAvailable(c, CameraCharacteristics.CONTROL_MAX_REGIONS_AWB                         , LEGACY   ,   BC                   );
+                expectKeyAvailable(c, CameraCharacteristics.EDGE_AVAILABLE_EDGE_MODES                       , FULL     ,   NONE                 );
+                expectKeyAvailable(c, CameraCharacteristics.FLASH_INFO_AVAILABLE                            , LEGACY   ,   BC                   );
+                expectKeyAvailable(c, CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL                   , LEGACY   ,   BC                   );
+                expectKeyAvailable(c, CameraCharacteristics.JPEG_AVAILABLE_THUMBNAIL_SIZES                  , LEGACY   ,   BC                   );
+                expectKeyAvailable(c, CameraCharacteristics.LENS_FACING                                     , LEGACY   ,   BC                   );
+                expectKeyAvailable(c, CameraCharacteristics.LENS_INFO_AVAILABLE_APERTURES                   , FULL     ,   MANUAL_SENSOR        );
+                expectKeyAvailable(c, CameraCharacteristics.LENS_INFO_AVAILABLE_FILTER_DENSITIES            , FULL     ,   MANUAL_SENSOR        );
+                expectKeyAvailable(c, CameraCharacteristics.LENS_INFO_AVAILABLE_FOCAL_LENGTHS               , LEGACY   ,   BC                   );
+                expectKeyAvailable(c, CameraCharacteristics.LENS_INFO_AVAILABLE_OPTICAL_STABILIZATION       , LIMITED  ,   MANUAL_SENSOR        );
+                expectKeyAvailable(c, CameraCharacteristics.LENS_INFO_FOCUS_DISTANCE_CALIBRATION            , LIMITED  ,   MANUAL_SENSOR        );
+                expectKeyAvailable(c, CameraCharacteristics.LENS_INFO_HYPERFOCAL_DISTANCE                   , LIMITED  ,   MANUAL_SENSOR        );
+                expectKeyAvailable(c, CameraCharacteristics.LENS_INFO_MINIMUM_FOCUS_DISTANCE                , LIMITED  ,   NONE                 );
+                expectKeyAvailable(c, CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES                  , LEGACY   ,   BC                   );
+                expectKeyAvailable(c, CameraCharacteristics.REQUEST_MAX_NUM_OUTPUT_PROC                     , LEGACY   ,   BC                   );
+                expectKeyAvailable(c, CameraCharacteristics.REQUEST_MAX_NUM_OUTPUT_PROC_STALLING            , LEGACY   ,   BC                   );
+                expectKeyAvailable(c, CameraCharacteristics.REQUEST_MAX_NUM_OUTPUT_RAW                      , LEGACY   ,   BC                   );
+                expectKeyAvailable(c, CameraCharacteristics.REQUEST_PARTIAL_RESULT_COUNT                    , LEGACY   ,   BC                   );
+                expectKeyAvailable(c, CameraCharacteristics.REQUEST_PIPELINE_MAX_DEPTH                      , LEGACY   ,   BC                   );
+                expectKeyAvailable(c, CameraCharacteristics.SCALER_AVAILABLE_MAX_DIGITAL_ZOOM               , LEGACY   ,   BC                   );
+                expectKeyAvailable(c, CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP                 , LEGACY   ,   BC                   );
+                expectKeyAvailable(c, CameraCharacteristics.SCALER_CROPPING_TYPE                            , LEGACY   ,   BC                   );
+                expectKeyAvailable(c, CameraCharacteristics.SENSOR_AVAILABLE_TEST_PATTERN_MODES             , LEGACY   ,   NONE                 );
+                expectKeyAvailable(c, CameraCharacteristics.SENSOR_BLACK_LEVEL_PATTERN                      , FULL     ,   MANUAL_SENSOR        );
+                expectKeyAvailable(c, CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE                   , LEGACY   ,   BC , RAW             );
+                expectKeyAvailable(c, CameraCharacteristics.SENSOR_INFO_COLOR_FILTER_ARRANGEMENT            , FULL     ,   RAW                  );
+                expectKeyAvailable(c, CameraCharacteristics.SENSOR_INFO_EXPOSURE_TIME_RANGE                 , FULL     ,   MANUAL_SENSOR        );
+                expectKeyAvailable(c, CameraCharacteristics.SENSOR_INFO_MAX_FRAME_DURATION                  , FULL     ,   MANUAL_SENSOR        );
+                expectKeyAvailable(c, CameraCharacteristics.SENSOR_INFO_PHYSICAL_SIZE                       , LEGACY   ,   BC                   );
+                expectKeyAvailable(c, CameraCharacteristics.SENSOR_INFO_PIXEL_ARRAY_SIZE                    , LEGACY   ,   BC                   );
+                expectKeyAvailable(c, CameraCharacteristics.SENSOR_INFO_SENSITIVITY_RANGE                   , FULL     ,   MANUAL_SENSOR        );
+                expectKeyAvailable(c, CameraCharacteristics.SENSOR_MAX_ANALOG_SENSITIVITY                   , FULL     ,   MANUAL_SENSOR        );
+                expectKeyAvailable(c, CameraCharacteristics.SENSOR_ORIENTATION                              , LEGACY   ,   BC                   );
+                expectKeyAvailable(c, CameraCharacteristics.STATISTICS_INFO_AVAILABLE_FACE_DETECT_MODES     , LEGACY   ,   BC                   );
+                expectKeyAvailable(c, CameraCharacteristics.STATISTICS_INFO_MAX_FACE_COUNT                  , LEGACY   ,   BC                   );
+                expectKeyAvailable(c, CameraCharacteristics.SYNC_MAX_LATENCY                                , LEGACY   ,   BC                   );
+                expectKeyAvailable(c, CameraCharacteristics.TONEMAP_AVAILABLE_TONE_MAP_MODES                , FULL     ,   MANUAL_SENSOR        );
+                expectKeyAvailable(c, CameraCharacteristics.TONEMAP_MAX_CURVE_POINTS                        , FULL     ,   MANUAL_SENSOR        );
+
+                // Future: Use column editors for modifying above, ignore line length to keep 1 key per line
+
+                // TODO: check that no other 'android' keys are listed in #getKeys if they aren't in the above list
+            }
+
+            counter++;
+        }
+    }
+
+    /**
+     * Check key is present in characteristics if the hardware level is at least {@code hwLevel};
+     * check that the key is present if the actual capabilities are one of {@code capabilities};
+     * lastly check that {@code LEGACY} devices don't list any addition keys that they shouldn't
+     * be.
+     *
+     * @return value of the {@code key} from {@code c}
+     */
+    private <T> T expectKeyAvailable(CameraCharacteristics c, CameraCharacteristics.Key<T> key,
+            int hwLevel, int... capabilities) {
+
+        Integer actualHwLevel = c.get(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL);
+        assertNotNull("android.info.supportedHardwareLevel must never be null", actualHwLevel);
+
+        int[] actualCapabilities = c.get(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES);
+        assertNotNull("android.request.availableCapabilities must never be null");
+
+        List<Key<?>> allKeys = c.getKeys();
+
+        T value = c.get(key);
+
+        if (compareHardwareLevel(actualHwLevel, hwLevel) >= 0) {
+            mCollector.expectTrue(
+                    String.format("Key (%s) must be in characteristics for this hardware level " +
+                            "(required minimal HW level %s, actual HW level %s)",
+                            key.getName(), toStringHardwareLevel(hwLevel),
+                            toStringHardwareLevel(actualHwLevel)),
+                    value != null);
+            mCollector.expectTrue(
+                    String.format("Key (%s) must be in characteristics list of keys for this " +
+                            "hardware level (required minimal HW level %s, actual HW level %s)",
+                            key.getName(), toStringHardwareLevel(hwLevel),
+                            toStringHardwareLevel(actualHwLevel)),
+                    allKeys.contains(key));
+        } else if (arrayContainsAnyOf(actualCapabilities, capabilities)) {
+            mCollector.expectTrue(
+                    String.format("Key (%s) must be in characteristics for these capabilities " +
+                            "(required capabilities %s, actual capabilities %s)",
+                            key.getName(), Arrays.toString(capabilities),
+                            Arrays.toString(actualCapabilities)),
+                    value != null);
+            mCollector.expectTrue(
+                    String.format("Key (%s) must be in characteristics list of keys for " +
+                            "these capabilities (required capabilities %s, actual capabilities %s)",
+                            key.getName(), Arrays.toString(capabilities),
+                            Arrays.toString(actualCapabilities)),
+                    allKeys.contains(key));
+        } else {
+            if (actualHwLevel == LEGACY) {
+                mCollector.expectTrue(
+                        String.format("Key (%s) must not be in characteristics for LEGACY devices",
+                                key.getName()),
+                        value == null);
+
+                mCollector.expectTrue(
+                        String.format("Key (%s) must not be in characteristics list of keys" +
+                                "for LEGACY devices",
+                                key.getName()),
+                        !allKeys.contains(key));
+
+                // TODO: a few keys like aeLock, awbLock are optional in api1. Revisit this.
+            }
+
+            // OK: Key may or may not be present.
+        }
+
+        return value;
+    }
+
+    private static boolean arrayContains(int[] arr, int needle) {
+        if (arr == null) {
+            return false;
+        }
+
+        for (int elem : arr) {
+            if (elem == needle) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    private static boolean arrayContainsAnyOf(int[] arr, int[] needles) {
+        for (int needle : needles) {
+            if (arrayContains(arr, needle)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * The key name has a prefix of either "android." or "com."; other prefixes are not valid.
+     */
+    private static void assertKeyPrefixValid(String keyName) {
+        assertStartsWithAnyOf(
+                "All metadata keys must start with 'android.' (built-in keys) " +
+                "or 'com.' (vendor-extended keys)", new String[] {
+                        PREFIX_ANDROID + ".",
+                        PREFIX_VENDOR + ".",
+                }, keyName);
+    }
+
+    private static void assertTrueForKey(String msg, CameraCharacteristics.Key<?> key,
+            boolean actual) {
+        assertTrue(msg + " (key = '" + key.getName() + "')", actual);
+    }
+
+    private static <T> void assertOneOf(String msg, T[] expected, T actual) {
+        for (int i = 0; i < expected.length; ++i) {
+            if (Objects.equals(expected[i], actual)) {
+                return;
+            }
+        }
+
+        fail(String.format("%s: (expected one of %s, actual %s)",
+                msg, Arrays.toString(expected), actual));
+    }
+
+    private static <T> void assertStartsWithAnyOf(String msg, String[] expected, String actual) {
+        for (int i = 0; i < expected.length; ++i) {
+            if (actual.startsWith(expected[i])) {
+                return;
+            }
+        }
+
+        fail(String.format("%s: (expected to start with any of %s, but value was %s)",
+                msg, Arrays.toString(expected), actual));
+    }
+
+    /** Return a positive int if left > right, 0 if left==right, negative int if left < right */
+    private static int compareHardwareLevel(int left, int right) {
+        return remapHardwareLevel(left) - remapHardwareLevel(right);
+    }
+
+    /** Remap HW levels worst<->best, 0 = worst, 2 = best */
+    private static int remapHardwareLevel(int level) {
+        switch (level) {
+            case LEGACY:
+                return 0; // lowest
+            case LIMITED:
+                return 1; // second lowest
+            case FULL:
+                return 2; // best
+        }
+
+        fail("Unknown HW level: " + level);
+        return -1;
+    }
+
+    private static String toStringHardwareLevel(int level) {
+        switch (level) {
+            case LEGACY:
+                return "LEGACY";
+            case LIMITED:
+                return "LIMITED";
+            case FULL:
+                return "FULL";
+        }
+
+        // unknown
+        Log.w(TAG, "Unknown hardware level " + level);
+        return Integer.toString(level);
+    }
+}
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..a44b255 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/ImageReaderTest.java
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/ImageReaderTest.java
@@ -17,30 +17,23 @@
 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.graphics.BitmapFactory;
 import android.graphics.ImageFormat;
-import android.hardware.camera2.CameraAccessException;
 import android.hardware.camera2.CameraDevice;
-import android.hardware.camera2.CameraManager;
 import android.hardware.camera2.CameraCharacteristics;
 import android.hardware.camera2.CaptureRequest;
-import android.hardware.camera2.Size;
+import android.hardware.camera2.CaptureResult;
+import android.util.Size;
+import android.hardware.camera2.cts.helpers.StaticMetadata;
+import android.hardware.camera2.cts.testcases.Camera2AndroidTestCase;
 import android.media.Image;
 import android.media.ImageReader;
-import android.os.Environment;
-import android.os.Handler;
-import android.test.AndroidTestCase;
+import android.os.ConditionVariable;
 import android.util.Log;
 import android.view.Surface;
 
-import com.android.ex.camera2.blocking.BlockingCameraManager.BlockingOpenException;
-import com.android.ex.camera2.blocking.BlockingStateListener;
-
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.List;
 
 /**
@@ -53,305 +46,368 @@
  * <p>Some invalid access test. </p>
  * <p>TODO: Add more format tests? </p>
  */
-public class ImageReaderTest extends AndroidTestCase {
+public class ImageReaderTest extends Camera2AndroidTestCase {
     private static final String TAG = "ImageReaderTest";
     private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
-    private static final boolean DUMP_FILE = false;
-    private static final String DEBUG_FILE_NAME_BASE =
-            Environment.getExternalStorageDirectory().getPath();
     // number of frame (for streaming requests) to be verified.
-    // TODO: Need extend it to bigger number
-    private static final int NUM_FRAME_VERIFIED = 1;
+    private static final int NUM_FRAME_VERIFIED = 2;
     // Max number of images can be accessed simultaneously from ImageReader.
     private static final int MAX_NUM_IMAGES = 5;
 
-    private CameraManager mCameraManager;
-    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 SimpleImageListener mListener;
 
     @Override
     public void setContext(Context context) {
         super.setContext(context);
-        mCameraManager = (CameraManager) context.getSystemService(Context.CAMERA_SERVICE);
-        assertNotNull("Can't connect to camera manager!", mCameraManager);
     }
 
     @Override
     protected void setUp() throws Exception {
         super.setUp();
-        mCameraIds = mCameraManager.getCameraIdList();
-        mLooperThread = new CameraTestThread();
-        mHandler = mLooperThread.start();
-        mCameraListener = new BlockingStateListener();
     }
 
     @Override
     protected void tearDown() throws Exception {
-        if (mCamera != null) {
-            mCamera.close();
-            mCamera = null;
-        }
-        if (mReader != null) {
-            mReader.close();
-            mReader = null;
-        }
-        mLooperThread.close();
-        mHandler = null;
         super.tearDown();
     }
 
-    public void testImageReaderFromCameraFlexibleYuv() throws Exception {
-        for (int i = 0; i < mCameraIds.length; i++) {
-            Log.i(TAG, "Testing Camera " + mCameraIds[i]);
-            openDevice(mCameraIds[i]);
-            bufferFormatTestByCamera(ImageFormat.YUV_420_888, mCameraIds[i]);
-            closeDevice(mCameraIds[i]);
+    public void testFlexibleYuv() throws Exception {
+        for (String id : mCameraIds) {
+            try {
+                Log.i(TAG, "Testing Camera " + id);
+                openDevice(id);
+                bufferFormatTestByCamera(ImageFormat.YUV_420_888, /*repeating*/true);
+            } finally {
+                closeDevice(id);
+            }
         }
     }
 
-    public void testImageReaderFromCameraJpeg() throws Exception {
-        for (int i = 0; i < mCameraIds.length; i++) {
-            Log.v(TAG, "Testing Camera " + mCameraIds[i]);
-            openDevice(mCameraIds[i]);
-            bufferFormatTestByCamera(ImageFormat.JPEG, mCameraIds[i]);
-            closeDevice(mCameraIds[i]);
+    public void testJpeg() throws Exception {
+        for (String id : mCameraIds) {
+            try {
+                Log.v(TAG, "Testing jpeg capture for Camera " + id);
+                openDevice(id);
+                bufferFormatTestByCamera(ImageFormat.JPEG, /*repeating*/false);
+            } finally {
+                closeDevice(id);
+            }
         }
     }
 
-    public void testImageReaderFromCameraRaw() {
-        // TODO: can test this once raw is supported
+    public void testRaw() throws Exception {
+        for (String id : mCameraIds) {
+            try {
+                Log.v(TAG, "Testing raw capture for camera " + id);
+                openDevice(id);
+
+                bufferFormatTestByCamera(ImageFormat.RAW_SENSOR, /*repeating*/false);
+            } finally {
+                closeDevice(id);
+            }
+        }
     }
 
-    public void testImageReaderInvalidAccessTest() {
+    public void testRepeatingJpeg() throws Exception {
+        for (String id : mCameraIds) {
+            try {
+                Log.v(TAG, "Testing repeating jpeg capture for Camera " + id);
+                openDevice(id);
+                bufferFormatTestByCamera(ImageFormat.JPEG, /*repeating*/true);
+            } finally {
+                closeDevice(id);
+            }
+        }
+    }
+
+    public void testRepeatingRaw() throws Exception {
+        for (String id : mCameraIds) {
+            try {
+                Log.v(TAG, "Testing repeating raw capture for camera " + id);
+                openDevice(id);
+
+                bufferFormatTestByCamera(ImageFormat.RAW_SENSOR, /*repeating*/true);
+            } finally {
+                closeDevice(id);
+            }
+        }
+    }
+
+    public void testInvalidAccessTest() {
         // TODO: test invalid access case, see if we can receive expected
         // exceptions
     }
 
-    private void bufferFormatTestByCamera(int format, String cameraId) throws Exception {
-        CameraCharacteristics properties = mCameraManager.getCameraCharacteristics(cameraId);
-        assertNotNull("Can't get camera properties!", properties);
+    /**
+     * Test two image stream (YUV420_888 and JPEG) capture by using ImageReader.
+     *
+     * <p>Both stream formats are mandatory for Camera2 API</p>
+     */
+    public void testYuvAndJpeg() throws Exception {
+        for (String id : mCameraIds) {
+            try {
+                Log.v(TAG, "YUV and JPEG testing for camera " + id);
+                openDevice(id);
 
-        /**
-         * 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");
-        Arrays.sort(availableFormats);
-        assertTrue("Can't find the format " + format + " in supported formats " +
-                Arrays.toString(availableFormats),
-                Arrays.binarySearch(availableFormats, format) >= 0);
+                bufferFormatWithYuvTestByCamera(ImageFormat.JPEG);
+            } finally {
+                closeDevice(id);
+            }
+        }
+    }
 
-        Size[] availableSizes = getSupportedSizeForFormat(format, mCamera.getId(), mCameraManager);
-        assertArrayNotEmpty(availableSizes, "availableSizes should not be empty");
+    /**
+     * Test two image stream (YUV420_888 and RAW_SENSOR) capture by using ImageReader.
+     *
+     */
+    public void testImageReaderYuvAndRaw() throws Exception {
+        for (String id : mCameraIds) {
+            try {
+                Log.v(TAG, "YUV and RAW testing for camera " + id);
+                openDevice(id);
+
+                bufferFormatWithYuvTestByCamera(ImageFormat.RAW_SENSOR);
+            } finally {
+                closeDevice(id);
+            }
+        }
+    }
+
+
+    /**
+     * Test capture a given format stream with yuv stream simultaneously.
+     *
+     * <p>Use fixed yuv size, varies targeted format capture size. Single capture is tested.</p>
+     *
+     * @param format The capture format to be tested along with yuv format.
+     */
+    private void bufferFormatWithYuvTestByCamera(int format) throws Exception {
+        if (format != ImageFormat.JPEG && format != ImageFormat.RAW_SENSOR
+                && format != ImageFormat.YUV_420_888) {
+            throw new IllegalArgumentException("Unsupported format: " + format);
+        }
+
+        final int NUM_SINGLE_CAPTURE_TESTED = MAX_NUM_IMAGES - 1;
+        Size maxYuvSz = mOrderedPreviewSizes.get(0);
+        Size[] targetCaptureSizes = mStaticInfo.getAvailableSizesForFormatChecked(format,
+                StaticMetadata.StreamDirection.Output);
+
+        for (Size captureSz : targetCaptureSizes) {
+            if (VERBOSE) {
+                Log.v(TAG, "Testing yuv size " + maxYuvSz.toString() + " and capture size "
+                        + captureSz.toString() + " for camera " + mCamera.getId());
+            }
+
+            ImageReader captureReader = null;
+            ImageReader yuvReader = null;
+            try {
+                // Create YUV image reader
+                SimpleImageReaderListener yuvListener  = new SimpleImageReaderListener();
+                yuvReader = createImageReader(maxYuvSz, ImageFormat.YUV_420_888, MAX_NUM_IMAGES,
+                        yuvListener);
+                Surface yuvSurface = yuvReader.getSurface();
+
+                // Create capture image reader
+                SimpleImageReaderListener captureListener = new SimpleImageReaderListener();
+                captureReader = createImageReader(captureSz, format, MAX_NUM_IMAGES,
+                        captureListener);
+                Surface captureSurface = captureReader.getSurface();
+
+                // Capture images.
+                List<Surface> outputSurfaces = new ArrayList<Surface>();
+                outputSurfaces.add(yuvSurface);
+                outputSurfaces.add(captureSurface);
+                CaptureRequest.Builder request = prepareCaptureRequestForSurfaces(outputSurfaces);
+                SimpleCaptureListener resultListener = new SimpleCaptureListener();
+
+                for (int i = 0; i < NUM_SINGLE_CAPTURE_TESTED; i++) {
+                    startCapture(request.build(), /*repeating*/false, resultListener, mHandler);
+                }
+
+                // Verify capture result and images
+                for (int i = 0; i < NUM_SINGLE_CAPTURE_TESTED; i++) {
+                    resultListener.getCaptureResult(CAPTURE_WAIT_TIMEOUT_MS);
+                    if (VERBOSE) {
+                        Log.v(TAG, " Got the capture result back for " + i + "th capture");
+                    }
+
+                    Image yuvImage = yuvListener.getImage(CAPTURE_WAIT_TIMEOUT_MS);
+                    if (VERBOSE) {
+                        Log.v(TAG, " Got the yuv image back for " + i + "th capture");
+                    }
+
+                    Image captureImage = captureListener.getImage(CAPTURE_WAIT_TIMEOUT_MS);
+                    if (VERBOSE) {
+                        Log.v(TAG, " Got the capture image back for " + i + "th capture");
+                    }
+
+                    //Validate captured images.
+                    CameraTestUtils.validateImage(yuvImage, maxYuvSz.getWidth(),
+                            maxYuvSz.getHeight(), ImageFormat.YUV_420_888, /*filePath*/null);
+                    CameraTestUtils.validateImage(captureImage, captureSz.getWidth(),
+                            captureSz.getHeight(), format, /*filePath*/null);
+                }
+
+                // Stop capture, delete the streams.
+                stopCapture(/*fast*/false);
+            } finally {
+                closeImageReader(captureReader);
+                captureReader = null;
+                closeImageReader(yuvReader);
+                yuvReader = null;
+            }
+        }
+    }
+
+    private void bufferFormatTestByCamera(int format, boolean repeating) throws Exception {
+
+        Size[] availableSizes = mStaticInfo.getAvailableSizesForFormatChecked(format,
+                StaticMetadata.StreamDirection.Output);
 
         // for each resolution, test imageReader:
         for (Size sz : availableSizes) {
-            if (VERBOSE) Log.v(TAG, "Testing size " + sz.toString() + " for camera " + cameraId);
+            try {
+                if (VERBOSE) {
+                    Log.v(TAG, "Testing size " + sz.toString() + " format " + format
+                            + " for camera " + mCamera.getId());
+                }
 
-            prepareImageReader(sz, format);
+                // Create ImageReader.
+                mListener  = new SimpleImageListener();
+                createDefaultImageReader(sz, format, MAX_NUM_IMAGES, mListener);
 
-            CaptureRequest request = prepareCaptureRequest(format);
+                // Start capture.
+                CaptureRequest request = prepareCaptureRequest();
+                SimpleCaptureListener listener = new SimpleCaptureListener();
+                startCapture(request, repeating, listener, mHandler);
 
-            captureAndValidateImage(request, sz, format);
+                int numFrameVerified = repeating ? NUM_FRAME_VERIFIED : 1;
 
-            stopCapture();
+                // Validate images.
+                validateImage(sz, format, numFrameVerified);
+
+                // Validate capture result.
+                validateCaptureResult(format, sz, listener, numFrameVerified);
+
+                // stop capture.
+                stopCapture(/*fast*/false);
+            } finally {
+                closeDefaultImageReader();
+            }
+
         }
     }
 
-    private class SimpleImageListener implements ImageReader.OnImageAvailableListener {
-        private int mPendingImages = 0;
-        private final Object mImageSyncObject = new Object();
+    /**
+     * Validate capture results.
+     *
+     * @param format The format of this capture.
+     * @param size The capture size.
+     * @param listener The capture listener to get capture result callbacks.
+     */
+    private void validateCaptureResult(int format, Size size, SimpleCaptureListener listener,
+            int numFrameVerified) {
+        for (int i = 0; i < numFrameVerified; i++) {
+            CaptureResult result = listener.getCaptureResult(CAPTURE_RESULT_TIMEOUT_MS);
 
+            // TODO: Update this to use availableResultKeys once shim supports this.
+            if (mStaticInfo.isCapabilitySupported(
+                    CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR)) {
+                Long exposureTime = getValueNotNull(result, CaptureResult.SENSOR_EXPOSURE_TIME);
+                Integer sensitivity = getValueNotNull(result, CaptureResult.SENSOR_SENSITIVITY);
+                mCollector.expectInRange(
+                        String.format(
+                                "Capture for format %d, size %s exposure time is invalid.",
+                                format, size.toString()),
+                        exposureTime,
+                        mStaticInfo.getExposureMinimumOrDefault(),
+                        mStaticInfo.getExposureMaximumOrDefault()
+                );
+                mCollector.expectInRange(
+                        String.format("Capture for format %d, size %s sensitivity is invalid.",
+                                format, size.toString()),
+                        sensitivity,
+                        mStaticInfo.getSensitivityMinimumOrDefault(),
+                        mStaticInfo.getSensitivityMaximumOrDefault()
+                );
+            }
+            // TODO: add more key validations.
+        }
+    }
+
+    private final class SimpleImageListener implements ImageReader.OnImageAvailableListener {
+        private final ConditionVariable imageAvailable = new ConditionVariable();
         @Override
         public void onImageAvailable(ImageReader reader) {
+            if (mReader != reader) {
+                return;
+            }
+
             if (VERBOSE) Log.v(TAG, "new image available");
-            synchronized (mImageSyncObject) {
-                mPendingImages++;
-                mImageSyncObject.notifyAll();
+            imageAvailable.open();
+        }
+
+        public void waitForAnyImageAvailable(long timeout) {
+            if (imageAvailable.block(timeout)) {
+                imageAvailable.close();
+            } else {
+                fail("wait for image available timed out after " + timeout + "ms");
             }
         }
 
-        public boolean isImagePending() {
-            synchronized (mImageSyncObject) {
-                return (mPendingImages > 0);
-            }
-        }
-
-        public void waitForImage() {
-            final int TIMEOUT_MS = 5000;
-            synchronized (mImageSyncObject) {
-                while (mPendingImages == 0) {
-                    try {
-                        if (VERBOSE)
-                            Log.d(TAG, "waiting for next image");
-                        mImageSyncObject.wait(TIMEOUT_MS);
-                        if (mPendingImages == 0) {
-                            fail("wait for next image timed out");
-                        }
-                    } catch (InterruptedException ie) {
-                        throw new RuntimeException(ie);
-                    }
-                }
-                mPendingImages--;
+        public void closePendingImages() {
+            Image image = mReader.acquireLatestImage();
+            if (image != null) {
+                image.close();
             }
         }
     }
 
-    private void prepareImageReader(Size sz, int format) throws Exception {
-        int width = sz.getWidth();
-        int height = sz.getHeight();
-        mReader = ImageReader.newInstance(width, height, format, MAX_NUM_IMAGES);
-        mListener  = new SimpleImageListener();
-        mReader.setOnImageAvailableListener(mListener, mHandler);
-        if (VERBOSE) Log.v(TAG, "Preparing ImageReader size " + sz.toString());
-    }
-
-    private CaptureRequest prepareCaptureRequest(int format) throws Exception {
-        List<Surface> outputSurfaces = new ArrayList<Surface>(1);
+    private CaptureRequest prepareCaptureRequest() throws Exception {
+        List<Surface> outputSurfaces = new ArrayList<Surface>();
         Surface surface = mReader.getSurface();
         assertNotNull("Fail to get surface from ImageReader", surface);
         outputSurfaces.add(surface);
-        mCamera.configureOutputs(outputSurfaces);
-        mCameraListener.waitForState(STATE_BUSY, CAMERA_BUSY_TIMEOUT_MS);
-        mCameraListener.waitForState(STATE_IDLE, CAMERA_IDLE_TIMEOUT_MS);
+        return prepareCaptureRequestForSurfaces(outputSurfaces).build();
+    }
+
+    private CaptureRequest.Builder prepareCaptureRequestForSurfaces(List<Surface> surfaces)
+            throws Exception {
+        createSession(surfaces);
 
         CaptureRequest.Builder captureBuilder =
                 mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
         assertNotNull("Fail to get captureRequest", captureBuilder);
-        captureBuilder.addTarget(mReader.getSurface());
+        for (Surface surface : surfaces) {
+            captureBuilder.addTarget(surface);
+        }
 
-        return captureBuilder.build();
+        return captureBuilder;
     }
 
-    private void captureAndValidateImage(CaptureRequest request,
-            Size sz, int format) throws Exception {
+    private void validateImage(Size sz, int format, int captureCount) throws Exception {
         // TODO: Add more format here, and wrap each one as a function.
         Image img;
-        int captureCount = NUM_FRAME_VERIFIED;
-
-        // Only verify single image for still capture
-        if (format == ImageFormat.JPEG) {
-            captureCount = 1;
-            mCamera.capture(request, null, null);
-        } else {
-            mCamera.setRepeatingRequest(request, null, null);
-        }
 
         for (int i = 0; i < captureCount; i++) {
             assertNotNull("Image listener is null", mListener);
             if (VERBOSE) Log.v(TAG, "Waiting for an Image");
-            mListener.waitForImage();
-            img = mReader.acquireNextImage();
-            if (VERBOSE) Log.v(TAG, "Got next image");
-            validateImage(img, sz.getWidth(), sz.getHeight(), format);
+            mListener.waitForAnyImageAvailable(CAPTURE_WAIT_TIMEOUT_MS);
+            /**
+             * Acquire the latest image in case the validation is slower than
+             * the image producing rate.
+             */
+            img = mReader.acquireLatestImage();
+            assertNotNull("Unable to acquire the latest image", img);
+            if (VERBOSE) Log.v(TAG, "Got the latest image");
+            CameraTestUtils.validateImage(img, sz.getWidth(), sz.getHeight(), format,
+                    DEBUG_FILE_NAME_BASE);
+            if (VERBOSE) Log.v(TAG, "finish vaildation of image " + i);
             img.close();
-            // Return the pending images to producer in case the validation is slower
-            // than the image producing rate. Otherwise, it could cause the producer
-            // starvation.
-            while (mListener.isImagePending()) {
-                mListener.waitForImage();
-                img = mReader.acquireNextImage();
-                img.close();
-            }
         }
-    }
 
-    private void stopCapture() throws CameraAccessException {
-        if (VERBOSE) Log.v(TAG, "Stopping capture 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);
-        // Camera has disconnected, clear out the reader
-        mReader.close();
-        mReader = null;
-        mListener = null;
-    }
-
-    private void openDevice(String cameraId) {
-        if (mCamera != null) {
-            throw new IllegalStateException("Already have open camera device");
-        }
-        try {
-            mCamera = CameraTestUtils.openCamera(
-                mCameraManager, cameraId, mCameraListener, mHandler);
-        } catch (CameraAccessException e) {
-            mCamera = null;
-            fail("Fail to open camera, " + Log.getStackTraceString(e));
-        } catch (BlockingOpenException e) {
-            mCamera = null;
-            fail("Fail to open camera, " + Log.getStackTraceString(e));
-        }
-        mCameraListener.waitForState(STATE_UNCONFIGURED, CAMERA_OPEN_TIMEOUT_MS);
-    }
-
-    private void closeDevice(String cameraId) {
-        mCamera.close();
-        mCamera = null;
-    }
-
-    private void validateImage(Image image, int width, int height, int format) {
-        checkImage(image, width, height, format);
-
-        /**
-         * TODO: validate timestamp:
-         * 1. capture result timestamp against the image timestamp (need
-         * consider frame drops)
-         * 2. timestamps should be monotonically increasing for different requests
-         */
-        if(VERBOSE) Log.v(TAG, "validating Image");
-        byte[] data = getDataFromImage(image);
-        assertTrue("Invalid image data", data != null && data.length > 0);
-
-        if (format == ImageFormat.JPEG) {
-            validateJpegData(data, width, height);
-        } else {
-            validateYuvData(data, width, height, format, image.getTimestamp());
-        }
-    }
-
-    private void validateJpegData(byte[] jpegData, int width, int height) {
-        BitmapFactory.Options bmpOptions = new BitmapFactory.Options();
-        // DecodeBound mode: only parse the frame header to get width/height.
-        // it doesn't decode the pixel.
-        bmpOptions.inJustDecodeBounds = true;
-        BitmapFactory.decodeByteArray(jpegData, 0, jpegData.length, bmpOptions);
-        assertEquals(width, bmpOptions.outWidth);
-        assertEquals(height, bmpOptions.outHeight);
-
-        // Pixel decoding mode: decode whole image. check if the image data
-        // is decodable here.
-        assertNotNull("Decoding jpeg failed",
-                BitmapFactory.decodeByteArray(jpegData, 0, jpegData.length));
-        if (DUMP_FILE) {
-            String fileName =
-                    DEBUG_FILE_NAME_BASE + width + "x" + height + ".yuv";
-            dumpFile(fileName, jpegData);
-        }
-    }
-
-    private void validateYuvData(byte[] yuvData, int width, int height, int format, long ts) {
-        checkYuvFormat(format);
-        if (VERBOSE) Log.v(TAG, "Validating YUV data");
-        int expectedSize = width * height * ImageFormat.getBitsPerPixel(format) / 8;
-        assertEquals("Yuv data doesn't match", expectedSize, yuvData.length);
-
-        // TODO: Can add data validation if we have test pattern(tracked by b/9625427)
-
-        if (DUMP_FILE) {
-            String fileName =
-                    DEBUG_FILE_NAME_BASE + "/" + width + "x" + height + "_" + ts / 1e6 + ".yuv";
-            dumpFile(fileName, yuvData);
-        }
+        // Return all pending images to the ImageReader as the validateImage may
+        // take a while to return and there could be many images pending.
+        mListener.closePendingImages();
     }
 }
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/MultiViewTest.java b/tests/tests/hardware/src/android/hardware/camera2/cts/MultiViewTest.java
new file mode 100644
index 0000000..fdf4774
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/MultiViewTest.java
@@ -0,0 +1,151 @@
+/*
+ * 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 android.graphics.ImageFormat;
+import android.graphics.SurfaceTexture;
+import android.hardware.camera2.cts.CameraTestUtils.ImageVerifierListener;
+import android.hardware.camera2.cts.testcases.Camera2MultiViewTestCase;
+import android.hardware.camera2.cts.testcases.Camera2MultiViewTestCase.CameraPreviewListener;
+import android.media.ImageReader;
+import android.os.SystemClock;
+import android.util.Size;
+import android.view.Surface;
+import android.view.TextureView;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * CameraDevice test by using combination of SurfaceView, TextureView and ImageReader
+ */
+public class MultiViewTest extends Camera2MultiViewTestCase {
+
+    private final static long WAIT_FOR_COMMAND_TO_COMPLETE = 2000;
+    private final static long PREVIEW_TIME_MS = 2000;
+
+    private enum NumberOfPreview {
+        ONE(1), TWO(2);
+
+        private final int mValue;
+        private NumberOfPreview(int value) { mValue = value; }
+        public int getValue() { return mValue; }
+    }
+
+    public void testTextureViewPreview() throws Exception {
+        for (String cameraId : mCameraIds) {
+            openCamera(cameraId);
+            textureViewPreview(NumberOfPreview.ONE, /*testImagerReader*/false);
+            closeCamera();
+        }
+    }
+
+    public void testTextureViewPreviewWithImageReader() throws Exception {
+        for (String cameraId : mCameraIds) {
+            try {
+                openCamera(cameraId);
+                int maxNumStreamsProc = mStaticInfo.getMaxNumOutputStreamsProcessedChecked();
+                if (maxNumStreamsProc < 2) {
+                    continue;
+                }
+                textureViewPreview(NumberOfPreview.ONE, /*testImagerReader*/true);
+            } finally {
+                closeCamera();
+            }
+        }
+    }
+
+    public void testDualTextureViewPreview() throws Exception {
+        for (String cameraId : mCameraIds) {
+            try {
+                openCamera(cameraId);
+                int maxNumStreamsProc = mStaticInfo.getMaxNumOutputStreamsProcessedChecked();
+                if (maxNumStreamsProc < 2) {
+                    continue;
+                }
+                textureViewPreview(NumberOfPreview.TWO, /*testImagerReader*/false);
+            } finally {
+                closeCamera();
+            }
+        }
+    }
+
+    public void testDualTextureViewAndImageReaderPreview() throws Exception {
+        for (String cameraId : mCameraIds) {
+            try {
+                openCamera(cameraId);
+                int maxNumStreamsProc = mStaticInfo.getMaxNumOutputStreamsProcessedChecked();
+                if (maxNumStreamsProc < 3) {
+                    continue;
+                }
+                textureViewPreview(NumberOfPreview.TWO, /*testImagerReader*/true);
+            } finally {
+                closeCamera();
+            }
+        }
+    }
+
+    /**
+     * Test camera Preview using one texture view
+     */
+    private void textureViewPreview(NumberOfPreview n, boolean testImagerReader)
+            throws Exception {
+        int numPreview = n.getValue();
+        Size previewSize = mOrderedPreviewSizes.get(0);
+        CameraPreviewListener[] previewListener =
+                new CameraPreviewListener[numPreview];
+        SurfaceTexture[] previewTexture = new SurfaceTexture[numPreview];
+        List<Surface> surfaces = new ArrayList<Surface>();
+
+        // Prepare preview surface.
+        for (int i = 0; i < numPreview; i++) {
+            TextureView view = mTextureView[i];
+            previewListener[i] = new CameraPreviewListener();
+            view.setSurfaceTextureListener(previewListener[i]);
+            previewTexture[i] = getAvailableSurfaceTexture(WAIT_FOR_COMMAND_TO_COMPLETE, view);
+            assertNotNull("Unable to get preview surface texture", previewTexture[i]);
+            previewTexture[i].setDefaultBufferSize(previewSize.getWidth(), previewSize.getHeight());
+            // Correct the preview display rotation.
+            updatePreviewDisplayRotation(previewSize, view);
+            surfaces.add(new Surface(previewTexture[i]));
+        }
+
+        if (testImagerReader) {
+            ImageVerifierListener yuvListener =
+                    new ImageVerifierListener(previewSize, ImageFormat.YUV_420_888);
+            ImageReader yuvReader = makeImageReader(previewSize,
+                    ImageFormat.YUV_420_888, MAX_READER_IMAGES, yuvListener, mHandler);
+            surfaces.add(yuvReader.getSurface());
+        }
+
+        startPreview(surfaces, null);
+
+        for (int i = 0; i < numPreview; i++) {
+            TextureView view = mTextureView[i];
+            boolean previewDone =
+                    previewListener[i].waitForPreviewDone(WAIT_FOR_COMMAND_TO_COMPLETE);
+            assertTrue("Unable to start preview " + i, previewDone);
+            view.setSurfaceTextureListener(null);
+        }
+
+        // TODO: check the framerate is correct
+        SystemClock.sleep(PREVIEW_TIME_MS);
+
+        stopPreview();
+    }
+}
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/PerformanceTest.java b/tests/tests/hardware/src/android/hardware/camera2/cts/PerformanceTest.java
new file mode 100644
index 0000000..81152a0
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/PerformanceTest.java
@@ -0,0 +1,270 @@
+/*
+ * 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 com.android.ex.camera2.blocking.BlockingSessionListener.*;
+
+import android.graphics.ImageFormat;
+import android.hardware.camera2.CameraCaptureSession.CaptureListener;
+import android.hardware.camera2.CameraDevice;
+import android.hardware.camera2.CaptureRequest;
+import android.hardware.camera2.cts.CameraTestUtils.SimpleImageReaderListener;
+import android.hardware.camera2.cts.helpers.StaticMetadata;
+import android.hardware.camera2.cts.helpers.StaticMetadata.CheckLevel;
+import android.hardware.camera2.cts.testcases.Camera2SurfaceViewTestCase;
+import android.util.Log;
+import android.util.Size;
+import android.view.Surface;
+import android.cts.util.DeviceReportLog;
+import android.media.Image;
+import android.media.ImageReader;
+import android.os.ConditionVariable;
+
+import com.android.cts.util.ReportLog;
+import com.android.cts.util.ResultType;
+import com.android.cts.util.ResultUnit;
+import com.android.cts.util.Stat;
+import com.android.ex.camera2.blocking.BlockingSessionListener;
+import com.android.ex.camera2.exceptions.TimeoutRuntimeException;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Test camera2 API use case performance KPIs, such as camera open time, session creation time,
+ * shutter lag etc. The KPI data will be reported in cts results.
+ */
+public class PerformanceTest extends Camera2SurfaceViewTestCase {
+    private static final String TAG = "SurfaceViewPreviewTest";
+    private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
+    private static final int NUM_TEST_LOOPS = 5;
+    private static final int NUM_MAX_IMAGES = 4;
+    private static final int NUM_RESULTS_WAIT = 30;
+
+    private DeviceReportLog mReportLog;
+
+    @Override
+    protected void setUp() throws Exception {
+        mReportLog = new DeviceReportLog();
+        super.setUp();
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        // Deliver the report to host will automatically clear the report log.
+        mReportLog.deliverReportToHost(getInstrumentation());
+        super.tearDown();
+    }
+
+    /**
+     * Test camera launch KPI: the time duration between a camera device is
+     * being opened and first preview frame is available.
+     * <p>
+     * It includes camera open time, session creation time, and sending first
+     * preview request processing latency etc. For the SurfaceView based preview use
+     * case, there is no way for client to know the exact preview frame
+     * arrival time. To approximate this time, a companion YUV420_888 stream is
+     * created. The first YUV420_888 Image coming out of the ImageReader is treated
+     * as the first preview arrival time.
+     * </p>
+     */
+    public void testCameraLaunch() throws Exception {
+        double[] cameraOpenTimes = new double[NUM_TEST_LOOPS];
+        double[] configureStreamTimes = new double[NUM_TEST_LOOPS];
+        double[] startPreviewTimes = new double[NUM_TEST_LOOPS];
+        double[] stopPreviewTimes = new double[NUM_TEST_LOOPS];
+        double[] cameraCloseTimes = new double[NUM_TEST_LOOPS];
+        double[] cameraLaunchTimes = new double[NUM_TEST_LOOPS];
+
+        for (String id : mCameraIds) {
+            try {
+                initializeImageReader(id, ImageFormat.YUV_420_888);
+                SimpleImageListener imageListener = null;
+                long startTimeMs, openTimeMs, configureTimeMs, previewStartedTimeMs;
+                for (int i = 0; i < NUM_TEST_LOOPS; i++) {
+                    try {
+                        // Need create a new listener every iteration to be able to wait
+                        // for the first image comes out.
+                        imageListener = new SimpleImageListener();
+                        mReader.setOnImageAvailableListener(imageListener, mHandler);
+                        startTimeMs = System.currentTimeMillis();
+
+                        // Blocking open camera
+                        simpleOpenCamera(id);
+                        openTimeMs = System.currentTimeMillis();
+                        cameraOpenTimes[i] = openTimeMs - startTimeMs;
+
+                        // Blocking configure outputs.
+                        configureReaderAndPreviewOutputs();
+                        configureTimeMs = System.currentTimeMillis();
+                        configureStreamTimes[i] = configureTimeMs - openTimeMs;
+
+                        // Blocking start preview (start preview to first image arrives)
+                        CameraTestUtils.SimpleCaptureListener resultListener =
+                                new CameraTestUtils.SimpleCaptureListener();
+                        blockingStartPreview(resultListener, imageListener);
+                        previewStartedTimeMs = System.currentTimeMillis();
+                        startPreviewTimes[i] = previewStartedTimeMs - configureTimeMs;
+                        cameraLaunchTimes[i] = previewStartedTimeMs - startTimeMs;
+
+                        // Let preview on for a couple of frames
+                        waitForNumResults(resultListener, NUM_RESULTS_WAIT);
+
+                        // Blocking stop preview
+                        startTimeMs = System.currentTimeMillis();
+                        blockingStopPreview();
+                        stopPreviewTimes[i] = System.currentTimeMillis() - startTimeMs;
+                    }
+                    finally {
+                        // Blocking camera close
+                        startTimeMs = System.currentTimeMillis();
+                        closeDevice();
+                        cameraCloseTimes[i] = System.currentTimeMillis() - startTimeMs;
+                    }
+                }
+
+                // Finish the data collection, report the KPIs.
+                mReportLog.printArray("Camera " + id
+                        + ": Camera open time", cameraOpenTimes,
+                        ResultType.LOWER_BETTER, ResultUnit.MS);
+                mReportLog.printArray("Camera " + id
+                        + ": Camera configure stream time", configureStreamTimes,
+                        ResultType.LOWER_BETTER, ResultUnit.MS);
+                mReportLog.printArray("Camera " + id
+                        + ": Camera start preview time", startPreviewTimes,
+                        ResultType.LOWER_BETTER, ResultUnit.MS);
+                mReportLog.printArray("Camera " + id
+                        + ": Camera stop preview", stopPreviewTimes,
+                        ResultType.LOWER_BETTER, ResultUnit.MS);
+                mReportLog.printArray("Camera " + id
+                        + ": Camera close time", cameraCloseTimes,
+                        ResultType.LOWER_BETTER, ResultUnit.MS);
+                mReportLog.printArray("Camera " + id
+                        + ": Camera launch time", cameraLaunchTimes,
+                        ResultType.LOWER_BETTER, ResultUnit.MS);
+                mReportLog.printSummary("Camera launch average time for Camera " + id,
+                        Stat.getAverage(cameraLaunchTimes),
+                        ResultType.LOWER_BETTER, ResultUnit.MS);
+            }
+            finally {
+                closeImageReader();
+            }
+        }
+    }
+
+    private void blockingStopPreview() throws Exception {
+        stopPreview();
+        mSessionListener.getStateWaiter().waitForState(SESSION_CLOSED,
+                CameraTestUtils.SESSION_CLOSE_TIMEOUT_MS);
+    }
+
+    private void blockingStartPreview(CaptureListener listener, SimpleImageListener imageListener)
+            throws Exception {
+        if (mPreviewSurface == null || mReaderSurface == null) {
+            throw new IllegalStateException("preview and reader surface must be initilized first");
+        }
+
+        CaptureRequest.Builder previewBuilder =
+                mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
+        previewBuilder.addTarget(mPreviewSurface);
+        previewBuilder.addTarget(mReaderSurface);
+        mSession.setRepeatingRequest(previewBuilder.build(), listener, mHandler);
+        imageListener.waitForImageAvailable(CameraTestUtils.CAPTURE_IMAGE_TIMEOUT_MS);
+    }
+
+    /**
+     * Configure reader and preview outputs and wait until done.
+     */
+    private void configureReaderAndPreviewOutputs() throws Exception {
+        if (mPreviewSurface == null || mReaderSurface == null) {
+            throw new IllegalStateException("preview and reader surface must be initilized first");
+        }
+        mSessionListener = new BlockingSessionListener();
+        List<Surface> outputSurfaces = new ArrayList<>();
+        outputSurfaces.add(mPreviewSurface);
+        outputSurfaces.add(mReaderSurface);
+        mSession = CameraTestUtils.configureCameraSession(mCamera, outputSurfaces,
+                mSessionListener, mHandler);
+    }
+
+    /**
+     * Initialize the ImageReader instance and preview surface.
+     * @param cameraId The camera to be opened.
+     * @param format The format used to create ImageReader instance.
+     */
+    private void initializeImageReader(String cameraId, int format) throws Exception {
+        mOrderedPreviewSizes = CameraTestUtils.getSupportedPreviewSizes(
+                cameraId, mCameraManager, CameraTestUtils.PREVIEW_SIZE_BOUND);
+        Size maxPreviewSize = mOrderedPreviewSizes.get(0);
+        createImageReader(maxPreviewSize, format, NUM_MAX_IMAGES, /*listener*/null);
+        updatePreviewSurface(maxPreviewSize);
+    }
+
+    private void simpleOpenCamera(String cameraId) throws Exception {
+        mCamera = CameraTestUtils.openCamera(
+                mCameraManager, cameraId, mCameraListener, mHandler);
+        mCollector.setCameraId(cameraId);
+        mStaticInfo = new StaticMetadata(mCameraManager.getCameraCharacteristics(cameraId),
+                CheckLevel.ASSERT, /*collector*/null);
+        mMinPreviewFrameDurationMap =
+                mStaticInfo.getAvailableMinFrameDurationsForFormatChecked(ImageFormat.YUV_420_888);
+    }
+
+    /**
+     * Simple image listener that can be used to time the availability of first image.
+     *
+     */
+    private static class SimpleImageListener implements ImageReader.OnImageAvailableListener {
+        private ConditionVariable imageAvailable = new ConditionVariable();
+        private boolean imageReceived = false;
+
+        @Override
+        public void onImageAvailable(ImageReader reader) {
+            Image image = null;
+            if (!imageReceived) {
+                if (VERBOSE) {
+                    Log.v(TAG, "First image arrives");
+                }
+                imageReceived = true;
+                imageAvailable.open();
+            }
+            image = reader.acquireNextImage();
+            if (image != null) {
+                image.close();
+            }
+        }
+
+        /**
+         * Wait for image available, return immediately if the image was already
+         * received, otherwise wait until an image arrives.
+         */
+        public void waitForImageAvailable(long timeout) {
+            if (imageReceived) {
+                imageReceived = false;
+                return;
+            }
+
+            if (imageAvailable.block(timeout)) {
+                imageAvailable.close();
+                imageReceived = false;
+            } else {
+                throw new TimeoutRuntimeException("Unable to get the first image after "
+                        + CameraTestUtils.CAPTURE_IMAGE_TIMEOUT_MS + "ms");
+            }
+        }
+    }
+}
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/RecordingTest.java b/tests/tests/hardware/src/android/hardware/camera2/cts/RecordingTest.java
new file mode 100644
index 0000000..652462c
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/RecordingTest.java
@@ -0,0 +1,1054 @@
+/*
+ * 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.BlockingSessionListener.*;
+
+import android.graphics.ImageFormat;
+import android.hardware.camera2.CameraCharacteristics;
+import android.hardware.camera2.CameraCaptureSession;
+import android.hardware.camera2.CameraDevice;
+import android.hardware.camera2.CaptureRequest;
+import android.hardware.camera2.CaptureResult;
+import android.hardware.camera2.params.StreamConfigurationMap;
+import android.util.Size;
+import android.hardware.camera2.cts.testcases.Camera2SurfaceViewTestCase;
+import android.media.CamcorderProfile;
+import android.media.MediaCodecInfo;
+import android.media.MediaCodecInfo.CodecCapabilities;
+import android.media.MediaCodecInfo.CodecProfileLevel;
+import android.media.Image;
+import android.media.ImageReader;
+import android.media.MediaCodecList;
+import android.media.MediaPlayer;
+import android.media.MediaRecorder;
+import android.os.Environment;
+import android.os.SystemClock;
+import android.test.suitebuilder.annotation.LargeTest;
+import android.util.Log;
+import android.util.Range;
+import android.view.Surface;
+
+import com.android.ex.camera2.blocking.BlockingSessionListener;
+
+import junit.framework.AssertionFailedError;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * CameraDevice video recording use case tests by using MediaRecorder and
+ * MediaCodec.
+ */
+@LargeTest
+public class RecordingTest extends Camera2SurfaceViewTestCase {
+    private static final String TAG = "RecordingTest";
+    private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
+    private static final boolean DEBUG_DUMP = false;
+    private static final Size VIDEO_SIZE_BOUND = new Size(1920, 1080);
+    private static final int RECORDING_DURATION_MS = 2000;
+    private static final int DURATION_MARGIN_MS = 400;
+    private static final int FRAME_DURATION_ERROR_TOLERANCE_MS = 3;
+    private static final int BIT_RATE_1080P = 16000000;
+    private static final int BIT_RATE_MIN = 64000;
+    private static final int BIT_RATE_MAX = 40000000;
+    private static final int VIDEO_FRAME_RATE = 30;
+    private final String VIDEO_FILE_PATH = Environment.getExternalStorageDirectory().getPath();
+    private static final int[] mCamcorderProfileList = {
+            CamcorderProfile.QUALITY_2160P,
+            CamcorderProfile.QUALITY_1080P,
+            CamcorderProfile.QUALITY_480P,
+            CamcorderProfile.QUALITY_720P,
+            CamcorderProfile.QUALITY_CIF,
+            CamcorderProfile.QUALITY_LOW,
+            CamcorderProfile.QUALITY_HIGH,
+            CamcorderProfile.QUALITY_QCIF,
+            CamcorderProfile.QUALITY_QVGA,
+    };
+    private static final int MAX_VIDEO_SNAPSHOT_IMAGES = 5;
+    private static final int BURST_VIDEO_SNAPSHOT_NUM = 3;
+    private static final int SLOWMO_SLOW_FACTOR = 4;
+
+    private List<Size> mSupportedVideoSizes;
+    private Surface mRecordingSurface;
+    private MediaRecorder mMediaRecorder;
+    private String mOutMediaFileName;
+    private int mVideoFrameRate;
+    private Size mVideoSize;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        super.tearDown();
+    }
+
+    /**
+     * <p>
+     * Test basic camera recording.
+     * </p>
+     * <p>
+     * This test covers the typical basic use case of camera recording.
+     * MediaRecorder is used to record the audio and video, CamcorderProfile is
+     * used to configure the MediaRecorder. It goes through the pre-defined
+     * CamcorderProfile list, test each profile configuration and validate the
+     * recorded video. Preview is set to the video size.
+     * </p>
+     */
+    public void testBasicRecording() throws Exception {
+        for (int i = 0; i < mCameraIds.length; i++) {
+            try {
+                Log.i(TAG, "Testing basic recording for camera " + mCameraIds[i]);
+                // Re-use the MediaRecorder object for the same camera device.
+                mMediaRecorder = new MediaRecorder();
+                openDevice(mCameraIds[i]);
+                mSupportedVideoSizes = getSupportedVideoSizes(mCamera.getId(), mCameraManager,
+                        VIDEO_SIZE_BOUND);
+
+                basicRecordingTestByCamera();
+            } finally {
+                closeDevice();
+                releaseRecorder();
+            }
+        }
+    }
+
+    /**
+     * <p>
+     * Test camera recording for all supported sizes by using MediaRecorder.
+     * </p>
+     * <p>
+     * This test covers camera recording for all supported sizes by camera. MediaRecorder
+     * is used to encode the video. Preview is set to the video size. Recorded videos are
+     * validated according to the recording configuration.
+     * </p>
+     */
+    public void testSupportedVideoSizes() throws Exception {
+        for (int i = 0; i < mCameraIds.length; i++) {
+            try {
+                Log.i(TAG, "Testing supported video size recording for camera " + mCameraIds[i]);
+                // Re-use the MediaRecorder object for the same camera device.
+                mMediaRecorder = new MediaRecorder();
+                openDevice(mCameraIds[i]);
+
+                mSupportedVideoSizes = getSupportedVideoSizes(mCamera.getId(), mCameraManager,
+                        VIDEO_SIZE_BOUND);
+
+                recordingSizeTestByCamera();
+            } finally {
+                closeDevice();
+                releaseRecorder();
+            }
+        }
+    }
+
+    /**
+     * Test different start/stop orders of Camera and Recorder.
+     *
+     * <p>The recording should be working fine for any kind of start/stop orders.</p>
+     */
+    public void testCameraRecorderOrdering() {
+        // TODO: need implement
+    }
+
+    /**
+     * <p>
+     * Test camera recording for all supported sizes by using MediaCodec.
+     * </p>
+     * <p>
+     * This test covers video only recording for all supported sizes (camera and
+     * encoder). MediaCodec is used to encode the video. The recorded videos are
+     * validated according to the recording configuration.
+     * </p>
+     */
+    public void testMediaCodecRecording() throws Exception {
+        // TODO. Need implement.
+    }
+
+    /**
+     * <p>
+     * Test video snapshot for each camera.
+     * </p>
+     * <p>
+     * This test covers video snapshot typical use case. The MediaRecorder is used to record the
+     * video for each available video size. The largest still capture size is selected to
+     * capture the JPEG image. The still capture images are validated according to the capture
+     * configuration. The timestamp of capture result before and after video snapshot is also
+     * checked to make sure no frame drop caused by video snapshot.
+     * </p>
+     */
+    public void testVideoSnapshot() throws Exception {
+        videoSnapshotHelper(/*burstTest*/false);
+    }
+
+    /**
+     * <p>
+     * Test burst video snapshot for each camera.
+     * </p>
+     * <p>
+     * This test covers burst video snapshot capture. The MediaRecorder is used to record the
+     * video for each available video size. The largest still capture size is selected to
+     * capture the JPEG image. {@value #BURST_VIDEO_SNAPSHOT_NUM} video snapshot requests will be
+     * sent during the test. The still capture images are validated according to the capture
+     * configuration.
+     * </p>
+     */
+    public void testBurstVideoSnapshot() throws Exception {
+        videoSnapshotHelper(/*burstTest*/true);
+    }
+
+    /**
+     * Test timelapse recording, where capture rate is slower than video (playback) frame rate.
+     */
+    public void testTimelapseRecording() throws Exception {
+        // TODO. Need implement.
+    }
+
+    public void testSlowMotionRecording() throws Exception {
+        slowMotionRecording();
+    }
+
+    /**
+     * Test slow motion recording where capture rate (camera output) is different with
+     * video (playback) frame rate for each camera if high speed recording is supported
+     * by both camera and encoder.
+     *
+     * <p>
+     * Normal recording use cases make the capture rate (camera output frame
+     * rate) the same as the video (playback) frame rate. This guarantees that
+     * the motions in the scene play at the normal speed. If the capture rate is
+     * faster than video frame rate, for a given time duration, more number of
+     * frames are captured than it can be played in the same time duration. This
+     * generates "slow motion" effect during playback.
+     * </p>
+     */
+    private void slowMotionRecording() throws Exception {
+        for (String id : mCameraIds) {
+            try {
+                Log.i(TAG, "Testing slow motion recording for camera " + id);
+                // Re-use the MediaRecorder object for the same camera device.
+                mMediaRecorder = new MediaRecorder();
+                openDevice(id);
+
+                if (!mStaticInfo.isHighSpeedVideoSupported()) {
+                    continue;
+                }
+
+                StreamConfigurationMap config =
+                        mStaticInfo.getValueFromKeyNonNull(
+                                CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
+                Size[] highSpeedVideoSizes = config.getHighSpeedVideoSizes();
+                for (Size size : highSpeedVideoSizes) {
+                    Range<Integer> fpsRange = getHighestHighSpeedFixedFpsRangeForSize(config, size);
+                    mCollector.expectNotNull("Unable to find the fixed frame rate fps range for " +
+                            "size " + size, fpsRange);
+                    if (fpsRange == null) {
+                        continue;
+                    }
+
+                    int captureRate = fpsRange.getLower();
+                    int videoFramerate = captureRate / SLOWMO_SLOW_FACTOR;
+                    /**
+                     * Check if encoder support this. TODO: use HIGH_SPEED_720p
+                     * CamCorderProfile to get the performance guarantee. Also
+                     * add the test in StaticMetadataTest to check: 1. Camera
+                     * high speed recording metadata is correctly reported 2.
+                     * Encoder profile/level info is correctly reported. After
+                     * that, we only need check the CamcorderProfile before
+                     * skipping the test.
+                     */
+                    if (!isSupportedByAVCEncoder(size, captureRate)) {
+                        Log.i(TAG, "high speed recording " + size + "@" + captureRate + "fps"
+                                + " is not supported by AVC encoder");
+                        continue;
+                    }
+
+                    mOutMediaFileName = VIDEO_FILE_PATH + "/test_slowMo_video.mp4";
+                    if (DEBUG_DUMP) {
+                        mOutMediaFileName = VIDEO_FILE_PATH + "/test_slowMo_video_" + id + "_"
+                                + size.toString() + ".mp4";
+                    }
+
+                    prepareRecording(size, videoFramerate, captureRate);
+
+                    // prepare preview surface: preview size is same as video size.
+                    updatePreviewSurface(size);
+
+                    // Start recording
+                    startSlowMotionRecording(/*useMediaRecorder*/true, videoFramerate, captureRate,
+                            fpsRange);
+
+                    // Record certain duration.
+                    SystemClock.sleep(RECORDING_DURATION_MS);
+
+                    // Stop recording and preview
+                    stopRecording(/*useMediaRecorder*/true);
+
+                    // Validation.
+                    validateRecording(size, RECORDING_DURATION_MS * SLOWMO_SLOW_FACTOR);
+
+                }
+
+            } finally {
+                closeDevice();
+                releaseRecorder();
+            }
+        }
+    }
+
+    private Range<Integer> getHighestHighSpeedFixedFpsRangeForSize(StreamConfigurationMap config,
+            Size size) {
+        Range<Integer>[] availableFpsRanges = config.getHighSpeedVideoFpsRangesFor(size);
+        Range<Integer> maxRange = availableFpsRanges[0];
+        boolean foundRange = false;
+        for (Range<Integer> range : availableFpsRanges) {
+            if (range.getLower() == range.getUpper() && range.getLower() >= maxRange.getLower()) {
+                foundRange = true;
+                maxRange = range;
+            }
+        }
+
+        if (!foundRange) {
+            return null;
+        }
+        return maxRange;
+    }
+
+    private void startSlowMotionRecording(boolean useMediaRecorder, int videoFrameRate,
+            int captureRate, Range<Integer> fpsRange) throws Exception {
+        List<Surface> outputSurfaces = new ArrayList<Surface>(2);
+        assertTrue("Both preview and recording surfaces should be valid",
+                mPreviewSurface.isValid() && mRecordingSurface.isValid());
+        outputSurfaces.add(mPreviewSurface);
+        outputSurfaces.add(mRecordingSurface);
+        // Video snapshot surface
+        if (mReaderSurface != null) {
+            outputSurfaces.add(mReaderSurface);
+        }
+        mSessionListener = new BlockingSessionListener();
+        mSession = configureCameraSession(mCamera, outputSurfaces, mSessionListener, mHandler);
+
+        CaptureRequest.Builder recordingRequestBuilder =
+                mCamera.createCaptureRequest(CameraDevice.TEMPLATE_RECORD);
+        recordingRequestBuilder.set(CaptureRequest.CONTROL_MODE,
+                CaptureRequest.CONTROL_MODE_USE_SCENE_MODE);
+        recordingRequestBuilder.set(CaptureRequest.CONTROL_SCENE_MODE,
+                CaptureRequest.CONTROL_SCENE_MODE_HIGH_SPEED_VIDEO);
+
+        CaptureRequest.Builder recordingOnlyBuilder =
+                mCamera.createCaptureRequest(CameraDevice.TEMPLATE_RECORD);
+        recordingOnlyBuilder.set(CaptureRequest.CONTROL_MODE,
+                CaptureRequest.CONTROL_MODE_USE_SCENE_MODE);
+        recordingOnlyBuilder.set(CaptureRequest.CONTROL_SCENE_MODE,
+                CaptureRequest.CONTROL_SCENE_MODE_HIGH_SPEED_VIDEO);
+        int slowMotionFactor = captureRate / videoFrameRate;
+
+        // Make sure camera output frame rate is set to correct value.
+        recordingRequestBuilder.set(CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE, fpsRange);
+        recordingRequestBuilder.addTarget(mRecordingSurface);
+        recordingRequestBuilder.addTarget(mPreviewSurface);
+        recordingOnlyBuilder.set(CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE, fpsRange);
+        recordingOnlyBuilder.addTarget(mRecordingSurface);
+
+        List<CaptureRequest> slowMoRequests = new ArrayList<CaptureRequest>();
+        slowMoRequests.add(recordingRequestBuilder.build());// Preview + recording.
+
+        for (int i = 0; i < slowMotionFactor - 1; i++) {
+            slowMoRequests.add(recordingOnlyBuilder.build()); // Recording only.
+        }
+        mSession.setRepeatingBurst(slowMoRequests, null, null);
+
+        if (useMediaRecorder) {
+            mMediaRecorder.start();
+        } else {
+            // TODO: need implement MediaCodec path.
+        }
+
+    }
+
+    /**
+     * Test camera recording by using each available CamcorderProfile for a
+     * given camera. preview size is set to the video size.
+     */
+    private void basicRecordingTestByCamera() throws Exception {
+        for (int profileId : mCamcorderProfileList) {
+            int cameraId = Integer.valueOf(mCamera.getId());
+            if (!CamcorderProfile.hasProfile(cameraId, profileId) ||
+                    allowedUnsupported(cameraId, profileId)) {
+                continue;
+            }
+
+            CamcorderProfile profile = CamcorderProfile.get(cameraId, profileId);
+            Size videoSz = new Size(profile.videoFrameWidth, profile.videoFrameHeight);
+            assertTrue("Video size " + videoSz.toString() + " for profile ID " + profileId +
+                            " must be one of the camera device supported video size!",
+                            mSupportedVideoSizes.contains(videoSz));
+
+            if (VERBOSE) {
+                Log.v(TAG, "Testing camera recording with video size " + videoSz.toString());
+            }
+
+            // Configure preview and recording surfaces.
+            mOutMediaFileName = VIDEO_FILE_PATH + "/test_video.mp4";
+            if (DEBUG_DUMP) {
+                mOutMediaFileName = VIDEO_FILE_PATH + "/test_video_" + cameraId + "_"
+                        + videoSz.toString() + ".mp4";
+            }
+
+            prepareRecordingWithProfile(profile);
+
+            // prepare preview surface: preview size is same as video size.
+            updatePreviewSurface(videoSz);
+
+            // Start recording
+            startRecording(/* useMediaRecorder */true);
+
+            // Record certain duration.
+            SystemClock.sleep(RECORDING_DURATION_MS);
+
+            // Stop recording and preview
+            stopRecording(/* useMediaRecorder */true);
+
+            // Validation.
+            validateRecording(videoSz, RECORDING_DURATION_MS);
+        }
+    }
+
+    /**
+     * Test camera recording for each supported video size by camera, preview
+     * size is set to the video size.
+     */
+    private void recordingSizeTestByCamera() throws Exception {
+        for (Size sz : mSupportedVideoSizes) {
+            if (!isSupported(sz, VIDEO_FRAME_RATE, VIDEO_FRAME_RATE)) {
+                continue;
+            }
+
+            if (VERBOSE) {
+                Log.v(TAG, "Testing camera recording with video size " + sz.toString());
+            }
+
+            // Configure preview and recording surfaces.
+            mOutMediaFileName = VIDEO_FILE_PATH + "/test_video.mp4";
+            if (DEBUG_DUMP) {
+                mOutMediaFileName = VIDEO_FILE_PATH + "/test_video_" + mCamera.getId() + "_"
+                        + sz.toString() + ".mp4";
+            }
+
+            // Use AVC and AAC a/v compression format.
+            prepareRecording(sz, VIDEO_FRAME_RATE, VIDEO_FRAME_RATE);
+
+            // prepare preview surface: preview size is same as video size.
+            updatePreviewSurface(sz);
+
+            // Start recording
+            startRecording(/* useMediaRecorder */true);
+
+            // Record certain duration.
+            SystemClock.sleep(RECORDING_DURATION_MS);
+
+            // Stop recording and preview
+            stopRecording(/* useMediaRecorder */true);
+
+            // Validation.
+            validateRecording(sz, RECORDING_DURATION_MS);
+        }
+    }
+
+    /**
+     * Simple wrapper to wrap normal/burst video snapshot tests
+     */
+    private void videoSnapshotHelper(boolean burstTest) throws Exception {
+            for (String id : mCameraIds) {
+                try {
+                    Log.i(TAG, "Testing video snapshot for camera " + id);
+                    // Re-use the MediaRecorder object for the same camera device.
+                    mMediaRecorder = new MediaRecorder();
+                    openDevice(id);
+                    mSupportedVideoSizes =
+                            getSupportedVideoSizes(id, mCameraManager, VIDEO_SIZE_BOUND);
+
+                    videoSnapshotTestByCamera(burstTest);
+                } finally {
+                    closeDevice();
+                    releaseRecorder();
+                }
+            }
+    }
+
+    /**
+     * Returns {@code true} if the {@link CamcorderProfile} ID is allowed to be unsupported.
+     *
+     * <p>This only allows unsupported profiles when using the LEGACY mode of the Camera API.</p>
+     *
+     * @param profileId a {@link CamcorderProfile} ID to check.
+     * @return {@code true} if supported.
+     */
+    private boolean allowedUnsupported(int cameraId, int profileId) {
+        if (!mStaticInfo.isHardwareLevelLegacy()) {
+            return false;
+        }
+
+        switch(profileId) {
+            case CamcorderProfile.QUALITY_2160P:
+            case CamcorderProfile.QUALITY_1080P:
+            case CamcorderProfile.QUALITY_HIGH:
+                return !CamcorderProfile.hasProfile(cameraId, profileId) ||
+                        CamcorderProfile.get(cameraId, profileId).videoFrameWidth >= 1080;
+        }
+        return false;
+    }
+
+    /**
+     * Test video snapshot for each  available CamcorderProfile for a given camera.
+     *
+     * <p>
+     * Preview size is set to the video size. For the burst test, frame drop and jittering
+     * is not checked.
+     * </p>
+     *
+     * @param burstTest Perform burst capture or single capture. For burst capture
+     *                  {@value #BURST_VIDEO_SNAPSHOT_NUM} capture requests will be sent.
+     */
+    private void videoSnapshotTestByCamera(boolean burstTest)
+            throws Exception {
+        for (int profileId : mCamcorderProfileList) {
+            int cameraId = Integer.valueOf(mCamera.getId());
+            if (!CamcorderProfile.hasProfile(cameraId, profileId) ||
+                    allowedUnsupported(cameraId, profileId)) {
+                continue;
+            }
+
+            CamcorderProfile profile = CamcorderProfile.get(cameraId, profileId);
+            Size videoSz = new Size(profile.videoFrameWidth, profile.videoFrameHeight);
+            assertTrue("Video size " + videoSz.toString() + " for profile ID " + profileId +
+                            " must be one of the camera device supported video size!",
+                            mSupportedVideoSizes.contains(videoSz));
+
+            Size videoSnapshotSz = mStaticInfo.isHardwareLevelFull() ?
+                    mOrderedStillSizes.get(0) : // Full device tests largest jpeg size
+                    videoSz;                    // Non-full device tests video size
+            createImageReader(
+                    videoSnapshotSz, ImageFormat.JPEG,
+                    MAX_VIDEO_SNAPSHOT_IMAGES, /*listener*/null);
+
+            if (VERBOSE) {
+                Log.v(TAG, "Testing camera recording with video size " + videoSz.toString());
+            }
+
+            // Configure preview and recording surfaces.
+            mOutMediaFileName = VIDEO_FILE_PATH + "/test_video.mp4";
+            if (DEBUG_DUMP) {
+                mOutMediaFileName = VIDEO_FILE_PATH + "/test_video_" + cameraId + "_"
+                        + videoSz.toString() + ".mp4";
+            }
+
+            prepareRecordingWithProfile(profile);
+
+            // prepare video snapshot
+            SimpleCaptureListener resultListener = new SimpleCaptureListener();
+            SimpleImageReaderListener imageListener = new SimpleImageReaderListener();
+            CaptureRequest.Builder videoSnapshotRequestBuilder =
+                    mCamera.createCaptureRequest(CameraDevice.TEMPLATE_VIDEO_SNAPSHOT);
+
+            // prepare preview surface: preview size is same as video size.
+            updatePreviewSurface(videoSz);
+
+            prepareVideoSnapshot(videoSnapshotRequestBuilder, imageListener);
+
+            // Start recording
+            startRecording(/* useMediaRecorder */true, resultListener);
+
+            // Record certain duration.
+            SystemClock.sleep(RECORDING_DURATION_MS / 2);
+
+            // take a video snapshot
+            CaptureRequest request = videoSnapshotRequestBuilder.build();
+            if (burstTest) {
+                List<CaptureRequest> requests =
+                        new ArrayList<CaptureRequest>(BURST_VIDEO_SNAPSHOT_NUM);
+                for (int i = 0; i < BURST_VIDEO_SNAPSHOT_NUM; i++) {
+                    requests.add(request);
+                }
+                mSession.captureBurst(requests, resultListener, mHandler);
+            } else {
+                mSession.capture(request, resultListener, mHandler);
+            }
+
+            // make sure recording is still going after video snapshot
+            SystemClock.sleep(RECORDING_DURATION_MS / 2);
+
+            // Stop recording and preview
+            stopRecording(/* useMediaRecorder */true);
+
+            // Validation recorded video
+            validateRecording(videoSz, RECORDING_DURATION_MS);
+
+            if (burstTest) {
+                for (int i = 0; i < BURST_VIDEO_SNAPSHOT_NUM; i++) {
+                    Image image = imageListener.getImage(CAPTURE_IMAGE_TIMEOUT_MS);
+                    validateVideoSnapshotCapture(image, videoSnapshotSz);
+                    image.close();
+                }
+            } else {
+                // validate video snapshot image
+                Image image = imageListener.getImage(CAPTURE_IMAGE_TIMEOUT_MS);
+                validateVideoSnapshotCapture(image, videoSnapshotSz);
+
+                // validate if there is framedrop around video snapshot
+                validateFrameDropAroundVideoSnapshot(resultListener, image.getTimestamp());
+
+                //TODO: validate jittering. Should move to PTS
+                //validateJittering(resultListener);
+
+                image.close();
+            }
+
+            closeImageReader();
+        }
+    }
+
+    /**
+     * Configure video snapshot request according to the still capture size
+     */
+    private void prepareVideoSnapshot(
+            CaptureRequest.Builder requestBuilder,
+            ImageReader.OnImageAvailableListener imageListener)
+            throws Exception {
+        mReader.setOnImageAvailableListener(imageListener, mHandler);
+        assertNotNull("Recording surface must be non-null!", mRecordingSurface);
+        requestBuilder.addTarget(mRecordingSurface);
+        assertNotNull("Preview surface must be non-null!", mPreviewSurface);
+        requestBuilder.addTarget(mPreviewSurface);
+        assertNotNull("Reader surface must be non-null!", mReaderSurface);
+        requestBuilder.addTarget(mReaderSurface);
+    }
+
+    /**
+     * Configure MediaRecorder recording session with CamcorderProfile, prepare
+     * the recording surface.
+     */
+    private void prepareRecordingWithProfile(CamcorderProfile profile)
+            throws Exception {
+        // Prepare MediaRecorder.
+        mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.CAMCORDER);
+        mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.SURFACE);
+        mMediaRecorder.setProfile(profile);
+        mMediaRecorder.setOutputFile(mOutMediaFileName);
+        mMediaRecorder.prepare();
+        mRecordingSurface = mMediaRecorder.getSurface();
+        assertNotNull("Recording surface must be non-null!", mRecordingSurface);
+        mVideoFrameRate = profile.videoFrameRate;
+        mVideoSize = new Size(profile.videoFrameWidth, profile.videoFrameHeight);
+    }
+
+    /**
+     * Configure MediaRecorder recording session with CamcorderProfile, prepare
+     * the recording surface. Use AVC for video compression, AAC for audio compression.
+     * Both are required for android devices by android CDD.
+     */
+    private void prepareRecording(Size sz, int videoFrameRate, int captureRate)
+            throws Exception {
+        // Prepare MediaRecorder.
+        mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.CAMCORDER);
+        mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.SURFACE);
+        mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
+        mMediaRecorder.setOutputFile(mOutMediaFileName);
+        mMediaRecorder.setVideoEncodingBitRate(getVideoBitRate(sz));
+        mMediaRecorder.setVideoFrameRate(videoFrameRate);
+        mMediaRecorder.setCaptureRate(captureRate);
+        mMediaRecorder.setVideoSize(sz.getWidth(), sz.getHeight());
+        mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264);
+        mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
+        mMediaRecorder.prepare();
+        mRecordingSurface = mMediaRecorder.getSurface();
+        assertNotNull("Recording surface must be non-null!", mRecordingSurface);
+        mVideoFrameRate = videoFrameRate;
+        mVideoSize = sz;
+    }
+
+    private void startRecording(boolean useMediaRecorder,
+            CameraCaptureSession.CaptureListener listener) throws Exception {
+        List<Surface> outputSurfaces = new ArrayList<Surface>(2);
+        assertTrue("Both preview and recording surfaces should be valid",
+                mPreviewSurface.isValid() && mRecordingSurface.isValid());
+        outputSurfaces.add(mPreviewSurface);
+        outputSurfaces.add(mRecordingSurface);
+        // Video snapshot surface
+        if (mReaderSurface != null) {
+            outputSurfaces.add(mReaderSurface);
+        }
+        mSessionListener = new BlockingSessionListener();
+        mSession = configureCameraSession(mCamera, outputSurfaces, mSessionListener, mHandler);
+
+        CaptureRequest.Builder recordingRequestBuilder =
+                mCamera.createCaptureRequest(CameraDevice.TEMPLATE_RECORD);
+        // Make sure camera output frame rate is set to correct value.
+        Range<Integer> fpsRange = Range.create(mVideoFrameRate, mVideoFrameRate);
+        recordingRequestBuilder.set(CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE, fpsRange);
+        recordingRequestBuilder.addTarget(mRecordingSurface);
+        recordingRequestBuilder.addTarget(mPreviewSurface);
+        mSession.setRepeatingRequest(recordingRequestBuilder.build(), listener, mHandler);
+
+        if (useMediaRecorder) {
+            mMediaRecorder.start();
+        } else {
+            // TODO: need implement MediaCodec path.
+        }
+    }
+
+    private void startRecording(boolean useMediaRecorder)  throws Exception {
+        startRecording(useMediaRecorder, null);
+    }
+
+    private void stopCameraStreaming() throws Exception {
+        if (VERBOSE) {
+            Log.v(TAG, "Stopping camera streaming and waiting for idle");
+        }
+        // Stop repeating, wait for captures to complete, and disconnect from
+        // surfaces
+        mSession.close();
+        mSessionListener.getStateWaiter().waitForState(SESSION_CLOSED, SESSION_CLOSE_TIMEOUT_MS);
+    }
+
+    private void stopRecording(boolean useMediaRecorder) throws Exception {
+        if (useMediaRecorder) {
+            stopCameraStreaming();
+
+            mMediaRecorder.stop();
+            // Can reuse the MediaRecorder object after reset.
+            mMediaRecorder.reset();
+        } else {
+            // TODO: need implement MediaCodec path.
+        }
+        if (mRecordingSurface != null) {
+            mRecordingSurface.release();
+            mRecordingSurface = null;
+        }
+    }
+
+    private void releaseRecorder() {
+        if (mMediaRecorder != null) {
+            mMediaRecorder.release();
+            mMediaRecorder = null;
+        }
+    }
+
+    private void validateRecording(Size sz, int durationMs) throws Exception {
+        File outFile = new File(mOutMediaFileName);
+        assertTrue("No video is recorded", outFile.exists());
+
+        MediaPlayer mediaPlayer = new MediaPlayer();
+        try {
+            mediaPlayer.setDataSource(mOutMediaFileName);
+            mediaPlayer.prepare();
+            Size videoSz = new Size(mediaPlayer.getVideoWidth(), mediaPlayer.getVideoHeight());
+            assertTrue("Video size doesn't match, expected " + sz.toString() +
+                    " got " + videoSz.toString(), videoSz.equals(sz));
+            int duration = mediaPlayer.getDuration();
+
+            // TODO: Don't skip this for video snapshot
+            if (!mStaticInfo.isHardwareLevelLegacy()) {
+                assertTrue(String.format(
+                        "Video duration doesn't match: recorded %dms, expected %dms", duration,
+                        durationMs), Math.abs(duration - durationMs) < DURATION_MARGIN_MS);
+            }
+        } finally {
+            mediaPlayer.release();
+            if (!DEBUG_DUMP) {
+                outFile.delete();
+            }
+        }
+    }
+
+    /**
+     * Validate video snapshot capture image object sanity and test.
+     *
+     * <p> Check for size, format and jpeg decoding</p>
+     *
+     * @param image The JPEG image to be verified.
+     * @param size The JPEG capture size to be verified against.
+     */
+    private void validateVideoSnapshotCapture(Image image, Size size) {
+        CameraTestUtils.validateImage(image, size.getWidth(), size.getHeight(),
+                ImageFormat.JPEG, /*filePath*/null);
+    }
+
+    /**
+     * Validate if video snapshot causes frame drop.
+     * Here frame drop is defined as frame duration >= 2 * expected frame duration.
+     */
+    private void validateFrameDropAroundVideoSnapshot(
+            SimpleCaptureListener resultListener, long imageTimeStamp) {
+        int expectedDurationMs = 1000 / mVideoFrameRate;
+        CaptureResult prevResult = resultListener.getCaptureResult(WAIT_FOR_RESULT_TIMEOUT_MS);
+        long prevTS = getValueNotNull(prevResult, CaptureResult.SENSOR_TIMESTAMP);
+        while (!resultListener.hasMoreResults()) {
+            CaptureResult currentResult =
+                    resultListener.getCaptureResult(WAIT_FOR_RESULT_TIMEOUT_MS);
+            long currentTS = getValueNotNull(currentResult, CaptureResult.SENSOR_TIMESTAMP);
+            if (currentTS == imageTimeStamp) {
+                // validate the timestamp before and after, then return
+                CaptureResult nextResult =
+                        resultListener.getCaptureResult(WAIT_FOR_RESULT_TIMEOUT_MS);
+                long nextTS = getValueNotNull(nextResult, CaptureResult.SENSOR_TIMESTAMP);
+                int durationMs = (int) (currentTS - prevTS) / 1000000;
+
+                // Snapshots in legacy mode pause the preview briefly.  Skip the duration
+                // requirements for legacy mode unless this is fixed.
+                if (!mStaticInfo.isHardwareLevelLegacy()) {
+                    mCollector.expectTrue(
+                            String.format(
+                                    "Video %dx%d Frame drop detected before video snapshot: " +
+                                            "duration %dms (expected %dms)",
+                                    mVideoSize.getWidth(), mVideoSize.getHeight(),
+                                    durationMs, expectedDurationMs
+                            ),
+                            durationMs < (expectedDurationMs * 2)
+                    );
+                    durationMs = (int) (nextTS - currentTS) / 1000000;
+                    mCollector.expectTrue(
+                            String.format(
+                                    "Video %dx%d Frame drop detected after video snapshot: " +
+                                            "duration %dms (expected %dms)",
+                                    mVideoSize.getWidth(), mVideoSize.getHeight(),
+                                    durationMs, expectedDurationMs
+                            ),
+                            durationMs < (expectedDurationMs * 2)
+                    );
+                }
+                return;
+            }
+            prevTS = currentTS;
+        }
+        throw new AssertionFailedError(
+                "Video snapshot timestamp does not match any of capture results!");
+    }
+
+    /**
+     * Validate frame jittering from the input simple listener's buffered results
+     */
+    private void validateJittering(SimpleCaptureListener resultListener) {
+        int expectedDurationMs = 1000 / mVideoFrameRate;
+        CaptureResult prevResult = resultListener.getCaptureResult(WAIT_FOR_RESULT_TIMEOUT_MS);
+        long prevTS = getValueNotNull(prevResult, CaptureResult.SENSOR_TIMESTAMP);
+        while (!resultListener.hasMoreResults()) {
+            CaptureResult currentResult =
+                    resultListener.getCaptureResult(WAIT_FOR_RESULT_TIMEOUT_MS);
+            long currentTS = getValueNotNull(currentResult, CaptureResult.SENSOR_TIMESTAMP);
+            int durationMs = (int) (currentTS - prevTS) / 1000000;
+            int durationError = Math.abs(durationMs - expectedDurationMs);
+            long frameNumber = currentResult.getFrameNumber();
+            mCollector.expectTrue(
+                    String.format(
+                            "Resolution %dx%d Frame %d: jittering (%dms) exceeds bound [%dms,%dms]",
+                            mVideoSize.getWidth(), mVideoSize.getHeight(),
+                            frameNumber, durationMs,
+                            expectedDurationMs - FRAME_DURATION_ERROR_TOLERANCE_MS,
+                            expectedDurationMs + FRAME_DURATION_ERROR_TOLERANCE_MS),
+                    durationError <= FRAME_DURATION_ERROR_TOLERANCE_MS);
+            prevTS = currentTS;
+        }
+    }
+
+    /**
+     * Calculate a video bit rate based on the size. The bit rate is scaled
+     * based on ratio of video size to 1080p size.
+     */
+    private int getVideoBitRate(Size sz) {
+        int rate = BIT_RATE_1080P;
+        float scaleFactor = sz.getHeight() * sz.getWidth() / (float)(1920 * 1080);
+        rate = (int)(rate * scaleFactor);
+
+        // Clamp to the MIN, MAX range.
+        return Math.max(BIT_RATE_MIN, Math.min(BIT_RATE_MAX, rate));
+    }
+
+    /**
+     * Check if the encoder and camera are able to support this size and frame rate.
+     * Assume the video compression format is AVC.
+     */
+    private boolean isSupported(Size sz, int captureRate, int encodingRate) throws Exception {
+        // Check camera capability.
+        if (!isSupportedByCamera(sz, captureRate)) {
+            return false;
+        }
+
+        // Check encode capability.
+        if (!isSupportedByAVCEncoder(sz, encodingRate)){
+            return false;
+        }
+
+        if(VERBOSE) {
+            Log.v(TAG, "Both encoder and camera support " + sz.toString() + "@" + encodingRate + "@"
+                    + getVideoBitRate(sz) / 1000 + "Kbps");
+        }
+
+        return true;
+    }
+
+    private boolean isSupportedByCamera(Size sz, int frameRate) {
+        // Check if camera can support this sz and frame rate combination.
+        StreamConfigurationMap config = mStaticInfo.
+                getValueFromKeyNonNull(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
+
+        long minDuration = config.getOutputMinFrameDuration(MediaRecorder.class, sz);
+        if (minDuration == 0) {
+            return false;
+        }
+
+        int maxFrameRate = (int) (1e9f / minDuration);
+        return maxFrameRate >= frameRate;
+    }
+
+    /**
+     * Check if encoder can support this size and frame rate combination by querying
+     * MediaCodec capability. Check is based on size and frame rate. Ignore the bit rate
+     * as the bit rates targeted in this test are well below the bit rate max value specified
+     * by AVC specification for certain level.
+     */
+    private static boolean isSupportedByAVCEncoder(Size sz, int frameRate) {
+        String mimeType = "video/avc";
+        MediaCodecInfo codecInfo = getEncoderInfo(mimeType);
+        if (codecInfo == null) {
+            return false;
+        }
+        CodecCapabilities cap = codecInfo.getCapabilitiesForType(mimeType);
+        if (cap == null) {
+            return false;
+        }
+
+        int highestLevel = 0;
+        for (CodecProfileLevel lvl : cap.profileLevels) {
+            if (lvl.level > highestLevel) {
+                highestLevel = lvl.level;
+            }
+        }
+        // Don't support anything meaningful for level 1 or 2.
+        if (highestLevel <= CodecProfileLevel.AVCLevel2) {
+            return false;
+        }
+
+        if(VERBOSE) {
+            Log.v(TAG, "The highest level supported by encoder is: " + highestLevel);
+        }
+
+        // Put bitRate here for future use.
+        int maxW, maxH, bitRate;
+        // Max encoding speed.
+        int maxMacroblocksPerSecond = 0;
+        switch(highestLevel) {
+            case CodecProfileLevel.AVCLevel21:
+                maxW = 352;
+                maxH = 576;
+                bitRate = 4000000;
+                maxMacroblocksPerSecond = 19800;
+                break;
+            case CodecProfileLevel.AVCLevel22:
+                maxW = 720;
+                maxH = 480;
+                bitRate = 4000000;
+                maxMacroblocksPerSecond = 20250;
+                break;
+            case CodecProfileLevel.AVCLevel3:
+                maxW = 720;
+                maxH = 480;
+                bitRate = 10000000;
+                maxMacroblocksPerSecond = 40500;
+                break;
+            case CodecProfileLevel.AVCLevel31:
+                maxW = 1280;
+                maxH = 720;
+                bitRate = 14000000;
+                maxMacroblocksPerSecond = 108000;
+                break;
+            case CodecProfileLevel.AVCLevel32:
+                maxW = 1280;
+                maxH = 720;
+                bitRate = 20000000;
+                maxMacroblocksPerSecond = 216000;
+                break;
+            case CodecProfileLevel.AVCLevel4:
+                maxW = 1920;
+                maxH = 1088; // It should be 1088 in terms of AVC capability.
+                bitRate = 20000000;
+                maxMacroblocksPerSecond = 245760;
+                break;
+            case CodecProfileLevel.AVCLevel41:
+                maxW = 1920;
+                maxH = 1088; // It should be 1088 in terms of AVC capability.
+                bitRate = 50000000;
+                maxMacroblocksPerSecond = 245760;
+                break;
+            case CodecProfileLevel.AVCLevel42:
+                maxW = 2048;
+                maxH = 1088; // It should be 1088 in terms of AVC capability.
+                bitRate = 50000000;
+                maxMacroblocksPerSecond = 522240;
+                break;
+            case CodecProfileLevel.AVCLevel5:
+                maxW = 3672;
+                maxH = 1536;
+                bitRate = 135000000;
+                maxMacroblocksPerSecond = 589824;
+                break;
+            case CodecProfileLevel.AVCLevel51:
+            default:
+                maxW = 4096;
+                maxH = 2304;
+                bitRate = 240000000;
+                maxMacroblocksPerSecond = 983040;
+                break;
+        }
+
+        // Check size limit.
+        if (sz.getWidth() > maxW || sz.getHeight() > maxH) {
+            Log.i(TAG, "Requested resolution " + sz.toString() + " exceeds (" +
+                    maxW + "," + maxH + ")");
+            return false;
+        }
+
+        // Check frame rate limit.
+        Size sizeInMb = new Size((sz.getWidth() + 15) / 16, (sz.getHeight() + 15) / 16);
+        int maxFps = maxMacroblocksPerSecond / (sizeInMb.getWidth() * sizeInMb.getHeight());
+        if (frameRate > maxFps) {
+            Log.i(TAG, "Requested frame rate " + frameRate + " exceeds " + maxFps);
+            return false;
+        }
+
+        return true;
+    }
+
+    private static MediaCodecInfo getEncoderInfo(String mimeType) {
+        int numCodecs = MediaCodecList.getCodecCount();
+        for (int i = 0; i < numCodecs; i++) {
+            MediaCodecInfo codecInfo = MediaCodecList.getCodecInfoAt(i);
+
+            if (!codecInfo.isEncoder()) {
+                continue;
+            }
+
+            String[] types = codecInfo.getSupportedTypes();
+            for (int j = 0; j < types.length; j++) {
+                if (types[j].equalsIgnoreCase(mimeType)) {
+                    return codecInfo;
+                }
+            }
+        }
+        return null;
+    }
+}
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/StaticMetadataTest.java b/tests/tests/hardware/src/android/hardware/camera2/cts/StaticMetadataTest.java
new file mode 100644
index 0000000..e9eaf24
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/StaticMetadataTest.java
@@ -0,0 +1,142 @@
+/*
+ * 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.CameraCharacteristics.*;
+
+import android.graphics.ImageFormat;
+import android.hardware.camera2.CameraCharacteristics;
+import android.hardware.camera2.cts.helpers.StaticMetadata;
+import android.hardware.camera2.cts.helpers.StaticMetadata.CheckLevel;
+import android.hardware.camera2.cts.testcases.Camera2AndroidTestCase;
+import android.util.Log;
+import android.util.Size;
+
+import java.util.HashMap;
+import java.util.List;
+
+/**
+ * <p>
+ * This class covers the {@link CameraCharacteristics} tests that are not
+ * covered by {@link CaptureRequestTest} and {@link CameraCharacteristicsTest}
+ * (auto-generated tests that only do the non-null checks).
+ * </p>
+ * <p>
+ * Note that most of the tests in this class don't require camera open.
+ * </p>
+ */
+public class StaticMetadataTest extends Camera2AndroidTestCase {
+    private static final String TAG = "StaticMetadataTest";
+    private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
+    private static final float MIN_FPS_FOR_FULL_DEVICE = 20.0f;
+
+    /**
+     * Test the available capability for different hardware support level devices.
+     */
+    public void testHwSupportedLevel() throws Exception {
+        for (String id : mCameraIds) {
+            initStaticMetadata(id);
+            List<Integer> availableCaps = mStaticInfo.getAvailableCapabilitiesChecked();
+
+            mCollector.expectTrue("All device must contains BACKWARD_COMPATIBLE capability",
+                    availableCaps.contains(REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE));
+
+            if (mStaticInfo.isHardwareLevelFull()) {
+                // Capability advertisement must be right.
+                mCollector.expectTrue("Full device must contains MANUAL_SENSOR capability",
+                        availableCaps.contains(REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR));
+                mCollector.expectTrue("Full device must contains MANUAL_POST_PROCESSING capability",
+                        availableCaps.contains(
+                                REQUEST_AVAILABLE_CAPABILITIES_MANUAL_POST_PROCESSING));
+
+                // Max resolution fps must be >= 20.
+                mCollector.expectTrue("Full device must support at least 20fps for max resolution",
+                        getFpsForMaxSize(id) >= MIN_FPS_FOR_FULL_DEVICE);
+
+                // Need support per frame control
+                mCollector.expectTrue("Full device must support per frame control",
+                        mStaticInfo.isPerFrameControlSupported());
+            }
+
+            // TODO: test all the keys mandatory for all capability devices.
+        }
+    }
+
+    /**
+     * Test max number of output stream reported by device
+     */
+    public void testMaxNumOutputStreams() throws Exception {
+        for (String id : mCameraIds) {
+            initStaticMetadata(id);
+            int maxNumStreamsRaw = mStaticInfo.getMaxNumOutputStreamsRawChecked();
+            int maxNumStreamsProc = mStaticInfo.getMaxNumOutputStreamsProcessedChecked();
+            int maxNumStreamsProcStall = mStaticInfo.getMaxNumOutputStreamsProcessedStallChecked();
+
+            mCollector.expectTrue("max number of raw output streams must be a non negative number",
+                    maxNumStreamsRaw >= 0);
+            mCollector.expectTrue("max number of processed (stalling) output streams must be >= 1",
+                    maxNumStreamsProcStall >= 1);
+
+            if (mStaticInfo.isHardwareLevelFull()) {
+                mCollector.expectTrue("max number of processed (non-stalling) output streams" +
+                        "must be >= 3 for FULL device",
+                        maxNumStreamsProc >= 3);
+            } else {
+                mCollector.expectTrue("max number of processed (non-stalling) output streams" +
+                        "must be >= 2 for LIMITED device",
+                        maxNumStreamsProc >= 2);
+            }
+        }
+
+    }
+
+    /**
+     * Test lens facing.
+     */
+    public void testLensFacing() throws Exception {
+        for (String id : mCameraIds) {
+            initStaticMetadata(id);
+            mStaticInfo.getLensFacingChecked();
+        }
+    }
+
+    private float getFpsForMaxSize(String cameraId) throws Exception {
+        HashMap<Size, Long> minFrameDurationMap =
+                mStaticInfo.getAvailableMinFrameDurationsForFormatChecked(ImageFormat.YUV_420_888);
+
+        Size[] sizes = CameraTestUtils.getSupportedSizeForFormat(ImageFormat.YUV_420_888,
+                cameraId, mCameraManager);
+        Size maxSize = CameraTestUtils.getMaxSize(sizes);
+        Long minDuration = minFrameDurationMap.get(maxSize);
+        if (VERBOSE) {
+            Log.v(TAG, "min frame duration for size " + maxSize + " is " + minDuration);
+        }
+        assertTrue("min duration for max size must be postive number",
+                minDuration != null && minDuration > 0);
+
+        return 1e9f / minDuration;
+    }
+
+    /**
+     * Initialize static metadata for a given camera id.
+     */
+    private void initStaticMetadata(String cameraId) throws Exception {
+        mCollector.setCameraId(cameraId);
+        mStaticInfo = new StaticMetadata(mCameraManager.getCameraCharacteristics(cameraId),
+                CheckLevel.COLLECT, /* collector */mCollector);
+    }
+}
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/StillCaptureTest.java b/tests/tests/hardware/src/android/hardware/camera2/cts/StillCaptureTest.java
new file mode 100644
index 0000000..1b915f4
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/StillCaptureTest.java
@@ -0,0 +1,1478 @@
+/*
+ * 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 android.graphics.ImageFormat;
+import android.graphics.Rect;
+import android.hardware.camera2.CameraCharacteristics;
+import android.hardware.camera2.CameraDevice;
+import android.hardware.camera2.CaptureRequest;
+import android.hardware.camera2.CaptureResult;
+import android.location.Location;
+import android.location.LocationManager;
+import android.hardware.camera2.DngCreator;
+import android.media.ImageReader;
+import android.util.Size;
+import android.hardware.camera2.cts.CameraTestUtils.SimpleCaptureListener;
+import android.hardware.camera2.cts.CameraTestUtils.SimpleImageReaderListener;
+import android.hardware.camera2.cts.helpers.Camera2Focuser;
+import android.hardware.camera2.cts.testcases.Camera2SurfaceViewTestCase;
+import android.hardware.camera2.params.MeteringRectangle;
+import android.media.ExifInterface;
+import android.media.Image;
+import android.os.Build;
+import android.os.ConditionVariable;
+import android.util.Log;
+import android.util.Range;
+import android.util.Rational;
+import android.view.Surface;
+
+import com.android.ex.camera2.blocking.BlockingSessionListener;
+import com.android.ex.camera2.exceptions.TimeoutRuntimeException;
+
+import java.io.ByteArrayOutputStream;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.List;
+
+public class StillCaptureTest extends Camera2SurfaceViewTestCase {
+    private static final String TAG = "StillCaptureTest";
+    private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
+    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+    // 60 second to accommodate the possible long exposure time.
+    private static final int EXIF_DATETIME_ERROR_MARGIN_SEC = 60;
+    private static final float EXIF_FOCAL_LENGTH_ERROR_MARGIN = 0.001f;
+    // TODO: exposure time error margin need to be scaled with exposure time.
+    private static final float EXIF_EXPOSURE_TIME_ERROR_MARGIN_SEC = 0.002f;
+    private static final float EXIF_APERTURE_ERROR_MARGIN = 0.001f;
+    private static final Location sTestLocation0 = new Location(LocationManager.GPS_PROVIDER);
+    private static final Location sTestLocation1 = new Location(LocationManager.GPS_PROVIDER);
+    private static final Location sTestLocation2 = new Location(LocationManager.NETWORK_PROVIDER);
+    static {
+        sTestLocation0.setTime(1199145600L);
+        sTestLocation0.setLatitude(37.736071);
+        sTestLocation0.setLongitude(-122.441983);
+        sTestLocation0.setAltitude(21.0);
+
+        sTestLocation1.setTime(1199145601L);
+        sTestLocation1.setLatitude(0.736071);
+        sTestLocation1.setLongitude(0.441983);
+        sTestLocation1.setAltitude(1.0);
+
+        sTestLocation2.setTime(1199145602L);
+        sTestLocation2.setLatitude(-89.736071);
+        sTestLocation2.setLongitude(-179.441983);
+        sTestLocation2.setAltitude(100000.0);
+    }
+    // Exif test data vectors.
+    private static final ExifTestData[] EXIF_TEST_DATA = {
+            new ExifTestData(
+                    /*gpsLocation*/ sTestLocation0,
+                    /* orientation */90,
+                    /* jpgQuality */(byte) 80,
+                    /* thumbQuality */(byte) 75),
+            new ExifTestData(
+                    /*gpsLocation*/ sTestLocation1,
+                    /* orientation */180,
+                    /* jpgQuality */(byte) 90,
+                    /* thumbQuality */(byte) 85),
+            new ExifTestData(
+                    /*gpsLocation*/ sTestLocation2,
+                    /* orientation */270,
+                    /* jpgQuality */(byte) 100,
+                    /* thumbQuality */(byte) 100)
+    };
+
+    // 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 static final int EXIF_DATETIME_LENGTH = 19;
+    private static final int MAX_REGIONS_AE_INDEX = 0;
+    private static final int MAX_REGIONS_AWB_INDEX = 1;
+    private static final int MAX_REGIONS_AF_INDEX = 2;
+    private static final int WAIT_FOR_FOCUS_DONE_TIMEOUT_MS = 3000;
+    private static final double AE_COMPENSATION_ERROR_TOLERANCE = 0.2;
+    private static final int NUM_FRAMES_WAITED = 30;
+    // 5 percent error margin for resulting metering regions
+    private static final float METERING_REGION_ERROR_PERCENT_DELTA = 0.05f;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        super.tearDown();
+    }
+
+    /**
+     * Test JPEG capture exif fields for each camera.
+     */
+    public void testJpegExif() throws Exception {
+        for (int i = 0; i < mCameraIds.length; i++) {
+            try {
+                Log.i(TAG, "Testing JPEG exif for Camera " + mCameraIds[i]);
+                openDevice(mCameraIds[i]);
+
+                jpegExifTestByCamera();
+            } finally {
+                closeDevice();
+                closeImageReader();
+            }
+        }
+    }
+
+    /**
+     * Test normal still capture sequence.
+     * <p>
+     * Preview and and jpeg output streams are configured. Max still capture
+     * size is used for jpeg capture. The sequence of still capture being test
+     * is: start preview, auto focus, precapture metering (if AE is not
+     * converged), then capture jpeg. The AWB and AE are in auto modes. AF mode
+     * is CONTINUOUS_PICTURE.
+     * </p>
+     */
+    public void testTakePicture() throws Exception{
+        for (String id : mCameraIds) {
+            try {
+                Log.i(TAG, "Testing touch for focus for Camera " + id);
+                openDevice(id);
+
+                takePictureTestByCamera(/*aeRegions*/null, /*awbRegions*/null, /*afRegions*/null);
+            } finally {
+                closeDevice();
+                closeImageReader();
+            }
+        }
+    }
+
+    /**
+     * Test basic Raw capture. Raw buffer avaiablility is checked, but raw buffer data is not.
+     */
+    public void testBasicRawCapture()  throws Exception {
+       for (int i = 0; i < mCameraIds.length; i++) {
+           try {
+               Log.i(TAG, "Testing raw capture for Camera " + mCameraIds[i]);
+               openDevice(mCameraIds[i]);
+
+               rawCaptureTestByCamera();
+           } finally {
+               closeDevice();
+               closeImageReader();
+           }
+       }
+    }
+
+
+    /**
+     * Test the full raw capture use case.
+     *
+     * This includes:
+     * - Configuring the camera with a preview, jpeg, and raw output stream.
+     * - Running preview until AE/AF can settle.
+     * - Capturing with a request targeting all three output streams.
+     */
+    public void testFullRawCapture() throws Exception {
+        for (int i = 0; i < mCameraIds.length; i++) {
+            try {
+                Log.i(TAG, "Testing raw capture for Camera " + mCameraIds[i]);
+                openDevice(mCameraIds[i]);
+                if (!mStaticInfo.isCapabilitySupported(
+                        CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_RAW)) {
+                    Log.i(TAG, "RAW capability is not supported in camera " + mCameraIds[i] +
+                            ". Skip the test.");
+                    continue;
+                }
+
+                fullRawCaptureTestByCamera();
+            } finally {
+                closeDevice();
+                closeImageReader();
+            }
+        }
+    }
+    /**
+     * Test touch for focus.
+     * <p>
+     * AF is in CAF mode when preview is started, test uses several pre-selected
+     * regions to simulate touches. Active scan is triggered to make sure the AF
+     * converges in reasonable time.
+     * </p>
+     */
+    public void testTouchForFocus() throws Exception {
+        for (String id : mCameraIds) {
+            try {
+                Log.i(TAG, "Testing touch for focus for Camera " + id);
+                openDevice(id);
+                int maxAfRegions = mStaticInfo.getAfMaxRegionsChecked();
+                if (!(mStaticInfo.hasFocuser() && maxAfRegions > 0)) {
+                    continue;
+                }
+
+                touchForFocusTestByCamera();
+            } finally {
+                closeDevice();
+                closeImageReader();
+            }
+        }
+    }
+
+    /**
+     * Test all combination of available preview sizes and still sizes.
+     * <p>
+     * For each still capture, Only the jpeg buffer is validated, capture
+     * result validation is covered by {@link #jpegExifTestByCamera} test.
+     * </p>
+     */
+    public void testStillPreviewCombination() throws Exception {
+        for (String id : mCameraIds) {
+            try {
+                Log.i(TAG, "Testing Still preview capture combination for Camera " + id);
+                openDevice(id);
+
+                previewStillCombinationTestByCamera();
+            } finally {
+                closeDevice();
+                closeImageReader();
+            }
+        }
+    }
+
+    /**
+     * Test AE compensation.
+     * <p>
+     * For each integer EV compensation setting: retrieve the exposure value (exposure time *
+     * sensitivity) with or without compensation, verify if the exposure value is legal (conformed
+     * to what static info has) and the ratio between two exposure values matches EV compensation
+     * setting. Also test for the behavior that exposure settings should be changed when AE
+     * compensation settings is changed, even when AE lock is ON.
+     * </p>
+     */
+    public void testAeCompensation() throws Exception {
+        for (String id : mCameraIds) {
+            try {
+                Log.i(TAG, "Testing AE compensation for Camera " + id);
+                openDevice(id);
+                aeCompensationTestByCamera();
+            } finally {
+                closeDevice();
+                closeImageReader();
+            }
+        }
+    }
+
+    /**
+     * Test Ae region for still capture.
+     */
+    public void testAeRegions() throws Exception {
+        for (String id : mCameraIds) {
+            try {
+                Log.i(TAG, "Testing AE regions for Camera " + id);
+                openDevice(id);
+
+                boolean aeRegionsSupported = isRegionsSupportedFor3A(MAX_REGIONS_AE_INDEX);
+                if (!aeRegionsSupported) {
+                    continue;
+                }
+
+                ArrayList<MeteringRectangle[]> aeRegionTestCases = get3ARegionTestCasesForCamera();
+                for (MeteringRectangle[] aeRegions : aeRegionTestCases) {
+                    takePictureTestByCamera(aeRegions, /*awbRegions*/null, /*afRegions*/null);
+                }
+            } finally {
+                closeDevice();
+                closeImageReader();
+            }
+        }
+    }
+
+    /**
+     * Test AWB region for still capture.
+     */
+    public void testAwbRegions() throws Exception {
+        for (String id : mCameraIds) {
+            try {
+                Log.i(TAG, "Testing AE regions for Camera " + id);
+                openDevice(id);
+
+                boolean awbRegionsSupported = isRegionsSupportedFor3A(MAX_REGIONS_AWB_INDEX);
+                if (!awbRegionsSupported) {
+                    continue;
+                }
+
+                ArrayList<MeteringRectangle[]> awbRegionTestCases = get3ARegionTestCasesForCamera();
+                for (MeteringRectangle[] awbRegions : awbRegionTestCases) {
+                    takePictureTestByCamera(/*aeRegions*/null, awbRegions, /*afRegions*/null);
+                }
+            } finally {
+                closeDevice();
+                closeImageReader();
+            }
+        }
+    }
+
+    /**
+     * Test Af region for still capture.
+     */
+    public void testAfRegions() throws Exception {
+        for (String id : mCameraIds) {
+            try {
+                Log.i(TAG, "Testing AF regions for Camera " + id);
+                openDevice(id);
+
+                boolean afRegionsSupported = isRegionsSupportedFor3A(MAX_REGIONS_AF_INDEX);
+                if (!afRegionsSupported) {
+                    continue;
+                }
+
+                ArrayList<MeteringRectangle[]> afRegionTestCases = get3ARegionTestCasesForCamera();
+                for (MeteringRectangle[] afRegions : afRegionTestCases) {
+                    takePictureTestByCamera(/*aeRegions*/null, /*awbRegions*/null, afRegions);
+                }
+            } finally {
+                closeDevice();
+                closeImageReader();
+            }
+        }
+    }
+
+    /**
+     * Test preview is still running after a still request
+     */
+    public void testPreviewPersistence() throws Exception {
+        for (String id : mCameraIds) {
+            try {
+                Log.i(TAG, "Testing preview persistence for Camera " + id);
+                openDevice(id);
+                previewPersistenceTestByCamera();
+            } finally {
+                closeDevice();
+                closeImageReader();
+            }
+        }
+    }
+
+    /**
+     * Start preview,take a picture and test preview is still running after snapshot
+     */
+    private void previewPersistenceTestByCamera() throws Exception {
+        Size maxStillSz = mOrderedStillSizes.get(0);
+        Size maxPreviewSz = mOrderedPreviewSizes.get(0);
+
+        SimpleCaptureListener resultListener = new SimpleCaptureListener();
+        SimpleCaptureListener stillResultListener = new SimpleCaptureListener();
+        SimpleImageReaderListener imageListener = new SimpleImageReaderListener();
+        CaptureRequest.Builder previewRequest =
+                mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
+        CaptureRequest.Builder stillRequest =
+                mCamera.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
+        prepareStillCaptureAndStartPreview(previewRequest, stillRequest, maxPreviewSz,
+                maxStillSz, resultListener, imageListener);
+
+        // make sure preview is actually running
+        waitForNumResults(resultListener, NUM_FRAMES_WAITED);
+
+        // take a picture
+        CaptureRequest request = stillRequest.build();
+        mSession.capture(request, stillResultListener, mHandler);
+        stillResultListener.getCaptureResultForRequest(request,
+                WAIT_FOR_RESULT_TIMEOUT_MS);
+
+        // validate image
+        Image image = imageListener.getImage(CAPTURE_IMAGE_TIMEOUT_MS);
+        validateJpegCapture(image, maxStillSz);
+
+        // make sure preview is still running after still capture
+        waitForNumResults(resultListener, NUM_FRAMES_WAITED);
+
+        stopPreview();
+        return;
+    }
+
+    /**
+     * Take a picture for a given set of 3A regions for a particular camera.
+     * <p>
+     * Before take a still capture, it triggers an auto focus and lock it first,
+     * then wait for AWB to converge and lock it, then trigger a precapture
+     * metering sequence and wait for AE converged. After capture is received, the
+     * capture result and image are validated.
+     * </p>
+     *
+     * @param aeRegions AE regions for this capture
+     * @param awbRegions AWB regions for this capture
+     * @param afRegions AF regions for this capture
+     */
+    private void takePictureTestByCamera(
+            MeteringRectangle[] aeRegions, MeteringRectangle[] awbRegions,
+            MeteringRectangle[] afRegions) throws Exception {
+
+        boolean hasFocuser = mStaticInfo.hasFocuser();
+
+        Size maxStillSz = mOrderedStillSizes.get(0);
+        Size maxPreviewSz = mOrderedPreviewSizes.get(0);
+        CaptureResult result;
+        SimpleCaptureListener resultListener = new SimpleCaptureListener();
+        SimpleImageReaderListener imageListener = new SimpleImageReaderListener();
+        CaptureRequest.Builder previewRequest =
+                mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
+        CaptureRequest.Builder stillRequest =
+                mCamera.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
+        prepareStillCaptureAndStartPreview(previewRequest, stillRequest, maxPreviewSz,
+                maxStillSz, resultListener, imageListener);
+
+        // Set AE mode to ON_AUTO_FLASH if flash is available.
+        if (mStaticInfo.hasFlash()) {
+            previewRequest.set(CaptureRequest.CONTROL_AE_MODE,
+                    CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH);
+            stillRequest.set(CaptureRequest.CONTROL_AE_MODE,
+                    CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH);
+        }
+
+        Camera2Focuser focuser = null;
+        /**
+         * Step 1: trigger an auto focus run, and wait for AF locked.
+         */
+        boolean canSetAfRegion = hasFocuser && (afRegions != null) &&
+                isRegionsSupportedFor3A(MAX_REGIONS_AF_INDEX);
+        if (hasFocuser) {
+            SimpleAutoFocusListener afListener = new SimpleAutoFocusListener();
+            focuser = new Camera2Focuser(mCamera, mSession, mPreviewSurface, afListener,
+                    mStaticInfo.getCharacteristics(), mHandler);
+            if (canSetAfRegion) {
+                stillRequest.set(CaptureRequest.CONTROL_AF_REGIONS, afRegions);
+            }
+            focuser.startAutoFocus(afRegions);
+            afListener.waitForAutoFocusDone(WAIT_FOR_FOCUS_DONE_TIMEOUT_MS);
+        }
+
+        /**
+         * Have to get the current AF mode to be used for other 3A repeating
+         * request, otherwise, the new AF mode in AE/AWB request could be
+         * different with existing repeating requests being sent by focuser,
+         * then it could make AF unlocked too early. Beside that, for still
+         * capture, AF mode must not be different with the one in current
+         * repeating request, otherwise, the still capture itself would trigger
+         * an AF mode change, and the AF lock would be lost for this capture.
+         */
+        int currentAfMode = CaptureRequest.CONTROL_AF_MODE_OFF;
+        if (hasFocuser) {
+            currentAfMode = focuser.getCurrentAfMode();
+        }
+        previewRequest.set(CaptureRequest.CONTROL_AF_MODE, currentAfMode);
+        stillRequest.set(CaptureRequest.CONTROL_AF_MODE, currentAfMode);
+
+        /**
+         * Step 2: AF is already locked, wait for AWB converged, then lock it.
+         */
+        resultListener = new SimpleCaptureListener();
+        boolean canSetAwbRegion =
+                (awbRegions != null) && isRegionsSupportedFor3A(MAX_REGIONS_AWB_INDEX);
+        if (canSetAwbRegion) {
+            previewRequest.set(CaptureRequest.CONTROL_AWB_REGIONS, awbRegions);
+            stillRequest.set(CaptureRequest.CONTROL_AWB_REGIONS, awbRegions);
+        }
+        mSession.setRepeatingRequest(previewRequest.build(), resultListener, mHandler);
+        if (mStaticInfo.isHardwareLevelLimitedOrBetter()) {
+            waitForResultValue(resultListener, CaptureResult.CONTROL_AWB_STATE,
+                    CaptureResult.CONTROL_AWB_STATE_CONVERGED, NUM_RESULTS_WAIT_TIMEOUT);
+        } else {
+            // LEGACY Devices don't have the AWB_STATE reported in results, so just wait
+            waitForSettingsApplied(resultListener, NUM_FRAMES_WAITED_FOR_UNKNOWN_LATENCY);
+        }
+        previewRequest.set(CaptureRequest.CONTROL_AWB_LOCK, true);
+        mSession.setRepeatingRequest(previewRequest.build(), resultListener, mHandler);
+        // Validate the next result immediately for region and mode.
+        result = resultListener.getCaptureResult(WAIT_FOR_RESULT_TIMEOUT_MS);
+        mCollector.expectEquals("AWB mode in result and request should be same",
+                previewRequest.get(CaptureRequest.CONTROL_AWB_MODE),
+                result.get(CaptureResult.CONTROL_AWB_MODE));
+        if (canSetAwbRegion) {
+            MeteringRectangle[] resultAwbRegions =
+                    getValueNotNull(result, CaptureResult.CONTROL_AWB_REGIONS);
+            mCollector.expectEquals("AWB regions in result and request should be same",
+                    awbRegions, resultAwbRegions);
+        }
+
+        /**
+         * Step 3: trigger an AE precapture metering sequence and wait for AE converged.
+         */
+        resultListener = new SimpleCaptureListener();
+        boolean canSetAeRegion =
+                (aeRegions != null) && isRegionsSupportedFor3A(MAX_REGIONS_AE_INDEX);
+        if (canSetAeRegion) {
+            previewRequest.set(CaptureRequest.CONTROL_AE_REGIONS, aeRegions);
+            stillRequest.set(CaptureRequest.CONTROL_AE_REGIONS, aeRegions);
+        }
+        mSession.setRepeatingRequest(previewRequest.build(), resultListener, mHandler);
+        previewRequest.set(CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER,
+                CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER_START);
+        mSession.capture(previewRequest.build(), resultListener, mHandler);
+        waitForAeStable(resultListener, NUM_FRAMES_WAITED_FOR_UNKNOWN_LATENCY);
+
+        // Validate the next result immediately for region and mode.
+        result = resultListener.getCaptureResult(WAIT_FOR_RESULT_TIMEOUT_MS);
+        mCollector.expectEquals("AE mode in result and request should be same",
+                previewRequest.get(CaptureRequest.CONTROL_AE_MODE),
+                result.get(CaptureResult.CONTROL_AE_MODE));
+        if (canSetAeRegion) {
+            MeteringRectangle[] resultAeRegions =
+                    getValueNotNull(result, CaptureResult.CONTROL_AE_REGIONS);
+
+            mCollector.expectMeteringRegionsAreSimilar(
+                    "AE regions in result and request should be similar",
+                    aeRegions,
+                    resultAeRegions,
+                    METERING_REGION_ERROR_PERCENT_DELTA);
+        }
+
+        /**
+         * Step 4: take a picture when all 3A are in good state.
+         */
+        resultListener = new SimpleCaptureListener();
+        CaptureRequest request = stillRequest.build();
+        mSession.capture(request, resultListener, mHandler);
+        // Validate the next result immediately for region and mode.
+        result = resultListener.getCaptureResultForRequest(request, WAIT_FOR_RESULT_TIMEOUT_MS);
+        mCollector.expectEquals("AF mode in result and request should be same",
+                stillRequest.get(CaptureRequest.CONTROL_AF_MODE),
+                result.get(CaptureResult.CONTROL_AF_MODE));
+        if (canSetAfRegion) {
+            MeteringRectangle[] resultAfRegions =
+                    getValueNotNull(result, CaptureResult.CONTROL_AF_REGIONS);
+            mCollector.expectEquals("AF regions in result and request should be same",
+                    afRegions, resultAfRegions);
+        }
+
+        if (hasFocuser) {
+            // Unlock auto focus.
+            focuser.cancelAutoFocus();
+        }
+
+        // validate image
+        Image image = imageListener.getImage(CAPTURE_IMAGE_TIMEOUT_MS);
+        validateJpegCapture(image, maxStillSz);
+
+        stopPreview();
+    }
+
+    /**
+     * Test touch region for focus by camera.
+     */
+    private void touchForFocusTestByCamera() throws Exception {
+        SimpleCaptureListener listener = new SimpleCaptureListener();
+        CaptureRequest.Builder requestBuilder =
+                mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
+        Size maxPreviewSz = mOrderedPreviewSizes.get(0);
+        startPreview(requestBuilder, maxPreviewSz, listener);
+
+        SimpleAutoFocusListener afListener = new SimpleAutoFocusListener();
+        Camera2Focuser focuser = new Camera2Focuser(mCamera, mSession, mPreviewSurface, afListener,
+                mStaticInfo.getCharacteristics(), mHandler);
+        ArrayList<MeteringRectangle[]> testAfRegions = get3ARegionTestCasesForCamera();
+
+        for (MeteringRectangle[] afRegions : testAfRegions) {
+            focuser.touchForAutoFocus(afRegions);
+            afListener.waitForAutoFocusDone(WAIT_FOR_FOCUS_DONE_TIMEOUT_MS);
+            focuser.cancelAutoFocus();
+        }
+    }
+
+    private void previewStillCombinationTestByCamera() throws Exception {
+        SimpleCaptureListener resultListener = new SimpleCaptureListener();
+        SimpleImageReaderListener imageListener = new SimpleImageReaderListener();
+
+        for (Size stillSz : mOrderedStillSizes)
+            for (Size previewSz : mOrderedPreviewSizes) {
+                if (VERBOSE) {
+                    Log.v(TAG, "Testing JPEG capture size " + stillSz.toString()
+                            + " with preview size " + previewSz.toString() + " for camera "
+                            + mCamera.getId());
+                }
+                CaptureRequest.Builder previewRequest =
+                        mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
+                CaptureRequest.Builder stillRequest =
+                        mCamera.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
+                prepareStillCaptureAndStartPreview(previewRequest, stillRequest, previewSz,
+                        stillSz, resultListener, imageListener);
+                mSession.capture(stillRequest.build(), resultListener, mHandler);
+                Image image = imageListener.getImage(CAPTURE_IMAGE_TIMEOUT_MS);
+                validateJpegCapture(image, stillSz);
+                // stopPreview must be called here to make sure next time a preview stream
+                // is created with new size.
+                stopPreview();
+            }
+    }
+
+    /**
+     * Basic raw capture test for each camera.
+     */
+    private void rawCaptureTestByCamera() throws Exception {
+        Size maxPreviewSz = mOrderedPreviewSizes.get(0);
+        Size[] rawSizes = mStaticInfo.getRawOutputSizesChecked();
+        for (Size size : rawSizes) {
+            if (VERBOSE) {
+                Log.v(TAG, "Testing Raw capture with size " + size.toString()
+                        + ", preview size " + maxPreviewSz);
+            }
+
+            // Prepare raw capture and start preview.
+            CaptureRequest.Builder previewBuilder =
+                    mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
+            CaptureRequest.Builder rawBuilder =
+                    mCamera.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
+            SimpleCaptureListener resultListener = new SimpleCaptureListener();
+            SimpleImageReaderListener imageListener = new SimpleImageReaderListener();
+            prepareRawCaptureAndStartPreview(previewBuilder, rawBuilder, maxPreviewSz, size,
+                    resultListener, imageListener);
+
+            CaptureRequest rawRequest = rawBuilder.build();
+            mSession.capture(rawRequest, resultListener, mHandler);
+
+            Image image = imageListener.getImage(CAPTURE_IMAGE_TIMEOUT_MS);
+            validateRaw16Image(image, size);
+            if (DEBUG) {
+                byte[] rawBuffer = getDataFromImage(image);
+                String rawFileName =
+                        DEBUG_FILE_NAME_BASE + "/test" + "_" + size.toString() +
+                        "_cam" + mCamera.getId() +  ".raw16";
+                Log.d(TAG, "Dump raw file into " + rawFileName);
+                dumpFile(rawFileName, rawBuffer);
+            }
+
+            verifyRawCaptureResult(rawRequest, resultListener);
+            stopPreview();
+        }
+    }
+
+    private void fullRawCaptureTestByCamera() throws Exception {
+        Size maxPreviewSz = mOrderedPreviewSizes.get(0);
+        Size maxStillSz = mOrderedStillSizes.get(0);
+        Size[] rawSizes = mStaticInfo.getRawOutputSizesChecked();
+        for (Size size : rawSizes) {
+            if (VERBOSE) {
+                Log.v(TAG, "Testing multi capture with size " + size.toString()
+                        + ", preview size " + maxPreviewSz);
+            }
+
+            // Prepare raw capture and start preview.
+            CaptureRequest.Builder previewBuilder =
+                    mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
+            CaptureRequest.Builder multiBuilder =
+                    mCamera.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
+
+            SimpleCaptureListener resultListener = new SimpleCaptureListener();
+            SimpleImageReaderListener jpegListener = new SimpleImageReaderListener();
+            SimpleImageReaderListener rawListener = new SimpleImageReaderListener();
+
+            updatePreviewSurface(maxPreviewSz);
+
+            ImageReader rawReader = null;
+            ImageReader jpegReader = null;
+            try {
+
+                // Create ImageReaders.
+                rawReader = makeImageReader(size,
+                        ImageFormat.RAW_SENSOR, MAX_READER_IMAGES, rawListener, mHandler);
+                jpegReader = makeImageReader(maxStillSz,
+                        ImageFormat.JPEG, MAX_READER_IMAGES, jpegListener, mHandler);
+
+                // Configure output streams with preview and jpeg streams.
+                List<Surface> outputSurfaces = new ArrayList<Surface>();
+                outputSurfaces.add(rawReader.getSurface());
+                outputSurfaces.add(jpegReader.getSurface());
+                outputSurfaces.add(mPreviewSurface);
+                mSessionListener = new BlockingSessionListener();
+                mSession = configureCameraSession(mCamera, outputSurfaces,
+                        mSessionListener, mHandler);
+
+                // Configure the requests.
+                previewBuilder.addTarget(mPreviewSurface);
+                multiBuilder.addTarget(mPreviewSurface);
+                multiBuilder.addTarget(rawReader.getSurface());
+                multiBuilder.addTarget(jpegReader.getSurface());
+
+                // Start preview.
+                mSession.setRepeatingRequest(previewBuilder.build(), null, mHandler);
+
+                // Poor man's 3A, wait 2 seconds for AE/AF (if any) to settle.
+                // TODO: Do proper 3A trigger and lock (see testTakePictureTest).
+                Thread.sleep(3000);
+
+                multiBuilder.set(CaptureRequest.STATISTICS_LENS_SHADING_MAP_MODE,
+                        CaptureRequest.STATISTICS_LENS_SHADING_MAP_MODE_ON);
+                CaptureRequest multiRequest = multiBuilder.build();
+
+                mSession.capture(multiRequest, resultListener, mHandler);
+
+                CaptureResult result = resultListener.getCaptureResultForRequest(multiRequest,
+                        NUM_RESULTS_WAIT_TIMEOUT);
+                Image jpegImage = jpegListener.getImage(CAPTURE_IMAGE_TIMEOUT_MS);
+                basicValidateJpegImage(jpegImage, maxStillSz);
+                Image rawImage = rawListener.getImage(CAPTURE_IMAGE_TIMEOUT_MS);
+                validateRaw16Image(rawImage, size);
+
+
+                ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+                try (DngCreator dngCreator =
+                        new DngCreator(mStaticInfo.getCharacteristics(), result)) {
+                    dngCreator.writeImage(outputStream, rawImage);
+                }
+
+                if (DEBUG) {
+                    byte[] rawBuffer = outputStream.toByteArray();
+                    String rawFileName =
+                            DEBUG_FILE_NAME_BASE + "/raw16_" + TAG + size.toString() +
+                                    "_cam_" + mCamera.getId() + ".dng";
+                    Log.d(TAG, "Dump raw file into " + rawFileName);
+                    dumpFile(rawFileName, rawBuffer);
+
+                    byte[] jpegBuffer = getDataFromImage(jpegImage);
+                    String jpegFileName =
+                            DEBUG_FILE_NAME_BASE + "/jpeg_" + TAG + size.toString() +
+                                    "_cam_" + mCamera.getId() + ".jpg";
+                    Log.d(TAG, "Dump jpeg file into " + rawFileName);
+                    dumpFile(jpegFileName, jpegBuffer);
+                }
+
+                stopPreview();
+            } finally {
+                CameraTestUtils.closeImageReader(rawReader);
+                CameraTestUtils.closeImageReader(jpegReader);
+                rawReader = null;
+                jpegReader = null;
+            }
+        }
+    }
+
+    private void verifyRawCaptureResult(CaptureRequest rawRequest,
+            SimpleCaptureListener resultListener) {
+        // TODO: validate DNG metadata tags.
+    }
+
+    private static boolean areGpsFieldsEqual(Location a, Location b) {
+        if (a == null || b == null) {
+            return false;
+        }
+
+        return a.getTime() == b.getTime() && a.getLatitude() == b.getLatitude() &&
+                a.getLongitude() == b.getLongitude() && a.getAltitude() == b.getAltitude() &&
+                a.getProvider() == b.getProvider();
+    }
+    /**
+     * Issue a Jpeg capture and validate the exif information.
+     * <p>
+     * TODO: Differentiate full and limited device, some of the checks rely on
+     * per frame control and synchronization, most of them don't.
+     * </p>
+     */
+    private void jpegExifTestByCamera() throws Exception {
+        Size maxPreviewSz = mOrderedPreviewSizes.get(0);
+        Size maxStillSz = mOrderedStillSizes.get(0);
+        if (VERBOSE) {
+            Log.v(TAG, "Testing JPEG exif with jpeg size " + maxStillSz.toString()
+                    + ", preview size " + maxPreviewSz);
+        }
+
+        // prepare capture and start preview.
+        CaptureRequest.Builder previewBuilder =
+                mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
+        CaptureRequest.Builder stillBuilder =
+                mCamera.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
+        SimpleCaptureListener resultListener = new SimpleCaptureListener();
+        SimpleImageReaderListener imageListener = new SimpleImageReaderListener();
+        prepareStillCaptureAndStartPreview(previewBuilder, stillBuilder, maxPreviewSz, maxStillSz,
+                resultListener, imageListener);
+
+        // Set the jpeg keys, then issue a capture
+        Size[] thumbnailSizes = mStaticInfo.getAvailableThumbnailSizesChecked();
+        Size maxThumbnailSize = thumbnailSizes[thumbnailSizes.length - 1];
+        Size[] testThumbnailSizes = new Size[EXIF_TEST_DATA.length];
+        Arrays.fill(testThumbnailSizes, maxThumbnailSize);
+        // Make sure thumbnail size (0, 0) is covered.
+        testThumbnailSizes[0] = new Size(0, 0);
+
+        for (int i = 0; i < EXIF_TEST_DATA.length; i++) {
+            /**
+             * Capture multiple shots.
+             *
+             * Verify that:
+             * - Capture request get values are same as were set.
+             * - capture result's exif data is the same as was set by
+             *   the capture request.
+             * - new tags in the result set by the camera service are
+             *   present and semantically correct.
+             */
+            stillBuilder.set(CaptureRequest.JPEG_THUMBNAIL_SIZE, testThumbnailSizes[i]);
+            stillBuilder.set(CaptureRequest.JPEG_GPS_LOCATION, EXIF_TEST_DATA[i].gpsLocation);
+            stillBuilder.set(CaptureRequest.JPEG_ORIENTATION, EXIF_TEST_DATA[i].jpegOrientation);
+            stillBuilder.set(CaptureRequest.JPEG_QUALITY, EXIF_TEST_DATA[i].jpegQuality);
+            stillBuilder.set(CaptureRequest.JPEG_THUMBNAIL_QUALITY,
+                    EXIF_TEST_DATA[i].thumbnailQuality);
+
+            // Validate request set and get.
+            mCollector.expectEquals("JPEG thumbnail size request set and get should match",
+                    testThumbnailSizes[i],
+                    stillBuilder.get(CaptureRequest.JPEG_THUMBNAIL_SIZE));
+            mCollector.expectTrue("GPS locations request set and get should match.",
+                    areGpsFieldsEqual(EXIF_TEST_DATA[i].gpsLocation,
+                            stillBuilder.get(CaptureRequest.JPEG_GPS_LOCATION)));
+            mCollector.expectEquals("JPEG orientation request set and get should match",
+                    EXIF_TEST_DATA[i].jpegOrientation,
+                    stillBuilder.get(CaptureRequest.JPEG_ORIENTATION));
+            mCollector.expectEquals("JPEG quality request set and get should match",
+                    EXIF_TEST_DATA[i].jpegQuality, stillBuilder.get(CaptureRequest.JPEG_QUALITY));
+            mCollector.expectEquals("JPEG thumbnail quality request set and get should match",
+                    EXIF_TEST_DATA[i].thumbnailQuality,
+                    stillBuilder.get(CaptureRequest.JPEG_THUMBNAIL_QUALITY));
+
+            // Capture a jpeg image.
+            CaptureRequest request = stillBuilder.build();
+            mSession.capture(request, resultListener, mHandler);
+            CaptureResult stillResult =
+                    resultListener.getCaptureResultForRequest(request, NUM_RESULTS_WAIT_TIMEOUT);
+            Image image = imageListener.getImage(CAPTURE_IMAGE_TIMEOUT_MS);
+            basicValidateJpegImage(image, maxStillSz);
+
+            byte[] jpegBuffer = getDataFromImage(image);
+            // Have to dump into a file to be able to use ExifInterface
+            String jpegFileName =
+                    DEBUG_FILE_NAME_BASE + "/Camera_" + mCamera.getId() + "_test.jpeg";
+            dumpFile(jpegFileName, jpegBuffer);
+            ExifInterface exif = new ExifInterface(jpegFileName);
+
+            if (testThumbnailSizes[i].equals(new Size(0,0))) {
+                mCollector.expectTrue(
+                        "Jpeg shouldn't have thumbnail when thumbnail size is (0, 0)",
+                        !exif.hasThumbnail());
+            } else {
+                mCollector.expectTrue(
+                        "Jpeg must have thumbnail for thumbnail size " + testThumbnailSizes[i],
+                        exif.hasThumbnail());
+            }
+
+            // Validate capture result vs. request
+            mCollector.expectEquals("JPEG thumbnail size result and request should match",
+                    testThumbnailSizes[i],
+                    stillResult.get(CaptureResult.JPEG_THUMBNAIL_SIZE));
+            if (mCollector.expectKeyValueNotNull(stillResult, CaptureResult.JPEG_GPS_LOCATION) !=
+                    null) {
+                mCollector.expectTrue("GPS location result and request should match.",
+                        areGpsFieldsEqual(EXIF_TEST_DATA[i].gpsLocation,
+                                stillResult.get(CaptureResult.JPEG_GPS_LOCATION)));
+            }
+            mCollector.expectEquals("JPEG orientation result and request should match",
+                    EXIF_TEST_DATA[i].jpegOrientation,
+                    stillResult.get(CaptureResult.JPEG_ORIENTATION));
+            mCollector.expectEquals("JPEG quality result and request should match",
+                    EXIF_TEST_DATA[i].jpegQuality, stillResult.get(CaptureResult.JPEG_QUALITY));
+            mCollector.expectEquals("JPEG thumbnail quality result and request should match",
+                    EXIF_TEST_DATA[i].thumbnailQuality,
+                    stillResult.get(CaptureResult.JPEG_THUMBNAIL_QUALITY));
+
+            // Validate other exif tags.
+            jpegTestExifExtraTags(exif, maxStillSz, stillResult);
+        }
+    }
+
+    private void jpegTestExifExtraTags(ExifInterface exif, Size jpegSize, CaptureResult result)
+            throws ParseException {
+        /**
+         * TAG_IMAGE_WIDTH and TAG_IMAGE_LENGTH and TAG_ORIENTATION.
+         * Orientation and exif width/height need to be tested carefully, two cases:
+         *
+         * 1. Device rotate the image buffer physically, then exif width/height may not match
+         * the requested still capture size, we need swap them to check.
+         *
+         * 2. Device use the exif tag to record the image orientation, it doesn't rotate
+         * the jpeg image buffer itself. In this case, the exif width/height should always match
+         * the requested still capture size, and the exif orientation should always match the
+         * requested orientation.
+         *
+         */
+        int exifWidth = exif.getAttributeInt(ExifInterface.TAG_IMAGE_WIDTH, /*defaultValue*/0);
+        int exifHeight = exif.getAttributeInt(ExifInterface.TAG_IMAGE_LENGTH, /*defaultValue*/0);
+        Size exifSize = new Size(exifWidth, exifHeight);
+        // Orientation could be missing, which is ok, default to 0.
+        int exifOrientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION,
+                /*defaultValue*/-1);
+        // Get requested orientation from result, because they should be same.
+        if (mCollector.expectKeyValueNotNull(result, CaptureResult.JPEG_ORIENTATION) != null) {
+            int requestedOrientation = result.get(CaptureResult.JPEG_ORIENTATION);
+            final int ORIENTATION_MIN = ExifInterface.ORIENTATION_UNDEFINED;
+            final int ORIENTATION_MAX = ExifInterface.ORIENTATION_ROTATE_270;
+            boolean orientationValid = mCollector.expectTrue(String.format(
+                    "Exif orientation must be in range of [%d, %d]",
+                    ORIENTATION_MIN, ORIENTATION_MAX),
+                    exifOrientation >= ORIENTATION_MIN && exifOrientation <= ORIENTATION_MAX);
+            if (orientationValid) {
+                /**
+                 * Device captured image doesn't respect the requested orientation,
+                 * which means it rotates the image buffer physically. Then we
+                 * should swap the exif width/height accordingly to compare.
+                 */
+                boolean deviceRotatedImage = exifOrientation == ExifInterface.ORIENTATION_UNDEFINED;
+
+                if (deviceRotatedImage) {
+                    // Case 1.
+                    boolean needSwap = (requestedOrientation % 180 == 90);
+                    if (needSwap) {
+                        exifSize = new Size(exifHeight, exifWidth);
+                    }
+                } else {
+                    // Case 2.
+                    mCollector.expectEquals("Exif orientaiton should match requested orientation",
+                            requestedOrientation, getExifOrientationInDegress(exifOrientation));
+                }
+            }
+        }
+
+        /**
+         * Ideally, need check exifSize == jpegSize == actual buffer size. But
+         * jpegSize == jpeg decode bounds size(from jpeg jpeg frame
+         * header, not exif) was validated in ImageReaderTest, no need to
+         * validate again here.
+         */
+        mCollector.expectEquals("Exif size should match jpeg capture size", jpegSize, exifSize);
+
+        // TAG_DATETIME, it should be local time
+        long currentTimeInMs = System.currentTimeMillis();
+        long currentTimeInSecond = currentTimeInMs / 1000;
+        Date date = new Date(currentTimeInMs);
+        String localDatetime = new SimpleDateFormat("yyyy:MM:dd HH:").format(date);
+        String dateTime = exif.getAttribute(ExifInterface.TAG_DATETIME);
+        if (mCollector.expectTrue("Exif TAG_DATETIME shouldn't be null", dateTime != null)) {
+            mCollector.expectTrue("Exif TAG_DATETIME is wrong",
+                    dateTime.length() == EXIF_DATETIME_LENGTH);
+            long exifTimeInSecond =
+                    new SimpleDateFormat("yyyy:MM:dd HH:mm:ss").parse(dateTime).getTime() / 1000;
+            long delta = currentTimeInSecond - exifTimeInSecond;
+            mCollector.expectTrue("Capture time deviates too much from the current time",
+                    Math.abs(delta) < EXIF_DATETIME_ERROR_MARGIN_SEC);
+            // It should be local time.
+            mCollector.expectTrue("Exif date time should be local time",
+                    dateTime.startsWith(localDatetime));
+        }
+
+        // TAG_FOCAL_LENGTH.
+        float[] focalLengths = mStaticInfo.getAvailableFocalLengthsChecked();
+        float exifFocalLength = (float)exif.getAttributeDouble(ExifInterface.TAG_FOCAL_LENGTH, -1);
+        mCollector.expectEquals("Focal length should match",
+                getClosestValueInArray(focalLengths, exifFocalLength),
+                exifFocalLength, EXIF_FOCAL_LENGTH_ERROR_MARGIN);
+        // More checks for focal length.
+        mCollector.expectEquals("Exif focal length should match capture result",
+                validateFocalLength(result), exifFocalLength);
+
+        // 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);
+        mCollector.expectNotNull("Exif TAG_EXPOSURE_TIME shouldn't be null", exposureTime);
+        if (exposureTime != null) {
+            double exposureTimeValue = Double.parseDouble(exposureTime);
+            long  expTimeResult = result.get(CaptureResult.SENSOR_EXPOSURE_TIME);
+            double expected = expTimeResult / 1e9;
+            mCollector.expectEquals("Exif exposure time doesn't match", expected,
+                    exposureTimeValue, EXIF_EXPOSURE_TIME_ERROR_MARGIN_SEC);
+        }
+
+        // TAG_APERTURE
+        // ExifInterface API gives aperture value in the form of float instead of rational
+        String exifAperture = exif.getAttribute(ExifInterface.TAG_APERTURE);
+        float[] apertures = mStaticInfo.getAvailableAperturesChecked();
+        mCollector.expectNotNull("Exif TAG_APERTURE shouldn't be null", exifAperture);
+        if (exifAperture != null) {
+            float apertureValue = Float.parseFloat(exifAperture);
+            mCollector.expectEquals("Aperture value should match",
+                    getClosestValueInArray(apertures, apertureValue),
+                    apertureValue, EXIF_APERTURE_ERROR_MARGIN);
+            // More checks for aperture.
+            mCollector.expectEquals("Exif aperture length should match capture result",
+                    validateAperture(result), apertureValue);
+        }
+
+        /**
+         * TAG_FLASH. TODO: For full devices, can check a lot more info
+         * (http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/EXIF.html#Flash)
+         */
+        String flash = exif.getAttribute(ExifInterface.TAG_FLASH);
+        mCollector.expectNotNull("Exif TAG_FLASH shouldn't be null", flash);
+
+        /**
+         * TAG_WHITE_BALANCE. TODO: For full devices, with the DNG tags, we
+         * should be able to cross-check android.sensor.referenceIlluminant.
+         */
+        String whiteBalance = exif.getAttribute(ExifInterface.TAG_WHITE_BALANCE);
+        mCollector.expectNotNull("Exif TAG_WHITE_BALANCE shouldn't be null", whiteBalance);
+
+        // TAG_MAKE
+        String make = exif.getAttribute(ExifInterface.TAG_MAKE);
+        mCollector.expectEquals("Exif TAG_MAKE is incorrect", Build.MANUFACTURER, make);
+
+        // TAG_MODEL
+        String model = exif.getAttribute(ExifInterface.TAG_MODEL);
+        mCollector.expectEquals("Exif TAG_MODEL is incorrect", Build.MODEL, model);
+
+
+        // TAG_ISO
+        int iso = exif.getAttributeInt(ExifInterface.TAG_ISO, /*defaultValue*/-1);
+        int expectedIso = result.get(CaptureResult.SENSOR_SENSITIVITY);
+        mCollector.expectEquals("Exif TAG_ISO is incorrect", expectedIso, iso);
+
+        // TAG_DATETIME_DIGITIZED (a.k.a Create time for digital cameras).
+        String digitizedTime = exif.getAttribute(TAG_DATETIME_DIGITIZED);
+        mCollector.expectNotNull("Exif TAG_DATETIME_DIGITIZED shouldn't be null", digitizedTime);
+        if (digitizedTime != null) {
+            String expectedDateTime = exif.getAttribute(ExifInterface.TAG_DATETIME);
+            mCollector.expectNotNull("Exif TAG_DATETIME shouldn't be null", expectedDateTime);
+            if (expectedDateTime != null) {
+                mCollector.expectEquals("dataTime should match digitizedTime",
+                        expectedDateTime, digitizedTime);
+            }
+        }
+
+        /**
+         * 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, /*defaultValue*/-1);
+        mCollector.expectTrue("Exif TAG_SUBSEC_TIME value is null or invalid!", subSecTime > 0);
+
+        // TAG_SUBSEC_TIME_ORIG
+        int subSecTimeOrig = exif.getAttributeInt(TAG_SUBSEC_TIME_ORIG, /*defaultValue*/-1);
+        mCollector.expectTrue("Exif TAG_SUBSEC_TIME_ORIG value is null or invalid!",
+                subSecTimeOrig > 0);
+
+        // TAG_SUBSEC_TIME_DIG
+        int subSecTimeDig = exif.getAttributeInt(TAG_SUBSEC_TIME_DIG, /*defaultValue*/-1);
+        mCollector.expectTrue(
+                "Exif TAG_SUBSEC_TIME_DIG value is null or invalid!", subSecTimeDig > 0);
+    }
+
+    private int getExifOrientationInDegress(int exifOrientation) {
+        switch (exifOrientation) {
+            case ExifInterface.ORIENTATION_NORMAL:
+                return 0;
+            case ExifInterface.ORIENTATION_ROTATE_90:
+                return 90;
+            case ExifInterface.ORIENTATION_ROTATE_180:
+                return 180;
+            case ExifInterface.ORIENTATION_ROTATE_270:
+                return 270;
+            default:
+                mCollector.addMessage("It is impossible to get non 0, 90, 180, 270 degress exif" +
+                        "info based on the request orientation range");
+                return 0;
+        }
+    }
+    /**
+     * Immutable class wrapping the exif test data.
+     */
+    private static class ExifTestData {
+        public final Location gpsLocation;
+        public final int jpegOrientation;
+        public final byte jpegQuality;
+        public final byte thumbnailQuality;
+
+        public ExifTestData(Location location, int orientation,
+                byte jpgQuality, byte thumbQuality) {
+            gpsLocation = location;
+            jpegOrientation = orientation;
+            jpegQuality = jpgQuality;
+            thumbnailQuality = thumbQuality;
+        }
+    }
+
+    private void aeCompensationTestByCamera() throws Exception {
+        Range<Integer> compensationRange = mStaticInfo.getAeCompensationRangeChecked();
+        Rational step = mStaticInfo.getAeCompensationStepChecked();
+        float stepF = (float) step.getNumerator() / step.getDenominator();
+        int stepsPerEv = (int) Math.round(1.0 / stepF);
+        int numSteps = (compensationRange.getUpper() - compensationRange.getLower()) / stepsPerEv;
+
+        Size maxStillSz = mOrderedStillSizes.get(0);
+        Size maxPreviewSz = mOrderedPreviewSizes.get(0);
+        SimpleCaptureListener resultListener = new SimpleCaptureListener();
+        SimpleImageReaderListener imageListener = new SimpleImageReaderListener();
+        CaptureRequest.Builder previewRequest =
+                mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
+        CaptureRequest.Builder stillRequest =
+                mCamera.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
+        stillRequest.set(CaptureRequest.CONTROL_AE_LOCK, true);
+
+        // The following variables should only be read under the MANUAL_SENSOR capability guard:
+        long minExposureValue = -1;
+        long maxExposureTimeUs = -1;
+        long maxExposureValuePreview = -1;
+        long maxExposureValueStill = -1;
+        if (mStaticInfo.isCapabilitySupported(
+                CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR)) {
+            // Minimum exposure settings is mostly static while maximum exposure setting depends on
+            // frame rate range which in term depends on capture request.
+            minExposureValue = mStaticInfo.getSensitivityMinimumOrDefault() *
+                    mStaticInfo.getExposureMinimumOrDefault() / 1000;
+            long maxSensitivity = mStaticInfo.getSensitivityMaximumOrDefault();
+            maxExposureTimeUs = mStaticInfo.getExposureMaximumOrDefault() / 1000;
+            maxExposureValuePreview = getMaxExposureValue(previewRequest, maxExposureTimeUs,
+                    maxSensitivity);
+            maxExposureValueStill = getMaxExposureValue(stillRequest, maxExposureTimeUs,
+                    maxSensitivity);
+        }
+
+        // Set the max number of images to be same as the burst count, as the verification
+        // could be much slower than producing rate, and we don't want to starve producer.
+        prepareStillCaptureAndStartPreview(previewRequest, stillRequest, maxPreviewSz,
+                maxStillSz, resultListener, numSteps, imageListener);
+
+        for (int i = 0; i <= numSteps; i++) {
+            int exposureCompensation = i * stepsPerEv + compensationRange.getLower();
+            double expectedRatio = Math.pow(2.0, exposureCompensation / stepsPerEv);
+
+            // Wait for AE to be stabilized before capture: CONVERGED or FLASH_REQUIRED.
+            waitForAeStable(resultListener, NUM_FRAMES_WAITED_FOR_UNKNOWN_LATENCY);
+            CaptureResult result = resultListener.getCaptureResult(WAIT_FOR_RESULT_TIMEOUT_MS);
+
+            long normalExposureValue = -1;
+            if (mStaticInfo.isCapabilitySupported(
+                    CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR)) {
+                // get and check if current exposure value is valid
+                normalExposureValue = getExposureValue(result);
+                mCollector.expectInRange("Exposure setting out of bound", normalExposureValue,
+                        minExposureValue, maxExposureValuePreview);
+
+                // Only run the test if expectedExposureValue is within valid range
+                long expectedExposureValue = (long) (normalExposureValue * expectedRatio);
+                if (expectedExposureValue < minExposureValue ||
+                    expectedExposureValue > maxExposureValueStill) {
+                    continue;
+                }
+            }
+
+            // Now issue exposure compensation and wait for AE locked. AE could take a few
+            // frames to go back to locked state
+            previewRequest.set(CaptureRequest.CONTROL_AE_EXPOSURE_COMPENSATION,
+                    exposureCompensation);
+            previewRequest.set(CaptureRequest.CONTROL_AE_LOCK, true);
+            mSession.setRepeatingRequest(previewRequest.build(), resultListener, mHandler);
+            waitForAeLocked(resultListener, NUM_FRAMES_WAITED_FOR_UNKNOWN_LATENCY);
+
+            // Issue still capture
+            if (VERBOSE) {
+                Log.v(TAG, "Verifying capture result for ae compensation value "
+                        + exposureCompensation);
+            }
+
+            stillRequest.set(CaptureRequest.CONTROL_AE_EXPOSURE_COMPENSATION, exposureCompensation);
+            CaptureRequest request = stillRequest.build();
+            mSession.capture(request, resultListener, mHandler);
+
+            result = resultListener.getCaptureResultForRequest(request, WAIT_FOR_RESULT_TIMEOUT_MS);
+
+            if (mStaticInfo.isCapabilitySupported(
+                    CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR)) {
+                // Verify the exposure value compensates as requested
+                long compensatedExposureValue = getExposureValue(result);
+                mCollector.expectInRange("Exposure setting out of bound", compensatedExposureValue,
+                        minExposureValue, maxExposureValueStill);
+                double observedRatio = (double) compensatedExposureValue / normalExposureValue;
+                double error = observedRatio / expectedRatio;
+                mCollector.expectInRange(String.format(
+                        "Exposure compensation ratio exceeds error tolerence:"
+                        + " expected(%f) observed(%f) ", expectedRatio, observedRatio),
+                        error,
+                        1.0 - AE_COMPENSATION_ERROR_TOLERANCE,
+                        1.0 + AE_COMPENSATION_ERROR_TOLERANCE);
+            }
+
+            mCollector.expectEquals("Exposure compensation result should match requested value.",
+                    exposureCompensation,
+                    result.get(CaptureResult.CONTROL_AE_EXPOSURE_COMPENSATION));
+            mCollector.expectTrue("Exposure lock should be set",
+                    result.get(CaptureResult.CONTROL_AE_LOCK));
+
+            Image image = imageListener.getImage(CAPTURE_IMAGE_TIMEOUT_MS);
+            validateJpegCapture(image, maxStillSz);
+            image.close();
+
+            // Recover AE compensation and lock
+            previewRequest.set(CaptureRequest.CONTROL_AE_EXPOSURE_COMPENSATION, 0);
+            previewRequest.set(CaptureRequest.CONTROL_AE_LOCK, false);
+            mSession.setRepeatingRequest(previewRequest.build(), resultListener, mHandler);
+        }
+    }
+
+    private long getExposureValue(CaptureResult result) throws Exception {
+        int expTimeUs = (int) (getValueNotNull(result, CaptureResult.SENSOR_EXPOSURE_TIME) / 1000);
+        int sensitivity = getValueNotNull(result, CaptureResult.SENSOR_SENSITIVITY);
+        return expTimeUs * sensitivity;
+    }
+
+    private long getMaxExposureValue(CaptureRequest.Builder request, long maxExposureTimeUs,
+                long maxSensitivity)  throws Exception {
+        Range<Integer> fpsRange = request.get(CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE);
+        long maxFrameDurationUs = Math.round(1000000.0 / fpsRange.getLower());
+        long currentMaxExposureTimeUs = Math.min(maxFrameDurationUs, maxExposureTimeUs);
+        return currentMaxExposureTimeUs * maxSensitivity;
+    }
+
+
+    //----------------------------------------------------------------
+    //---------Below are common functions for all tests.--------------
+    //----------------------------------------------------------------
+
+    /**
+     * Simple validation of JPEG image size and format.
+     * <p>
+     * Only validate the image object sanity. It is fast, but doesn't actually
+     * check the buffer data. Assert is used here as it make no sense to
+     * continue the test if the jpeg image captured has some serious failures.
+     * </p>
+     *
+     * @param image The captured jpeg image
+     * @param expectedSize Expected capture jpeg size
+     */
+    private static void basicValidateJpegImage(Image image, Size expectedSize) {
+        Size imageSz = new Size(image.getWidth(), image.getHeight());
+        assertTrue(
+                String.format("Image size doesn't match (expected %s, actual %s) ",
+                        expectedSize.toString(), imageSz.toString()), expectedSize.equals(imageSz));
+        assertEquals("Image format should be JPEG", ImageFormat.JPEG, image.getFormat());
+        assertNotNull("Image plane shouldn't be null", image.getPlanes());
+        assertEquals("Image plane number should be 1", 1, image.getPlanes().length);
+
+        // Jpeg decoding validate was done in ImageReaderTest, no need to duplicate the test here.
+    }
+
+    /**
+     * Validate standard raw (RAW16) capture image.
+     *
+     * @param image The raw16 format image captured
+     * @param rawSize The expected raw size
+     */
+    private static void validateRaw16Image(Image image, Size rawSize) {
+        CameraTestUtils.validateImage(image, rawSize.getWidth(), rawSize.getHeight(),
+                ImageFormat.RAW_SENSOR, /*filePath*/null);
+    }
+
+    /**
+     * Validate JPEG capture image object sanity and test.
+     * <p>
+     * In addition to image object sanity, this function also does the decoding
+     * test, which is slower.
+     * </p>
+     *
+     * @param image The JPEG image to be verified.
+     * @param jpegSize The JPEG capture size to be verified against.
+     */
+    private static void validateJpegCapture(Image image, Size jpegSize) {
+        CameraTestUtils.validateImage(image, jpegSize.getWidth(), jpegSize.getHeight(),
+                ImageFormat.JPEG, /*filePath*/null);
+    }
+
+    private static float getClosestValueInArray(float[] values, float target) {
+        int minIdx = 0;
+        float minDistance = Math.abs(values[0] - target);
+        for(int i = 0; i < values.length; i++) {
+            float distance = Math.abs(values[i] - target);
+            if (minDistance > distance) {
+                minDistance = distance;
+                minIdx = i;
+            }
+        }
+
+        return values[minIdx];
+    }
+
+    /**
+     * Validate and return the focal length.
+     *
+     * @param result Capture result to get the focal length
+     * @return Focal length from capture result or -1 if focal length is not available.
+     */
+    private float validateFocalLength(CaptureResult result) {
+        float[] focalLengths = mStaticInfo.getAvailableFocalLengthsChecked();
+        Float resultFocalLength = result.get(CaptureResult.LENS_FOCAL_LENGTH);
+        if (mCollector.expectTrue("Focal length is invalid",
+                resultFocalLength != null && resultFocalLength > 0)) {
+            List<Float> focalLengthList =
+                    Arrays.asList(CameraTestUtils.toObject(focalLengths));
+            mCollector.expectTrue("Focal length should be one of the available focal length",
+                    focalLengthList.contains(resultFocalLength));
+            return resultFocalLength;
+        }
+        return -1;
+    }
+
+    /**
+     * Validate and return the aperture.
+     *
+     * @param result Capture result to get the aperture
+     * @return Aperture from capture result or -1 if aperture is not available.
+     */
+    private float validateAperture(CaptureResult result) {
+        float[] apertures = mStaticInfo.getAvailableAperturesChecked();
+        Float resultAperture = result.get(CaptureResult.LENS_APERTURE);
+        if (mCollector.expectTrue("Capture result aperture is invalid",
+                resultAperture != null && resultAperture > 0)) {
+            List<Float> apertureList =
+                    Arrays.asList(CameraTestUtils.toObject(apertures));
+            mCollector.expectTrue("Aperture should be one of the available apertures",
+                    apertureList.contains(resultAperture));
+            return resultAperture;
+        }
+        return -1;
+    }
+
+    private static class SimpleAutoFocusListener implements Camera2Focuser.AutoFocusListener {
+        final ConditionVariable focusDone = new ConditionVariable();
+        @Override
+        public void onAutoFocusLocked(boolean success) {
+            focusDone.open();
+        }
+
+        public void waitForAutoFocusDone(long timeoutMs) {
+            if (focusDone.block(timeoutMs)) {
+                focusDone.close();
+            } else {
+                throw new TimeoutRuntimeException("Wait for auto focus done timed out after "
+                        + timeoutMs + "ms");
+            }
+        }
+    }
+
+    /**
+     * Get 5 3A region test cases, each with one square region in it.
+     * The first one is at center, the other four are at corners of
+     * active array rectangle.
+     *
+     * @return array of test 3A regions
+     */
+    private ArrayList<MeteringRectangle[]> get3ARegionTestCasesForCamera() {
+        final int TEST_3A_REGION_NUM = 5;
+        final int DEFAULT_REGION_WEIGHT = 30;
+        final int DEFAULT_REGION_SCALE_RATIO = 8;
+        ArrayList<MeteringRectangle[]> testCases =
+                new ArrayList<MeteringRectangle[]>(TEST_3A_REGION_NUM);
+        final Rect activeArraySize = mStaticInfo.getActiveArraySizeChecked();
+        int regionWidth = activeArraySize.width() / DEFAULT_REGION_SCALE_RATIO - 1;
+        int regionHeight = activeArraySize.height() / DEFAULT_REGION_SCALE_RATIO - 1;
+        int centerX = activeArraySize.width() / 2;
+        int centerY = activeArraySize.height() / 2;
+        int bottomRightX = activeArraySize.width() - 1;
+        int bottomRightY = activeArraySize.height() - 1;
+
+        // Center region
+        testCases.add(
+                new MeteringRectangle[] {
+                    new MeteringRectangle(
+                            centerX - regionWidth / 2,  // x
+                            centerY - regionHeight / 2, // y
+                            regionWidth,                // width
+                            regionHeight,               // height
+                            DEFAULT_REGION_WEIGHT)});
+
+        // Upper left corner
+        testCases.add(
+                new MeteringRectangle[] {
+                    new MeteringRectangle(
+                            0,                // x
+                            0,                // y
+                            regionWidth,      // width
+                            regionHeight,     // height
+                            DEFAULT_REGION_WEIGHT)});
+
+        // Upper right corner
+        testCases.add(
+                new MeteringRectangle[] {
+                    new MeteringRectangle(
+                            bottomRightX - regionWidth, // x
+                            0,                          // y
+                            regionWidth,                // width
+                            regionHeight,               // height
+                            DEFAULT_REGION_WEIGHT)});
+
+        // Bottom left corner
+        testCases.add(
+                new MeteringRectangle[] {
+                    new MeteringRectangle(
+                            0,                           // x
+                            bottomRightY - regionHeight, // y
+                            regionWidth,                 // width
+                            regionHeight,                // height
+                            DEFAULT_REGION_WEIGHT)});
+
+        // Bottom right corner
+        testCases.add(
+                new MeteringRectangle[] {
+                    new MeteringRectangle(
+                            bottomRightX - regionWidth,  // x
+                            bottomRightY - regionHeight, // y
+                            regionWidth,                 // width
+                            regionHeight,                // height
+                            DEFAULT_REGION_WEIGHT)});
+
+        if (VERBOSE) {
+            StringBuilder sb = new StringBuilder();
+            for (MeteringRectangle[] mr : testCases) {
+                sb.append("{");
+                sb.append(Arrays.toString(mr));
+                sb.append("}, ");
+            }
+            if (sb.length() > 1)
+                sb.setLength(sb.length() - 2); // Remove the redundant comma and space at the end
+            Log.v(TAG, "Generated test regions are: " + sb.toString());
+        }
+
+        return testCases;
+    }
+
+    private boolean isRegionsSupportedFor3A(int index) {
+        int maxRegions = 0;
+        switch (index) {
+            case MAX_REGIONS_AE_INDEX:
+                maxRegions = mStaticInfo.getAeMaxRegionsChecked();
+                break;
+            case MAX_REGIONS_AWB_INDEX:
+                maxRegions = mStaticInfo.getAwbMaxRegionsChecked();
+                break;
+            case  MAX_REGIONS_AF_INDEX:
+                maxRegions = mStaticInfo.getAfMaxRegionsChecked();
+                break;
+            default:
+                throw new IllegalArgumentException("Unknown algorithm index");
+        }
+        boolean isRegionsSupported = maxRegions > 0;
+        if (index == MAX_REGIONS_AF_INDEX && isRegionsSupported) {
+            mCollector.expectTrue(
+                    "Device reports non-zero max AF region count for a camera without focuser!",
+                    mStaticInfo.hasFocuser());
+            isRegionsSupported = isRegionsSupported && mStaticInfo.hasFocuser();
+        }
+
+        return isRegionsSupported;
+    }
+}
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/SurfaceViewPreviewTest.java b/tests/tests/hardware/src/android/hardware/camera2/cts/SurfaceViewPreviewTest.java
new file mode 100644
index 0000000..6dd7c1e
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/SurfaceViewPreviewTest.java
@@ -0,0 +1,295 @@
+/*
+ * 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 android.hardware.camera2.CameraCaptureSession;
+import android.hardware.camera2.CameraCaptureSession.CaptureListener;
+import android.hardware.camera2.CameraDevice;
+import android.hardware.camera2.CaptureFailure;
+import android.hardware.camera2.CaptureRequest;
+import android.hardware.camera2.CaptureResult;
+import android.hardware.camera2.TotalCaptureResult;
+import android.util.Size;
+import android.hardware.camera2.cts.CameraTestUtils.SimpleCaptureListener;
+import android.hardware.camera2.cts.testcases.Camera2SurfaceViewTestCase;
+import android.util.Log;
+import android.util.Range;
+
+import org.mockito.ArgumentCaptor;
+import org.mockito.ArgumentMatcher;
+
+import static org.mockito.Mockito.*;
+
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * CameraDevice preview test by using SurfaceView.
+ */
+public class SurfaceViewPreviewTest extends Camera2SurfaceViewTestCase {
+    private static final String TAG = "SurfaceViewPreviewTest";
+    private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
+    private static final int FRAME_TIMEOUT_MS = 1000;
+    private static final int NUM_FRAMES_VERIFIED = 30;
+    private static final int NUM_TEST_PATTERN_FRAMES_VERIFIED = 60;
+    private static final float FRAME_DURATION_ERROR_MARGIN = 0.005f; // 0.5 percent error margin.
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        super.tearDown();
+    }
+
+    /**
+     * Test all supported preview sizes for each camera device.
+     * <p>
+     * For the first  {@link #NUM_FRAMES_VERIFIED}  of capture results,
+     * the {@link CaptureListener} callback availability and the capture timestamp
+     * (monotonically increasing) ordering are verified.
+     * </p>
+     */
+    public void testCameraPreview() throws Exception {
+        for (int i = 0; i < mCameraIds.length; i++) {
+            try {
+                Log.i(TAG, "Testing preview for Camera " + mCameraIds[i]);
+                openDevice(mCameraIds[i]);
+
+                previewTestByCamera();
+            } finally {
+                closeDevice();
+            }
+        }
+    }
+
+    /**
+     * Basic test pattern mode preview.
+     * <p>
+     * Only test the test pattern preview and capture result, the image buffer
+     * is not validated.
+     * </p>
+     */
+    public void testBasicTestPatternPreview() throws Exception{
+        for (int i = 0; i < mCameraIds.length; i++) {
+            try {
+                Log.i(TAG, "Testing preview for Camera " + mCameraIds[i]);
+                openDevice(mCameraIds[i]);
+
+                previewTestPatternTestByCamera();
+            } finally {
+                closeDevice();
+            }
+        }
+    }
+
+    /**
+     * Test {@link CaptureRequest#CONTROL_AE_TARGET_FPS_RANGE} for preview, validate the preview
+     * frame duration and exposure time.
+     */
+    public void testPreviewFpsRange() throws Exception {
+        for (String id : mCameraIds) {
+            try {
+                openDevice(id);
+
+                previewFpsRangeTestByCamera();
+            } finally {
+                closeDevice();
+            }
+        }
+    }
+
+    /**
+     * Test preview fps range for all supported ranges. The exposure time are frame duration are
+     * validated.
+     */
+    private void previewFpsRangeTestByCamera() throws Exception {
+        final int FPS_RANGE_SIZE = 2;
+        Size maxPreviewSz = mOrderedPreviewSizes.get(0);
+        Range<Integer>[] fpsRanges = mStaticInfo.getAeAvailableTargetFpsRangesChecked();
+        boolean antiBandingOffIsSupported = mStaticInfo.isAntiBandingOffModeSupported();
+        Range<Integer> fpsRange;
+        CaptureRequest.Builder requestBuilder =
+                mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
+        SimpleCaptureListener resultListener = new SimpleCaptureListener();
+        startPreview(requestBuilder, maxPreviewSz, resultListener);
+
+        for (int i = 0; i < fpsRanges.length; i += 1) {
+            fpsRange = fpsRanges[i];
+
+            requestBuilder.set(CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE, fpsRange);
+            // Turn off auto antibanding to avoid exposure time and frame duration interference
+            // from antibanding algorithm.
+            if (antiBandingOffIsSupported) {
+                requestBuilder.set(CaptureRequest.CONTROL_AE_ANTIBANDING_MODE,
+                        CaptureRequest.CONTROL_AE_ANTIBANDING_MODE_OFF);
+            } else {
+                // The device doesn't implement the OFF mode, test continues. It need make sure
+                // that the antibanding algorithm doesn't interfere with the fps range control.
+                Log.i(TAG, "OFF antibanding mode is not supported, the camera device output must" +
+                        " satisfy the specified fps range regardless of its current antibanding" +
+                        " mode");
+            }
+
+            resultListener = new SimpleCaptureListener();
+            mSession.setRepeatingRequest(requestBuilder.build(), resultListener, mHandler);
+
+            verifyPreviewTargetFpsRange(resultListener, NUM_FRAMES_VERIFIED, fpsRange,
+                    maxPreviewSz);
+        }
+
+        stopPreview();
+    }
+
+    private void verifyPreviewTargetFpsRange(SimpleCaptureListener resultListener,
+            int numFramesVerified, Range<Integer> fpsRange, Size previewSz) {
+        CaptureResult result = resultListener.getCaptureResult(WAIT_FOR_RESULT_TIMEOUT_MS);
+        List<Integer> capabilities = mStaticInfo.getAvailableCapabilitiesChecked();
+
+        if (capabilities.contains(CaptureRequest.REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR)) {
+            long frameDuration = getValueNotNull(result, CaptureResult.SENSOR_FRAME_DURATION);
+            long[] frameDurationRange =
+                    new long[]{(long) (1e9 / fpsRange.getUpper()), (long) (1e9 / fpsRange.getLower())};
+            mCollector.expectInRange(
+                    "Frame duration must be in the range of " + Arrays.toString(frameDurationRange),
+                    frameDuration, (long) (frameDurationRange[0] * (1 - FRAME_DURATION_ERROR_MARGIN)),
+                    (long) (frameDurationRange[1] * (1 + FRAME_DURATION_ERROR_MARGIN)));
+            long expTime = getValueNotNull(result, CaptureResult.SENSOR_EXPOSURE_TIME);
+            mCollector.expectTrue(String.format("Exposure time %d must be no larger than frame"
+                    + "duration %d", expTime, frameDuration), expTime <= frameDuration);
+
+            Long minFrameDuration = mMinPreviewFrameDurationMap.get(previewSz);
+            boolean findDuration = mCollector.expectTrue("Unable to find minFrameDuration for size "
+                    + previewSz.toString(), minFrameDuration != null);
+            if (findDuration) {
+                mCollector.expectTrue("Frame duration " + frameDuration + " must be no smaller than"
+                        + " minFrameDuration " + minFrameDuration, frameDuration >= minFrameDuration);
+            }
+        } else {
+            Log.i(TAG, "verifyPreviewTargetFpsRange - MANUAL_SENSOR control is not supported," +
+                    " skipping duration and exposure time check.");
+        }
+    }
+
+    /**
+     * 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);
+
+        for (final Size sz : previewSizes) {
+            if (VERBOSE) {
+                Log.v(TAG, "Testing camera preview size: " + sz.toString());
+            }
+
+            // TODO: vary the different settings like crop region to cover more cases.
+            CaptureRequest.Builder requestBuilder =
+                    mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
+            CaptureListener mockCaptureListener =
+                    mock(CameraCaptureSession.CaptureListener.class);
+
+            startPreview(requestBuilder, sz, mockCaptureListener);
+            verifyCaptureResults(mSession, mockCaptureListener, NUM_FRAMES_VERIFIED,
+                    NUM_FRAMES_VERIFIED * FRAME_TIMEOUT_MS);
+            stopPreview();
+        }
+    }
+
+    private void previewTestPatternTestByCamera() throws Exception {
+        Size maxPreviewSize = mOrderedPreviewSizes.get(0);
+        int[] testPatternModes = mStaticInfo.getAvailableTestPatternModesChecked();
+        CaptureRequest.Builder requestBuilder =
+                mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
+        CaptureListener mockCaptureListener;
+
+        final int[] TEST_PATTERN_DATA = {0, 0xFFFFFFFF, 0xFFFFFFFF, 0}; // G:100%, RB:0.
+        for (int mode : testPatternModes) {
+            if (VERBOSE) {
+                Log.v(TAG, "Test pattern mode: " + mode);
+            }
+            requestBuilder.set(CaptureRequest.SENSOR_TEST_PATTERN_MODE, mode);
+            if (mode == CaptureRequest.SENSOR_TEST_PATTERN_MODE_SOLID_COLOR) {
+                // Assign color pattern to SENSOR_TEST_PATTERN_MODE_DATA
+                requestBuilder.set(CaptureRequest.SENSOR_TEST_PATTERN_DATA, TEST_PATTERN_DATA);
+            }
+            mockCaptureListener = mock(CaptureListener.class);
+            startPreview(requestBuilder, maxPreviewSize, mockCaptureListener);
+            verifyCaptureResults(mSession, mockCaptureListener, NUM_TEST_PATTERN_FRAMES_VERIFIED,
+                    NUM_TEST_PATTERN_FRAMES_VERIFIED * FRAME_TIMEOUT_MS);
+        }
+
+        stopPreview();
+    }
+
+    private class IsCaptureResultValid extends ArgumentMatcher<TotalCaptureResult> {
+        @Override
+        public boolean matches(Object obj) {
+            TotalCaptureResult result = (TotalCaptureResult)obj;
+            Long timeStamp = result.get(CaptureResult.SENSOR_TIMESTAMP);
+            if (timeStamp != null && timeStamp.longValue() > 0L) {
+                return true;
+            }
+            return false;
+        }
+    }
+
+    private void verifyCaptureResults(
+            CameraCaptureSession session,
+            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(session),
+                                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()) {
+            assertNotNull("Next timestamp is null!", nextTimestamp);
+            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(session),
+                                isA(CaptureRequest.class),
+                                argThat(new IsCaptureResultValid()));
+
+        // Should not receive any capture failed callbacks.
+        verify(mockListener, never())
+                        .onCaptureFailed(
+                                eq(session),
+                                isA(CaptureRequest.class),
+                                isA(CaptureFailure.class));
+    }
+
+}
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/helpers/AssertHelpers.java b/tests/tests/hardware/src/android/hardware/camera2/cts/helpers/AssertHelpers.java
new file mode 100644
index 0000000..a2e6a91
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/helpers/AssertHelpers.java
@@ -0,0 +1,233 @@
+/*
+ * 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.helpers;
+
+import static junit.framework.Assert.*;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * Helper set of methods to add extra useful assert functionality missing in junit.
+ */
+public class AssertHelpers {
+
+    private static final int MAX_FORMAT_STRING = 50;
+
+    /**
+     * Assert that at least one of the elements in data is non-zero.
+     *
+     * <p>An empty or a null array always fails.</p>
+     */
+    public static void assertArrayNotAllZeroes(String message, byte[] data) {
+        int size = data.length;
+
+        int i = 0;
+        for (i = 0; i < size; ++i) {
+            if (data[i] != 0) {
+                break;
+            }
+        }
+
+        assertTrue(message, i < size);
+    }
+
+    /**
+     * Assert that every element in left is less than or equals to the corresponding element in
+     * right.
+     *
+     * <p>Array sizes must match.</p>
+     *
+     * @param message Message to use in case the assertion fails
+     * @param left Left array
+     * @param right Right array
+     */
+    public static void assertArrayNotGreater(String message, float[] left, float[] right) {
+        assertEquals("Array lengths did not match", left.length, right.length);
+
+        String leftString = Arrays.toString(left);
+        String rightString = Arrays.toString(right);
+
+        for (int i = 0; i < left.length; ++i) {
+            String msg = String.format(
+                    "%s: (%s should be less than or equals than %s; item index %d; left = %s; " +
+                    "right = %s)",
+                    message, left[i], right[i], i, leftString, rightString);
+
+            assertTrue(msg, left[i] <= right[i]);
+        }
+    }
+
+    /**
+     * Assert that every element in the value array is greater than the lower bound (exclusive).
+     *
+     * @param value an array of items
+     * @param lowerBound the exclusive lower bound
+     */
+    public static void assertArrayWithinLowerBound(String message, float[] value, float lowerBound)
+    {
+        for (int i = 0; i < value.length; ++i) {
+            assertTrue(
+                    String.format("%s: (%s should be greater than than %s; item index %d in %s)",
+                            message, value[i], lowerBound, i, Arrays.toString(value)),
+                    value[i] > lowerBound);
+        }
+    }
+
+    /**
+     * Assert that every element in the value array is less than the upper bound (exclusive).
+     *
+     * @param value an array of items
+     * @param upperBound the exclusive upper bound
+     */
+    public static void assertArrayWithinUpperBound(String message, float[] value, float upperBound)
+    {
+        for (int i = 0; i < value.length; ++i) {
+            assertTrue(
+                    String.format("%s: (%s should be less than than %s; item index %d in %s)",
+                            message, value[i], upperBound, i, Arrays.toString(value)),
+                    value[i] < upperBound);
+        }
+    }
+
+    /**
+     * Assert that {@code low <= value <= high}
+     */
+    public static void assertInRange(float value, float low, float high) {
+        assertTrue(
+                String.format("Value %s must be greater or equal to %s, but was lower", value, low),
+                value >= low);
+        assertTrue(
+                String.format("Value %s must be less than or equal to %s, but was higher",
+                        value, high),
+                value <= high);
+
+        // TODO: generic by using comparators
+    }
+
+    /**
+     * Assert that the given array contains the given value.
+     *
+     * @param message message to print on failure.
+     * @param actual array to test.
+     * @param checkVals value to check for array membership.
+     */
+    public static <T> void assertArrayContains(String message, T[] actual, T checkVals) {
+        assertCollectionContainsAnyOf(message, buildList(actual), Arrays.asList(checkVals));
+    }
+
+
+    /**
+     * Assert that the given array contains the given value.
+     *
+     * @param message message to print on failure.
+     * @param actual array to test.
+     * @param checkVals value to check for array membership.
+     */
+    public static void assertArrayContains(String message, int[] actual, int checkVals) {
+        assertCollectionContainsAnyOf(message, buildList(actual), Arrays.asList(checkVals));
+    }
+
+    /**
+     * Assert that the given array contains at least one of the given values.
+     *
+     * @param message message to print on failure.
+     * @param actual array to test
+     * @param checkVals values to check for array membership.
+     * @return the value contained, or null.
+     */
+    public static <T> T assertArrayContainsAnyOf(String message, T[] actual, T[] checkVals) {
+        return assertCollectionContainsAnyOf(message, buildList(actual), buildList(checkVals));
+    }
+
+    /**
+     * Assert that the given array contains at least one of the given values.
+     *
+     * @param message message to print on failure.
+     * @param actual array to test
+     * @param checkVals values to check for array membership.
+     * @return the value contained.
+     */
+    public static int assertArrayContainsAnyOf(String message, int[] actual, int[] checkVals) {
+        return assertCollectionContainsAnyOf(message, buildList(actual), buildList(checkVals));
+    }
+
+    /**
+     * Assert that the given {@link Collection} contains at least one of the given values.
+     *
+     * @param message message to print on failure.
+     * @param actual {@link Collection} to test.
+     * @param checkVals a {@link Collection} of values to check for membership.
+     * @return the value contained, or null.
+     */
+    public static <T> T assertCollectionContainsAnyOf(String message, Collection<T> actual,
+                                                      Collection<T> checkVals) {
+        boolean contains = false;
+        T selected = null;
+        for (T check : checkVals) {
+            contains = actual.contains(check);
+            if (contains) {
+                selected = check;
+                break;
+            }
+        }
+
+        if (!contains) {
+            fail(String.format("%s : No elements from %s in %s", message,
+                    formatCollection(actual, MAX_FORMAT_STRING),
+                    formatCollection(checkVals, MAX_FORMAT_STRING)));
+        }
+        return selected;
+    }
+
+    private static <T> List<T> buildList(T[] array) {
+        return new ArrayList<T>(Arrays.asList(array));
+    }
+
+    private static List<Integer> buildList(int[] array) {
+        List<Integer> list = new ArrayList<Integer>(array.length);
+        for (Integer val : array) {
+            list.add(val);
+        }
+        return list;
+    }
+
+    private static <T> String formatCollection(Collection<T> collection, int maxLen) {
+        StringBuilder builder = new StringBuilder();
+        builder.append("[");
+
+        boolean first = true;
+        for (T elem : collection) {
+            String val = ((first) ? ", " : "") + ((elem != null) ? elem.toString() : "null");
+            first = false;
+            if ((builder.length() + val.length()) > maxLen - "...]".length()) {
+                builder.append("...");
+                break;
+            } else {
+                builder.append(val);
+            }
+        }
+        builder.append("]");
+        return builder.toString();
+    }
+
+
+    // Suppress default constructor for noninstantiability
+    private AssertHelpers() { throw new AssertionError(); }
+}
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/helpers/Camera2Focuser.java b/tests/tests/hardware/src/android/hardware/camera2/cts/helpers/Camera2Focuser.java
new file mode 100644
index 0000000..c03caca
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/helpers/Camera2Focuser.java
@@ -0,0 +1,392 @@
+/*
+ * 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.helpers;
+
+import android.graphics.Rect;
+import android.hardware.camera2.CameraAccessException;
+import android.hardware.camera2.CameraCaptureSession;
+import android.hardware.camera2.CameraCaptureSession.CaptureListener;
+import android.hardware.camera2.CameraCharacteristics;
+import android.hardware.camera2.CameraDevice;
+import android.hardware.camera2.params.MeteringRectangle;
+import android.hardware.camera2.CaptureRequest;
+import android.hardware.camera2.CaptureResult;
+import android.hardware.camera2.TotalCaptureResult;
+import android.os.Handler;
+import android.util.Log;
+import android.view.Surface;
+
+import com.android.ex.camera2.pos.AutoFocusStateMachine;
+import com.android.ex.camera2.pos.AutoFocusStateMachine.AutoFocusStateListener;
+
+/**
+ * A focuser utility class to assist camera to do auto focus.
+ * <p>
+ * This class need create repeating request and single request to do auto focus.
+ * The repeating request is used to get the auto focus states; the single
+ * request is used to trigger the auto focus. This class assumes the camera device
+ * supports auto-focus. Don't use this class if the camera device doesn't have focuser
+ * unit.
+ * </p>
+ */
+public class Camera2Focuser implements AutoFocusStateListener {
+    private static final String TAG = "Focuser";
+    private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
+
+    private final AutoFocusStateMachine mAutoFocus = new AutoFocusStateMachine(this);
+    private final Handler mHandler;
+    private final AutoFocusListener mAutoFocusListener;
+    private final CameraDevice mCamera;
+    private final CameraCaptureSession mSession;
+    private final Surface mRequestSurface;
+    private final StaticMetadata mStaticInfo;
+
+    private int mAfRun = 0;
+    private MeteringRectangle[] mAfRegions;
+    private boolean mLocked = false;
+    private boolean mSuccess = false;
+    private CaptureRequest.Builder mRepeatingBuilder;
+
+    /**
+     * The callback interface to notify auto focus result.
+     */
+    public interface AutoFocusListener {
+        /**
+         * This callback is called when auto focus completes and locked.
+         *
+         * @param success true if focus was successful, false if otherwise
+         */
+        void onAutoFocusLocked(boolean success);
+    }
+
+    /**
+     * Construct a focuser object, with given capture requestSurface, listener
+     * and handler.
+     * <p>
+     * The focuser object will use camera and requestSurface to submit capture
+     * request and receive focus state changes. The {@link AutoFocusListener} is
+     * used to notify the auto focus callback.
+     * </p>
+     *
+     * @param camera The camera device associated with this focuser
+     * @param session The camera capture session associated with this focuser
+     * @param requestSurface The surface to issue the capture request with
+     * @param listener The auto focus listener to notify AF result
+     * @param staticInfo The CameraCharacteristics of the camera device
+     * @param handler The handler used to post auto focus callbacks
+     * @throws CameraAccessException
+     */
+    public Camera2Focuser(CameraDevice camera, CameraCaptureSession session, Surface requestSurface,
+            AutoFocusListener listener, CameraCharacteristics staticInfo, Handler handler)
+            throws CameraAccessException {
+        if (camera == null) {
+            throw new IllegalArgumentException("camera must not be null");
+        }
+        if (session == null) {
+            throw new IllegalArgumentException("session must not be null");
+        }
+        if (listener == null) {
+            throw new IllegalArgumentException("listener must not be null");
+        }
+        if (handler == null) {
+            throw new IllegalArgumentException("handler must not be null");
+        }
+        if (requestSurface == null) {
+            throw new IllegalArgumentException("requestSurface must not be null");
+        }
+        if (staticInfo == null) {
+            throw new IllegalArgumentException("staticInfo must not be null");
+        }
+
+        mCamera = camera;
+        mSession = session;
+        mRequestSurface = requestSurface;
+        mAutoFocusListener = listener;
+        mStaticInfo = new StaticMetadata(staticInfo,
+                StaticMetadata.CheckLevel.ASSERT, /*collector*/null);
+        mHandler = handler;
+
+        if (!mStaticInfo.hasFocuser()) {
+            throw new IllegalArgumentException("this camera doesn't have a focuser");
+        }
+
+        /**
+         * Begin by always being in passive auto focus.
+         */
+        cancelAutoFocus();
+    }
+
+    @Override
+    public synchronized void onAutoFocusSuccess(CaptureResult result, boolean locked) {
+        mSuccess = true;
+        mLocked = locked;
+
+        if (locked) {
+            dispatchAutoFocusStatusLocked(/*success*/true);
+        }
+    }
+
+    @Override
+    public synchronized void onAutoFocusFail(CaptureResult result, boolean locked) {
+        mSuccess = false;
+        mLocked = locked;
+
+        if (locked) {
+            dispatchAutoFocusStatusLocked(/*success*/false);
+        }
+    }
+
+    @Override
+    public synchronized void onAutoFocusScan(CaptureResult result) {
+        mSuccess = false;
+        mLocked = false;
+    }
+
+    @Override
+    public synchronized void onAutoFocusInactive(CaptureResult result) {
+        mSuccess = false;
+        mLocked = false;
+    }
+
+    /**
+     * Start a active auto focus scan based on the given regions.
+     *
+     * <p>This is usually used for touch for focus, it can make the auto-focus converge based
+     * on some particular region aggressively. But it is usually slow as a full active scan
+     * is initiated. After the auto focus is converged, the {@link cancelAutoFocus} must be called
+     * to resume the continuous auto-focus.</p>
+     *
+     * @param afRegions The AF regions used by focuser auto focus, full active
+     * array size is used if afRegions is null.
+     * @throws CameraAccessException
+     */
+    public synchronized void touchForAutoFocus(MeteringRectangle[] afRegions)
+            throws CameraAccessException {
+        startAutoFocusLocked(/*active*/true, afRegions);
+    }
+
+    /**
+     * Start auto focus scan.
+     * <p>
+     * Start an auto focus scan if it was not done yet. If AF passively focused,
+     * lock it. If AF is already locked, return. Otherwise, initiate a full
+     * active scan. This is suitable for still capture: focus should need to be
+     * accurate, but the AF latency also need to be as short as possible.
+     * </p>
+     *
+     * @param afRegions The AF regions used by focuser auto focus, full active
+     *            array size is used if afRegions is null.
+     * @throws CameraAccessException
+     */
+    public synchronized void startAutoFocus(MeteringRectangle[] afRegions)
+            throws CameraAccessException {
+        startAutoFocusLocked(/*forceActive*/false, afRegions);
+    }
+
+    /**
+     * Cancel ongoing auto focus, unlock the auto-focus if it was locked, and
+     * resume to passive continuous auto focus.
+     *
+     * @throws CameraAccessException
+     */
+    public synchronized void cancelAutoFocus() throws CameraAccessException {
+        mSuccess = false;
+        mLocked = false;
+
+        // reset the AF regions:
+        setAfRegions(null);
+
+        // Create request builders, the af regions are automatically updated.
+        mRepeatingBuilder = createRequestBuilder();
+        CaptureRequest.Builder requestBuilder = createRequestBuilder();
+        mAutoFocus.setPassiveAutoFocus(/*picture*/true, mRepeatingBuilder);
+        mAutoFocus.unlockAutoFocus(mRepeatingBuilder, requestBuilder);
+        CaptureListener listener = createCaptureListener();
+        mSession.setRepeatingRequest(mRepeatingBuilder.build(), listener, mHandler);
+        mSession.capture(requestBuilder.build(), listener, mHandler);
+    }
+
+    /**
+     * Get current AF mode.
+     * @return current AF mode
+     * @throws IllegalStateException if there auto focus is not running.
+     */
+    public synchronized int getCurrentAfMode() {
+        if (mRepeatingBuilder == null) {
+            throw new IllegalStateException("Auto focus is not running, unable to get AF mode");
+        }
+
+        return mRepeatingBuilder.get(CaptureRequest.CONTROL_AF_MODE);
+    }
+
+    private void startAutoFocusLocked(
+            boolean forceActive, MeteringRectangle[] afRegions) throws CameraAccessException {
+
+        setAfRegions(afRegions);
+        mAfRun++;
+
+        // Create request builders, the af regions are automatically updated.
+        mRepeatingBuilder = createRequestBuilder();
+        CaptureRequest.Builder requestBuilder = createRequestBuilder();
+        if (forceActive) {
+            startAutoFocusFullActiveLocked();
+        } else {
+            // Not forcing a full active scan. If AF passively focused, lock it. If AF is already
+            // locked, return. Otherwise, initiate a full active scan.
+            if (mSuccess && mLocked) {
+                dispatchAutoFocusStatusLocked(/*success*/true);
+                return;
+            } else if (mSuccess) {
+                mAutoFocus.lockAutoFocus(mRepeatingBuilder, requestBuilder);
+                CaptureListener listener = createCaptureListener();
+                mSession.setRepeatingRequest(mRepeatingBuilder.build(), listener, mHandler);
+                mSession.capture(requestBuilder.build(), listener, mHandler);
+            } else {
+                startAutoFocusFullActiveLocked();
+            }
+        }
+    }
+
+    private void startAutoFocusFullActiveLocked() throws CameraAccessException {
+        // Create request builders, the af regions are automatically updated.
+        mRepeatingBuilder = createRequestBuilder();
+        CaptureRequest.Builder requestBuilder = createRequestBuilder();
+        mAutoFocus.setActiveAutoFocus(mRepeatingBuilder, requestBuilder);
+        if (mRepeatingBuilder.get(CaptureRequest.CONTROL_AF_TRIGGER)
+                != CaptureRequest.CONTROL_AF_TRIGGER_IDLE) {
+            throw new AssertionError("Wrong trigger set in repeating request");
+        }
+        if (requestBuilder.get(CaptureRequest.CONTROL_AF_TRIGGER)
+                != CaptureRequest.CONTROL_AF_TRIGGER_START) {
+            throw new AssertionError("Wrong trigger set in queued request");
+        }
+        mAutoFocus.resetState();
+
+        CaptureListener listener = createCaptureListener();
+        mSession.setRepeatingRequest(mRepeatingBuilder.build(), listener, mHandler);
+        mSession.capture(requestBuilder.build(), listener, mHandler);
+    }
+
+    private void dispatchAutoFocusStatusLocked(final boolean success) {
+        mHandler.post(new Runnable() {
+            @Override
+            public void run() {
+                mAutoFocusListener.onAutoFocusLocked(success);
+            }
+        });
+    }
+
+    /**
+     * Create request builder, set the af regions.
+     * @throws CameraAccessException
+     */
+    private CaptureRequest.Builder createRequestBuilder() throws CameraAccessException {
+        CaptureRequest.Builder requestBuilder =
+                mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
+
+        requestBuilder.set(CaptureRequest.CONTROL_AF_REGIONS, mAfRegions);
+        requestBuilder.addTarget(mRequestSurface);
+
+        return requestBuilder;
+    }
+
+    /**
+     * Set AF regions, fall back to default region if afRegions is null.
+     *
+     * @param afRegions The AF regions to set
+     * @throws IllegalArgumentException if the region is malformed (length is 0).
+     */
+    private void setAfRegions(MeteringRectangle[] afRegions) {
+        if (afRegions == null) {
+            setDefaultAfRegions();
+            return;
+        }
+        // Throw IAE if AF regions are malformed.
+        if (afRegions.length == 0) {
+            throw new IllegalArgumentException("afRegions is malformed, length: 0");
+        }
+
+        mAfRegions = afRegions;
+    }
+
+    /**
+     * Set default AF region to full active array size.
+     */
+    private void setDefaultAfRegions() {
+        // Initialize AF regions with all zeros, meaning that it is up to camera device to device
+        // the regions used by AF.
+        mAfRegions = new MeteringRectangle[] {
+                new MeteringRectangle(0, 0, 0, 0, MeteringRectangle.METERING_WEIGHT_DONT_CARE)};
+    }
+    private CaptureListener createCaptureListener() {
+
+        int thisAfRun;
+        synchronized (this) {
+            thisAfRun = mAfRun;
+        }
+
+        final int finalAfRun = thisAfRun;
+
+        return new CaptureListener() {
+            private long mLatestFrameCount = -1;
+
+            @Override
+            public void onCaptureProgressed(CameraCaptureSession session, CaptureRequest request,
+                    CaptureResult result) {
+                // In case of a partial result, send to focuser if necessary
+                // 3A fields are present
+                if (result.get(CaptureResult.CONTROL_AF_STATE) != null &&
+                        result.get(CaptureResult.CONTROL_AF_MODE) != null) {
+                    if (VERBOSE) {
+                        Log.v(TAG, "Focuser - got early AF state");
+                    }
+
+                    dispatchToFocuser(result);
+                }
+            }
+
+            @Override
+            public void onCaptureCompleted(CameraCaptureSession session, CaptureRequest request,
+                    TotalCaptureResult result) {
+                    dispatchToFocuser(result);
+            }
+
+            private void dispatchToFocuser(CaptureResult result) {
+                int afRun;
+                synchronized (Camera2Focuser.this) {
+                    // In case of partial results, don't send AF update twice
+                    long frameCount = result.getFrameNumber();
+                    if (frameCount <= mLatestFrameCount) return;
+                    mLatestFrameCount = frameCount;
+
+                    afRun = mAfRun;
+                }
+
+                if (afRun != finalAfRun) {
+                    if (VERBOSE) {
+                        Log.w(TAG,
+                                "onCaptureCompleted - Ignoring results from previous AF run "
+                                + finalAfRun);
+                    }
+                    return;
+                }
+
+                mAutoFocus.onCaptureCompleted(result);
+            }
+        };
+    }
+}
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/helpers/CameraErrorCollector.java b/tests/tests/hardware/src/android/hardware/camera2/cts/helpers/CameraErrorCollector.java
new file mode 100644
index 0000000..9073ec5
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/helpers/CameraErrorCollector.java
@@ -0,0 +1,650 @@
+/*
+ * 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.helpers;
+
+import android.graphics.Rect;
+import android.hardware.camera2.CameraCharacteristics;
+import android.hardware.camera2.CaptureRequest;
+import android.hardware.camera2.CaptureRequest.Builder;
+import android.hardware.camera2.CaptureResult;
+import android.hardware.camera2.params.MeteringRectangle;
+import android.util.Log;
+import android.util.Size;
+
+import org.hamcrest.CoreMatchers;
+import org.hamcrest.Matcher;
+import org.junit.rules.ErrorCollector;
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+
+/**
+ * A camera test ErrorCollector class to gather the test failures during a test,
+ * instead of failing the test immediately for each failure.
+ */
+public class CameraErrorCollector extends ErrorCollector {
+
+    private static final String TAG = "CameraErrorCollector";
+    private static final boolean LOG_ERRORS = Log.isLoggable(TAG, Log.ERROR);
+
+    private String mCameraMsg = "";
+
+    @Override
+    public void verify() throws Throwable {
+        // Do not remove if using JUnit 3 test runners. super.verify() is protected.
+        super.verify();
+    }
+
+    /**
+     * Adds an unconditional error to the table.
+     *
+     * <p>Execution continues, but test will fail at the end.</p>
+     *
+     * @param message A string containing the failure reason.
+     */
+    public void addMessage(String message) {
+        addErrorSuper(new Throwable(mCameraMsg + message));
+    }
+
+    /**
+     * Adds a Throwable to the table. <p>Execution continues, but the test will fail at the end.</p>
+     */
+    @Override
+    public void addError(Throwable error) {
+        addErrorSuper(new Throwable(mCameraMsg + error.getMessage(), error));
+    }
+
+    private void addErrorSuper(Throwable error) {
+        if (LOG_ERRORS) Log.e(TAG, error.getMessage());
+        super.addError(error);
+    }
+
+    /**
+     * Adds a failure to the table if {@code matcher} does not match {@code value}.
+     * Execution continues, but the test will fail at the end if the match fails.
+     * The camera id is included into the failure log.
+     */
+    @Override
+    public <T> void checkThat(final T value, final Matcher<T> matcher) {
+        super.checkThat(mCameraMsg, value, matcher);
+    }
+
+    /**
+     * Adds a failure with the given {@code reason} to the table if
+     * {@code matcher} does not match {@code value}. Execution continues, but
+     * the test will fail at the end if the match fails. The camera id is
+     * included into the failure log.
+     */
+    @Override
+    public <T> void checkThat(final String reason, final T value, final Matcher<T> matcher) {
+        super.checkThat(mCameraMsg + reason, value, matcher);
+    }
+
+    /**
+     * Set the camera id to this error collector object for logging purpose.
+     *
+     * @param id The camera id to be set.
+     */
+    public void setCameraId(String id) {
+        if (id != null) {
+            mCameraMsg = "Test failed for camera " + id + ": ";
+        } else {
+            mCameraMsg = "";
+        }
+    }
+
+    /**
+     * Adds a failure to the table if {@code condition} is not {@code true}.
+     * <p>
+     * Execution continues, but the test will fail at the end if the condition
+     * failed.
+     * </p>
+     *
+     * @param msg Message to be logged when check fails.
+     * @param condition Log the failure if it is not true.
+     */
+    public boolean expectTrue(String msg, boolean condition) {
+        if (!condition) {
+            addMessage(msg);
+        }
+
+        return condition;
+    }
+
+    /**
+     * Check if the two values are equal.
+     *
+     * @param msg Message to be logged when check fails.
+     * @param expected Expected value to be checked against.
+     * @param actual Actual value to be checked.
+     * @return {@code true} if the two values are equal, {@code false} otherwise.
+     *
+     * @throws IllegalArgumentException if {@code expected} was {@code null}
+     */
+    public <T> boolean expectEquals(String msg, T expected, T actual) {
+        if (expected == null) {
+            throw new IllegalArgumentException("expected value shouldn't be null");
+        }
+
+        if (!Objects.equals(expected, actual)) {
+            if (actual == null) {
+                addMessage(msg + ", actual value is null");
+                return false;
+            }
+
+            addMessage(String.format("%s (expected = %s, actual = %s) ", msg, expected,
+                    actual));
+            return false;
+        }
+
+        return true;
+    }
+
+    /**
+     * Check if the two arrays of values are deeply equal.
+     *
+     * @param msg Message to be logged when check fails.
+     * @param expected Expected array of values to be checked against.
+     * @param actual Actual array of values to be checked.
+     * @return {@code true} if the two arrays of values are deeply equal, {@code false} otherwise.
+     *
+     * @throws IllegalArgumentException if {@code expected} was {@code null}
+     */
+    public <T> boolean expectEquals(String msg, T[] expected, T[] actual) {
+        if (expected == null) {
+            throw new IllegalArgumentException("expected value shouldn't be null");
+        }
+
+        if (!Arrays.deepEquals(expected, actual)) {
+            if (actual == null) {
+                addMessage(msg + ", actual value is null");
+                return false;
+            }
+
+            addMessage(String.format("%s (expected = %s, actual = %s) ", msg,
+                    Arrays.deepToString(expected), Arrays.deepToString(actual)));
+            return false;
+        }
+
+        return true;
+    }
+
+    /**
+     * Check that the {@code actual} value is greater than the {@code expected} value.
+     *
+     * @param msg Message to be logged when check fails.
+     * @param expected The expected value to check that the actual value is larger than.
+     * @param actual Actual value to check.
+     * @return {@code true} if {@code actual} is greater than {@code expected}.
+     */
+    public <T extends Comparable<? super T>> boolean expectGreater(String msg, T expected,
+            T actual) {
+        return expectTrue(String.format("%s: (expected = %s was not greater than actual = %s) ",
+                msg, expected, actual), actual.compareTo(expected) > 0);
+    }
+
+    /**
+     * Check that the {@code actual} value is greater than or equal to the {@code expected} value.
+     *
+     * @param msg Message to be logged when check fails.
+     * @param expected The expected value to check that the actual value is larger than or equal to.
+     * @param actual Actual value to check.
+     * @return {@code true} if {@code actual} is greater than or equal to {@code expected}.
+     */
+    public <T extends Comparable<? super T>> boolean expectGreaterOrEqual(String msg, T expected,
+                                                                       T actual) {
+        return expectTrue(String.format("%s: (expected = %s was not greater than actual = %s) ",
+                msg, expected, actual), actual.compareTo(expected) >= 0);
+    }
+
+    /**
+     * Check if the two float values are equal with given error tolerance.
+     *
+     * @param msg Message to be logged when check fails.
+     * @param expected Expected value to be checked against.
+     * @param actual Actual value to be checked.
+     * @param tolerance The error margin for the equality check.
+     * @return {@code true} if the two values are equal, {@code false} otherwise.
+     */
+    public <T> boolean expectEquals(String msg, float expected, float actual, float tolerance) {
+        if (expected == actual) {
+            return true;
+        }
+
+        if (!(Math.abs(expected - actual) <= tolerance)) {
+            addMessage(String.format("%s (expected = %s, actual = %s, tolerance = %s) ", msg,
+                    expected, actual, tolerance));
+            return false;
+        }
+
+        return true;
+    }
+
+    /**
+     * Check if the two double values are equal with given error tolerance.
+     *
+     * @param msg Message to be logged when check fails.
+     * @param expected Expected value to be checked against.
+     * @param actual Actual value to be checked.
+     * @param tolerance The error margin for the equality check
+     * @return {@code true} if the two values are equal, {@code false} otherwise.
+     */
+    public <T> boolean expectEquals(String msg, double expected, double actual, double tolerance) {
+        if (expected == actual) {
+            return true;
+        }
+
+        if (!(Math.abs(expected - actual) <= tolerance)) {
+            addMessage(String.format("%s (expected = %s, actual = %s, tolerance = %s) ", msg,
+                    expected, actual, tolerance));
+            return false;
+        }
+
+        return true;
+    }
+
+    /**
+     * Expect the list of values are in the range.
+     *
+     * @param msg Message to be logged
+     * @param list The list of values to be checked
+     * @param min The min value of the range
+     * @param max The max value of the range
+     */
+    public <T extends Comparable<? super T>> void expectValuesInRange(String msg, List<T> list,
+            T min, T max) {
+        for (T value : list) {
+            expectTrue(msg + String.format(", array value " + value.toString() +
+                    " is out of range [%s, %s]",
+                    min.toString(), max.toString()),
+                    value.compareTo(max)<= 0 && value.compareTo(min) >= 0);
+        }
+    }
+
+    /**
+     * Expect the array of values are in the range.
+     *
+     * @param msg Message to be logged
+     * @param array The array of values to be checked
+     * @param min The min value of the range
+     * @param max The max value of the range
+     */
+    public <T extends Comparable<? super T>> void expectValuesInRange(String msg, T[] array,
+            T min, T max) {
+        expectValuesInRange(msg, Arrays.asList(array), min, max);
+    }
+
+    /**
+     * Expect the value is in the range.
+     *
+     * @param msg Message to be logged
+     * @param value The value to be checked
+     * @param min The min value of the range
+     * @param max The max value of the range
+     *
+     * @return {@code true} if the value was in range, {@code false} otherwise
+     */
+    public <T extends Comparable<? super T>> boolean expectInRange(String msg, T value,
+            T min, T max) {
+        return expectTrue(msg + String.format(", value " + value.toString()
+                + " is out of range [%s, %s]",
+                min.toString(), max.toString()),
+                value.compareTo(max)<= 0 && value.compareTo(min) >= 0);
+    }
+
+
+    /**
+     * Check that two metering region arrays are similar enough by ensuring that each of their width,
+     * height, and all corners are within {@code errorPercent} of each other.
+     *
+     * <p>Note that the length of the arrays must be the same, and each weight must be the same
+     * as well. We assume the order is also equivalent.</p>
+     *
+     * <p>At most 1 error per each dissimilar metering region is collected.</p>
+     *
+     * @param msg Message to be logged
+     * @param expected The reference 'expected' values to be used to check against
+     * @param actual The actual values that were received
+     * @param errorPercent Within how many percent the components should be
+     *
+     * @return {@code true} if all expects passed, {@code false} otherwise
+     */
+    public boolean expectMeteringRegionsAreSimilar(String msg,
+            MeteringRectangle[] expected, MeteringRectangle[] actual,
+            float errorPercent) {
+        String expectedActualMsg = String.format("expected (%s), actual (%s)",
+                Arrays.deepToString(expected), Arrays.deepToString(actual));
+
+        String differentSizesMsg = String.format(
+                "%s: rect lists are different sizes; %s",
+                msg, expectedActualMsg);
+
+        String differentWeightsMsg = String.format(
+                "%s: rect weights are different; %s",
+                msg, expectedActualMsg);
+
+        if (!expectTrue(differentSizesMsg, actual != null)) {
+            return false;
+        }
+
+        if (!expectEquals(differentSizesMsg, expected.length, actual.length)) return false;
+
+        boolean succ = true;;
+        for (int i = 0; i < expected.length; ++i) {
+            if (i < actual.length) {
+                // Avoid printing multiple errors for the same rectangle
+                if (!expectRectsAreSimilar(
+                        msg, expected[i].getRect(), actual[i].getRect(), errorPercent)) {
+                    succ = false;
+                    continue;
+                }
+                if (!expectEquals(differentWeightsMsg,
+                        expected[i].getMeteringWeight(), actual[i].getMeteringWeight())) {
+                    succ = false;
+                    continue;
+                }
+            }
+        }
+
+        return succ;
+    }
+
+    /**
+     * Check that two rectangles are similar enough by ensuring that their width, height,
+     * and all corners are within {@code errorPercent} of each other.
+     *
+     * <p>Only the first error is collected, to avoid spamming several error messages when
+     * the rectangle is hugely dissimilar.</p>
+     *
+     * @param msg Message to be logged
+     * @param expected The reference 'expected' value to be used to check against
+     * @param actual The actual value that was received
+     * @param errorPercent Within how many percent the components should be
+     *
+     * @return {@code true} if all expects passed, {@code false} otherwise
+     */
+    public boolean expectRectsAreSimilar(String msg, Rect expected, Rect actual,
+            float errorPercent) {
+        String formattedMsg = String.format("%s: rects are not similar enough; expected (%s), " +
+                "actual (%s), error percent (%s), reason: ",
+                msg, expected, actual, errorPercent);
+
+        if (!expectSimilarValues(
+                formattedMsg, "too wide", "too narrow", actual.width(), expected.width(),
+                errorPercent)) return false;
+
+        if (!expectSimilarValues(
+                formattedMsg, "too tall", "too short", actual.height(), expected.height(),
+                errorPercent)) return false;
+
+        if (!expectSimilarValues(
+                formattedMsg, "left pt too right", "left pt too left", actual.left, expected.left,
+                errorPercent)) return false;
+
+        if (!expectSimilarValues(
+                formattedMsg, "right pt too right", "right pt too left",
+                actual.right, expected.right, errorPercent)) return false;
+
+        if (!expectSimilarValues(
+                formattedMsg, "top pt too low", "top pt too high", actual.top, expected.top,
+                errorPercent)) return false;
+
+        if (!expectSimilarValues(
+                formattedMsg, "bottom pt too low", "bottom pt too high", actual.top, expected.top,
+                errorPercent)) return false;
+
+        return true;
+    }
+
+    /**
+     * Check that the rectangle is centered within a certain tolerance of {@code errorPercent},
+     * with respect to the {@code bounds} bounding rectangle.
+     *
+     * @param msg Message to be logged
+     * @param expectedBounds The width/height of the bounding rectangle
+     * @param actual The actual value that was received
+     * @param errorPercent Within how many percent the centering should be
+     */
+    public void expectRectCentered(String msg, Size expectedBounds, Rect actual,
+            float errorPercent) {
+        String formattedMsg = String.format("%s: rect should be centered; expected bounds (%s), " +
+                "actual (%s), error percent (%s), reason: ",
+                msg, expectedBounds, actual, errorPercent);
+
+        int centerBoundX = expectedBounds.getWidth() / 2;
+        int centerBoundY = expectedBounds.getHeight() / 2;
+
+        expectSimilarValues(
+                formattedMsg, "too low", "too high", actual.centerY(), centerBoundY,
+                errorPercent);
+
+        expectSimilarValues(
+                formattedMsg, "too right", "too left", actual.centerX(), centerBoundX,
+                errorPercent);
+    }
+
+    private boolean expectSimilarValues(
+            String formattedMsg, String tooSmall, String tooLarge, int actualValue,
+            int expectedValue, float errorPercent) {
+        boolean succ = true;
+        succ = expectTrue(formattedMsg + tooLarge,
+                actualValue <= (expectedValue * (1.0f + errorPercent))) && succ;
+        succ = expectTrue(formattedMsg + tooSmall,
+                actualValue >= (expectedValue * (1.0f - errorPercent))) && succ;
+
+        return succ;
+    }
+
+    public void expectNotNull(String msg, Object obj) {
+        checkThat(msg, obj, CoreMatchers.notNullValue());
+    }
+
+    /**
+     * Check if the values in the array are monotonically increasing (decreasing) and not all
+     * equal.
+     *
+     * @param array The array of values to be checked
+     * @param ascendingOrder The monotonicity ordering to be checked with
+     */
+    public <T extends Comparable<? super T>>  void checkArrayMonotonicityAndNotAllEqual(T[] array,
+            boolean ascendingOrder) {
+        String orderMsg = ascendingOrder ? ("increasing order") : ("decreasing order");
+        for (int i = 0; i < array.length - 1; i++) {
+            int compareResult = array[i + 1].compareTo(array[i]);
+            boolean condition = compareResult >= 0;
+            if (!ascendingOrder) {
+                condition = compareResult <= 0;
+            }
+
+            expectTrue(String.format("Adjacent values (%s and %s) %s monotonicity is broken",
+                    array[i].toString(), array[i + 1].toString(), orderMsg), condition);
+        }
+
+        expectTrue("All values of this array are equal: " + array[0].toString(),
+                array[0].compareTo(array[array.length - 1]) != 0);
+    }
+
+    /**
+     * Check if the key value is not null and return the value.
+     *
+     * @param request The {@link CameraCharacteristics} to get the key from.
+     * @param key The {@link CameraCharacteristics} key to be checked.
+     *
+     * @return The value of the key.
+     */
+    public <T> T expectKeyValueNotNull(CameraCharacteristics characteristics,
+            CameraCharacteristics.Key<T> key) {
+
+        T value = characteristics.get(key);
+        if (value == null) {
+            addMessage("Key " + key.getName() + " shouldn't be null");
+        }
+
+        return value;
+    }
+
+    /**
+     * Check if the key value is not null and return the value.
+     *
+     * @param request The {@link CaptureRequest#Builder} to get the key from.
+     * @param key The {@link CaptureRequest} key to be checked.
+     * @return The value of the key.
+     */
+    public <T> T expectKeyValueNotNull(Builder request, CaptureRequest.Key<T> key) {
+
+        T value = request.get(key);
+        if (value == null) {
+            addMessage("Key " + key.getName() + " shouldn't be null");
+        }
+
+        return value;
+    }
+
+    /**
+     * Check if the key value is not null and return the value.
+     *
+     * @param result The {@link CaptureResult} to get the key from.
+     * @param key The {@link CaptureResult} key to be checked.
+     * @return The value of the key.
+     */
+    public <T> T expectKeyValueNotNull(CaptureResult result, CaptureResult.Key<T> key) {
+        return expectKeyValueNotNull("", result, key);
+    }
+
+    /**
+     * Check if the key value is not null and return the value.
+     *
+     * @param msg The message to be logged.
+     * @param result The {@link CaptureResult} to get the key from.
+     * @param key The {@link CaptureResult} key to be checked.
+     * @return The value of the key.
+     */
+    public <T> T expectKeyValueNotNull(String msg, CaptureResult result, CaptureResult.Key<T> key) {
+
+        T value = result.get(key);
+        if (value == null) {
+            addMessage(msg + " Key " + key.getName() + " shouldn't be null");
+        }
+
+        return value;
+    }
+
+    /**
+     * Check if the key is non-null and the value is not equal to target.
+     *
+     * @param request The The {@link CaptureRequest#Builder} to get the key from.
+     * @param key The {@link CaptureRequest} key to be checked.
+     * @param expected The expected value of the CaptureRequest key.
+     */
+    public <T> void expectKeyValueNotEquals(
+            Builder request, CaptureRequest.Key<T> key, T expected) {
+        if (request == null || key == null || expected == null) {
+            throw new IllegalArgumentException("request, key and expected shouldn't be null");
+        }
+
+        T value;
+        if ((value = expectKeyValueNotNull(request, key)) == null) {
+            return;
+        }
+
+        String reason = "Key " + key.getName() + " shouldn't have value " + value.toString();
+        checkThat(reason, value, CoreMatchers.not(expected));
+    }
+
+    /**
+     * Check if the key is non-null and the value is not equal to target.
+     *
+     * @param result The {@link CaptureResult} to get the key from.
+     * @param key The {@link CaptureResult} key to be checked.
+     * @param expected The expected value of the CaptureResult key.
+     */
+    public <T> void expectKeyValueNotEquals(
+            CaptureResult result, CaptureResult.Key<T> key, T expected) {
+        if (result == null || key == null || expected == null) {
+            throw new IllegalArgumentException("result, key and expected shouldn't be null");
+        }
+
+        T value;
+        if ((value = expectKeyValueNotNull(result, key)) == null) {
+            return;
+        }
+
+        String reason = "Key " + key.getName() + " shouldn't have value " + value.toString();
+        checkThat(reason, value, CoreMatchers.not(expected));
+    }
+
+    /**
+     * Check if the value is non-null and the value is equal to target.
+     *
+     * @param result The  {@link CaptureResult} to lookup the value in.
+     * @param key The {@link CaptureResult} key to be checked.
+     * @param expected The expected value of the {@link CaptureResult} key.
+     */
+    public <T> void expectKeyValueEquals(CaptureResult result, CaptureResult.Key<T> key,
+            T expected) {
+        if (result == null || key == null || expected == null) {
+            throw new IllegalArgumentException("request, key and expected shouldn't be null");
+        }
+
+        T value;
+        if ((value = expectKeyValueNotNull(result, key)) == null) {
+            return;
+        }
+
+        String reason = "Key " + key.getName() + " value " + value.toString()
+                + " doesn't match the expected value " + expected.toString();
+        checkThat(reason, value, CoreMatchers.equalTo(expected));
+    }
+
+    /**
+     * Check if the key is non-null and the value is equal to target.
+     *
+     * <p>Only check non-null if the target is null.</p>
+     *
+     * @param request The The {@link CaptureRequest#Builder} to get the key from.
+     * @param key The {@link CaptureRequest} key to be checked.
+     * @param expected The expected value of the CaptureRequest key.
+     */
+    public <T> void expectKeyValueEquals(Builder request, CaptureRequest.Key<T> key, T expected) {
+        if (request == null || key == null || expected == null) {
+            throw new IllegalArgumentException("request, key and expected shouldn't be null");
+        }
+
+        T value;
+        if ((value = expectKeyValueNotNull(request, key)) == null) {
+            return;
+        }
+
+        String reason = "Key " + key.getName() + " value " + value.toString()
+                + " doesn't match the expected value " + expected.toString();
+        checkThat(reason, value, CoreMatchers.equalTo(expected));
+    }
+
+    /**
+     * Check if the element inside of the list are unique.
+     *
+     * @param msg The message to be logged
+     * @param list The list of values to be checked
+     */
+    public <T> void expectValuesUnique(String msg, List<T> list) {
+        Set<T> sizeSet = new HashSet<T>(list);
+        expectTrue(msg + " each size must be distinct", sizeSet.size() == list.size());
+    }
+}
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/helpers/CameraSessionUtils.java b/tests/tests/hardware/src/android/hardware/camera2/cts/helpers/CameraSessionUtils.java
new file mode 100644
index 0000000..d85a610
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/helpers/CameraSessionUtils.java
@@ -0,0 +1,236 @@
+/*
+ * 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.helpers;
+
+import android.hardware.camera2.CameraAccessException;
+import android.hardware.camera2.CameraCaptureSession;
+import android.hardware.camera2.CameraDevice;
+import android.hardware.camera2.CaptureFailure;
+import android.hardware.camera2.CaptureRequest;
+import android.hardware.camera2.TotalCaptureResult;
+import android.hardware.camera2.cts.CameraTestUtils;
+import android.os.Handler;
+import android.util.Log;
+import android.util.Pair;
+import android.view.Surface;
+
+import com.android.ex.camera2.blocking.BlockingCaptureListener;
+import com.android.ex.camera2.blocking.BlockingSessionListener;
+import com.android.ex.camera2.exceptions.TimeoutRuntimeException;
+
+import junit.framework.Assert;
+
+import org.mockito.internal.util.MockUtil;
+
+import java.util.List;
+import java.util.concurrent.LinkedBlockingQueue;
+
+import static com.android.internal.util.Preconditions.*;
+import static org.mockito.Mockito.*;
+
+/**
+ * A utility class with common functions setting up sessions and capturing.
+ */
+public class CameraSessionUtils extends Assert {
+    private static final String TAG = "CameraSessionUtils";
+    private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
+
+    /**
+     * A blocking listener class for synchronously opening and configuring sessions.
+     */
+    public static class SessionListener extends BlockingSessionListener {
+        private final LinkedBlockingQueue<CameraCaptureSession> mSessionQueue =
+                new LinkedBlockingQueue<>();
+
+        /**
+         * Get a new configured {@link CameraCaptureSession}.
+         *
+         * <p>
+         * This method is blocking, and will time out after
+         * {@link CameraTestUtils#SESSION_CONFIGURE_TIMEOUT_MS}.
+         * </p>
+         *
+         * @param device the {@link CameraDevice} to open a session for.
+         * @param outputs the {@link Surface} outputs to configure.
+         * @param handler the {@link Handler} to use for callbacks.
+         * @return a configured {@link CameraCaptureSession}.
+         *
+         * @throws CameraAccessException if any of the {@link CameraDevice} methods fail.
+         * @throws TimeoutRuntimeException if no result was received before the timeout.
+         */
+        public synchronized CameraCaptureSession getConfiguredSession(CameraDevice device,
+                                                                      List<Surface> outputs,
+                                                                      Handler handler)
+                throws CameraAccessException {
+            device.createCaptureSession(outputs, this, handler);
+            getStateWaiter().waitForState(SESSION_CONFIGURED,
+                    CameraTestUtils.SESSION_CONFIGURE_TIMEOUT_MS);
+            return mSessionQueue.poll();
+        }
+
+        @Override
+        public void onConfigured(CameraCaptureSession session) {
+            mSessionQueue.offer(session);
+            super.onConfigured(session);
+        }
+    }
+
+    /**
+     * A blocking listener class for synchronously capturing and results with a session.
+     */
+    public static class CaptureListener extends BlockingCaptureListener {
+        private final LinkedBlockingQueue<TotalCaptureResult> mResultQueue =
+                new LinkedBlockingQueue<>();
+        private final LinkedBlockingQueue<Long> mCaptureTimeQueue =
+                new LinkedBlockingQueue<>();
+
+        /**
+         * Capture a new result with the given {@link CameraCaptureSession}.
+         *
+         * <p>
+         * This method is blocking, and will time out after
+         * {@link CameraTestUtils#CAPTURE_RESULT_TIMEOUT_MS}.
+         * </p>
+         *
+         * @param session the {@link CameraCaptureSession} to use.
+         * @param request the {@link CaptureRequest} to capture with.
+         * @param handler the {@link Handler} to use for callbacks.
+         * @return a {@link Pair} containing the capture result and capture time.
+         *
+         * @throws CameraAccessException if any of the {@link CameraDevice} methods fail.
+         * @throws TimeoutRuntimeException if no result was received before the timeout.
+         */
+        public synchronized Pair<TotalCaptureResult, Long> getCapturedResult(
+                CameraCaptureSession session, CaptureRequest request, Handler handler)
+                throws CameraAccessException {
+            session.capture(request, this, handler);
+            getStateWaiter().waitForState(CAPTURE_COMPLETED,
+                    CameraTestUtils.CAPTURE_RESULT_TIMEOUT_MS);
+            return new Pair<>(mResultQueue.poll(), mCaptureTimeQueue.poll());
+        }
+
+        @Override
+        public void onCaptureStarted(CameraCaptureSession session, CaptureRequest request,
+                                     long timestamp) {
+            mCaptureTimeQueue.offer(timestamp);
+            super.onCaptureStarted(session, request, timestamp);
+        }
+
+        @Override
+        public void onCaptureCompleted(CameraCaptureSession session, CaptureRequest request,
+                                       TotalCaptureResult result) {
+            mResultQueue.offer(result);
+            super.onCaptureCompleted(session, request, result);
+        }
+    }
+
+    /**
+     * Get a mocked {@link CaptureListener}.
+     */
+    public static CaptureListener getMockCaptureListener() {
+        return spy(new CaptureListener());
+    }
+
+    /**
+     * Get a mocked {@link CaptureListener}.
+     */
+    public static SessionListener getMockSessionListener() {
+        return spy(new SessionListener());
+    }
+
+    /**
+     * Configure and return a new {@link CameraCaptureSession}.
+     *
+     * <p>
+     * This will verify that the correct session callbacks are called if a mocked listener is
+     * passed as the {@code listener} argument. This method is blocking, and will time out after
+     * {@link CameraTestUtils#SESSION_CONFIGURE_TIMEOUT_MS}.
+     * </p>
+     *
+     * @param listener a {@link SessionListener} to use for callbacks.
+     * @param device the {@link CameraDevice} to use.
+     * @param outputs the {@link Surface} outputs to configure.
+     * @param handler the {@link Handler} to call callbacks on.
+     * @return a configured {@link CameraCaptureSession}.
+     *
+     * @throws CameraAccessException if any of the {@link CameraDevice} methods fail.
+     * @throws TimeoutRuntimeException if no result was received before the timeout.
+     */
+    public static CameraCaptureSession configureAndVerifySession(SessionListener listener,
+                                                                 CameraDevice device,
+                                                                 List<Surface> outputs,
+                                                                 Handler handler)
+            throws CameraAccessException {
+        checkNotNull(listener);
+        checkNotNull(device);
+        checkNotNull(handler);
+        checkCollectionNotEmpty(outputs, "outputs");
+        checkCollectionElementsNotNull(outputs, "outputs");
+
+        CameraCaptureSession session = listener.getConfiguredSession(device, outputs, handler);
+        if (new MockUtil().isMock(listener)) {
+            verify(listener, never()).onConfigureFailed(any(CameraCaptureSession.class));
+            verify(listener, never()).onClosed(eq(session));
+            verify(listener, atLeastOnce()).onConfigured(eq(session));
+        }
+
+        checkNotNull(session);
+        return session;
+    }
+
+    /**
+     * Capture and return a new {@link TotalCaptureResult}.
+     *
+     * <p>
+     * This will verify that the correct capture callbacks are called if a mocked listener is
+     * passed as the {@code listener} argument. This method is blocking, and will time out after
+     * {@link CameraTestUtils#CAPTURE_RESULT_TIMEOUT_MS}.
+     * </p>
+     *
+     * @param listener a {@link CaptureListener} to use for callbacks.
+     * @param session the {@link CameraCaptureSession} to use.
+     * @param request the {@link CaptureRequest} to capture with.
+     * @param handler the {@link Handler} to call callbacks on.
+     * @return a {@link Pair} containing the capture result and capture time.
+     *
+     * @throws CameraAccessException if any of the {@link CameraDevice} methods fail.
+     * @throws TimeoutRuntimeException if no result was received before the timeout.
+     */
+    public static Pair<TotalCaptureResult, Long> captureAndVerifyResult(CaptureListener listener,
+            CameraCaptureSession session, CaptureRequest request, Handler handler)
+            throws CameraAccessException {
+        checkNotNull(listener);
+        checkNotNull(session);
+        checkNotNull(request);
+        checkNotNull(handler);
+
+        Pair<TotalCaptureResult, Long> result = listener.getCapturedResult(session, request,
+                handler);
+        if (new MockUtil().isMock(listener)) {
+            verify(listener, never()).onCaptureFailed(any(CameraCaptureSession.class),
+                    any(CaptureRequest.class), any(CaptureFailure.class));
+            verify(listener, atLeastOnce()).onCaptureStarted(eq(session), eq(request), anyLong());
+            verify(listener, atLeastOnce()).onCaptureCompleted(eq(session), eq(request),
+                    eq(result.first));
+        }
+
+        checkNotNull(result);
+        return result;
+    }
+
+    // Suppress default constructor for noninstantiability
+    private CameraSessionUtils() { throw new AssertionError(); }
+}
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/helpers/MaybeNull.java b/tests/tests/hardware/src/android/hardware/camera2/cts/helpers/MaybeNull.java
new file mode 100644
index 0000000..029ab03
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/helpers/MaybeNull.java
@@ -0,0 +1,76 @@
+/*
+ * 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.helpers;
+
+import java.io.Closeable;
+import java.io.IOException;
+
+/**
+ * Helper set of methods for dealing with objects that are sometimes {@code null}.
+ *
+ * <p>Used to remove common patterns like: <pre>{@code
+ * if (obj != null) {
+ *     obj.doSomething();
+ * }</pre>
+ *
+ * If this is common, consider adding {@code doSomething} to this class so that the code
+ * looks more like <pre>{@code
+ * MaybeNull.doSomething(obj);
+ * }</pre>
+ */
+public class MaybeNull {
+    /**
+     * Close the underlying {@link AutoCloseable}, if it's not {@code null}.
+     *
+     * @param closeable An object which implements {@link AutoCloseable}.
+     * @throws Exception If {@link AutoCloseable#close} fails.
+     */
+    public static <T extends AutoCloseable> void close(T closeable) throws Exception {
+        if (closeable != null) {
+            closeable.close();
+        }
+    }
+
+    /**
+     * Close the underlying {@link UncheckedCloseable}, if it's not {@code null}.
+     *
+     * <p>No checked exceptions are thrown. An unknown runtime exception might still
+     * be raised.</p>
+     *
+     * @param closeable An object which implements {@link UncheckedCloseable}.
+     */
+    public static <T extends UncheckedCloseable> void close(T closeable) {
+        if (closeable != null) {
+            closeable.close();
+        }
+    }
+
+    /**
+     * Close the underlying {@link Closeable}, if it's not {@code null}.
+     *
+     * @param closeable An object which implements {@link Closeable}.
+     * @throws Exception If {@link Closeable#close} fails.
+     */
+    public static <T extends Closeable> void close(T closeable) throws IOException {
+        if (closeable != null) {
+            closeable.close();
+        }
+    }
+
+    // Suppress default constructor for noninstantiability
+    private MaybeNull() { throw new AssertionError(); }
+}
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/helpers/Preconditions.java b/tests/tests/hardware/src/android/hardware/camera2/cts/helpers/Preconditions.java
new file mode 100644
index 0000000..8520a48
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/helpers/Preconditions.java
@@ -0,0 +1,112 @@
+/*
+ * 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.helpers;
+
+import java.util.Objects;
+
+/**
+ * Helper set of methods to perform precondition checks before starting method execution.
+ *
+ * <p>Typically used to sanity check arguments or the current object state.</p>
+ */
+public final class Preconditions {
+
+    /**
+     * Checks that the value has the expected bitwise flags set.
+     *
+     * @param argName Name of the argument
+     * @param arg Argument to check
+     * @param flagsName Name of the bitwise flags
+     * @param flags Bit flags to check.
+     * @return arg
+     *
+     * @throws IllegalArgumentException if the bitwise flags weren't set
+     */
+    public static int checkBitFlags(String argName, int arg, String flagsName, int flags) {
+        if ((arg & flags) == 0) {
+            throw new IllegalArgumentException(
+                    String.format("Argument '%s' must have flags '%s' set", argName, flagsName));
+        }
+
+        return arg;
+    }
+
+    /**
+     * Checks that the value is {@link Object#equals equal} to the expected value.
+     *
+     * @param argName Name of the argument
+     * @param arg Argument to check
+     * @param expectedName Name of the expected value
+     * @param expectedValue Expected value
+     * @return arg
+     *
+     * @throws IllegalArgumentException if the values were not equal
+     */
+    public static <T> T checkEquals(String argName, T arg,
+            String expectedName, T expectedValue) {
+        if (!Objects.equals(arg, expectedValue)) {
+            throw new IllegalArgumentException(
+                    String.format(
+                            "Argument '%s' must be equal to '%s' (was '%s', but expected '%s')",
+                            argName, expectedName, arg, expectedValue));
+        }
+
+        return arg;
+    }
+
+    /**
+     * Checks that the value is not {@code null}.
+     *
+     * <p>
+     * Returns the value directly, so you can use {@code checkNotNull("value", value)} inline.
+     * </p>
+     *
+     * @param argName Name of the argument
+     * @param arg Argument to check
+     * @return arg
+     *
+     * @throws NullPointerException if arg was {@code null}
+     */
+    public static <T> T checkNotNull(String argName, T arg) {
+        if (arg == null) {
+            throw new NullPointerException("Argument '" + argName + "' must not be null");
+        }
+
+        return arg;
+    }
+
+    /**
+     * Checks that the state is currently {@link true}.
+     *
+     * @param message Message to raise an exception with if the state checking fails.
+     * @param state State to check
+     *
+     * @throws IllegalStateException if state was {@code false}
+     *
+     * @return The state value (always {@code true}).
+     */
+    public static boolean checkState(String message, boolean state) {
+        if (!state) {
+            throw new IllegalStateException(message);
+        }
+
+        return state;
+    }
+
+    // Suppress default constructor for noninstantiability
+    private Preconditions() { throw new AssertionError(); }
+}
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/helpers/StaticMetadata.java b/tests/tests/hardware/src/android/hardware/camera2/cts/helpers/StaticMetadata.java
new file mode 100644
index 0000000..a79d3f3
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/helpers/StaticMetadata.java
@@ -0,0 +1,1865 @@
+/*
+ * 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.helpers;
+
+import android.graphics.Rect;
+import android.graphics.ImageFormat;
+import android.hardware.camera2.CameraCharacteristics;
+import android.hardware.camera2.CameraCharacteristics.Key;
+import android.hardware.camera2.CameraMetadata;
+import android.hardware.camera2.CaptureRequest;
+import android.hardware.camera2.CaptureResult;
+import android.hardware.camera2.cts.CameraTestUtils;
+import android.hardware.camera2.params.StreamConfigurationMap;
+import android.util.Range;
+import android.util.Size;
+import android.util.Log;
+import android.util.Rational;
+
+import junit.framework.Assert;
+
+import java.lang.reflect.Array;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Helpers to get common static info out of the camera.
+ *
+ * <p>Avoid boiler plate by putting repetitive get/set patterns in this class.</p>
+ *
+ * <p>Attempt to be durable against the camera device having bad or missing metadata
+ * by providing reasonable defaults and logging warnings when that happens.</p>
+ */
+public class StaticMetadata {
+
+    private static final String TAG = "StaticMetadata";
+    private static final int IGNORE_SIZE_CHECK = -1;
+
+    private static final long SENSOR_INFO_EXPOSURE_TIME_RANGE_MIN_AT_MOST = 100000L; // 100us
+    private static final long SENSOR_INFO_EXPOSURE_TIME_RANGE_MAX_AT_LEAST = 100000000; // 100ms
+    private static final int SENSOR_INFO_SENSITIVITY_RANGE_MIN_AT_MOST = 100;
+    private static final int SENSOR_INFO_SENSITIVITY_RANGE_MAX_AT_LEAST = 1600;
+    private static final int STATISTICS_INFO_MAX_FACE_COUNT_MIN_AT_LEAST = 4;
+    private static final int TONEMAP_MAX_CURVE_POINTS_AT_LEAST = 64;
+    private static final int CONTROL_AE_COMPENSATION_RANGE_DEFAULT_MIN = -2;
+    private static final int CONTROL_AE_COMPENSATION_RANGE_DEFAULT_MAX = 2;
+    private static final Rational CONTROL_AE_COMPENSATION_STEP_DEFAULT = new Rational(1, 2);
+    private static final byte REQUEST_PIPELINE_MAX_DEPTH_MAX = 8;
+
+    // TODO: Consider making this work across any metadata object, not just camera characteristics
+    private final CameraCharacteristics mCharacteristics;
+    private final CheckLevel mLevel;
+    private final CameraErrorCollector mCollector;
+
+    public enum CheckLevel {
+        /** Only log warnings for metadata check failures. Execution continues. */
+        WARN,
+        /**
+         * Use ErrorCollector to collect the metadata check failures, Execution
+         * continues.
+         */
+        COLLECT,
+        /** Assert the metadata check failures. Execution aborts. */
+        ASSERT
+    }
+
+    /**
+     * Construct a new StaticMetadata object.
+     *
+     *<p> Default constructor, only log warnings for the static metadata check failures</p>
+     *
+     * @param characteristics static info for a camera
+     * @throws IllegalArgumentException if characteristics was null
+     */
+    public StaticMetadata(CameraCharacteristics characteristics) {
+        this(characteristics, CheckLevel.WARN, /*collector*/null);
+    }
+
+    /**
+     * Construct a new StaticMetadata object with {@link CameraErrorCollector}.
+     * <p>
+     * When level is not {@link CheckLevel.COLLECT}, the {@link CameraErrorCollector} will be
+     * ignored, otherwise, it will be used to log the check failures.
+     * </p>
+     *
+     * @param characteristics static info for a camera
+     * @param collector The {@link CameraErrorCollector} used by this StaticMetadata
+     * @throws IllegalArgumentException if characteristics or collector was null.
+     */
+    public StaticMetadata(CameraCharacteristics characteristics, CameraErrorCollector collector) {
+        this(characteristics, CheckLevel.COLLECT, collector);
+    }
+
+    /**
+     * Construct a new StaticMetadata object with {@link CheckLevel} and
+     * {@link CameraErrorCollector}.
+     * <p>
+     * When level is not {@link CheckLevel.COLLECT}, the {@link CameraErrorCollector} will be
+     * ignored, otherwise, it will be used to log the check failures.
+     * </p>
+     *
+     * @param characteristics static info for a camera
+     * @param level The {@link CheckLevel} of this StaticMetadata
+     * @param collector The {@link CameraErrorCollector} used by this StaticMetadata
+     * @throws IllegalArgumentException if characteristics was null or level was
+     *         {@link CheckLevel.COLLECT} but collector was null.
+     */
+    public StaticMetadata(CameraCharacteristics characteristics, CheckLevel level,
+            CameraErrorCollector collector) {
+        if (characteristics == null) {
+            throw new IllegalArgumentException("characteristics was null");
+        }
+        if (level == CheckLevel.COLLECT && collector == null) {
+            throw new IllegalArgumentException("collector must valid when COLLECT level is set");
+        }
+
+        mCharacteristics = characteristics;
+        mLevel = level;
+        mCollector = collector;
+    }
+
+    /**
+     * Get the CameraCharacteristics associated with this StaticMetadata.
+     *
+     * @return A non-null CameraCharacteristics object
+     */
+    public CameraCharacteristics getCharacteristics() {
+        return mCharacteristics;
+    }
+
+    /**
+     * Whether or not the hardware level reported by android.info.supportedHardwareLevel
+     * is {@value CameraMetadata#INFO_SUPPORTED_HARDWARE_LEVEL_FULL}.
+     *
+     * <p>If the camera device is not reporting the hardwareLevel, this
+     * will cause the test to fail.</p>
+     *
+     * @return {@code true} if the device is {@code FULL}, {@code false} otherwise.
+     */
+    public boolean isHardwareLevelFull() {
+        return getHardwareLevelChecked() == CameraMetadata.INFO_SUPPORTED_HARDWARE_LEVEL_FULL;
+    }
+
+    /**
+     * Whether or not the hardware level reported by android.info.supportedHardwareLevel
+     * Return the supported hardware level of the device, or fail if no value is reported.
+     *
+     * @return the supported hardware level as a constant defined for
+     *      {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL}.
+     */
+    public int getHardwareLevelChecked() {
+        Integer hwLevel = getValueFromKeyNonNull(
+                CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL);
+        if (hwLevel == null) {
+            Assert.fail("No supported hardware level reported.");
+        }
+        return hwLevel;
+    }
+
+    /**
+     * Whether or not the hardware level reported by android.info.supportedHardwareLevel
+     * is {@value CameraMetadata#INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY}.
+     *
+     * <p>If the camera device is not reporting the hardwareLevel, this
+     * will cause the test to fail.</p>
+     *
+     * @return {@code true} if the device is {@code LEGACY}, {@code false} otherwise.
+     */
+    public boolean isHardwareLevelLegacy() {
+        return getHardwareLevelChecked() == CameraMetadata.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY;
+    }
+
+    /**
+     * Whether or not the per frame control is supported by the camera device.
+     *
+     * @return {@code true} if per frame control is supported, {@code false} otherwise.
+     */
+    public boolean isPerFrameControlSupported() {
+        return getSyncMaxLatency() == CameraMetadata.SYNC_MAX_LATENCY_PER_FRAME_CONTROL;
+    }
+
+    /**
+     * Get the maximum number of frames to wait for a request settings being applied
+     *
+     * @return CameraMetadata.SYNC_MAX_LATENCY_UNKNOWN for unknown latency
+     *         CameraMetadata.SYNC_MAX_LATENCY_PER_FRAME_CONTROL for per frame control
+     *         a positive int otherwise
+     */
+    public int getSyncMaxLatency() {
+        Integer value = getValueFromKeyNonNull(CameraCharacteristics.SYNC_MAX_LATENCY);
+        if (value == null) {
+            return CameraMetadata.SYNC_MAX_LATENCY_UNKNOWN;
+        }
+        return value;
+    }
+
+    /**
+     * Whether or not the hardware level reported by android.info.supportedHardwareLevel
+     * is {@value CameraMetadata#INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED}.
+     *
+     * <p>If the camera device is incorrectly reporting the hardwareLevel, this
+     * will always return {@code true}.</p>
+     *
+     * @return {@code true} if the device is {@code LIMITED}, {@code false} otherwise.
+     */
+    public boolean isHardwareLevelLimited() {
+        return getHardwareLevelChecked() == CameraMetadata.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED;
+    }
+
+    /**
+     * Whether or not the hardware level reported by {@code android.info.supportedHardwareLevel}
+     * is at least {@link CameraMetadata#INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED}.
+     *
+     * <p>If the camera device is incorrectly reporting the hardwareLevel, this
+     * will always return {@code false}.</p>
+     *
+     * @return
+     *          {@code true} if the device is {@code LIMITED} or {@code FULL},
+     *          {@code false} otherwise (i.e. LEGACY).
+     */
+    public boolean isHardwareLevelLimitedOrBetter() {
+        Integer hwLevel = getValueFromKeyNonNull(
+                CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL);
+
+        if (hwLevel == null) {
+            return false;
+        }
+
+        // Normal. Device could be limited.
+        int hwLevelInt = hwLevel;
+        return hwLevelInt == CameraMetadata.INFO_SUPPORTED_HARDWARE_LEVEL_FULL ||
+                hwLevelInt == CameraMetadata.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED;
+    }
+
+    /**
+     * Get the exposure time value and clamp to the range if needed.
+     *
+     * @param exposure Input exposure time value to check.
+     * @return Exposure value in the legal range.
+     */
+    public long getExposureClampToRange(long exposure) {
+        long minExposure = getExposureMinimumOrDefault(Long.MAX_VALUE);
+        long maxExposure = getExposureMaximumOrDefault(Long.MIN_VALUE);
+        if (minExposure > SENSOR_INFO_EXPOSURE_TIME_RANGE_MIN_AT_MOST) {
+            failKeyCheck(CameraCharacteristics.SENSOR_INFO_EXPOSURE_TIME_RANGE,
+                    String.format(
+                    "Min value %d is too large, set to maximal legal value %d",
+                    minExposure, SENSOR_INFO_EXPOSURE_TIME_RANGE_MIN_AT_MOST));
+            minExposure = SENSOR_INFO_EXPOSURE_TIME_RANGE_MIN_AT_MOST;
+        }
+        if (maxExposure < SENSOR_INFO_EXPOSURE_TIME_RANGE_MAX_AT_LEAST) {
+            failKeyCheck(CameraCharacteristics.SENSOR_INFO_EXPOSURE_TIME_RANGE,
+                    String.format(
+                    "Max value %d is too small, set to minimal legal value %d",
+                    maxExposure, SENSOR_INFO_EXPOSURE_TIME_RANGE_MAX_AT_LEAST));
+            maxExposure = SENSOR_INFO_EXPOSURE_TIME_RANGE_MAX_AT_LEAST;
+        }
+
+        return Math.max(minExposure, Math.min(maxExposure, exposure));
+    }
+
+    /**
+     * Check if the camera device support focuser.
+     *
+     * @return true if camera device support focuser, false otherwise.
+     */
+    public boolean hasFocuser() {
+        if (areKeysAvailable(CameraCharacteristics.LENS_INFO_MINIMUM_FOCUS_DISTANCE)) {
+            // LEGACY devices don't have lens.info.minimumFocusDistance, so guard this query
+            return (getMinimumFocusDistanceChecked() > 0);
+        } else {
+            // Check available AF modes
+            int[] availableAfModes = mCharacteristics.get(
+                    CameraCharacteristics.CONTROL_AF_AVAILABLE_MODES);
+
+            if (availableAfModes == null) {
+                return false;
+            }
+
+            // Assume that if we have an AF mode which doesn't ignore AF trigger, we have a focuser
+            boolean hasFocuser = false;
+            loop: for (int mode : availableAfModes) {
+                switch (mode) {
+                    case CameraMetadata.CONTROL_AF_MODE_AUTO:
+                    case CameraMetadata.CONTROL_AF_MODE_CONTINUOUS_PICTURE:
+                    case CameraMetadata.CONTROL_AF_MODE_CONTINUOUS_VIDEO:
+                    case CameraMetadata.CONTROL_AF_MODE_MACRO:
+                        hasFocuser = true;
+                        break loop;
+                }
+            }
+
+            return hasFocuser;
+        }
+    }
+
+    /**
+     * Check if the camera device has flash unit.
+     * @return true if flash unit is available, false otherwise.
+     */
+    public boolean hasFlash() {
+        return getFlashInfoChecked();
+    }
+
+    /**
+     * Get minimum focus distance.
+     *
+     * @return minimum focus distance, 0 if minimum focus distance is invalid.
+     */
+    public float getMinimumFocusDistanceChecked() {
+        Key<Float> key = CameraCharacteristics.LENS_INFO_MINIMUM_FOCUS_DISTANCE;
+        Float minFocusDistance;
+
+        /**
+         * android.lens.info.minimumFocusDistance - required for FULL and MANUAL_SENSOR-capable
+         *   devices; optional for all other devices.
+         */
+        if (isHardwareLevelFull() || isCapabilitySupported(
+                CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR)) {
+            minFocusDistance = getValueFromKeyNonNull(key);
+        } else {
+            minFocusDistance = mCharacteristics.get(key);
+        }
+
+        if (minFocusDistance == null) {
+            return 0.0f;
+        }
+
+        checkTrueForKey(key, " minFocusDistance value shouldn't be negative",
+                minFocusDistance >= 0);
+        if (minFocusDistance < 0) {
+            minFocusDistance = 0.0f;
+        }
+
+        return minFocusDistance;
+    }
+
+    /**
+     * Get focusDistanceCalibration.
+     *
+     * @return focusDistanceCalibration, UNCALIBRATED if value is invalid.
+     */
+    public int getFocusDistanceCalibrationChecked() {
+        Key<Integer> key = CameraCharacteristics.LENS_INFO_FOCUS_DISTANCE_CALIBRATION;
+        Integer calibration = getValueFromKeyNonNull(key);
+
+        if (calibration == null) {
+            return CameraMetadata.LENS_INFO_FOCUS_DISTANCE_CALIBRATION_UNCALIBRATED;
+        }
+
+        checkTrueForKey(key, " value is out of range" ,
+                calibration >= CameraMetadata.LENS_INFO_FOCUS_DISTANCE_CALIBRATION_UNCALIBRATED &&
+                calibration <= CameraMetadata.LENS_INFO_FOCUS_DISTANCE_CALIBRATION_CALIBRATED);
+
+        return calibration;
+    }
+
+    /**
+     * Get max AE regions and do sanity check.
+     *
+     * @return AE max regions supported by the camera device
+     */
+    public int getAeMaxRegionsChecked() {
+        Integer regionCount = getValueFromKeyNonNull(CameraCharacteristics.CONTROL_MAX_REGIONS_AE);
+        if (regionCount == null) {
+            return 0;
+        }
+        return regionCount;
+    }
+
+    /**
+     * Get max AWB regions and do sanity check.
+     *
+     * @return AWB max regions supported by the camera device
+     */
+    public int getAwbMaxRegionsChecked() {
+        Integer regionCount = getValueFromKeyNonNull(CameraCharacteristics.CONTROL_MAX_REGIONS_AWB);
+        if (regionCount == null) {
+            return 0;
+        }
+        return regionCount;
+    }
+
+    /**
+     * Get max AF regions and do sanity check.
+     *
+     * @return AF max regions supported by the camera device
+     */
+    public int getAfMaxRegionsChecked() {
+        Integer regionCount = getValueFromKeyNonNull(CameraCharacteristics.CONTROL_MAX_REGIONS_AF);
+        if (regionCount == null) {
+            return 0;
+        }
+        return regionCount;
+    }
+    /**
+     * Get the available anti-banding modes.
+     *
+     * @return The array contains available anti-banding modes.
+     */
+    public int[] getAeAvailableAntiBandingModesChecked() {
+        Key<int[]> key = CameraCharacteristics.CONTROL_AE_AVAILABLE_ANTIBANDING_MODES;
+        int[] modes = getValueFromKeyNonNull(key);
+
+        boolean foundAuto = false;
+        for (int mode : modes) {
+            checkTrueForKey(key, "mode value " + mode + " is out if range",
+                    mode >= CameraMetadata.CONTROL_AE_ANTIBANDING_MODE_OFF ||
+                    mode <= CameraMetadata.CONTROL_AE_ANTIBANDING_MODE_AUTO);
+            if (mode == CameraMetadata.CONTROL_AE_ANTIBANDING_MODE_AUTO) {
+                foundAuto = true;
+                return modes;
+            }
+        }
+        // Must contain AUTO mode.
+        checkTrueForKey(key, "AUTO mode is missing", foundAuto);
+
+        return modes;
+    }
+
+    /**
+     * Check if the antibanding OFF mode is supported.
+     *
+     * @return true if antibanding OFF mode is supported, false otherwise.
+     */
+    public boolean isAntiBandingOffModeSupported() {
+        List<Integer> antiBandingModes =
+                Arrays.asList(CameraTestUtils.toObject(getAeAvailableAntiBandingModesChecked()));
+
+        return antiBandingModes.contains(CameraMetadata.CONTROL_AE_ANTIBANDING_MODE_OFF);
+    }
+
+    public Boolean getFlashInfoChecked() {
+        Key<Boolean> key = CameraCharacteristics.FLASH_INFO_AVAILABLE;
+        Boolean hasFlash = getValueFromKeyNonNull(key);
+
+        // In case the failOnKey only gives warning.
+        if (hasFlash == null) {
+            return false;
+        }
+
+        return hasFlash;
+    }
+
+    public int[] getAvailableTestPatternModesChecked() {
+        Key<int[]> key =
+                CameraCharacteristics.SENSOR_AVAILABLE_TEST_PATTERN_MODES;
+        int[] modes = getValueFromKeyNonNull(key);
+
+        if (modes == null) {
+            return new int[0];
+        }
+
+        int expectValue = CameraCharacteristics.SENSOR_TEST_PATTERN_MODE_OFF;
+        Integer[] boxedModes = CameraTestUtils.toObject(modes);
+        checkTrueForKey(key, " value must contain OFF mode",
+                Arrays.asList(boxedModes).contains(expectValue));
+
+        return modes;
+    }
+
+    /**
+     * Get available thumbnail sizes and do the sanity check.
+     *
+     * @return The array of available thumbnail sizes
+     */
+    public Size[] getAvailableThumbnailSizesChecked() {
+        Key<Size[]> key = CameraCharacteristics.JPEG_AVAILABLE_THUMBNAIL_SIZES;
+        Size[] sizes = getValueFromKeyNonNull(key);
+        final List<Size> sizeList = Arrays.asList(sizes);
+
+        // Size must contain (0, 0).
+        checkTrueForKey(key, "size should contain (0, 0)", sizeList.contains(new Size(0, 0)));
+
+        // Each size must be distinct.
+        checkElementDistinct(key, sizeList);
+
+        // Must be sorted in ascending order by area, by width if areas are same.
+        List<Size> orderedSizes =
+                CameraTestUtils.getAscendingOrderSizes(sizeList, /*ascending*/true);
+        checkTrueForKey(key, "Sizes should be in ascending order: Original " + sizeList.toString()
+                + ", Expected " + orderedSizes.toString(), orderedSizes.equals(sizeList));
+
+        // TODO: Aspect ratio match, need wait for android.scaler.availableStreamConfigurations
+        // implementation see b/12958122.
+
+        return sizes;
+    }
+
+    /**
+     * Get available focal lengths and do the sanity check.
+     *
+     * @return The array of available focal lengths
+     */
+    public float[] getAvailableFocalLengthsChecked() {
+        Key<float[]> key = CameraCharacteristics.LENS_INFO_AVAILABLE_FOCAL_LENGTHS;
+        float[] focalLengths = getValueFromKeyNonNull(key);
+
+        checkTrueForKey(key, "Array should contain at least one element", focalLengths.length >= 1);
+
+        for (int i = 0; i < focalLengths.length; i++) {
+            checkTrueForKey(key,
+                    String.format("focalLength[%d] %f should be positive.", i, focalLengths[i]),
+                    focalLengths[i] > 0);
+        }
+        checkElementDistinct(key, Arrays.asList(CameraTestUtils.toObject(focalLengths)));
+
+        return focalLengths;
+    }
+
+    /**
+     * Get available apertures and do the sanity check.
+     *
+     * @return The non-null array of available apertures
+     */
+    public float[] getAvailableAperturesChecked() {
+        Key<float[]> key = CameraCharacteristics.LENS_INFO_AVAILABLE_APERTURES;
+        float[] apertures = getValueFromKeyNonNull(key);
+
+        checkTrueForKey(key, "Array should contain at least one element", apertures.length >= 1);
+
+        for (int i = 0; i < apertures.length; i++) {
+            checkTrueForKey(key,
+                    String.format("apertures[%d] %f should be positive.", i, apertures[i]),
+                    apertures[i] > 0);
+        }
+        checkElementDistinct(key, Arrays.asList(CameraTestUtils.toObject(apertures)));
+
+        return apertures;
+    }
+
+    /**
+     * Get and check the available hot pixel map modes.
+     *
+     * @return the available hot pixel map modes
+     */
+    public int[] getAvailableHotPixelMapModesChecked() {
+        Key<int[]> key = CameraCharacteristics.HOT_PIXEL_AVAILABLE_HOT_PIXEL_MODES;
+        int[] modes = getValueFromKeyNonNull(key);
+
+        if (modes == null) {
+            return new int[0];
+        }
+
+        List<Integer> modeList = Arrays.asList(CameraTestUtils.toObject(modes));
+        if (isHardwareLevelFull()) {
+            checkTrueForKey(key, "Full-capability camera devices must support FAST mode",
+                    modeList.contains(CameraMetadata.HOT_PIXEL_MODE_FAST));
+        }
+        checkElementDistinct(key, modeList);
+        checkArrayValuesInRange(key, modes, CameraMetadata.HOT_PIXEL_MODE_OFF,
+                CameraMetadata.HOT_PIXEL_MODE_HIGH_QUALITY);
+
+        return modes;
+    }
+
+    /**
+     * Get and check available face detection modes.
+     *
+     * @return The non-null array of available face detection modes
+     */
+    public int[] getAvailableFaceDetectModesChecked() {
+        Key<int[]> key = CameraCharacteristics.STATISTICS_INFO_AVAILABLE_FACE_DETECT_MODES;
+        int[] modes = getValueFromKeyNonNull(key);
+
+        if (modes == null) {
+            return new int[0];
+        }
+
+        List<Integer> modeList = Arrays.asList(CameraTestUtils.toObject(modes));
+        checkTrueForKey(key, "Array should contain OFF mode",
+                modeList.contains(CameraMetadata.STATISTICS_FACE_DETECT_MODE_OFF));
+        checkElementDistinct(key, modeList);
+        checkArrayValuesInRange(key, modes, CameraMetadata.STATISTICS_FACE_DETECT_MODE_OFF,
+                CameraMetadata.STATISTICS_FACE_DETECT_MODE_FULL);
+
+        return modes;
+    }
+
+    /**
+     * Get and check max face detected count.
+     *
+     * @return max number of faces that can be detected
+     */
+    public int getMaxFaceCountChecked() {
+        Key<Integer> key = CameraCharacteristics.STATISTICS_INFO_MAX_FACE_COUNT;
+        Integer count = getValueFromKeyNonNull(key);
+
+        if (count == null) {
+            return 0;
+        }
+
+        List<Integer> faceDetectModes =
+                Arrays.asList(CameraTestUtils.toObject(getAvailableFaceDetectModesChecked()));
+        if (faceDetectModes.contains(CameraMetadata.STATISTICS_FACE_DETECT_MODE_OFF) &&
+                faceDetectModes.size() == 1) {
+            checkTrueForKey(key, " value must be 0 if only OFF mode is supported in "
+                    + "availableFaceDetectionModes", count == 0);
+        } else {
+            int maxFaceCountAtLeat = STATISTICS_INFO_MAX_FACE_COUNT_MIN_AT_LEAST;
+            checkTrueForKey(key, " value must be no less than " + maxFaceCountAtLeat + " if SIMPLE"
+                    + "or FULL is also supported in availableFaceDetectionModes",
+                    count >= maxFaceCountAtLeat);
+        }
+
+        return count;
+    }
+
+    /**
+     * Get and check the available tone map modes.
+     *
+     * @return the available tone map modes
+     */
+    public int[] getAvailableToneMapModesChecked() {
+        Key<int[]> key = CameraCharacteristics.TONEMAP_AVAILABLE_TONE_MAP_MODES;
+        int[] modes = getValueFromKeyNonNull(key);
+
+        if (modes == null) {
+            return new int[0];
+        }
+
+        List<Integer> modeList = Arrays.asList(CameraTestUtils.toObject(modes));
+        checkTrueForKey(key, " Camera devices must always support FAST mode",
+                modeList.contains(CameraMetadata.TONEMAP_MODE_FAST));
+        if (isHardwareLevelFull()) {
+            checkTrueForKey(key, "Full-capability camera devices must support"
+                    + "CONTRAST_CURVE mode",
+                    modeList.contains(CameraMetadata.TONEMAP_MODE_CONTRAST_CURVE) &&
+                    modeList.contains(CameraMetadata.TONEMAP_MODE_FAST));
+        }
+        checkElementDistinct(key, modeList);
+        checkArrayValuesInRange(key, modes, CameraMetadata.TONEMAP_MODE_CONTRAST_CURVE,
+                CameraMetadata.TONEMAP_MODE_HIGH_QUALITY);
+
+        return modes;
+    }
+
+    /**
+     * Get and check max tonemap curve point.
+     *
+     * @return Max tonemap curve points.
+     */
+    public int getMaxTonemapCurvePointChecked() {
+        Key<Integer> key = CameraCharacteristics.TONEMAP_MAX_CURVE_POINTS;
+        Integer count = getValueFromKeyNonNull(key);
+
+        if (count == null) {
+            return 0;
+        }
+
+        List<Integer> modeList =
+                Arrays.asList(CameraTestUtils.toObject(getAvailableToneMapModesChecked()));
+        if (modeList.contains(CameraMetadata.TONEMAP_MODE_CONTRAST_CURVE)) {
+            checkTrueForKey(key, "Full-capability camera device must support maxCurvePoints "
+                    + ">= " + TONEMAP_MAX_CURVE_POINTS_AT_LEAST,
+                    count >= TONEMAP_MAX_CURVE_POINTS_AT_LEAST);
+        }
+
+        return count;
+    }
+
+    /**
+     * Get and check pixel array size.
+     */
+    public Size getPixelArraySizeChecked() {
+        Key<Size> key = CameraCharacteristics.SENSOR_INFO_PIXEL_ARRAY_SIZE;
+        Size pixelArray = getValueFromKeyNonNull(key);
+        if (pixelArray == null) {
+            return new Size(0, 0);
+        }
+
+        return pixelArray;
+    }
+
+    /**
+     * Get and check active array size.
+     */
+    public Rect getActiveArraySizeChecked() {
+        Key<Rect> key = CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE;
+        Rect activeArray = getValueFromKeyNonNull(key);
+
+        if (activeArray == null) {
+            return new Rect(0, 0, 0, 0);
+        }
+
+        Size pixelArraySize = getPixelArraySizeChecked();
+        checkTrueForKey(key, "values left/top are invalid", activeArray.left >= 0 && activeArray.top >= 0);
+        checkTrueForKey(key, "values width/height are invalid",
+                activeArray.width() <= pixelArraySize.getWidth() &&
+                activeArray.height() <= pixelArraySize.getHeight());
+
+        return activeArray;
+    }
+
+    /**
+     * Get the sensitivity value and clamp to the range if needed.
+     *
+     * @param sensitivity Input sensitivity value to check.
+     * @return Sensitivity value in legal range.
+     */
+    public int getSensitivityClampToRange(int sensitivity) {
+        int minSensitivity = getSensitivityMinimumOrDefault(Integer.MAX_VALUE);
+        int maxSensitivity = getSensitivityMaximumOrDefault(Integer.MIN_VALUE);
+        if (minSensitivity > SENSOR_INFO_SENSITIVITY_RANGE_MIN_AT_MOST) {
+            failKeyCheck(CameraCharacteristics.SENSOR_INFO_SENSITIVITY_RANGE,
+                    String.format(
+                    "Min value %d is too large, set to maximal legal value %d",
+                    minSensitivity, SENSOR_INFO_SENSITIVITY_RANGE_MIN_AT_MOST));
+            minSensitivity = SENSOR_INFO_SENSITIVITY_RANGE_MIN_AT_MOST;
+        }
+        if (maxSensitivity < SENSOR_INFO_SENSITIVITY_RANGE_MAX_AT_LEAST) {
+            failKeyCheck(CameraCharacteristics.SENSOR_INFO_SENSITIVITY_RANGE,
+                    String.format(
+                    "Max value %d is too small, set to minimal legal value %d",
+                    maxSensitivity, SENSOR_INFO_SENSITIVITY_RANGE_MAX_AT_LEAST));
+            maxSensitivity = SENSOR_INFO_SENSITIVITY_RANGE_MAX_AT_LEAST;
+        }
+
+        return Math.max(minSensitivity, Math.min(maxSensitivity, sensitivity));
+    }
+
+    /**
+     * Get maxAnalogSensitivity for a camera device.
+     * <p>
+     * This is only available for FULL capability device, return 0 if it is unavailable.
+     * </p>
+     *
+     * @return maxAnalogSensitivity, 0 if it is not available.
+     */
+    public int getMaxAnalogSensitivityChecked() {
+
+        Key<Integer> key = CameraCharacteristics.SENSOR_MAX_ANALOG_SENSITIVITY;
+        Integer maxAnalogsensitivity = mCharacteristics.get(key);
+        if (maxAnalogsensitivity == null) {
+            if (isHardwareLevelFull()) {
+                Assert.fail("Full device should report max analog sensitivity");
+            }
+            return 0;
+        }
+
+        int minSensitivity = getSensitivityMinimumOrDefault();
+        int maxSensitivity = getSensitivityMaximumOrDefault();
+        checkTrueForKey(key, " Max analog sensitivity " + maxAnalogsensitivity
+                + " should be no larger than max sensitivity " + maxSensitivity,
+                maxAnalogsensitivity <= maxSensitivity);
+        checkTrueForKey(key, " Max analog sensitivity " + maxAnalogsensitivity
+                + " should be larger than min sensitivity " + maxSensitivity,
+                maxAnalogsensitivity > minSensitivity);
+
+        return maxAnalogsensitivity;
+    }
+
+    /**
+     * Get hyperfocalDistance and do the sanity check.
+     * <p>
+     * Note that, this tag is optional, will return -1 if this tag is not
+     * available.
+     * </p>
+     *
+     * @return hyperfocalDistance of this device, -1 if this tag is not available.
+     */
+    public float getHyperfocalDistanceChecked() {
+        Key<Float> key = CameraCharacteristics.LENS_INFO_HYPERFOCAL_DISTANCE;
+        Float hyperfocalDistance = getValueFromKeyNonNull(key);
+        if (hyperfocalDistance == null) {
+            return -1;
+        }
+
+        if (hasFocuser()) {
+            float minFocusDistance = getMinimumFocusDistanceChecked();
+            checkTrueForKey(key, String.format(" hyperfocal distance %f should be in the range of"
+                    + " should be in the range of (%f, %f]", hyperfocalDistance, 0.0f,
+                    minFocusDistance),
+                    hyperfocalDistance > 0 && hyperfocalDistance <= minFocusDistance);
+        }
+
+        return hyperfocalDistance;
+    }
+
+    /**
+     * Get the minimum value for a sensitivity range from android.sensor.info.sensitivityRange.
+     *
+     * <p>If the camera is incorrectly reporting values, log a warning and return
+     * the default value instead, which is the largest minimum value required to be supported
+     * by all camera devices.</p>
+     *
+     * @return The value reported by the camera device or the defaultValue otherwise.
+     */
+    public int getSensitivityMinimumOrDefault() {
+        return getSensitivityMinimumOrDefault(SENSOR_INFO_SENSITIVITY_RANGE_MIN_AT_MOST);
+    }
+
+    /**
+     * Get the minimum value for a sensitivity range from android.sensor.info.sensitivityRange.
+     *
+     * <p>If the camera is incorrectly reporting values, log a warning and return
+     * the default value instead.</p>
+     *
+     * @param defaultValue Value to return if no legal value is available
+     * @return The value reported by the camera device or the defaultValue otherwise.
+     */
+    public int getSensitivityMinimumOrDefault(int defaultValue) {
+        Range<Integer> range = getValueFromKeyNonNull(
+                CameraCharacteristics.SENSOR_INFO_SENSITIVITY_RANGE);
+        if (range == null) {
+            failKeyCheck(CameraCharacteristics.SENSOR_INFO_SENSITIVITY_RANGE,
+                    "had no valid minimum value; using default of " + defaultValue);
+            return defaultValue;
+        }
+        return range.getLower();
+    }
+
+    /**
+     * Get the maximum value for a sensitivity range from android.sensor.info.sensitivityRange.
+     *
+     * <p>If the camera is incorrectly reporting values, log a warning and return
+     * the default value instead, which is the smallest maximum value required to be supported
+     * by all camera devices.</p>
+     *
+     * @return The value reported by the camera device or the defaultValue otherwise.
+     */
+    public int getSensitivityMaximumOrDefault() {
+        return getSensitivityMaximumOrDefault(SENSOR_INFO_SENSITIVITY_RANGE_MAX_AT_LEAST);
+    }
+
+    /**
+     * Get the maximum value for a sensitivity range from android.sensor.info.sensitivityRange.
+     *
+     * <p>If the camera is incorrectly reporting values, log a warning and return
+     * the default value instead.</p>
+     *
+     * @param defaultValue Value to return if no legal value is available
+     * @return The value reported by the camera device or the defaultValue otherwise.
+     */
+    public int getSensitivityMaximumOrDefault(int defaultValue) {
+        Range<Integer> range = getValueFromKeyNonNull(
+                CameraCharacteristics.SENSOR_INFO_SENSITIVITY_RANGE);
+        if (range == null) {
+            failKeyCheck(CameraCharacteristics.SENSOR_INFO_SENSITIVITY_RANGE,
+                    "had no valid maximum value; using default of " + defaultValue);
+            return defaultValue;
+        }
+        return range.getUpper();
+    }
+
+    /**
+     * Get the minimum value for an exposure range from android.sensor.info.exposureTimeRange.
+     *
+     * <p>If the camera is incorrectly reporting values, log a warning and return
+     * the default value instead.</p>
+     *
+     * @param defaultValue Value to return if no legal value is available
+     * @return The value reported by the camera device or the defaultValue otherwise.
+     */
+    public long getExposureMinimumOrDefault(long defaultValue) {
+        Range<Long> range = getValueFromKeyNonNull(
+                CameraCharacteristics.SENSOR_INFO_EXPOSURE_TIME_RANGE);
+        if (range == null) {
+            failKeyCheck(CameraCharacteristics.SENSOR_INFO_EXPOSURE_TIME_RANGE,
+                    "had no valid minimum value; using default of " + defaultValue);
+            return defaultValue;
+        }
+        return range.getLower();
+    }
+
+    /**
+     * Get the minimum value for an exposure range from android.sensor.info.exposureTimeRange.
+     *
+     * <p>If the camera is incorrectly reporting values, log a warning and return
+     * the default value instead, which is the largest minimum value required to be supported
+     * by all camera devices.</p>
+     *
+     * @return The value reported by the camera device or the defaultValue otherwise.
+     */
+    public long getExposureMinimumOrDefault() {
+        return getExposureMinimumOrDefault(SENSOR_INFO_EXPOSURE_TIME_RANGE_MIN_AT_MOST);
+    }
+
+    /**
+     * Get the maximum value for an exposure range from android.sensor.info.exposureTimeRange.
+     *
+     * <p>If the camera is incorrectly reporting values, log a warning and return
+     * the default value instead.</p>
+     *
+     * @param defaultValue Value to return if no legal value is available
+     * @return The value reported by the camera device or the defaultValue otherwise.
+     */
+    public long getExposureMaximumOrDefault(long defaultValue) {
+        Range<Long> range = getValueFromKeyNonNull(
+                CameraCharacteristics.SENSOR_INFO_EXPOSURE_TIME_RANGE);
+        if (range == null) {
+            failKeyCheck(CameraCharacteristics.SENSOR_INFO_EXPOSURE_TIME_RANGE,
+                    "had no valid maximum value; using default of " + defaultValue);
+            return defaultValue;
+        }
+        return range.getUpper();
+    }
+
+    /**
+     * Get the maximum value for an exposure range from android.sensor.info.exposureTimeRange.
+     *
+     * <p>If the camera is incorrectly reporting values, log a warning and return
+     * the default value instead, which is the smallest maximum value required to be supported
+     * by all camera devices.</p>
+     *
+     * @return The value reported by the camera device or the defaultValue otherwise.
+     */
+    public long getExposureMaximumOrDefault() {
+        return getExposureMaximumOrDefault(SENSOR_INFO_EXPOSURE_TIME_RANGE_MAX_AT_LEAST);
+    }
+
+    /**
+     * Get aeAvailableModes and do the sanity check.
+     *
+     * <p>Depending on the check level this class has, for WAR or COLLECT levels,
+     * If the aeMode list is invalid, return an empty mode array. The the caller doesn't
+     * have to abort the execution even the aeMode list is invalid.</p>
+     * @return AE available modes
+     */
+    public int[] getAeAvailableModesChecked() {
+        Key<int[]> modesKey = CameraCharacteristics.CONTROL_AE_AVAILABLE_MODES;
+        int[] modes = getValueFromKeyNonNull(modesKey);
+        if (modes == null) {
+            modes = new int[0];
+        }
+        List<Integer> modeList = new ArrayList<Integer>();
+        for (int mode : modes) {
+            modeList.add(mode);
+        }
+        checkTrueForKey(modesKey, "value is empty", !modeList.isEmpty());
+
+        // All camera device must support ON
+        checkTrueForKey(modesKey, "values " + modeList.toString() + " must contain ON mode",
+                modeList.contains(CameraMetadata.CONTROL_AE_MODE_ON));
+
+        // All camera devices with flash units support ON_AUTO_FLASH and ON_ALWAYS_FLASH
+        Key<Boolean> flashKey= CameraCharacteristics.FLASH_INFO_AVAILABLE;
+        Boolean hasFlash = getValueFromKeyNonNull(flashKey);
+        if (hasFlash == null) {
+            hasFlash = false;
+        }
+        if (hasFlash) {
+            boolean flashModeConsistentWithFlash =
+                    modeList.contains(CameraMetadata.CONTROL_AE_MODE_ON_AUTO_FLASH) &&
+                    modeList.contains(CameraMetadata.CONTROL_AE_MODE_ON_ALWAYS_FLASH);
+            checkTrueForKey(modesKey,
+                    "value must contain ON_AUTO_FLASH and ON_ALWAYS_FLASH and  when flash is" +
+                    "available", flashModeConsistentWithFlash);
+        } else {
+            boolean flashModeConsistentWithoutFlash =
+                    !(modeList.contains(CameraMetadata.CONTROL_AE_MODE_ON_AUTO_FLASH) ||
+                    modeList.contains(CameraMetadata.CONTROL_AE_MODE_ON_ALWAYS_FLASH) ||
+                    modeList.contains(CameraMetadata.CONTROL_AE_MODE_ON_AUTO_FLASH_REDEYE));
+            checkTrueForKey(modesKey,
+                    "value must not contain ON_AUTO_FLASH, ON_ALWAYS_FLASH and" +
+                    "ON_AUTO_FLASH_REDEYE when flash is unavailable",
+                    flashModeConsistentWithoutFlash);
+        }
+
+        // FULL mode camera devices always support OFF mode.
+        boolean condition =
+                !isHardwareLevelFull() || modeList.contains(CameraMetadata.CONTROL_AE_MODE_OFF);
+        checkTrueForKey(modesKey, "Full capability device must have OFF mode", condition);
+
+        // Boundary check.
+        for (int mode : modes) {
+            checkTrueForKey(modesKey, "Value " + mode + " is out of bound",
+                    mode >= CameraMetadata.CONTROL_AE_MODE_OFF
+                    && mode <= CameraMetadata.CONTROL_AE_MODE_ON_AUTO_FLASH_REDEYE);
+        }
+
+        return modes;
+    }
+
+    /**
+     * Get available AWB modes and do the sanity check.
+     *
+     * @return array that contains available AWB modes, empty array if awbAvailableModes is
+     * unavailable.
+     */
+    public int[] getAwbAvailableModesChecked() {
+        Key<int[]> key =
+                CameraCharacteristics.CONTROL_AWB_AVAILABLE_MODES;
+        int[] awbModes = getValueFromKeyNonNull(key);
+
+        if (awbModes == null) {
+            return new int[0];
+        }
+
+        List<Integer> modesList = Arrays.asList(CameraTestUtils.toObject(awbModes));
+        checkTrueForKey(key, " All camera devices must support AUTO mode",
+                modesList.contains(CameraMetadata.CONTROL_AWB_MODE_AUTO));
+        if (isHardwareLevelFull()) {
+            checkTrueForKey(key, " Full capability camera devices must support OFF mode",
+                    modesList.contains(CameraMetadata.CONTROL_AWB_MODE_OFF));
+        }
+
+        return awbModes;
+    }
+
+    /**
+     * Get available AF modes and do the sanity check.
+     *
+     * @return array that contains available AF modes, empty array if afAvailableModes is
+     * unavailable.
+     */
+    public int[] getAfAvailableModesChecked() {
+        Key<int[]> key =
+                CameraCharacteristics.CONTROL_AF_AVAILABLE_MODES;
+        int[] afModes = getValueFromKeyNonNull(key);
+
+        if (afModes == null) {
+            return new int[0];
+        }
+
+        List<Integer> modesList = Arrays.asList(CameraTestUtils.toObject(afModes));
+        checkTrueForKey(key, " All camera devices must support OFF mode",
+                modesList.contains(CameraMetadata.CONTROL_AF_MODE_OFF));
+        if (hasFocuser()) {
+            checkTrueForKey(key, " Camera devices that have focuser units must support AUTO mode",
+                    modesList.contains(CameraMetadata.CONTROL_AF_MODE_AUTO));
+        }
+
+        return afModes;
+    }
+
+    /**
+     * Get supported raw output sizes and do the check.
+     *
+     * @return Empty size array if raw output is not supported
+     */
+    public Size[] getRawOutputSizesChecked() {
+        return getAvailableSizesForFormatChecked(ImageFormat.RAW_SENSOR,
+                StreamDirection.Output);
+    }
+
+    /**
+     * Get supported jpeg output sizes and do the check.
+     *
+     * @return Empty size array if jpeg output is not supported
+     */
+    public Size[] getJpegOutputSizeChecked() {
+        return getAvailableSizesForFormatChecked(ImageFormat.JPEG,
+                StreamDirection.Output);
+    }
+
+    /**
+     * Used to determine the stream direction for various helpers that look up
+     * format or size information.
+     */
+    public enum StreamDirection {
+        /** Stream is used with {@link android.hardware.camera2.CameraDevice#configureOutputs} */
+        Output,
+        /** Stream is used with {@code CameraDevice#configureInputs} -- NOT YET PUBLIC */
+        Input
+    }
+
+    /**
+     * Get available sizes for given user-defined format.
+     *
+     * <p><strong>Does not</strong> work with implementation-defined format.</p>
+     *
+     * @param format The format for the requested size array.
+     * @param direction The stream direction, input or output.
+     * @return The sizes of the given format, empty array if no available size is found.
+     */
+    public Size[] getAvailableSizesForFormatChecked(int format, StreamDirection direction) {
+        Key<StreamConfigurationMap> key =
+                CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP;
+        StreamConfigurationMap config = getValueFromKeyNonNull(key);
+
+        if (config == null) {
+            return new Size[0];
+        }
+
+        android.util.Size[] utilSizes;
+
+        switch (direction) {
+            case Output:
+                utilSizes = config.getOutputSizes(format);
+                break;
+            case Input:
+                utilSizes = null;
+                break;
+            default:
+                throw new IllegalArgumentException("direction must be output or input");
+        }
+
+        // TODO: Get rid of android.util.Size
+        if (utilSizes == null) {
+            Log.i(TAG, "This camera doesn't support format " + format + " for " + direction);
+            return new Size[0];
+        }
+
+        Size[] sizes = new Size[utilSizes.length];
+        for (int i = 0; i < utilSizes.length; ++i) {
+            sizes[i] = new Size(utilSizes[i].getWidth(), utilSizes[i].getHeight());
+        }
+
+        return sizes;
+    }
+
+    /**
+     * Get available AE target fps ranges.
+     *
+     * @return Empty int array if aeAvailableTargetFpsRanges is invalid.
+     */
+    @SuppressWarnings("raw")
+    public Range<Integer>[] getAeAvailableTargetFpsRangesChecked() {
+        Key<Range<Integer>[]> key =
+                CameraCharacteristics.CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES;
+        Range<Integer>[] fpsRanges = getValueFromKeyNonNull(key);
+
+        if (fpsRanges == null) {
+            return new Range[0];
+        }
+
+        // Round down to 2 boundary if it is not integer times of 2, to avoid array out of bound
+        // in case the above check fails.
+        int fpsRangeLength = fpsRanges.length;
+        int minFps, maxFps;
+        long maxFrameDuration = getMaxFrameDurationChecked();
+        for (int i = 0; i < fpsRangeLength; i += 1) {
+            minFps = fpsRanges[i].getLower();
+            maxFps = fpsRanges[i].getUpper();
+            checkTrueForKey(key, " min fps must be no larger than max fps!",
+                    minFps > 0 && maxFps >= minFps);
+            long maxDuration = (long) (1e9 / minFps);
+            checkTrueForKey(key, String.format(
+                    " the frame duration %d for min fps %d must smaller than maxFrameDuration %d",
+                    maxDuration, minFps, maxFrameDuration), maxDuration <= maxFrameDuration);
+        }
+
+        return fpsRanges;
+    }
+
+    /**
+     * Get max frame duration.
+     *
+     * @return 0 if maxFrameDuration is null
+     */
+    public long getMaxFrameDurationChecked() {
+        Key<Long> key =
+                CameraCharacteristics.SENSOR_INFO_MAX_FRAME_DURATION;
+        Long maxDuration = getValueFromKeyNonNull(key);
+
+        if (maxDuration == null) {
+            return 0;
+        }
+
+        return maxDuration;
+    }
+
+    /**
+     * Get available minimal frame durations for a given user-defined format.
+     *
+     * <p><strong>Does not</strong> work with implementation-defined format.</p>
+     *
+     * @param format One of the format from {@link ImageFormat}.
+     * @return HashMap of minimal frame durations for different sizes, empty HashMap
+     *         if availableMinFrameDurations is null.
+     */
+    public HashMap<Size, Long> getAvailableMinFrameDurationsForFormatChecked(int format) {
+
+        HashMap<Size, Long> minDurationMap = new HashMap<Size, Long>();
+
+        Key<StreamConfigurationMap> key =
+                CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP;
+        StreamConfigurationMap config = getValueFromKeyNonNull(key);
+
+        if (config == null) {
+            return minDurationMap;
+        }
+
+        for (android.util.Size size : config.getOutputSizes(format)) {
+            long minFrameDuration = config.getOutputMinFrameDuration(format, size);
+
+            if (minFrameDuration != 0) {
+                minDurationMap.put(new Size(size.getWidth(), size.getHeight()), minFrameDuration);
+            }
+        }
+
+        return minDurationMap;
+    }
+
+    public int[] getAvailableEdgeModesChecked() {
+        Key<int[]> key = CameraCharacteristics.EDGE_AVAILABLE_EDGE_MODES;
+        int[] edgeModes = getValueFromKeyNonNull(key);
+
+        if (edgeModes == null) {
+            return new int[0];
+        }
+
+        // Full device should always include OFF and FAST
+        if (isHardwareLevelFull()) {
+            List<Integer> modeList = Arrays.asList(CameraTestUtils.toObject(edgeModes));
+            checkTrueForKey(key, "Full device must contain OFF and FAST edge modes",
+                    modeList.contains(CameraMetadata.EDGE_MODE_OFF) &&
+                    modeList.contains(CameraMetadata.EDGE_MODE_FAST));
+        }
+
+        return edgeModes;
+    }
+
+    public int[] getAvailableNoiseReductionModesChecked() {
+        Key<int[]> key =
+                CameraCharacteristics.NOISE_REDUCTION_AVAILABLE_NOISE_REDUCTION_MODES;
+        int[] noiseReductionModes = getValueFromKeyNonNull(key);
+
+        if (noiseReductionModes == null) {
+            return new int[0];
+        }
+
+        // Full device should always include OFF and FAST
+        if (isHardwareLevelFull()) {
+            List<Integer> modeList = Arrays.asList(CameraTestUtils.toObject(noiseReductionModes));
+            checkTrueForKey(key, "Full device must contain OFF and FAST noise reduction modes",
+                    modeList.contains(CameraMetadata.NOISE_REDUCTION_MODE_OFF) &&
+                    modeList.contains(CameraMetadata.NOISE_REDUCTION_MODE_FAST));
+        }
+
+        return noiseReductionModes;
+    }
+
+    /**
+     * Get value of key android.control.aeCompensationStep and do the sanity check.
+     *
+     * @return default value if the value is null.
+     */
+    public Rational getAeCompensationStepChecked() {
+        Key<Rational> key =
+                CameraCharacteristics.CONTROL_AE_COMPENSATION_STEP;
+        Rational compensationStep = getValueFromKeyNonNull(key);
+
+        if (compensationStep == null) {
+            // Return default step.
+            return CONTROL_AE_COMPENSATION_STEP_DEFAULT;
+        }
+
+        // Legacy devices don't have a minimum step requirement
+        if (isHardwareLevelLimitedOrBetter()) {
+            float compensationStepF =
+                    (float) compensationStep.getNumerator() / compensationStep.getDenominator();
+            checkTrueForKey(key, " value must be no more than 1/2", compensationStepF < 0.5f);
+        }
+
+        return compensationStep;
+    }
+
+    /**
+     * Get value of key android.control.aeCompensationRange and do the sanity check.
+     *
+     * @return default value if the value is null or malformed.
+     */
+    public Range<Integer> getAeCompensationRangeChecked() {
+        Key<Range<Integer>> key =
+                CameraCharacteristics.CONTROL_AE_COMPENSATION_RANGE;
+        Range<Integer> compensationRange = getValueFromKeyNonNull(key);
+        Rational compensationStep = getAeCompensationStepChecked();
+        float compensationStepF = compensationStep.floatValue();
+        final Range<Integer> DEFAULT_RANGE = Range.create(
+                (int)(CONTROL_AE_COMPENSATION_RANGE_DEFAULT_MIN / compensationStepF),
+                (int)(CONTROL_AE_COMPENSATION_RANGE_DEFAULT_MAX / compensationStepF));
+        if (compensationRange == null) {
+            return DEFAULT_RANGE;
+        }
+
+        // Legacy devices don't have a minimum range requirement
+        if (isHardwareLevelLimitedOrBetter()) {
+            checkTrueForKey(key, " range value must be at least " + DEFAULT_RANGE
+                    + ", actual " + compensationRange + ", compensation step " + compensationStep,
+                   compensationRange.getLower() <= DEFAULT_RANGE.getLower() &&
+                   compensationRange.getUpper() >= DEFAULT_RANGE.getUpper());
+        }
+
+        return compensationRange;
+    }
+
+    /**
+     * Get availableVideoStabilizationModes and do the sanity check.
+     *
+     * @return available video stabilization modes, empty array if it is unavailable.
+     */
+    public int[] getAvailableVideoStabilizationModesChecked() {
+        Key<int[]> key =
+                CameraCharacteristics.CONTROL_AVAILABLE_VIDEO_STABILIZATION_MODES;
+        int[] modes = getValueFromKeyNonNull(key);
+
+        if (modes == null) {
+            return new int[0];
+        }
+
+        List<Integer> modeList = Arrays.asList(CameraTestUtils.toObject(modes));
+        checkTrueForKey(key, " All device should support OFF mode",
+                modeList.contains(CameraMetadata.CONTROL_VIDEO_STABILIZATION_MODE_OFF));
+        checkArrayValuesInRange(key, modes,
+                CameraMetadata.CONTROL_VIDEO_STABILIZATION_MODE_OFF,
+                CameraMetadata.CONTROL_VIDEO_STABILIZATION_MODE_ON);
+
+        return modes;
+    }
+
+    /**
+     * Get availableOpticalStabilization and do the sanity check.
+     *
+     * @return available optical stabilization modes, empty array if it is unavailable.
+     */
+    public int[] getAvailableOpticalStabilizationChecked() {
+        Key<int[]> key =
+                CameraCharacteristics.LENS_INFO_AVAILABLE_OPTICAL_STABILIZATION;
+        int[] modes = getValueFromKeyNonNull(key);
+
+        if (modes == null) {
+            return new int[0];
+        }
+
+        checkArrayValuesInRange(key, modes,
+                CameraMetadata.LENS_OPTICAL_STABILIZATION_MODE_OFF,
+                CameraMetadata.LENS_OPTICAL_STABILIZATION_MODE_ON);
+
+        return modes;
+    }
+
+    /**
+     * Get the scaler's max digital zoom ({@code >= 1.0f}) ratio between crop and active array
+     * @return the max zoom ratio, or {@code 1.0f} if the value is unavailable
+     */
+    public float getAvailableMaxDigitalZoomChecked() {
+        Key<Float> key =
+                CameraCharacteristics.SCALER_AVAILABLE_MAX_DIGITAL_ZOOM;
+
+        Float maxZoom = getValueFromKeyNonNull(key);
+        if (maxZoom == null) {
+            return 1.0f;
+        }
+
+        checkTrueForKey(key, " max digital zoom should be no less than 1",
+                maxZoom >= 1.0f && !Float.isNaN(maxZoom) && !Float.isInfinite(maxZoom));
+
+        return maxZoom;
+    }
+
+    public int[] getAvailableSceneModesChecked() {
+        Key<int[]> key =
+                CameraCharacteristics.CONTROL_AVAILABLE_SCENE_MODES;
+        int[] modes = getValueFromKeyNonNull(key);
+
+        if (modes == null) {
+            return new int[0];
+        }
+
+        List<Integer> modeList = Arrays.asList(CameraTestUtils.toObject(modes));
+        // FACE_PRIORITY must be included if face detection is supported.
+        if (areKeysAvailable(CameraCharacteristics.STATISTICS_INFO_MAX_FACE_COUNT) &&
+                getMaxFaceCountChecked() > 0) {
+            checkTrueForKey(key, " FACE_PRIORITY must be included if face detection is supported",
+                    modeList.contains(CameraMetadata.CONTROL_SCENE_MODE_FACE_PRIORITY));
+        }
+
+        return modes;
+    }
+
+    public int[] getAvailableEffectModesChecked() {
+        Key<int[]> key =
+                CameraCharacteristics.CONTROL_AVAILABLE_EFFECTS;
+        int[] modes = getValueFromKeyNonNull(key);
+
+        if (modes == null) {
+            return new int[0];
+        }
+
+        List<Integer> modeList = Arrays.asList(CameraTestUtils.toObject(modes));
+        // OFF must be included.
+        checkTrueForKey(key, " OFF must be included",
+                modeList.contains(CameraMetadata.CONTROL_EFFECT_MODE_OFF));
+
+        return modes;
+    }
+
+    /**
+     * Get and check the available color aberration modes
+     *
+     * @return the available color aberration modes
+     */
+    public int[] getAvailableColorAberrationModesChecked() {
+        Key<int[]> key =
+                CameraCharacteristics.COLOR_CORRECTION_AVAILABLE_ABERRATION_CORRECTION_MODES;
+        int[] modes = getValueFromKeyNonNull(key);
+
+        if (modes == null) {
+            return new int[0];
+        }
+
+        List<Integer> modeList = Arrays.asList(CameraTestUtils.toObject(modes));
+        checkTrueForKey(key, " Camera devices must always support OFF mode",
+                modeList.contains(CameraMetadata.COLOR_CORRECTION_ABERRATION_CORRECTION_MODE_OFF));
+        checkElementDistinct(key, modeList);
+        checkArrayValuesInRange(key, modes,
+                CameraMetadata.COLOR_CORRECTION_ABERRATION_CORRECTION_MODE_OFF,
+                CameraMetadata.COLOR_CORRECTION_ABERRATION_CORRECTION_MODE_HIGH_QUALITY);
+
+        return modes;
+    }
+
+    /**
+     * Get max pipeline depth and do the sanity check.
+     *
+     * @return max pipeline depth, default value if it is not available.
+     */
+    public byte getPipelineMaxDepthChecked() {
+        Key<Byte> key =
+                CameraCharacteristics.REQUEST_PIPELINE_MAX_DEPTH;
+        Byte maxDepth = getValueFromKeyNonNull(key);
+
+        if (maxDepth == null) {
+            return REQUEST_PIPELINE_MAX_DEPTH_MAX;
+        }
+
+        checkTrueForKey(key, " max pipeline depth should be no larger than "
+                + REQUEST_PIPELINE_MAX_DEPTH_MAX, maxDepth <= REQUEST_PIPELINE_MAX_DEPTH_MAX);
+
+        return maxDepth;
+    }
+
+    /**
+     * Get available capabilities and do the sanity check.
+     *
+     * @return reported available capabilities list, empty list if the value is unavailable.
+     */
+    public List<Integer> getAvailableCapabilitiesChecked() {
+        Key<int[]> key =
+                CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES;
+        int[] availableCaps = getValueFromKeyNonNull(key);
+        List<Integer> capList;
+
+        if (availableCaps == null) {
+            return new ArrayList<Integer>();
+        }
+
+        checkArrayValuesInRange(key, availableCaps,
+                CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE,
+                CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_RAW);
+        capList = Arrays.asList(CameraTestUtils.toObject(availableCaps));
+        return capList;
+    }
+
+    /**
+     * Determine whether the current device supports a capability or not.
+     *
+     * @param capability (non-negative)
+     *
+     * @return {@code true} if the capability is supported, {@code false} otherwise.
+     *
+     * @throws IllegalArgumentException if {@code capability} was negative
+     *
+     * @see CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES
+     */
+    public boolean isCapabilitySupported(int capability) {
+        if (capability < 0) {
+            throw new IllegalArgumentException("capability must be non-negative");
+        }
+
+        List<Integer> availableCapabilities = getAvailableCapabilitiesChecked();
+
+        return availableCapabilities.contains(capability);
+    }
+
+    /**
+     * Determine whether or not all the {@code keys} are available characteristics keys
+     * (as in {@link CameraCharacteristics#getKeys}.
+     *
+     * <p>If this returns {@code true}, then querying for this key from a characteristics
+     * object will always return a non-{@code null} value.</p>
+     *
+     * @param keys one or more camera characteristic keys
+     * @return whether or not all characteristics keys are available
+     */
+    @SafeVarargs
+    public final boolean areKeysAvailable(CameraCharacteristics.Key<?>... keys) {
+        return mCharacteristics.getKeys().containsAll(Arrays.asList(keys));
+    }
+
+    /**
+     * Determine whether or not all the {@code keys} are available result keys
+     * (as in {@link CameraCharacteristics#getAvailableCaptureResultKeys}.
+     *
+     * <p>If this returns {@code true}, then querying for this key from a result
+     * object will almost always return a non-{@code null} value.</p>
+     *
+     * <p>In some cases (e.g. lens shading map), the request must have additional settings
+     * configured in order for the key to correspond to a value.</p>
+     *
+     * @param keys one or more capture result keys
+     * @return whether or not all result keys are available
+     */
+    @SafeVarargs
+    public final boolean areKeysAvailable(CaptureResult.Key<?>... keys) {
+        return mCharacteristics.getAvailableCaptureResultKeys().containsAll(Arrays.asList(keys));
+    }
+
+    /**
+     * Determine whether or not all the {@code keys} are available request keys
+     * (as in {@link CameraCharacteristics#getAvailableCaptureRequestKeys}.
+     *
+     * <p>If this returns {@code true}, then setting this key in the request builder
+     * may have some effect (and if it's {@code false}, then the camera device will
+     * definitely ignore it).</p>
+     *
+     * <p>In some cases (e.g. manual control of exposure), other keys must be also be set
+     * in order for a key to take effect (e.g. control.mode set to OFF).</p>
+     *
+     * @param keys one or more capture request keys
+     * @return whether or not all result keys are available
+     */
+    @SafeVarargs
+    public final boolean areKeysAvailable(CaptureRequest.Key<?>... keys) {
+        return mCharacteristics.getAvailableCaptureRequestKeys().containsAll(Arrays.asList(keys));
+    }
+
+    /*
+     * Determine if camera device support manual lens shading map control
+     *
+     * @return {@code true} if manual lens shading map control is supported
+     */
+    public boolean isManualLensShadingMapSupported() {
+        return areKeysAvailable(CaptureRequest.SHADING_MODE);
+    }
+
+    /**
+     * Determine if camera device support manual color correction control
+     *
+     * @return {@code true} if manual color correction control is supported
+     */
+    public boolean isManualColorCorrectionSupported() {
+        return areKeysAvailable(CaptureRequest.COLOR_CORRECTION_MODE);
+    }
+
+    /**
+     * Determine if camera device support manual tone mapping control
+     *
+     * @return {@code true} if manual tone mapping control is supported
+     */
+    public boolean isManualToneMapSupported() {
+        return areKeysAvailable(CaptureRequest.TONEMAP_MODE);
+    }
+
+    /**
+     * Determine if camera device support manual color aberration control
+     *
+     * @return {@code true} if manual color aberration control is supported
+     */
+    public boolean isManualColorAberrationControlSupported() {
+        return areKeysAvailable(CaptureRequest.COLOR_CORRECTION_ABERRATION_CORRECTION_MODE);
+    }
+
+    /**
+     * Determine if camera device support edge mode control
+     *
+     * @return {@code true} if edge mode control is supported
+     */
+    public boolean isEdgeModeControlSupported() {
+        return areKeysAvailable(CaptureRequest.EDGE_MODE);
+    }
+
+    /**
+     * Determine if camera device support hot pixel mode control
+     *
+     * @return {@code true} if hot pixel mode control is supported
+     */
+    public boolean isHotPixelMapModeControlSupported() {
+        return areKeysAvailable(CaptureRequest.HOT_PIXEL_MODE);
+    }
+
+    /**
+     * Determine if camera device support noise reduction mode control
+     *
+     * @return {@code true} if noise reduction mode control is supported
+     */
+    public boolean isNoiseReductionModeControlSupported() {
+        return areKeysAvailable(CaptureRequest.NOISE_REDUCTION_MODE);
+    }
+
+    /**
+     * Get max number of output raw streams and do the basic sanity check.
+     *
+     * @return reported max number of raw output stream
+     */
+    public int getMaxNumOutputStreamsRawChecked() {
+        Integer maxNumStreams =
+                getValueFromKeyNonNull(CameraCharacteristics.REQUEST_MAX_NUM_OUTPUT_RAW);
+        if (maxNumStreams == null)
+            return 0;
+        return maxNumStreams;
+    }
+
+    /**
+     * Get max number of output processed streams and do the basic sanity check.
+     *
+     * @return reported max number of processed output stream
+     */
+    public int getMaxNumOutputStreamsProcessedChecked() {
+        Integer maxNumStreams =
+                getValueFromKeyNonNull(CameraCharacteristics.REQUEST_MAX_NUM_OUTPUT_PROC);
+        if (maxNumStreams == null)
+            return 0;
+        return maxNumStreams;
+    }
+
+    /**
+     * Get max number of output stalling processed streams and do the basic sanity check.
+     *
+     * @return reported max number of stalling processed output stream
+     */
+    public int getMaxNumOutputStreamsProcessedStallChecked() {
+        Integer maxNumStreams =
+                getValueFromKeyNonNull(CameraCharacteristics.REQUEST_MAX_NUM_OUTPUT_PROC_STALLING);
+        if (maxNumStreams == null)
+            return 0;
+        return maxNumStreams;
+    }
+
+    /**
+     * Get lens facing and do the sanity check
+     * @return lens facing, return default value (BACK) if value is unavailable.
+     */
+    public int getLensFacingChecked() {
+        Key<Integer> key =
+                CameraCharacteristics.LENS_FACING;
+        Integer facing = getValueFromKeyNonNull(key);
+
+        if (facing == null) {
+            return CameraCharacteristics.LENS_FACING_BACK;
+        }
+
+        checkTrueForKey(key, " value is out of range ",
+                facing >= CameraCharacteristics.LENS_FACING_FRONT &&
+                facing <= CameraCharacteristics.LENS_FACING_BACK);
+        return facing;
+    }
+
+    /**
+     * Get the scaler's cropping type (center only or freeform)
+     * @return cropping type, return default value (CENTER_ONLY) if value is unavailable
+     */
+    public int getScalerCroppingTypeChecked() {
+        Key<Integer> key =
+                CameraCharacteristics.SCALER_CROPPING_TYPE;
+        Integer value = getValueFromKeyNonNull(key);
+
+        if (value == null) {
+            return CameraCharacteristics.SCALER_CROPPING_TYPE_CENTER_ONLY;
+        }
+
+        checkTrueForKey(key, " value is out of range ",
+                value >= CameraCharacteristics.SCALER_CROPPING_TYPE_CENTER_ONLY &&
+                value <= CameraCharacteristics.SCALER_CROPPING_TYPE_FREEFORM);
+
+        return value;
+    }
+
+    /**
+     * Check if high speed video is supported (HIGH_SPEED_VIDEO scene mode is
+     * supported, supported high speed fps ranges and sizes are valid).
+     *
+     * @return true if high speed video is supported.
+     */
+    public boolean isHighSpeedVideoSupported() {
+        List<Integer> sceneModes =
+                Arrays.asList(CameraTestUtils.toObject(getAvailableSceneModesChecked()));
+        if (sceneModes.contains(CameraCharacteristics.CONTROL_SCENE_MODE_HIGH_SPEED_VIDEO)) {
+            StreamConfigurationMap config =
+                    getValueFromKeyNonNull(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
+            if (config == null) {
+                return false;
+            }
+            Size[] availableSizes = config.getHighSpeedVideoSizes();
+            if (availableSizes.length == 0) {
+                return false;
+            }
+
+            for (Size size : availableSizes) {
+                Range<Integer>[] availableFpsRanges = config.getHighSpeedVideoFpsRangesFor(size);
+                if (availableFpsRanges.length == 0) {
+                    return false;
+                }
+            }
+
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    /**
+     * Get the value in index for a fixed-size array from a given key.
+     *
+     * <p>If the camera device is incorrectly reporting values, log a warning and return
+     * the default value instead.</p>
+     *
+     * @param key Key to fetch
+     * @param defaultValue Default value to return if camera device uses invalid values
+     * @param name Human-readable name for the array index (logging only)
+     * @param index Array index of the subelement
+     * @param size Expected fixed size of the array
+     *
+     * @return The value reported by the camera device, or the defaultValue otherwise.
+     */
+    private <T> T getArrayElementOrDefault(Key<?> key, T defaultValue, String name, int index,
+            int size) {
+        T elementValue = getArrayElementCheckRangeNonNull(
+                key,
+                index,
+                size);
+
+        if (elementValue == null) {
+            failKeyCheck(key,
+                    "had no valid " + name + " value; using default of " + defaultValue);
+            elementValue = defaultValue;
+        }
+
+        return elementValue;
+    }
+
+    /**
+     * Fetch an array sub-element from an array value given by a key.
+     *
+     * <p>
+     * Prints a warning if the sub-element was null.
+     * </p>
+     *
+     * <p>Use for variable-size arrays since this does not check the array size.</p>
+     *
+     * @param key Metadata key to look up
+     * @param element A non-negative index value.
+     * @return The array sub-element, or null if the checking failed.
+     */
+    private <T> T getArrayElementNonNull(Key<?> key, int element) {
+        return getArrayElementCheckRangeNonNull(key, element, IGNORE_SIZE_CHECK);
+    }
+
+    /**
+     * Fetch an array sub-element from an array value given by a key.
+     *
+     * <p>
+     * Prints a warning if the array size does not match the size, or if the sub-element was null.
+     * </p>
+     *
+     * @param key Metadata key to look up
+     * @param element The index in [0,size)
+     * @param size A positive size value or otherwise {@value #IGNORE_SIZE_CHECK}
+     * @return The array sub-element, or null if the checking failed.
+     */
+    private <T> T getArrayElementCheckRangeNonNull(Key<?> key, int element, int size) {
+        Object array = getValueFromKeyNonNull(key);
+
+        if (array == null) {
+            // Warning already printed
+            return null;
+        }
+
+        if (size != IGNORE_SIZE_CHECK) {
+            int actualLength = Array.getLength(array);
+            if (actualLength != size) {
+                failKeyCheck(key,
+                        String.format("had the wrong number of elements (%d), expected (%d)",
+                                actualLength, size));
+                return null;
+            }
+        }
+
+        @SuppressWarnings("unchecked")
+        T val = (T) Array.get(array, element);
+
+        if (val == null) {
+            failKeyCheck(key, "had a null element at index" + element);
+            return null;
+        }
+
+        return val;
+    }
+
+    /**
+     * Gets the key, logging warnings for null values.
+     */
+    public <T> T getValueFromKeyNonNull(Key<T> key) {
+        if (key == null) {
+            throw new IllegalArgumentException("key was null");
+        }
+
+        T value = mCharacteristics.get(key);
+
+        if (value == null) {
+            failKeyCheck(key, "was null");
+        }
+
+        return value;
+    }
+
+    private void checkArrayValuesInRange(Key<int[]> key, int[] array, int min, int max) {
+        for (int value : array) {
+            checkTrueForKey(key, String.format(" value is out of range [%d, %d]", min, max),
+                    value <= max && value >= min);
+        }
+    }
+
+    private void checkArrayValuesInRange(Key<byte[]> key, byte[] array, byte min, byte max) {
+        for (byte value : array) {
+            checkTrueForKey(key, String.format(" value is out of range [%d, %d]", min, max),
+                    value <= max && value >= min);
+        }
+    }
+
+    /**
+     * Check the uniqueness of the values in a list.
+     *
+     * @param key The key to be checked
+     * @param list The list contains the value of the key
+     */
+    private <U, T> void checkElementDistinct(Key<U> key, List<T> list) {
+        // Each size must be distinct.
+        Set<T> sizeSet = new HashSet<T>(list);
+        checkTrueForKey(key, "Each size must be distinct", sizeSet.size() == list.size());
+    }
+
+    private <T> void checkTrueForKey(Key<T> key, String message, boolean condition) {
+        if (!condition) {
+            failKeyCheck(key, message);
+        }
+    }
+
+    private <T> void failKeyCheck(Key<T> key, String message) {
+        // TODO: Consider only warning once per key/message combination if it's too spammy.
+        // TODO: Consider offering other options such as throwing an assertion exception
+        String failureCause = String.format("The static info key '%s' %s", key.getName(), message);
+        switch (mLevel) {
+            case WARN:
+                Log.w(TAG, failureCause);
+                break;
+            case COLLECT:
+                mCollector.addMessage(failureCause);
+                break;
+            case ASSERT:
+                Assert.fail(failureCause);
+            default:
+                throw new UnsupportedOperationException("Unhandled level " + mLevel);
+        }
+    }
+}
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/helpers/UncheckedCloseable.java b/tests/tests/hardware/src/android/hardware/camera2/cts/helpers/UncheckedCloseable.java
new file mode 100644
index 0000000..570ef2c
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/helpers/UncheckedCloseable.java
@@ -0,0 +1,48 @@
+/*
+ * 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.helpers;
+
+/**
+ * Defines an interface for classes that can (or need to) be closed once they
+ * are not used any longer; calling the {@code close} method releases resources
+ * that the object holds.</p>
+ *
+ * <p>This signifies that the implementor will never throw checked exceptions when closing,
+ * allowing for more fine grained exception handling at call sites handling this interface
+ * generically.</p>
+ *
+ * <p>A common pattern for using an {@code UncheckedCloseable} resource:
+ * <pre>   {@code
+ *   // where <Foo extends UncheckedCloseable>
+ *   UncheckedCloseable foo = new Foo();
+ *   try {
+ *      ...;
+ *   } finally {
+ *      foo.close();
+ *   }
+ * }</pre>
+ */
+public interface UncheckedCloseable extends AutoCloseable {
+
+    /**
+     * Closes the object and release any system resources it holds.
+     *
+     * <p>Does not throw any checked exceptions.</p>
+     */
+    @Override
+    void close();
+}
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/rs/AllocationCache.java b/tests/tests/hardware/src/android/hardware/camera2/cts/rs/AllocationCache.java
new file mode 100644
index 0000000..e65e819
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/rs/AllocationCache.java
@@ -0,0 +1,242 @@
+/*
+ * 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.rs;
+
+import android.hardware.camera2.cts.helpers.UncheckedCloseable;
+import android.renderscript.Allocation;
+import android.renderscript.RenderScript;
+import android.renderscript.Type;
+import android.util.Log;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import static android.hardware.camera2.cts.helpers.Preconditions.*;
+
+/**
+ * Cache {@link Allocation} objects based on their type and usage.
+ *
+ * <p>This avoids expensive re-allocation of objects when they are used over and over again
+ * by different scripts.</p>
+ */
+public class AllocationCache implements UncheckedCloseable {
+
+    private static final String TAG = "AllocationCache";
+    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+    private static int sDebugHits = 0;
+    private static int sDebugMisses = 0;
+
+    private final RenderScript mRS;
+    private final HashMap<AllocationKey, List<Allocation>> mAllocationMap =
+            new HashMap<AllocationKey, List<Allocation>>();
+    private boolean mClosed = false;
+
+    /**
+     * Create a new cache with the specified RenderScript context.
+     *
+     * @param rs A non-{@code null} RenderScript context.
+     *
+     * @throws NullPointerException if rs was null
+     */
+    public AllocationCache(RenderScript rs) {
+        mRS = checkNotNull("rs", rs);
+    }
+
+    /**
+     * Returns the {@link RenderScript} context associated with this AllocationCache.
+     *
+     * @return A non-{@code null} RenderScript value.
+     */
+    public RenderScript getRenderScript() {
+        return mRS;
+    }
+
+    /**
+     * Try to lookup a compatible Allocation from the cache, create one if none exist.
+     *
+     * @param type A non-{@code null} RenderScript Type.
+     * @throws NullPointerException if type was null
+     * @throws IllegalStateException if the cache was closed with {@link #close}
+     */
+    public synchronized Allocation getOrCreateTyped(Type type, int usage) {
+        checkNotNull("type", type);
+        checkNotClosed();
+
+        AllocationKey key = new AllocationKey(type, usage);
+        List<Allocation> list = mAllocationMap.get(key);
+
+        Allocation alloc;
+
+        if (list == null || list.isEmpty()) {
+            alloc = Allocation.createTyped(mRS, type, usage);
+
+            if (DEBUG) {
+                sDebugMisses++;
+                Log.d(TAG, String.format(
+                    "Cache MISS (%d): type = '%s', usage = '%x'", sDebugMisses, type, usage));
+            }
+        } else {
+            alloc = list.remove(list.size() - 1);
+
+            if (DEBUG) {
+                sDebugHits++;
+                Log.d(TAG, String.format(
+                    "Cache HIT (%d): type = '%s', usage = '%x'", sDebugHits, type, usage));
+            }
+        }
+
+        return alloc;
+    }
+
+    /**
+     * Return the Allocation to the cache.
+     *
+     * <p>Future calls to getOrCreateTyped with the same type and usage may
+     * return this allocation.</p>
+     *
+     * <p>Allocations that have usage {@link Allocation#USAGE_IO_INPUT} get their
+     * listeners reset. Those that have {@link Allocation#USAGE_IO_OUTPUT} get their
+     * surfaces reset.</p>
+     *
+     * @param allocation A non-{@code null} RenderScript {@link Allocation}
+     * @throws NullPointerException if allocation was null
+     * @throws IllegalArgumentException if the allocation was already returned previously
+     * @throws IllegalStateException if the cache was closed with {@link #close}
+     */
+    public synchronized void returnToCache(Allocation allocation) {
+        checkNotNull("allocation", allocation);
+        checkNotClosed();
+
+        int usage = allocation.getUsage();
+        AllocationKey key = new AllocationKey(allocation.getType(), usage);
+        List<Allocation> value = mAllocationMap.get(key);
+
+        if (value != null && value.contains(allocation)) {
+            throw new IllegalArgumentException("allocation was already returned to the cache");
+        }
+
+        if ((usage & Allocation.USAGE_IO_INPUT) != 0) {
+            allocation.setOnBufferAvailableListener(null);
+        }
+        if ((usage & Allocation.USAGE_IO_OUTPUT) != 0) {
+            allocation.setSurface(null);
+        }
+
+        if (value == null) {
+            value = new ArrayList<Allocation>(/*capacity*/1);
+            mAllocationMap.put(key, value);
+        }
+
+        value.add(allocation);
+
+        // TODO: Evict existing allocations from cache when we get too many items in it,
+        // to avoid running out of memory
+
+        // TODO: move to using android.util.LruCache under the hood
+    }
+
+    /**
+     * Return the allocation to the cache, if it wasn't {@code null}.
+     *
+     * <p>Future calls to getOrCreateTyped with the same type and usage may
+     * return this allocation.</p>
+     *
+     * <p>Allocations that have usage {@link Allocation#USAGE_IO_INPUT} get their
+     * listeners reset. Those that have {@link Allocation#USAGE_IO_OUTPUT} get their
+     * surfaces reset.</p>
+     *
+     * <p>{@code null} values are a no-op.</p>
+     *
+     * @param allocation A potentially {@code null} RenderScript {@link Allocation}
+     * @throws IllegalArgumentException if the allocation was already returned previously
+     * @throws IllegalStateException if the cache was closed with {@link #close}
+     */
+    public synchronized void returnToCacheIfNotNull(Allocation allocation) {
+        if (allocation != null) {
+            returnToCache(allocation);
+        }
+    }
+
+    /**
+     * Closes the object and destroys any Allocations still in the cache.
+     */
+    @Override
+    public synchronized void close() {
+        if (mClosed) return;
+
+        for (Map.Entry<AllocationKey, List<Allocation>> entry : mAllocationMap.entrySet()) {
+            List<Allocation> value = entry.getValue();
+
+            for (Allocation alloc : value) {
+                alloc.destroy();
+            }
+
+            value.clear();
+        }
+
+        mAllocationMap.clear();
+        mClosed = true;
+    }
+
+    @Override
+    protected void finalize() throws Throwable {
+        try {
+            close();
+        } finally {
+            super.finalize();
+        }
+    }
+
+    /**
+     * Holder class to check if one allocation is compatible with another.
+     *
+     * <p>An Allocation is considered compatible if both it's Type and usage is equivalent.</p>
+     */
+    private static class AllocationKey {
+        private final Type mType;
+        private final int mUsage;
+
+        public AllocationKey(Type type, int usage) {
+            mType = type;
+            mUsage = usage;
+        }
+
+        @Override
+        public int hashCode() {
+            return mType.hashCode() ^ mUsage;
+        }
+
+        @Override
+        public boolean equals(Object other) {
+            if (other instanceof AllocationKey){
+                AllocationKey otherKey = (AllocationKey) other;
+
+                return otherKey.mType.equals(mType) && otherKey.mUsage == otherKey.mUsage;
+            }
+
+            return false;
+        }
+    }
+
+    private void checkNotClosed() {
+        if (mClosed == true) {
+            throw new IllegalStateException("AllocationCache has already been closed");
+        }
+    }
+}
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/rs/AllocationInfo.java b/tests/tests/hardware/src/android/hardware/camera2/cts/rs/AllocationInfo.java
new file mode 100644
index 0000000..3324783
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/rs/AllocationInfo.java
@@ -0,0 +1,441 @@
+/*
+ * 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.rs;
+
+import static android.hardware.camera2.cts.helpers.Preconditions.*;
+
+import android.graphics.ImageFormat;
+import android.graphics.PixelFormat;
+import android.util.Size;
+import android.renderscript.Allocation;
+import android.renderscript.Element;
+import android.renderscript.RenderScript;
+import android.renderscript.Type;
+import android.util.Log;
+
+/**
+ * Abstract the information necessary to create new {@link Allocation allocations} with
+ * their size, element, type, and usage.
+ *
+ * <p>This also includes convenience functions for printing to a string, something RenderScript
+ * lacks at the time of writing.</p>
+ *
+ * <p>Note that when creating a new {@link AllocationInfo} the usage flags <b>always</b> get ORd
+ * to {@link Allocation#USAGE_IO_SCRIPT}.</p>
+ */
+public class AllocationInfo {
+
+    private final RenderScript mRS = RenderScriptSingleton.getRS();
+
+    private final Size mSize;
+    private final Element mElement;
+    private final Type mType;
+    private final int mUsage;
+
+    private static final String TAG = "AllocationInfo";
+    private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
+
+    /**
+     * Create a new {@link AllocationInfo} holding the element, size, and usage
+     * from an existing {@link Allocation}.
+     *
+     * @param allocation {@link Allocation}
+     *
+     * @return A new {@link AllocationInfo}
+     *
+     * @throws NullPointerException if allocation was {@code null}.
+     */
+    public static AllocationInfo newInstance(Allocation allocation) {
+        checkNotNull("allocation", allocation);
+
+        return new AllocationInfo(allocation.getElement(),
+                new Size(allocation.getType().getX(), allocation.getType().getY()),
+                allocation.getUsage());
+    }
+
+    /**
+     * Create a new {@link AllocationInfo} holding the specified format, {@link Size},
+     * and {@link Allocation#USAGE_SCRIPT usage}.
+     *
+     * <p>The usage is always ORd with {@link Allocation#USAGE_SCRIPT}.</p>
+     *
+     * <p>The closest {@link Element} possible is created from the format.</p>
+     *
+     * @param size {@link Size}
+     * @param format An int format
+     * @param usage Usage flags
+     *
+     * @return A new {@link AllocationInfo} holding the given arguments.
+     *
+     * @throws NullPointerException if size was {@code null}.
+     *
+     * @see ImageFormat
+     * @see PixelFormat
+     */
+    public static AllocationInfo newInstance(Size size, int format, int usage) {
+        RenderScript rs = RenderScriptSingleton.getRS();
+
+        Element element;
+        switch (format) {
+            case ImageFormat.YUV_420_888:
+                element = Element.YUV(rs);
+                break;
+            case PixelFormat.RGBA_8888:
+                element = Element.RGBA_8888(rs);
+                break;
+            // TODO: map more formats here
+            default:
+                throw new UnsupportedOperationException("Unsupported format " + format);
+        }
+
+        return new AllocationInfo(element, size, usage);
+    }
+
+
+    /**
+     * Create a new {@link AllocationInfo} holding the specified format, {@link Size},
+     * with the default usage.
+     *
+     * <p>The default usage is always {@link Allocation#USAGE_SCRIPT}.</p>
+     *
+     * <p>The closest {@link Element} possible is created from the format.</p>
+     *
+     * @param size {@link Size}
+     * @param format An int format
+     *
+     * @return A new {@link AllocationInfo} holding the given arguments.
+     *
+     * @throws NullPointerException if size was {@code null}.
+     *
+     * @see ImageFormat
+     * @see PixelFormat
+     */
+    public static AllocationInfo newInstance(Size size, int format) {
+        return newInstance(size, format, Allocation.USAGE_SCRIPT);
+    }
+
+    /**
+     * Create a new {@link AllocationInfo} holding the specified {@link Element}, {@link Size},
+     * with the default usage.
+     *
+     * <p>The default usage is always {@link Allocation#USAGE_SCRIPT}.</p>
+     *
+     * @param element {@link Element}
+     * @param size {@link Size}
+     *
+     * @return A new {@link AllocationInfo} holding the given arguments.
+     *
+     * @throws NullPointerException if size was {@code null}.
+     * @throws NullPointerException if element was {@code null}.
+     */
+    public static AllocationInfo newInstance(Element element, Size size) {
+        return new AllocationInfo(element, size, Allocation.USAGE_SCRIPT);
+    }
+
+    /**
+     * Create a new {@link AllocationInfo} holding the specified {@link Element}, {@link Size},
+     * and {@link Allocation#USAGE_SCRIPT usage}.
+     *
+     * <p>The usage is always ORd with {@link Allocation#USAGE_SCRIPT}.</p>
+     *
+     * @param element {@link Element}
+     * @param size {@link Size}
+     * @param usage usage flags
+     *
+     * @return A new {@link AllocationInfo} holding the given arguments.
+     *
+     * @throws NullPointerException if size was {@code null}.
+     * @throws NullPointerException if element was {@code null}.
+     */
+    public static AllocationInfo newInstance(Element element, Size size, int usage) {
+        return new AllocationInfo(element, size, usage);
+    }
+
+    /**
+     * Create a new {@link AllocationInfo} by copying the existing data but appending
+     * the new usage flags to the old usage flags.
+     *
+     * @param usage usage flags
+     *
+     * @return A new {@link AllocationInfo} with new usage flags ORd to the old ones.
+     */
+    public AllocationInfo addExtraUsage(int usage) {
+        return new AllocationInfo(mElement, mSize, mUsage | usage);
+    }
+
+    /**
+     * Create a new {@link AllocationInfo} by copying the existing data but changing the format,
+     * and appending the new usage flags to the old usage flags.
+     *
+     * @param format Format
+     * @param usage usage flags
+     *
+     * @return A new {@link AllocationInfo} with new format/usage.
+     *
+     * @see ImageFormat
+     * @see PixelFormat
+     */
+    public AllocationInfo changeFormatAndUsage(int format, int usage) {
+        return newInstance(getSize(), format, usage);
+    }
+
+    /**
+     * Create a new {@link AllocationInfo} by copying the existing data but replacing the old
+     * usage with the new usage flags.
+     *
+     * @param usage usage flags
+     *
+     * @return A new {@link AllocationInfo} with new format/usage.
+     *
+     * @see ImageFormat
+     * @see PixelFormat
+     */
+    public AllocationInfo changeElementWithDefaultUsage(Element element) {
+        return newInstance(element, getSize());
+    }
+
+    /**
+     * Create a new {@link AllocationInfo} by copying the existing data but changing the format,
+     * and replacing the old usage flags with default usage flags.
+     *
+     * @param format Format
+     *
+     * @return A new {@link AllocationInfo} with new format/usage.
+     *
+     * @see ImageFormat
+     * @see PixelFormat
+     */
+    public AllocationInfo changeFormatWithDefaultUsage(int format) {
+        return newInstance(getSize(), format, Allocation.USAGE_SCRIPT);
+    }
+
+    private AllocationInfo(Element element, Size size, int usage) {
+        checkNotNull("element", element);
+        checkNotNull("size", size);
+
+        mElement = element;
+        mSize = size;
+        mUsage = usage;
+
+        Type.Builder typeBuilder = typeBuilder(element, size);
+
+        if (element.equals(Element.YUV(mRS))) {
+            typeBuilder.setYuvFormat(ImageFormat.YUV_420_888);
+        }
+
+        mType = typeBuilder.create();
+    }
+
+    /**
+     * Get the {@link Type type} for this info.
+     *
+     * <p>Note that this is the same type that would get used by the {@link Allocation}
+     * created with {@link #createAllocation()}.
+     *
+     * @return The type (never {@code null}).
+     */
+    public Type getType() {
+        return mType;
+    }
+
+    /**
+     * Get the usage.
+     *
+     * <p>The bit for {@link Allocation#USAGE_SCRIPT} will always be set to 1.</p>
+     *
+     * @return usage flags
+     */
+    public int getUsage() {
+        return mUsage;
+    }
+
+    /**
+     * Get the size.
+     *
+     * @return The size (never {@code null}).
+     */
+    public Size getSize() {
+        return mSize;
+    }
+
+    /**
+     * Get the {@link Element}.
+     *
+     * @return The element (never {@code null}).
+     */
+    public Element getElement() {
+        return mElement;
+    }
+
+    /**
+     * Convenience enum to represent commonly-used elements without needing a RenderScript object.
+     */
+    public enum ElementInfo {
+        YUV,
+        RGBA_8888,
+        U8_3,
+        U8_4;
+
+        private static final String TAG = "ElementInfo";
+
+        /**
+         * Create an {@link ElementInfo} by converting it from a {@link Element}.
+         *
+         * @param element The element for which you want to get an enum for.
+         *
+         * @return The element info is a corresponding one exists, or {@code null} otherwise.
+         */
+        public static ElementInfo fromElement(Element element) {
+            checkNotNull("element", element);
+
+            if (element.equals(Element.YUV(RenderScriptSingleton.getRS()))) {
+                return YUV;
+            } else if (element.equals(Element.RGBA_8888(RenderScriptSingleton.getRS()))) {
+                return RGBA_8888;
+            } else if (element.equals(Element.U8_3(RenderScriptSingleton.getRS()))) {
+                return U8_3;
+            } else if (element.equals(Element.U8_4(RenderScriptSingleton.getRS()))) {
+                return U8_4;
+            }
+            // TODO: add more comparisons here as necessary
+
+            Log.w(TAG, "Unknown element of data kind " + element.getDataKind());
+            return null;
+        }
+    }
+
+    /**
+     * Compare the current element against the suggested element (info).
+     *
+     * @param element The other element to compare against.
+     *
+     * @return true if the elements are equal, false otherwise.
+     */
+    public boolean isElementEqualTo(ElementInfo element) {
+        checkNotNull("element", element);
+
+        Element comparison;
+        switch (element) {
+            case YUV:
+                comparison = Element.YUV(mRS);
+                break;
+            case RGBA_8888:
+                comparison = Element.RGBA_8888(mRS);
+                break;
+            case U8_3:
+                comparison = Element.U8_3(mRS);
+                break;
+            case U8_4:
+                comparison = Element.U8_4(mRS);
+                break;
+            default:
+            // TODO: add more comparisons here as necessary
+                comparison = null;
+        }
+
+        return mElement.equals(comparison);
+    }
+
+    /**
+     * Human-readable representation of this info.
+     */
+    @Override
+    public String toString() {
+        return String.format("Size: %s, Element: %s, Usage: %x", mSize,
+                ElementInfo.fromElement(mElement), mUsage);
+    }
+
+    /**
+     * Compare against another object.
+     *
+     * <p>Comparisons against objects that are not instances of {@link AllocationInfo}
+     * always return {@code false}.</p>
+     *
+     * <p>Two {@link AllocationInfo infos} are considered equal only if their elements,
+     * sizes, and usage flags are also equal.</p>
+     *
+     * @param other Another info object
+     *
+     * @return true if this is equal to other
+     */
+    @Override
+    public boolean equals(Object other) {
+        if (other instanceof AllocationInfo) {
+            return equals((AllocationInfo)other);
+        } else {
+            return false;
+        }
+    }
+
+    /**
+     * Compare against another object.
+     *
+     * <p>Two {@link AllocationInfo infos} are considered equal only if their elements,
+     * sizes, and usage flags are also equal.</p>
+     *
+     * @param other Another info object
+     *
+     * @return true if this is equal to other
+     */
+    public boolean equals(AllocationInfo other) {
+        if (other == null) {
+            return false;
+        }
+
+        // Element, Size equality is already incorporated into Type equality
+        return mType.equals(other.mType) && mUsage == other.mUsage;
+    }
+
+    /**
+     * Create a new {@link Allocation} using the {@link #getType type} and {@link #getUsage usage}
+     * from this info object.
+     *
+     * <p>The allocation is always created from a {@link AllocationCache cache}. If possible,
+     * return it to the cache once done (although this is not necessary).</p>
+     *
+     * @return a new {@link Allocation}
+     */
+    public Allocation createAllocation() {
+        if (VERBOSE) Log.v(TAG, "createAllocation - for info =" + toString());
+        return RenderScriptSingleton.getCache().getOrCreateTyped(mType, mUsage);
+    }
+
+    /**
+     * Create a new {@link Allocation} using the {@link #getType type} and {@link #getUsage usage}
+     * from this info object; immediately wrap inside a new {@link BlockingInputAllocation}.
+     *
+     * <p>The allocation is always created from a {@link AllocationCache cache}. If possible,
+     * return it to the cache once done (although this is not necessary).</p>
+     *
+     * @return a new {@link Allocation}
+     *
+     * @throws IllegalArgumentException
+     *            If the usage did not have one of {@code USAGE_IO_INPUT} or {@code USAGE_IO_OUTPUT}
+     */
+    public BlockingInputAllocation createBlockingInputAllocation() {
+        Allocation alloc = createAllocation();
+        return BlockingInputAllocation.wrap(alloc);
+    }
+
+    private static Type.Builder typeBuilder(Element element, Size size) {
+        Type.Builder builder = (new Type.Builder(RenderScriptSingleton.getRS(), element))
+                .setX(size.getWidth())
+                .setY(size.getHeight());
+
+        return builder;
+    }
+}
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/rs/BlockingInputAllocation.java b/tests/tests/hardware/src/android/hardware/camera2/cts/rs/BlockingInputAllocation.java
new file mode 100644
index 0000000..0305540
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/rs/BlockingInputAllocation.java
@@ -0,0 +1,206 @@
+/*
+ * 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.rs;
+
+import static android.hardware.camera2.cts.helpers.Preconditions.*;
+
+import android.hardware.camera2.cts.helpers.UncheckedCloseable;
+import android.renderscript.Allocation;
+import android.util.Log;
+
+import com.android.ex.camera2.exceptions.TimeoutRuntimeException;
+
+/**
+ * An {@link Allocation} wrapper that can be used to block until new buffers are available.
+ *
+ * <p>Can only be used only with {@link Allocation#USAGE_IO_INPUT} usage Allocations.</p>
+ *
+ * <p>When used with a {@link android.hardware.camera2.CameraDevice CameraDevice} this
+ * must be used as an output surface.</p>
+ */
+class BlockingInputAllocation implements UncheckedCloseable {
+
+    private static final String TAG = BlockingInputAllocation.class.getSimpleName();
+    private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
+
+    private final Allocation mAllocation;
+    private final OnBufferAvailableListener mListener;
+    private boolean mClosed;
+
+    /**
+     * Wrap an existing Allocation with this {@link BlockingInputAllocation}.
+     *
+     * <p>Doing this will clear any existing associated buffer listeners and replace
+     * it with a new one.</p>
+     *
+     * @param allocation A non-{@code null} {@link Allocation allocation}
+     * @return a new {@link BlockingInputAllocation} instance
+     *
+     * @throws NullPointerException
+     *           If {@code allocation} was {@code null}
+     * @throws IllegalArgumentException
+     *           If {@code allocation}'s usage did not have one of USAGE_IO_INPUT or USAGE_IO_OUTPUT
+     * @throws IllegalStateException
+     *           If this object has already been {@link #close closed}
+     */
+    public static BlockingInputAllocation wrap(Allocation allocation) {
+        checkNotNull("allocation", allocation);
+        checkBitFlags("usage", allocation.getUsage(), "USAGE_IO_INPUT", Allocation.USAGE_IO_INPUT);
+
+        return new BlockingInputAllocation(allocation);
+    }
+
+    /**
+     * Get the Allocation backing this {@link BlockingInputAllocation}.
+     *
+     * @return Allocation instance (non-{@code null}).
+     *
+     * @throws IllegalStateException If this object has already been {@link #close closed}
+     */
+    public Allocation getAllocation() {
+        checkNotClosed();
+
+        return mAllocation;
+    }
+
+    /**
+     * Waits for a buffer to become available, then immediately
+     * {@link Allocation#ioReceive receives} it.
+     *
+     * <p>After calling this, the next script used with this allocation will use the
+     * newer buffer.</p>
+     *
+     * @throws TimeoutRuntimeException If waiting for the buffer has timed out.
+     * @throws IllegalStateException If this object has already been {@link #close closed}
+     */
+    public synchronized void waitForBufferAndReceive() {
+        checkNotClosed();
+
+        if (VERBOSE) Log.v(TAG, "waitForBufferAndReceive - begin");
+
+        mListener.waitForBuffer();
+        mAllocation.ioReceive();
+
+        if (VERBOSE) Log.v(TAG, "waitForBufferAndReceive - Allocation#ioReceive");
+    }
+
+    /**
+     * If there are multiple pending buffers, {@link Allocation#ioReceive receive} the latest one.
+     *
+     * <p>Does not block if there are no currently pending buffers.</p>
+     *
+     * @return {@code true} only if any buffers were received.
+     *
+     * @throws IllegalStateException If this object has already been {@link #close closed}
+     */
+    public synchronized boolean receiveLatestAvailableBuffers() {
+        checkNotClosed();
+
+        int updatedBuffers = 0;
+        while (mListener.isBufferPending()) {
+            mListener.waitForBuffer();
+            mAllocation.ioReceive();
+            updatedBuffers++;
+        }
+
+        if (VERBOSE) Log.v(TAG, "receiveLatestAvailableBuffers - updated = " + updatedBuffers);
+
+        return updatedBuffers > 0;
+    }
+
+    /**
+     * Closes the object and detaches the listener from the {@link Allocation}.
+     *
+     * <p>This has a side effect of calling {@link #receiveLatestAvailableBuffers}
+     *
+     * <p>Does <i>not</i> destroy the underlying {@link Allocation}.</p>
+     */
+    @Override
+    public synchronized void close() {
+        if (mClosed) return;
+
+        receiveLatestAvailableBuffers();
+        mAllocation.setOnBufferAvailableListener(/*callback*/null);
+        mClosed = true;
+    }
+
+    protected void checkNotClosed() {
+        if (mClosed) {
+            throw new IllegalStateException(TAG + " has been closed");
+        }
+    }
+
+    @Override
+    protected void finalize() throws Throwable {
+        try {
+            close();
+        } finally {
+            super.finalize();
+        }
+    }
+
+    private BlockingInputAllocation(Allocation allocation) {
+        mAllocation = allocation;
+
+        mListener = new OnBufferAvailableListener();
+        mAllocation.setOnBufferAvailableListener(mListener);
+    }
+
+    // TODO: refactor with the ImageReader Listener code to use a LinkedBlockingQueue
+    private static class OnBufferAvailableListener implements Allocation.OnBufferAvailableListener {
+        private int mPendingBuffers = 0;
+        private final Object mBufferSyncObject = new Object();
+        private static final int TIMEOUT_MS = 5000;
+
+        public boolean isBufferPending() {
+            synchronized (mBufferSyncObject) {
+                return (mPendingBuffers > 0);
+            }
+        }
+
+        /**
+         * Waits for a buffer. Caller must call ioReceive exactly once after calling this.
+         *
+         * @throws TimeoutRuntimeException If waiting for the buffer has timed out.
+         */
+        public void waitForBuffer() {
+            synchronized (mBufferSyncObject) {
+                while (mPendingBuffers == 0) {
+                    try {
+                        if (VERBOSE) Log.v(TAG, "waiting for next buffer");
+                        mBufferSyncObject.wait(TIMEOUT_MS);
+                        if (mPendingBuffers == 0) {
+                            throw new TimeoutRuntimeException("wait for buffer image timed out");
+                        }
+                    } catch (InterruptedException ie) {
+                        throw new AssertionError(ie);
+                    }
+                }
+                mPendingBuffers--;
+            }
+        }
+
+        @Override
+        public void onBufferAvailable(Allocation a) {
+            if (VERBOSE) Log.v(TAG, "new buffer in allocation available");
+            synchronized (mBufferSyncObject) {
+                mPendingBuffers++;
+                mBufferSyncObject.notifyAll();
+            }
+        }
+    }
+}
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/rs/RenderScriptSingleton.java b/tests/tests/hardware/src/android/hardware/camera2/cts/rs/RenderScriptSingleton.java
new file mode 100644
index 0000000..8e4c8e9
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/rs/RenderScriptSingleton.java
@@ -0,0 +1,110 @@
+/*
+ * 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.rs;
+
+import android.content.Context;
+import android.renderscript.RenderScript;
+import android.util.Log;
+
+// TODO : Replace with dependency injection
+/**
+ * Singleton to hold {@link RenderScript} and {@link AllocationCache} objects.
+ *
+ * <p>The test method must call {@link #setContext} before attempting to retrieve
+ * the underlying objects.</p> *
+ */
+public class RenderScriptSingleton {
+
+    private static final String TAG = "RenderScriptSingleton";
+
+    private static Context sContext;
+    private static RenderScript sRS;
+    private static AllocationCache sCache;
+
+    /**
+     * Initialize the singletons from the given context; the
+     * {@link RenderScript} and {@link AllocationCache} objects are instantiated.
+     *
+     * @param context a non-{@code null} Context.
+     *
+     * @throws IllegalStateException If this was called repeatedly without {@link #clearContext}
+     */
+    public static synchronized void setContext(Context context) {
+        if (context.equals(sContext)) {
+            return;
+        } else if (sContext != null) {
+            Log.v(TAG,
+                    "Trying to set new context " + context +
+                    ", before clearing previous "+ sContext);
+            throw new IllegalStateException(
+                    "Call #clearContext before trying to set a new context");
+        }
+
+        sRS = RenderScript.create(context);
+        sContext = context;
+        sCache = new AllocationCache(sRS);
+    }
+
+    /**
+     * Clean up the singletons from the given context; the
+     * {@link RenderScript} and {@link AllocationCache} objects are destroyed.
+     *
+     * <p>Safe to call multiple times; subsequent invocations have no effect.</p>
+     */
+    public static synchronized void clearContext() {
+        if (sContext != null) {
+            sCache.close();
+            sCache = null;
+
+            sRS.destroy();
+            sRS = null;
+            sContext = null;
+        }
+    }
+
+    /**
+     * Get the current {@link RenderScript} singleton.
+     *
+     * @return A non-{@code null} {@link RenderScript} object.
+     *
+     * @throws IllegalStateException if {@link #setContext} was not called prior to this
+     */
+    public static synchronized RenderScript getRS() {
+        if (sRS == null) {
+            throw new IllegalStateException("Call #setContext before using #get");
+        }
+
+        return sRS;
+    }
+    /**
+     * Get the current {@link AllocationCache} singleton.
+     *
+     * @return A non-{@code null} {@link AllocationCache} object.
+     *
+     * @throws IllegalStateException if {@link #setContext} was not called prior to this
+     */
+    public static synchronized AllocationCache getCache() {
+        if (sCache == null) {
+            throw new IllegalStateException("Call #setContext before using #getCache");
+        }
+
+        return sCache;
+    }
+
+    // Suppress default constructor for noninstantiability
+    private RenderScriptSingleton() { throw new AssertionError(); }
+}
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/rs/Script.java b/tests/tests/hardware/src/android/hardware/camera2/cts/rs/Script.java
new file mode 100644
index 0000000..92ff1cb
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/rs/Script.java
@@ -0,0 +1,368 @@
+/*
+ * 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.rs;
+
+import static android.hardware.camera2.cts.helpers.Preconditions.*;
+
+import android.hardware.camera2.cts.helpers.UncheckedCloseable;
+import android.renderscript.Allocation;
+import android.renderscript.RenderScript;
+import android.util.Log;
+
+import java.util.HashMap;
+
+/**
+ * Base class for all renderscript script abstractions.
+ *
+ * <p>Each script has exactly one input and one output allocation, and is able to execute
+ * one {@link android.renderscript.Script} script file.</p>
+ *
+ * <p>Each script owns it's input allocation, but not the output allocation.</p>
+ *
+ * <p>Subclasses of this class must implement exactly one of two constructors:
+ * <ul>
+ * <li>{@code ScriptSubclass(AllocationInfo inputInfo)}
+ *  - if it expects 0 parameters
+ * <li>{@code ScriptSubclass(AllocationInfo inputInfo, ParameterMap<T> parameterMap))}
+ *  - if it expects 1 or more parameters
+ * </ul>
+ *
+ * @param <T> A concrete subclass of {@link android.renderscript.Script}
+ */
+public abstract class Script<T extends android.renderscript.Script> implements UncheckedCloseable {
+
+    /**
+     * A type-safe heterogenous parameter map for script parameters.
+     *
+     * @param <ScriptT> A concrete subclass of {@link Script}.
+     */
+    public static class ParameterMap<ScriptT extends Script<?>> {
+        private final HashMap<Script.ScriptParameter<ScriptT, ?>, Object> mParameterMap =
+                new HashMap<Script.ScriptParameter<ScriptT, ?>, Object>();
+
+        /**
+         * Create a new parameter map with 0 parameters.</p>
+         */
+        public ParameterMap() {}
+
+        /**
+         * Get the value associated with the given parameter key.
+         *
+         * @param parameter A type-safe key corresponding to a parameter.
+         *
+         * @return The value, or {@code null} if none was set.
+         *
+         * @param <T> The type of the value
+         *
+         * @throws NullPointerException if parameter was {@code null}
+         */
+        @SuppressWarnings("unchecked")
+        public <T> T get(Script.ScriptParameter<ScriptT, T> parameter) {
+            checkNotNull("parameter", parameter);
+
+            return (T) mParameterMap.get(parameter);
+        }
+
+        /**
+         * Sets the value associated with the given parameter key.
+         *
+         * @param parameter A type-safe key corresponding to a parameter.
+         * @param value The value
+         *
+         * @param <T> The type of the value
+         *
+         * @throws NullPointerException if parameter was {@code null}
+         * @throws NullPointerException if value was {@code null}
+         */
+        public <T> void set(Script.ScriptParameter<ScriptT, T> parameter, T value) {
+            checkNotNull("parameter", parameter);
+            checkNotNull("value", value);
+
+            if (!parameter.getValueClass().isInstance(value)) {
+                throw new IllegalArgumentException(
+                        "Runtime type mismatch between " + parameter + " and value " + value);
+            }
+
+            mParameterMap.put(parameter, value);
+        }
+
+        /**
+         * Whether or not at least one parameter has been {@link #set}.
+         *
+         * @return true if there is at least one element in the map
+         */
+        public boolean isEmpty() {
+            return mParameterMap.isEmpty();
+        }
+
+        /**
+         * Check if the parameter has been {@link #set} to a value.
+         *
+         * @param parameter A type-safe key corresponding to a parameter.
+         * @return true if there is a value corresponding to this parameter, false otherwise.
+         */
+        public boolean contains(Script.ScriptParameter<ScriptT, ?> parameter) {
+            checkNotNull("parameter", parameter);
+
+            return mParameterMap.containsKey(parameter);
+        }
+    }
+
+    /**
+     * A type-safe parameter key to be used with {@link ParameterMap}.
+     *
+     * @param <J> A concrete subclass of {@link Script}.
+     * @param <K> The type of the value that the parameter holds.
+     */
+    public static class ScriptParameter<J extends Script<?>, K> {
+        private final Class<J> mScriptClass;
+        private final Class<K> mValueClass;
+
+        ScriptParameter(Class<J> jClass, Class<K> kClass) {
+            checkNotNull("jClass", jClass);
+            checkNotNull("kClass", kClass);
+
+            mScriptClass = jClass;
+            mValueClass = kClass;
+        }
+
+        /**
+         * Get the runtime class associated with the value.
+         */
+        public Class<K> getValueClass() {
+            return mValueClass;
+        }
+
+        /**
+         * Compare with another object.
+         *
+         * <p>Two script parameters are considered equal only if their script class and value
+         * class are both equal.</p>
+         */
+        @SuppressWarnings("unchecked")
+        @Override
+        public boolean equals(Object other) {
+            if (other instanceof ScriptParameter) {
+                ScriptParameter<J, K> otherParam = (ScriptParameter<J,K>) other;
+
+                return mScriptClass.equals(otherParam.mScriptClass) &&
+                        mValueClass.equals(otherParam.mValueClass);
+            }
+
+            return false;
+        }
+
+        /**
+         * Gets the hash code for this object.
+         */
+        @Override
+        public int hashCode() {
+            return mScriptClass.hashCode() ^ mValueClass.hashCode();
+        }
+    }
+
+    private static final String TAG = "Script";
+    private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
+
+    protected final AllocationCache mCache = RenderScriptSingleton.getCache();
+    protected final RenderScript mRS = RenderScriptSingleton.getRS();
+
+    protected final AllocationInfo mInputInfo;
+    protected final AllocationInfo mOutputInfo;
+
+    protected Allocation mOutputAllocation;
+    protected Allocation mInputAllocation;
+
+    protected final T mScript;
+    private boolean mClosed = false;
+
+    /**
+     * Gets the {@link AllocationInfo info} associated with this script's input.
+     *
+     * @return A non-{@code null} {@link AllocationInfo} object.
+     *
+     * @throws IllegalStateException If the script has already been {@link #close closed}.
+     */
+    public AllocationInfo getInputInfo() {
+        checkNotClosed();
+
+        return mInputInfo;
+    }
+    /**
+     * Gets the {@link AllocationInfo info} associated with this script's output.
+     *
+     * @return A non-{@code null} {@link AllocationInfo} object.
+     *
+     * @throws IllegalStateException If the script has already been {@link #close closed}.
+     */
+    public AllocationInfo getOutputInfo() {
+        checkNotClosed();
+
+        return mOutputInfo;
+    }
+
+    /**
+     * Set the input.
+     *
+     * <p>Must be called before executing any scripts.</p>
+     *
+     * @throws IllegalStateException If the script has already been {@link #close closed}.
+     */
+    void setInput(Allocation allocation) {
+        checkNotClosed();
+        checkNotNull("allocation", allocation);
+        checkEquals("allocation info", AllocationInfo.newInstance(allocation),
+                "input info", mInputInfo);
+
+        // Scripts own the input, so return old input to cache if the input changes
+        if (mInputAllocation != allocation) {
+            mCache.returnToCacheIfNotNull(mInputAllocation);
+        }
+
+        mInputAllocation = allocation;
+        updateScriptInput();
+    }
+
+    protected abstract void updateScriptInput();
+
+    /**
+     * Set the output.
+     *
+     * <p>Must be called before executing any scripts.</p>
+     *
+     * @throws IllegalStateException If the script has already been {@link #close closed}.
+     */
+    void setOutput(Allocation allocation) {
+        checkNotClosed();
+        checkNotNull("allocation", allocation);
+        checkEquals("allocation info", AllocationInfo.newInstance(allocation),
+                "output info", mOutputInfo);
+
+        // Scripts do not own the output, simply set a reference to the new one.
+        mOutputAllocation = allocation;
+    }
+
+    protected Script(AllocationInfo inputInfo, AllocationInfo outputInfo, T rsScript) {
+        checkNotNull("inputInfo", inputInfo);
+        checkNotNull("outputInfo", outputInfo);
+        checkNotNull("rsScript", rsScript);
+
+        mInputInfo = inputInfo;
+        mOutputInfo = outputInfo;
+        mScript = rsScript;
+
+        if (VERBOSE) {
+            Log.v(TAG, String.format("%s - inputInfo = %s, outputInfo = %s, rsScript = %s",
+                    getName(), inputInfo, outputInfo, rsScript));
+        }
+    }
+
+    /**
+     * Get the {@link Allocation} associated with this script's input.</p>
+     *
+     * @return The input {@link Allocation}, which is never {@code null}.
+     *
+     * @throws IllegalStateException If the script has already been {@link #close closed}.
+     */
+    public Allocation getInput() {
+        checkNotClosed();
+
+        return mInputAllocation;
+    }
+    /**
+     * Get the {@link Allocation} associated with this script's output.</p>
+     *
+     * @return The output {@link Allocation}, which is never {@code null}.
+     *
+     * @throws IllegalStateException If the script has already been {@link #close closed}.
+     */
+    public Allocation getOutput() {
+        checkNotClosed();
+
+        return mOutputAllocation;
+    }
+
+    /**
+     * Execute the script's kernel against the input/output {@link Allocation allocations}.
+     *
+     * <p>Once this is complete, the output will have the new data available (for either
+     * the next script, or to read out with a copy).</p>
+     *
+     * @throws IllegalStateException If the script has already been {@link #close closed}.
+     */
+    public void execute() {
+        checkNotClosed();
+
+        if (mInputAllocation == null || mOutputAllocation == null) {
+            throw new IllegalStateException("Both inputs and outputs must have been set");
+        }
+
+        executeUnchecked();
+    }
+
+    /**
+     * Get the name of this script.
+     *
+     * <p>The name is the short hand name of the concrete class backing this script.</p>
+     *
+     * <p>This method works even if the script has already been {@link #close closed}.</p>
+     *
+     * @return A string representing the script name.
+     */
+    public String getName() {
+        return getClass().getSimpleName();
+    }
+
+    protected abstract void executeUnchecked();
+
+    protected void checkNotClosed() {
+        if (mClosed) {
+            throw new IllegalStateException("Script has been closed");
+        }
+    }
+
+    /**
+     * Destroy the underlying script object and return the input allocation back to the
+     * {@link AllocationCache cache}.
+     *
+     * <p>This method has no effect if called more than once.</p>
+     */
+    @Override
+    public void close() {
+        if (mClosed) return;
+
+        // Scripts own the input allocation. They do NOT own outputs.
+        mCache.returnToCacheIfNotNull(mInputAllocation);
+
+        mScript.destroy();
+
+        mClosed = true;
+    }
+
+    @Override
+    protected void finalize() throws Throwable {
+        try {
+            close();
+        } finally {
+            super.finalize();
+        }
+    }
+
+    protected static RenderScript getRS() {
+        return RenderScriptSingleton.getRS();
+    }
+}
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/rs/ScriptGraph.java b/tests/tests/hardware/src/android/hardware/camera2/cts/rs/ScriptGraph.java
new file mode 100644
index 0000000..56d8703
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/rs/ScriptGraph.java
@@ -0,0 +1,656 @@
+/*
+ * 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.rs;
+
+import static android.hardware.camera2.cts.helpers.Preconditions.*;
+import static junit.framework.Assert.*;
+
+import android.graphics.ImageFormat;
+import android.graphics.PixelFormat;
+import android.util.Size;
+import android.hardware.camera2.cts.helpers.MaybeNull;
+import android.hardware.camera2.cts.helpers.UncheckedCloseable;
+import android.hardware.camera2.cts.rs.Script.ParameterMap;
+import android.renderscript.Allocation;
+import android.util.Log;
+import android.view.Surface;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.util.ArrayList;
+import java.util.List;
+import com.android.ex.camera2.exceptions.TimeoutRuntimeException;
+
+/**
+ * An abstraction to simplify chaining together the execution of multiple RenderScript
+ * {@link android.renderscript.Script scripts} and managing their {@link Allocation allocations}.
+ *
+ * <p>Create a new script graph by using {@link #create}, configure the input with
+ * {@link Builder#configureInput}, then configure one or more scripts with
+ * {@link Builder#configureScript} or {@link Builder#chainScript}. Finally, freeze the graph
+ * with {@link Builder#buildGraph}.</p>
+ *
+ * <p>Once a script graph has been built, all underlying scripts and allocations are instantiated.
+ * Each script may be executed with {@link #execute}. Scripts are executed in the order that they
+ * were configured, with each previous script's output used as the input for the next script.
+ * </p>
+ *
+ * <p>In case the input {@link Allocation} is actually backed by a {@link Surface}, convenience
+ * methods ({@link #advanceInputWaiting} and {@link #advanceInputAndDrop} are provided to
+ * automatically update the {@link Allocation allocation} with the latest buffer available.</p>
+ *
+ * <p>All resources are managed by the {@link ScriptGraph} and {@link #close closing} the graph
+ * will release all underlying resources. See {@link #close} for more details.</p>
+ */
+public class ScriptGraph implements UncheckedCloseable {
+
+    private static final String TAG = "ScriptGraph";
+    private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
+
+    private static final int INPUT_SCRIPT_LOCATION = 0;
+    private final int OUTPUT_SCRIPT_LOCATION; // calculated in constructor
+
+    private final AllocationCache mCache = RenderScriptSingleton.getCache();
+
+    private final Size mSize;
+    private final int mFormat;
+    private final int mUsage;
+    private final List<Script<?>> mScripts;
+
+    private final BlockingInputAllocation mInputBlocker;
+    private final Allocation mOutputAllocation;
+    private boolean mClosed = false;
+
+    /**
+     * Create a new {@link Builder} that will be used to configure the graph's inputs
+     * and scripts (and parameters).
+     *
+     * <p>Once a graph has been fully built, the configuration is immutable.</p>
+     *
+     * @return a {@link Builder} that will be used to configure the graph settings
+     */
+    public static Builder create() {
+        return new Builder();
+    }
+
+    /**
+     * Wait until another buffer is produced into the input {@link Surface}, then
+     * update the backing input {@link Allocation} with the latest buffer with
+     * {@link Allocation#ioReceive ioReceive}.
+     *
+     * @throws IllegalArgumentException
+     *            if the graph wasn't configured with
+     *            {@link Builder#configureInputWithSurface configureInputWithSurface}
+     * @throws TimeoutRuntimeException
+     *            if waiting for the buffer times out
+     */
+    public void advanceInputWaiting() {
+        checkNotClosed();
+        if (!isInputFromSurface()) {
+            throw new IllegalArgumentException("Graph was not configured with USAGE_IO_INPUT");
+        }
+
+        mInputBlocker.waitForBufferAndReceive();
+    }
+
+    /**
+     * Update the backing input {@link Allocation} with the latest buffer with
+     * {@link Allocation#ioReceive ioReceive} repeatedly until no more buffers are pending.
+     *
+     * <p>Does not wait for new buffers to become available if none are currently available
+     * (i.e. {@code false} is returned immediately).</p>
+     *
+     * @return true if any buffers were pending
+     *
+     * @throws IllegalArgumentException
+     *            if the graph wasn't configured with
+     *            {@link Builder#configureInputWithSurface configureInputWithSurface}
+     * @throws TimeoutRuntimeException
+     *            if waiting for the buffer times out
+     */
+    public boolean advanceInputAndDrop() {
+        checkNotClosed();
+        if (!isInputFromSurface()) {
+            throw new IllegalArgumentException("Graph was not configured with USAGE_IO_INPUT");
+        }
+
+        return mInputBlocker.receiveLatestAvailableBuffers();
+    }
+
+    /**
+     * Execute each script in the graph, with each next script's input using the
+     * previous script's output.
+     *
+     * <p>Scripts are executed in the same order that they were configured by the {@link Builder}.
+     * </p>
+     *
+     * @throws IllegalStateException if the graph was already {@link #close closed}
+     */
+    public void execute() {
+        checkNotClosed();
+
+        // TODO: Can we use android.renderscript.ScriptGroup here to make it faster?
+
+        int i = 0;
+        for (Script<?> script : mScripts) {
+            script.execute();
+            i++;
+        }
+
+        if (VERBOSE) Log.v(TAG, "execute - invoked " + i + " scripts");
+    }
+
+    /**
+     * Copies the data from the last script's output {@link Allocation} into a byte array.
+     *
+     * <p>The output allocation must be of an 8 bit integer
+     * {@link android.renderscript.Element Element} type.</p>
+     *
+     * @return A byte[] copy.
+     *
+     * @throws IllegalStateException if the graph was already {@link #close closed}
+     */
+    public byte[] getOutputData() {
+        checkNotClosed();
+
+        Allocation outputAllocation = getOutputAllocation();
+
+        byte[] destination = new byte[outputAllocation.getBytesSize()];
+        outputAllocation.copyTo(destination);
+
+        return destination;
+    }
+
+    /**
+     * Copies the data from the first script's input {@link Allocation} into a byte array.
+     *
+     * <p>The input allocation must be of an 8 bit integer
+     * {@link android.renderscript.Element Element} type.</p>
+     *
+     * @return A byte[] copy.
+     *
+     * @throws IllegalStateException if the graph was already {@link #close closed}
+     */
+    public byte[] getInputData() {
+        checkNotClosed();
+
+        Allocation inputAllocation = getInputAllocation();
+
+        byte[] destination = new byte[inputAllocation.getBytesSize()];
+        inputAllocation.copyTo(destination);
+
+        return destination;
+    }
+
+    /**
+     * Builds a {@link ScriptGraph} by configuring input size/format/usage,
+     * the script classes in the graph, and the parameters passed to the scripts.
+     *
+     * @see ScriptGraph#create
+     */
+    public static class Builder {
+
+        private Size mSize;
+        private int mFormat;
+        private int mUsage;
+
+        private final List<ScriptBuilder<? extends Script<?>>> mChainedScriptBuilders =
+                new ArrayList<ScriptBuilder<? extends Script<?>>>();
+
+        /**
+         * Configure the {@link Allocation} that will be used as the input to the first
+         * script, using the default usage.
+         *
+         * <p>Short hand for calling {@link #configureInput(int, int, int, int)} with a
+         * {@code 0} usage.</p>
+         *
+         * @param width Width in pixels
+         * @param height Height in pixels
+         * @param format Format from {@link ImageFormat} or {@link PixelFormat}
+         *
+         * @return The current builder ({@code this}). Use for chaining method calls.
+         */
+        public Builder configureInput(int width, int height, int format) {
+            return configureInput(new Size(width, height), format, /*usage*/0);
+        }
+
+        /**
+         * Configure the {@link Allocation} that will be used as the input to the first
+         * script.
+         *
+         * <p>The {@code usage} is always ORd together with {@link Allocation#USAGE_SCRIPT}.</p>
+         *
+         * @param width Width in pixels
+         * @param height Height in pixels
+         * @param format Format from {@link ImageFormat} or {@link PixelFormat}
+         * @param usage Usage flags such as {@link Allocation#USAGE_IO_INPUT}
+         *
+         * @return The current builder ({@code this}). Use for chaining method calls.
+         */
+        public Builder configureInput(int width, int height, int format, int usage) {
+            return configureInput(new Size(width, height), format, usage);
+        }
+
+        /**
+         * Configure the {@link Allocation} that will be used as the input to the first
+         * script, using the default usage.
+         *
+         * <p>Short hand for calling {@link #configureInput(Size, int, int)} with a
+         * {@code 0} usage.</p>
+         *
+         * @param size Size (width, height)
+         * @param format Format from {@link ImageFormat} or {@link PixelFormat}
+         *
+         * @return The current builder ({@code this}). Use for chaining method calls.
+         *
+         * @throws NullPointerException if size was {@code null}
+         */
+        public Builder configureInput(Size size, int format) {
+            return configureInput(size, format, /*usage*/0);
+        }
+
+        /**
+         * Configure the {@link Allocation} that will use a {@link Surface} to produce input into
+         * the first script.
+         *
+         * <p>Short hand for calling {@link #configureInput(Size, int, int)} with the
+         * {@link Allocation#USAGE_IO_INPUT} usage.</p>
+         *
+         * <p>The {@code usage} is always ORd together with {@link Allocation#USAGE_SCRIPT}.</p>
+         *
+         * @param size Size (width, height)
+         * @param format Format from {@link ImageFormat} or {@link PixelFormat}
+         *
+         * @return The current builder ({@code this}). Use for chaining method calls.
+         *
+         * @throws NullPointerException if size was {@code null}
+         */
+        public Builder configureInputWithSurface(Size size, int format) {
+            return configureInput(size, format, Allocation.USAGE_IO_INPUT);
+        }
+
+        /**
+         * Configure the {@link Allocation} that will be used as the input to the first
+         * script.
+         *
+         * <p>The {@code usage} is always ORd together with {@link Allocation#USAGE_SCRIPT}.</p>
+         *
+         * @param size Size (width, height)
+         * @param format Format from {@link ImageFormat} or {@link PixelFormat}
+         * @param usage Usage flags such as {@link Allocation#USAGE_IO_INPUT}
+         *
+         * @return The current builder ({@code this}). Use for chaining method calls.
+         *
+         * @throws NullPointerException if size was {@code null}
+         */
+        public Builder configureInput(Size size, int format, int usage) {
+            checkNotNull("size", size);
+
+            mSize = size;
+            mFormat = format;
+            mUsage = usage | Allocation.USAGE_SCRIPT;
+
+            return this;
+        }
+
+        /**
+         * Build a {@link Script} by setting parameters it might require for execution.
+         *
+         * <p>Refer to the documentation for {@code T} to see if there are any
+         * {@link Script.ScriptParameter parameters} in it.
+         * </p>
+         *
+         * @param <T> Concrete type subclassing the {@link Script} class.
+         */
+        public class ScriptBuilder<T extends Script<?>> {
+
+            private final Class<T> mScriptClass;
+
+            private ScriptBuilder(Class<T> scriptClass) {
+                mScriptClass = scriptClass;
+            }
+
+            private final ParameterMap<T> mParameterMap = new ParameterMap<T>();
+
+            /**
+             * Set a script parameter to the specified value.
+             *
+             * @param parameter The {@link Script.ScriptParameter parameter key} in {@code T}
+             * @param value A value of type {@code K} that the script expects.
+             * @param <K> The type of the parameter {@code value}.
+             *
+             * @return The current builder ({@code this}). Use to chain method calls.
+             *
+             * @throws NullPointerException if parameter was {@code null}
+             * @throws NullPointerException if value was {@code null}
+             * @throws IllegalStateException if the parameter was already {@link #set}
+             */
+            public <K> ScriptBuilder<T> set(Script.ScriptParameter<T, K> parameter, K value) {
+                checkNotNull("parameter", parameter);
+                checkNotNull("value", value);
+                checkState("Parameter has already been set", !mParameterMap.contains(parameter));
+
+                mParameterMap.set(parameter, value);
+
+                return this;
+            }
+
+            ParameterMap<T> getParameterMap() {
+                return mParameterMap;
+            }
+
+            Class<T> getScriptClass() {
+                return mScriptClass;
+            }
+
+            /**
+             * Build the script and freeze the parameter list to what was {@link #set}.
+             *
+             * @return
+             *            The {@link ScriptGraph#Builder} that was used to configure
+             *            {@link this} script.</p>
+             */
+            public Builder buildScript() {
+                mChainedScriptBuilders.add(this);
+
+                return Builder.this;
+            }
+        }
+
+        /**
+         * Configure the script with no parameters.
+         *
+         * <p>Short hand for invoking {@link #configureScript} immediately followed by
+         * {@link ScriptBuilder#buildScript()}.
+         *
+         * @param scriptClass A concrete class that subclasses {@link Script}
+         * @return The current builder ({@code this}). Use to chain method calls.
+         *
+         * @throws NullPointerException if {@code scriptClass} was {@code null}
+         */
+        public <T extends Script<?>> Builder chainScript(Class<T> scriptClass) {
+            checkNotNull("scriptClass", scriptClass);
+
+            return (new ScriptBuilder<T>(scriptClass)).buildScript();
+        }
+
+        /**
+         * Configure the script with parameters.
+         *
+         * <p>Only useful when the {@code scriptClass} has one or more
+         * {@link Script.ScriptParameter script parameters} defined.</p>
+         *
+         * @param scriptClass A concrete class that subclasses {@link Script}
+         * @return A script configuration {@link ScriptBuilder builder}. Use to chain method calls.
+         *
+         * @throws NullPointerException if {@code scriptClass} was {@code null}
+         */
+        public <T extends Script<?>> ScriptBuilder<T> configureScript(Class<T> scriptClass) {
+            checkNotNull("scriptClass", scriptClass);
+
+            return new ScriptBuilder<T>(scriptClass);
+        }
+
+        /**
+         * Finish configuring the graph and freeze the settings, instantiating all
+         * the {@link Script scripts} and {@link Allocation allocations}.
+         *
+         * @return A constructed {@link ScriptGraph}.
+         */
+        public ScriptGraph buildGraph() {
+            return new ScriptGraph(this);
+        }
+
+        private Builder() {}
+    }
+
+    private ScriptGraph(Builder builder) {
+        mSize = builder.mSize;
+        mFormat = builder.mFormat;
+        mUsage = builder.mUsage;
+        List<Builder.ScriptBuilder<? extends Script<?>>> chainedScriptBuilders =
+                builder.mChainedScriptBuilders;
+        mScripts = new ArrayList<Script<?>>(/*capacity*/chainedScriptBuilders.size());
+        OUTPUT_SCRIPT_LOCATION = chainedScriptBuilders.size() - 1;
+
+        if (mSize == null) {
+            throw new IllegalArgumentException("Inputs were not configured");
+        }
+
+        if (chainedScriptBuilders.isEmpty()) {
+            throw new IllegalArgumentException("At least one script should be chained");
+        }
+
+        /*
+         * The first input is special since it could be USAGE_IO_INPUT.
+         */
+        AllocationInfo inputInfo = AllocationInfo.newInstance(mSize, mFormat, mUsage);
+        Allocation inputAllocation;
+
+        // Create an Allocation with a Surface if the input to the graph requires it
+        if (isInputFromSurface()) {
+            mInputBlocker = inputInfo.createBlockingInputAllocation();
+            inputAllocation = mInputBlocker.getAllocation();
+        } else {
+            mInputBlocker = null;
+            inputAllocation = inputInfo.createAllocation();
+        }
+
+        if (VERBOSE) Log.v(TAG, "ScriptGraph() - Instantiating all script classes");
+
+        // Create all scripts.
+        for (Builder.ScriptBuilder<? extends Script<?>> scriptBuilder: chainedScriptBuilders) {
+
+            @SuppressWarnings("unchecked")
+            Class<Script<?>> scriptClass = (Class<Script<?>>) scriptBuilder.getScriptClass();
+
+            @SuppressWarnings("unchecked")
+            ParameterMap<Script<?>> parameters = (ParameterMap<Script<?>>)
+                    scriptBuilder.getParameterMap();
+
+            Script<?> script = instantiateScript(scriptClass, inputInfo, parameters);
+            mScripts.add(script);
+
+            // The next script's input info is the current script's output info
+            inputInfo = script.getOutputInfo();
+        }
+
+        if (VERBOSE) Log.v(TAG, "ScriptGraph() - Creating all inputs");
+
+        // Create and wire up all inputs.
+        int i = 0;
+        Script<?> inputScript = mScripts.get(INPUT_SCRIPT_LOCATION);
+        do {
+            if (VERBOSE) {
+                Log.v(TAG, "ScriptGraph() - Setting input for script " + inputScript.getName());
+            }
+
+            inputScript.setInput(inputAllocation);
+
+            i++;
+
+            if (i >= mScripts.size()) {
+                break;
+            }
+
+            // Use the graph input for the first loop iteration
+            inputScript = mScripts.get(i);
+            inputInfo = inputScript.getInputInfo();
+            inputAllocation = inputInfo.createAllocation();
+        } while (true);
+
+        if (VERBOSE) Log.v(TAG, "ScriptGraph() - Creating all outputs");
+
+        // Create and wire up all outputs.
+        Allocation lastOutput = null;
+        for (i = 0; i < mScripts.size(); ++i) {
+            Script<?> script = mScripts.get(i);
+            Script<?> nextScript = (i + 1 < mScripts.size()) ? mScripts.get(i + 1) : null;
+
+            // Each script's output uses the next script's input.
+            // -- Since the last script has no next script, we own its output allocation.
+            lastOutput = (nextScript != null) ? nextScript.getInput()
+                                              : script.getOutputInfo().createAllocation();
+
+            if (VERBOSE) {
+                Log.v(TAG, "ScriptGraph() - Setting output for script " + script.getName());
+            }
+
+            script.setOutput(lastOutput);
+        }
+        mOutputAllocation = checkNotNull("lastOutput", lastOutput);
+
+        // Done. Safe to execute the graph now.
+
+        if (VERBOSE) Log.v(TAG, "ScriptGraph() - Graph has been built");
+    }
+
+    /**
+     * Construct the script by instantiating it via reflection.
+     *
+     * <p>The {@link Script scriptClass} should have a {@code Script(AllocationInfo inputInfo)}
+     * constructor if it expects an empty parameter map.</p>
+     *
+     * <p>If it expects a non-empty parameter map, it should have a
+     * {@code Script(AllocationInfo inputInfo, ParameterMap<T> parameterMap)} constructor.</p>
+     */
+    private static <T extends Script<?>> T instantiateScript(
+            Class<T> scriptClass, AllocationInfo inputInfo, ParameterMap<T> parameterMap) {
+
+        Constructor<T> ctor;
+        try {
+            // TODO: Would be better if we looked at the script class to see if it expects params
+            if (parameterMap.isEmpty()) {
+                // Script(AllocationInfo inputInfo)
+                ctor = scriptClass.getConstructor(AllocationInfo.class);
+            } else {
+                // Script(AllocationInfo inputInfo, ParameterMap<T> parameterMap)
+                ctor = scriptClass.getConstructor(AllocationInfo.class, ParameterMap.class);
+            }
+        } catch (NoSuchMethodException e) {
+            throw new UnsupportedOperationException(
+                    "Script class " + scriptClass + " must have a matching constructor", e);
+        }
+
+        try {
+            if (parameterMap.isEmpty()) {
+                return ctor.newInstance(inputInfo);
+            } else {
+                return ctor.newInstance(inputInfo, parameterMap);
+            }
+        } catch (InstantiationException e) {
+            throw new UnsupportedOperationException(e);
+        } catch (IllegalAccessException e) {
+            throw new UnsupportedOperationException(e);
+        } catch (IllegalArgumentException e) {
+            throw new UnsupportedOperationException(e);
+        } catch (InvocationTargetException e) {
+            throw new UnsupportedOperationException(e);
+        }
+    }
+
+    private boolean isInputFromSurface() {
+        return (mUsage & Allocation.USAGE_IO_INPUT) != 0;
+    }
+
+    /**
+     * Get the input {@link Allocation} that is used by the first script as the input.
+     *
+     * @return An {@link Allocation} (never {@code null}).
+     *
+     * @throws IllegalStateException if the graph was already {@link #close closed}
+     */
+    public Allocation getInputAllocation() {
+        checkNotClosed();
+
+        return mScripts.get(INPUT_SCRIPT_LOCATION).getInput();
+    }
+
+    /**
+     * Get the output {@link Allocation} that is used by the last script as the output.
+     *
+     * @return An {@link Allocation} (never {@code null}).
+     *
+     * @throws IllegalStateException if the graph was already {@link #close closed}
+     */
+    public Allocation getOutputAllocation() {
+        checkNotClosed();
+        Allocation output = mScripts.get(OUTPUT_SCRIPT_LOCATION).getOutput();
+
+        assertEquals("Graph's output should match last script's output", mOutputAllocation, output);
+
+        return output;
+    }
+
+    /**
+     * Get the {@link Surface} that can be used produce buffers into the
+     * {@link #getInputAllocation input allocation}.
+     *
+     * @throws IllegalStateException
+     *            if input wasn't configured with {@link Allocation#USAGE_IO_INPUT} {@code usage}.
+     * @throws IllegalStateException
+     *            if the graph was already {@link #close closed}
+     *
+     * @return A {@link Surface} (never {@code null}).
+     */
+    public Surface getInputSurface() {
+        checkNotClosed();
+        checkState("This graph was not configured with IO_USAGE_INPUT", isInputFromSurface());
+
+        return getInputAllocation().getSurface();
+    }
+
+    private void checkNotClosed() {
+        checkState("ScriptGraph has been closed", !mClosed);
+    }
+
+    /**
+     * Releases all underlying resources associated with this {@link ScriptGraph}.
+     *
+     * <p>In particular, all underlying {@link Script scripts} and all
+     * {@link Allocation allocations} are also closed.</p>
+     *
+     * <p>All further calls to any other public methods (other than {@link #close}) will throw
+     * an {@link IllegalStateException}.</p>
+     *
+     * <p>This method is idempotent; calling it more than once will
+     * have no further effect.</p>
+     */
+    @Override
+    public synchronized void close() {
+        if (mClosed) return;
+
+        for (Script<?> script : mScripts) {
+            script.close();
+        }
+        mScripts.clear();
+
+        MaybeNull.close(mInputBlocker);
+        mCache.returnToCache(mOutputAllocation);
+
+        mClosed = true;
+    }
+
+    @Override
+    protected void finalize() throws Throwable {
+        try {
+            close();
+        } finally {
+            super.finalize();
+        }
+    }
+}
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/rs/ScriptYuvCrop.java b/tests/tests/hardware/src/android/hardware/camera2/cts/rs/ScriptYuvCrop.java
new file mode 100644
index 0000000..ae55cc9
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/rs/ScriptYuvCrop.java
@@ -0,0 +1,109 @@
+/*
+ * 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.rs;
+
+import static android.hardware.camera2.cts.helpers.Preconditions.*;
+
+import android.graphics.ImageFormat;
+import android.graphics.RectF;
+import android.util.Size;
+import android.hardware.camera2.cts.ScriptC_crop_yuvf_420_to_yuvx_444;
+import android.hardware.camera2.cts.rs.AllocationInfo.ElementInfo;
+import android.renderscript.Element;
+import android.util.Log;
+
+/**
+ * Crop {@link ImageFormat#YUV_420_888 flexible-YUV} {@link Allocation allocations} into
+ * a {@link ElementInfo#U8_3 U8_3} {@link Allocation allocation}.
+ *
+ * <p>Users of this script must configure it with the
+ * {@link ScriptYuvCrop#CROP_WINDOW crop window} parameter.</p>
+ *
+ */
+public class ScriptYuvCrop extends Script<ScriptC_crop_yuvf_420_to_yuvx_444> {
+    private static final String TAG = "ScriptYuvCrop";
+    private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
+
+    /**
+     * A rectangle holding the top,left,right,bottom normalized coordinates each within [0,1].
+     *
+     * <p>The output will be a cropped copy of the input to only this crop window.</p>
+     */
+    // TODO: Change this to use Patch
+    public static final Script.ScriptParameter<ScriptYuvCrop, RectF> CROP_WINDOW =
+            new Script.ScriptParameter<ScriptYuvCrop, RectF>(ScriptYuvCrop.class,
+                    RectF.class);
+
+    private final RectF mCropWindow;
+
+    private static AllocationInfo createOutputInfo(AllocationInfo inputInfo,
+            ParameterMap<ScriptYuvCrop> parameters) {
+        checkNotNull("inputInfo", inputInfo);
+        checkNotNull("parameters", parameters);
+
+        if (!parameters.contains(CROP_WINDOW)) {
+            throw new IllegalArgumentException("Script's CROP_WINDOW was not set");
+        }
+
+        Size inputSize = inputInfo.getSize();
+        RectF crop = parameters.get(CROP_WINDOW);
+        Size outputSize = new Size(
+                (int)(crop.width() * inputSize.getWidth()),
+                (int)(crop.height() * inputSize.getHeight()));
+
+        if (VERBOSE) Log.v(TAG, String.format("createOutputInfo - outputSize is %s", outputSize));
+
+        /**
+         * Input  YUV  W     x H
+         * Output U8_3 CropW x CropH
+         */
+        return AllocationInfo.newInstance(Element.U8_3(getRS()), outputSize);
+    }
+
+    public ScriptYuvCrop(AllocationInfo inputInfo,
+            ParameterMap<ScriptYuvCrop> parameterMap) {
+        super(inputInfo,
+              createOutputInfo(inputInfo, parameterMap),
+              new ScriptC_crop_yuvf_420_to_yuvx_444(getRS()));
+
+        // YUV_420_888 is the only supported format here
+        if (!inputInfo.isElementEqualTo(ElementInfo.YUV)) {
+            throw new UnsupportedOperationException("Unsupported element "
+                    + inputInfo.getElement());
+        }
+
+        mCropWindow = parameterMap.get(CROP_WINDOW);
+    }
+
+    @Override
+    protected void executeUnchecked() {
+        mScript.forEach_crop(mOutputAllocation);
+
+        if (VERBOSE) { Log.v(TAG, "executeUnchecked - forEach_crop done"); }
+    }
+
+    @Override
+    protected void updateScriptInput() {
+        int x = (int)(mCropWindow.left * mInputInfo.getSize().getWidth());
+        int y = (int)(mCropWindow.top * mInputInfo.getSize().getHeight());
+
+        mScript.set_src_x(x);
+        mScript.set_src_y(y);
+
+        mScript.set_mInput(mInputAllocation);
+    }
+}
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/rs/ScriptYuvMeans1d.java b/tests/tests/hardware/src/android/hardware/camera2/cts/rs/ScriptYuvMeans1d.java
new file mode 100644
index 0000000..fb76196
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/rs/ScriptYuvMeans1d.java
@@ -0,0 +1,69 @@
+/*
+ * 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.rs;
+
+import static android.hardware.camera2.cts.helpers.Preconditions.*;
+
+import android.util.Size;
+import android.hardware.camera2.cts.ScriptC_means_yuvx_444_1d_to_single;
+import android.hardware.camera2.cts.rs.AllocationInfo.ElementInfo;
+import android.renderscript.Element;
+
+/**
+ * Average a {@code Hx1} {@link ElementInfo#U8_3 U8_3} {@link Allocation allocation} into a 1x1
+ * {@code U8x3} {@link Allocation allocation}.
+ *
+ * <p>Users of this script should chain {@link ScriptYuvMeans1d} immediately before this
+ * to average the input down to a 1x1 element.</p>
+ */
+public class ScriptYuvMeans1d extends Script<ScriptC_means_yuvx_444_1d_to_single>{
+    private static final String TAG = "ScriptYuvMeans1d";
+
+    private static final Size UNIT_SQUARE = new Size(/*width*/1, /*height*/1);
+
+    private static AllocationInfo createOutputInfo(AllocationInfo inputInfo) {
+        checkNotNull("inputInfo", inputInfo);
+        // (input) Hx1 -> 1x1
+        return AllocationInfo.newInstance(Element.U8_3(getRS()), UNIT_SQUARE);
+    }
+
+    public ScriptYuvMeans1d(AllocationInfo inputInfo) {
+        super(inputInfo,
+              createOutputInfo(inputInfo),
+              new ScriptC_means_yuvx_444_1d_to_single(getRS()));
+
+        // U8x3 is the only supported element here
+        if (!inputInfo.isElementEqualTo(ElementInfo.U8_3)) {
+            throw new UnsupportedOperationException("Unsupported element "
+                    + inputInfo.getElement());
+        }
+    }
+
+    @Override
+    protected void executeUnchecked() {
+        mScript.forEach_means_yuvx_444(mOutputAllocation);
+    }
+
+    @Override
+    protected void updateScriptInput() {
+        mScript.set_mInput(mInputAllocation);
+
+        int width = mInputAllocation.getType().getX();
+        mScript.set_width(width);
+        mScript.set_inv_width(1.0f / width);
+    }
+}
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/rs/ScriptYuvMeans2dTo1d.java b/tests/tests/hardware/src/android/hardware/camera2/cts/rs/ScriptYuvMeans2dTo1d.java
new file mode 100644
index 0000000..2776f96
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/rs/ScriptYuvMeans2dTo1d.java
@@ -0,0 +1,90 @@
+/*
+ * 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.rs;
+
+import static android.hardware.camera2.cts.helpers.Preconditions.*;
+
+import android.graphics.ImageFormat;
+import android.graphics.RectF;
+import android.util.Size;
+import android.hardware.camera2.cts.ScriptC_means_yuvx_444_2d_to_1d;
+import android.hardware.camera2.cts.rs.AllocationInfo.ElementInfo;
+import android.util.Log;
+
+/**
+ * Average pixels from a {@link ImageFormat#YUV_420_888 flexible-YUV} or
+ * {@link ElementInfo#U8_3 U8_3} {@link Allocation allocations} into a 1D Hx1
+ * {@link ElementInfo#U8_3 U8_3} {@link Allocation allocation}.
+ *
+ * <p>Users of this script should chain {@link ScriptYuvMeans1d} immediately afterwards
+ * to average the output down to a 1x1 element.</p>
+ */
+public class ScriptYuvMeans2dTo1d extends Script<ScriptC_means_yuvx_444_2d_to_1d> {
+
+    private static final String TAG = "ScriptYuvMeans2dTo1d";
+    private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
+
+    private static AllocationInfo createOutputInfo(AllocationInfo inputInfo) {
+        checkNotNull("inputInfo", inputInfo);
+        // (input) WxH -> (output) Hx1
+        return AllocationInfo.newInstance(inputInfo.getElement(),
+                new Size(inputInfo.getSize().getHeight(), /*height*/1));
+    }
+
+    public ScriptYuvMeans2dTo1d(AllocationInfo inputInfo) {
+        super(inputInfo,
+              createOutputInfo(inputInfo),
+              new ScriptC_means_yuvx_444_2d_to_1d(getRS()));
+
+        // YUV_420_888 and U8_3 is the only supported format here
+        if (!inputInfo.isElementEqualTo(ElementInfo.YUV) &&
+                !inputInfo.isElementEqualTo(ElementInfo.U8_3)) {
+            throw new UnsupportedOperationException("Unsupported element "
+                    + inputInfo.getElement());
+        }
+    }
+
+    @Override
+    protected void executeUnchecked() {
+        // TODO: replace with switch statement
+        if (mInputInfo.isElementEqualTo(ElementInfo.YUV)) {
+            mScript.forEach_means_yuvf_420(mOutputAllocation);
+
+            if (VERBOSE) Log.v(TAG, "executeUnchecked - forEach_means_yuvf_420");
+        } else if (mInputInfo.isElementEqualTo(ElementInfo.U8_3)) {
+            mScript.forEach_means_yuvx_444(mOutputAllocation);
+
+            if (VERBOSE) Log.v(TAG, "executeUnchecked - forEach_means_yuvx_444");
+        } else {
+            throw new UnsupportedOperationException("Unsupported element "
+                    + mInputInfo.getElement());
+        }
+    }
+
+    @Override
+    protected void updateScriptInput() {
+        mScript.set_mInput(mInputAllocation);
+
+        int width = mInputAllocation.getType().getX();
+        mScript.set_width(width);
+        mScript.set_inv_width(1.0f / width);
+
+        // Do not crop. Those who want to crop should use ScriptYuvCrop.class
+        mScript.set_src_x(0);
+        mScript.set_src_y(0);
+    }
+}
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/rs/ScriptYuvToRgb.java b/tests/tests/hardware/src/android/hardware/camera2/cts/rs/ScriptYuvToRgb.java
new file mode 100644
index 0000000..5cdc0a0
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/rs/ScriptYuvToRgb.java
@@ -0,0 +1,64 @@
+/*
+ * 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.rs;
+
+import static android.hardware.camera2.cts.helpers.Preconditions.*;
+
+import android.graphics.ImageFormat;
+import android.graphics.PixelFormat;
+import android.hardware.camera2.cts.rs.AllocationInfo.ElementInfo;
+import android.renderscript.Element;
+import android.renderscript.ScriptIntrinsicYuvToRGB;
+import android.util.Log;
+
+/**
+ * Convert {@link ImageFormat#YUV_420_888 flexible-YUV} {@link Allocation allocations} into
+ * a {@link ElementInfo#RGBA_8888 RGBA_8888} {@link Allocation allocation}.
+ */
+public class ScriptYuvToRgb extends Script<ScriptIntrinsicYuvToRGB> {
+    private static final String TAG = "ScriptYuvToRgb";
+    private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
+
+    private static AllocationInfo createOutputInfo(AllocationInfo outputInfo) {
+        checkNotNull("outputInfo", outputInfo);
+        return outputInfo.changeFormatWithDefaultUsage(PixelFormat.RGBA_8888);
+    }
+
+    public ScriptYuvToRgb(AllocationInfo inputInfo) {
+        super(inputInfo,
+              createOutputInfo(inputInfo),
+              ScriptIntrinsicYuvToRGB.create(getRS(), Element.YUV(getRS())));
+
+        // YUV_420_888 is the only supported format here
+        //      XX: Supports any YUV 4:2:0 such as NV21/YV12 or just YUV_420_888 ?
+        if (!inputInfo.isElementEqualTo(ElementInfo.YUV)) {
+            throw new UnsupportedOperationException("Unsupported element "
+                    + inputInfo.getElement());
+        }
+    }
+
+    @Override
+    protected void executeUnchecked() {
+        mScript.forEach(mOutputAllocation);
+
+        if (VERBOSE) { Log.v(TAG, "executeUnchecked - forEach done"); }
+    }
+
+    @Override
+    protected void updateScriptInput() {
+        mScript.setInput(mInputAllocation);
+    }
+}
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/testcases/Camera2AndroidTestCase.java b/tests/tests/hardware/src/android/hardware/camera2/cts/testcases/Camera2AndroidTestCase.java
new file mode 100644
index 0000000..56f0dba
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/testcases/Camera2AndroidTestCase.java
@@ -0,0 +1,324 @@
+/*
+ * 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.testcases;
+
+import static android.hardware.camera2.cts.CameraTestUtils.*;
+import static com.android.ex.camera2.blocking.BlockingStateListener.*;
+
+import android.content.Context;
+import android.hardware.camera2.CameraCaptureSession;
+import android.hardware.camera2.CameraCaptureSession.CaptureListener;
+import android.hardware.camera2.CameraDevice;
+import android.hardware.camera2.CameraManager;
+import android.hardware.camera2.CaptureRequest;
+import android.util.Size;
+import android.hardware.camera2.cts.CameraTestUtils;
+import android.hardware.camera2.cts.helpers.CameraErrorCollector;
+import android.hardware.camera2.cts.helpers.StaticMetadata;
+import android.hardware.camera2.cts.helpers.StaticMetadata.CheckLevel;
+import android.media.Image;
+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;
+
+import com.android.ex.camera2.blocking.BlockingSessionListener;
+import com.android.ex.camera2.blocking.BlockingStateListener;
+
+import java.util.List;
+
+public class Camera2AndroidTestCase extends AndroidTestCase {
+    private static final String TAG = "Camera2AndroidTestCase";
+    private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
+
+    protected static final String DEBUG_FILE_NAME_BASE =
+            Environment.getExternalStorageDirectory().getPath();
+    // Default capture size: VGA size is required by CDD.
+    protected static final Size DEFAULT_CAPTURE_SIZE = new Size(640, 480);
+    protected static final int CAPTURE_WAIT_TIMEOUT_MS = 5000;
+
+    protected CameraManager mCameraManager;
+    protected CameraDevice mCamera;
+    protected CameraCaptureSession mCameraSession;
+    protected BlockingSessionListener mCameraSessionListener;
+    protected BlockingStateListener mCameraListener;
+    protected String[] mCameraIds;
+    protected ImageReader mReader;
+    protected Surface mReaderSurface;
+    protected Handler mHandler;
+    protected HandlerThread mHandlerThread;
+    protected StaticMetadata mStaticInfo;
+    protected CameraErrorCollector mCollector;
+    protected List<Size> mOrderedPreviewSizes; // In descending order.
+    protected List<Size> mOrderedVideoSizes; // In descending order.
+    protected List<Size> mOrderedStillSizes; // In descending order.
+
+    @Override
+    public void setContext(Context context) {
+        super.setContext(context);
+        mCameraManager = (CameraManager) context.getSystemService(Context.CAMERA_SERVICE);
+        assertNotNull("Can't connect to camera manager!", mCameraManager);
+    }
+
+    /**
+     * Set up the camera2 test case required environments, including CameraManager,
+     * HandlerThread, Camera IDs, and CameraStateListener etc.
+     */
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+
+        /**
+         * 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", getContext().getCacheDir().toString());
+
+        mCameraIds = mCameraManager.getCameraIdList();
+        assertNotNull("Camera ids shouldn't be null", mCameraIds);
+        mHandlerThread = new HandlerThread(TAG);
+        mHandlerThread.start();
+        mHandler = new Handler(mHandlerThread.getLooper());
+        mCameraListener = new BlockingStateListener();
+        mCollector = new CameraErrorCollector();
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        mHandlerThread.quitSafely();
+        mHandler = null;
+        closeDefaultImageReader();
+
+        try {
+            mCollector.verify();
+        } catch (Throwable e) {
+            // When new Exception(e) is used, exception info will be printed twice.
+            throw new Exception(e.getMessage());
+        } finally {
+            super.tearDown();
+        }
+    }
+
+    /**
+     * Start capture with given {@link #CaptureRequest}.
+     *
+     * @param request The {@link #CaptureRequest} to be captured.
+     * @param repeating If the capture is single capture or repeating.
+     * @param listener The {@link #CaptureListener} camera device used to notify callbacks.
+     * @param handler The handler camera device used to post callbacks.
+     */
+    protected void startCapture(CaptureRequest request, boolean repeating,
+            CaptureListener listener, Handler handler) throws Exception {
+        if (VERBOSE) Log.v(TAG, "Starting capture from device");
+
+        if (repeating) {
+            mCameraSession.setRepeatingRequest(request, listener, handler);
+        } else {
+            mCameraSession.capture(request, listener, handler);
+        }
+    }
+
+    /**
+     * Stop the current active capture.
+     *
+     * @param fast When it is true, {@link CameraDevice#flush} is called, the stop capture
+     * could be faster.
+     */
+    protected void stopCapture(boolean fast) throws Exception {
+        if (VERBOSE) Log.v(TAG, "Stopping capture");
+
+        if (fast) {
+            /**
+             * Flush is useful for canceling long exposure single capture, it also could help
+             * to make the streaming capture stop sooner.
+             */
+            mCameraSession.abortCaptures();
+            mCameraSessionListener.getStateWaiter().
+                    waitForState(BlockingSessionListener.SESSION_READY, CAMERA_IDLE_TIMEOUT_MS);
+        } else {
+            mCameraSession.close();
+        }
+    }
+
+    /**
+     * Open a {@link #CameraDevice camera device} and get the StaticMetadata for a given camera id.
+     * The default mCameraListener is used to wait for states.
+     *
+     * @param cameraId The id of the camera device to be opened.
+     */
+    protected void openDevice(String cameraId) throws Exception {
+        openDevice(cameraId, mCameraListener);
+    }
+
+    /**
+     * Open a {@link #CameraDevice} and get the StaticMetadata for a given camera id and listener.
+     *
+     * @param cameraId The id of the camera device to be opened.
+     * @param listener The {@link #BlockingStateListener} used to wait for states.
+     */
+    protected void openDevice(String cameraId, BlockingStateListener listener) throws Exception {
+        mCamera = CameraTestUtils.openCamera(
+                mCameraManager, cameraId, listener, mHandler);
+        mCollector.setCameraId(cameraId);
+        mStaticInfo = new StaticMetadata(mCameraManager.getCameraCharacteristics(cameraId),
+                CheckLevel.ASSERT, /*collector*/null);
+        mOrderedPreviewSizes = getSupportedPreviewSizes(
+                cameraId, mCameraManager, PREVIEW_SIZE_BOUND);
+        mOrderedVideoSizes = getSupportedVideoSizes(cameraId, mCameraManager, PREVIEW_SIZE_BOUND);
+        mOrderedStillSizes = getSupportedStillSizes(cameraId, mCameraManager, null);
+
+        if (VERBOSE) {
+            Log.v(TAG, "Camera " + cameraId + " is opened");
+        }
+    }
+
+    /**
+     * Create a {@link #CameraCaptureSession} using the currently open camera.
+     *
+     * @param outputSurfaces The set of output surfaces to configure for this session
+     */
+    protected void createSession(List<Surface> outputSurfaces) throws Exception {
+        mCameraSessionListener = new BlockingSessionListener();
+        mCameraSession = CameraTestUtils.configureCameraSession(mCamera, outputSurfaces,
+                mCameraSessionListener, mHandler);
+    }
+
+    /**
+     * Close a {@link #CameraDevice camera device} and clear the associated StaticInfo field for a
+     * given camera id. The default mCameraListener is used to wait for states.
+     * <p>
+     * This function must be used along with the {@link #openDevice} for the
+     * same camera id.
+     * </p>
+     *
+     * @param cameraId The id of the {@link #CameraDevice camera device} to be closed.
+     */
+    protected void closeDevice(String cameraId) {
+        closeDevice(cameraId, mCameraListener);
+    }
+
+    /**
+     * Close a {@link #CameraDevice camera device} and clear the associated StaticInfo field for a
+     * given camera id and listener.
+     * <p>
+     * This function must be used along with the {@link #openDevice} for the
+     * same camera id.
+     * </p>
+     *
+     * @param cameraId The id of the camera device to be closed.
+     * @param listener The BlockingStateListener used to wait for states.
+     */
+    protected void closeDevice(String cameraId, BlockingStateListener listener) {
+        if (mCamera != null) {
+            if (!cameraId.equals(mCamera.getId())) {
+                throw new IllegalStateException("Try to close a device that is not opened yet");
+            }
+            mCamera.close();
+            listener.waitForState(STATE_CLOSED, CAMERA_CLOSE_TIMEOUT_MS);
+            mCamera = null;
+            mCameraSession = null;
+            mCameraSessionListener = null;
+            mStaticInfo = null;
+            mOrderedPreviewSizes = null;
+            mOrderedVideoSizes = null;
+            mOrderedStillSizes = null;
+
+            if (VERBOSE) {
+                Log.v(TAG, "Camera " + cameraId + " is closed");
+            }
+        }
+    }
+
+    /**
+     * Create an {@link ImageReader} object and get the surface.
+     * <p>
+     * This function creates {@link ImageReader} object and surface, then assign
+     * to the default {@link mReader} and {@link mReaderSurface}. It closes the
+     * current default active {@link ImageReader} if it exists.
+     * </p>
+     *
+     * @param size The size of this ImageReader to be created.
+     * @param format The format of this ImageReader to be created
+     * @param maxNumImages The max number of images that can be acquired
+     *            simultaneously.
+     * @param listener The listener used by this ImageReader to notify
+     *            callbacks.
+     */
+    protected void createDefaultImageReader(Size size, int format, int maxNumImages,
+            ImageReader.OnImageAvailableListener listener) throws Exception {
+        closeDefaultImageReader();
+
+        mReader = createImageReader(size, format, maxNumImages, listener);
+        mReaderSurface = mReader.getSurface();
+        if (VERBOSE) Log.v(TAG, "Created ImageReader size " + size.toString());
+    }
+
+    /**
+     * Create an {@link ImageReader} object.
+     *
+     * <p>This function creates image reader object for given format, maxImages, and size.</p>
+     *
+     * @param size The size of this ImageReader to be created.
+     * @param format The format of this ImageReader to be created
+     * @param maxNumImages The max number of images that can be acquired simultaneously.
+     * @param listener The listener used by this ImageReader to notify callbacks.
+     */
+
+    protected ImageReader createImageReader(Size size, int format, int maxNumImages,
+            ImageReader.OnImageAvailableListener listener) throws Exception {
+
+        ImageReader reader = ImageReader.newInstance(size.getWidth(), size.getHeight(),
+                format, maxNumImages);
+        reader.setOnImageAvailableListener(listener, mHandler);
+        if (VERBOSE) Log.v(TAG, "Created ImageReader size " + size.toString());
+        return reader;
+    }
+
+    /**
+     * Close the pending images then close current default {@link ImageReader} object.
+     */
+    protected void closeDefaultImageReader() {
+        closeImageReader(mReader);
+        mReader = null;
+        mReaderSurface = null;
+    }
+
+    /**
+     * Close an image reader instance.
+     *
+     * @param reader
+     */
+    protected void closeImageReader(ImageReader reader) {
+        if (reader != null) {
+            try {
+                // Close all possible pending images first.
+                Image image = reader.acquireLatestImage();
+                if (image != null) {
+                    image.close();
+                }
+            } finally {
+                reader.close();
+                reader = null;
+            }
+        }
+    }
+}
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/testcases/Camera2MultiViewTestCase.java b/tests/tests/hardware/src/android/hardware/camera2/cts/testcases/Camera2MultiViewTestCase.java
new file mode 100644
index 0000000..d91efbb
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/testcases/Camera2MultiViewTestCase.java
@@ -0,0 +1,325 @@
+/*
+ * 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.testcases;
+
+import static android.hardware.camera2.cts.CameraTestUtils.*;
+import static com.android.ex.camera2.blocking.BlockingSessionListener.*;
+import static com.android.ex.camera2.blocking.BlockingStateListener.*;
+
+import android.content.Context;
+import android.content.res.Configuration;
+import android.graphics.Matrix;
+import android.graphics.RectF;
+import android.graphics.SurfaceTexture;
+import android.hardware.camera2.CameraCaptureSession;
+import android.hardware.camera2.CameraCaptureSession.CaptureListener;
+import android.hardware.camera2.CameraDevice;
+import android.hardware.camera2.CameraManager;
+import android.hardware.camera2.CaptureRequest;
+import android.hardware.camera2.cts.Camera2MultiViewStubActivity;
+import android.hardware.camera2.cts.helpers.StaticMetadata;
+import android.hardware.camera2.cts.helpers.StaticMetadata.CheckLevel;
+import android.os.ConditionVariable;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.os.SystemClock;
+import android.test.ActivityInstrumentationTestCase2;
+import android.util.Log;
+import android.util.Size;
+import android.view.Surface;
+import android.view.TextureView;
+
+import com.android.ex.camera2.blocking.BlockingCameraManager;
+import com.android.ex.camera2.blocking.BlockingSessionListener;
+import com.android.ex.camera2.blocking.BlockingStateListener;
+
+import java.util.List;
+
+/**
+ * Camera2 test case base class by using mixed SurfaceView and TextureView as rendering target.
+ */
+public class Camera2MultiViewTestCase extends
+        ActivityInstrumentationTestCase2<Camera2MultiViewStubActivity> {
+    private static final String TAG = "MultiViewTestCase";
+    private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
+
+
+    private static final long SHORT_SLEEP_WAIT_TIME_MS = 100;
+    // Default timeouts for reaching various camera states
+    private static final int CAMERA_CLOSE_TIMEOUT_MS = 2000;
+    private static final int CAMERA_IDLE_TIMEOUT_MS = 2000;
+    private static final int CAMERA_BUSY_TIMEOUT_MS = 500;
+
+    protected TextureView[] mTextureView = new TextureView[2];
+    protected Context mContext;
+    protected CameraManager mCameraManager;
+    protected String[] mCameraIds;
+    protected HandlerThread mHandlerThread;
+    protected Handler mHandler;
+    protected BlockingStateListener mCameraListener;
+
+    // Per device fields:
+    protected BlockingSessionListener mSessionListener;
+    protected CameraCaptureSession mSession;
+    protected CameraDevice mCamera;
+    protected StaticMetadata mStaticInfo;
+    protected List<Size> mOrderedPreviewSizes;
+
+    public Camera2MultiViewTestCase() {
+        super(Camera2MultiViewStubActivity.class);
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mContext = getActivity();
+        assertNotNull("Unable to get activity", mContext);
+        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();
+        Camera2MultiViewStubActivity activity = (Camera2MultiViewStubActivity)mContext;
+        mTextureView[0] = activity.getTextureView(0);
+        mTextureView[1] = activity.getTextureView(1);
+        assertNotNull("Unable to get texture view", mTextureView);
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        mHandlerThread.quitSafely();
+        mHandler = null;
+        mCameraListener = null;
+        if (mCamera != null) {
+            mCamera.close();
+            mCamera = null;
+        }
+        super.tearDown();
+    }
+
+    /**
+     * Update preview TextureView rotation to accommodate discrepancy between preview
+     * buffer and the view window orientation.
+     *
+     * Assumptions:
+     * - Aspect ratio for the sensor buffers is in landscape orientation,
+     * - Dimensions of buffers received are rotated to the natural device orientation.
+     * - The contents of each buffer are rotated by the inverse of the display rotation.
+     * - Surface scales the buffer to fit the current view bounds.
+     * TODO: Make this method works for all orientations
+     *
+     */
+    protected void updatePreviewDisplayRotation(Size previewSize, TextureView textureView) {
+        int rotationDegrees = 0;
+        Camera2MultiViewStubActivity activity = (Camera2MultiViewStubActivity) mContext;
+        int displayRotation = activity.getWindowManager().getDefaultDisplay().getRotation();
+        Configuration config = activity.getResources().getConfiguration();
+
+        // Get UI display rotation
+        switch (displayRotation) {
+            case Surface.ROTATION_0:
+                rotationDegrees = 0;
+                break;
+            case Surface.ROTATION_90:
+                rotationDegrees = 90;
+            break;
+            case Surface.ROTATION_180:
+                rotationDegrees = 180;
+            break;
+            case Surface.ROTATION_270:
+                rotationDegrees = 270;
+            break;
+        }
+
+        // Get device natural orientation
+        int deviceOrientation = Configuration.ORIENTATION_PORTRAIT;
+        if ((rotationDegrees % 180 == 0 &&
+                config.orientation == Configuration.ORIENTATION_LANDSCAPE) ||
+                ((rotationDegrees % 180 != 0 &&
+                config.orientation == Configuration.ORIENTATION_PORTRAIT))) {
+            deviceOrientation = Configuration.ORIENTATION_LANDSCAPE;
+        }
+
+        // Rotate the buffer dimensions if device orientation is portrait.
+        int effectiveWidth = previewSize.getWidth();
+        int effectiveHeight = previewSize.getHeight();
+        if (deviceOrientation == Configuration.ORIENTATION_PORTRAIT) {
+            effectiveWidth = previewSize.getHeight();
+            effectiveHeight = previewSize.getWidth();
+        }
+
+        // Find and center view rect and buffer rect
+        Matrix transformMatrix =  textureView.getTransform(null);
+        int viewWidth = textureView.getWidth();
+        int viewHeight = textureView.getHeight();
+        RectF viewRect = new RectF(0, 0, viewWidth, viewHeight);
+        RectF bufRect = new RectF(0, 0, effectiveWidth, effectiveHeight);
+        float centerX = viewRect.centerX();
+        float centerY = viewRect.centerY();
+        bufRect.offset(centerX - bufRect.centerX(), centerY - bufRect.centerY());
+
+        // Undo ScaleToFit.FILL done by the surface
+        transformMatrix.setRectToRect(viewRect, bufRect, Matrix.ScaleToFit.FILL);
+
+        // Rotate buffer contents to proper orientation
+        transformMatrix.postRotate((360 - rotationDegrees) % 360, centerX, centerY);
+        if ((rotationDegrees % 180) == 90) {
+            int temp = effectiveWidth;
+            effectiveWidth = effectiveHeight;
+            effectiveHeight = temp;
+        }
+
+        // Scale to fit view, cropping the longest dimension
+        float scale =
+                Math.max(viewWidth / (float) effectiveWidth, viewHeight / (float) effectiveHeight);
+        transformMatrix.postScale(scale, scale, centerX, centerY);
+
+        Handler handler = new Handler(Looper.getMainLooper());
+        class TransformUpdater implements Runnable {
+            TextureView mView;
+            Matrix mTransformMatrix;
+            TransformUpdater(TextureView view, Matrix matrix) {
+                mView = view;
+                mTransformMatrix = matrix;
+            }
+
+            @Override
+            public void run() {
+                mView.setTransform(mTransformMatrix);
+            }
+        }
+        handler.post(new TransformUpdater(textureView, transformMatrix));
+    }
+
+    protected void openCamera(String cameraId) throws Exception {
+        assertNull("Camera is already opened", mCamera);
+        mCamera = (new BlockingCameraManager(mCameraManager)).openCamera(
+                cameraId, mCameraListener, mHandler);
+        mStaticInfo = new StaticMetadata(mCameraManager.getCameraCharacteristics(cameraId),
+                CheckLevel.ASSERT, /*collector*/null);
+        mOrderedPreviewSizes = getSupportedPreviewSizes(cameraId, mCameraManager, PREVIEW_SIZE_BOUND);
+        assertNotNull(String.format("Failed to open camera device ID: %s", cameraId), mCamera);
+    }
+
+    protected void closeCamera() throws Exception {
+        assertNotNull("Camera is already closed!", mCamera);
+        mCamera.close();
+        mCameraListener.waitForState(STATE_CLOSED, CAMERA_CLOSE_TIMEOUT_MS);
+        mCamera = null;
+        mSession = null;
+        mStaticInfo = null;
+        mOrderedPreviewSizes = null;
+    }
+
+    protected void startPreview(List<Surface> outputSurfaces, CaptureListener listener)
+            throws Exception {
+        mSessionListener = new BlockingSessionListener();
+        mSession = configureCameraSession(mCamera, outputSurfaces, mSessionListener, mHandler);
+
+        // TODO: vary the different settings like crop region to cover more cases.
+        CaptureRequest.Builder captureBuilder =
+                mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
+
+        for (Surface surface : outputSurfaces) {
+            captureBuilder.addTarget(surface);
+        }
+        mSession.setRepeatingRequest(captureBuilder.build(), listener, mHandler);
+    }
+
+    protected 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
+        mSession.close();
+        mSessionListener.getStateWaiter().waitForState(SESSION_CLOSED, SESSION_CLOSE_TIMEOUT_MS);
+        mSessionListener = null;
+    }
+
+    /**
+     * Wait until the SurfaceTexture available from the TextureView, then return it.
+     * Return null if the wait times out.
+     *
+     * @param timeOutMs The timeout value for the wait
+     * @return The available SurfaceTexture, return null if the wait times out.
+     */
+    protected SurfaceTexture getAvailableSurfaceTexture(long timeOutMs, TextureView view) {
+        long waitTime = timeOutMs;
+
+        while (!view.isAvailable() && waitTime > 0) {
+            long startTimeMs = SystemClock.elapsedRealtime();
+            SystemClock.sleep(SHORT_SLEEP_WAIT_TIME_MS);
+            waitTime -= (SystemClock.elapsedRealtime() - startTimeMs);
+        }
+
+        if (view.isAvailable()) {
+            return view.getSurfaceTexture();
+        } else {
+            Log.w(TAG, "Wait for SurfaceTexture available timed out after " + timeOutMs + "ms");
+            return null;
+        }
+    }
+
+    public static class CameraPreviewListener implements TextureView.SurfaceTextureListener {
+        private boolean mFirstPreviewAvailable = false;
+        private final ConditionVariable mPreviewDone = new ConditionVariable();
+
+        @Override
+        public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
+            // Ignored. The SurfaceTexture is polled by getAvailableSurfaceTexture.
+        }
+
+        @Override
+        public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
+            // Ignored. The CameraDevice should already know the changed size.
+        }
+
+        @Override
+        public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
+            /**
+             * Return true, assume that client detaches the surface before it is
+             * destroyed. For example, CameraDevice should detach this surface when
+             * stopping preview. No need to release the SurfaceTexture here as it
+             * is released by TextureView after onSurfaceTextureDestroyed is called.
+             */
+            return true;
+        }
+
+        @Override
+        public void onSurfaceTextureUpdated(SurfaceTexture surface) {
+            // Invoked every time there's a new Camera preview frame
+            if (!mFirstPreviewAvailable) {
+                mFirstPreviewAvailable = true;
+                mPreviewDone.open();
+            }
+        }
+
+        /** Waits until the camera preview is up running */
+        public boolean waitForPreviewDone(long timeOutMs) {
+            if (!mPreviewDone.block(timeOutMs)) {
+                // timeout could be expected or unexpected. The caller will decide.
+                Log.w(TAG, "waitForPreviewDone timed out after " + timeOutMs + "ms");
+                return false;
+            }
+            mPreviewDone.close();
+            return true;
+        }
+    }
+}
+
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/testcases/Camera2SurfaceViewTestCase.java b/tests/tests/hardware/src/android/hardware/camera2/cts/testcases/Camera2SurfaceViewTestCase.java
new file mode 100644
index 0000000..3fa7b58
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/testcases/Camera2SurfaceViewTestCase.java
@@ -0,0 +1,687 @@
+/*
+ * 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.testcases;
+
+import static android.hardware.camera2.cts.CameraTestUtils.*;
+import static com.android.ex.camera2.blocking.BlockingStateListener.STATE_CLOSED;
+
+import android.hardware.camera2.params.StreamConfigurationMap;
+import android.media.ImageReader;
+import android.os.Environment;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.test.ActivityInstrumentationTestCase2;
+import android.util.Log;
+import android.view.Surface;
+import android.view.SurfaceHolder;
+import android.content.Context;
+import android.graphics.ImageFormat;
+import android.hardware.camera2.CameraAccessException;
+import android.hardware.camera2.CameraCaptureSession;
+import android.hardware.camera2.CameraCaptureSession.CaptureListener;
+import android.hardware.camera2.CameraDevice;
+import android.hardware.camera2.CameraManager;
+import android.hardware.camera2.CameraMetadata;
+import android.hardware.camera2.CaptureRequest;
+import android.hardware.camera2.CaptureResult;
+import android.util.Size;
+import android.util.Range;
+import android.hardware.camera2.cts.Camera2SurfaceViewStubActivity;
+import android.hardware.camera2.cts.CameraTestUtils;
+import android.hardware.camera2.cts.CameraTestUtils.SimpleCaptureListener;
+import android.hardware.camera2.cts.helpers.CameraErrorCollector;
+import android.hardware.camera2.cts.helpers.StaticMetadata;
+import android.hardware.camera2.cts.helpers.StaticMetadata.CheckLevel;
+
+import com.android.ex.camera2.blocking.BlockingSessionListener;
+import com.android.ex.camera2.blocking.BlockingStateListener;
+import com.android.ex.camera2.exceptions.TimeoutRuntimeException;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+
+/**
+ * Camera2 Preview test case base class by using SurfaceView as rendering target.
+ *
+ * <p>This class encapsulates the SurfaceView based preview common functionalities.
+ * The setup and teardown of CameraManager, test HandlerThread, Activity, Camera IDs
+ * and CameraStateListener are handled in this class. Some basic preview related utility
+ * functions are provided to facilitate the derived preview-based test classes.
+ * </p>
+ */
+
+public class Camera2SurfaceViewTestCase extends
+        ActivityInstrumentationTestCase2<Camera2SurfaceViewStubActivity> {
+    private static final String TAG = "SurfaceViewTestCase";
+    private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
+    private static final int WAIT_FOR_SURFACE_CHANGE_TIMEOUT_MS = 1000;
+
+    // TODO: Use internal storage for this to make sure the file is only visible to test.
+    protected static final String DEBUG_FILE_NAME_BASE =
+            Environment.getExternalStorageDirectory().getPath();
+    protected static final int WAIT_FOR_RESULT_TIMEOUT_MS = 3000;
+    protected static final float FRAME_DURATION_ERROR_MARGIN = 0.005f; // 0.5 percent error margin.
+    protected static final int NUM_RESULTS_WAIT_TIMEOUT = 100;
+    protected static final int NUM_FRAMES_WAITED_FOR_UNKNOWN_LATENCY = 8;
+
+    protected Context mContext;
+    protected CameraManager mCameraManager;
+    protected String[] mCameraIds;
+    protected HandlerThread mHandlerThread;
+    protected Handler mHandler;
+    protected BlockingStateListener mCameraListener;
+    protected BlockingSessionListener mSessionListener;
+    protected CameraErrorCollector mCollector;
+    // Per device fields:
+    protected StaticMetadata mStaticInfo;
+    protected CameraDevice mCamera;
+    protected CameraCaptureSession mSession;
+    protected ImageReader mReader;
+    protected Surface mReaderSurface;
+    protected Surface mPreviewSurface;
+    protected Size mPreviewSize;
+    protected List<Size> mOrderedPreviewSizes; // In descending order.
+    protected List<Size> mOrderedVideoSizes; // In descending order.
+    protected List<Size> mOrderedStillSizes; // In descending order.
+    protected HashMap<Size, Long> mMinPreviewFrameDurationMap;
+
+
+    public Camera2SurfaceViewTestCase() {
+        super(Camera2SurfaceViewStubActivity.class);
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        /**
+         * Set up the camera preview required environments, including activity,
+         * CameraManager, HandlerThread, Camera IDs, and CameraStateListener.
+         */
+        super.setUp();
+        mContext = getActivity();
+        /**
+         * 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());
+        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();
+        mCollector = new CameraErrorCollector();
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        // Teardown the camera preview required environments.
+        mHandlerThread.quitSafely();
+        mHandler = null;
+        mCameraListener = null;
+
+        try {
+            mCollector.verify();
+        } catch (Throwable e) {
+            // When new Exception(e) is used, exception info will be printed twice.
+            throw new Exception(e.getMessage());
+        } finally {
+            super.tearDown();
+        }
+    }
+
+    /**
+     * Start camera preview by using the given request, preview size and capture
+     * listener.
+     * <p>
+     * If preview is already started, calling this function will stop the
+     * current preview stream and start a new preview stream with given
+     * parameters. No need to call stopPreview between two startPreview calls.
+     * </p>
+     *
+     * @param request The request builder used to start the preview.
+     * @param previewSz The size of the camera device output preview stream.
+     * @param listener The callbacks the camera device will notify when preview
+     *            capture is available.
+     */
+    protected void startPreview(CaptureRequest.Builder request, Size previewSz,
+            CaptureListener listener) throws Exception {
+        // Update preview size.
+        updatePreviewSurface(previewSz);
+        if (VERBOSE) {
+            Log.v(TAG, "start preview with size " + mPreviewSize.toString());
+        }
+
+        configurePreviewOutput(request);
+
+        mSession.setRepeatingRequest(request.build(), listener, mHandler);
+    }
+
+    /**
+     * Configure the preview output stream.
+     *
+     * @param request The request to be configured with preview surface
+     */
+    protected void configurePreviewOutput(CaptureRequest.Builder request)
+            throws CameraAccessException {
+        List<Surface> outputSurfaces = new ArrayList<Surface>(/*capacity*/1);
+        outputSurfaces.add(mPreviewSurface);
+        mSessionListener = new BlockingSessionListener();
+        mSession = configureCameraSession(mCamera, outputSurfaces, mSessionListener, mHandler);
+
+        request.addTarget(mPreviewSurface);
+    }
+
+    /**
+     * Create a {@link CaptureRequest#Builder} and add the default preview surface.
+     *
+     * @return The {@link CaptureRequest#Builder} to be created
+     * @throws CameraAccessException When create capture request from camera fails
+     */
+    protected CaptureRequest.Builder createRequestForPreview() throws CameraAccessException {
+        if (mPreviewSurface == null) {
+            throw new IllegalStateException(
+                    "Preview surface is not set yet, call updatePreviewSurface or startPreview"
+                    + "first to set the preview surface properly.");
+        }
+        CaptureRequest.Builder requestBuilder =
+                mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
+        requestBuilder.addTarget(mPreviewSurface);
+        return requestBuilder;
+    }
+
+    /**
+     * Stop preview for current camera device.
+     */
+    protected 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
+        mSession.close();
+    }
+
+    /**
+     * Setup still (JPEG) capture configuration and start preview.
+     * <p>
+     * The default max number of image is set to image reader.
+     * </p>
+     *
+     * @param previewRequest The capture request to be used for preview
+     * @param stillRequest The capture request to be used for still capture
+     * @param previewSz Preview size
+     * @param stillSz The still capture size
+     * @param resultListener Capture result listener
+     * @param imageListener The still capture image listener
+     */
+    protected void prepareStillCaptureAndStartPreview(CaptureRequest.Builder previewRequest,
+            CaptureRequest.Builder stillRequest, Size previewSz, Size stillSz,
+            CaptureListener resultListener,
+            ImageReader.OnImageAvailableListener imageListener) throws Exception {
+        prepareCaptureAndStartPreview(previewRequest, stillRequest, previewSz, stillSz,
+                ImageFormat.JPEG, resultListener, MAX_READER_IMAGES, imageListener);
+    }
+
+    /**
+     * Setup still (JPEG) capture configuration and start preview.
+     *
+     * @param previewRequest The capture request to be used for preview
+     * @param stillRequest The capture request to be used for still capture
+     * @param previewSz Preview size
+     * @param stillSz The still capture size
+     * @param resultListener Capture result listener
+     * @param maxNumImages The max number of images set to the image reader
+     * @param imageListener The still capture image listener
+     */
+    protected void prepareStillCaptureAndStartPreview(CaptureRequest.Builder previewRequest,
+            CaptureRequest.Builder stillRequest, Size previewSz, Size stillSz,
+            CaptureListener resultListener, int maxNumImages,
+            ImageReader.OnImageAvailableListener imageListener) throws Exception {
+        prepareCaptureAndStartPreview(previewRequest, stillRequest, previewSz, stillSz,
+                ImageFormat.JPEG, resultListener, maxNumImages, imageListener);
+    }
+
+    /**
+     * Setup raw capture configuration and start preview.
+     *
+     * <p>
+     * The default max number of image is set to image reader.
+     * </p>
+     *
+     * @param previewRequest The capture request to be used for preview
+     * @param rawRequest The capture request to be used for raw capture
+     * @param previewSz Preview size
+     * @param rawSz The raw capture size
+     * @param resultListener Capture result listener
+     * @param imageListener The raw capture image listener
+     */
+    protected void prepareRawCaptureAndStartPreview(CaptureRequest.Builder previewRequest,
+            CaptureRequest.Builder rawRequest, Size previewSz, Size rawSz,
+            CaptureListener resultListener,
+            ImageReader.OnImageAvailableListener imageListener) throws Exception {
+        prepareCaptureAndStartPreview(previewRequest, rawRequest, previewSz, rawSz,
+                ImageFormat.RAW_SENSOR, resultListener, MAX_READER_IMAGES, imageListener);
+    }
+
+    /**
+     * Wait for expected result key value available in a certain number of results.
+     *
+     * <p>
+     * Check the result immediately if numFramesWait is 0.
+     * </p>
+     *
+     * @param listener The capture listener to get capture result
+     * @param resultKey The capture result key associated with the result value
+     * @param expectedValue The result value need to be waited for
+     * @param numResultsWait Number of frame to wait before times out
+     * @throws TimeoutRuntimeException If more than numResultsWait results are
+     * seen before the result matching myRequest arrives, or each individual wait
+     * for result times out after {@value #WAIT_FOR_RESULT_TIMEOUT_MS}ms.
+     */
+    protected static <T> void waitForResultValue(SimpleCaptureListener listener,
+            CaptureResult.Key<T> resultKey,
+            T expectedValue, int numResultsWait) {
+        List<T> expectedValues = new ArrayList<T>();
+        expectedValues.add(expectedValue);
+        waitForAnyResultValue(listener, resultKey, expectedValues, numResultsWait);
+    }
+
+    /**
+     * Wait for any expected result key values available in a certain number of results.
+     *
+     * <p>
+     * Check the result immediately if numFramesWait is 0.
+     * </p>
+     *
+     * @param listener The capture listener to get capture result.
+     * @param resultKey The capture result key associated with the result value.
+     * @param expectedValues The list of result value need to be waited for,
+     * return immediately if the list is empty.
+     * @param numResultsWait Number of frame to wait before times out.
+     * @throws TimeoutRuntimeException If more than numResultsWait results are.
+     * seen before the result matching myRequest arrives, or each individual wait
+     * for result times out after {@value #WAIT_FOR_RESULT_TIMEOUT_MS}ms.
+     */
+    protected static <T> void waitForAnyResultValue(SimpleCaptureListener listener,
+            CaptureResult.Key<T> resultKey,
+            List<T> expectedValues, int numResultsWait) {
+        if (numResultsWait < 0 || listener == null || expectedValues == null) {
+            throw new IllegalArgumentException(
+                    "Input must be non-negative number and listener/expectedValues "
+                    + "must be non-null");
+        }
+
+        int i = 0;
+        CaptureResult result;
+        do {
+            result = listener.getCaptureResult(WAIT_FOR_RESULT_TIMEOUT_MS);
+            T value = result.get(resultKey);
+            for ( T expectedValue : expectedValues) {
+                if (VERBOSE) {
+                    Log.v(TAG, "Current result value for key " + resultKey.getName() + " is: "
+                            + value.toString());
+                }
+                if (value.equals(expectedValue)) {
+                    return;
+                }
+            }
+        } while (i++ < numResultsWait);
+
+        throw new TimeoutRuntimeException(
+                "Unable to get the expected result value " + expectedValues + " for key " +
+                        resultKey.getName() + " after waiting for " + numResultsWait + " results");
+    }
+
+    /**
+     * Submit a capture once, then submit additional captures in order to ensure that
+     * the camera will be synchronized.
+     *
+     * <p>
+     * The additional capture count is determined by android.sync.maxLatency (or
+     * a fixed {@value #NUM_FRAMES_WAITED_FOR_UNKNOWN_LATENCY}) captures if maxLatency is unknown).
+     * </p>
+     *
+     * <p>Returns the number of captures that were submitted (at least 1), which is useful
+     * with {@link #waitForNumResults}.</p>
+     *
+     * @param request capture request to forward to {@link CameraDevice#capture}
+     * @param listener request listener to forward to {@link CameraDevice#capture}
+     * @param handler handler to forward to {@link CameraDevice#capture}
+     *
+     * @return the number of captures that were submitted
+     *
+     * @throws CameraAccessException if capturing failed
+     */
+    protected int captureRequestsSynchronized(
+            CaptureRequest request, CaptureListener listener, Handler handler)
+                    throws CameraAccessException {
+        return captureRequestsSynchronized(request, /*count*/1, listener, handler);
+    }
+
+    /**
+     * Submit a capture {@code count} times, then submit additional captures in order to ensure that
+     * the camera will be synchronized.
+     *
+     * <p>
+     * The additional capture count is determined by android.sync.maxLatency (or
+     * a fixed {@value #NUM_FRAMES_WAITED_FOR_UNKNOWN_LATENCY}) captures if maxLatency is unknown).
+     * </p>
+     *
+     * <p>Returns the number of captures that were submitted (at least 1), which is useful
+     * with {@link #waitForNumResults}.</p>
+     *
+     * @param request capture request to forward to {@link CameraDevice#capture}
+     * @param count the number of times to submit the request (minimally), must be at least 1
+     * @param listener request listener to forward to {@link CameraDevice#capture}
+     * @param handler handler to forward to {@link CameraDevice#capture}
+     *
+     * @return the number of captures that were submitted
+     *
+     * @throws IllegalArgumentException if {@code count} was not at least 1
+     * @throws CameraAccessException if capturing failed
+     */
+    protected int captureRequestsSynchronized(
+            CaptureRequest request, int count, CaptureListener listener, Handler handler)
+                    throws CameraAccessException {
+        if (count < 1) {
+            throw new IllegalArgumentException("count must be positive");
+        }
+
+        int maxLatency = mStaticInfo.getSyncMaxLatency();
+        if (maxLatency == CameraMetadata.SYNC_MAX_LATENCY_UNKNOWN) {
+            maxLatency = NUM_FRAMES_WAITED_FOR_UNKNOWN_LATENCY;
+        }
+
+        assertTrue("maxLatency is non-negative", maxLatency >= 0);
+
+        int numCaptures = maxLatency + count;
+
+        for (int i = 0; i < numCaptures; ++i) {
+            mSession.capture(request, listener, handler);
+        }
+
+        return numCaptures;
+    }
+
+    /**
+     * Wait for numResultWait frames
+     *
+     * @param resultListener The capture listener to get capture result back.
+     * @param numResultsWait Number of frame to wait
+     *
+     * @return the last result, or {@code null} if there was none
+     */
+    protected static CaptureResult waitForNumResults(SimpleCaptureListener resultListener,
+            int numResultsWait) {
+        if (numResultsWait < 0 || resultListener == null) {
+            throw new IllegalArgumentException(
+                    "Input must be positive number and listener must be non-null");
+        }
+
+        CaptureResult result = null;
+        for (int i = 0; i < numResultsWait; i++) {
+            result = resultListener.getCaptureResult(WAIT_FOR_RESULT_TIMEOUT_MS);
+        }
+
+        return result;
+    }
+
+    /**
+     * Wait for enough results for settings to be applied
+     *
+     * @param resultListener The capture listener to get capture result back.
+     * @param numResultWaitForUnknownLatency Number of frame to wait if camera device latency is
+     *                                       unknown.
+     */
+    protected void waitForSettingsApplied(SimpleCaptureListener resultListener,
+            int numResultWaitForUnknownLatency) {
+        int maxLatency = mStaticInfo.getSyncMaxLatency();
+        if (maxLatency == CameraMetadata.SYNC_MAX_LATENCY_UNKNOWN) {
+            maxLatency = numResultWaitForUnknownLatency;
+        }
+        // Wait for settings to take effect
+        waitForNumResults(resultListener, maxLatency);
+    }
+
+
+    /**
+     * Wait for AE to be stabilized before capture: CONVERGED or FLASH_REQUIRED.
+     *
+     * <p>Waits for {@code android.sync.maxLatency} number of results first, to make sure
+     * that the result is synchronized (or {@code numResultWaitForUnknownLatency} if the latency
+     * is unknown.</p>
+     *
+     * <p>This is a no-op for {@code LEGACY} devices since they don't report
+     * the {@code aeState} result.</p>
+     *
+     * @param resultListener The capture listener to get capture result back.
+     * @param numResultWaitForUnknownLatency Number of frame to wait if camera device latency is
+     *                                       unknown.
+     */
+    protected void waitForAeStable(SimpleCaptureListener resultListener,
+            int numResultWaitForUnknownLatency) {
+        waitForSettingsApplied(resultListener, numResultWaitForUnknownLatency);
+
+        if (!mStaticInfo.isHardwareLevelLimitedOrBetter()) {
+            // No-op for metadata
+            return;
+        }
+        List<Integer> expectedAeStates = new ArrayList<Integer>();
+        expectedAeStates.add(new Integer(CaptureResult.CONTROL_AE_STATE_CONVERGED));
+        expectedAeStates.add(new Integer(CaptureResult.CONTROL_AE_STATE_FLASH_REQUIRED));
+        waitForAnyResultValue(resultListener, CaptureResult.CONTROL_AE_STATE, expectedAeStates,
+                NUM_RESULTS_WAIT_TIMEOUT);
+    }
+
+    /**
+     * Wait for AE to be: LOCKED
+     *
+     * <p>Waits for {@code android.sync.maxLatency} number of results first, to make sure
+     * that the result is synchronized (or {@code numResultWaitForUnknownLatency} if the latency
+     * is unknown.</p>
+     *
+     * <p>This is a no-op for {@code LEGACY} devices since they don't report
+     * the {@code aeState} result.</p>
+     *
+     * @param resultListener The capture listener to get capture result back.
+     * @param numResultWaitForUnknownLatency Number of frame to wait if camera device latency is
+     *                                       unknown.
+     */
+    protected void waitForAeLocked(SimpleCaptureListener resultListener,
+            int numResultWaitForUnknownLatency) {
+
+        waitForSettingsApplied(resultListener, numResultWaitForUnknownLatency);
+
+        if (!mStaticInfo.isHardwareLevelLimitedOrBetter()) {
+            // No-op for legacy devices
+            return;
+        }
+
+        List<Integer> expectedAeStates = new ArrayList<Integer>();
+        expectedAeStates.add(new Integer(CaptureResult.CONTROL_AE_STATE_LOCKED));
+        waitForAnyResultValue(resultListener, CaptureResult.CONTROL_AE_STATE, expectedAeStates,
+                NUM_RESULTS_WAIT_TIMEOUT);
+    }
+
+    /**
+     * Create an {@link ImageReader} object and get the surface.
+     *
+     * @param size The size of this ImageReader to be created.
+     * @param format The format of this ImageReader to be created
+     * @param maxNumImages The max number of images that can be acquired simultaneously.
+     * @param listener The listener used by this ImageReader to notify callbacks.
+     */
+    protected void createImageReader(Size size, int format, int maxNumImages,
+            ImageReader.OnImageAvailableListener listener) throws Exception {
+        closeImageReader();
+
+        ImageReader r = makeImageReader(size, format, maxNumImages, listener,
+                mHandler);
+        mReader = r;
+        mReaderSurface = r.getSurface();
+    }
+
+    /**
+     * Close the pending images then close current active {@link ImageReader} object.
+     */
+    protected void closeImageReader() {
+        CameraTestUtils.closeImageReader(mReader);
+        mReader = null;
+        mReaderSurface = null;
+    }
+
+    /**
+     * Open a camera device and get the StaticMetadata for a given camera id.
+     *
+     * @param cameraId The id of the camera device to be opened.
+     */
+    protected void openDevice(String cameraId) throws Exception {
+        mCamera = CameraTestUtils.openCamera(
+                mCameraManager, cameraId, mCameraListener, mHandler);
+        mCollector.setCameraId(cameraId);
+        mStaticInfo = new StaticMetadata(mCameraManager.getCameraCharacteristics(cameraId),
+                CheckLevel.ASSERT, /*collector*/null);
+        mOrderedPreviewSizes = getSupportedPreviewSizes(cameraId, mCameraManager, PREVIEW_SIZE_BOUND);
+        mOrderedVideoSizes = getSupportedVideoSizes(cameraId, mCameraManager, PREVIEW_SIZE_BOUND);
+        mOrderedStillSizes = getSupportedStillSizes(cameraId, mCameraManager, null);
+        // Use ImageFormat.YUV_420_888 for now. TODO: need figure out what's format for preview
+        // in public API side.
+        mMinPreviewFrameDurationMap =
+                mStaticInfo.getAvailableMinFrameDurationsForFormatChecked(ImageFormat.YUV_420_888);
+    }
+
+    /**
+     * Close the current actively used camera device.
+     */
+    protected void closeDevice() {
+        if (mCamera != null) {
+            mCamera.close();
+            mCameraListener.waitForState(STATE_CLOSED, CAMERA_CLOSE_TIMEOUT_MS);
+            mCamera = null;
+            mSession = null;
+            mSessionListener = null;
+            mStaticInfo = null;
+            mOrderedPreviewSizes = null;
+            mOrderedVideoSizes = null;
+            mOrderedStillSizes = null;
+        }
+    }
+
+    protected void updatePreviewSurface(Size size) {
+        if (size.equals(mPreviewSize) && mPreviewSurface != null) {
+            Log.w(TAG, "Skipping update preview surface size...");
+            return;
+        }
+
+        mPreviewSize = size;
+        Camera2SurfaceViewStubActivity stubActivity = getActivity();
+        final SurfaceHolder holder = stubActivity.getSurfaceView().getHolder();
+        Handler handler = new Handler(Looper.getMainLooper());
+        handler.post(new Runnable() {
+            @Override
+            public void run() {
+                holder.setFixedSize(mPreviewSize.getWidth(), mPreviewSize.getHeight());
+            }
+        });
+
+        boolean res = stubActivity.waitForSurfaceSizeChanged(
+                WAIT_FOR_SURFACE_CHANGE_TIMEOUT_MS, mPreviewSize.getWidth(),
+                mPreviewSize.getHeight());
+        assertTrue("wait for surface change to " + mPreviewSize.toString() + " timed out", res);
+        mPreviewSurface = holder.getSurface();
+        assertTrue("Preview surface is invalid", mPreviewSurface.isValid());
+        assertNotNull("Preview surface is null", mPreviewSurface);
+    }
+
+    /**
+     * Setup single capture configuration and start preview.
+     *
+     * @param previewRequest The capture request to be used for preview
+     * @param stillRequest The capture request to be used for still capture
+     * @param previewSz Preview size
+     * @param captureSz Still capture size
+     * @param format The single capture image format
+     * @param resultListener Capture result listener
+     * @param maxNumImages The max number of images set to the image reader
+     * @param imageListener The single capture capture image listener
+     */
+    private void prepareCaptureAndStartPreview(CaptureRequest.Builder previewRequest,
+            CaptureRequest.Builder stillRequest, Size previewSz, Size captureSz, int format,
+            CaptureListener resultListener, int maxNumImages,
+            ImageReader.OnImageAvailableListener imageListener) throws Exception {
+        if (VERBOSE) {
+            Log.v(TAG, String.format("Prepare single capture (%s) and preview (%s)",
+                    captureSz.toString(), previewSz.toString()));
+        }
+
+        // Update preview size.
+        updatePreviewSurface(previewSz);
+
+        // Create ImageReader.
+        createImageReader(captureSz, format, maxNumImages, imageListener);
+
+        // Configure output streams with preview and jpeg streams.
+        List<Surface> outputSurfaces = new ArrayList<Surface>();
+        outputSurfaces.add(mPreviewSurface);
+        outputSurfaces.add(mReaderSurface);
+        mSessionListener = new BlockingSessionListener();
+        mSession = configureCameraSession(mCamera, outputSurfaces, mSessionListener, mHandler);
+
+        // Configure the requests.
+        previewRequest.addTarget(mPreviewSurface);
+        stillRequest.addTarget(mPreviewSurface);
+        stillRequest.addTarget(mReaderSurface);
+
+        // Start preview.
+        mSession.setRepeatingRequest(previewRequest.build(), resultListener, mHandler);
+    }
+
+    /**
+     * Get the max preview size that supports the given fpsRange.
+     *
+     * @param fpsRange The fps range the returned size must support.
+     * @return max size that support the given fps range.
+     */
+    protected Size getMaxPreviewSizeForFpsRange(Range<Integer> fpsRange) {
+        if (fpsRange == null || fpsRange.getLower() <= 0 || fpsRange.getUpper() <= 0) {
+            throw new IllegalArgumentException("Invalid fps range argument");
+        }
+        if (mOrderedPreviewSizes == null || mMinPreviewFrameDurationMap == null) {
+            throw new IllegalStateException("mOrderedPreviewSizes and mMinPreviewFrameDurationMap"
+                    + " must be initialized");
+        }
+
+        long[] frameDurationRange =
+                new long[]{(long) (1e9 / fpsRange.getUpper()), (long) (1e9 / fpsRange.getLower())};
+        for (Size size : mOrderedPreviewSizes) {
+            Long minDuration = mMinPreviewFrameDurationMap.get(size);
+            if (minDuration == null ||
+                    minDuration == 0) {
+                throw new IllegalArgumentException(
+                        "No min frame duration available for the selected format.");
+            }
+            if (minDuration <= frameDurationRange[0]) {
+                return size;
+            }
+        }
+
+        return null;
+    }
+}
diff --git a/tests/tests/hardware/src/android/hardware/cts/CameraTest.java b/tests/tests/hardware/src/android/hardware/cts/CameraTest.java
index c6e28f7..50a3573 100644
--- a/tests/tests/hardware/src/android/hardware/cts/CameraTest.java
+++ b/tests/tests/hardware/src/android/hardware/cts/CameraTest.java
@@ -33,6 +33,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;
@@ -47,12 +48,14 @@
 import java.io.File;
 import java.io.FileOutputStream;
 import java.io.IOException;
+import java.text.ParsePosition;
 import java.text.SimpleDateFormat;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Date;
 import java.util.Iterator;
 import java.util.List;
+import java.util.TimeZone;
 
 import junit.framework.AssertionFailedError;
 
@@ -63,7 +66,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;
@@ -91,6 +94,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();
@@ -108,7 +118,7 @@
 
     public CameraTest() {
         super(PACKAGE, CameraStubActivity.class);
-        if (LOGV) Log.v(TAG, "Camera Constructor");
+        if (VERBOSE) Log.v(TAG, "Camera Constructor");
     }
 
     @Override
@@ -151,7 +161,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();
 
@@ -187,7 +197,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);
         }
@@ -204,7 +214,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);
             }
 
@@ -238,9 +248,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");
         }
     }
 
@@ -248,7 +258,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");
         }
     }
 
@@ -256,7 +266,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");
         }
     }
 
@@ -273,14 +283,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.");
@@ -313,7 +323,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");
@@ -340,7 +350,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);
@@ -385,7 +406,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 " +
@@ -810,6 +831,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
@@ -835,8 +871,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.
@@ -849,6 +887,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");
@@ -856,6 +895,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.
@@ -865,11 +905,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);
@@ -906,7 +1099,26 @@
         assertEquals((float)latitude, latLong[0], 0.0001f);
         assertEquals((float)longitude, latLong[1], 0.0001f);
         assertEquals(altitude, exif.getAltitude(-1), 1);
-        assertEquals(timestamp, exif.getGpsDateTime() / 1000);
+        assertEquals(timestamp, getGpsDateTimeFromJpeg(exif) / 1000);
+    }
+
+    private long getGpsDateTimeFromJpeg(ExifInterface exif) {
+        String date = exif.getAttribute(ExifInterface.TAG_GPS_DATESTAMP);
+        String time = exif.getAttribute(ExifInterface.TAG_GPS_TIMESTAMP);
+        if (date == null || time == null) return -1;
+
+        String dateTimeString = date + ' ' + time;
+        ParsePosition pos = new ParsePosition(0);
+        try {
+            SimpleDateFormat formatter = new SimpleDateFormat("yyyy:MM:dd HH:mm:ss");
+            formatter.setTimeZone(TimeZone.getTimeZone("UTC"));
+
+            Date datetime = formatter.parse(dateTimeString, pos);
+            if (datetime == null) return -1;
+            return datetime.getTime();
+        } catch (IllegalArgumentException ex) {
+            return -1;
+        }
     }
 
     private void checkGpsDataNull(ExifInterface exif) {
@@ -1153,8 +1365,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++) {
@@ -1317,8 +1528,8 @@
 
     private void testFocusDistancesByCamera(int cameraId) throws Exception {
         initializeMessageLooper(cameraId);
-        mCamera.startPreview();
-        waitForPreviewDone();
+        blockingStartPreview();
+
         Parameters parameters = mCamera.getParameters();
 
         // Test every supported focus mode.
@@ -1786,7 +1997,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=" +
@@ -1891,8 +2102,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);
@@ -2065,7 +2275,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");
@@ -2087,11 +2297,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++) {
@@ -2099,7 +2309,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.
@@ -2111,11 +2321,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.
@@ -2125,11 +2335,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();
@@ -2145,7 +2355,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();
         }
     }
@@ -2786,6 +2996,7 @@
     }
 
     private static final int[] mCamcorderProfileList = {
+        CamcorderProfile.QUALITY_2160P,
         CamcorderProfile.QUALITY_1080P,
         CamcorderProfile.QUALITY_480P,
         CamcorderProfile.QUALITY_720P,
diff --git a/tests/tests/hardware/src/android/hardware/cts/SensorTest.java b/tests/tests/hardware/src/android/hardware/cts/SensorTest.java
index b1691c3..a1487b7 100644
--- a/tests/tests/hardware/src/android/hardware/cts/SensorTest.java
+++ b/tests/tests/hardware/src/android/hardware/cts/SensorTest.java
@@ -35,6 +35,7 @@
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.os.PowerManager;
+import android.os.SystemClock;
 import android.test.AndroidTestCase;
 import android.util.Log;
 
@@ -46,7 +47,7 @@
     private static final String TAG = "SensorTest";
     // Test only SDK defined sensors. Any sensors with type > 100 are ignored.
     private static final int MAX_SENSOR_TYPE = 100;
-    private static final int TIMEOUT_S = 40;
+    private static final int TIMEOUT = 40;
 
     private PowerManager.WakeLock mWakeLock;
 
@@ -186,6 +187,89 @@
         }
     }
 
+    class SensorEventTimeStampListener implements SensorEventListener {
+        SensorEventTimeStampListener(long samplingPeriod, CountDownLatch latch) {
+            mSamplingPeriodNs = samplingPeriod;
+            mPrevTimeStampNs = -1;
+            mLatch = latch;
+            numErrors = 0;
+        }
+
+        @Override
+        public void onSensorChanged(SensorEvent event) {
+            if (mPrevTimeStampNs == -1) {
+                mPrevTimeStampNs = event.timestamp;
+                return;
+            }
+            long currTimeStampNs = event.timestamp;
+            if (currTimeStampNs <= mPrevTimeStampNs) {
+                Log.w(TAG, "Timestamps not monotonically increasing curr_ts=" +
+                        event.timestamp + " prev_ts=" + mPrevTimeStampNs);
+                numErrors++;
+                return;
+            }
+            mLatch.countDown();
+            if ((double)(currTimeStampNs - mPrevTimeStampNs - mSamplingPeriodNs)/mSamplingPeriodNs
+                    > INTERVAL_ERROR) {
+                Log.w(TAG, "Timestamp Interval error curr=" + currTimeStampNs + " prev_ts="
+                        + mPrevTimeStampNs + " rateNs=" + mSamplingPeriodNs +
+                        " diffNs=" + (currTimeStampNs - mPrevTimeStampNs));
+                numErrors++;
+            }
+
+            final long elapsedRealtimeNs = SystemClock.elapsedRealtimeNanos();
+            if (elapsedRealtimeNs - currTimeStampNs > SYNC_TOLERANCE) {
+                Log.w(TAG, "Timestamp sync error elapsedRealTimeNs=" + elapsedRealtimeNs +
+                        " curr_ts=" + currTimeStampNs +
+                        " diff=" + (elapsedRealtimeNs - currTimeStampNs)/1000 + "Us");
+                numErrors++;
+            }
+            mPrevTimeStampNs = currTimeStampNs;
+        }
+
+        public int getNumErrors() {
+            return numErrors;
+        }
+
+        @Override
+        public void onAccuracyChanged(Sensor sensor, int accuracy) {
+        }
+
+        private int numErrors;
+        private long mSamplingPeriodNs;
+        private long mPrevTimeStampNs;
+        private final double INTERVAL_ERROR = 0.01; // 1% tolerance for interval_error.
+        private final CountDownLatch mLatch;
+        private final long SYNC_TOLERANCE = 3000000000L; // 3 seconds approx.
+    }
+
+    public void testSensorTimeStamps() throws Exception {
+        final CountDownLatch eventReceived = new CountDownLatch(10000);
+
+        Sensor sensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
+        if (sensor == null) {
+            return;
+        }
+        // SENSOR_DELAY_GAME in nanoseconds
+        final long samplingPeriodNs = 20000000;
+        SensorEventTimeStampListener listener = new SensorEventTimeStampListener(samplingPeriodNs,
+                eventReceived);
+
+        boolean result = mSensorManager.registerListener(listener, sensor,
+                SensorManager.SENSOR_DELAY_GAME);
+        assertTrue("Sensor registerListener failed ", result);
+
+        // Wait for 300 seconds.
+        boolean countZero = eventReceived.await(300, TimeUnit.SECONDS);
+        if (!countZero) {
+            fail("Timed out waiting for events from " + sensor.getName());
+        }
+        if (listener.getNumErrors() > 5) {
+            fail("Timestamp test failed. " + listener.getNumErrors());
+        }
+        mSensorManager.unregisterListener(listener);
+    }
+
     // Register for updates from each continuous mode sensor, wait for 25 events, call flush and
     // wait for flushCompleteEvent before unregistering for the sensor.
     public void testBatchAndFlush() throws Exception {
@@ -244,7 +328,7 @@
         };
         // Consider only continuous mode sensors for testing registerListener.
         // For on-change sensors, call registerListener() so that the listener is associated
-        // with the sensor and flush(listener) can be called on it.
+        // with the sensor so that flush(listener) can be called on it.
         if (sensor.getMinDelay() >= 0) {
             Log.i(TAG, "testBatch " + sensor.getName());
             boolean result = mSensorManager.registerListener(listener, sensor,
@@ -252,17 +336,16 @@
             assertTrue("registerListener failed " + sensor.getName(), result);
             // Wait for 25 events or 40 seconds only for continuous mode sensors.
             if (sensor.getMinDelay() > 0) {
-                boolean countZero = eventReceived.await(TIMEOUT_S, TimeUnit.SECONDS);
+                boolean countZero = eventReceived.await(TIMEOUT, TimeUnit.SECONDS);
                 if (!countZero) {
                     fail("Timed out waiting for events from " + sensor.getName());
                 }
             }
         }
-
         Log.i(TAG, "testFlush " + sensor.getName());
         boolean result = mSensorManager.flush(listener);
         assertTrue("flush failed " + sensor.getName(), result);
-        boolean countZero = flushReceived.await(TIMEOUT_S, TimeUnit.SECONDS);
+        boolean countZero = flushReceived.await(TIMEOUT, TimeUnit.SECONDS);
         if (!countZero) {
             fail("Timed out waiting for flushCompleteEvent from " + sensor.getName());
         }
@@ -318,13 +401,13 @@
 
             Log.i(TAG, "testBatchAndFlushWithMutipleSensors " + registeredSensors);
             // Wait for numSensors * 50 events or 40 seconds.
-            boolean countZero = eventReceived.await(TIMEOUT_S, TimeUnit.SECONDS);
+            boolean countZero = eventReceived.await(TIMEOUT, TimeUnit.SECONDS);
             if (!countZero) {
                 fail("Timed out waiting for events from " + registeredSensors.toString());
             }
             boolean result = mSensorManager.flush(listener);
             assertTrue("flush failed " + registeredSensors.toString(), result);
-            countZero = flushReceived.await(TIMEOUT_S, TimeUnit.SECONDS);
+            countZero = flushReceived.await(TIMEOUT, TimeUnit.SECONDS);
             if (!countZero) {
                 fail("Timed out waiting for flushCompleteEvent from " +
                       registeredSensors.toString());
diff --git a/tests/tests/hardware/src/android/hardware/cts/SingleSensorTests.java b/tests/tests/hardware/src/android/hardware/cts/SingleSensorTests.java
index cd6adb1..d33a108 100644
--- a/tests/tests/hardware/src/android/hardware/cts/SingleSensorTests.java
+++ b/tests/tests/hardware/src/android/hardware/cts/SingleSensorTests.java
@@ -89,6 +89,7 @@
     private static final int BATCHING_OFF = 0;
     private static final int BATCHING_5S = 5000000;
 
+    private static final int RATE_200HZ = 5000;
     private static final int RATE_100HZ = 10000;
     private static final int RATE_50HZ = 20000;
     private static final int RATE_25HZ = 40000;
@@ -144,6 +145,10 @@
         runSensorTest(Sensor.TYPE_ACCELEROMETER, RATE_100HZ, BATCHING_OFF);
     }
 
+    public void testAccelerometer_200hz() throws Throwable {
+        runSensorTest(Sensor.TYPE_ACCELEROMETER, RATE_200HZ, BATCHING_OFF);
+    }
+
     public void testAccelerometer_50hz() throws Throwable {
         runSensorTest(Sensor.TYPE_ACCELEROMETER, RATE_50HZ, BATCHING_OFF);
     }
@@ -180,6 +185,10 @@
         runSensorTest(Sensor.TYPE_MAGNETIC_FIELD, SensorManager.SENSOR_DELAY_FASTEST, BATCHING_OFF);
     }
 
+    public void testMagneticField_200hz() throws Throwable {
+        runSensorTest(Sensor.TYPE_MAGNETIC_FIELD, RATE_200HZ, BATCHING_OFF);
+    }
+
     public void testMagneticField_100hz() throws Throwable {
         runSensorTest(Sensor.TYPE_MAGNETIC_FIELD, RATE_100HZ, BATCHING_OFF);
     }
@@ -222,6 +231,10 @@
     }
 
     @SuppressWarnings("deprecation")
+    public void testOrientation_200hz() throws Throwable {
+        runSensorTest(Sensor.TYPE_ORIENTATION, RATE_200HZ, BATCHING_OFF);
+    }
+    @SuppressWarnings("deprecation")
     public void testOrientation_100hz() throws Throwable {
         runSensorTest(Sensor.TYPE_ORIENTATION, RATE_100HZ, BATCHING_OFF);
     }
@@ -270,6 +283,10 @@
         runSensorTest(Sensor.TYPE_GYROSCOPE, SensorManager.SENSOR_DELAY_FASTEST, BATCHING_OFF);
     }
 
+    public void testGyroscope_200hz() throws Throwable {
+        runSensorTest(Sensor.TYPE_GYROSCOPE, RATE_200HZ, BATCHING_OFF);
+    }
+
     public void testGyroscope_100hz() throws Throwable {
         runSensorTest(Sensor.TYPE_GYROSCOPE, RATE_100HZ, BATCHING_OFF);
     }
@@ -310,6 +327,10 @@
         runSensorTest(Sensor.TYPE_PRESSURE, SensorManager.SENSOR_DELAY_FASTEST, BATCHING_OFF);
     }
 
+    public void testPressure_200hz() throws Throwable {
+        runSensorTest(Sensor.TYPE_PRESSURE, RATE_200HZ, BATCHING_OFF);
+    }
+
     public void testPressure_100hz() throws Throwable {
         runSensorTest(Sensor.TYPE_PRESSURE, RATE_100HZ, BATCHING_OFF);
     }
@@ -350,6 +371,10 @@
         runSensorTest(Sensor.TYPE_GRAVITY, SensorManager.SENSOR_DELAY_FASTEST, BATCHING_OFF);
     }
 
+    public void testGravity_200hz() throws Throwable {
+        runSensorTest(Sensor.TYPE_GRAVITY, RATE_200HZ, BATCHING_OFF);
+    }
+
     public void testGravity_100hz() throws Throwable {
         runSensorTest(Sensor.TYPE_GRAVITY, RATE_100HZ, BATCHING_OFF);
     }
@@ -390,6 +415,11 @@
         runSensorTest(Sensor.TYPE_ROTATION_VECTOR, SensorManager.SENSOR_DELAY_FASTEST,
                 BATCHING_OFF);
     }
+
+    public void testRotationVector_200hz() throws Throwable {
+        runSensorTest(Sensor.TYPE_ROTATION_VECTOR, RATE_200HZ, BATCHING_OFF);
+    }
+
     public void testRotationVector_100hz() throws Throwable {
         runSensorTest(Sensor.TYPE_ROTATION_VECTOR, RATE_100HZ, BATCHING_OFF);
     }
@@ -431,6 +461,10 @@
                 BATCHING_OFF);
     }
 
+    public void testMagneticFieldUncalibrated_200hz() throws Throwable {
+        runSensorTest(Sensor.TYPE_MAGNETIC_FIELD_UNCALIBRATED, RATE_200HZ, BATCHING_OFF);
+    }
+
     public void testMagneticFieldUncalibrated_100hz() throws Throwable {
         runSensorTest(Sensor.TYPE_MAGNETIC_FIELD_UNCALIBRATED, RATE_100HZ, BATCHING_OFF);
     }
@@ -473,6 +507,10 @@
                 BATCHING_OFF);
     }
 
+    public void testGameRotationVector_200hz() throws Throwable {
+        runSensorTest(Sensor.TYPE_GAME_ROTATION_VECTOR, RATE_200HZ, BATCHING_OFF);
+    }
+
     public void testGameRotationVector_100hz() throws Throwable {
         runSensorTest(Sensor.TYPE_GAME_ROTATION_VECTOR, RATE_100HZ, BATCHING_OFF);
     }
@@ -515,6 +553,10 @@
                 BATCHING_OFF);
     }
 
+    public void testGyroscopeUncalibrated_200hz() throws Throwable {
+        runSensorTest(Sensor.TYPE_GYROSCOPE_UNCALIBRATED, RATE_200HZ, BATCHING_OFF);
+    }
+
     public void testGyroscopeUncalibrated_100hz() throws Throwable {
         runSensorTest(Sensor.TYPE_GYROSCOPE_UNCALIBRATED, RATE_100HZ, BATCHING_OFF);
     }
@@ -557,41 +599,45 @@
                 BATCHING_OFF);
     }
 
-    public void  testGeomagneticRotationVector_100hz() throws Throwable {
-        runSensorTest(Sensor.TYPE_GEOMAGNETIC_ROTATION_VECTOR, RATE_100HZ, BATCHING_OFF);
+    public void  testLinearAcceleration_200hz() throws Throwable {
+        runSensorTest(Sensor.TYPE_LINEAR_ACCELERATION, RATE_200HZ, BATCHING_OFF);
     }
 
-    public void testGeomagneticRotationVector_50hz() throws Throwable {
-        runSensorTest(Sensor.TYPE_GEOMAGNETIC_ROTATION_VECTOR, RATE_50HZ, BATCHING_OFF);
+    public void  testLinearAcceleration_100hz() throws Throwable {
+        runSensorTest(Sensor.TYPE_LINEAR_ACCELERATION, RATE_100HZ, BATCHING_OFF);
     }
 
-    public void testGeomagneticRotationVector_25hz() throws Throwable {
-        runSensorTest(Sensor.TYPE_GEOMAGNETIC_ROTATION_VECTOR, RATE_25HZ, BATCHING_OFF);
+    public void testLinearAcceleration_50hz() throws Throwable {
+        runSensorTest(Sensor.TYPE_LINEAR_ACCELERATION, RATE_50HZ, BATCHING_OFF);
     }
 
-    public void testGeomagneticRotationVector_15hz() throws Throwable {
-        runSensorTest(Sensor.TYPE_GEOMAGNETIC_ROTATION_VECTOR, RATE_15HZ, BATCHING_OFF);
+    public void testLinearAcceleration_25hz() throws Throwable {
+        runSensorTest(Sensor.TYPE_LINEAR_ACCELERATION, RATE_25HZ, BATCHING_OFF);
     }
 
-    public void testGeomagneticRotationVector_10hz() throws Throwable {
-        runSensorTest(Sensor.TYPE_GEOMAGNETIC_ROTATION_VECTOR, RATE_10HZ, BATCHING_OFF);
+    public void testLinearAcceleration_15hz() throws Throwable {
+        runSensorTest(Sensor.TYPE_LINEAR_ACCELERATION, RATE_15HZ, BATCHING_OFF);
     }
 
-    public void testGeomagneticRotationVector_5hz() throws Throwable {
-        runSensorTest(Sensor.TYPE_GEOMAGNETIC_ROTATION_VECTOR, RATE_5HZ, BATCHING_OFF);
+    public void testLinearAcceleration_10hz() throws Throwable {
+        runSensorTest(Sensor.TYPE_LINEAR_ACCELERATION, RATE_10HZ, BATCHING_OFF);
     }
 
-    public void testGeomagneticRotationVector_1hz() throws Throwable {
-        runSensorTest(Sensor.TYPE_GEOMAGNETIC_ROTATION_VECTOR, RATE_1HZ, BATCHING_OFF);
+    public void testLinearAcceleration_5hz() throws Throwable {
+        runSensorTest(Sensor.TYPE_LINEAR_ACCELERATION, RATE_5HZ, BATCHING_OFF);
     }
 
-    public void testGeomagneticRotationVector_fastest_batching() throws Throwable {
-        runSensorTest(Sensor.TYPE_GEOMAGNETIC_ROTATION_VECTOR, SensorManager.SENSOR_DELAY_FASTEST,
+    public void testLinearAcceleration_1hz() throws Throwable {
+        runSensorTest(Sensor.TYPE_LINEAR_ACCELERATION, RATE_1HZ, BATCHING_OFF);
+    }
+
+    public void testLinearAcceleration_fastest_batching() throws Throwable {
+        runSensorTest(Sensor.TYPE_LINEAR_ACCELERATION, SensorManager.SENSOR_DELAY_FASTEST,
                 BATCHING_5S);
     }
 
-    public void testGeomagneticRotationVector_50hz_batching() throws Throwable {
-        runSensorTest(Sensor.TYPE_GEOMAGNETIC_ROTATION_VECTOR, RATE_50HZ, BATCHING_5S);
+    public void testLinearAcceleration_50hz_batching() throws Throwable {
+        runSensorTest(Sensor.TYPE_LINEAR_ACCELERATION, RATE_50HZ, BATCHING_5S);
     }
 
     private void runSensorTest(int sensorType, int rateUs, int maxBatchReportLatencyUs)
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/FrequencyVerification.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/FrequencyVerification.java
index 4815688..b6704b6 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/FrequencyVerification.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/FrequencyVerification.java
@@ -22,6 +22,7 @@
 import android.hardware.cts.helpers.SensorTestInformation;
 import android.hardware.cts.helpers.SensorTestInformation.SensorReportingMode;
 import android.hardware.cts.helpers.TestSensorEvent;
+import android.util.Log;
 
 import junit.framework.Assert;
 
@@ -33,15 +34,17 @@
  */
 public class FrequencyVerification extends AbstractSensorVerification {
     public static final String PASSED_KEY = "frequency_passed";
+    private static final String LOG_TAG = FrequencyVerification.class.getSimpleName();
 
-    // lower threshold is (100 - 10)% expected
-    private static final int DEFAULT_LOWER_THRESHOLD = 10;
-    // upper threshold is (100 + 110)% expected
-    private static final int DEFAULT_UPPER_THRESHOLD = 110;
+    // Lowest acceptable frequency, as percentage of the requested one.
+    private static final int DEFAULT_LOWER_THRESHOLD = 90;
+    // Highest acceptable frequency, as percentage of the requested one.
+    private static final int DEFAULT_UPPER_THRESHOLD = 220;
 
-    private final double mExpected;
-    private final double mLowerThreshold;
-    private final double mUpperThreshold;
+    private final double mRequestedFrequencyHz;
+    private final double mLowerThresholdHz;
+    private final double mUpperThresholdHz;
+    private final String mSensorName;
 
     private long mMinTimestamp = 0;
     private long mMaxTimestamp = 0;
@@ -51,15 +54,15 @@
      * Construct a {@link FrequencyVerification}.
      *
      * @param expected the expected frequency in Hz.
-     * @param lowerTheshold the lower threshold in Hz. {@code expected - lower} should be the
-     * slowest acceptable frequency of the sensor.
-     * @param upperThreshold the upper threshold in Hz. {@code expected + upper} should be the
-     * fastest acceptable frequency of the sensor.
+     * @param lowerTheshold Lowest acceptable frequency Hz.
+     * @param upperThreshold Highest acceptable frequency Hz.
      */
-    public FrequencyVerification(double expected, double lowerTheshold, double upperThreshold) {
-        mExpected = expected;
-        mLowerThreshold = lowerTheshold;
-        mUpperThreshold = upperThreshold;
+    public FrequencyVerification(double expected, double lowerTheshold, double upperThreshold,
+        String sensorName) {
+        mRequestedFrequencyHz = expected;
+        mLowerThresholdHz = lowerTheshold;
+        mUpperThresholdHz = upperThreshold;
+        mSensorName = sensorName;
     }
 
     /**
@@ -75,13 +78,38 @@
             return null;
         }
 
-        // Expected frequency in Hz
-        double expected = SensorCtsHelper.getFrequency(SensorCtsHelper.getDelay(sensor, rateUs),
-                TimeUnit.MICROSECONDS);
-        // Expected frequency * threshold percentage
-        double lowerThreshold = expected * DEFAULT_LOWER_THRESHOLD / 100;
-        double upperThreshold = expected * DEFAULT_UPPER_THRESHOLD / 100;
-        return new FrequencyVerification(expected, lowerThreshold, upperThreshold);
+        Log.i(LOG_TAG, String.format("Preparing frequency test for \"%s\" for which "
+                + "minDelay=%dus and maxDelay=%dus",
+                sensor.getName(), sensor.getMinDelay(), sensor.getMaxDelay()));
+        double maxDelayUs = sensor.getMaxDelay();
+        if (maxDelayUs <= 0) {
+            // This sensor didn't report its maxDelay.
+            // It might be only capable of producing its max rate.
+            // Do as if it reported its minDelay as its maxDelay.
+            Log.w(LOG_TAG, "This sensor (" + sensor.getName() + ") didn't report its maxDelay."
+                    + "Please update it in the sensor HAL to avoid failures in coming "
+                    + "android releases.");
+            maxDelayUs = sensor.getMinDelay();
+        }
+
+        // Convert the rateUs parameter into a delay in microseconds and rate in Hz.
+        double delayUs = SensorCtsHelper.getDelay(sensor, rateUs);
+        double requestedFrequencyHz = SensorCtsHelper.getFrequency(
+            delayUs, TimeUnit.MICROSECONDS);
+
+        // When rateUs > maxDelay, the sensor can do as if we requested maxDelay.
+        double upperExpectedHz = SensorCtsHelper.getFrequency(
+                Math.min(delayUs, maxDelayUs), TimeUnit.MICROSECONDS);
+        // When rateUs < minDelay, the sensor can do as if we requested minDelay
+        double lowerExpectedHz = SensorCtsHelper.getFrequency(
+                Math.max(delayUs, sensor.getMinDelay()), TimeUnit.MICROSECONDS);
+
+        // Set the pass thresholds based on default multipliers.
+        double lowerThresholdHz = lowerExpectedHz * DEFAULT_LOWER_THRESHOLD / 100;
+        double upperThresholdHz = upperExpectedHz * DEFAULT_UPPER_THRESHOLD / 100;
+
+        return new FrequencyVerification(requestedFrequencyHz, lowerThresholdHz, upperThresholdHz,
+            sensor.getName());
     }
 
     /**
@@ -97,18 +125,22 @@
             return;
         }
 
-        double frequency = SensorCtsHelper.getFrequency(
+        double measuredFrequencyHz = SensorCtsHelper.getFrequency(
                 ((double) (mMaxTimestamp - mMinTimestamp)) / (mCount - 1), TimeUnit.NANOSECONDS);
-        boolean failed = (frequency <= mExpected - mLowerThreshold
-                || frequency >= mExpected + mUpperThreshold);
+        boolean failed = (measuredFrequencyHz <= mLowerThresholdHz
+                || measuredFrequencyHz >= mUpperThresholdHz);
 
-        stats.addValue(SensorStats.FREQUENCY_KEY, frequency);
+        stats.addValue(SensorStats.FREQUENCY_KEY, measuredFrequencyHz);
         stats.addValue(PASSED_KEY, !failed);
+        String resultString = String.format("Requested \"" + mSensorName + "\" at %.2fHz "
+                + "(expecting between %.2fHz and %.2fHz, measured %.2fHz)",
+                mRequestedFrequencyHz, mLowerThresholdHz, mUpperThresholdHz, measuredFrequencyHz);
 
         if (failed) {
-            Assert.fail(String.format("Frequency out of range: frequency=%.2fHz "
-                    + "(expected (%.2f-%.2fHz, %.2f+%.2fHz))", frequency, mExpected,
-                    mLowerThreshold, mExpected, mUpperThreshold));
+            Log.e(LOG_TAG, "Frequency test FAIL: " + resultString);
+            Assert.fail(String.format("Frequency out of range: " + resultString));
+        } else {
+            Log.i(LOG_TAG, "Frequency test pass: " + resultString);
         }
     }
 
@@ -117,7 +149,8 @@
      */
     @Override
     public FrequencyVerification clone() {
-        return new FrequencyVerification(mExpected, mLowerThreshold, mUpperThreshold);
+        return new FrequencyVerification(mRequestedFrequencyHz, mLowerThresholdHz,
+            mUpperThresholdHz, mSensorName);
     }
 
     /**
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/FrequencyVerificationTest.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/FrequencyVerificationTest.java
index cec09a5..82d93e5 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/FrequencyVerificationTest.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/FrequencyVerificationTest.java
@@ -33,22 +33,22 @@
         long[] timestamps = {0, 1000000, 2000000, 3000000, 4000000};  // 1000Hz
 
         SensorStats stats = new SensorStats();
-        ISensorVerification verification = getVerification(1000.0, 1.0, 1.0, timestamps);
+        ISensorVerification verification = getVerification(1000.0, 999.0, 1001.0, timestamps);
         verification.verify(stats);
         verifyStats(stats, true, 1000.0);
 
         stats = new SensorStats();
-        verification = getVerification(950.0, 100.0, 100.0, timestamps);
+        verification = getVerification(950.0, 850.0, 1050.0, timestamps);
         verification.verify(stats);
         verifyStats(stats, true, 1000.0);
 
         stats = new SensorStats();
-        verification = getVerification(1050.0, 100.0, 100.0, timestamps);
+        verification = getVerification(1050.0, 950.0, 1150.0, timestamps);
         verification.verify(stats);
         verifyStats(stats, true, 1000.0);
 
         stats = new SensorStats();
-        verification = getVerification(950.0, 100.0, 25.0, timestamps);
+        verification = getVerification(950.0, 850.0, 975.0, timestamps);
         try {
             verification.verify(stats);
             fail("Expected an AssertionError");
@@ -58,7 +58,7 @@
         verifyStats(stats, false, 1000.0);
 
         stats = new SensorStats();
-        verification = getVerification(1050.0, 25.0, 100.0, timestamps);
+        verification = getVerification(1050.0, 1025.0, 1150.0, timestamps);
         try {
             verification.verify(stats);
             fail("Expected an AssertionError");
@@ -71,7 +71,7 @@
     private ISensorVerification getVerification(double expected, double lowerThreshold,
             double upperThreshold, long ... timestamps) {
         ISensorVerification verification = new FrequencyVerification(expected, lowerThreshold,
-                upperThreshold);
+                upperThreshold, "Test sensor");
         for (long timestamp : timestamps) {
             verification.addSensorEvent(new TestSensorEvent(null, timestamp, 0, null));
         }
diff --git a/tests/tests/holo/Android.mk b/tests/tests/holo/Android.mk
index afc4e17..e21b7bd 100644
--- a/tests/tests/holo/Android.mk
+++ b/tests/tests/holo/Android.mk
@@ -20,8 +20,6 @@
 # and when built explicitly put it in the data partition
 LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
 
-LOCAL_JAVA_LIBRARIES := android.test.runner
-
 LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
diff --git a/tests/tests/holo/AndroidManifest.xml b/tests/tests/holo/AndroidManifest.xml
index 1c59630..34f8e72 100644
--- a/tests/tests/holo/AndroidManifest.xml
+++ b/tests/tests/holo/AndroidManifest.xml
@@ -59,8 +59,11 @@
 
     </application>
 
-    <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
             android:targetPackage="com.android.cts.holo"
-            android:label="CTS tests for the Holo theme" />
+            android:label="CTS tests for the Holo theme" >
+        <meta-data android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
 
 </manifest>
diff --git a/tests/tests/jni/Android.mk b/tests/tests/jni/Android.mk
index 4f44e15..8b3edd2 100644
--- a/tests/tests/jni/Android.mk
+++ b/tests/tests/jni/Android.mk
@@ -18,16 +18,12 @@
 
 LOCAL_PACKAGE_NAME := CtsJniTestCases
 
-
 # Don't include this package in any target.
 LOCAL_MODULE_TAGS := optional
 
 # When built, explicitly put it in the data partition.
 LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
 
-# All tests should include android.test.runner.
-LOCAL_JAVA_LIBRARIES := android.test.runner
-
 LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner
 
 LOCAL_JNI_SHARED_LIBRARIES := libjnitest
diff --git a/tests/tests/jni/AndroidManifest.xml b/tests/tests/jni/AndroidManifest.xml
index c3407d1..843b322 100644
--- a/tests/tests/jni/AndroidManifest.xml
+++ b/tests/tests/jni/AndroidManifest.xml
@@ -23,9 +23,12 @@
     </application>
 
     <!-- This is a self-instrumenting test package. -->
-    <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
                      android:targetPackage="com.android.cts.jni"
-                     android:label="CTS tests of calling native code via JNI"/>
+                     android:label="CTS tests of calling native code via JNI">
+        <meta-data android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
 
 </manifest>
 
diff --git a/tests/tests/keystore/Android.mk b/tests/tests/keystore/Android.mk
index f2dae38..0f2cd03 100644
--- a/tests/tests/keystore/Android.mk
+++ b/tests/tests/keystore/Android.mk
@@ -18,8 +18,6 @@
 
 LOCAL_MODULE_TAGS := optional
 
-LOCAL_JAVA_LIBRARIES := android.test.runner
-
 LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner core-tests-support
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
diff --git a/tests/tests/keystore/AndroidManifest.xml b/tests/tests/keystore/AndroidManifest.xml
index 0ce9f09..106a0dc 100644
--- a/tests/tests/keystore/AndroidManifest.xml
+++ b/tests/tests/keystore/AndroidManifest.xml
@@ -24,9 +24,12 @@
         <uses-library android:name="android.test.runner" />
     </application>
 
-    <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
                      android:targetPackage="com.android.cts.keystore"
-                     android:label="CTS tests of com.android.cts.keystore"/>
+                     android:label="CTS tests of com.android.cts.keystore">
+        <meta-data android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
 
 </manifest>
 
diff --git a/tests/tests/location/Android.mk b/tests/tests/location/Android.mk
index b76672c..2503fc7 100644
--- a/tests/tests/location/Android.mk
+++ b/tests/tests/location/Android.mk
@@ -21,8 +21,6 @@
 # and when built explicitly put it in the data partition
 LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
 
-LOCAL_JAVA_LIBRARIES := android.test.runner
-
 LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
@@ -31,7 +29,6 @@
 
 LOCAL_INSTRUMENTATION_FOR := CtsTestStubs
 
-# uncomment when dalvik.annotation.Test* are removed or part of SDK
-#LOCAL_SDK_VERSION := current
+LOCAL_SDK_VERSION := current
 
 include $(BUILD_CTS_PACKAGE)
diff --git a/tests/tests/location/AndroidManifest.xml b/tests/tests/location/AndroidManifest.xml
index 147f0ba..5016f49 100644
--- a/tests/tests/location/AndroidManifest.xml
+++ b/tests/tests/location/AndroidManifest.xml
@@ -27,8 +27,11 @@
     <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
     <uses-permission android:name="android.permission.ACCESS_LOCATION_EXTRA_COMMANDS"/>
 
-    <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
                      android:targetPackage="com.android.cts.location"
-                     android:label="CTS tests of android.location"/>
+                     android:label="CTS tests of android.location">
+        <meta-data android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
 </manifest>
 
diff --git a/tests/tests/location/src/android/location/cts/LocationManagerTest.java b/tests/tests/location/src/android/location/cts/LocationManagerTest.java
index 64b15dc..ab11b3c 100644
--- a/tests/tests/location/src/android/location/cts/LocationManagerTest.java
+++ b/tests/tests/location/src/android/location/cts/LocationManagerTest.java
@@ -81,7 +81,7 @@
         // remove test provider if left over from an aborted run
         LocationProvider lp = mManager.getProvider(TEST_MOCK_PROVIDER_NAME);
         if (lp != null) {
-            removeTestProvider(TEST_MOCK_PROVIDER_NAME);
+            mManager.removeTestProvider(TEST_MOCK_PROVIDER_NAME);
         }
 
         addTestProvider(TEST_MOCK_PROVIDER_NAME);
@@ -107,7 +107,7 @@
     protected void tearDown() throws Exception {
         LocationProvider provider = mManager.getProvider(TEST_MOCK_PROVIDER_NAME);
         if (provider != null) {
-            removeTestProvider(TEST_MOCK_PROVIDER_NAME);
+            mManager.removeTestProvider(TEST_MOCK_PROVIDER_NAME);
         }
         if (mPendingIntent != null) {
             mManager.removeProximityAlert(mPendingIntent);
@@ -138,12 +138,12 @@
             // expected
         }
 
-        removeTestProvider(TEST_MOCK_PROVIDER_NAME);
+        mManager.removeTestProvider(TEST_MOCK_PROVIDER_NAME);
         provider = mManager.getProvider(TEST_MOCK_PROVIDER_NAME);
         assertNull(provider);
 
         try {
-            removeTestProvider(UNKNOWN_PROVIDER_NAME);
+            mManager.removeTestProvider(UNKNOWN_PROVIDER_NAME);
             fail("Should throw IllegalArgumentException when no provider exists!");
         } catch (IllegalArgumentException e) {
             // expected
@@ -177,7 +177,7 @@
         assertEquals(oldSizeAllProviders, providers.size());
         assertTrue(hasTestProvider(providers));
 
-        removeTestProvider(TEST_MOCK_PROVIDER_NAME);
+        mManager.removeTestProvider(TEST_MOCK_PROVIDER_NAME);
         providers = mManager.getAllProviders();
         assertEquals(oldSizeAllProviders - 1, providers.size());
         assertFalse(hasTestProvider(providers));
@@ -277,8 +277,8 @@
 
         // Find out location manager's opinion on the matter, making sure we dont' get spurious
         // results from test versions of the two providers.
-        forceClearTestProvider(LocationManager.GPS_PROVIDER);
-        forceClearTestProvider(LocationManager.NETWORK_PROVIDER);
+        forceRemoveTestProvider(LocationManager.GPS_PROVIDER);
+        forceRemoveTestProvider(LocationManager.NETWORK_PROVIDER);
         boolean lmGps = mManager.isProviderEnabled(LocationManager.GPS_PROVIDER);
         boolean lmNlp = mManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER);
 
@@ -330,20 +330,12 @@
     }
 
     /**
-     * Clears the test provider. Works around b/11446702 by temporarily adding the test provider
-     * so we are allowed to clear it.
+     * Ensures the test provider is removed. {@link LocationManager#removeTestProvider(String)}
+     * throws an {@link java.lang.IllegalArgumentException} if there is no such test provider,
+     * so we have to add it before we clear it.
      */
-    private void forceClearTestProvider(String provider) {
+    private void forceRemoveTestProvider(String provider) {
         addTestProvider(provider);
-        mManager.clearTestProviderEnabled(provider);
-        removeTestProvider(provider);
-    }
-
-    /**
-     * Work around b/11446702 by clearing the test provider before removing it
-     */
-    private void removeTestProvider(String provider) {
-        mManager.clearTestProviderEnabled(provider);
         mManager.removeTestProvider(provider);
     }
 
diff --git a/tests/tests/location2/Android.mk b/tests/tests/location2/Android.mk
index e89204d..6daca72 100644
--- a/tests/tests/location2/Android.mk
+++ b/tests/tests/location2/Android.mk
@@ -21,8 +21,6 @@
 # and when built explicitly put it in the data partition
 LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
 
-LOCAL_JAVA_LIBRARIES := android.test.runner
-
 LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
@@ -31,7 +29,7 @@
 
 LOCAL_INSTRUMENTATION_FOR := CtsTestStubs
 
-# uncomment when dalvik.annotation.Test* are removed or part of SDK
-#LOCAL_SDK_VERSION := current
+# uncomment when Location.EXTRA_NO_GPS_LOCATION is removed
+#LOCAL_SDK_VERSION := curren
 
 include $(BUILD_CTS_PACKAGE)
diff --git a/tests/tests/location2/AndroidManifest.xml b/tests/tests/location2/AndroidManifest.xml
index 118278b..ad77b7e 100644
--- a/tests/tests/location2/AndroidManifest.xml
+++ b/tests/tests/location2/AndroidManifest.xml
@@ -27,8 +27,11 @@
     <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
     <uses-permission android:name="android.permission.ACCESS_LOCATION_EXTRA_COMMANDS"/>
 
-    <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
                      android:targetPackage="com.android.cts.location2"
-                     android:label="CTS tests of android.location"/>
+                     android:label="CTS tests of android.location">
+        <meta-data android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
 </manifest>
 
diff --git a/tests/tests/media/Android.mk b/tests/tests/media/Android.mk
index 4474b4a..936a35e 100644
--- a/tests/tests/media/Android.mk
+++ b/tests/tests/media/Android.mk
@@ -21,14 +21,18 @@
 # and when built explicitly put it in the data partition
 LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
 
-LOCAL_JAVA_LIBRARIES := android.test.runner
 LOCAL_STATIC_JAVA_LIBRARIES := ctsdeviceutil ctstestserver ctstestrunner
 
+LOCAL_JNI_SHARED_LIBRARIES := libctsmediacodec_jni
+
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
 LOCAL_PACKAGE_NAME := CtsMediaTestCases
 
-# uncomment when dalvik.annotation.Test* are removed or part of SDK
+# uncomment when b/13249737 is fixed
 #LOCAL_SDK_VERSION := current
+LOCAL_JAVA_LIBRARIES += android.test.runner
 
 include $(BUILD_CTS_PACKAGE)
+
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/tests/tests/media/AndroidManifest.xml b/tests/tests/media/AndroidManifest.xml
index 90024c4..d53b2c6 100644
--- a/tests/tests/media/AndroidManifest.xml
+++ b/tests/tests/media/AndroidManifest.xml
@@ -66,9 +66,12 @@
         </service>
     </application>
 
-    <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
                      android:targetPackage="com.android.cts.media"
-                     android:label="CTS tests of android.media"/>
+                     android:label="CTS tests of android.media">
+        <meta-data android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
 
 </manifest>
 
diff --git a/tests/tests/media/libmediandkjni/Android.mk b/tests/tests/media/libmediandkjni/Android.mk
new file mode 100644
index 0000000..2d2033f
--- /dev/null
+++ b/tests/tests/media/libmediandkjni/Android.mk
@@ -0,0 +1,31 @@
+# Copyright (C) 2012 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE    := libctsmediacodec_jni
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_SRC_FILES := native-media-jni.cpp
+
+LOCAL_C_INCLUDES := $(JNI_H_INCLUDE)
+
+LOCAL_C_INCLUDES += $(call include-path-for, mediandk)
+
+LOCAL_SHARED_LIBRARIES := libandroid libnativehelper liblog libmediandk
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/tests/tests/media/libmediandkjni/native-media-jni.cpp b/tests/tests/media/libmediandkjni/native-media-jni.cpp
new file mode 100644
index 0000000..51d2cf2
--- /dev/null
+++ b/tests/tests/media/libmediandkjni/native-media-jni.cpp
@@ -0,0 +1,662 @@
+/*
+ * 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.
+ */
+
+/* Original code copied from NDK Native-media sample code */
+
+#undef NDEBUG
+#include <assert.h>
+#include <jni.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <semaphore.h>
+
+#include <android/native_window_jni.h>
+
+// for __android_log_print(ANDROID_LOG_INFO, "YourApp", "formatted message");
+#include <android/log.h>
+#define TAG "NativeMedia"
+#define ALOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, TAG, __VA_ARGS__)
+#define ALOGE(...) __android_log_print(ANDROID_LOG_ERROR, TAG, __VA_ARGS__)
+#define ALOGI(...) __android_log_print(ANDROID_LOG_INFO, TAG, __VA_ARGS__)
+
+#include "ndk/NdkMediaExtractor.h"
+#include "ndk/NdkMediaCodec.h"
+#include "ndk/NdkMediaCrypto.h"
+#include "ndk/NdkMediaFormat.h"
+#include "ndk/NdkMediaMuxer.h"
+
+template <class T>
+class simplevector {
+    T *storage;
+    int capacity;
+    int numfilled;
+public:
+    simplevector() {
+        capacity = 16;
+        numfilled = 0;
+        storage = new T[capacity];
+    }
+    ~simplevector() {
+        delete[] storage;
+    }
+
+    void add(T item) {
+        if (numfilled == capacity) {
+            T *old = storage;
+            capacity *= 2;
+            storage = new T[capacity];
+            for (int i = 0; i < numfilled; i++) {
+                storage[i] = old[i];
+            }
+            delete[] old;
+        }
+        storage[numfilled] = item;
+        numfilled++;
+    }
+
+    int size() {
+        return numfilled;
+    }
+
+    T* data() {
+        return storage;
+    }
+};
+
+
+
+jobject testExtractor(AMediaExtractor *ex, JNIEnv *env) {
+
+    simplevector<int> sizes;
+    int numtracks = AMediaExtractor_getTrackCount(ex);
+    sizes.add(numtracks);
+    for (int i = 0; i < numtracks; i++) {
+        AMediaFormat *format = AMediaExtractor_getTrackFormat(ex, i);
+        const char *s = AMediaFormat_toString(format);
+        ALOGI("track %d format: %s", i, s);
+        const char *mime;
+        if (!AMediaFormat_getString(format, AMEDIAFORMAT_KEY_MIME, &mime)) {
+            ALOGE("no mime type");
+            return NULL;
+        } else if (!strncmp(mime, "audio/", 6)) {
+            sizes.add(0);
+            int32_t val32;
+            int64_t val64;
+            AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_SAMPLE_RATE, &val32);
+            sizes.add(val32);
+            AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_CHANNEL_COUNT, &val32);
+            sizes.add(val32);
+            AMediaFormat_getInt64(format, AMEDIAFORMAT_KEY_DURATION, &val64);
+            sizes.add(val64);
+        } else if (!strncmp(mime, "video/", 6)) {
+            sizes.add(1);
+            int32_t val32;
+            int64_t val64;
+            AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_WIDTH, &val32);
+            sizes.add(val32);
+            AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_HEIGHT, &val32);
+            sizes.add(val32);
+            AMediaFormat_getInt64(format, AMEDIAFORMAT_KEY_DURATION, &val64);
+            sizes.add(val64);
+        } else {
+            ALOGE("expected audio or video mime type, got %s", mime);
+        }
+        AMediaFormat_delete(format);
+        AMediaExtractor_selectTrack(ex, i);
+    }
+    int bufsize = 1024*1024;
+    uint8_t *buf = new uint8_t[bufsize];
+    while(true) {
+        int n = AMediaExtractor_readSampleData(ex, buf, bufsize);
+        if (n < 0) {
+            break;
+        }
+        sizes.add(n);
+        sizes.add(AMediaExtractor_getSampleTrackIndex(ex));
+        sizes.add(AMediaExtractor_getSampleFlags(ex));
+        sizes.add(AMediaExtractor_getSampleTime(ex));
+        AMediaExtractor_advance(ex);
+    }
+
+    // allocate java int array for result and return it
+    int *data = sizes.data();
+    int numsamples = sizes.size();
+    jintArray ret = env->NewIntArray(numsamples);
+    jboolean isCopy;
+    jint *dst = env->GetIntArrayElements(ret, &isCopy);
+    for (int i = 0; i < numsamples; ++i) {
+        dst[i] = data[i];
+    }
+    env->ReleaseIntArrayElements(ret, dst, 0);
+
+    delete[] buf;
+    AMediaExtractor_delete(ex);
+    return ret;
+}
+
+
+// get the sample sizes for the file
+extern "C" jobject Java_android_media_cts_NativeDecoderTest_getSampleSizesNative(JNIEnv *env,
+        jclass /*clazz*/, int fd, jlong offset, jlong size)
+{
+    AMediaExtractor *ex = AMediaExtractor_new();
+    int err = AMediaExtractor_setDataSourceFd(ex, fd, offset, size);
+    if (err != 0) {
+        ALOGE("setDataSource error: %d", err);
+        return NULL;
+    }
+    return testExtractor(ex, env);
+}
+
+// get the sample sizes for the path
+extern "C" jobject Java_android_media_cts_NativeDecoderTest_getSampleSizesNativePath(JNIEnv *env,
+        jclass /*clazz*/, jstring jpath)
+{
+    AMediaExtractor *ex = AMediaExtractor_new();
+
+    const char *tmp = env->GetStringUTFChars(jpath, NULL);
+    if (tmp == NULL) {  // Out of memory
+        return NULL;
+    }
+
+    int err = AMediaExtractor_setDataSource(ex, tmp);
+
+    env->ReleaseStringUTFChars(jpath, tmp);
+
+    if (err != 0) {
+        ALOGE("setDataSource error: %d", err);
+        return NULL;
+    }
+    return testExtractor(ex, env);
+}
+
+static int adler32(const uint8_t *input, int len) {
+
+    int a = 1;
+    int b = 0;
+    for (int i = 0; i < len; i++) {
+        a += input[i];
+        b += a;
+    }
+    a = a % 65521;
+    b = b % 65521;
+    int ret = b * 65536 + a;
+    ALOGV("adler %d/%d", len, ret);
+    return ret;
+}
+
+static int checksum(const uint8_t *in, int len, AMediaFormat *format) {
+    int width, stride, height;
+    if (!AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_WIDTH, &width)) {
+        width = len;
+    }
+    if (!AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_STRIDE, &stride)) {
+        stride = width;
+    }
+    if (!AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_HEIGHT, &height)) {
+        height = 1;
+    }
+    uint8_t *bb = new uint8_t[width * height];
+    for (int i = 0; i < height; i++) {
+        memcpy(bb + i * width, in + i * stride, width);
+    }
+    // bb is filled with data
+    int sum = adler32(bb, width * height);
+    delete[] bb;
+    return sum;
+}
+
+extern "C" jobject Java_android_media_cts_NativeDecoderTest_getDecodedDataNative(JNIEnv *env,
+        jclass /*clazz*/, int fd, jlong offset, jlong size) {
+    ALOGV("getDecodedDataNative");
+
+    AMediaExtractor *ex = AMediaExtractor_new();
+    int err = AMediaExtractor_setDataSourceFd(ex, fd, offset, size);
+    if (err != 0) {
+        ALOGE("setDataSource error: %d", err);
+        return NULL;
+    }
+
+    int numtracks = AMediaExtractor_getTrackCount(ex);
+
+    AMediaCodec **codec = new AMediaCodec*[numtracks];
+    AMediaFormat **format = new AMediaFormat*[numtracks];
+    bool *sawInputEOS = new bool[numtracks];
+    bool *sawOutputEOS = new bool[numtracks];
+    simplevector<int> sizes[numtracks];
+
+    ALOGV("input has %d tracks", numtracks);
+    for (int i = 0; i < numtracks; i++) {
+        AMediaFormat *format = AMediaExtractor_getTrackFormat(ex, i);
+        const char *s = AMediaFormat_toString(format);
+        ALOGI("track %d format: %s", i, s);
+        const char *mime;
+        if (!AMediaFormat_getString(format, AMEDIAFORMAT_KEY_MIME, &mime)) {
+            ALOGE("no mime type");
+            return NULL;
+        } else if (!strncmp(mime, "audio/", 6) || !strncmp(mime, "video/", 6)) {
+            codec[i] = AMediaCodec_createDecoderByType(mime);
+            AMediaCodec_configure(codec[i], format, NULL /* surface */, NULL /* crypto */, 0);
+            AMediaCodec_start(codec[i]);
+            sawInputEOS[i] = false;
+            sawOutputEOS[i] = false;
+        } else {
+            ALOGE("expected audio or video mime type, got %s", mime);
+            return NULL;
+        }
+        AMediaFormat_delete(format);
+        AMediaExtractor_selectTrack(ex, i);
+    }
+    int eosCount = 0;
+    while(eosCount < numtracks) {
+        int t = AMediaExtractor_getSampleTrackIndex(ex);
+        if (t >=0) {
+            ssize_t bufidx = AMediaCodec_dequeueInputBuffer(codec[t], 5000);
+            ALOGV("track %d, input buffer %d", t, bufidx);
+            if (bufidx >= 0) {
+                size_t bufsize;
+                uint8_t *buf = AMediaCodec_getInputBuffer(codec[t], bufidx, &bufsize);
+                int sampleSize = AMediaExtractor_readSampleData(ex, buf, bufsize);
+                ALOGV("read %d", sampleSize);
+                if (sampleSize < 0) {
+                    sampleSize = 0;
+                    sawInputEOS[t] = true;
+                    ALOGV("EOS");
+                    //break;
+                }
+                int64_t presentationTimeUs = AMediaExtractor_getSampleTime(ex);
+
+                AMediaCodec_queueInputBuffer(codec[t], bufidx, 0, sampleSize, presentationTimeUs,
+                        sawInputEOS[t] ? AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM : 0);
+                AMediaExtractor_advance(ex);
+            }
+        } else {
+            ALOGV("@@@@ no more input samples");
+            for (int tt = 0; tt < numtracks; tt++) {
+                if (!sawInputEOS[tt]) {
+                    // we ran out of samples without ever signaling EOS to the codec,
+                    // so do that now
+                    int bufidx = AMediaCodec_dequeueInputBuffer(codec[tt], 5000);
+                    if (bufidx >= 0) {
+                        AMediaCodec_queueInputBuffer(codec[tt], bufidx, 0, 0, 0,
+                                AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM);
+                        sawInputEOS[tt] = true;
+                    }
+                }
+            }
+        }
+
+        // check all codecs for available data
+        AMediaCodecBufferInfo info;
+        for (int tt = 0; tt < numtracks; tt++) {
+            if (!sawOutputEOS[tt]) {
+                int status = AMediaCodec_dequeueOutputBuffer(codec[tt], &info, 1);
+                ALOGV("dequeueoutput on track %d: %d", tt, status);
+                if (status >= 0) {
+                    if (info.flags & AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM) {
+                        ALOGV("EOS on track %d", tt);
+                        sawOutputEOS[tt] = true;
+                        eosCount++;
+                    }
+                    ALOGV("got decoded buffer for track %d, size %d", tt, info.size);
+                    if (info.size > 0) {
+                        size_t bufsize;
+                        uint8_t *buf = AMediaCodec_getOutputBuffer(codec[tt], status, &bufsize);
+                        int adler = checksum(buf, info.size, format[tt]);
+                        sizes[tt].add(adler);
+                    }
+                    AMediaCodec_releaseOutputBuffer(codec[tt], status, false);
+                } else if (status == AMEDIACODEC_INFO_OUTPUT_BUFFERS_CHANGED) {
+                    ALOGV("output buffers changed for track %d", tt);
+                } else if (status == AMEDIACODEC_INFO_OUTPUT_FORMAT_CHANGED) {
+                    format[tt] = AMediaCodec_getOutputFormat(codec[tt]);
+                    ALOGV("format changed for track %d: %s", tt, AMediaFormat_toString(format[tt]));
+                } else if (status == AMEDIACODEC_INFO_TRY_AGAIN_LATER) {
+                    ALOGV("no output buffer right now for track %d", tt);
+                } else {
+                    ALOGV("unexpected info code for track %d : %d", tt, status);
+                }
+            } else {
+                ALOGV("already at EOS on track %d", tt);
+            }
+        }
+    }
+    ALOGV("decoding loop done");
+
+    // allocate java int array for result and return it
+    int numsamples = 0;
+    for (int i = 0; i < numtracks; i++) {
+        numsamples += sizes[i].size();
+    }
+    ALOGV("checksums: %d", numsamples);
+    jintArray ret = env->NewIntArray(numsamples);
+    jboolean isCopy;
+    jint *org = env->GetIntArrayElements(ret, &isCopy);
+    jint *dst = org;
+    for (int i = 0; i < numtracks; i++) {
+        int *data = sizes[i].data();
+        int len = sizes[i].size();
+        ALOGV("copying %d", len);
+        for (int j = 0; j < len; j++) {
+            *dst++ = data[j];
+        }
+    }
+    env->ReleaseIntArrayElements(ret, org, 0);
+
+    delete[] sawInputEOS;
+    for (int i = 0; i < numtracks; i++) {
+        AMediaFormat_delete(format[i]);
+        AMediaCodec_stop(codec[i]);
+        AMediaCodec_delete(codec[i]);
+    }
+    delete[] format;
+    delete[] codec;
+    AMediaExtractor_delete(ex);
+    return ret;
+}
+
+extern "C" jboolean Java_android_media_cts_NativeDecoderTest_testPlaybackNative(JNIEnv *env,
+        jclass /*clazz*/, jobject surface, int fd, jlong offset, jlong size) {
+
+    ANativeWindow *window = ANativeWindow_fromSurface(env, surface);
+    ALOGI("@@@@ native window: %p", window);
+
+    AMediaExtractor *ex = AMediaExtractor_new();
+    int err = AMediaExtractor_setDataSourceFd(ex, fd, offset, size);
+    if (err != 0) {
+        ALOGE("setDataSource error: %d", err);
+        return false;
+    }
+
+    int numtracks = AMediaExtractor_getTrackCount(ex);
+
+    AMediaCodec *codec = NULL;
+    AMediaFormat *format = NULL;
+    bool sawInputEOS = false;
+    bool sawOutputEOS = false;
+
+    ALOGV("input has %d tracks", numtracks);
+    for (int i = 0; i < numtracks; i++) {
+        AMediaFormat *format = AMediaExtractor_getTrackFormat(ex, i);
+        const char *s = AMediaFormat_toString(format);
+        ALOGI("track %d format: %s", i, s);
+        const char *mime;
+        if (!AMediaFormat_getString(format, AMEDIAFORMAT_KEY_MIME, &mime)) {
+            ALOGE("no mime type");
+            return false;
+        } else if (!strncmp(mime, "video/", 6)) {
+            codec = AMediaCodec_createDecoderByType(mime);
+            AMediaCodec_configure(codec, format, window, NULL, 0);
+            AMediaCodec_start(codec);
+            AMediaExtractor_selectTrack(ex, i);
+        }
+        AMediaFormat_delete(format);
+    }
+
+    while (!sawOutputEOS) {
+        ssize_t bufidx = AMediaCodec_dequeueInputBuffer(codec, 5000);
+        ALOGV("input buffer %d", bufidx);
+        if (bufidx >= 0) {
+            size_t bufsize;
+            uint8_t *buf = AMediaCodec_getInputBuffer(codec, bufidx, &bufsize);
+            int sampleSize = AMediaExtractor_readSampleData(ex, buf, bufsize);
+            ALOGV("read %d", sampleSize);
+            if (sampleSize < 0) {
+                sampleSize = 0;
+                sawInputEOS = true;
+                ALOGV("EOS");
+            }
+            int64_t presentationTimeUs = AMediaExtractor_getSampleTime(ex);
+
+            AMediaCodec_queueInputBuffer(codec, bufidx, 0, sampleSize, presentationTimeUs,
+                    sawInputEOS ? AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM : 0);
+            AMediaExtractor_advance(ex);
+        }
+
+        AMediaCodecBufferInfo info;
+        int status = AMediaCodec_dequeueOutputBuffer(codec, &info, 1);
+        ALOGV("dequeueoutput returned: %d", status);
+        if (status >= 0) {
+            if (info.flags & AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM) {
+                ALOGV("output EOS");
+                sawOutputEOS = true;
+            }
+            ALOGV("got decoded buffer size %d", info.size);
+            AMediaCodec_releaseOutputBuffer(codec, status, true);
+            usleep(20000);
+        } else if (status == AMEDIACODEC_INFO_OUTPUT_BUFFERS_CHANGED) {
+            ALOGV("output buffers changed");
+        } else if (status == AMEDIACODEC_INFO_OUTPUT_FORMAT_CHANGED) {
+            format = AMediaCodec_getOutputFormat(codec);
+            ALOGV("format changed to: %s", AMediaFormat_toString(format));
+        } else if (status == AMEDIACODEC_INFO_TRY_AGAIN_LATER) {
+            ALOGV("no output buffer right now");
+        } else {
+            ALOGV("unexpected info code: %d", status);
+        }
+    }
+
+    AMediaCodec_stop(codec);
+    AMediaCodec_delete(codec);
+    AMediaExtractor_delete(ex);
+    return true;
+}
+
+extern "C" jboolean Java_android_media_cts_NativeDecoderTest_testMuxerNative(JNIEnv */*env*/,
+        jclass /*clazz*/, int infd, jlong inoffset, jlong insize, int outfd, jboolean webm) {
+
+
+    AMediaMuxer *muxer = AMediaMuxer_new(outfd,
+            webm ? AMEDIAMUXER_OUTPUT_FORMAT_WEBM : AMEDIAMUXER_OUTPUT_FORMAT_MPEG_4);
+
+    AMediaExtractor *ex = AMediaExtractor_new();
+    int err = AMediaExtractor_setDataSourceFd(ex, infd, inoffset, insize);
+    if (err != 0) {
+        ALOGE("setDataSource error: %d", err);
+        return false;
+    }
+
+    int numtracks = AMediaExtractor_getTrackCount(ex);
+    ALOGI("input tracks: %d", numtracks);
+    for (int i = 0; i < numtracks; i++) {
+        AMediaFormat *format = AMediaExtractor_getTrackFormat(ex, i);
+        const char *s = AMediaFormat_toString(format);
+        ALOGI("track %d format: %s", i, s);
+        const char *mime;
+        if (!AMediaFormat_getString(format, AMEDIAFORMAT_KEY_MIME, &mime)) {
+            ALOGE("no mime type");
+            return false;
+        } else if (!strncmp(mime, "audio/", 6) || !strncmp(mime, "video/", 6)) {
+            ssize_t tidx = AMediaMuxer_addTrack(muxer, format);
+            ALOGI("track %d -> %d format %s", i, tidx, s);
+            AMediaExtractor_selectTrack(ex, i);
+        } else {
+            ALOGE("expected audio or video mime type, got %s", mime);
+            return false;
+        }
+        AMediaFormat_delete(format);
+        AMediaExtractor_selectTrack(ex, i);
+    }
+    AMediaMuxer_start(muxer);
+
+    int bufsize = 1024*1024;
+    uint8_t *buf = new uint8_t[bufsize];
+    AMediaCodecBufferInfo info;
+    while(true) {
+        int n = AMediaExtractor_readSampleData(ex, buf, bufsize);
+        if (n < 0) {
+            break;
+        }
+        info.offset = 0;
+        info.size = n;
+        info.presentationTimeUs = AMediaExtractor_getSampleTime(ex);
+        info.flags = AMediaExtractor_getSampleFlags(ex);
+
+        size_t idx = (size_t) AMediaExtractor_getSampleTrackIndex(ex);
+        AMediaMuxer_writeSampleData(muxer, idx, buf, &info);
+
+        AMediaExtractor_advance(ex);
+    }
+
+    AMediaExtractor_delete(ex);
+    AMediaMuxer_stop(muxer);
+    AMediaMuxer_delete(muxer);
+    return true;
+
+}
+
+extern "C" jboolean Java_android_media_cts_NativeDecoderTest_testFormatNative(JNIEnv * /*env*/,
+        jclass /*clazz*/) {
+    AMediaFormat* format = AMediaFormat_new();
+    if (!format) {
+        return false;
+    }
+
+    AMediaFormat_setInt32(format, AMEDIAFORMAT_KEY_BIT_RATE, 8000);
+    int32_t bitrate = 0;
+    if (!AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_BIT_RATE, &bitrate) || bitrate != 8000) {
+        ALOGE("AMediaFormat_getInt32 fail: %d", bitrate);
+        return false;
+    }
+
+    AMediaFormat_setInt64(format, AMEDIAFORMAT_KEY_DURATION, 123456789123456789ll);
+    int64_t duration = 0;
+    if (!AMediaFormat_getInt64(format, AMEDIAFORMAT_KEY_DURATION, &duration)
+            || duration != 123456789123456789ll) {
+        ALOGE("AMediaFormat_getInt64 fail: %lld", duration);
+        return false;
+    }
+
+    AMediaFormat_setFloat(format, AMEDIAFORMAT_KEY_FRAME_RATE, 25.0f);
+    float framerate = 0.0f;
+    if (!AMediaFormat_getFloat(format, AMEDIAFORMAT_KEY_FRAME_RATE, &framerate)
+            || framerate != 25.0f) {
+        ALOGE("AMediaFormat_getFloat fail: %f", framerate);
+        return false;
+    }
+
+    const char* value = "audio/mpeg";
+    AMediaFormat_setString(format, AMEDIAFORMAT_KEY_MIME, value);
+    const char* readback = NULL;
+    if (!AMediaFormat_getString(format, AMEDIAFORMAT_KEY_MIME, &readback)
+            || strcmp(value, readback) || value == readback) {
+        ALOGE("AMediaFormat_getString fail");
+        return false;
+    }
+
+    uint32_t foo = 0xdeadbeef;
+    AMediaFormat_setBuffer(format, "csd-0", &foo, sizeof(foo));
+    foo = 0xabadcafe;
+    void *bytes;
+    size_t bytesize = 0;
+    if(!AMediaFormat_getBuffer(format, "csd-0", &bytes, &bytesize)
+            || bytesize != sizeof(foo) || *((uint32_t*)bytes) != 0xdeadbeef) {
+        ALOGE("AMediaFormat_getBuffer fail");
+        return false;
+    }
+
+    return true;
+}
+
+
+extern "C" jboolean Java_android_media_cts_NativeDecoderTest_testPsshNative(JNIEnv * /*env*/,
+        jclass /*clazz*/, int fd, jlong offset, jlong size) {
+
+    AMediaExtractor *ex = AMediaExtractor_new();
+    int err = AMediaExtractor_setDataSourceFd(ex, fd, offset, size);
+    if (err != 0) {
+        ALOGE("setDataSource error: %d", err);
+        return false;
+    }
+
+    PsshInfo* info = AMediaExtractor_getPsshInfo(ex);
+    if (info == NULL) {
+        ALOGI("null pssh");
+        return false;
+    }
+
+    ALOGI("pssh has %u entries", info->numentries);
+    if (info->numentries != 2) {
+        return false;
+    }
+
+    for (size_t i = 0; i < info->numentries; i++) {
+        PsshEntry *entry = &info->entries[i];
+        ALOGI("entry uuid %02x%02x..%02x%02x, data size %u",
+                entry->uuid[0],
+                entry->uuid[1],
+                entry->uuid[14],
+                entry->uuid[15],
+                entry->datalen);
+
+        AMediaCrypto *crypto = AMediaCrypto_new(entry->uuid, entry->data, entry->datalen);
+        if (crypto) {
+            ALOGI("got crypto");
+            AMediaCrypto_delete(crypto);
+        } else {
+            ALOGI("no crypto");
+        }
+    }
+    return true;
+}
+
+extern "C" jboolean Java_android_media_cts_NativeDecoderTest_testCryptoInfoNative(JNIEnv * /*env*/,
+        jclass /*clazz*/) {
+
+    size_t numsubsamples = 4;
+    uint8_t key[16] = { 1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4 };
+    uint8_t iv[16] = { 4,3,2,1,4,3,2,1,4,3,2,1,4,3,2,1 };
+    size_t clearbytes[4] = { 5, 6, 7, 8 };
+    size_t encryptedbytes[4] = { 8, 7, 6, 5 };
+
+    AMediaCodecCryptoInfo *ci =
+            AMediaCodecCryptoInfo_new(numsubsamples, key, iv, AMEDIACODECRYPTOINFO_MODE_CLEAR, clearbytes, encryptedbytes);
+
+    if (AMediaCodecCryptoInfo_getNumSubSamples(ci) != 4) {
+        ALOGE("numsubsamples mismatch");
+        return false;
+    }
+    uint8_t bytes[16];
+    AMediaCodecCryptoInfo_getKey(ci, bytes);
+    if (memcmp(key, bytes, 16) != 0) {
+        ALOGE("key mismatch");
+        return false;
+    }
+    AMediaCodecCryptoInfo_getIV(ci, bytes);
+    if (memcmp(iv, bytes, 16) != 0) {
+        ALOGE("IV mismatch");
+        return false;
+    }
+    if (AMediaCodecCryptoInfo_getMode(ci) != AMEDIACODECRYPTOINFO_MODE_CLEAR) {
+        ALOGE("mode mismatch");
+        return false;
+    }
+    size_t sizes[numsubsamples];
+    AMediaCodecCryptoInfo_getClearBytes(ci, sizes);
+    if (memcmp(clearbytes, sizes, sizeof(size_t) * numsubsamples)) {
+        ALOGE("clear size mismatch");
+        return false;
+    }
+    AMediaCodecCryptoInfo_getEncryptedBytes(ci, sizes);
+    if (memcmp(encryptedbytes, sizes, sizeof(size_t) * numsubsamples)) {
+        ALOGE("encrypted size mismatch");
+        return false;
+    }
+    return true;
+}
+
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_7.mp3 b/tests/tests/media/res/raw/cp1251_7.mp3
new file mode 100644
index 0000000..d1c492b
--- /dev/null
+++ b/tests/tests/media/res/raw/cp1251_7.mp3
Binary files differ
diff --git a/tests/tests/media/res/raw/cp1251_8.mp3 b/tests/tests/media/res/raw/cp1251_8.mp3
new file mode 100644
index 0000000..17f7e31
--- /dev/null
+++ b/tests/tests/media/res/raw/cp1251_8.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/football_qvga.yuv b/tests/tests/media/res/raw/football_qvga.yuv
new file mode 100644
index 0000000..f18f676
--- /dev/null
+++ b/tests/tests/media/res/raw/football_qvga.yuv
Binary files differ
diff --git a/tests/tests/media/res/raw/football_qvga_desc.txt b/tests/tests/media/res/raw/football_qvga_desc.txt
new file mode 100644
index 0000000..f6b44b2
--- /dev/null
+++ b/tests/tests/media/res/raw/football_qvga_desc.txt
@@ -0,0 +1,2 @@
+Football_qvga.yuv contains 3 seconds of raw 320x240 yuv420 video @ 30 fps.
+Extracted from http://media.xiph.org/video/derf/y4m/football_cif.y4m.
\ No newline at end of file
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/iso88591_2.mp3 b/tests/tests/media/res/raw/iso88591_2.mp3
new file mode 100644
index 0000000..bcfdaad
--- /dev/null
+++ b/tests/tests/media/res/raw/iso88591_2.mp3
Binary files differ
diff --git a/tests/tests/media/res/raw/psshtest.mp4 b/tests/tests/media/res/raw/psshtest.mp4
new file mode 100644
index 0000000..98ffeb0
--- /dev/null
+++ b/tests/tests/media/res/raw/psshtest.mp4
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/res/raw/sinesweeptsaac.m4a b/tests/tests/media/res/raw/sinesweeptsaac.m4a
new file mode 100644
index 0000000..f583012
--- /dev/null
+++ b/tests/tests/media/res/raw/sinesweeptsaac.m4a
Binary files differ
diff --git a/tests/tests/media/res/raw/testmp3_3.raw b/tests/tests/media/res/raw/testmp3_3.raw
new file mode 100644
index 0000000..1d5ee19
--- /dev/null
+++ b/tests/tests/media/res/raw/testmp3_3.raw
@@ -0,0 +1 @@
+SUQzBABAAAAAegAAAAwBIAUPNyZSM1RQRTEAAAAOAAAATmV3IFdhdmUgTGFic1RJVDIAAAAPAAAAVW5jb21tb24gU3RvcnlDT01NAAAAMwAAAAAAAAAoYykgQ29weXJpZ2h0IDIwMDggQW5kcm9pZCBPcGVuIFNvdXJjZSBQcm9qZWN0//pwBDV5AAABqxzGvSTAAD9DmHGnsACJ3IV9uUoQETYQrXc1IAIgBAWoAHtdG2ogYXCgYQRXFYrbUAYDTYgQIZZBDLJ388RH93rE77Z71rJ2Udz4ICQ4D6nfwwEGoafUbm2IYqFOJoLgdEVPlvJ2hakADAOW0gkCIJCg7BoTHTszfymrFiztXr32DM/+lNbfoZn74gDHUCBRwP4DgcDgcDgcDgYAAAAAAJKgbF6umkB5UoGyQJWgZIQAcM8DSlQMWH8BgGBYQAkP+AoTAxgoDEBAtM/xygbqBfQAYWG3/+RMzJ83My/++D78BgMBgMBgMBgKAAAAAAabN7JmcJXTG7HmYGSrGziBgiOH5duAfqDYLXdMOXDjAvm2+Fp4NkA20EAFNsnkOD4BHgXBil//K6Zums3/fB9MQf/6cgTaUgANgiQ+Vx88oARCRksH55QAiAzJVGw8p9EAnSqM9Ik6ABd3Aa1SH2V0YlB7IWiFbGlh73twkBhYOKPGu5WbGuHw6wACw1XG/rzXFb/+V5V1//Ra0KQ5WIb/8w26OVDDUvFh/UHgHABy7gZOBwuqS6Qha25y3BvTWeugYPAMOFRqiSU7BMqFHkd1b9ptAFEQ7dH+LGGlLWXf/1epSzVGfqBmJTINU7uoAKcjKHEoG1ZQ5sw8YfSjWDslm1pGlHXqKLaBe8lfrzbQFcmEQJZVX1fWNBRA+v+LWUdN//NShkNEh389jRLt9QBM3AGAzw7mZBBjCSgZhZ4xgQTDjXPA+88I2ZR0SpTv+uJG2aPkWame+9A8uCHR//QE5t7//q99UMZhnT31XIGTEFNRTMuOTcAAAAAA//pwBOp9AAyCFyNTmww8JERHWoNlgk6IOI1SbDCn0QgRqpz0iTMAObADFtkjB9TSwuVAKwwdHcYq78pk1DcsrCSnNhNDLVWmrsb5esBv+Pw4IbFrer2zGEgxnNf1dS5I1X11FYMD2fbaADdwBMockXHliie6kSMBQV7DR5IWVrSkxcWhzGox4o2vSfacLUorxlPsmLf81+Kt/2MLOyo6My9fypa6/Vqps6stBlWAJfwBjICCCWUKJEv2iyGJMdEFeiSQWCp+RRft5Fs3yv1JExnLAbN+r+gCB5yuj32dR51njawqJjYDABIoh80BBZIQU9+AMBHTOKRtBCjYEhEggWdSbLJG1s0HGieVmE9yWJOV9psEOJfHqsGclVGY+n4tAzZZG/vq76kddCIi69/Pa/aYgpqKZlxybv/6cgR03QAIghM6VBsJE7RCArqnPwkiiLTDVOkwRZkWCmnNh60iALv4A1LyHEOQcrhLqSplRqTs+5Zr3La+WcBtw5GSPfV7Hh/MOMJqzL61biDUVP5Kran9GzkU53QhSMLamiXRlecIwAQibvwOFcnytWjRamTibaBhPLXpph1L6RIUX2d+Vytj+A2wizntEWkZM/IHTJz+N1kEgQuwXYTAAWWUsmhYyQRisQVL/wJMAmAdhwlCYNwTPlY5u8cZgo2SS0pnXmXZHDGYDGqpWT1Sy1BGN/0NRr/zJr3oZQ4OQmlX05S7ShTt6PtXfilDegUZtgNCzSwFisYFBICxEAqj2V0TNniTVUaNU94pWa3AnnpVYgKxXb5WAmkTEwG74xjmv8yZWHGBx5lr2kzOy8VZFo7XMuTEFNRQ//pwBBnqAAiCESLU0wkSdEPlumNlYkzIZI9K7KSykQqYKtzziVoAAAglLvgNTMKKCy5QZUNM1mdaCD8mlEE5YBywn2zzc83E+r6Kj63yOTzrnJgblbf1YEFDq/bqkiy3khR4LLJJvReAnPwB0ebJSaXQyIugPZTwWKH66MjwigwLzlDaShH5mevRwznEUWFfb5xVLEMpWp/Qd3/6FV2RVFItgCjRBlX/dbEHDYglXYAZ2hGuRQy0Rzo+vYm5QzylUslt6ST7ZsKeG5li3dXbza0q9dewECSFPFoq/6pdRf/5QkLSQ5Dru4qDpFaL/uTEF3f8CopBQreGclAh5GoaAhyJrAwaNKOGk99YAeDHBdvdW7sKAZ3T6oURKiU23si7JRpwZYJzr4FOJQywWfQR1IWlMQU1FAAAAP/6cgQ3PgAJghoi1VMMKURCBKqXYOZfiDjLVOwcbdEUGKkNhJ3LAAwAJvf8D5aVUvLtEEMzRG/ca1KpbiMyj2CxDNvo4liRBbO6djjzvcRDa/9BiwicZpmFtWLFAwbOUwfUtxyumYn6ZAAIJ0sAGki2wtTqqNwSJSbd2ma3neJhqBIlHjQktzdYTGsqHmq7p275TAxNz7V/pZHHTY3VkrE0K5MOizA0KGxFcmILm+nIBvPfSMZNJoBq5Ri1P5Q2OrOUO2S+kdz2XZ/nnq+UDSN/3o4WMoJod9CvubZmxwjIh84KOFkWk5ufrUq9iABJsAJkGMgQrHLkZUjwXQcKthEdsWGXI8s9VdZuaAETuGVZKqoCVY9prN/bSgbJEFo29c25zX/2bbmnnlCJ7xHv1pgy/UxBTUUzLjk3//pwBP+kAAiR+yNUOwwRVEHGGqo9AluIyMlS56RHOQQYaiTxihcAIKV2wH2FootzQywEBzEA5cHaqOpfGWbzlAFUvdKwLKprR/r8gpf/eKDagrYlT2zx0o2u+deZItUsotbrmL1AAIGSQ7IBINEoEFETh/ByqhlIczmeNCwqbfB3dl0PqlYDmkxlqG289TUf/7ueXp9brpIlnxIfLI7WCTwuSdyKIAYUEuSQD1LoKpUkvIaKoLNBEBTspB9FO8NqDpr6wFpziVcIOj+3ugMb/mI5ToJp3vv61SxEUoysVcELVwNaoVx32/aJ5qFUqRA/KCaFOVT8sj+A9nseqcYYz/bW8MxjmXMOTGQa7w4g4YZZkb7ht2UUXRW+aSRQ1Pq93dHNYpYsBARDvfrlEr+JiCmopmXHJuAAAP/6cgRSzAAMghYwVVHpEbY/4SqqPEZQiJh1Tmwwp5ENEupc9BVaAAAYJTf8Dvg+DibQH87TkG0jIgXdzo5mFc1Rz799XQ6gnT0p1c4nT+ZbOOVv2TRX3fEKTV4P36blbt+hsy54zzu6YAAAIFS/8DAsJUTYUylNKNcjmBx7jQGHAXFi5B4oXo3aEuFRA9GgHq3ZcIpMnXgyIgZMH3yDX3vGDFVlwGkk+KgqXbAfICqyHIPWFW8UJUlw3La1AlkNm5oXP2dMOpFFl2Vyi7rT9HQzxgONHX8GipN188szS9rSBkR1EkrKrQq5bi0ZseMJrbbDhlGEgHFtZRqL6KVscluFB0dRHHJCeltWM0lOtaUt/8w0GnIPlkOFCuKwDywMEQtAy0CiAXRmFGwniLN5vF9CYgpqKZlxybgA//pwBB5iAAiCGSXS0wcb1j6Eqqc1hQjIqJtVp5hHuQMS6ujyoVYAAAgDbsAMoWMoKAw4IpInKrx7BTl9dQn441GksGyL0GpSTORHVcu3/v6RqOZar2HHsxetbPR9fiFugSoTJbKLT/8KgBBc//Ag+Jg2VoaQuwpAzQ1FKQg0SI2VPSbVi906Wl1HM//FlBHUQIWZ5QY/yf1yA+b0O7cpfIsrRr86v47oAEBUtkA7A+IjIiypDtoWkKKngSO7zX/6wdeIFAn4Qceyv+90QrHO9PoYzFsIgrOfKsH50uImBnltHu2Zn+Gvb5L7nvvgAZTlsgFh/qdAwJdoKjaqpEHczwnP9pHp5WDr65VojRX+fbFeK5b1x/6bLaiNBV7FiOceaQoLIQ+cw2ndzKHKqLpiCmopmXHJuAAAAP/6cgR/lgAAkhUi1dCvEExDREqHPWVDyMg1SOwwylEIkypo84lvAEAcJu2QCFlGasYn5ol8hyrFQTwRwbQURUQczYKm52Mc7r8qCXKdbf84UXEAseaeNOfi8lY02JGMvS4+KSrrzrUqWAEFSWQCCEcChoF6xsagal8SJIP7BHDouM2u+kJZhyyvsnatyGOJJ/iuhRXdv094Y/9a0T08Mqekwa/sb3e6dV23CYYAQQ5baM3eKn4dfkUu7Qvl/Yw1HjQ3xCKI8eU4GsxYskuAUpQKmtOTEHqJ7NS6ios5llZ19YiJOrOqVc9olLZh41SkGaL1iGSnZBIwerOtajKl/CPFrh9DTGiA4Wp0u88Lsw+NmR7ebZEPL/wrkoqIEt3niwLp66Xx1Pnfwgo7sVd+Fui80n5CkxBTUUAA//pwBCTPAAiCFSNT0eMTpEJDioo9JRmImHdQ5+EEcQ8KqN2HpFoAAAgVNtqMIaBtK+OZAaoQ9ean7+Vaz3c7OsfxUJerfvbdPU1tRbrf/CnFQyTAp+xS3PCY8YepQnTULkYrrVaHWgVYAABAlSNsShJgoJDxIMIhONlCeC5qcemLEB87mtRy6grMyPb2toP+gRxYWmnnjMYEYpYhpJ1O03nkNz652tj+9D/aUtSRwDn2yjbXgSEbHnT1oLkDW5sQatrv6a/+UhwcOJIqRO9fzUrOwsTB4EGXvNLFelceq5wBfa2/axgcEh8OiqDRFbCoQkbbQJg1JTlKRqcPpUHYwOAqk8MiTJmik+H1SJfpVsc2t+wsypdrOfUXJhn5VBcGQ0cKnK6yGcrSBaCFjl22O2PWPWmIKaigAP/6cgTOAwAIghgcVNHjKexEZKqKPGVpiCyRV0ecy/EKkipo9IinAABcFOSRjowgBrKk6ick4C0U6iJcJYVS5PFUtLiQImb73pUgqjyhZZ8A+fsboLCip4y4PB60TLQtAs4BCIRMHIb/6wAASJMljYlAtEAFUxOTRLI1woemjFQWOiMMp2zhAjYmIHE1pv0bSolF3N+7odnGIoSpWPbrUbB1AqTaHFAyCST7d3/9AJtPWyAdnHtZzKNoGbErElluc7LblXMOdCwpGTyYMsn9UuiHiFKvLmd3f/+sSuaofOPeZSw4Uta56GwvIHlmS0goAgU7ZANA8A+DyVKZZaL2OYkzdujQEh0uV7Ulwg5M1OwJ953Iz/5gQEM8yUn7r/KjpRdd/sZHZFvFPvpf8utiGf78qYgpqKZlxybg//pwBFhOAAiCCBxUUeY5rEJjieNp6CyIZHFPR5RrcRWUqejxFT8AABgSpJGMqwU5AOAYppDjFUNo5F7CvU5dzTX9ndAz1f83Spb6QuVDRt/VpRFGXhiTyDz4wYKBRwtNKQtPpRzLbQQXIAH+OqVEMxlgsHR5IWBbLCTwDyXYjDfiqyfzhIO/AWUbUSop7GwLo4CSCWFer/4jqtR5P0itPRW9qHYoljUtvUAQSUjYGWsZQbi+WwdYNJVsz/AwXoLlaIR+jDn8JWjIKUL0/5M0NRjvoJOUJQ8o0tqiq5UwatHQ0Zep5w68ogstT96BCKTlkAbQBQIMWjZAV6qPZiA1BD0UjOMcUvvyhiGARhaODb3TZ5g1//ciUP9sU6F7jp8zvFJftWddBlCoAYvAdW83bxv0mIKaigAAAP/6cgTslQAIgfccVNHmKmxCRgqaPOJNyHBzTUegSXEYE6ldhJUaAABcluVwDLc+G0qhBGsdCR479f6ynhJGn6Oo77+A6tPc1Fb1upD0BXfGl0KC1sqKLGZoVTq+stW8WNKJmGOWAAAaKdsgEgf74iHFsqeD+MH0Luk549IdWI7IEpCdTL+g29Trf/UxDuRFJBde9k3dQa+bIpIp3fojhvzn/yhC5B/0AOAnJGxs/QhYNU8BZhMAjKEKEiZblrtQzmTj4etMKkpl/3vUK6jncjjgG5xk8YXW2KocoDwqSNEXoVtzqa6rrdIxPLBBc22ooRGZRdQlzo29DWWkxkNLdrJdV2DagLrm8em5fXsMKV0YjjS9vuiVZyMPS8AsicUStqBpd4UWsaGkvT32MsDBX1dKYgpqKZlxybgA//pwBNaBAAGCFhzUUekaTEOFmmc8wlWIJJVVR5hpcRAOKc0HjCMAAKylJZAO+BJpVjGGeAW5hRxb273lwmOGo1I8JNCqIxP4jJdv5ze9gQS15uDOmXSwHp8+VQXf2pJBKHWMWYGVvaq0CIBjrYEgApB6l5ePYwZY7G9GQy0FvgicQOF2d3bCRJkZzafo9VUOJK5kL653qrH/QqEmQQ8+ETx9KjTr21wj0TKtY4AFXynb3opq3OKaf64T740xlS2RmimnHD1l7UWKYIppDpf4yy7oQSb/24uVDGxpkT+1yTQMhxAqRLLqJPx2qtT9ITe/4CYE4HGzq1cJktyCVyNeGMUwPIVD6l/4TfBwGalPymrQgo////wdBx++k7ydjJUvZ0xhVhkmZqYA+zX7Q6adt1uJiCmooAAAAP/6cgRN8gAIghU0UznjEuxAw4qKPGZsiNUPRuwkqpD/Dmpo8wleAGEVJGBhmFlCzPE4RrhMiyqhNvkFtDhE5PDEXLLMP0MtGWCwZa3q2Ocn/IqA5s7a/6LvdrWooISAUyLDAsfOf0hY0ABBQbm/4GgSpITbc3biFgRlgZ7RHGNmIyC5xgFSM+6/GFUEQ1/stItBxD4WSNWeYhMewaSdRQK7xWYr22GbBqXBBLu2A1DrACi8PDHFBkw0bHJh56+Lp2L9zKdg+7M97kuMdmO7q36PUYZf+6jTmmZjr6OxFZisSylT8jmR01vtvsSvqSNmaoaCUtkA4XbYa8RTpA5TQen3DMvH2qwzX3Ryd1vOkrMcb6G2mAgwL+Cw9pvKJWSYGDYqk1fDcTHRddjK61scxD0xBTUUzLjk3AAA//pwBBImAAiCFCTSOwIz7kLkelpgYlyISKtCbKSq2QgOKR2DCV4BIBcsAGpOOIKBRgRwVoSuS2dd7Icp8qklpr6HIoBZyLtcUrK4vvb6O1A4x6f+wbVrFI7E/GsUgdvpraxKdKP6nqBgAACCcu2AlJkAOAZtZlMDE00qmtw9MBjtQHM7PRqSlL5xOKDGnnBC7LcwJjTf46jb2WjgVjgeyy0nXITscPYzYh9dQBd2AHv4QWK+nSz5FCogIw2VO6yJCYI0wgzFqCQM5aiO3cBTvHs8v966Bu/+pAmzsWeazkYRyK8vgYlbd/Z6AWoopUAFxsCaBKSFUOT7CFppFDIZtVnVLp1CHW2BmaS+aEQ6M+3966i3/bOtfKBWdFksaBGAJCFteKiIAlS4xwpknqJPWmIKaimZccm4AP/6cASXkQAIghYj0rsPKTZD44qKPMVTiHynTueY6NkHDioo8SV/ACC3tsBuRFVzaXSpNlXxZWiOi6zREBUs5gPlu3hSRBxLf5aVsYG/+c4DMB02znk1/cHM9va1vYagfjzAg7/tp136wAAZclqSRiUNoojagIpWh4SrIbT2sd9H1dC48x9CzkezUd/a7mZhwaj7hHF+dFqs7g6UCqWhY6ostqXFRxVgo1osi7ytA5N/wMsgUIPEEM0p2IukWlEpHtrbkWS/92lBYF2JGv50m8sapJ2/7ur/alpca+mip6cbJrkKnNdNuaZWdZSJhhWTrrFMFS2QCQGyxlbRTn4SUolNBhJS4DtH5mUiI3BCHzf32gglItr/jI5QtIOYyPhGedr+RkC6mekWun+pkvq1+6Nb/FMQU1FAAAD/+nIEAoIACIIDE9LTBjrcQAOKmjxiU4iwcVNHmEixHI4qqPCVYwAATBKkbA7K4GKGvMIgKSg5kUH3oCzq67Ss6Q329jzjZc6KDlSR7hjBge8EmiaBk3Ota8il2+YkiuWYeIN1tXJAAKMS3JJBwrijSsNIqYWs52JH3QfhgWiivtnYHM43v9WRnoP+UcnvwBQL5QwMQEBcmF2GmRRgovUjQgDaE0LyVLkq2yQeAQMhrYFM8VaLjWLImEp03DouRakYyl3G3R/01oEMH3axhkREjhCXQfDcmGYHQaLpHBlVYESRpn5J5nmuA3VIPgGJN//xgSpU5jvE+Q5hgpb3KHM12Q0a01BSIdHRtb540HBfvfXIWguvfSXMpe065JD79ZPSBpf7/6h+R3dKWbv37Sdv9d7cTEFNRQAAAP/6cAQk7gAIkhEc1WnmEexAg4qaPGdPiLxzUUeYSXEDEanphIjfAAARSgKtskHhinrL1XIGQFRrEkTW7a5u6/PMXWzpxvf9suJnxd46KtGNXekgDwZiponpxcWE5CtXGgQTss73VRb1AAAOE5bIBYJszUvo9j1IQkGsMudh/pjcuTHEMslHlZ//eZQ0NAC/KrMoAKCqWA0eHFRdGNRICI8ocwbEqEC/YKI2S5LIx5AdrmtBtngfSIqgKKtvv3iO2WBVuRtw7QYlu/0CqhbB3ML6EuG1qQJoqdJhsCrS4/WQlTZoagecIiRZAgpxXZ6lWCTtUuhON9RUbkD0V1xyldpbduORamRuCmwQoPE/+XQV/9gw2CQGA/HAOiZfIRZNXQgMM+AZiJjKex38PcX5vBMQU1FMy45NwAD/+nIESIkACAIPHVVR4xJMQgOZ+mUiWojgTTpsvMjBFo5qdPSIzgAIrKdtkg8EqUu6Ha5I99GVxI/TsQUNbhVd3pLkd62/FS6AnFtD2bKAdA9M9Jj2tePS3byQrCq2n6axz1p+LKj2XgAAEAnJIA8RtzkhREUuMqmkCxppoIXlX645UxaXUfwzJrMPFlNrNdeVJgm33pvIKkT2VFAk+xyV+QsVuo1bMb0r1AGf/8ZorCJUfiQRhAgG/AYQjDZ0P8trGyZSKEIRTkZmgtMU+zPKir2ltXySiAlKHPLgIYTfFT6GEUEO76GMQQpMO9vRoIOWAACCFCnLZIJRdDY3h6zgehrssRuHsaZJ3IZGWuzB1Mv5CU46s8wLnzrZtJQOJWJkDRa04bIqINHSlqbkqNoY2rtDA+7UhMQU0P/6cATyjgAIghQc0FMmE6RB44oHZSJGh9SbU0eMSXEMjiq08RU+AAAIAuSSDSvU5CLNFYqyNPDpGWy1k0EfB3MHyepaQSlLbOHU1AmLqOn/vlYoxw5fnGhKu5i9jVClTOv7WCtSKv+2oAIAckkEFGFCOkKwK9IBiEsKENXdYOFlB10eKJ2R22REMya5hk2//UWDLE6A+w269Ua/GoFBlaGoE6lebt78nfrxoLkt2yQdyKhLJQuyGHSwVQ0LlczBZz3l4jI++yf2sVlb/9SG2xw6IQawCgwlG1Wl6FpoXHo8RPgZC/WNQA1JJSgLlskGRcrOnbE6ERewIMEd8cUcQcXRrnP01Gf/oy6jQ1noHHRcxJIippNI1SBMRWi0BjtKYofdoFo58csTiE/45aYgpqKZlxybgAAAAAD/+nIEBWwAAIHnHM+7LxG0QUR6mjzCOIksNVFHmEZxIQzn6Zeg2gAgJySQUKVhLYz5AWFSSYQTTLkhwTKdalXldEWw0sTFLe9iJO2yPf1R3uFnfuHGzDtCl8zRq2e86xje9dQAA1hy//8cQFrcWxFpBIxCymX/oLO0WiI7OzddRP/ZF7EFJ/6MDQL2pmxrFNULQylzil6LpeaWx90zNLNPJnafiwAINgu2yQdnGUmlURlMm8LOEoOPUezHiVjHTLBtFL6xMMcCgJP2lweAg+SUEWiFQqw4Q1nS5QVLCeNa8YwdYHjzEc49JtmxR9KACCTkkglxPcMkvagNLmpnE8xLoZTl7YTWjWkIwe5MghMN9uyWM4CUI1Hzd/xsO6gaPX4sdS6tQjxZd1pzoSlnFS1eRle81dxVMQU1FP/6cAQw8gAIkh8RVOnoEcxD44qNPSU5iERxTUegSbD4Eqoo8xTnAAACRhTlskHVimNpoVrkNo2A3c3hk4h6Wxe7mstgSisHdS9NfSWoCgy57AACCXlbUIpdZPkGCCo9jC7RfaLvKovucxwAAALMKctkAqIApXJCz4RaHTXehXf/YXWjpYV4m+oytiBqHrX5ztoOIublEiUs5osZdULOLmcQlyxlArMGBDWseyx2VBdFSRtiyLO8iELMAlQt8BtCKyaxo6G0kZ6Mq8EWAmh9uv/+iizpt+lSUDMybe8XUKCgpV64uSomA+8+n60YqLi7Vo2UpbCH0Z1IfzKQxE0gIL3/VS2UB+bujOkYYatf6X0HM3f45RakZj8uf3qbk1vO/VX7lwG2/qt8T++n6ZK2mIKaimZccm4AAAD/+nIEN3YACIIWHNRQ7xBMQKOKOWHlDYgwc0tHjErREo4o6YSU5gAAbBktkghQs0axGFDDe3KsZjbA7mjbqhq7av/utjneUQxH3k3JcxWaLoF1nzAwpDpBFb0tILCTCcUcE5kuW05GtaAAADB/qI6IDHYoh0mGWwOWlC0Q+Dry2TxrqLbOZqWRmOwz/29AxCuUmTKlRy+wzSlYqpzGHR889lSM8+9VqKtVILAub7YYL6IKlSlVxOQYkVqMWYy4QQY7LaWuk3H/9BvjO3yqwteeh08LGDh4uGFzYo4kkLEGmxcwRem6En3Rvk8cAQCjaTFAMFfa0vdhMNIlth4CRZd3vAdzE7FOQ0rY21l0+6avEwUGjSUtIknOqKu7J1jKWsb0vsO/p4lMlbgm484s0yHUxBTUUzLjk3AAAP/6cARo4wAAAfQZ0DsMEjRDA5pKPMJTiMyNTUwYSvEKjir0wxTuAiA5LbBdL5FVMLSvLzl/DoGMNEAeSliO9kZKf5jR1eiL4Ncf/qEFkAAiPhTt9FFh8aPodVlhySRbQT9z6v6QAECBVkbQlANmNlSSgOsl066R6mlUPxDQiCe5m9M413M3612IBgYRqFRMLGXKJ4YcUlENcJxjHohcnY9NA9spEE4uoASLynLZGPb2AYHiT0xoaXI7755kaV+Lr0iwEkx5aK16DpZH+hlTKN/9lBoCbyQseI2sJkBY24RvKDDIq6xo7Cz3IAL1/9AABFKbUltkgkDMOKS5ppFhywCbvxe9OE29lfuxqhYqVSBFfYpqrUdk8UIJGMmnC0xuS19DJgILS6KMqZtb5iLo2UDExBTUUzLjk3D/+nIErfEACIIIHE0TLyqkQkOJo2snHgicc1FFvEDxEBXp9PQdPgb/yPjb6wAlMDzVIGLKduzK1ZXwVyhcRmo9OFA3GW/MEk22GqHDHK73G/16oOd9SB62jmo5Ltczo3N/t07exm/sALotoh4JhEg9axzjgYozBDgDZSSBhAywFNn+LFGcz91W2XRagqzxzbT7MFRlVYv9lZUSPjAcp7Op///RT9nXI/3a4fSdtkgkNF5aR7FUHQ8qICRi4I8ZwCjGbrCMmTb+e2gtwgbo2ikNh9KBkNqaTaXUWuGBYsTMqHBb0ubOpADbXK1jFCjEsBRNWWQDItEV+uDDgI+raFGRFgc3jM/Wn7lEtKQHBOiWIJjf/KggOGNTZ3u9fenR5yDkyOXnBxxZmckHrpSMUBBtDhAmIKaigAAAAP/6cAT6YgAIAggT0LMJGcxBo4mjZMKECJhPSUwZCPEbkWmo8IlmACC/0Vi5bMo2zx6CoAqlGkS4nd6+CuWB6lvWy+EDI5hnM1saVf9s0c6lCjIrpeRCgUEZ2BQuOmzxz2WUeoUY9+sAPbbB0j7hYUwA8BTAbAy4GqXlEBsFOnc5EnodWd3PMDpauTdxvoFjqtIgeijf9pQv+2YnN9moMXV0/0IX23Ve30QuSpI2xdYRnxtZ22NedvTiU8P7gqE2o+fhrmpwmqRlFu90mCxrpQ08HmOcwYXNgwQLiSElvc12tK2NTQuB+5iO5RAYQSACC6KtsjGRJGxmHmdjOjI68rYRTJtUwHhQ2nEh2d1elv37sEV133yAnqeLA0RMiQsYDS3ZCPYLB5lyEIeWAaXkmmbWVVM60xBTUUD/+nIEqQ0ACIIVHE4bBxQgPcOKmi0iCYikcUNMGEkRGY5pHPMVVgHP/+KAQZT/ZMLTe4UecIKldWTMgnsKnLDCKK3M2RoumdH4XagwXj/nmSw5/6isOHFXEfJoQnYf7YzIN2762/uGH3AAI2k5bJBAvOKnAegZVgP6j0dSzROVfVhDaDVX/2oM70plAHmkBYRhfk2AA6yqKDZooA5sa+ncej0V8Uel4JAyW60VUEz4vPBEEv4NpUMv0tBW/kpvDA3+HaqrJ26v0IiEs8UD13AIol9zzwXSxCMCih0gQsA6ix9qyTEO0YpptV84gwKkjbEoVTEjBKqtKVUjWeV+PZkgLdkoctF/D6VYzdfdr6hlKrZ1ywCgapyQiYQok0NnyJVdhoyKxzrTV5OROJxxnOqi9NKYgpqKZlxybv/6cAR3twAAghocU1HpEdxBA5o3PSIpiMSPT0eMSLEHjqp0pIguAACsFy2RjA4YrtllSRCz7U16y721YOSAvbkjSVe26afr9RkdcKjia0nhYqrA4KgusylyhyT8sFEBNAuGVkMQGoQ/54AJJkiTFAHFtQgOcabl2hsSbM1OniNNBezN1OIM1UKzN/9RMWdsY5pww5sVzYCJE3u37V61p/0rSXLSJNoKGybaAARXKctkg5NpIztrYxjHvMgSHg2jXUNsze9pyLKtPtZstP+0cxQXOimtbMMm96GMaMKCiwuLmlE7VsDjH8BEBUXHoAVYnWiREnrZIIHl0IWKvbO4Lcu+GJaqv0Ik7uCZUlqjf0Zs4gSIB6DICx5eepSFFAyhBFsTG1qXOb4aZPas6qXcWUuAO1MQU1FAAAD/+nIE3LAACJH1Fk6bLBpEPmOKiimCB4j4kU9HhE8Y/g5qNMENXgi5JILIt4jJaVghhOcJUQGNBq3cb2Qx85n9qaz70L+WnrqrQWC64mPfljRxJNq+y+/qWtysYLSwolNW9zV/WAAgwUtskEIEvBCcK2mGBMGR0cNopNE/6IZKfonuEB+m8qtR9KjofgukjIInlJDz1MzCJx6dVhtZEfouPpspWsSb//jknXUz9MLg5TOjz5ljejrsCsjQk/LJmwkQvmm1EHdLL1k4+KaUpVL4aouwa3ct7FfVtWm/728zfO3V23u2Dg6/20+ggiJyXArQ1QfkLlCZpXKjNh7C2G8lBQ4cK58hf/vnbBnf4dCohoTJTr3LUspecQg8GqxVtSj19KqbmCxFnaLmkxBTUUzLjk3AAAAAAAAAAP/6cATa+wAAoggT09HhGtxDI4o6PWUyiMRNOGwwqMD2jinowwjeABRcKSyNjJOJHUBJPCPQ+fcI1JG04ikSfjyEI/hEY02dc/nD4wMoMjgIXQtYtOImjo8IHCT2r61UmHUrcvUpeioAAAgnbtqIIXjicZGlWZBYQzSCVx24ELDqCu6zKq10cqqf+SbQFC7ELjhZeuudU9km4gXRPHlIcIQAKD3ZVtTlLLtLAS//8R4y9kSQLSUZzEMkIIMqcAY7lPl4/vReQXypXtuOY4SDGX0lmMLAX7Sws121x5bXLvr4RnThkg0VSzSESmNZtY29GhSrJBg0lx4DIn0F4KRrWfusKeCQdtP10c23/ZqjB7qUpUqoDHnGygaCjHKQ3MH06iDr1MSfAaKqzCkJz4GWmIKaimZccm4AAAD/+nIEIWgAAIIPE04bL0IgQ0OKajDCRYhcTVWkhMixAhJpqMOVVgFf/+IiVYk7XMfYQ3xQfNAAycJWMuH+tldAVgGNEwn3/ZyZQmcL8ppBb1iEsESA5ucxddErbHd9tNO5KbF+kjQ/GAAIOUrI3BIJCYsCM5JYfEBDDZS++icPHTvsjkzDM5Tf39RCTLSThVlStf0v8uXMaEKsOUPakvLpNA4QHg4cmwGYMOQAACS1W5rZIKAVXIiETIweeN3N9wKQz+2zvUaCzQENJCi8IlBxdI0ydQQQUSN9XRsh43FVsvGOGCpJrb19d95wtqHPCtJyxtiQpEwfSAkOwMK3kWXrKGuGXSVll5hPGsSc30R7pQFunX3u49j484RRi7V4uneae/RpWLto2972UJTXGKTEFNRTMuOTcAAAAP/6cATrlAAIggcc02nlGlxBA4nmYSJHiKgvU6ewZHEGjmp0wIk2AAAIBibsjbGRcm/cYWRNl6LezA8RqrLjfIsYaCHPMEVWZ0/7M7wekAxQoY5uipTb3Na3DSG01L3WktSuoCcl5hYAwalTMSkw4eYGJQ+TIDSigYcVIyprpuJ5UIoKWM1u5FKzDqqP62WaokPu1lMiVuQL9wvLPRNnEOqK79HsTq9/NotVOW2SDl5rfBVIlApdbUQmMimIFHD48XIkGB9JHhNsg59EsUUYsCCjLSDJF6HuP4pC6zTnNSLGC94o4+RaxL8MJeTKS5IVTltkgkiF9Eb3SB+xznmkpx8jQjB242jr3RbVKMdafPGEhwygo4HBG/VL2hgqLMwCbQB6aUILlBGJxZw4X22vpTEFNRTMuOTcAAD/+nAE7N8AAMILHFLRrxC8QyJ6KmGCM4iotUtHoEhxC4nnTZMNGgAALKlkbYggnUspcpFCShQsnRqTPTwiyVRitUV3f2RHd5gEF7hQWY1jK6nDhqkXtBtSfTxRaCAsxqUJZe59vWtFQAABAqNpIZKbkI4jADosLFrAeQYmFGqM+Q4rq1KTUK489qeiKhFLlmnguxiFJvFmNvQoUi4pQp6h3vRrcjvUxLX6MUEARYN2VtioZEXajBDuKFDuWgrs63B6sL1Vvvq7sV3I2lt6mMDor5FXPZvO6KY9rlEpBo6eGq5NClvROUVo/e48MG5x1KAU5JIJksoSVotBgsClUk4iiQGDEZkMWaoAXCRafUJjI81JRctxR4qh1/eaWCebSKj6ktTdX76pMNlOtS44keeTTt9aYgpqKAAA//pyBJ/aAAmCHxzS0eMq/EODmn08ZVuHsFNLR4yqsQeOKaj0iNoAAag5ZG2LBz7jyCKmO4F/UT/OcgwhVoUEXKX/37Jjp+pxnJSlDJ9MTOCIVSyaqUsnW5j1XHLmcU17AwlavWhzn2vF1AAAMFQp6WNjCTO0103CXARLhp3nEp5sVo3nVXtMHejy6f6dAOzmMLr3udUNFbxKggHELjzZQFBIdYLta2j2567r6EVROG3MiSVsyieFqxlDMluDNYUMSuC9Wf01ljQ4105u+kXZDTmoJEmtpxZL2hrtvU0kgUChR556qR+w4zcyKdMHb/9hk/kQRacuewaZEualkNSwQkqg2yf9B3didaoxjugQf6RVThK0+H9gkcYBVR8sKijz8KoFhbrJbumta/GLuTEFNRTMuOTcAAAAAAD/+nAEA8sAAIIDHdVp4ipcQ8OaWmBiVYhUTUEsHGqxDoqnHaYNUAgAWmo3dbJBhJy5ciq1CeQQWIgjzKFML++jnRFdKa7UBy7kVBqb3rFXPexEEiyUAFay58LoR/+5zGS+95qdcr1gAYuErI2x7uodG2d3F0xpUbryDjnJgjNIXe57JpM14J/o/oKImMFkLASQBeh/Nkl6r3JNQk8PMIt0UrjB55T9Vo849QAADDVVFCDUVKFaiEoeYRETao1PVUNw3uUhH8yOcNYUHaLcStcR/0PZ77xSiFWNJOaDGxKHUDbCahUmPI0UCqbZ5LQhT//8UIKLEMpQAmLJkDBE2hZxXcJiEHe5MC0C9MPlgtqyHJ5AuUQnhlZGbNIY3j6SaLepQ9N5/Ta9qlqvX/Z+pG5FqYgpqKZlxybg//pyBMwCAAgCERxMm0wSsD2DKik3BwmIXGVLp5hIsR8I5l2mCVgBW22h4DfL2QImAwWYvmClgXIoBy7y8Xi/IBrD5VX34oqv0fVwSoihCdP1M0+JBNvZOcVmUF1Tj7pNhPWlO93//vcACo0/9xCEHTFAHclS0A1soeiI2zGzwkNlabJ7MENXvr9DiYsgCm/756jYc9vQtWt4uiBiQgDQQGgWN1d/UiVCXJG2NiBQqoIDaXsRJJ4H1PcsZmFojq2h0ku43/xDhUE7PFT14u9JJS1HHl2DzRMImkPGilXRFYE69j3GWNSkVpACAdttg7RmQIkXJ0KqpbsgVHtXtXYwGO2pOINdoAEb8JC9Ran7JJDKBADbz1QnfJuFp9q9Qvet19LVsKTdykXT4UrcNarphNMQU1FMy45NwAD/+nAElRgAAAIHHFTpaROcQGJqnTyjRYigcT1MsEiRFonpaPGlbgQAEXMlJbJBhFklgeBOHimuma9b2A1WVZMKkQ6OPqN7/rQlB/wqgIskHiNEypzM/ATzxprUrnkGV3+7SLkMklE+AAAQ1W5rZIOyFWsztanEajww3fqmK0v2EdpLmgx5HIiihUHDiwZJjCkSsXWSFHjm0zAxlFS24pcazBAx13+9xAABAgnJJIKYHdz0acEk+TeDPWAWRYoaixh1tLsOsYirbKuJW7N2YgFYRMPoNqy4cIOULpvKqPn6GMoS0Wfv2OGdXXT0ZIAFm03ZHGMIsEi/eTFGBKRVypHFx9CALliSc59vUmHHWQIfbVqAYWaMxhkEiqbr0UIrXGKVYvKgYgAR8ebY9OfvvdxhCpiYgpqKAAAA//pyBLZSAAiCCATUaYx4HD2CejphgjSIXHFLp4ynsREOJ6WTIVYEABEmGTWyQaBxy5aAscBJ7CEOWOWqOfsUOMB8PT4TRQ0kVMqhFqj4uQS2kk97zTVqUVkpJyW08Vcdtm7Ebza16wAITEet2o28SOqtdlsjCA9JYWhBdn/S5qK+wQzGJ9RakjL5R1ZlZEHwQF+i3R/AaWMDjAi6y54yXTjLT6q0CIU7I2xgb8KCeALsekGkLZ1HH/h6QvIDdSPCaukbdX/S1VExGYIoNpSdc2q2c7hLA8NPqEA9U0KhDPJctcWxiG7dQBD9KmCXqYhRE4zDhgw4yX6gBM2mkZjWRV+W3Dvbrxk9Ght56/rkJDz7eWPDrw++9ai5baOUzs/bTtUrYqiq9ypvJ2dSYgpqKZlxybgAAAAAAAD/+nAEvkMACYIWINLRuBAsQ2OZ6WUCWYeclUtHhOqxDA4pKQeIXgAITRekbYWGDcw8ZAV2xYADFBBbPc7s8fcHTVBhZGgv+muDAh6OrCTiAbJ0New7VkNqFSrs3TlQFxcOsTpKLUy1rKwAACC6VMUNhhweCe1MtAifIDnsigGkwptYkfmQpMnFre63sGH6/ghTpqLU8A0TI2m7uTfLK9Yyq2NhOVqTZqT9TXLKA2U7cEhw9Qo+g5hDYlcR0uFIzFSzFtrd1ufr/PTqHSk3mdztHHQSWAno0VRRtVFSEpa8wxQYaKtJp3M/FBDCckbYYbwRiYbT1I2BKStyXqjbr0E7M7Km2jv6+jo/FDarWgqqg+ZA7G0pdLz0WGpOGlFAsEUYELz5LOrr0oawai5MQU1FMy45NwAAAAAA//pyBHt6AACCHhPT6eMx7ESBqbpnSRYIiHU4dZKAAQmM52awgAYEABhmNx2yQZLleZYCgIUDXFOSwTfkJSG/vbC5n0IDxZ7lu+F0OW+80Qbi4CEayIuAI+FgpBwsNucuafNBKJLRtDm4EAAAMI///jIWTC9w9Ys5TAVCHWEsVf0WFQ+MI6g0p2dDxPuIQs9wdhY4royKmplueyLq/Ww3RTchD0Jdc5Tcav3MoAt6gX//+F0G4Kre7CRZn3GMaS0LUdJzWAUE/n+5eLD2GAOc66a1xg//+2ixR6d0hK1WNOs1oXe8XKKcyvPsJUsaNW9iO9awCC6VJlXCkhtCpBxA5Q8pcpjcev0EL7ruSSowN9nPrSes11N13/oMLw+4y8YM3vWKuu0zG+wVEdXInkW0IQiriyvUmIKaigD/+nAEXuUAAAImJ1S2PkAAQ+TqUMxMAAhw7WlchQARB51sj5agAgACAQwwwAEvIeJmocW0EDBRzYHgivmuRACX55Y0xo+ecGx8G3APg9A0ThkcXCQw79vL50tkmY//kmRhMmxWO/xxRP/1IZ+0CSs0hlAkjdkHFiaURb7XcehkEF6jRYe+IN7lo+AcsAhAHxHmhEE4AIw20UgR/0Fvk2WSuPZR//K5QLpsbL/jv/9AAQAAJOXcaWx5LOB5BQsGJeOT7C91UVSDDwFA4scJzTzNEP1IXyDPNFjU58jXUk1M5nm9v/06f5GflSryDu6co+T2dYBLkvHEqF7Wmgri3BQTKfbTz5DUqD4nmlAwA6OFjRZJ2VXRCj6mvpqR+nP2I2Pnmcznt1//Tp/kfKut6JT8hfV7aExBTUUA//pyBBrtAAACFz3aUakWDELHu0owwqSIxQdg40RH0QIiLKi2ncogMhAICi4Ka1nkUy8HRNRwXDaxwleROXTWsZhnU8OUlnG3IVeDh5VCJgjG4ntwfVeD6Pyc3+/L/+z6k47cpv+hYi/TgCKALLt3GLd9B2Yj3nsC40m2Lf3jCZ0cSjul4sz3Jb2M/YVv5pKqoKfHK3Py8rcXzcravy+PqJ9u+pH3bUV5CR/rSiAAlSbjmc6wKxgZHLxrFOQ1FfWOW2RhLKY2CvPssmW0G1TUC69E4E2z6gHJ76F69Opud9W/vwYfgLfoLcVf0xeKVuUyBKEAhABJLv470zRjrDK7YPQW9Q/yX1BQAkqipnoBXck2U6kObxxtSXO6N/9P/1fKj3ObTxo2BvT35z2Qf91cYyd6UxBTUUAAAAD/+nAED3cAAAIkPdjR6zrkQserKjUqkIh5CWOjPOeRFSIsaGac8hAhARIVm48sm7QqPSassaouW9UfvyZfQhDCICsFjGLD6vMprfLfDPR9CHN/oLf6voS/+pXlX5v8q2pTjduXqq7W0J75JAIQQSHbuLKSIJybiqyCIxxa6jbjw1BRCSFFgY3A++VbI30FHO5T2/jIUc3n8i//MOyo/5rfoMeY+heit89Zy8Cu+sIAQEEABKXgc4qcvBwZzueI3Tfyoz/s2s6cjagYYRESzws2V8vxnu+CgV42fFbaH9/f//zOOF9X68wtlRvxJ/qOYecQgBEAABO/jplUF7LBJE5iGMg6n4y+S46KSMBEZUAjbyr6E+nE3+/PLczt//6NsNvbVdDnwjL5x3bsmpPRMJe8mPbQ42BqkxBA//pyBOlYAAACAT3ZUG0R9EPnqxoxolyI0QlfR6hUkRqerKi1CXMgKQAAFbuNYIoOmCjQNZOKOcHjzHizF5U4TeM/XqPxPV9B+nX05f6m/+gPhx9W/mGwYbh3ulN3Ws4KgdyTKY4LYBgAAA7NxqyDV0MugHaU3D6n0+MjWJqa4sBWuoad/Kz/Nw/BdG5eFXQT36+/9BtD9f8VyvoL4GcvFBQUaYUWNFNaQ6kgFEACFJsO5X1iDdoCOPY1WquPZb1vvt/IUM8caalc+Bh7eLymgrJjwU83B/683tqN/9G4R/6dW1PqCFt1oFyB7KtuJa+iqAIBAAXb+NsNLG3IRa54VRTqX4n5wsNgiCl4iunF76n4z5n0F83IPo/Tg20/36AH9OpeLHqgEYHXtts73/X70JdXVfNs3dkmIID/+nAEID0AAAIaOVhQb2m0Q6ebLS1Cesh072dGpPIRC56sqDeoyhAkAABNm4xqWEajqZq7XDl/Kzcw1gRpumxMIzY72uvojZz/Kn5nzD27//v/9Rb0n7/zDE1D2pn7V9gwTjnCzKwukAJQBECAAACs3G7Y1C3AJHpiiPuIpI4mcnLJB4W9SXvyvUWORcG/N/R/69ff+Uft/+TlegQ1+8FT9fzvOOf3S2WO82sV9EBKAQDNdxTOrcym4bJ5lDQf0SBy/xNR+8WDb4iPq+hHjXp1D/9tW4/1bQtX0XrtHP/zuo3w7mxNvGLyZoZQsyDqLkEYAgAAKcu4yuOxOSGasISBtxzjfg+fhWCPQk6+2LgWea2DI7qbyJtH79un/6Py3R/0P5xexQ6tYsenPdBNKSVQuVWxCYgpqKAA//pyBOHbAAACGCNVGTlpcENHezoFpzTIhQ9pRChLWQciLKg2nOogB+cDhhOYIbGXIEUD/2uJ+YZ/MO73+tQtcyf1NmbvM/DjiGyja7sW5wpPUB7N3ysmdPnW1n5R6s/8WcWq9MtAG5nohDEAAK67izMA0QfHLWKnygt5biKG1jwJcb9OUbhrvwqX5nK9zNSn8o3/shmr//n8qX8d//Nzv8/9cjI86ri94FhaOkQNQGAZZ+N9lnUAG0sGRbQb8Vmyg7lQpBq6j99H0EcB6dRPbr1N06N//5f6P/R+eSKGoRzuoXOEGYOMz1ydm7nn6u4zmAcoEFPTYUrkD1DbUyhjFDOEO8p6g6C5bi5ZUDlt4g5UWcU8dO0I9upbt0+qKjW8qMMv0/06PoenVOiuW0bIqjdMpiCmooAAAAD/+nAEHfEAAAISO1YZbzl0PuPq1w3qPIjQ9WTmpFgRHh6tdJOV2gAE3IJMEFwmrZHgIJw449kB/d9r5DFv/GTf52AWSCoCxMVcDAKtOBc+Km78l0bm9/8//6J06f578la7J55Sr9BVWyEgBBbtG9akKIG4w4zDr/dBV+a/MACaPCBbL5qAEWvbBEN8hLNiM+Ubl/Jddf4lp/w/AmwpVQOpepoo5q62zIKCIZpsKKlmFM4EmPtIhG1nuLDk8tycMtJo5/5wp7LlhbcEQc+oI5fb+A++ptLL8ED/2/lfnsgU2baqlaHZ9AuhMaLhp7C7RQcRBICn348qa1KWj0nWAUGt51bkOI4MRCh/BL+MfQd/Cm/m6l7fyj9CETZFYNYiNxn+GOsUH8duPhw4GsUGMDZ/GHR94pemIKaA//pwBKVZAAACIz1bUQo7fEPnexotpVjITQ9m4SDg2QgZLSgWHI4QNgQE1JINoQ31A+DYKBj14/bF4nrUWuNP6idwqv2w8BBbd8XltPO9///JcuWqi1Y5sk7qwTJkwo1bA+aIHUJh7nbLYpAEgAALl2HUXpUuBCdWgfC3WWcp6w6o5wh6gnG8KH6Af+FNr43o+j9v//R9E79OiceP1n6FxjeaosrOtud9FetKXc761EEBS38RoQL6OAor3DuZxY+KQatSzYa6dU5T+PBvlOrZQnzOj9P/z+O9U/if10IF6MymZ6kHgfG2g0e79HwN6PLa3gGIhJJqkFlR1oKvJxN0FvFnEkvw96Fdxo+PJlQFdOPNyHTlH/pziC3R9UdB4kbkerPzKQWlVanmhCOnFldyqY6fcmIKaigAAP/6cgQNrgAEghQ7WFFtE9RER3s6DWc2yBD1aUQk5JEGnazoM5TiABIAAB23DM9Ew9wDRdckGHPUNeOZskA1nazPlHnG1o8r/zdf6CuH/y+vNqEXVNfZFiSaj7EIjLvLpHqg6FmnkgM4ugJgABHd+NAQAoOKC8PgVFnmBP4b4qfKh/fndW5HryZ7xMF+RfKP/+3/1bXStr+dRC5L/zacOYtavm710FTZt0mlbPdsCMBKb/jSUCvmArhiIhGOG+O8DA9amc721fjj5j9ento3M69P/bN5FWNZedZC4cymlLoUWsDFsTPI2LdYeMZEkpTAWZduO0h4w4DDSULAhoLuHOhQDfCxbU3XjQyoQDnAj6v36d36d+rf/VsTK9Xf0jTI8VF3MruzXuS1OkXPESQZKpiCmopmXHJuAAAA//pwBNofAAACETBZUW0T3ELHaxok4m6ItMFSZ8ipwRUerGizlcsBMQiSCy4N6sD/ewUJONFa3qpc49QqCdIn1i11l1sinsrHpslyp+P/toL/r7f/iPfyWq78ghUHBM5gNMOECi3ixM5ACJACT12Hq7pK2gnJ2Li3qS4N4SPiEzi3+VGNBH424hGdT+V4N/5ejlruqskQI1H5H/UTzDqzKigopKzdcYM2cuXWAyvvxz8TJ3hfN+7ClqJawjJV0BNHzA0qBOhkDA2UU1VGr5GtoCoskDlwGY0LCXD/VuDd/5h6adOgmQQ+y6GKVK+jmPT+qAIgACpb+Ns5ltOB5oyaLy31W8M6AY2FjuD/1Hagbs2Fj9U/hEfycz4x//jG5//ycRfcfRCmqcZDj9xLWahLpYl3KDZ+6TEFNP/6cgS4owAAAhc72dDnE3RDx3rnGaU8iGzvZ0McS5kFpOxchQluASJEhOffiquDGeIrPBV8TCvjfioOR5LjZv0Xcav+OgjyD5jak4X23N7Z1dUDWpshaqSk3QXnRZYnP9BiAo4URszGLggABVtw+ucTFwNDsk6waUdfidbD2GxSjqOoE7dRXhnPyvxX/ftydH/k7vsHnREMlNEPw8PezqvpU9gRCySBB0+ZLHhwGQnIBO/8eucA22DsriEvw1yxepwIo7C3gtX+/N09e/BNz9Orc//4/3QMLRp3TdIQEmw3nF0MQlvup9uibsp9H/g/eoUgEFqQdbiNLHgP7cMRvq3GuoZjVEKtoMeGbBnwQHxL6N36dG79+d//q+i83vz9XqGpcs1meput6PYc0LobPsXzCYgpqKZlxybg//pwBEV2AAACDjjUGw85UEHIKyodYpGIaPdlRBxLkRKg7CiVFTcAB/8DTkvA1sINLpoZ90icEiHvHX1Z68hTVEsiiDxx9QCnsJ2x8X5QAktiM2VM5bk2x9P5XqX7J7RX0Tp/jzyO76YAhCABUcguiixDyQkKcopBvLclwqJ3tVQbAuram6cF0LydW0E9OFfBk9teFTf29+G6EUzGe7ahjrXiZjZnNNqtMqAzFJAKbfjOGSCE0xmOcdD+GeNeJxflD9Q7+r8fr2fi+Xs2o/J0f10owVrIvM2jaqzuEfieLhlSBWB2tYqTSQFFjYumAEhQAE1IPCqQpRMBpotNBO5hPxAl8VQ6ps1sQO41uXifM3Tn6+/CfV9V//L79v530eqAK/KaYc9ZlG6Fy7uH+GLTB/dpiCmooAAAAP/6cgSJNAAAAiQ42NEnK6Y/p3saBYcWiLzzYUawshkXIiwoo457ASIVgF3fjuSUIfdGpZpQO/nFec+NQ9aF2cYAZhGvG9Sf0fU3fjG1J/t2+kn0uXutUBfFvi27PSUGNGPvlz2eafR/25/ZAgFAKe+4jKq+jIpidsOA3+IExMHrjPKdepfjT20DHM/25n9U7L+hCrCY+aqvXRCAyeg8O5Oqmnfrw8tZEqsMgBhGQi7fxKamEi9hwsmTg+FLn7zDUNARbdpB5u3G9gFbGvjH1bk4xuX+o7/4z/P1fQ3JsNARuNMafGref78dNvDn5pLf+kACCILlv4pNJCsdAcV4uC1dzdQX2xcGswnxW36pyfM4gCFKHNo2UJ9+vt/9D/6v/R++oJYmKOVeFFGavvpve1Xtp1KqsnExBTUU//pwBFGkAAACGyfUmw0TVEQnGtc9ZUbIlPdjRZxUuQCdqgzzFhIABN2j3ErtJYBzVghNPduFVj8bnTbURXWoCmGxBTpMyz7VETlbYIRwj4Ifk78N5ODyX/neXloBNJ80SSZ2tutPsQi5EKkCFbsPXNFbBfyDjZqgQhq5ef/DQfiz8eLtAD+oZxXhQ7Qf/tobv19enbpTJ17aRr+OSIpP3Xm5GJ71/qe/xfddd3XAAiGKJccA01aGmt4F2NgRho7k5+p8ksIqRTNAy2D8skUtuAe+c2Z9BPLw73BAO4J8nRq9avTrd6W1DPQQ9FFPz2pLafQQCVJB7NdCuXEYxQACXmnFOMb5lp/w6lX8HPyxVTFBmhNqoBdA53DNQG4pxo7RuX///v09SbdjYwXlvY5P0ez60xBTUUAAAP/6cgSZ6QAIAgI72NFnEvw+xGrKIwowiHj1ZUUw8LEiHuwoN5zOACEMkkxyDUVgn8uAga6jgZ25XicCpyj41ucG3x3z8qBnHWw78fv1fv/BdHdvqb+R/ym3HFUO0NODngNSab9M0pAMACAFZcNRsoGBlkJJGewMPsW1ANfICfQTDnQqWyr6PyZtW5/f+hPLblNj3NUJP8/EFHX7cCpGUkwKIhm1DCPZJVcgkKni1sG5zKJYm6NylN0SVd6uwWfZAp16Py/AN78Tvqn9i/9+c7smpyVdGnMf39+JPZ8oThlaHrd1Fnv8fc+jAJgkQVHINrkxDifyVsChfM8LcoGlcQAFjyRszMOl/4i9WyoJdujai7o2raHUmWt1flf8/UUtdB+sfg2hC4CigQVde9t7HqYHUxBTUUzLjk3A//pwBFIUAAACGDjYaQw5pEIHOwoFhxfIiONjp6RJcRQh7CgWFFIAAIEQoFO78YtuFOFAK7aENVM7cVkSpQRn1F70FP++gu68KvqvO6P35v+/9G/mb+hCl4mFgNeeWQaloGWZeLvEt/LJgCYEAJRyCjft1oBha9wgWjl+K2xM/G3HU0fTxG6NgaARp7ltW5Xq2//8Ybbp19C2g6au8O/Hzb2Qc775FH8fqf/UAAEDLQILrkHi4tBz7DCnkXB3fqSP/iCrgca+DZrm0CcGK5+E4PycvOmoLu+FfVzWq2Novdv0FysWLcd2C1SY40lKqlOgDeJAF/fi76H+AGVGUzzAcDcc+FAiGCwRkh4v9+I9G4L369T9P5hZ3OukiIh7RPo+yanHaPIxZEe70PQZLBR982g3d2LTEFNRQP/6cgTSewAAAhQ7VRlzEeRCB6sKDaU9yKTtX0G85vELHusdAx0TABUuoxmQTgvLyeEbkQPRQo2+f4y+xm64/oLs2ZsG2oLi2wgjQLzcG3L0//9T6M9WbtuLeod0BSNS0KoaJCoiRru+uUFWiinHAMoLdGcFCePWP9uYJVjuIKpWXVwBec71Gh7nfcM0F3wFfZsLAz7kwod/9vLyv+69myC0hRZ+u1MM6U89QAJzCADZAMAihJQAFhDtqKgvuHC40dlB6+JyzLMbK9W0DPN5Ru/8q3/yDVRmMdTGcqH3RUU0V7umVCyVGHKIG6jDmRK0fCoAA27C5YTQJRywAtFFKbH+2DcbFsqW1CRse6vq3ETq3IdPbt/+b9E0yXXrvfjxvWkg+DoAh+XKLn/fk/j9a+n/r2mIKaigAAAA//pwBByEAAwCFTBWOeoTfEJnatc9RW7IRONUZODmEQgdquj1CboEgIJibHY9zLMlGUAbU1bLhLfLcQfGHF4o5Ok4al8gI2nAr7vhWHP5/K3G/yf1JYIKaj3rOyKZtbVvLH+tAokcga1cKiAptgPRqo6hTaEbV1dytPmPxJL5ZsgDUkeMej/xX6tiMDTzW3Lan4r/MK0nKxKq07bHfsSndpWBzwb3dvx+n7lUIBNuosYTYXLqCcWy7pIwDhnQjOURNAUGEqHGPB5ynEwzqPczlDdX7dG/+Vbsmvct26v00LUvgkseHSaBO4i0gd/0gBAIgCrqB9tURVscz8Aco2maIP5PoBhJxFw8ArjJkYuso/8RD6Ni4T+R9W5Ob3+Vyf1NqZ1zttcgALIDBCKnOSN0piCmopmXHJuAAP/6cgTR8wAAAiMw0ZtLajBDp7rHJiI+iEj3YUG0R9EQnKqdhokzABdoAuKqvjYb5ur/rDG1iIHWwCz3tMBPvYatgt4olSjE1QcqEta62w+BnVJo4dhxHtJPWPDpPk4jalc526v9Z7dZxFKQqQAd2w8JISIZmoDXFd5KDa1oaxzzXdtMTIrHkjA/p08W27aG5P6Cf6Pp+leondE72drkDOkw7WD/yNrjENuPLnlWkRUVjYKm/4xQN2R1AwTzSe/P1TfkUnrYd5TUhCy/oPoAcHw4+heTnG4vryH0/ylXH6v31A1xVZxz02ycR6MUIuQiqqARIBmwH3a9E1GzRzIVTNIuHQV+3ESWoIoIrUM4xEce5/M2gvl4MbRefo2j9f8X/vybJZqFMgpCyC0QCSqRuLViSUa8yYgpqKAA//pwBOeBAAQCDzrVuTgppkOnKwos5XzITOtc4z1GGQWcrGizib4BAAMtwHEsXmDkCQUdT5bjsj+Ik4pwKGrCIcfFOfjB+gD/wsN6c+oi2NGWmbGH/vxB+vR+2cZZBD81Y3YMlI825uCARmIBP/8cKwT1LXBg67EIz6hvB8LHcm+oIlsSP9+Z17hvr3LaE6f1Sl7/MAP2Uj6eEh9iA0Bc+/1Lc11V1+9+fd/dsFAp3fjh2qdhwbcI12FPJeX4jPw0LUK2r05UL7+Qjui83kHVuv/09H/o3ZK9h9ZZMDzlwKfHSM0Qfkt4ZGEPW5p9u6BgUgEZJBqqpmyVMA8vsEK/8UrUTjNQoFySoE7f24u/iEHXM5Utof+vv/WtDfojI9mSccVQRLvWff1W98rllqHj0xBTUUzLjk3AAP/6cgTLTQAIAg4419DPOYxDJyraLecmyJD5XUG85pkQnGsc1JZKACIAgAxuDoNo8/rHg9OcKDNEHeW482JgRLOhfmdC2oItp79/5T/8q7ei+f3vo2i8jQdstRWxupbGjwRccVWpoZBlAEiEAVd8NythCYj0NelbFlrzj+BhK5gzoNj8h/VuJP8qMd/bkuf/Ul/6V7poWc35pLXMEzTEECV0ZQlDbX1b77v7tEQwS9vxi4oRXQOpmrgzLd0J8luKWwUFo5QfnfQtofz+jczmdH7f1f6XeYK7Jbn6+iXYp6jjVs8Rw9mx3O/i0benrfT8gKWAHdsLMSQl4/sWg6C6fh+I2sj8e7ZweuZQlJG8I7h+/Abr1Xl/25Ojat9tenEEPsl1bEg5XY5jn0kVgA8iRQZACdqExBTUUAAA//pwBPidAATCCSVZUYMrJELHWxMkYqKIMOti56RLkQcS68z0ndKAAAAAptuJpixSxCZhK5DGQK5WAGwg3RcrChfib6op0Yj0Jq+QUnt31dMQJD9BRxOFnOygDLswIsPLf/t4fnRStIAUu3HmkgE1rk48sq8YZq5ElWoe9tpbe83OhzPeUX6PYRq+FCTze1UTCEzu3p+fSvyMc4frVSQYGdE/+nlwowwAZ9MAALt2HhxTfc25qI8qNnNcHDEVhsS+RC/pm0sUikaz0CF+32kgrweKAl+41vl1VczZC/Pq2V/VW1/8uQFD/qf4pUAA7Lh8rx0l4bCoJuneQ9bRDLJK1CgamdMadE5b5p0VNmFz2JoVTHBmgbPlXa5av/vlC1CJhCwwNOVoo9bhNT/+lCYgpqKZlxybgAAAAP/6cgSCmQAIAhtJ2JnnEvRDiTsqPSc6iJkpYOwcS9EEnqyc84oiAClu49oZvRthsIIu8ApdEgZlVxVqXHyy42bAfy9BLScXjQENXxhpzL34UVlLt/1Lu/b5//EaCcr///bQuDEu+VN7A5AACgAMt3GL4nYZBBEscOiLpGPxgEZtiHIAepTZoScq9Xx3R6BTXjhtf+pRpQ/P/83tp106/pxrqv+vrSnXUai+/YVQEABy7j8cF2yDEqiljsXGv9A8EMTok3VwVUpHEaoQY49RVj7zAX7Pn//vhOv5O//RsF300fQ279PX7fROLckaYUCqaERkBAAXd+PjvE3YNal8EN1I4f2GvNa7fKh2koLXwp3xVfmgf+Hf/8H0y/gun/cRgn304Z7jtUfehc4uHSyJ+ypTDyExBTUUAAAA//pwBC3vAAiCHjzYuecT5EMnixcl5yqIlOtkZTzjcQYebJz2iPoA5ATuwHtkk0Og5iGLbGP658xdeqMRhs24ih5VSgvagEWlHxrlcuDrTUSnlHbv6YJs35++nV3VsrIn8DVg4iwPp95eVgAADLuB52NiiyBFJDhDrjYm+Emf1NVkoGc+UD3wX/inVpwUejY5p/1R5QWtOO+nfv3ubu+Z31L1ChsqE0Vjhr2yaCQAKjkFoJOWDfIlWwRaNHzD/akC8TvRxQ/Db4lPQ+hJZrROHaajBed/1Fue2b6Oun9XROVmJ+pmVI3bnPreMWIEELNh/oEAC9vx8Srypq+IlisQy5rohlePjsVHVUM25KPRsFq2DanR6f+2h//1GzE1oyJKP/yD5B5RRtyN9DXKMiF9rbVK6ExBTUUAAP/6cATk1gAMAhY8WJnpEsZD52sHPOVciIkfYmC84ZEOHGxo84oiIKmvA98D/eZBJo1PLo9cA6f2wOFdyBT+nloVzpp4MVrzr5BteQft/UbI2Re7iLAmzNMjOC5qJ/6j4MXb5FA63/SeLgAAC7vh6ysJj8ozxfSBczKWxAB9xKCV0GUYvUi2GmlOVuVa4npxMfR/+bRNP06dtci6D0nfN3wOZYKJSHyBdlmSp99IATu/EGScKmUEKj22GHqfSi17DXNhO2Mj7LE5mgZ71Ra5gxpyD0+n8pz0++j6/9V7V6GvKps1E6qypZu15CJk7nRdZRUACCAFTb4e2XJdYGy6b4Qje4MX/ZTaybL5JxRh2r8HuUfPWVaYCjUfCJ0/wugjcnunPZS6jYrP2XXBuEsTJTp1Jih1SYgpqKD/+nIED1AADAIGOtkZ5xx0QqSqs2EthojFIWBnqK7RAx5sHPYpWgAnv8PnB/rd06RcRtJZVsadddoVrSt3UZKykqMcPyj0FurThL/J9vfy+d0+f0TfvWKKANaEc+w9Qikw8lalbOugACSQD7NRkqLOBAeKrCNFEQqW7M94+LKPwWI1/Kdp4aoPiifoia4eP+gEi5XJ4fdT5mj/7KTeRCVgg9+qna1f3Q0twAN24HtKX5isEIFVdMBGaJhT5wepNEYJySopaVxrwpMZoD5GjQC7Zmr/xENuq1L+KUjnkornRHoLd/x+MDcVpm9kbQn3HGggSE9eB58Hkhd0Sea21BWza7HNRb+Asefj8LLMDnFzRcO4E829BK/Z6t/00P1/IHqxqo+7Xf9/7Z2aTTOpLsJDExBTUUzLjk3AAP/6cAQiogAMkhA6V5sPOrY/R2snPOJciL0fXmwkrtEHHaxcw5WrAAcvA/HSuWzTBIyGspUPLo0lEn7wPNsmHB7W5XiqhugO8eao9lMdDm+a3/52Y+379v/76t9TcoZnSmgexe0gSvVEgQCFN+B9YaFdeiWYcDP3HMoJdQJm1LleUH+K84vgs01C9thFW/5moTa/m5e+Z+t2WXR2c4zOEHWG1gJjiHiqSCndwP71X0N3CFCoJl6Vkbmn01t8I7bwwi53Tryvr77AS+rYqlMQFdm1HT/J4rjO/5deT9XeK6l347CAF2JVMtTavep9h2AQZN8J4w5CeE5DARWApRYlYLScqcr7g3hB+C8xinfVs7f/UbjBXM/70No3PYeU1lQVvNKIOpAiZsbNLDqT6//qhpiCmopmXHJuAAD/+nIE5BEACQIXLdg56Su0Q8erFz1Cboho8WBnrKvRFJ5sDNKjUgIIA3bgfXKxHZRwWMKADViQ9TduIuMiZXp4w9b07dUCsM6Ij8wpn47X/iYaiCzb29tXxeXQZaxb2zMGnCA13PrHC9sCgSp9wPTn4p8JKGp2ocNMSzPJaBgTReJhjnSpFxbahapIdKNYSGq2LD//V3gheR+zBHqpphmSBSpmbN/4HgxP775I2QXPnBC0IegbyGs66EDsqQM5WBqspU5owhuot8L//CcWpybDr9G+Ai8j+vG8uqfY2pMrasp2S6VZ/Q+NFDRru3ScmhgBU34FahNxKnJxSKEC2fUO16yHkseyMpztZB5BfPaF1NNxHvVxcm/+oE0NmX2JxDrqVXWOUizK7to7jBQbY2zegw5UWiVMQU1FAP/6cARlgAAJgg86WNILEfRCR1sHPOJsiDztYGecb9ERHWwos6KiAAQhAKb8CpYpAisI5iWEUBKWnF8ikzpRPVOk6vUr1R3VCahkcE9QLTinp/1TCPl/L+r7Kyslud9HUCqFcoxU73OSAIAObcD4yujE1bCuaxwas0TypTCpriYanOOUN4laFsbqcjWEGnGLQf/U+CEZS+dX11fpdvSt15HkCG0CkWSpm8P3EBz55sn3sMUh8bQeZpGr+VK55SriekTE+DnRsW571Lb8eL/26DGKn3/azgvqfJPNySEo9QvB3gcdIaNPYCSUuNWoCEgK/8DbjtIPYsQJQAr42X44X5NuF5XJ9Rb1CHRsWK3OFLU5hf/6kLiAJ8/60uo/U7u1iPxiwjTN6bj6kIgvarOG9ucTEFNRTMuOTcD/+nIEr3YACIIlOti55yteQeeq4z0iXIiM42VHnEtREJ4r3PWVqoMAEuSAe2UObNnINaKvF/3JXVgz1rNcrKC98HWjUCew66DD5eJPS38YKzOep0+mg+/S6KjN2IuRoK9RC6kh01KtWFX/mQE7uB7ZOIeW1JpOXEanTEDFnCPdTP/movQ7TLf8H2l/xfxt5TPTjm/p5dB9bebd/9GaxuZ/W9Qr2GPgNgjKG0rhwkCEpv+PrnCleKYqpLl63I5wxic6spShcvQJeWqW4YbRsbdv9W2H0XrLp0vLlZSn0Ojmqgay8kFYxepDGnnsrWmwlesYIEObcD6wcJeKt1HcMmmmthm3jKoHhvLFF0uCev6Hw7wTeFR+IN8AWqOT+iaE0dvY+zojc6KO73+1ExrRF47iI6crHhhMQU1FAP/6cAQF/wAIwhA5Vpg4OFQ95UrzKScciJzJXOeccZkekuvM1KiaAAduovqNTT7g6xvJLHVIcm5zscGMIGrSK6hovglq1TULXuBd68/X/+UfN9a6Zh93VaNmJ2boTqzodcRQ9zxRc3/pABu3AvA8AuSHBlQb42Fd50g+EyDjhcrUPL5/0GyFlL0EP5z1b/R7iAf5/WUgRLLw+lT9tsKCURoVYQlGsixACXtwPqg1EKwPoRCSRQ4X8/tWb3QuDVlYTVOL4w+mWQsVaFQMWvPb/6dH/wmtBHIZ/IRiNpmS/t5bljs3sFJKIYx2uxwAntuKEZApOuHBiwNcPufYIXUMyvFzs5Us+bq2qGq8jEb9Uq3p/QvVzw5t4usOBsi+Cj0GCiytiZde0WUoBEDQeIohkikywkmIKaigAAD/+nIEgH8AAJIfO1cZ6zp2QmYbJwUlDoiQ7WLnpEdRBhnsTMEOYiAXtwPeEQsxKhjipc7CayBX9IZzjbHrRq9IGdqKMqK4PxSLYFW+rV/6dX39NjKGHt0ITTEc+r7+Ocg/FzD4voXUuPcsA4Auf/idBcUYXZb57prbZwv9FjWQKbir4k1W2j7xAJtVsaO/p79N29Z6zKrNjjkSou1ph8apIcPWVsYgrEZz1K8GIBAFTfgZswE4bTfB9uJI056oWPaXCXwPTnKKRSJFKOo4ig1TaMoIXbeUDr/0JsOp2+nUjX6NQQkS5DtrwFiKKxddDfxcAm7YqCATRzAXh4uZjQyso+5tODVO/0AzJOV1CiMbNNaHR2Vj5//4AHpVHVL5lNqy8UnIyYQWFRNcTmC9/Qhb6noTEFNRQAAAAP/6cARabgAGAgUx2LnmE7RBJns6MMKEiLxzYmYlp5EbHWvM9ZXagAAC9uBmzAbqWHwPlBqOO43ozHHGlOD4TQeuYm/HBYpcgMN42JWlUp3qb/6vo/8tEdSJbrwjyWxuSoogle8mLWQAAgAFN/xtkhiRwKDijP40W74fvcabJLRPvZN2qxG4sYzDZeyyfzOj/9O6otNUS++n6DXE20FDqDm3Voy0WR4zp2/E4mKh4Ow6BuAFxlYPEYLWkDP7LfloeP+kYnM3hWrlbVOv1lvvM2+t/3jQPnBWA5NnTRegaaSUFR48qMqvDCUtrDcRtIAAS7gfCSYEQPUSUpxXXSjxI/EkrIo94YrthexqqGQH68VhaEFcSug5SBO6N33/xr/KWrV369t8x2Qz/NmvFKh6YiC6SqitCYgpqKD/+nIEsaIADIH7HNiZ6RJkQWZbFz2CaIi45WJnnE2RFRVs6MCO2gCXtwPpvLyoSMmA5FFGXOjSAM4cf9Wh/oeP4cIye38ifwvq1oGALMCHqN9b1v4CXNmi2Iot66bmsFhK69jWJeAgACfcD6uhKEhtqhBFBDefFijy5KnCp8u2henItHKPRWaz1CqkacJpfJ/9W+n0lPZdHdLOc48tbP6XihjEgZ8TqAKm/A+zPRyXcC/TEzylm5qbHKUL43P4WLuJhWJxMMyPetWRlMCXVHhxNG/wSZB0f5v17XSgUuz7u5qiDgxR1qWEyAiekVJEAGf/ifTnRbCUJlRPXHt80Or8Hs4ZOZXtpMES8lRypik36qj9yD/8l956Kw4VKio1hBTF0mhQQPA7zbd7d9TBbSu7i6UxBTUUzLjk3P/6cAQAAAAMgeoZWBnpWsRCA6rDYeY+iLxzXmewq1ERmSwo84mjAKe3A+TtVLUVT50XBhMhSyIfdCe7yHlktwsdFIpOxvvj9aai6cXxas+a9Ej9zAjkio8qPFuZU6EQ8pYEQkAB2gD7zVVNWlClhGqTBeqkVbBJAASdYafc75sTzZcevVax9H3ziaOWO7mGT+yRVZzW8JdH6SjGKngoRJXirTf0xcAGb8D7iDzQKvTsQsSmLimE4krObF07l9t0y9Ayeh0o1ar1dBfumlVyhqzHS4uoyXjgeUPI50BhsPPCzYHFBXniqaFOY8qYAAV34Hu1msgAtQjhiFlKdEfGBYgnhapp87PlC4kiYZhxsGPh73qRO0zbenBk6al717v+tRbUm34ZRxr34e/o+f/tWWTEFNRTMuOTcAD/+nIEnsgACAIWI1gZ6RLUPYY7Kj2HKIh0k2bgMOFRIY5r3BwsKgQJvwPhIIpXolhySGgz0mHxAX51B4uhaXpzNXhoWoQP4N5w0daHF9dR//g1k2WIKMEWtjAaFX2CrgaCYZHHwkLfGa4ADQAKu/A1A0qGMRdEGxm0cuTgMVNn6su6l6Eh1Hgc1XzO6RHfvcN7f6kefaya7u6J/SW1uqUbQyw3tp2mCBJX/8CchqmGqChDLiFDY+sLXrVHUsdoPloqaj2Iy6VKgsWivUv/8xMoOonkqdKNhKTo0sCVvNXNIgs0VQ97mnNdZFwCAgntwJ3F7woUKiex1/aqzotzNT66gk4OJdMMS7y4nt8WGUD27klsVVvcQH83okq/q3v2kUOShYVBYyZ9KVDnBQ+MSBBs1KvTEFNRQAAAAP/6cATVtQAIghUjWRnmK1RChssXPWVaiEzrXuecVJkWliuNhJWiKDv34+G544QmrRzyENXMrGfWNP868LLDS+4JV6DMdHbevdgX9Db/5B0rc5S4IOOmi51n3uviqowewybFsYEF09O3aAgEGb8D4kQhUBmBVqldVLArcxEjXB78qbxmLOXnlVsiGWdto9XlC+uo7/6E6f9dG30nZSLQt3ccEXtoJ3VFyB++9KASZ+APVjKVWwYbggGoNM7tPy52XpPhup4jTDTVszso0aIWn6ZcfOQVAmd2hG/+bx7b7lGOyMSS8pns7XNY3yJBoXBDm2A/Gaa3iI2CALbw3JGjulzbB6OgVCIDsemzoQqNuEY5xEeo5aGVOYJWVmjhN1Hn6Nofv69nSwvIIUNg4fYryJn5kbQmIKaigAD/+nIEc8IACMISHNg4DDhkQaSq8z0ldshQcWBnpKsRHo6rjYWJagEAFT/gBeqKKQVohCK4ARBgaIWoV8Vn2GSodMJCKJi1Nf1apJM9kFBajlWqe7DKJ+1pxdgoSJKLjQi5cLR0BsStA4FK/gD25gJsDXCQjbZYI6j1w3H55TE2wqb8X8Ob8sVi3Ud3PSo7UBSijMWiATt7d3qD6IGerdK2V+ZsTTLz8XxLqAkzfgep2wWCOYLOXWKFsuFlgJyn/sZUX2LdJEdcMro0pEsyjQd9cY5ng1N+q5LkrWMBcuKmC408UNipAo1iGk0WtSAFLth964yGDhRb7KIzFVUD0KDcQeoRdwKOhGFCFhzUR3jjPzxev4d/hAGl9EjJpVSujeqMa5aDNA5BoWjRZO8VQ9tVwpdNaExBTUUAAP/6cATzVAANgfgc2BlpKtRApZsnPSU6iNjJXGek7RkbGevNhYl6BKm3A7WCZCiGSQuLCCAHItrhLba/RP97Z5JKRcu7nd9qFRkaIgB1xjK9dY9Da9cTT5p1CKEr2wEQKKTNgZAAAIETf+B93T1R8CfJbrAOI1G1PpN+GmPbktEDqhko+cNxlOQQ0XQf/+P5L7ZXuriMXJpJiQERtjHjmCF+vbe1KVAAz4KA/kLThZHkn2MGkf0speMTN/jJn30z0zkjo3nVP2V7oy5wS/v/+Pl81yqa/ZVmOrHTkZRp93N8tfI37z0MpAqxrZrigJV34H8xabDwygcNUu1WJPoYFb9WuQfbni2LmDYrWLcqM+T3q7K9Mrri6xYLb063xahs/f0V1Ub0GEwaoSaaAV9A171WlM2mIKaigAD/+nIEnzYACFITI1i4DzhUQiZLAzzCWshkyWBmKE24+ZFsHQecmgBFB3fgGw2vkkRiZxYReMOYSvlUxwlY8qWMKVC7oBAXKISUrKGNoM/lX2TvxokiS9dxftJBdijIswisUFlnBHRsvWEXN+B7ymtEFvCSFZqpHK8Cn+UPyCTlP8Zkx6s8B+HHwyVyJpawfE/8neWnZK73RT3fcfHZsGwWy83lzmOa3zLX+VAJckAnCsWEAlhYUD4pBKVmHytYNMnJbOqnpUjLwnGZPWkpUqK1kZoV6f8UvWdHf9dklemhh6k5Kar0XaPfi9/3p6/kAaVmB0aBMglo4aj58ZbI3PHKYHYRNMLxihMw15QZqZqgy25tr6nb/5Q8sUPB2PO0KJ5U8YLPnlEYoueTph1MQU1FMy45NwAAAAAAAP/6cAR/XAAMwfQx2JnpEuxDRGsDPMJ4iKi5WmegT1ESEqsNgx3aKDdkA/Y01KiVGmq7IdKUoS8oJfsZVj+/P6YfEiFqLN7TFWtgz62gx509fbV45Vb00K9Qw+2Djl7xW2tXr3jQ0rvwPnnGtC2CbkNb5TuW5HiV+FX+LnzrK/Wi6+UvnK93x11h3a5Jxv/oNSA7EpHrSNdYFkjWMMly6HnGMQxd62MNIBLe2A/I3cxhyA+zxskR6Go+IBt9dCXVijRg0NuFy5sUTw5rZuH+bgQwCTpNRSF+Tg+r/0XOQ+4UczaZPScgu6mm5j2VkACpbgPywaw+grgcW6sssJ8t7Hpa0Ps242c3GvBBO7pOw/JB+jZ3qoRNMOaYtX9PNORS4LX7Fum7RKYc1HfQxMDzrXxKhMQU1FAAAAD/+nAEWr4ACYIULti4DyhkQOXbBz2HNIhou2JgMOGZGJ+rnYUJswCBGX/gJGV4nh2x4tDpitT1k7eeTMxkEx8NzEFcY0aPqZW4wC/mJQ/vyE7pf7H59RAfCARQgooew97RYSmg4aNBHsAMAZP+B6wD/cSFgwVtM0LzLD0PKh2cNpx4mDfWX3p1+gi6Jkzqn+c3O+32qjullMJEJdBQG4xN4gWqgJFacXQhwadSlE9B8OSOmeGf+gL8KZ5ayDd0GaHINXoWikNuYx5EeeYHvplFq/+UN1dLfc61maTO5ppXe89PP/DtrQyhKNVxv9CAKu3A+1QN2jZItbK34/XiEM9nGvuhHmFcdGQJrUjJC9Mo+naoXWl5T7/3CG1oT6V9kZtNNp0Re6MtqsrqyK4ahjvu3rsZMQU1FAAA//pyBN8NAAyCDi/YmSMtlEPl+wcwwmjIvL1eZ6RJmRQaq8zzCPoBObcC1gwCRGOioCxPqzMyRrUwjde6R4kf2pNGZQO5CWGTFmgszI0PqYqe+4h1/3pWi1IYLFiV6lvdcgK2UCj9HFgEABTbgQtGBZQxED4I3jgkwWdJFEyEsKQZ/o4/vqmg1mNIdTN4X+7qT+4T9X96edluFHHJFude67i4x3+bJGh0nOek2ARLuA1ockIL0eyqPdHMg7xtC7UUYrJOtdgs/UteZ3QVas6khDNodnTVKf+DFk2pX1vb6QBb38bAX8vjS7I0ZvfpnNN2qpAKl3Ab2YsZ7tAPo2G0gaDS6gPGucB+5anoprUosnOIElYEKPuozm1QWZpp/uFFNuiF9X/6AhNKCDJVRA5LyqUrqK608WTEFND/+nAEEF4ADpIMJ1eZ6RNEPuRbBySjtIgcyV5nmEfZFg7rnPQJMwArtwGtmL05ujfKkz0E6j8zUXLq2pe3Rz1spRm7jKprBDshm+1dLh8tvu3d3inljQqrNsY5s84l4aHsuKk5p7iREAgAV2+B1CBQDIgHJARErzOJuS9mvoKt+s+xvuyjq7ppSMc3vXzhFf/AhqOc+lHHB203el+iOWOuITA41KF9//Aa2Visxn6UY+wuVsQTSvseXmZctt53hyAhaJt3Zuy09AT/9SrqpWb8tDpMcMhnZ4LshywrhOjtX/r/UdljhWREAnZTFFQOFck9RatQEYew3fwpWBkSR1vMeNUG44x2a8RZYiEMUawQfBq/6DnmkP61z/0GDb8tVV//bm4IzA6zaVoMR+3tMQU1FMy45NwAAAAA//pyBO4UAAkB8h1YGYMrRECjmuc9Ij6IXL1e54ytkRMOa5z0CTsBO/8DUAbVQgcOAA3n3dXJnPVKxZczPeigwpxaq9mbRx8u9gVgwnUA51ukgLMHkQ4E4bLH3WIHzeAjkdtFY8AgCd24EBCDtHU1HYd+hyDGJ6SUoZRQA0EKhtf33zWOI2WwZFdveDc2joEAqux+Czr2J6I/tHtoXIJJtS+x7EpegBzQFR54KOD8IO1xq4bXe8maP4uSEVMxAWxvfNCzjhTaQ8Bg65adc4grsR0pRr3vWlhyCgmDAtJ2EEcRj3qdsOhuXBAAXd+AvQRVkQxq45aI3MqQWfnBHLBladhJ3yzmjMchNn8snzP1iiMTf/o96Mpd4r//ndP0yvlNIN+iS9Es0303XVvl1MQU1FMy45NwAAAAAAD/+nAEpYYABFIQEte56RnGQGOq9zBmeIh8TVhnmSYQ+pGrTPYgewQAN3/gNVTqxFGmGsbpTYTBwugzHJ1mGVuMd94Ria31PaDQ7hBN13/7Nvd/PX2i3+L0g4QL+QxR1/vNcu+/u3J35AQAN7/gJhDEAGbgnnqPLXYysFXz5YAGZGEiUicb982Ks++2N0ttz0ni26g7XJLWScPlZ9KFttAVLxx4Z0i4T6FgE3bULo60QgUcYgR4GKDwYOBYwF4cfKbu4/LufmYm0mC0dSPlQ7Qx1Z35+1/S0cKJBQwZelLlRRiWjXsMVuF3vegIY8FPbOIMkhCM9eFxfq83CxDx6PiFhC/HmYMmeXMRPiRzDXCK7TDNpF+YNuNP/4z/iPours7L8SEqq/utr9sGTpiCmopmXHJuAAAAAAAA//pyBN1lAAzB9BVYmYIbNEOE2uM8qHyI5L1YbBhNGRoSqozzFhIJTf/j0E56ILiGFLlXoYWF0WQ9ZW70GuEhbU0CBxq2vlxW3nFuZpegyATpRwobbqzjw0lbU+KCN2sRNqXczoAM//AazqHmX96vQmaSqxTMJqxRAi6q5Rqu4saWKjB1PuO8K94ug+c/7XskggOHMDYokSPa8VDMu9tQ6KKWyeIlps/qATu2AkrzMrgSVKZjA2U0V6MTFJm3pZIA5+ldTdbxWF6mBhundNooMzVwEql/0HNT3+12YxClWHQLuF0+tpp0t3LVU3l00pUAHdsAeq+CeDuTEdEQUKdmm/qzq3JxClvbsLSkZRNJASkfQUDhnPTRDHTcL/cZZfTRyGRhFTJYcuYYNKVn1MgZJvodL3jsmmIKaij/+nAEBn0ACcIEGVe56BLUPoMbFzDCOIhc1VxniG/ZFBLrjMGJ6gCAJ7/gNdWdnXxdQ+0PaIDLKyhHmHNk30k3UFXLkjar2v4CRzRK5nWsKi+wu8+tqAIZhQLgOaasVNaBxMdUZI6wCAqb/8QmoQEltnF4G4FDPXwVDuvRXLd5Qa0S3MmoJ+CHxdp+5VbZOkuAxY31rWJ81IBm9dU0hNpQrptSoYoFvQUqlYS4JAUY83HaPw11bMOuyq9pLImKBFdVR7O9oQ6Vbiu7fOc72drp8n627anCcnshQh9V5f233LXdvLkzv2J4C3dtxQRA5Bsx3vkiR1fvV/FI6mFrdYGYqrxic7YVRXh8vEM8q7/52ZlEeFzAu1MBUjRd3jvSXR2GxSBT40c4sRCzOEw4mIKaimZccm4AAAAA//pyBPavAAjCHR1XGeYaJkMjGoNh6RiIZJda55Rt0Q2OKwzxlaoFzf8CiUdNbeOYthD1mCAD4oJKTrtPlhEmcJw2cYc5lOmB0rlgxjOd9+CdU90dQa4yfBVeZtzvlmumESdOtb/Vn+2v6AA7aA0RxSkadLimG6Z/ODyqerNlYJAUraUPmKoEyp4zASjDWkSctpfEFeunZZ+27lRRKChI1z4zsaKBzRcBmNMocHyAp7fgNTpLHEqBPwUCDiNxYXjdlagxSkn2OUh2zqWyIP7I9QNelDin/vJnR6SaObLqScWxYoONYNOQUEsTKrsa5QhWAXdtgki3k4LWWOkIJcdDospH26BPMDE+VLxGCEQ4tM7dfhWnET/2PLtJ1scxG5V7VLQk4PuFUZoWPjLUrWcXewPK5NMQU1FAAAD/+nAEeLMACIH4HNe54yrMQMOatz0laIiwsWDnoEbxFo5rHPSVagAgFSyANT9dNDOcoszrPMePkIsH5ZyVamoWLHd9HrrlDOlFHfFTYlWhVy2lCyjzXh8OGVRrVCNxtGeLvOIOEwBkFzbAQixA/TEYqnsxHI3D7yd2ErzNS5Xadl6pTLZYdyFpL1j1bCelJRBfq4zFUKJNCpGp4qs8PWV3ZrS9Rka4QKUskEJbYGeYoD+SuSDidPmpss670ULK796L0MA9NS/+yGRU5r6FYmDGEgahMG2ElLNmBxUNHRGS3mBQdaKlVwo3ze1jSAp7bYL5YhfnWy3hIQwwSDW1UTytfOMP+zTbv5FJyV5q9VIo6UIX1w6tHYmYUUU8ilaskfrsIpakTIoe11JF7UYZiuLbVpiCmopmXHJu//pyBDEGAAyB+xHWmY8yBEShuscl6QiIWHNQbCRLUQwTK5zzCVsFO67CEfnwoLwzHEPX2hexz5apDM+tmP6/ac0Qz0KOvkbYn6oCe7wq0QqaHgggzR9mKmH6BYVIKalTXUpK9mLAGBTu3wA86JkpO1qdGONBQ1UiJO9L4iuVWHz4FecBmh72uBtyXFoC8XW/vWqwe8XabZVWhgiXKoSeNlAbCNBSjRc9mygAqW0BssDNTYSsCKNLBWCwp2hKcYB1Ay0TMoy4y35wm895KlHtqf0ZjK9rHppBjqfytTX5ApILPZ4I67nVtS7aTtQoQSv/4DWjRmJ1moxwHzkOuwEYT8q/L+bYsdkoRla4TkYnn9oorXL0zIbtHiDDN/Ov6lvx285UE43sOqosxvq/dWtQlTEFNRTMuOTcAAD/+nAEnVQACMHmHFaZgxKkQsS6szzFWog4Y1zmPGgRGg6qyPQJpgTLt8EqAlIBWCQOyr6Ual9ZNjTBR7KLLqCcKbYjWL9/xcOOu7L6DrmMkI0xupYpUABoAlWQ5EJu5S9X97wVdtgNDgHaplg2EixMbWJ5UUMFPpNu9X2LWeXTRZb/DO12W+BKojwFOVyu2dpRN1aMnBF9L2AehiVW0NA0Dn5x1mAEAqbbcVKBiJOh0eks+UDxiA37Ice+UFn/EilxPy56hKppb+0qWKNQ2LvXUq02y4Yx+KJZpHe7HnjYgCAGhQwkUPLB/vEgoQV6pep9OrTkoBDaQ3T2pCJ0baBnDVbDyFPo6/YU7wgj9wNyzOSYNPPUhltU/YjM7UVEh4RKPAZa4WlVHYrpU64mQTEFNRTMuOTcAAAA//pyBLH2AAiCIRjXGSwQREIDirM8IniIiHNc5ITs8Q8ObCjEjG4FTb/hIcTh8oB9QT22ANxhD85bWQgJ/q5myZ/mEuuD/ztDl3LhhNTEgAPDZg+J3iNTWHaBE0CLcUOA+wQHUBDfeJ0JDgJd21CSL+hBrpYzbqpLVCavqKrsVQASNIxASMImiHV5BRvUX+KZb0LFEFqkHQML3JPCYfIzYuphZbRRDyV5aAGf9YQFJZGDibYRJxKKiEkRgNy0H4CiyV1qSUaFqtpboG/yZcIpeuw2pynTpIIJF6QkC41JUMjgrY5ZuwgeCrCJ4aDWjZ395KBKWyQWNhbkECzza4MfuM4iEYIyU4c+kUGk9jugxUfcTzngJsFKrBygAfJUOqraEPSaJjhBqFJNOo2FFi/aYHIagzSmIKaigAD/+nAE52IACIH7GNYZ4zLMQ4Xa5zBldYiEY19GIOtw/YysHJCVzgS3GwG5HsBN4A2EwcrK+F7l4IIgb7kPKHHLz1FkwzWivm9//YhxaEoAq2FnFt6GjCjxbGiNhp9HUGzIo/is1eAYFOWQBDHYZEd0VA56NcXebqgz6CHsI3oImyGcd6ezHNKA7qiYeS/+6b2mqtKNVmnZx4econSHWrBU7za2plxxJwfUqSKdskGmzBMoqMNNMF+JKVJSySfUWNC701qUUBE1VpvWrAJ4dcmqA0MB52gWFGw8GxZoFHVIrmjLBoc9FqaA8Jlq+yhAClskB2YAkmy4OHmtCWp2giqsppLapBw1lX9NWiYG6D6OgGpGOWCigOL1nz6iNSnDgA4mhTIntrnwtdqcr7kpiCmopmXHJuAAAAAA//pyBMZXAAjCJCNWuek5dkTjKsM9hRqIVI1WZhircO6OKoz0CaICoSn/4DWtK5jXYnbY4gQgJpZDeRuFCDY3qb9yb7TvZLnroA79Lbfnj5nGqsRO1GNLtHoyKeHM/d9OZsYtwPSf+7/mGbCMu2wazhEwQ1omkYGKCYhR0LyWalAyMzObJCA5c92ViGK5OGdZy7mQp6lJBF45aDpwXIYuQB6RcH2FWDufdvQHrF7BooAJI2AXVBSBodBsBsMBpoA12wxKD0vwaFuxe53qr6OwuhZrygL+HBNlZ7zbHHSxgUa8Dk3p2NGa6Re0gwZUl6aGOeoBS22jQxBcUQ4HwMs6UKZCW4lcldZAejdkepSVkMHRvLJ9doIHqmHIKvNTLkK0uurFRIPoyef/Tsjf32JiCmopmXHJuAAAAAD/+nAEMy4ACIH7HVe54yrcQIRqszzCWIi8fVJnmKyZFZKrXMMJpgBoJ2yQZWHqtoPZrdKydLWqFmXh9NLv1BZO2/va9Ap+tg390FCSrRghudiaYXDaUj33KQofFzxVKlqHu77WYoArdsAuEoCfQnbGLqxpuMQ/iBw3Srbu+tJWLooKcjXKvL2RH1TILIZ20C5QW2uNYXjAqZC0XSEm1wkhSKIs/tRSApbsA2FYOc7pSHhAWlVTnArZaKqwARZkbnctkwxRRbU7X6A7crKOPIIr98LUH296uXPo/X6M0+hi34ao11hDRs8Qkb7MTMA5bIAXh7Rjf4SCZsnZe0pf3685WREVFO1HjTt+++gzgYRSBDNMjXfo8glVKQYeHbYij5RiVzs0CwsPrWIglVQKhhjEiyYgpqKAAAAA//pwBF6oAAiCIBjTmwwZxD4DGto9gxiIgJ1U57BHGRIRqtzxlZoAmW2hqj6tff26SXJWrjAqLB5HqG4wgNR/q0J1jrBSWAgqjFBuHxuf0T0/TdLMSwRG0FDYc6WC6XgZFW9P7SL31eOpkQABhCc3/AhLYqHUWLBLL3hjjTN48auJ51EaMD7/C+3BHcKetBH4nzCcWijcDi5gkoVeKuFEB2HYwox8sPrsICptsAkkLTSCoWAfZDxM5UU6XI+E8tO433BudUHjUbd7aNk6PEkRH+11UzmGdxVwrWi3OjNrXpi852wz5+2xazZlW/MwKm22Eo/lebceyYLmxtRz6w6fVnXU8JQuRRhr0bbU+9Qb8KI6/TiKsPwgLvYZS0eKvw8SKMM50Snr12tQRsuZZFJKapTEFNRTMuOTcP/6cgTyEwAIgewY17nmGbxCg4raPMhbiPBhUOfgxREgmWscxJV3AKS1bJB1qLXlUpEGXblM4x2GMoX8qfM4ZQjzPRihZh95L4NYtJ0jb2hUPBZz7mJiFA6IOWIjc5fQqvS8nvAABklSRwBfaRSmTe15jZIKFTsQR+aVmdhS27tGiVtkQ9V1etxIIccTlHSDJq8URXPPogZD0UtvbVWl5x1ITSYILUsJDduwCZKYfxavB1hQyhbHbkggOb27/HUa69xSomKS7ZLGBa5//3bZC1E1nFPQ/SOCZq229i4vUdkiR85cCTBIhqh888+MYRGUZbIAUyaBAutNQE3YRZVfPSHL/l0t84d050kD0Yl1XPa11blZvQMOiN2ynMaFCqtulZ3TdKbMjFEygy0+uOk/mhsWLzuVvLJiCmoo//pwBLy2AAiCECxV0ewZzEBjmtc8whuI6GNW5jzDeRMe6ozxneIAAMAFLYAFy4mAyN4UoUKAJDUKRnSZi8Mx8uqVPHn5mo4IY//G2hat9LxJFIV95hKRUyebNwtKQM5ALjIs9KUozZoAYJcsjEE4Bipwu+CkaBaNL7OMJnMY2VSuZ5BnW0Ercn9RVPDUaCz5BbhLMgVWbBQ45j3HN6iBwYgYYqICiUf9RAUpZAGRYHim6BC+QCE5Zn7ZRR9nUlomY3WV8a0jV627Gfh3j5hmfqynu//w02/2PNY6dXJDjDZ+7iF8B7s/79yrUQTo0TrYKl2wDYWADTM1qiPUPryurnNOzKkV4pM0puqD9BlzJF+tPF/aoVDH9ecj1N2RE9bog4smru923O313etkfLvFnikeKL2piCmooP/6cgR8bAAIggIYVjnpEjRDRbrDPMUaiFSlWuYYSfEIlWsc9AlfAICrv+BIlFGmpBTEUYiM44geoKLYDMLXxlgudD1K9G2rwbcsdKvsnCC5lKSDo+pYBhQ2EyjBYa42gLBNi0KOZEKTf8BMJEkx1JijB4ALCNuu5IeI1dnVGcsjsoj7ILWRomAh2GDnUaOy/5dhja25FVOLkD55Z15xp5loaCb7GxzGjdqyhJ2yAISUtu1H4sBQigC0wNhfja0ju52uvuJ47L+jFNb0W0Eki9E/+zWmUjqrg2FhVRMRKmYYEiRBVjZNiaWgeVdLiCLtkAXSvDcNGZjSDQq5GXreqhaIzukcZHoLa72WqLJlL+drP/IClUEM5lS6K4uWeeTErqa58lt4n//u1V5CR3plUxBTUUzLjk3AAAAA//pwBORrAAiR8BRWmeYpvEDDitcwwjeJCI1W56RnWRiS6xz0CTcJO2QBuZzp1sbK7MgjhyGEwLVrWMpa7c5KD7ghQVD5IuaW8s3y5p6jAOGbRq6DDT7SJW1Sr5lKR5UXjntF6gAguWyASFQkhU+tJRFBZHcq9w2lq74MYZpjjSPS8l8ggY5lXGYkN5UGDL0h0ckMF0LjVoddYi9TCo1CJx7HyLBR4Lv/4GziJjHoeA+ycKWy0WVK+hJG0TmfqhirLV2PyPTTG6Xc5hUOwsvLCA3fYLxwnIogdXvZ0CFDJuWCz7IjHEClr70615LbOSrWs8gnj6ipBXspfrjN6NosKIqHxCiDMtpIiofeCR7ernf4RYWeiyzBUBnUZI+EpjV+LTwK8H7QTy5bQ/uQ/6+GTma27iYgpqKAAP/6cgRFiwAJQgoc1zmCGrxBorqjPSJEiISNV0eMsLkYkWrc8ZU3AWS5LJBCqH9HIPCaVFX2dDwLDpB3wSXlWVWXsxeuNQQCdmXhJj1i1dRc7YhhBZclNmgNC9UYpMinXFKhQQr2v0qBL22wTJ6jsJOnlsJbKO+ZnpyJ7WBkeZ2RrvjWrpeOqngDs+IUKDZgoJmHqD4YQ9LyqiaLbRIOxt4zVcKKduuFddYQAp1fem4l5znJCijjrl89YoOcUb+yLcMoMk83QyZJYJfSBrZYkI/+aN2B8VmgTLK1ONaK+NtXNyTv/xh5WhPo0/WO8gQE7ZAFycAL0lilGyDvfk8zFxwlCC81PynODOQcKfvJqPQPBP8Qff+gbpOiI1H5WeCktNEe8rQ0WBr1CTWMkQsbdWoYc+KmIKaigAAA//pwBCETAAySIxVTmekzFEPEqscwQ3vHREtIZ+EjEQKOayjzCR4EK3YBIi0ARGJoCEkDFyBXIall8wDqeckX9CKrdgINXabHnTktnZMYmd574GcouSSu+l11JoclTg8RIFVNc94bBopUscAcA22QDgOAHHNITAXD8d1y+a+tnEaQxpmdnggq2R/tZdBWvNdWNUzyBBmWKCmv+tr/L127ru7iu3bEt+CyK+uX78rtgBySAMADQByW4dQwc9SRO+C2KRBwonLUkx5KkdQWkcMs1CcwnPHSRO3i3KLDvhtSBRDk1WamevoWmolQlIuE6JY5n5Ai+C/VMEPpVjvJLMLmjMSxhZpgWRKuEKM0o726iku7j1ppkVSZFmmHpPNeaOQTtalxRTBfS9comIKaimZccm4AAAAAAAAAAP/6cgTQQgAIkg4l1jmDEuxAo5rHPCVjyPQlWOSwwPkNkyro8x03AGE5bIAhGIEqIxUcHYZ3O/odM0+6wyvMdRVUtVNg1Nx4Yrb4vU1/rCKYwOxQizAoxowoCT2jfUAD4XF6V6AuD4usBgLksgC6TIdBbWQ5wxDWObrHk6wgpwOlBybmZW06o+pTPNqFgjf/m6d/W/f4qh16of+IS8KPeiNdztf0859+d6QJO2SAaJwYFlwrlwTwZaKcyr8Fzmv9/wXzYC16p+H+/h9//9TJhKC9jh067wVT+SXv/PuyfMqzpzb++CGCHdft/2//ZyKFKBcBMsIyg6SXPxZQNyBP9kCaAsJ0WhjUihYOkqf9pWJfom913P/Kh1vtlT7Z5X9w5tJLdMypcDw3770LgOnu5/n23f9JiCmooAAA//pwBN3jAAmCDglVuWxITEHCOto9AzOIJHVXR5hK+RecKpzzCW8AwKUsjDg2C1YtKpkKSOiFtDiS1Kj0MBkWfGyDzCENSGWVs+xTZCVLuaGxwqIRO4uYKgN7BRoUXaZHtMRAhhYnojAAAZJLlkglOIWs2oJPB9ENY0EuDAs1bVBKdEw/cSYyHzuEuS+cJJtoLA4oexIkDy0LetUORGlGYHr484n1MS6vMoCUBSNZ9iXZXaQVZKb6R8cpi9TGbNFIxrDnmu09vPe9glfIKaz//nv6xp+Kdhf0Wm7K99U/ypJYolsp3NgU3TKwsUAuSQBfL+JsKWfwnx7hKVGpy+bdA1+HNfZ3sYGv+z0evNB/pnEfnT/3dkdHt3pZJ90yS3cxy5XdZyAobt3e5unc1r3C3aTEFNRTMuOTcP/6cgTRkAAIgeUY1BnsEbxCAxqDYSJYiPiVVUekaLEZFesphIluAKkbAPlfJYq2A3h7hmjAzHBQhVMtSGryOOfdNzMzK/ksnB2vH/kxhJYLGnsSgqGbGpKLGJxa1m1IkFeRaCndsA+L5FHEfHdQ5liqgM9DyvZw7FboUCV5st4ePSlN4NpBrPujJVBPIfKGdDIZHo2EgUDNNwrrHGBQogItB5awCAxLlsgC5LAFyeChRYyTDMRmAnawS9gnpzRbTOKGQEyimp/TwSSDHAa+FBObF/Ppt3HOhxQEtUDNAfg0FQZGMTIvXiVRVCV9gEpN2yAVWuPrAen9JTuHhTPPfTuP7p+WO+HxaqT1uLlxZxn2Qe8EBNvhP/qcurURmZro8UFJ0wGiRs8LXilqkpxYYZUMR2piCmooAAAA//pwBCjvAAjCFx1VOeMTnkCiynNhiTCH3GdXR6BokRERak2DCVcA4CVsgGiwB1nGj1KPc6yOYHdcaWqSqpCsWYc/kGUJ39k6Mys2N///wXpMTqv++GLKnoLeNtpr+XzyNdK8T1D1ORk0EldsBEHNJzorQelMKPA1wIGoKDofoe0sEYSL+/gfd6Vdfsv9Xz9SmqD+r8bZP3uC6DsAJKPkD+boAUo5Vz2pSTCUKTf8BeQ0sSsbGprciOaHOTCm4aNFU7KPenlYXDLTOFtjaHAl9o/pjzTz62CNZtQvEKblKxAUWKiohIGVsWAnK4A+C+VgErYcgZTQOvBz8LVo5JK4gLqiGvheoyMURdf+dNCpBE39dhdlReu+cZUx4aJF3M5/4Wv39wdwO3SZyNO4omIKaimZccm4AAAAAP/6cgRpYwAIggwq1dHmKjxAxYqnYYI2yMhjSGwwqVEYkWlNlhVbAADQFy2QBdMYoEysrpcKIxbkCNYFux5M9q0EH5DDlRqPqqWZQt/mK3/s53teyWogyNAgcfkbR44w16Rd90lsHzjgBBO7/gR6FkQVXULVRxCQZFWAbdgfizRkWPRWR22SnZ9dZzDcrVF77fX2X7ntQP5zOjfbrusdJ3G34yVs3dekRsAu20CJBCkPS8yl8PocC2yGqvgBQeO1oCLVLTt96fdHiP74egYVpa70ltQZxtPaLGaGFliwwKkAbU17RSL2WrfMNXauxNbAVNsAMwUEBQQU4/a6BDw1lJFy1Hm+9DX4KycsdMb02K4PlIYivjHabVlILvUuHRjo6b9RV5khT1u7nWBeHsSys/6AXbgNSmIKaigA//pwBKreAAiCCCTV0ewQ3D8iendh6zKIxHFQ56RKeRyMqpz2FN8AAkQnLJAGs1hGjtaQyANSh4M09fT5CkMqdgSVQDMSm1FCzKMp19qE1SvqrQcFdazFYbrkmoLIYtRXOxd5IZPUkgBAp3bAQavcSkAhwYWBDtBZJEnEJVGoZtlIMXRUyk1qL38VdVXSnGXFroa+Wqdy5AIDlQ2wSr7KyFZqfdS9tSAS5ZAD5U4kg4jFimcPhDlSJgwFNC20ysNocMzYsEFh0mbd9tvwR/P/+S9Q7X+1sGt3omTC62O3AF3jEcxBmAhuDs0/MiXCE5bIAmWEWgRRxQ/YTIwMgzMmmHZoMUjX4gGutT5ri7ckzFIMZf8TTHfPoVUOso2wk+COhCQP7v9sYxGF0OemX07/98EN9CmIKaigAP/6cgT9IgAIgfYjVenhG+xDZWqHYQVpiKRzW0YErvENjmso8wluAAAEAARtsgC5OAeJiq/TcuELeHrHhYZWpe+b2SzScnGE5wEsf4xUPKth4NHgqteqXNpAMK1GGoMHGDmLfY2kAJScsYEEq4RFQxf9JUZWkyySy8URj+4VhgntI/EIq0c49xsUw5MaR1aoTXTUWo3pol6tt6PElA8VF9B8iooXFo0JdRgFO2yQcPgfjdcxixG2DKkNnr4tzaN10gEbid5XVB1gJ7xEspRY94oKM5woR2EA+Hw+1rxdYXMGHbDBTmAQOLbrA497+sJknLZIKIQOscTcrAZdVlwRsLOM9B8Ydu7h8IaXQtppsYt1dEagS9HxLvCi1GVRNl1uCz0obAbaNjktdXYKi2Swz0dyYgpqKZlxybgA//pwBCARAAiR/RxSGwYTxENEWrc9AkvIwNNS55hNcPOOKujzCRYJSSQBRpFVBYbAjC+069iST3j4Je6EXab+4XJmpWbBmXBW0zCF8tS/rJa1xbOtYUjTHKYzSb/cYMcXa5L6ez9YAwnbZAF45S5CArJvBilefTwIyWgbud6005LrjaZkJqrwmjkYXJR4IU7NzdwZrTkvcn/h22J1ne5LTpr/HG3bte9/SlDckjAPZjFSHWzscCKf64J3EXWYzOQxnynU2eqip4FvOCUMIooPiSs6y3Gbtrtd6/Wvt3qvSYqOYE5cFyJIFXAVzkvsI9AykuVdK8VAq7nOlBrLM6VPIVZaDEWyQj1VgBTm15e+Zi7wh84/LOSpRw696HQOpjS4ES2plRkgl8g4/NZRMQU1FMy45NwAAAAAAP/6cgRc4gAIshoYU5noMqxBYfp3YeY0iGQrVOew5LEKDGlBhgziCSkbAbhShsgOh9n6+cSoTorzMLkrlBW9KV8ZTerO/oP9kNrM7q6Rii31BsGQZUwPpcPFH9iUAm9loMjDYuKLY87qYoBILlttE0j8lqEAc9W0qoTiJqtqmWSiAwsjrzkvwLO9ohjW949ZVzzhpWjAKDDG3JHzxt51SXuFNCYov2vray7/QYIu2RiU1xrCPnAwXUn+AxWtOoE7SoDDqJs8UeH3iJbl8t8TEiqnDklg0OpFjYReKBcIoOtDLw2GwrZW5jUGXizE9aILKBi8jOlcA4QZ1DQMFyJp9IFcraMwtY5bL5iSGi5ZHelqQMGaSdLhV1NaLxLAwBc4tWoOy2POilyIK7Fyyaj0TWITEFNRTMuOTcAA//pwBHlSAAgCFg3UuegyjECjqqo8InnIiI9RR6xrORUMaM2WFOIAoJcjjCZV5cA1mdiZFk0ZEu3BjfSxTYR9Py0TgOB1MM49wty/xRrgcJuYN24qBOl2+5oCCIEaGknDQJioEu5Z4tckAIAQVLZAEypyUB3WWh7Hgs0gUzWF+6KNPOzEFK8/rwDThwXv//lZampNXRkVfEa/XBJnziNSWgMYaW453P0tJIAwLtsACRLAJUGCzn+k1QVF1JK5ZS8YzqXUURS1eZRZungiL9tYEBqalcGGzCA181apRGSxU6kxZFZm2T3lpyu3PSbvSAlbaArKXoKSDMRRXHBW/HlACOkr08QU6dMr7UqzOxnJGkJXJ2tZUCaKiXyRUwsLuDUBhM+pRUXAbGKpVWYe9LpNqxe+LpiCmooAAP/6cATJzAAIkg4c0RsMKlRDRMqXPMI/yChPXUYESbEHHmoM8Q3/ATkkgjIcwHjObkTFbRUboCMsYBqYEFQQYykA+D259qIuorc7ZXCB3jG9rzJWz4qr0OKKcBx8n+gXoIMVzjtEe/40A5KlsgEEcAmYVx2JwkZqgGYw2x2tsNeZm3UfcKiAPPvF6cV+omv+pCoUNH4dFnWOqtwe+id9zjcEJ/6Zu623XvL8/AZLmtkggPTIGKGkL7SjRSixYcjS6yC4S8o0eTlWBkThAYYcoQKUkKHGLeh0XaQjYssxfUxiZsUWSSgxtu2qmo5iQjIkBwhFAeTlO4ZNlBve4D+J9O3l9bbK0L6AyMxuvvCvBofq99KLtrQrN397copJnEL3dTZKO3a5hKQCbjq0/evzTEFNRTMuOTcAAAD/+nIEBGIACIIQHVEbBhO0QGWahzxDX4jYR1dHpEjxGY4pnPMV5gVJJIEll9kcCfZeZAUW5QHBgoegGzQUq6d2XstwKD4TAnW9P1lgeuEHz01iG3yDOacPUwNJaHSsh1fp1qLo+r9i6ACEmytgGcdoNQFezF3J6lmvcSo9kBoUqLWx9BnUG2DVrNXk94HWEMn71YXdV+Z9V4SYMGRNBgmxuktrvMVbDf3FCU5bJBCOJEk0infFRyLq/3EmasPwsjM7o/MosKeReZvBMZak3qmDKioSNKGBcshCSqnAERGhraBg9h0eoKJVdrng61jbyQSVJGwDuDqAIQEw4kNhKpK9D2ZhjUxdoZ3j/iHW5Q3Kk6GNuWv7r0A6d4dGNa1osSHilkkasrcKGgFtW51JJSrryhQVabkkxBTUUP/6cAQueQAJghYdVVHhG6xBIjpDYegYiFBzUOewSLkHjGqo9IxeAAIUp22QCU2xrkvQ0/T+RxzVdfOk1WdPYUgitNIh9Xrp7H573659BYjyNLiQobEouGhE8XGuj1o6Cx4NGDoG12eLqBUt1AlY1IneFTg1RsCRYPhjNJhKC+qKN9Ht8JhzbYyoJcIhjXvFuEy6dAu1phBVDmrt3k7CBARAuwqCIm7VK44igFWnyozkJAOISAZTmPOAn3eF/0hWCZzsr7Ow8M9Sd16BE3xgPyHfemnZiKX9D/JptCs7yskdfa+6cst0on5ey+NfY2hSWyAJlCy/DCF0RSYfoOUOuQI6bVhhAGNw8EWGLZ/Y8O1HDpFnHPhkKFSCCJwDnKxa88x0PMQ0wdNFxa0UpUrlkxBTUUzLjk3AAAD/+nIEsjYACIH/G1PR4RPMQkNamj0iJYjc/07noE15GB8p3PSIagACAAckbAPUkgNUTkto+g/E6q3FahYyZEWZixLRuSEFwo61FclBnuMoXEnQgeqsXoWFxGLXj3rWvesK7/svexgAAJAy2yAHyjAOAVrOkZtkCQG7AdkHrdtWUh2BDWbXbUZXEFuCPg4BC66ljbRIeFaBjFSBFwvIhuEovfkbJI8GlrU8FSRwB6KQHOHEdAQswB+mVlCIPwabWX4xVqyN4Vp8Jh0Iv3JstjF+cd1Y36vo60u+/bmNQy9Hoi7bonLY4OpXsC7HVQWJjhhO7b4IkIwSMF8ABhGSvXAFRaryLWaVFK6GVjNXU/7QZ+mrTHZ0rq8xbHI/5l629yov0Vn5f1IpIlQCBZpYmESLksxG75JMQU1FAP/6cASGPwAIwh4YzJNZeaA/AypDYeMYiLxjV0esy3EWjGjI9BVGEcABRQcxaeV4d56c86bKqDwZ0F4jTBUxoBigEdfgzKm16xNuU+nkupFMB0IYzxmeRVvGovgW0jErZvFo/h61hnqhL7wXbbaHgGgk6g6A5Uk9MEuLwEdV+w8qJeBGNj+lZSrn9LKcK4BQOKFSp1B9QkhVBrOLVbw9bQw0xncLY6j69yTSUctkgSTWTQ21QUB/qFQRlqHwYwiT/af/UMu7p73kAHKOkZBPIPz8hw186DFS2vNjLaRosl43rU0MuWlFnRbvex6K32ygH9QDiAkQBgAFixKtSGmK4vAY7AaSXM0xsw+rLZRRAQfSyJfxXhkbbQLLY1jCjA4Uubxp4cfc/QJ8dHJL0rvXUlaa0SFKUxBTUUD/+nIE1RgACRIPElZphTK8Q4OqujzCXYg4P1dFvMExDA5qaPGVTwAABCYU5bJA0OxJBhg5AqyeOHjGEkAUBsY3Cg2JQm+llKIyqSc/qb6GG1NGsewlpvSh3U4M1EHMexAsyK5en4hkFAgK2m9bJAulodJ+UccXg1T3Mav7/nZ68cP4e70GX7n7eLKzD28MOgTWlSXD1G2LHCLSlUOEZdjEpfenf3vc6yN9GXHjWW7ZC5FGWpx3kxaVSyKW7YRjFfjyG5pyDN1kw2YCoqfYPF+hraECQk4qgQ920kdLIQilMa5LWpoeabZpa37WsRQAAaYb1sLUOs3Ubid6mtENocVmDLATuiGIx1emKFMoqsz6t2hYcKBBkO+v92lFlkJ4j49tAD3wO350/bt70x9/1/O1piCmopmXHJuAAP/6cATOGAAIgh4T1NHiOp49g5qHPMJRiHBzWaekpbEdDGjNhIlSAAFkOS2QBMqM/CeHoSk5yxI2UrnjsBUA8KnszqzVVdLuV843oUOmuI///625e/Xvad7ucXeN5znjjNd06j/3zlFFzFABhOSNsLseYaQsSbjsbC8bh/3A7SzpHZ92opzvZro3q7xvaozrkOD6CLdo+UW9HIC4W55KH3N+cvDjNOr6QVSW7bJA1qgxUS2lANAUybBid3H2iqhBupwi3u09ley98/8HU3UxBkWFKHmh6RrEvEzBh9SnVeQQt8yEWsLHzqM776wpLbaE24ZEqCTbl5ma60RXjS+jRBMIeyDeIdqgNmRTqPBNLaZ+wwUDMUVS7SHJIy2sVPgA33PLvdh48tDVJp4oxi7NrtKK6kxBTUUAAAD/+nIEa3sACIIhFFXRgxKcPyMahzxlUoikK09HjEpxFplqqPKJdgAApLMtkgZDwRwSoIwFBKWHRDchMZjLAm2RkxkGkeDFOYBcjZ2eh9r9aSS07oUBsWbTVHGmDDVsi2aFGi7axridaGOJADDm3/ASI/CclOyL0FBoywzWUUgfitCtI1iIp70aiMzX6AyM6fmSa0JHX6gHUSpUVeYejawPCFwjIIWpjht6AICMkbYSAxQ6gjKqAfwxCqZkqHUjmGFuCLClRCQiEIiJ6Twca8B8qAjbt0kZqQhS9uumWZQZsbkAgsifPrra0DqS91elALJuWyQUNQm6chv3kZohHXYs1R5p+qsdLh4XKcMhBHjHfuJ/ca/+qEd3ZbU3ruZju7ZPDlB3qoakMCdF7koVc1jKKSDdKExBTUUAAP/6cAS/fwAIkg8P0znlSyxCA4qqJCJRiCg3V0eoxjEEDmrowYluAGErI2AVR8hEAci4lKBWEIL2SUNxDV9ggWB0kdybs9D6hI8+oHGvBhORFFnXHApaLTae5qZpT1aLlzLIhoeWc2ZYAAEoStskAHAZB4LolQegORBKNhuICqAs3t0oDaCbN9H/DHQjygfAdrLYsTBctRxYXMosKC9tjgi4GDz7XN62iazYFpclskDcjk6QxiLJFHAHXBzoY/23lLtVC1RM9Q8mda9ws7N+1pd6QihV8XtXecIImKU9gENjmuONbrXfpXHre8+DxbtLy8GCFEXCFjvFDPH2s5FWO7SFN6+Ya6aNn/BUIyzefeBQAhBVSlKoFR5cKDIqXR2ioRGmTTHSqN5wYAlLxiYgpqKZlxybgAAAAAD/+nIE69EACMIWHVXR5Rs8QeOaYzzCU4iQczxsrE0BDQ5nzYYhSAAAtLmtkgyaxXj/hu3qAONnQqWTMDLUa1UCI7teT8/WspcoxXm4AwXYIFPQQa80wUEje3Y51uLNbp0uGXkfTIIRmKwVJE0FMGUtmTBUR2HIssRl1i4eBzVvbXq5SO5f02CacEZUWqUGSDR6wbIBFJFZdL3/bsoYQkQSAQCQKKKPd9736UgL//8FUkOPFADikCACTsVzDIQds18PATQa27KGNCACZU+YXh5SMVOF03pNfo+ayNj/mHCZ58crtehV6Pu9/6UX/4p6TYU///CyyfQn5QJb5I9YcJQ4rXyzUDDU+Y8UHdxhx0CI0FLAbuoe6aYSZr6OEHeeND7i+kKsO8jYyq2xd2/9f993vv0piCmooAAAAP/6cAQ8pAAAAhkc0h1goARC4xp6p4gBiMSZUPmGgAEZmO/3FtASCdttoW2rgmiMjZ2sKDYtNL1Pow+Jymu5WGeCod9Y5VmO9OjxFXSxgx3XsOQB1CtSNlKRk9s7keLXKWpyVVxXbp329YAAUhKStsF3Wz6LROOBIy8OOUBqT0xnZkc7IMXOUeed9dLdhXNfQEtgvcCRRgTBdp09lepaq1MUwWFVWGE91Bm6w6wAAECAcDgAAAB5VMgqNApZgGQmpGLrigI4tSJ/N20F24JXhXBzXVOsoHeNJCT6HLDUe5c/8FqAGgP4Jgl//x6FR5wx/ewBggkSDQ2qhgQDgcAAAAfMCITK6w3CL+4BHKGdOJ8OWtVnAoYsSEr+LQlRhyQ/8E6ABQNYEAHn/+B6B+G1Iehr//m8uLIBfSD/+nIEPH4ADOIWMFgfPOAEQWOK4+ekAIhU9V5sNE9RExgrTYec6gA3f+JGVmclDKnTPJFKqWF1GeNbLhBwHJqjpEZSOnnsRen/0KE+zESZzH1bKGa//r/R+OnwAESntg1E1G6Oke6ptjgQlN+MKiMQAailN0LWJZNOlKsK9/pARNKAFuKZ4/sSrc7VlX++/f8N9HG89uxgIV1V/+RiV63lPbxN171EerP2dYATn/FBuQSp8pLdgsojLrtRcNm9k/WsJFzN8mOucbU+ptTaYnXVNB//v3//8VzPOaqoaVz130VMQ9TuQgJyanSMj7rrxQRWVIfJMy3AlGTB0s1IisGKs4rRWa9T2tiIimjQukcTKMaar0FRLKl2RxvyPdtv/zP78UhM8GpO5/M0f/eOrZMuZuTEFNRTMuOTcP/6cARB8gAJAgw715ntK2REB2rnYSpGiGjVXOeccdEMmiuc/CjKJEe/AkcqrBqRIxZA0Z7roudrLKPCQ8cCs+1kj2cfOFn6xac2isCD8KHadP/0fjqjQ7cgAC23Rv71oLIOZmkSzkGAEAh2/gUG4wlNAtBBjGSJlZCHhDXWU3yC+4SeLfHzZEWzy2vGAmaixLQuOPv0Jn1M6f/KtkI5nLuY6U3poUeKdWcmGLCSJe0pYtx4rzYQIMusZdilbmxb/Ah3wD2xFvC5HH3ygdoZlBvxJmnFDs4YSvTzP6vzeImgLjFiJ8H32NQKwrpbYGpWAAIc34DcgsCCF7nTCIHk02dYOnYtvKgvagTLjTleRjmaW1bEYe6CzPKCB7cxv7f37tYYjlyFHnNuUffwXGLlfdDCGJiCmooAAAD/+nIEFsEACAH8O9a7DTskQ4dq5z1FiIjIw19GLU0xFxhr3PaV7wAgh2/AXZ+kXJZtyiNkUrNyOo0WerO6wQnhzTZp5PFL4XL48R1I6JxGvCJtW1b9B/+v9B3kG1N0M6ejTibf+ZAQAHb+BladjYFG1twkBy+Nl3+uzm34A/cYA3jbs+a2QFteMg30JpkA3P3bX10bl6+fgnWeyLpO6EzC8RoSZfoE4EcQAIEJyQCGh6IRHQ7BqVpdKQNe6Sd8APr4DJnZMdWxeaFYd3J8qT5UI3xV4lt27aedz/fXlCLiDaaImXoSLoGBCPFrWDBduiCJATkgC+05TAiTZYoBf1jS03WAbWqwpnw5XHKhQJzqYvnsqIGd7hfu+o/t16dfPub9RgOtBH5heFz7R+Uv44+X/dntI6hMQU1FAP/6cATGnAAEggMu1zntE2RAh1rnPaJ4iOzVWGecstEfmGuM9h0bBACHr+BVkYzcK+k6dFBGqzCJz3UInWIzrFVeTucdbEUX8rS76xKO7JEPo3bMM1V5H/txeZi1J0OZ5odHnZ7ncYBAELT8BriMNwS7EwvAa0HEGTMaxl7wRhnhyeXu3GtPNx0zvV9XtEc39hGDEc3/0fQnP32OqTNaCRQHhqM0xYAUQI98BIqWZDwdS6ypAaN4B8mBXWxa/eEnt/BINtCM3LupoEQ/KkPygC+OOUSAY2cM1H/2/jX6lZQg+OHtchVYTV0aQMaez2IADv/AqsSm+DqU65TJTV7SJNE4hfrAAf4Az5raMsRRnE5bXqT4sqg37c/uZqb3zB/3o7HGgcAS5F8lBa9KRvNf9qkKru/tCwYmIID/+nIEBKsACIIMMFe550P0QMYK9xnnDojYuVhjPOGRGZ2rDYaVcgAoBz/gSVdQy1vVdKvV4gzabyWzNh/hObi0vjhc1WL6lu/G3Kzpjat3/v/03yPdVlYkB1SkHlmEIiOxjjzMIjxC4AIIl34DDbEDGs9b0m2e7U4+9CJiYlMz4PWyb5XhQNYpN/Kl9Fi5htx/jr69X7bp15VseHnm4oaeyVkbEPEd/IIAFt2AKHaEFJtDzIU2naSGParULLJJB2oOT6Ac2KOhfltDtR3iiNmALDjmMI4nDVCqPVerWX05ahxgwLkkP3AIhXHClqXiiQg7vgKCliBKNtnw2+8h1YrBZvJwFHzIIq2Sz5c5ce4xh4PLrZzhZODTRBsU5H2bGpy8jZU1HM4QBdiEsrrQ7n9xd/bdesQJiCmooP/6cARX7AAAghI8V7mNE3Y9x4r3PYcdyF0bX0ecT1EOIuuc8446IAGXb+BKM+FazpZYlxOFV+WE6XYhcnczbPtpocxzI9nBgG1uyyt9P+rdP6/yPuB5LVcc4JFshK7KNf33UD7TxQHegBABNyASR5BorH3F57R2EXXpCCV9kPvqLOT4uogjjGU6NqDNdpXv069W/+nM48/rppMs861FLYhf//KE00EDECQQ7v8JZ4BdSkxbbjuHBMTX0ROsJe5HGr4hJ4XJ9P0Bf1vI+V+3bl/RenDK8ondnQ/3Z6mz6nRvV0Wx0W0E5rdUEEOb8DtMYXUjki3bVmHbWcHvUVP8qa+Ou0MviK+Ols59Ohur2ijtzOv/99jsRBnHuZFm5pfaxSq8C1yYjXk/7J4YdamIKaimZccm4AAAAAD/+nAE5ZwACAIXMFe57RL2Q0dq5zzihogUwV9HnQ/RFRhsaPSoogAgGbfgS0mhio1m63uNISKpwN98ZXM+ZO80Lcilv98nhanzlTM+CH1GyebYFbbR0sBaCPn1I+h7vY93JOW1Q631QUgBQw5vwJXdzNDwrGKIhOYDYTXW2oudsvmfGKuEzYg54Sub0fqmojQL27tp7/26cM+T2KrzI7OrvsxAq0iBu6fGg8bJAEAHv+BlwfoYIjrK+tadUS2NSm57AV45aONgocsUh/f9Ql60j3v2aczf2axu1xS7iu7AnCwoPvFM46LRd8e0llUAAZDN3/4a9Vgkc3UoOUykG/iYa9i+gXJuPb3bGYK0p18FHJciEVxl1bP60RV579mkbJFxNaJTJ5NZb2EqTFoutW43YylMQU1FAAAA//pyBLwOAAiCJTJW0eoTxD+juvcZ6haIhMNaZ5xPURkMqxz2NRIAAAgArvsMLMcZQqa3SJLKS3VGtyEF1cC7j+0uWx8WWaHdffQ3Tg+v327fys3XSYoXDHSQDgUQSW4Y5f6YrtvzTUrpFUAGID22wcZIoVi7gpiHtbhgeeoalpo8bHedoQDixNHf/GAatR7qX27BWg2JEIcMXcqKJRO6W3xjIoiqq6hkf8UILu2waqsoMhW4yugeNp9Pa0uSbdgKtjTldSAwqigG9tCWUB715Xyvq++31e1LbqzorCCrWxljXql5/ppqbfCMWtfK8eEAGW7Cia2RgN1Osa4EbbS8QQMVyAz6kSPLnLq6w206YYClr6uLhP6Ouj3TnJ0Vr30vqKgc2lCUtaSWLm0AxE4sKbwAIBRiYgpqKAD/+nAEs+YAAIIBRlg55xL0PcQqsz0qYIkcwVTsNKvRCB7sNLEO2obgB7/gSOWSiPPGEkdO6Ydj4IthriXxxMKFtCXbtqH/06P3ZltVunOzn1U7GOYCR1XYiurMt12oDRSOzU/f4FUAHLbQ1MsgyitMC6TA9VPFgHTN9DW1BqfEHznZwSFJjhKGnbRsSgVPk9Gbmclev8/rD8KhHrWrpDe37E8tHABBDltA9859JYm/LogyIefyn4UWQODB8jcZJ9o8zyaAnwoOzlzo6ZrlYThsytDOZ9R5avq38rVdiYo1RN1jwlS0k4kAVPX62lwSYAJv/go/FTzY4H8nQfSJ/qnb5ISvJG3RLCmAWZ7xu3bBCNUjW4/M+7797V9bl3Q2hnCLOTJc59K9m+gccd+p6q0xBTUUzLjk3AAA//pyBKv6AAiCAiFWGwlqJEIj6qdho16IpGdfR52pERSW6+j1FXoAOy6i7PUkbKL07wN3GhXkWif3qDajfji5N0QrxtUxPIGt9fMB96NlF7qfOOwcf/AsEMdF7uTIPCAXZamDyDaqwAgBySQVnz2whk7Y8GNjSMKCnD0fZRQzhJvjjZpm9EDuQXmhZk89qbEcV6yyMwAXRMTGp4pv1ZBQaaKM6E7mf9upYyAFbbYS0od4iWV47To0MKOUUm2Q4VXMNYqpqYfyzW/bODieet6FsChQwhAHMHIoHS5oZHi6lLJpW5T2o5FUYwygxQL+kkQArtsMu7qZBTbYA84cZRYxgM9RW5i4w4XJPi4tlf5EB9/1/ktR9f7mad9ApYOouNHHiqhqTIYSLMfwfodOCi35cZbpQmIKaigAAAD/+nAEe9EACIIQO1nR5hJkQQMq9z0rXIhQ62RnpEtRChLsjMOJ2gAACACu3482z3VVGxUFpcHSDYfQQh3u++7DHK/NX+LGzvnahD/J/9jneQjf5NiZz9VV6ETt6EaQQ8LCKq0DMn5uGAAgAy2gS4h4PtcMA5DphmXOOOR6HhJKzrftHOqXz0NbvFZiFrt+5+xomHBgon0A+aEHgCibtNnyjlajhQMpu/vpABdu4xqRJxNkaHoOHBbMvfCYeEs97Xs3Lr//kDRmxtrByUVqCyOfn//mztr+fnzv23z9fqPxcP9P2gAqfFhYn2B2kAF67jaMEpH4qLwM0NJ+Ma3AluJnlRefP7ZEwQyVbbQDANs9v/pxq8gBVbAGYD8/aqWOF2J170UUAksBmXMSWwkLDkxBTUUzLjk3AAAA//pyBEj0AAxCEjtaOYkpxESpSyM84nqH0GNkZ7TqURck7FzziXqAAAL2/EFZE00qVwoBtIZ8vljQN3pzKmyJwkwNERbUujUBGl5v/59W0/vQW2e6ZuK6m+pdBaVey5ithBQfnrwJavpAAd3480JIucq/kpoAvdRrY8F9itonO2ObH4vNE5zYu71CwS1Laf/wvE/9nx9H/TCto/WpanGnY/0fK/0e5meGa83O69AADs3Eke8MrKidrA1pRRXPc6HplCPUnnn0UCZoxCg2bbKZQVv3L9Z5Tkc4qrb/KWU/TpiBAgSbaJmRcylQ0rAADJdxI44Zo1TDU6ukED2QKtc3CpeVBDrwqMg5cqc1TdHwIgm6ttW//BPvq/07f+fRsn4TiGqb712+y9LxAbWksVmMgTWmIKaimZccm4D/+nAEAZMAAqIYJVebCxL0P+SrBz0KgIg442bnnEtQ9x1sTPMpSgAnJMPo9O6lhsgGytfzwqaWafwKI3UwNF/NPQOamPpdGPSi3kP9+8JRq95bkf/UTmGu0yFmun1VH1//loX1jiZWsokAIADsuEl+QWkhGUPKRclR5oO/K0/BMLaBStqsPBOCUyEGpbKcF4VNmn9/+nJLKi0hZSvd4dnlt2fRe8oupHtfBHb8ZjdXGr1aZbxsJLQ6sTghYHTUPypekLT6lbANWqMNXiNP/6tv+ui5i6bcuz/MHQDEyxYIPVchkQZGxzjKtSiVuwxPdbWLqUxkbIHTRONaQBYUjkoL9ewYOFXJNSLKNiqQ96f/zuWzv/P0b3Vu3/V8o8TPoYMex9Rp8Er0fWmIKaimZccm4AAAAAAAAAAA//pyBIctAAQCBjrZUecqpD5kaxc9ZWiJHM9k55xL2SCdrSi0iOOAAAACZbuF+By+JXB6HmgGIde0KVHglqAnQ2hedgEkK2HdGlHAtHof/9C7Nk/b8v1tk/83FbetNFthkSgJVhWzqAgABLbhmeVGpqVEE2c4Qb930ltAS28UZ4NvQ+KIJYotUF4C6NoSlq//xGKwxqer2ylGkRQ2tv+zmDAT+NWL0AAvb8SuGTmgyFVCPFvP67u0RFoBB6CX07MJ48ZlSWj0Ej8Vp/76N/x9RGH0M7q1x0ob5ud0eyl9QxTvuf/41Zv0oop76ZqJV4AIQACv34MXZcRoChc0qFLyTusJ7kQPz5/ONYGJbDfhj/T/+bUm7bPM6r1FVdJGanTVc74dutL40PgfkdpSMfes2CK97LjaCiYgpoD/+nAEDloAAQImOto55TrmPodbWj0HNohZH2ZklE2ZFQyrzPQdwoAgJXb8ebJ2R+unldFbpxuhsHbFt151CUTI1fLxMA8prVtP+rZxag7+vbbv++j/kcqn26RkiNJNbcqJ1anvTaqqq+meRgAgQAK//iVw7Y6wWVbgFui3khE0e5bb1Y+VNbX8dT/t/zGoHY7+p9y1ldtW6y6d6PHaOODAV4vc1ym6v127gBJWVIJ+ZIfLAb7s2HuAL1B9PFXAGEXavXUDN9pzjf69WzftquiKqUfcmlvkerLlYy7I68qMlluMZzUtdZzHgrqyGgAXbsO6wnnvDaFYsbD1GmxNhFi0MoTm0GcbNzCwdEI1GKjZ6vgSGsoXkPTEPqemitLlPR2OOFQ7Wi03S+Em2IUgwaPMctMQU1FAAAAA//pyBAYBAAkCBDraOWU7xEBHWwM9JXaIbJdkZ6SqWReWLNz1nOIAQBU2/EXj0Ocxo8qG3KnoH6BLCwbXwpBaQ7bfKhIbTlv/7Zjf99X7W26frrJ6l3GjFL2g/iCoyE7zrQPSFowgoAB27DE2D4c4K2IhVrJxqPneUQSXdV3zN/la/67gSxVh1TNUfgKajHk//l0fX8z5/7fv/1bHvJWTMfYqsx5Wr6AAZ59KFBdtruCUmjbeUmE8a0a+MfgOUVhQnwfTs/z//o+JDuH/5Pr/c/Mzx9mtfc//iZyDvMazcozFno3RldxoDu2uYGADv/4kviZN0KNAvgdtRPqCdtmFxONtPC6hDs2V1fd9sbz7/856BjKP1qXQ0ccQcDAkKLlnseGlk1vb1dEzIr3kxVF9zExBTUUzLjk3AAD/+nAEQQkAAIIPONnRKBPMP0Sq12HnPoi862bllHORHYyrzMwcooEARABcbgTHuBYS6B70hDcjt0RkyU3jfsQ15kFNp2t40Se+gjXitf/5xqBfuyyNlfuyWpr+oKyxlNNPVtSxDlOX0gEACkkA1I68aUZtFWb8t1g9XrWPQ/wgzhNXXbUpr31/HO1NlltsDtQM1eo8dQto8qXr+g0OcTBLZ+nvR8hDV0BgC7v+Fbx0l2k0zxNZYy8xEBDSm9U5UYlYxxf6l1agB/7lf/V9mzerG0fV7/vqXb87QE4CaUQXVetiDBwrbCk/IXFx7RQAF27DUcIpCJOSHbeiqvxh8xlqIrMdh5pgZ17iIHhdhMHPhf8Fpa4pJ+mRsxz8F2L0kPQ8OLIShwLLPOWUJoDB5jRQAoquRYmIKaig//pyBJweAAQB6zjZuYcT1EKHWyc9JT6I5ONiZJxNmRISrSh2HEogQBk33G9ITvb5y8yTMqhTqwMXijUrqveOSjcvy0qEz172M//bBd/+hsj+SZpXnenIJEqmod0ftIjzAB+xMQwAZtuJbVU7lkwSPtQh/LOtYCd5f84Z33vvQ9sPIF4z8Bj/P//LoTXzLfNqbfN7f8nEJLv3MZuIgITFDMJINiRCEAFTb8d1mTFg+B6CAE0E0boKCd3BfqQ0TdonnG6E9GlQYXrxtf/4Jtv23Imz7btc1vo4p+tTWzZCXWnt8ZJkZVd58CuTBOcRQABYgq7/hSEKuYVA3GZfPaOBtnIPQNYoafHmKSpHn9sRU3zP/76EZmu1EhYo+5JSBRGReGc8KKQt4ofkjTQkgfSq8BbUxBTUUzLjk3D/+nAEPAcACAIRGNo5KyqUQqdbEyXlGIgI8WRjvEVRGZjs6PMI4wAgp7b8VkgdFuH0YQo2meV9IJs4Y9R/ek8SjRHQZ8gROEbSj+LB0i4R88LGOptrKE4nuYTTMP2NUIkMLgDayQNnTwAU23HdQ2gJAoBjtPdZyv6hMAsS4nn+zxoK2L9sx999P/TR87/SkkqNqhi9VopH20uJnh/fc/jjwiUJTw5iEEGJJEQCrvsEsDoGXquLTdpViBvrs4taT2oNvpK4WFPoJ+hW++b/z5OfpRbO1+7VK5ZFdDybszoDSMu1429rdBXWzb0KQCSAAPf/iSLip9cNaDHN/BR/6OyFjcFt6MWpW07cm+XRv9A7QQLAB/Kr3eptaRn66i+DtrRRV/99nk7X7hZJtT32n+0332mIKaigAAAA//pyBD1QAAAB/iVYmYsrhD/DKuMnBxaJGOtnRKxF0RuTrKiXnEoAObbjI8BiMZIAPDruwFlCnCCOkNFfGj8njJogd8H04v+eqt/qbGb6h9r0Y40UeqwlcdTelXo7ws0ycqJsOtYgAFW3BLcDy4eEJFaGo9pMoECUiTqBr4rxs2hMsFphZqiX+Io1vRZ+iyu8RWqi/21XGDlP20OAEIiUkgmsCC6pxAAAyDbv+O/QaM8VCIJ9ihe+toR8kXyt4e0KTgZVm6po9QL2hlcIN99S3DNt+iVb7UZdjZqNORAaMCHLtbNpbcXLp3jw+nbfxYAICAAft+KfogWaJ4eAS0SKmyGKHyD1SjCc0flG5b8Ri/3/+YhBmCxVlIFlhoWebkqO7ycKFw1wVCD2kL2i49YaQClwo5iEpiCmooD/+nAERVgAAAIZOVnRLykUQ+MbJzzFNIiEk2RnpKmRBIns3PKNIoAQEBCu/4OtWBh1JJoI/SnBleFAXBmqOydyxTvl3fFW+3/6k1/9tWs/tKp53QxtERBJ1MNkZs4ZNga5zmC7W/TEpgxBQAG7fjLvK6rg7x4D3O6yYygK8PNmyPGRFw44wVehtHwFdnf2XlnOelLl3NNIQje8g6PeEJwYmCxxiC4feQZfFAjJkwSbtsM1Yzjgp86yclc4i83bDCj94V/7vB+TkspqJhQTehtdB4Zq9CV/8TqMHBH1pWVcqkm0paJmCGSRnmY9Qym5up72AEABm/4zdjXn6fFnSqPiLYJkYwPhbZcMnDIjAhEUedU8tyH7al7dLBeousTta2C5lDOFW7QCLi4uohfFDjohkHNTEFNRQAAA//pwBI7ZAAhh/CVYmekTREPDiuNhhz6H0Mls4LDhUQyZ7Jz0iTIAGW7DOlgwNmCDbHyQidGSvWwxfNv8LbfxQcdLrFnRChhYK+f9C94Rt/9RWCnGMvpt1i6neNTtax9JRWe/vZkwAHbaN0rM1JL/U7BrXjfHwK1J7CDFQlKX139p0j5fi2dD0ImBwLJBaM76F7IMal5R3xPQQ1XrS/YdmGu0BN/9+vp/5MQBN//FITNgcFZ2gNLYPwju75ukoWdKDxaEbKa/71Jr9P/zPo+bvstNGWlWIsompdwZQYv3naCql7G9S9IeqB23HmiHkgi6DvOM6KVF6QX8wZ26/d0h32foVHsLHuFfN3sUXrx///3/0nRbd2ymwai9pCJOh5Ds6CbiAmCwoC+tpNiYgpqKZlxybgAAAAAAAP/6cgQivwAIkhci1rsMK0Q+46szMEd0iOhzXGww7REBDKyc9JTiAIAFJABcxyWlLi4RdkWEJduJBunLtoqVNXPq7M2oJw2ctJSLQetgpxdEA3DuFvEQEXGtChZytT8ecKKOEberWyz7kAFXb8ZwODuRgWJoZEWzPWwuaxfahYITUeNmiAtN5vfHhRq2PUeLDaGHlj67ImiD/TNDhWXRABqDalspdXUyZABdto1fkqvZWMtS8LlXInBEQ5kwFiGs/X5jbKRFsn9oexbY5RMCMXxdlawKlsrmkFeg6q7svHnESl++9obRJuZraYVxPZOOYCTMce0FbN80CTFebmxwFpMeurxDwoXrUyQm+2HtWwiHa1t+JaOt5te6WRObz+fx72CBQAE5elTxOPHgQpRQipMQU1FMy45NwAAA//pwBIKQAAgCGBjaOC84REIDG1oFhQ2IbHNo54UOkRUMrSjDCdoAgJN/3FypbZ0izKYk+Ea31CZy70HNzELVeJxyI7IPat2VCcrJfLvS46qdFnhdR1rSKlPD4ecEiTqv/5G4eLtc6k2XAAGAAF2SCy8JPkEaj5Blzq6ZS+gsQ7XoPVeLMAgbI2XVriTFvE31tVdy1XYED5Nqhdbxce+bSjLraYdbYjUqEW0IQGlv/xndVRYaBAlSTbo9q9HVXBUY5zJTdXUM3If1iX9qhij/75HXcpWA2lb3xosbnSEco+mtjBfydeLlEruMDaADdUgANIBF//Ev3JrhwC1S1kL0cjrK0mNIXrxO1DYxpPFr6N9WvByV3ylKbYBD17EOGGUElgELSRlVIiGvIyLCdVzl11lVO0JiCmooAP/6cgR5mQAAMegc21EjKrxCwzs3MCaCiOh1XGwwq1EJmGzgwo4ygACQIqWSBOFE+kyADJMqWX4+gkzEnYn3UwjDA0hx8i4m04O/zjlO5l6liHjGmIrJl9C91Dt1yvMqbXkLr4DAk3/8Q+JokkQahqJuXVM5cXfYs3ucfREnkWfsh+VnL3989GnZOr/Qp3DoweMuKpB5C8WqIE9xIqM3wgsU9tZabjwA3baN2MmYSsqvHSlrUoLBQRpEZXt4jAz2ySmuNt+txHVGcWHJGUg48z6tGhBa2i4uc16LCT6kt0JRT0v7LmkX+nOufm87mgAAg3hNjKC9YRSzU8TRcNpgH/rNRdK3GBOPQNiGratgMHfo//5dD7+UOsay7+eDAaTOdpJWsW6dK+dAphbs6aT5UimIKaimZccm4AAA//pwBMXUAASCARlZGE84REEDOwM9ImiIvJdo57DmkRcMbKjDCdoAG77CNhEzn0BNsI2m461O47qBZo3yzlXOR0B+0QTzO/bFuSKv+J7xLfGqJ0HxosDh0AA+MY9wteh/or+uKfFlgBTbYeKiWZAnqe54IRZcr95B10Hwt+v/UUm616QXLakL8j6/gI/Kr9dR4wrFkCp21YJm1aZJdkXI4AmbnljidPTAClv/xjWVdGIEO04ha2LklGOjpDIbOVc1HQR8qxXq2lCgbGaK00v/7mJsTiRcyNKvfJDj5/Mqpy94shTn3LmqFU7L1uNKCAEmfbjOOAqU1Hs8V+PxcjwKJ8ipyRX47B+62B25y+z6aPgVDk/uVVsKICIfOBhDbmLb0mNIsKc5vNBkRjxdwuJy74WJrTEFNRQAAP/6cgSoVwABggMc2jnmEpQ9hJs3JQVSiLiTaOC8QVEXkuzckYmjgAEXf/xjeVbtRngSZnqgGvKX6G4+VwbolA4uCsPoulJn+UV8s8o8rnCGacmTnz7fCzK6S4DS6TTc9TQEHn1LfRAAEvbbhKQOB6aQ3xXEcG0sH9D8Z3UNq8PtR5uunL2aVMv+iM4xrXIfrv+h1/YuuhCDFq6DAKkp0w5Sxds2AUMTXtpuUI0EcecBctl5XkEs66wY6lgo8IyDrDGy6n/c1H/2bDDyOPwsbCZ1AoLiWxz5YWW5QoRDSmM2CmMFClQEZUKMcQEu//ing8WH0lWxFyMgZOgpnPFlHMG3euhPx2305RPziaf+2g+zvvza/R3fFYO/7j+GL1zE/A+Wva22rSG6Q1+1Z/4OH6TEFNRTMuOTcAAA//pwBJvHAAGB3hjaGC8oREOiuzckonaIyItrRhTrUR6ZK0z2FWoIrf/i6LOzkEWcVFk8mN42g/bWFvWgwWnZX0Pk1DlUMK9rS58z3FX1tC6nmDBi4Xpat1Z3mLehiUahVqAEBB3/8d6wYDtYja1ETzsb6iHXOQayLoRguMDDY9Us/OK+28brExw+YEYEGh8cReAz59ZaNecD0wpxOkXvI1Tz+dYtEAA7BJaHMM8B4hB7sJMYoHsGLEm2Cw1QiPoeE8vqmjxwA19GmN/8ocWODxe0gnNkQ0L7R5B6C4CY3CzeSWMWtAbKMzS112ggO20S2PU0RVK9qV4crobRiHyEAr7EfcY/cRJUB+vSxWINVaEEAzfJwFN22/+hP/usjna9GPzqPzJBR1dRtybWCzN+qvT05BMQU1FAAP/6cgRbJgAAghMu2TmPKTQ/QxsHMQV2iPxnaUeYqlESEy0ogwhyAICHdthvXMFIFypHiakiEy6wlapl2ca9R61h0XGI+O10B/wov/1XR0d7bIYqmdsY50OgqSZ64cfpOktTvHjNKCJJABAindsN4Hz0bib64QLDugxJiTMKqqbkxc1Y9htBkfACCtT/h5pZwVUf917/4jyodqCRhVq0htPQU9KPyGi0mgASALe//HmyuXRuiRumSyXkdwu01a017qxoxzCn64WBhzLsV6SJZ762AFQUAQHMBQfXBoWuEEPj45VNQgurFjaFOVMD1BQaoJA0dt+NRKGaQTOAdnI/a7jKwzJE0R5lgx4ThN+Fahn3/85ROplCE2mH+FWZmI/ag0OKDgicBkglJkaca15a80HlsYNvTEFNRQAA//pwBJnFAAAB8AjaOM8wpEFjm0okI4aIxItjR4zskR+Mq9zGiYKBgJV//DYaAoiII2qlLU4vXBOpqFE8eGSyznid2/5WtTu0TPGAkmacScNZOOYMBJLA7wRD64oylkmtYvStagAAgCc3/4rFSdgHINzwyipOPUJZ63mznZizcnjzO6APDFYNFu6AmyLFaTILVhgzQVYtKALyWVUJLgLllP1K2OchcAAAAp3bYNU/WWgvx4jyXVUJpvCdgeeGKB5LI5UUZs05q/joBDOiPNIV/49KjqYu1uy4YfF26bSR4Djx/a1iUpIXvecYzvgACFNtghWoQRgRx0NxqsRzHawTYKdnC7iQZGjgwjuR99NeYfWfJeQpR51CSixMSEY9hmMaIhh0459rBjimWADnS6zahSwRpamIKaigAP/6cgRJXQAIgiEl1xnpK7RBhNs6MGWFiGCNaOC8oPEQjmycw5WKABttoxO3D+ZQ2wRgURTsSHy6uXq75Abxv0jml9W06NIeptP+uMArfUd/8qrQeMeS2kqT/foU2VE45jtouhd4p2dnIvSAAGgAcjbGedKjUNSl8P0a/H5hTUcWI+NRpnRDm5V++vESfCxCq/1Ei6kcXRh2PunlFe+111a/Q+q0xWlzb2F77UEBCtkguAoMspReDUz4ZEC3DjzsM9TikJMqclF0DW+o6v/HjxzQYHiCAhofxjUHbYUPsaXIvVEj6GplU3lGJKtvWlVZAE9tuJjQjxSahM4t6sLmHlQKvCzZ0DKvZJWV2374DFpegLZ0nTwaFXVsGPoAwMpWtOZahdBH3TvEB6AhzxhMCnCsIk0xBTUUAAAA//pwBOD6AAAB+RlX0ewp5EBjqxM8wjqJJJlnRJRUkRwTbOiUCbIAAAgCpbaM2gR3IUwWYtA0bUUlon54Z96rtvEUtjfxkRa44gxtfjQoV1nW+dtRr8eMXoY1D3Pv0fZsa1Cfe7UAZtthm0C0QTg7kodzgJHg/FBf4vPdFCa7JOMrt/yk+EA6/GyJNKECi+ACKFpQKD2OfNMWJq3Dw0xOt50U7RS+tAEAwW9/+LguFD7yoREdMT21OjJv0Pv4ljIjep1oMHZvQLKV66vT/QM9mxsoLPJCIYlxGDbmDRGVYVciaFuLMUfGFlD3VLUvsNAAgkRHt9xcJkHJjIcn81uzmreK39DBqljuEoSjtq+/0yjAnu3bQ3/uC7uwyvsRxRGHhELqcGlC6YaTzApsXXDraAADTXYiQmIKaP/6cgQdxQAIgiQlWlAsKGw8xGsTPQI0iAxxZ0CkQdEVEezo8xziAAUIAO2SCoWYxPE1a53ZPLZWmmJdhmJuMZKzlbb8U/Cx+/9mBXiUkLJscQfuGSrIRPhvLsk4sD5w09KDY216G5e48SFgVNtsPFh4finlWmqlpKwljnwWwwIdNUEwbTNjfgQr8D3/lBaiJx2LqYwwM1i8uKXMSB2rfDEdeL1ilSf6wgAT334pVo0YB+ZzW0/TXizKyd3BauhPVW/wQCT4N7fAr0k0rUKOBGF4Prp11JoUJEgJ7Xk2yA59zFEkFanPH3jAG3//xir1qdqkkeitQgNwbqOaUlTDZdeYRq056DaraANLVV6kv/VJYsLKYlyxdbj5MLCm5RJ88F8PlFt3WZp1o1nunxqYgpqKZlxybgAAAAAA//pwBJCgAAgCGCLXuYUT1EIDmvM8ZXSINI1k5IRMMRISrJzBHaqAAIN22EHuiC8KlJsA3XoO+nruwK+KBT48YKN2G/Xwb9f/wsSD6HrDYmFxSYMRZjlbyKhritqBglWxmxaxjmhdcXbpBKu2wk7axo4KQO8x1HhX4Y8Ib4bR4PjAVFJTgIGStV8vbA3V5RT5Vk2eniA41MtFSTloqe9TY5JpvKZpMZfjt1+cgAHLJBT1hPqMUEQk44rp3IFgXdNHQSIrMnfTUj/Lu3+g8iL20wZIjzwQxKgXeB2wWdItXNmxR2pDp1bYqWOpNqEMBA29/+MR2RvE4RQzpW3G3n1D5HufNdBhr5zUV81FBz11N/+hJqkkMfOAmJLXCrQhWjVeYYJxTViI01anDnEwG60VotYmIKaigAAAAP/6cgR0nQAIghAV1ZnpEtRCxGs6PYIpiJiVXuY8ptEQjmuc8wmaABlto7mbApNSQl2JkF2riVorWW8SBv6GcuzEuycfQVQdV6btiKpJ+b+Gq/F4asCG0iqtDgUo6yChSi8ws19JhFcUAAJAAqWSDzwJY5RmA3WYa3clTzsGC5NbpGlZT83fI/aif+wLboQEhUCWaxQXFDpcNsWhjHQw409gvNcLJKWiOxgboIAnttxvsCtQkfwm7acTXWSN7rTId1WrGR4RGxB0O2veQENTs238pquIvfUttDxt3R6BY42SVeE8+KLB5BtAifAQ80RUswAd12EtmxX3G6CNFiQ18ZcRQ0QfSJ57x82R7F8ockMNO2uj52+QB+Aj6ws3IkHuWwax9uptuOaUPgUKkDb1qpqspMzCYgpqKAAA//pwBOVgAAiCFhjXOek6lEAEu0o8xziIQI1eZ4jvGRaSa8xnlGoAgBV22GaKdkTTU9M8OqCSSR6w5y8LXmjBVjUqKZwglF53eg35L5VxR7sNnqxfrQ5ZtI2pLIG7GPfQ2l7VOe6VAyxQAAtAW//+M6tFoYBRQJc6g/Qw0GjCCs0qXrulWu2nfCQ38v/9bopdRGAyKFWTJky1ya51poOOcuiK/jzLvgy/oAS3/w9W9faG+ITPTUcsvhqv1ccFsMDGMuwqKZy9u8qDoX/bX/oT8bMZ/6vgolPmyJ/ovGQZW1n3dMnod/rLADd/7IL22wcacLcR9BiGoY2ECwwssf7Rh/jAs0ucNDEHXbXvg/6G/9xrogicDQqIx/egGV4saLuAq4FFka5IextraAZK7b2GsemIKaimZccm4P/6cgSpEAAEAhwY1psFQ1RAxGr3GecYh6RzY0ekSFD9iGzoxBzmBLu2w1ZlLlSbjYIJIkdXFWnMYhUUwvIGBYIURe54dYaV9tb1Cr8N/Q9Y4e+LlR7S6UipjqZYplQxzkSGEGaCXW9vSRAIClttgykQh0LvAF6g4Sbbk1WFje4tL5opFlqDojt30TvUO0tVv/WXWtVCmIWLJmNGWN6zz1qkXC71Xbt7nMT6JAAVN/+JKPJItl0nC0uGLxJWZsJowMZFeHz/trkC+0W6vSkXHG7xRJlzRSK3E01teRFGVKo3U6z76FaynXAAMhKkskCF27UcRDD0zId4XcW0AqKMpZNAhB+B1k7rhEzBie9q3myzSMqWIlHaRbUKi5As7H2GE2y9DoztZjExBTUUzLjk3AAAAAAAAAAAAAAA//pwBCQGAAiCIR5XOek6VEIEWzo9BTWIRHdnQLDhkRIeLBz2FOMAgBt22GNpI6o7a5iuE9kAyo3om6k/ZO6krOnoKvoLk6N9/VGbtdmMJuSp5hrL6pW4zel4ge4o9o2AGjdo2XsfZu2r9AAASAJWyQY3uTn4O9UghBqHQ0rPBx1HQd0Gj3LVX17XAO++O/9xpOgICoulZJJQUZejMCxBJtZevSPFmXduh6me5ZIBS//8WhOfQhpBlJRv+oqVexq9KtV0V668ztHgCy9na7Sj+UaAxwaQFBRS2EhZDJh5e94mcxloqXcfX3pYtX03Y0gKv/4Hj1h2JEEJZhbhuvUa7PuTo8/j0rcYLizmxr5NWuBPwGC6E+uIi6LRE790ZdKq1rIdtXOlVPp6DvCmjcfllIcOaTEFNRQAAP/6cATItQAFkfck2TjPKRREAysDPMU2h/BlZGCwQVEEE2wcZ5xawACrv/w9FYsUeqvI+CEGIgHmbCXdWS6DBaNdTvl+o9/oszf7OsaO7RCbWcQfFWONeg0ULNoqahtpRupvTXTUAlv/xmz1iySMYJNguUFSW3MnCDzujo7oLu61Xr2wdtT/pcQCKd4DYtSAgwGAHHutLvGlHCouZK8yuYJB+NhRzUtIvCYSeOnURnZyCZa4szFqagvB9lZUdATyOkej6tUTyr/ikB9LlhUGSxMasUmUmSFwbBV7HopKGnmXTQthEQPrmrVGBTzwmJ2T1uIpBaa4zqTqH42LKo4mry97yvTVsY/XRv76+XbNkCSB4SFJ4WDUqdHDZQhpC9W1tbB6ByaSD3qSs0mIKaimZccm4AAAAAAAAAD/+nIEuO0ACAIWGVSbDypkQwRbFzzCOIfom2VAvEGxFY5raYYU4gAZJIMZ1N9qr5qqoEAKUMg+wsaYUbUHS8SI8tdinxJWXsEk8iKe8orLoIP+2ItYXez61C1rde1Lc+31sE0Z7bur/6YGBl//8ZccM2yYECR4VaRvZumAPgHllRq1MErhxqNl5v4//sDM4CUOeiwk9L0H2iBZJF7SbZhxQMXuWtC1DHhwxG+wIASrZIKbXOyQLLjV3JeN1vysiFCNRrn7tDNCcmjbN9f/urOwvGaYvHOjrm9bVzQstazBhtqXGHUGKa0pcQeaAACAIy22jVFGIFpEthQ0fIuDZNtjmTwPX3jxHYgUCorKQYy7NQVez1d/iYABN9zxCH7Ji3dfbdcfZP5sw1iIQNs7cnl9CYgpqKZlxybgAP/6cARw7wAAAiIY2VGIEixCg5rXPWdUiHRzXOekqlEEDGx0wxTiQACEJOWSDO5fx0FggC2xukinAVdQNqY5FqqQbKrYPNwM+LSS/CtamKtHtKngIJiR55ZL0ALprddcqgiJN4sF8Yt1IuRgKCnbbg1UhJaCFrBcFIWmH7XpkrPCenFXuJhvPahranaENHmBht+JK3+QvQWkszXPqvzlf1Ckyz4aFnjVcoHniwqwAgKl22GNrpLxRlswXhY9n3zODPOCecUxJ4adamxjS831Fh/xRfqS2tjQkPMkjzxglYwFkTox6bkf0WirselKLnyIweAAADCAVP/+MjXFioKloINskVjovoNIaFZQkG+ZfM++j4bwEvx7hWkE3Gn1C8MkUlN9IRMA2BqicVDjmaTs9omS6YgpqKAAAAD/+nIE19IACIIHM1e55RvsQsOK+jynVoh8+2BnmEmRFJGsDPMI6gCABSyAfaS1MhIwzgCegsWL9m90c0uYfDauoe0HHJ2/Ff0F//imR6Fy/tTyzZDqk5KdMw03OgwmEZUUEioG5IXcAADAKl22Hj0zCJAQcrTr04Sng0orkaQKMH9SXyRfbR8Y/KBJpztSUxwlDOQM4HQOsv8tFm3tQ+tbIJnBGELSe8SIeAXNuBKztjmsEyE5JWQ9lDFp6rk3RbvepOO76+4xbQRrI31LcroIcWUy/uFMUl1T639HdUOwoS9f/9UXMswl0hMezdFQApduJXNSxpiDCQp9Iilm6rpk1cH9vsitVFUKgRLqjfTfQaf/uUcSJKP+I1njISE+uejAspzWK3XYLXGh5Y6ZCRqyZBR6YgpqKAAAAP/6cARpEAAMwfswWRmDEmRDZkrzPSI+iLSXYmeYSNECESwM8xTiAKm34hgLhaiEcCoN0TvTHKixllc8VRR099wpgmTt9OR0Gn1/crqiV/bp1lMoI4ZR+ZMuqBx7nAkZQSFe7vR9QATu2DafjFCQ4LWEJXAUahFNqcoGGcKbneR/x26iNy0PMLo4349kggEpHq33UbvX/+qrdyhnUME76B0FjKDK7VN2VawUtv+MxCMK6coZ1klm0DJ2nSAtwp6PH3q41w4kPKrL7vogcZP/QMLKqCjuZviz2QCs6fHHy8ulgkn2tEYqEbWlk2MYn7Fgp3b8c0duoJPVGQ0gk5SeV6BNysdJC9SIHHjGEVKg76iFUcYGmXt8WSVK7/SsFoduX+0YJmB+zQFSyUQ2/rLl2JiCmopmXHJuAAD/+nIEJI8ADIH0I1eZ6So0QoMq0z0FSoiAc15njOmRDIyrnPSM4gFN/wPOH0nKD4GEmi8vjKNWsvjdYR3W53ugw4OkbTX5xb2KOFCaflQeMC07SdLAmq50AKPkZRxCxQfsHL1hgBG7cDkub3BaDYGEEHNmlnoOCilAHZwPVIslaepIMHxzxmTvUWAphhv6BcDN1WZRoNKIpHzyptqWkmBpyjbljVPCiwDdtuO8LsWBbDaLooi+wBYl0OkALjH2wynhkAG0c3b8s3oERwKLDDIPji8UayrHHyyEvRdeqm19Xq4o8UAqUtNuPQEVKCrd+BgpUiyyDmYAKR0zFVzIn6FqDQZP2bPMbigNXHBoc8z8KYhiwIqE1aS1XL3OQOk5UULsSsckEFF0CIc1iAMczCYgpqKZlxybgAAAAP/6cASUUQAIkhgc1pnmEpRBgmrXPWNGh/hjXueUy7EJFyrNhJTqAUt2wxKnyILUb6BJ8ZWlpJfBnQBngRBljXXBuHa7mp+YoklMIKV5W6yuy90raoqRCwoPMmkpOrLNay80mQ1kSVaUbgAgBXbYYHmd7O9J+lQgg5pzNA9JIgyCHAu22Cj2ElC6yDuK9mh/xLALEOvzRcQpqqPIPmzZKphhhs3JqZ3i5sfaoICUkcHsQNCWkPk31QeV3beqnwto1C3cnUhxh5TtRPjUKPnhpH4oYArtKvoUlqo02EQJqpv+lyEELQG121gWSsAu7Wo8Mi2naxgOiGx8Gj8lkJRYQ3xW4cy2zSYmxmKMBmzN6M9+VD/+QzolC67m1o6MIg6GhYoOUeYTi5eWa1D/doTEFNRTMuOTcAAAAAD/+nIEbEUACJIjGNcZ5hm0QcJ69z0iSIhocWDnmKWRDQyr3MMVSglNt+O9DvXaWQguadC5MQ2afQbNGF1wRdzj2cAbKXp+LZmg14YeNc9VUVKW926ZQ9rXCqhc04YQKqaNPhFDwTD42pt1AAgTN/+OadVuJFTwx0S9QfO1R8Cvysl9yLes+gpvLuwq3xZ504hz3w84SPLpOXirws4mi4MRiyVA221N0P3LDFDlkBc3/4+zNQ9mGwwWGwadjKedr06T7jV0Z0uV/3jQCauo9zeoTWttTF3vAQQWF1NVNDHPPRjULUo0aNtQ0hbsFnjtouEIvaPDB9VoewJKIihuSNcnOIOLcFTVZBlBYqU/Di5X60iiFBTJOUyG0BsyGoCCLBzqxIXCFDRqqgigJpYKFyM3Y5VCYgpqKAAAAP/6cAQO9QAMggcc1pgvKGQ/I4rnPShEiOBlUmekTREdjGmNhJXaCMu2wpOjHwaRYEucm1W366v7gjdxpBt2q6mECWGs0rU8E+o+E39VKvFUiboFRIaK1hkNi3Ywwi80JldhFKuPY1AAQnd/wOdqYc/IuQmEk+Xc/mk6BlcrMng2+utm4ObWL/4ADldXwYACHMAGYt1sg2C8Jm2pgWGaWF2pYqITxqsAqW2jvxOA0zaA3heKJToecykcm09qJAa3I4HDsZHs+2jD6FG6BjZ/hAo6JY99IS/FlGkuaICwSCyLULEO/2UP3ML3VyatKgCpJINhUBYM8U2xhPQQFXXAxQJd2GpbVVgZk29dAKStg5OM3fKOlc53qI9qhh9yg3dzj1J0vbWVHO6gGpLGp5+9mwb419uhaYgpqKD/+nIE0FUACBHQHFi5gRNkRGR7GjDHO4kAc1zgsEGZE5grTPGV3wDBK7/8TgnH7o0n0peIn/ZSBMkMgmFLzCw8P+Qn8MJvefVX3A2sZcnGbFISypjWOepEkeapKEvd8qkEBJAHLZIMJpSdftwbgZtG52aU6w3F/IfaaidenLUOCScYiEe391LqwJBw8achUsG3OJWpMjTKUiY1mV0TL395p/mqMqUFXb7iENKqFAVh4SGErMXjNraNBIPZhmR8Mz98v7v3uLF9t/hsaN7uppZudt322GricaSGkxt//HH3+68uv7hZxX7TQuX3isArNlMxNHX0iXx5BSknlgyR5SI6kJh7l0MqEDP17WUI1IyhrB5CrNnOrikxDy/LJTRaO5xlwR+NqE7m16/4pv733fJW0FMQU1FMy45NwP/6cARvRQAIAhcc2DmCM1xABHqzPSI2iHRzUGeEsBEaDqwckpm3AOS5LJBPic6oCxDNMsRmK3JiD6i1NaxuQOL69H84kKz7cUjINL6mXJXKEgygSizrxRrKCzDCXtItRomxfKSGbSPb1AJW24ZIY3k1uwrsInqIWJFxjmwQwF1S2jVcq1P07yjp8//5B2MY1bei+59mBSCTNc5WzNUuSIxUUUIYCM0gmpQIdttHZB3B0ESF8JUHWSOUyEKOtdEZug2bHTt1QpUoyMER8T1d0TVvw6UO+cDQJQaYxq/lh6EOxAwwKSCCkfv60O/+soYKsskHDY2ytTYbUWprNVUzvGcXfRlezPFyfR7AiaVCjZXjPyPG9pl+pNJEp+SV3F31FRSWW+T/uVdob/J1P0x9JvN39LxMQU1FAAD/+nIEIxEACYISHNdRbyjEQwR6wz0iNohccVrgvEFQ8o4rXMKJjgAANCd2245TEmRd1aTZBUYXs9VEc5cKjVQRdL2ejyi2b6CpPUVjVdYrYq2qtU9sUpVvWtbWzCCteNA6RZriSFnCuoFTb/DJbCNxZoCLFqxYjBW5CKzCHAxrmRerq+gvro+La+oQf/0cdyAaWkWJvZualREc5+s2qBUhpqQuFQFbBtei3+YCC7pGGqND5WkogpTWfOoqVBoIwNiZXW8jOj07dlRRvKHIgx1nTtHvySCoFUodfLKHLW466y5rzzJJBo8pokmkqOjnqALkcYwawtLfplJgTamwnfxikVLC46rfrYB0dv3y9KiVeoSBQ3tKuRZm9qLHsdqDrCINhMEhWJg7ZqRvpTEFNRTMuOTcAAAAAAAAAP/6cAQ7SgAOgfkeVhnpGcRDBIqjPYJUiJhzWmeMSlEUH6rc8wmTCd22wwwiDvJxdCVCGlCoIRxAbTYf+Zob3PzwiQEUZvFlSIoHXy2ZcxLNAuxKDhtLnVNY9Gmwgn9qVO2/0ggLgqbbAZJEUJD0ZISDa4YToRfOiVthd3LLoUZR+DGYEZpjZ++Y3eJVET+yGHZIKDSyg6CSkpMC+eyyloMXhSBmphpyiO247soTXZh3JwhZqSFLYUcoi4Eh7K+9mn1eqsqaC/qQSA1q58KKF0JGLap60Flss7lqaKTq7G9amHiqjwoLiQiJgCk8LlBV23AwQk5CPVLGQAvQzmIsM2NITzTKkKjMYtFsoyVbPk/IXsoYIVSr6yJPrKbW99mRmto1Sfeu+T7nSgM5kdcwSVaYRpiCmooAAAD/+nIEe4wACIIhIta56BIUQeRrCjAidYfkZ1zhPEExEZarKPQJUgAhJ7bceaKY2i/nKXIarcAfY9bGxMewMdUdSt4h9WROM/7f+4UWNitNRqukXMoF1TgsvYmWQtkO0Wy9h0REFLOihsJVAALIC5bJBI7CYg+yXnT+xv9ny1nGuFDma8ay0Z6/SOasqhCpb+jCSaE5AMRO2wfWLz1qRQwrmEFGVoFIvsF7k+buWCpLJBFnog35gpMXBG3R3ZxaFFbqSIDj0qV7YBz/QGI1GhF9jjYWTSXieAhoqXUQchy2RAmmlj7bnxb2IZXHBAA5vvhkjhBMLEFiJahEq6kzgRhi1dSzSRtqA7bP++ILtVne3+5n2/RTpNOUYPhguSE11uutJCWF1AkXLxR5tL+mhvF0xBTUUzLjk3AAAP/6cARSJAAMkh4UVZnmErRAgyr3MCJ1iNhzVGwYqFDxEauoxIjmCD22wywB3MyuTiHD8MdrVHlIk2l/CsDRjj+hb1DDRXmsq/1GDiWHiq0m0uJm2Cm15u0XPvH1EmMcNFiCh90coVcx1CACgtWyQSLweNGanaIfJNYdxXMaNQV1JQSujRjad6DsBEI+lqzBM4MFyg8PjAuTZtYaUen1uPme4KY9WpfWh96KQXLdsPmVtOLOtidJIMrHeEnpuAcKuFmGkZXpuSR1Htp2NEBql3KMea7zb1PYylD9Zs2GijBEHWrkhYrNBUbs0FZZqEJbjugZQVZoHGhsuSD2YCWTbEXEljZaTK1HzNTDEc07Kqe9ABXfilV2/weji63IHJCrydaRs6G3EVV80JH0KeSTEFNRTMuOTcAAAAD/+nIEY7kACIIgGVXRhRMkQkPa6jEoCYhUc17mBKsxBJGqDPSJEgAACBU2+wlMQh0fGo9GQNqroMdad1whBkqAOw6zM9TNX+HNoJelKjakgZhiSK0wqaMkjyVCQVCRYCsnlpbcW1UW+teRAACIJa2QDAnEYccNAqHi+NPpC/DUU738Dlj9NaU4jtq4vgSr8+YZdyUICAkU8LnR6Dqk5eHDISMk5QT76XENj7LKUAuyyQTU7H1ZdQHpYxF6UwDzMFmt42V6N+04CFsZ5xhU41Ce7IkQQi4dNQ6dGCiDzrAkQoeRWsFr2my9alwPXJpRHgiW20NoMoHqmkLL+eKiIdMyeNRBJMKAS5mOmCZE1aZwg7Ut8V+QZn9H6DMAgytttRZUq9aVISQdNLfd02erbambTEFNRTMuOTcAAP/6cAQ4rQAMghwjVBsJEtQ+xLrXMKNZiPytUGwYStENCevotIgmBUtto+RDp4EbVGduEAl+aZr9Q+1+LXDwozNp7ve9srq3oi1tjVF/lZSszP8oxgUWEmzyUYpHuSfZFEvI1M1WJ6k/+NACEZbIBgAoWvvF0vDuH1z9+4LQNzW0Yu62vsFu5Fr9I/+uU5l/6hDa0SxZ5VkNQKNInnk2ZJxOtMSttVHlo8Au66j4yVG5zY4NT6Y0fkr7Rsks4oJvOzJ28DQdRYwtaMmvbL+bt/VzyMlivoxTGDFypsQrAlb2dyWKNoBQeDpJoVeXNrd7ygMlKWyQYbHTcVCwNo7Wy49hWWtgzJqjSbDiZt5VmFEeAlFwUMC6w8QL3NmiASRk3GQsUMoWWGkBG2pAXIM1KcM2R6XJiCmooAD/+nAEKSsACIIaHFY57BFEQyR6xzWCC4hwcVTpMKUQ9Q4r6LCVRgCgO7b8ecgJFRB8B+Ig34oInerj+blS3Wsjrt/ykXbFgiwj5N4nUkWfYymgCz7dVabBOKJITaWoCSQ+kXqSXNFEMeUACCZI2wwZXCOrVFIfCK6MG91RBlw19GbXhtGpqvVvcPdWb3lZQQMERr5ZgSToOhUipLI7NY0USfqnlx4x03eh665FwwU7tsJ4gge9BrREAaPUAfnV4j71zuqliyUUisR1qOLr+R/gwz4AotUReIXnw69wxDnGQhGBl0ji48J9yXvv71tCB9ANFKSyQYnMF3SoER4cqgkeR41XeRkxquZmdhanXr9Q1h/ykg7bF02gdxIVqZHGBQ1DulJ9lLRS95inHtrNpiCmopmXHJuAAAAA//pyBHMcAAiCFRzXUYMrTEGDqoM8RnqH+HNQbBiskRUMa2jxiW4AAFgWpZIJ8LBkqH8PS3l122lFTFkYT3tOIXyXrNmfpDUvlH/jTb0j7RceeUMPFBAKSK5pbz5+19z7WNLbnIke22lASlttEEFQZjtwZFUOM63IaybmePtrEd4hS7+quxaufJ+EI3IHhTidCjECExEzyXucmWXtiKqhjmVzCkzdhtqRfbaCpLbR8sKiI9DZDlYF1lKZx5caS+25agbMplSZ6p3G6lTN+Aq+oQHh5j9V3gNCD6GtsWyfaotXopQje6yv5enQBYSskkHL+uqWbEmQ5JvXkUelFCln2lsoMuGbFDCnp8wx2WICY8aW0F5piHV6Ev7hQUcFUNNrMIQYCusFUoth19K1BTlVJiCmopmXHJuAAAD/+nAE6Q4ADYIfGNQbDCokPgOa6jBiWYiQc05sGE0REIfpjYYJIgFJbaNZkpmgvOW5T7oGQVRmweowGUGI2QpUF2HR31GF7f8JPEKwUNpYmqxfCwZQOUfMsFosLkH2nOOCbHPpSu235l/WAABQRtskGiZBFa5VEQua77XgoJ5aESqd4CXyPk/M/xFtGOignQmThd03gY6iTGKNka6xcJaspc5SSAY1WdACt1UR2JkxYRSRSg9xp9/KaL6WHFDwv69pFI0rWx9xNsZn/bT3RxhC9pq1oQK1Lcw24caSxSrqwWvpcaTYLL4CQh6UIQCZbbRcBAaGQWmfsgG0smh4MiqYUtY6Ee3v47nXthACFB4Vt0vfD7EKlqgMLvFCi0lWpkiRI2yNcoyLXS72NX9F2QR5hMQU1FMy45Nw//pyBJVZAAiR5RlRmykTQECDquckQ12JFHFa56RHeRwOa2j0HOYBX/8Dr7qZg8FyAbgAClxI2wUvljOUgLmJCIEqtmYkLE4enNZ3WWYQMUMv+wnSIDPwygBUIRyTt9f7LvioBypLZIOBouy9UFgNge5iQyYsW2my30Egunmql6BRyPDuPPLNBM6TjjAowOqSHK6+sBDWIF+iH1UVvvPI9AWYhbwXJZIPQgZ4umcyHmc0o6A30cftVGR+hdGmejRBv3eddcB/f//jO8tT31tKEgHryxWMxlvnuQqr/3jot7vq5LDqXXsZ9Pb/7HAwKcmCarmHOpGMVQGUKkPpnizEIMfX0FhceMmulG0dISrV3Vy7vQ9OfUi+MKtAC4fMJWZYs00g4cGQQjnACpxIuSFUpehoHTEFNRQAAAD/+nAEBQ4ACMIdHFbR5ilEPyOaxz2CDIi0j1tGsENRBQxqjYSU3gABhDm3348EKd6mh7ooB3QrbEOc1lZ1ePo32UUMyHer1CjaNEg8pPRuc26TKJLjAghmuuyo8BXBo69iryrwmlXc3clQBg3dtsMEPL7cioOQaE543uj3d1Shainq3QkvctDZ+KH+BGh/akETZApS1Iqq9FaaHt0oWxB+LL54WT2WS3xYlw3f/+MFCLZSKSfVByFn6pamrLVUhRCoQJPatW7vXqX4Mr1/li5F6BHijBLaMGqB9V4DmB4eS6HRa2Meh6i1CWzCXmSVIKcjbHFYIjA9hrb8DSQC2iVS1q2Ai8ijr26NFkoMM6I3qCvY0IUczJoUaoFXPoUeNBEV3NnkEUisd7krFfRl2ClKYgpqKZlxybgA//pyBA2/AAOSARzVOekRtEBjmuowwjmIbI1QrD0DGROOq2jzCN8B4Tu22Hgk9CaWE4S9QugiSzAtT0CueUrOM6cqMyyvf6CTvtcU3THBEJDVvIJsHuF2IrTKKQy/Ly/+tyUbK2r0AABKE5bJBgN2FcjSPQhqlWq6vCe6BY1rfBy0Cd22oV6tifwiSeggMU7jZmPexVKF3B8zXXcBaGAJxNq2Crr6ICBDuXKGCSnoOA/1eNSj2P5E7jCrzLvXpb4mS5r0hb9fwjiKeoGkV8d8/NmC9amrIG95aXS2GPHpu6Y3DHvV3rO30Z+0BQKslMBej0Vi0Hoh5iar4HwdWZSOuiO9ydtdG+goW4RV8TRnbf7s985ckX/Uj7mOAvywGu6tsZrrlVrXb9yz/vYy/+r1MQU1FMy45NwAAAD/+nAEVdQADAIXHVObDDm0QuPK6jBiT4hol1BsCQnRCAxojaeNEAFbdgMZwkgiM5yISvWHC19+GKNC9SlSIzUHqHL3KNt/4LDZ+hcdUXDIdXQLIhSZ2B481sIRC4OpXScYSDLkpEpbe1IAIlBrWyQSOJS6INCmPC55YLzgVvnoN0v5CvTU0vSbF/mdziwsPNixkXC49iVHiEVQjeOehdpKwIKF0N307lu+K35EF3bYDciLDoCg9NNfjwMnrjCA4MSgwVoNsSlfDHTGPr74AXK4Sbe2Wv/6VJhZH4WJmasYbeH3qFjCKlUvIMOED48f6gF//+KExI5HZUErQHCxEbIhUi7hTN6icT8kYSsFqtYIMpD5xAMhPKXG+F0E5VsMHqlmxWatMVUM7bBZPgfI///9aUxBTUUzLjk3//pyBIrtAAiSIRzV0eYqVkQieqo9IzWIVNlQZ5hqUQkOKp2EjN4AAJgpt/wMsgUx9HGJyxNZrfavGa4n++wZpa1tM1lDNPOigAW6socGEud+/TLX847+ju1c3Ac/HqNT/X9/dzkzm7GP/gAA0hCSOMYDpa2iAJ2fhZgIylZiCGMAnA5wJTy+EDVujSZLUe4wNoUsBFh5JyjSUn0VGDpmSZF0HmFAmRcNUDUPpXNawXNvgPDFmA8jyNQeRd0VhUSwgTcco+aSwWecY0yBjN+f8KJb9Gpz//3WW5vLyeX2Fved/Og3zJlCGwSgE+/uauu0COCdmStTxyCKshecmOAJpDTsJqSQkQTODpn82jdO1EbO7weHNgAMiQ4RfIqeggvallY9AbfYo+3rOPDZhgGLpefWDCYgpqKAAAD/+nAEn9AACIIVGNS55iokQqOa2ikiC4hMcVVHmKtxDhTojZSJ2gCBObfYd4SMGiwBQFWqRmWFsrsWUReUlB6j/MtWUUdt+kJ8NhTxUe83cRyhuw2gQAIAmYYOnlPD/fcbMITdR2qvT2gABMU7bJBA4MvbBQEw0FebzeZsY0t7xyrBAh5ZB2/tOb1EOXXyThKdMYAdSk4XsD9KnRRcKULSsXMRcWtQARSbV0pBIJSRtjDWDjLzUuyEoYPG6WstZr4Pz2+uuzviLnXj5JB+j/Cu2LDbKh5gkVAS9qhax0VU5Ms8ObAFLNGp+mv33J94JckkEqNMViKn2eDMSmgSwLKMpYRG41PJRWou9da1bF0kP7XODT63VlcirRHxH4g238q9+vwVjChNRhB/sRLXaG/9NSYgpqKAAAAA//pyBN5QAAuCHBzW0eMqTEHjmqo9IkuHIJVbBYRPMQUK62jzHSYAAJSXbZIPYaLbEXbivM8ohTdtQyXqO1HeMOiVI//Cb74irLJKgKCI6ZItNoRUwSBM016VWvFhUGUKKoHDm3da77LusAAXQlZHAJSQNi00KoWxCacGczRy1msp3VTXmfz+g3qFHI6O6Pi/sHFiBzWEANcYejXjK34zW26seFiwIAGLODThoQaIUjIcCLJ4L53rOw0ybESpL0x1QcvwTZ2i3N33+V2GKINB9jmRhGAxh+uoXUa2UMGso77em1pVAfJqWyQdIt0JzMN4Uyr2P1+m01rbw+QaMroFmPK0c9GKxep+9/mjRwrokFwOUFXu6IhBdUwPIaZlMYmixRKzS5NKYgpqKZlxybgAAAAAAAAAAAAAAAD/+nAEk3QACYIREVbQTBBsQsOayjBiV4fgL1tBMEFxDxZpzPCJ+gABlJjtkgiloydrGhj9lN3lWPL8YwuWy84Jg8SOcqDVSXemcHlRGGWD8VZPTaQ2SHrqABsY5pEfaq0G5Efctsf5JYAAMlOWyQaISZ3AVEsrDPTinpHvtWxaj7wSsV7pY+ugVPi3+Hg7FxgcKwyOvFdzlNcQFHgKlGVOGmsYh63Z1il6Y5AlBO2MPARQIViSWMrn8iuK0ggiRZFgisQ35V94Vdi0BFyjHhlsmSM2pa98OhJNaRYNtV20ILqABMWepD5XQpAKl22EgQWCyLAQAcYDSWJsMWD4kHuSxmYDK96HdnL8gq8hunL+T/KnQE7tFZokxm1ZGXUKjgHJE3PSdcFixelz0/agcmIKaimZccm4AAAA//pyBNOEAAiCFxjUueUbJEKjKndgxVTIhHVQ57BFUQ6MqhzzHVIAIcm+2GUsHcd0VdabRU2aWOnS9QJidpu9SWregQnVuGUopzIG+s9MD3IB6HybAwSEITig1TnV2fqSE0C9TJw+12rWAQJ23YCsk1K7MXHUPkqm8lxe9bvDhCq8Uy0f5iPQ7XTmwOnHU3RXo9X7aV//1z/vtXSam808iZg2RfD/avVP806pVgTu22FToChI4X7mcgWD1OfLHLCysBTnz17KrbIq1xWq9sB/EA0UpUokBTL0mXFHC4oRfEdMuuXJXxWQUAb3yDkKW+1bQpdtsOLyVLrxLxih2jUb09tniDhb6m1Wnb58yhbt1RQgHS5MsH9MiRBUqXOJJEJzF+Ta0C1Kgcy6fFEpX7Gr1IUhGhMQU1FAAAD/+nAE3ygACIHeGNQ5rBDkP8Jqh2ECUoiIdVVGJEjxERFrKJCNtgFAO63YRLwYhQA0cnIIAzhNH1+C/qo+xNMghRuh6H3/8NaoKiJ9NyEJWKClkxMbfr/40SBCKGuipGkWoACEZddhkruUXHVKhXhZk2vVu1Yhc4N4i9ldZkNvUQwvyeUs9kwttOKmQ5NBBdzvWqOfIgOMG1EEBLoWQWuloEGQF2RtiSomCW62yGG9DMNsSeYw/iNzoe3izOnTXplI3ix3DpEITx5iSZZTlQCNeDpJAHBcyZBIlkWPxb3PfQrUXWu7oBYJW1yCwEUonEo8KF0hXX7UIkn0OVkJwTBmL5teQgy+cqamOSHBs0eFkuFiSBUzuF2e+wJNMR5YJwKYelLTSK1KfZaSTEFNRTMuOTcAAAAAAAAA//pyBJGhAAiCGhNSuewZtESDmsoh5ReIGE1TR6RikQgMa7SRIVYBATlktEhQkjEqQ5XSgWwLLi2vcqBvtOzwrsYuCfOmd9xgdE0usBw0JmT77Q8hKr3+v+25raToshzjrTaTrSVztx1rQAAmKdskgwyUa3EYO6dpqxXqGSihivpO6kxZ67p29QsV0xUwQks0Agaass3H20MsCUeETceog5QrWTvfUEzRJjtaZlGkQg3dtsOhR3kxDFHSAGKFJcQcC/Ms4pU7xsqAhsTuJyOVFmNVQs4gXUMnR5hzgLTFip4Zm1C7iTErT6F7qoy+yYALhc1skFlvjIiIDYq09DF42SzOGR4x7i5tvmeGr/Gg44gsyDRsQIrNNhMPy6CZsu2Ab0s5BNnPhdMWat+Re5nejrTEFNRTMuOTcAD/+nAEOHcAAIIDI9bRgypcQ0MaqjGFCYgYT07mPKLREoxrKJEV1gAWrDdtkgk9S2NcoMSzJDnDdCIpFs9rLStQia7tvYqf7Xq9p4yfBxzhGXYu1WNGkC1AGc9xlF17jS1jk9lNXoLAAAuUZI2xgUoeH4XqFp+mJ97gJGHZw/4WPYO0MEKNvSyroC8gV+iBSio5LSMsXWpJ4eSqVOocXF7mX2bSioB0rTcgoAEKW23DQiQxWmrrgUb7MeKk1g3PHqpj7bMtRrQ5oC4EHAu0GAsHGgMoSBygLAQmERxRoZp/dpZpX99DFNsBy7JjOWrbJBwaXtoQkYlBlUTf6nazVwC2DNC3Y9Wu7zsp9A3hoUc49EahG4DvPEFgYKEnGgALF22sJT+uxy0NOtrxRjyHQm9MQU1FMy45NwAA//pwBHcsAAiCBhxVOYMSLEKh2no841CIjE9XpgjHcRSG6Vz2CNoCoUskcEjCAGwwZThUfyMy1JZoqWm9AGWpy69sCMtjoJFWB5qECdIwyxDapxNilgZ5gIVD9i6amruvomZsVYhuZAAAIFS27DBEKWdAQR9rA9DePCDcIrBXLKZA9BJUqC9/lm5J3iY+saXi0uv73e0D0PCx+XNjhRixfWuNDAfNikM1MUATClZZIMJ6RQe5CEAKdZ3uJzxvKUnGmBNvruhZleBbVoorXyA4+B2xUyUSbePasVS5ZgFFLFxKFUoKgIBZhAqgrDZjrQC5bbhCCV7nP8nhI0OBuEwALDPkGi9rNHdwd61ByFr7RZ7FB53UOekKRc1IOWnWvc1xusXWadWLorzHDzhdzTABKra4OpiCmooAAP/6cgSxGAAMggsZTxsME7BDAxoTYGJ2iHRjPmwwqYEEDmsoYJ4eBNttopTMpDgcYIjOenYQNUHqMqerbvz9WmYPvAUgLdlswyxTjAW3RR49q5muHpQUob37UIqvfaS/3/1IX39C/t0ApySAR0twzBoayh1S4UmhkLdBqd+hxZlnLqXILZHS24MNVfBM+j/GNigDCowWolkFhuSMIAwAEzc66i1be1S5feu5IK222FADVqmA+FYXJdcj8wF7QMkslmRIZJYHlnh3q6Frb8xFljS2fI5fm0AR+968UXF4Avo93pLRld8ZZ1Vd7moFuu49i5LZIOLKgamQHCiyX3CMhCX5Fe0+YgCK0CRd54NM2mS/0FkiiliYPsNXjC829Zo4aedFGk3uZbS88rrVRsL9yExBTUUzLjk3AAAA//pwBFBqAAyB/BzTmewpRkMjmsolhQeIrGVCbDEoSPuIahzBic4N27ajJhc8hfxV4kCj/d6kO0NFiFvo0zdRz0fdsDkryii7za8PvdzxHQeTujVaqb//65v/UgV8rnWEnkZMtJ1QAAqSltkgsag98aYPXXDOG1dIldR7v1F2YrNWifEwget0i0nM1uOEyxdCEC4owyOctqnNYp7RtZAmjF1I1UvaJrI7eHNv+BULcsBGqF0kllsIUqrypgrICAKMAgB5UXydMTdaWXq8F8TnvrHZ8qxLvfbFwav2Ym79pld2avIYk3/ebc997X1vXDlGSNsYBmAsNKR+Io147uSLO9yKHso7P7gwXHKNH1UPwy54tkzLpznhyWEzB40XqejGdrp9Kzh1hp/t0UPnUpiCmopmXHJuAAAAAP/6cgQtMQAAEh8M1lEmELw/ASrKJQYJiFxxTGekRRENDir0kQ2eABB0N2WSDhpcL+jB85bvC1HamGOQ1rh+DN5Z+LCq1Eb29yw2EUA4DocEh1csNILgQLBsSKLrSNYOmgsJgypK5+9K9NAAIlpvWyQUPFm0BzGmU3lE+oZcNhlgiDZMHz4PKTM3qa4nHBYcSais3SkaLNV0JUZZKqnUusAiF6WuZWxsWioku22FBSWs0FWTMQli1uis0NU2SYPML1vndc45XEijFfCgFuCWYlVQykLC2SHBYAFsjW9qS/U1N3eKTjGkkigB11ZYAAAExFzXjgfYe9zZbdrzLQwDceEcSDG38ini3fL6aAwd1mociPYBSD3mYvMmFNS3Cri7lrSp5FVaSOYneFt7qW6LLUxBTUUzLjk3AAAA//pwBAblAAgR/hNSGeUbpEDDmrokRl2I0HFA7DzFEREOKlzElR4JS26iwPNxBRmgTFXlalY8eiahInMjK8j4ajk9McGUFtLQ3B3QSd5FI9CP21B/q9wwiFEiMrKhFyiCyvervF+4AMDC7rZIOJwfOwKq6nJARPAaD7SKW3lOUFoSm29LCD9RjdW4ceozARAcsVDQ88dSkUthIz887GPmluW3VtYMRoIEpJJBVB2HhFSBxS8hXuDItnNQ/1YLlfR5+zJkSDI7r/6cs3PqLbPea/BEvLfBpc0KqH1GPXteg1M0jNzes9V352yxlKvuIOU7dMCCMj1LCeGNHXtGuyLxp9NoO3kYSSiD7+9gVK8axoGuJCIy5gJAhGg6C50eJRqyrnDLHbk0naJ1I91tabZHNprTEFNRQAAAAP/6cgQd9QAMwhEY0BsGK1RD4yqXJCV3iChzQGwgTRD9DynM9gxuDUkggyAT4sIiv0hOjK6lU4wpx7NrzXX0C8GvQkJXkt3Wzfn3B0GD3zhM66CsqACKjx9IYqFhMSN61cWXYQSr//s3AHKdtkg4fQCdq0jJBRufi6k1KK6JbMoQrHVX4kLhmRIoaHqSCS7QxEwOOdAbyaQg2hutLSNxsljDDkHVnnG48m+xYugBSSSCUgg04ATDUHpd27AUYd+OtcqtCw9EzsX1YUXSLkejvjR+/epx23UUwVGktR+dI2qWhKdaetKH3qo16BnX+oN2RxjKKKAr1jB0MjJeq7onckoa3bf2nfgLxgI4tX4yDEV4MZBzGTps28ZpvfONSyvatdgBx6qHBR6RBW3R/rTEFNRTMuOTcAAAAAAA//pwBD9JAAiCIRlVUSYQ3ESDGoo9Ih2IEHFM57CjEPsOKej0iG4AAqSXbZIKA9QdFwa52jmR6ELW8qi0X67EqP/fQRpFHYRFQ85LSUkBBySCRU0ggMZUhTQGRMhEQoQhqZhUVLnGNjNqNYAAdBOSOQdQjYUBKsuDQJ8Wr43SiHZt602U9YdEah9exoILqm35Lef2KSxJ5KWavpa2VE5xw5LBdhhLFiyADUNJoRlgyoF3XbDIwmIYwGj49NzFti6uxPhwFzQIjr2VldMf/fEfx4eKisRmQiVGGXOd1L8iccg9NLWPFekbc9tJp6PGwFcEISsjbHQoaR4jzZUSGYCubrSpY2SJV3rN0K6sobb9BH4GfNBq98E6y4wULqLEZKmmbsWrSq0UXxqq31Pb+lKYgpqKZlxybgAAAP/6cgSFNwAIghol1DmHG95Cg5ozYeUYiHBxSuewRRD8Ceno8ZlmAeQ7bIBgEUIbGoP0YNbIHWlZah+0sioZbaaYPmMj/y+ET6XYVyQvKMurXIGb7vfW1PXLjtp/TkTjGd818H6revoFv0J222jS4IeGiFgTswoRySnNqPpWx4BZVvVi8SCUbiRKW8wOO1oJzA+bFDAmsQPMXPTudcbQ4ktLLPfXaxNL1pqZ9yyBO7bYcLpqKYf5PQNGTi4ltouM6NnyQKZGt9Kbn7/C+8d3UsRvEQiFljGC4fljLWkkP1CjT5LcVUi99jGscvQ5ou5CASKMkbYwjxdytet0BbT+EDgdhVfasRfjBpyhef5Z9y6ebVtcbBG4tavB+xrYU0qocaA0VVIRZqHwnb26FCtT0xBTUUzLjk3AAAAA//pwBFk+AAiSEAzRGwwRtEEhKiNh5haIRGVS5jxC8QyOaN2GDGoFS22jhQyqTBTCFEMSXcL14DikcyXJVhA8HaLAbHiYZH05amcajx1i4DOFwAt59jR41OKqNqmDYol6RBpaKFzbWRQFS22j2WDuYARzHCEST8QOs/xBancFYFg04LsKrcVZ5rKGIfQ1SDrAq6CSFDrGZzUWCwuoLHTDyvoA7Jmlx6yqlRypbZIMBn5mgCBKhULXdSiqqCB3Z6jnH9Q6vuj07XILuYHnP1CFQ4GACOAlYsnUbStuJwI5iUIMMZyXLtaaiJatuwQUl0yMca0MYDi0wBIewHEg0rnLWmYuouPw6MKjej8/hRQDhQ4I/rFyLj9E+pMZDJQppcZQokMqacBNWx/1oCOLGG0piCmopmXHJuAAAP/6cgSI+wAJkhEZ01HpEbxAQ5qtMMVXiARJTOwkpTEIDmposwh+AAAIJORtjIuDCZBli3loTUTcTqQuu4TZtXUERKqWlDbB/oUVyf33LUkwm2IxdDhRd8USHW3l3oSPaXhfj777w/P0gAAFOIpa2SDUxyEVWIB9rpEvNSxNR0h8pDURbdQ5VuTf9PwSpxQNPJ3nwT6UFjGFqRVUUYaFkGFWPbq70Jq3/WUouaYH7pxwgOGwDmJhR7NzpdiULarQxOJs4m4WcpTZru+LJtcXFIRsGvPiVpQpMG1120tHW3KeE1zlCWPU576QtKmuTQUEyRwgFCDCKCe7yhv5G7iXfqAL1XZvyidyKJIIg+de0JmyCRI47WihQsoZvFXqKxaaxax1Y9xJ9M019HkUxBTUUzLjk3AAAAAAAAAA//pwBEkLAAiB/xjQGwkq1ELiehNl4xyIjFdPR5hKsReKqij0lOYJSSQCSkppGjYHJDAAa4cJ1YssWGSZ8LCwBb6lTpnxv7dlew+yK/S0oK/Ol0ta4QGaYxL+ggsvNIYu81FW3rueE5bbRxiRVgXccDAUIZQlVMNZHvpWyqkaftu/ZTmYNE3IGHMsG9dQuE2WY1Vg5Q+mVgMiRLxI80LENUZ3tuRy7kuvqDUNSRuDBKZV81wpl2SzVk3WqvqHIbcGvfexR0eEIYgSyIuFGK3GWEji7RS0xgchbilFFTkYgre7a5YGHoeTLDhGwgMIBMW5bJBhZGkcDHDM1JK+nOpD3i8dQ9q3XUospZQdVUZZfln/fSMFASP1CkOD7lnw4BQuJwmUcfiou55BiwNezM06x65NSUxBTUUAAP/6cgQcKAAIgd8SUBsCM6RAo5qKPMU3iLxlQuwkZtEUjiqoJggmCUkkgyBRmxsqTkLvCwEVV/S1/ICs2ohhD9Jqw1itvYRcLSKsvhPSYE4oQWBjj2sLo3nPxrdeOo/TVuXUAAK4jsjcGFtyK1HN6RQgez7LB7PSzot0ZDRsb0KHo1KI/01cHMDA0zoMt1eituqw3Zz012M8qoXFBUJAmBhMuXGG5bbRcFivi8apSUq1BN4TwQiOpABtI8arCZmVofgkh9HUFT/+irFjtDy4ZKD1ucm3S1BGwqoRiqk3OHBJhZ25T2p+Nu0gOrLbJBEA+GKzml59pvh4iUml1x11QICI5wADGCo16SNfinu8BB+IhpZIaPWMUulYCWblUlE2wAABVjCpMqnFGsWTuH6ExBTUUzLjk3AAAAAA//pwBBKiAAiCFRlROeMTLEMDGnM8wjeITGU8dZKAERMMqR6wIAYAgLjiYGwRcw6htg7T/CqPC7EyeUynUDQiguWvvUKxtKo/2AhgSDoDCN7jKrarxMNhsTi6VnG+q1MqPag00cfMnbiBLtjcHT4O9U3XjYKfgYpNbH/A2va/qY4di+34d9Rs+LspKCgMg00Lg+bteaWd5ub8xjLGptoFDFAcaLiFp8mUAAocUCpJJBDoKYcBNtIhIVJInVYj1vabKUIiy+NTocmUSIHBNK8BY6qjrLpsBXZQJRU8KlrzG5zC8h21XOPNWza73MZsmQguNpMRN9hg8HxaeWw+zQZlWizY5QVIxLZzWjKKoxGOjbJR+0OGRjihSx5i61uoEFOY51VWux4p/tVFKGIcSCSRoPrZrTEFNRQAAP/6cgQmTwAAAiI+WVYZoAREx8wNwUQCiKjXf1hWkBEWGvA3CtICAAABDCAoFAoFAAA24qzAvkuiPbPyAR5eKH4VcE8AzP8c4Tgmjz/8K8ToKoB5iAf/hcAuhGHOMgk//83N1IJIf///eboAAAAACAnFAwHA4HAAA+/iBfkJ5kafjNibBBlvw1eAqBqiP//GfJIZgTmOj/8QAEBBvDNiwE3//lcvpGiRo3//9brTk+gAABCCDAcDgcAAABBSmHgZasNR4pc93DAFuFNanoOHITYpP0OCOBOwbAHqE+/8CggZ41gchxkT/8jkkkzUv/8+tcopDcdAAAABAYEAwHA4HAAAAtfcGWsajyXPeBCAjwINan3ABYPM8F+/wdAXMJACshdv/BsgpY1g/DvIn/5aTkmal//umtZRSIR0//pwBAUsAAACER1Yvz3gBEPHexfntACH7QllQz1G0Qkg7JymHkJAQDCjuGMybcoT1xKRQ1kO41ZdZn/ydkLfeKqvYwdcaz5NxcfO/8uW3sBqm9khP8+b9slTVW2e5Kv9lkv56S/eVZRAABBSUDE0jcy5vsv9LQlUQ17XMZs0VSA+IZECOk6ykkzGfOH6JifooljqRMfbp9jbodLoJdu3trR6P9ZzpmzrfycR0kBkACkZNx6OGoH4ofUaPhuPrHlUHEW1iN6AyGS1Fnml8qZxX79CHj18Y9X6/1O6/5bnP3/u+hNx7/l937FBmkFACTl/FyiCfGcFhXNA2EOVLcb8FoiUywu0BDm9HypN8nzC2hXkH0M0N5vt2/oWyolc9+3IthA9hOLf7EpF33JDUVTEFNRTMuOTcAAAAP/6cgSfHwAAAgI9WmhtOexD52sHLadciOEJWGw9SVESHaxoxp0iCAEKABJKagGriKDMWKrGqaYkFzg288+P5/MQ1C10FCYoL6keIOVfQSOI38Qijjz8vyP/0fqX7/zS+UI8bv1nZuFAJDlvHChqSlDGQNu1QLJB1lmobNYKMbVKMEtQXN6RU+lzQec18o3JfxHDtC/P6t/9y3Evs+nQSeSOk/z30wE9TOL1gAGOgfIrvJFLIvAQUNHbVXZEwswkf/zS18g7yH3qNAOFKgqJaC58qN+FC+Y+LhN49bPbUa8zo+nr/Qh5A/9+j5QmpLP/JMnoEQAJJ13GndlKBnPgxrJ4QJDQTl3jfpiAk1UDCeoCMsfx9soDno+CjatyjZVv/mm9e3E5PQp3fvoNuNMl1uWf+qKb+LJiCmoo//pwBN+oAAAB8zvZ0ExQVD/nqxoxp2aJBQdU7Eit0RGZKs2JlbIQNgAApb+I0O4hZEL3q+lQKxmBFyHiSFtZEGz4iX06vyL+Mgp+74vbn8V+j6L/+Td30f+R8oNcP8UlH2dErXAGIAADt3GtbRuFh4BT0XjyN66HHTSJAjrRH4JfQAXfypbQZ5RsTf68dbUvzvfv/UvoO8z+pPiM2pP9lPyrr+TRAAAG4B79zcr+GoYa2YFtBwzmXytSiLBZIpH2ksFXI+WRrI4WNMxpaofI4NhlWxfp6Q7uWGzFuTt/Qf/9V4QfR+3EehuMf+LgAJuAfZktloUy8VscbLLOTIECdR0C1dxDmWgITFaw5YVkW2SDGT0DPi7HjWnxy2wk/BOR8K6k5G0f/83FJbct0xFup60xBTUUzLjk3P/6cARkCQAAAgs9WNBPUNQ/J1saPaVmiE0HX0e1TlEPIOsc+BW6EBJABKWfCEvqWr4QSPP6Rf1YSV8g4YAmuL+C7v7B9MOEWXnhNFPJeJbdtBV0O4v9P/qQc7o39G0Jax465YfmEf34AkAAC7NxjcstIEKgsl5+bC1WVE3kHnXxDg/LUF7cLDNQbir4mP0X+MfUvPxj//MO1X/8nGOn+H4l/qPebaHCAxAAAUuw8uI0Vty4gw4UWRIOHxIefL/CIDZXGYdfCc+jYuHvCgbQvxv09uX6der//m8gbQ3n8ZlunHzfqPJZy3/VCAABSUDEzXHcW7qkAHt1GoPq0a5C8W7QEJD64RxykXUTnLHI0q7B9GyLPj+W+lznGPydurdv6hmrd2/QL5x3FunGtKJiCmopmXHJuAAAAAD/+nIEY0sAAAILQ9nRajvkQ0h7KjFHZojE41bsPUlRCaHsKPUJ6mEmRADv340lHP3SMDrIEYaNiY/ca8Kx04XiI5X20L8Z++I4Fz6NkZbtwN5jZUJP98oNOSLa+nIFtA7li39uVd/AFQSAAFW3gZdlWrb3ggZc4hGlak3GuYFYthiFhsb8/z8TAL7NgaH9+PtlyTygS6En1b/6m8v2bT34oLaG99hwtdB1K1ggIAMco+I0GMQmL8uAJHmZ6j2FBvUFKY+jS16FFEpagqUyg3LxLLYwEJwDC+VL5UTOf1bl+nt16/0H/ZtW/lHKw91+/oUhcFUCAXbgPuA5wm2XQmTNH79Zn7WyaCG2C7FBkSQ1LSoStjeUE/PC+fObCj9OXh+g/CdW/+QfTztp1AuONdB/5m0EJiCmooAAAP/6cASwmwAAAh9D1hl4UaRAqHr3PaVciFT1W0e0TZEWoiuomCjCAAUmAx+UJAlUaiQLnMNQUoR6nD3gZ5EWRxGFXQQCNGPEYX8RbZhfm8l9tSnM9tDP/qdzm1N69WygmZqtqiohyHxm9FUIAAXLwPtqvNmV+DNcbZK2QyzW2sD+nmJK6AwLauVC/jRXiPInHd+Fv/O2jf/R9Dcrd9RXhwdMMMryvKIj9Bto8AIBAEFJcOs5eIXusQFg9o9fis3Uf4sHrME84Qkqxh9SL5kHt1mZJm7RmLeTm6NqJ5/6L/9X5W1H6cI+gPhX89M+pcAAIAEOTYWiD0HXawQRuWmVlCVXQe7gDQjIzRme9AjfXi4O5CDZyj5Qt35nIU47zeUb//y3P/yTkxa5QlX9H0LXv+KMu6kxBTUUAAD/+nIEeB0AAAIHQlc56it2P0drOhlHLIkM7V9CvUFRJR7rXPaVsgEgEuTgdzh1V2fci2a+D3Inc1OF22IAUq6jbheP+MwVPYGzo+UFHN9tU4r0fm/+o7n6+nN0Am4MGoiS1Gt0b2PisEqBAc3/Hw4ZcIoA9CsSGShPybQMBcsTmaiJ06F9CP8DgZ05NsoW1Trzv/x8tqKGxr1bHB9jVDAIyKNNTPo3AAiGQE/bxBJaLNfcgsWFKVZq+68aPQMBUjcXifwVtrxeNOP/4jCL5G+rcTOd0fKEf6c+NTHjF+vN5pexwqynu/FFPjYy8XYm2AAAKs2HrDcXcjVHAlGFywfCxWshccugDeKCkh9B8bJFs3bKjbUJF9XCgXUX5eMHdenV9l/UaO1Dvf+i8PDsaCWLhqI2No41vj0xBP/6cAQB8wAAAgREV7kNKfRCJKsaPSpEiLEJYUecTbETIexo04myASQDLOBi404A/KBoTPMEGL2s25LcWQ2IOdMUsber6h3in9AbjP4UAPCmzPx//0FffR/4x+KUUSqbd0GUQYqCcXlCIggqWfjGknFeWxYm0YwwsLbRVbi1qF2OJMCFqE3K9C2VFTo2cbqPO/GQo5JL1v+cib89JFxKX1Rx5aqDlildiEgJEIACWpB4Hhq740b9n9yzdYwxx3iMHJV0oEzbF8qXeglcq/bh+CbQ3B9X5P/oK7dE6cOPwuwA3rcdIOMNtzEueaKV3tQgExCCUt/FZW5quwRU1KMScWuOhjiTngNbEASo5UXqtOgxsD1sLthx+A8nOXQT/v/8E+p+bp1PwzYUJ/Q1kFwP6Hv1X9SYgpqKAAD/+nIENIQACEIRO1W58FIUQ+d7GhnnNIgo91bnwOuRBJ6rXGec0gEAAKTAYXOH4iETezxZTkI6KcXCroMuE8IUQ0aGuoqti4vkIUnHvNL8W/6NoW6dX1f+vn87oW/kJfQghh04s/DSScAIgEIz78dZqQ99QHduNkz6+hbiVxM2ICbrEjv0GMTEOWfKl9H78qT0Jc3oZ/WkRi74a6Np1DuPlmWE14fkaNC6NNnpIQgpKBlPSNxf4WbCTRouQlj2YZJsqYtWJWVc4VUKkHXKXMBstPB2SwoGqFQ1zPbQX8zo+pD/6jb/N+s5NCPJ+UpgIBVuw+CB5zaLB90iXE5R+KLwG8SxhGCwFnoEfZtAzoJfO4VD9D+bzBn+nQt/8dL6H9eraDnIFsqSQpaGXqJ8xQmIKaimZccm4AAAAP/6cAQi1QAAAgg9VrmNOkRAqErnHac8yEkPYUWIt1ELHuxok4m+ASQS5cBJ6kThV/Qifh+DCL5jOnNZnxYk1NFTmVB02nEw34QNu+JxI5P+eJXL9fN/+pvHm1O/hct0uhELc48nE40BJAJu4FxASDkUZBWDFGJqKbVePbWKqacqIWoRd/Laj3bml9SXfo/f+pbr/V9B7szV48C/QSyFh0hPZ2uhHncyGAiKyALu/GeUWBiaksaceIo1x5b+aflIzlqSD6sAV/9BXD9X1E9unVeXr5P/wnVsGI/iRHbhMfZ/K9Rj37s5MznKUogkKSUo5B4HFCOrgHWm5FAl6eNeD0Ez1KHNg5ZFEL5VeBVsm2JkyoY404Z9Sc/tqn9G5evTzdhrRe1wOuEyMXRzbEJiCmopmXHJuAAAAAD/+nIEAQIAAAIUPdhRaTsEQecqtz2iWojhEVhnnU3ZG57r6GaU/gIDNKCe/AzLKCDlJRm3MQ62Y0F+SGJVRduG+ANoOaD5z450fKncl0bUv/Xjg1/+OCT0rNGvbPCYJaO+YOPKpvE56aBAAArLR8rpZPK1FkG6ywrALAetR/j23BQDcnMBzvkLhHwaaD8K+g3XvwZO3Lwh3cuqJww1ZP6NqGaBUp5I2tsj8sATNeB0fLoS3tAb/PtOEfvGeEug0LrE4J8Crb8rygK8RuzchyvJSZqCxxY4VnV0SjWPcMn689+2o9bzUIhrfRnW7mkjCTD4cAJBkAhqQeDxoQ7UEyPmrBoNrOlyviOJkpEsI0+NvCn1FeO4x9D8INjW1L/Tobv/R/fv15R3FKRz7QOkCPULxPuIVbzCBcXTAP/6cAS78AAIghND1jnnK2ZCB2rnPUVoiBz1XUeY7FkSnutc84n6BYAAreB1BKwksxaiengUE4QU0TkmY4HWgKGUEITaButuJ24Tc3jH0X+jaF7876evd+Tkf8gJztNM6y3jTuqiK8w7QIUSHb8PlfiQa2dh/wL4LKJ/oKnDINEcqTnK4WmeJNq/AzYx9Cc/8abl6PovL/n4z3/jG4dSWS8XayH2qIM6Lzfi4aEAO7gdjlajpx8ofSDCDuxfuqLA1bDIxiEhqU6cq2pb+Ufv/ltC/8q/f+VL9ec9UfKF2VSYwtw445nhCkg9cijQgE5th9pG7TJuguDLA0YTR/76iW2KgYkqHnGHhgt+pDjX3xAAa2QfRs0CnO6v3//NzcERHm2MMhoThG0vq33NvG27XzaYgpqKZlxybgD/+nIEpXUAAAIgQlYZ7RN2QWd66gWHBsh071zmHE25GZ8rXNMWhyAXrwMr0sIX98xycSz0E6NS2a87rDujUPotGRWaNnedboc7ycf1p9JsGN/B8QKTvkbEitfBenPxI1jhaOt8SYvSnuOoQAIhgl28CFDXsQURAGLR8LB2rcSS7Hg/GKDoSFihgO2/QY0EbmPlA309tBr05V+nTp1N5nR/5nkdEU/9yEIzPLUAoJJLcAk5qdkrd4f7OcuDCWReKtQIi8+oZMdBq1yJfFNomBzoIj4IfU3Btq2o/J/v16E1P3ZK7OGtFCLNrlH1d16/ZgdIAFuAXlpdTQMwC6WF5ahWdp7jibNSzJgeRizADgiIcKEeVsK8Dd+raCXE/fU/f34huZqDDcjrCnqcSWoaO11x+WFK7VEJiCmooP/6cAQ2TQAAAgE7VjnqE3ZBB6saJaU9iNTvXUaU+rkUnKxoBhw2RASC7eB1FaUtKYcRwuMXQ7yt3Q/hQ6BID89VKuyDrvErs/D/nc/xr/CdRH87fX4Ib+CbX1bEjeGr/hQNwIv/0/ALgoBKuQWwMBLU7WCSJnmKyMxql5XziNYzDDg0wuyUbQfxPp0K2HerYwM5P40QbVqoq2At/2/nfjpoiiwyiQ0VggECCQY4BKKjYT96QTI+6waSiy1Jcd2oQyqiKYJtNOZtUZ8mhOG2bC30P/Cupe/Gt1pJsZdH4+1pmwc+NiyrD5O1L/3Pz2QQTARBckgg+jl+AeuXzDF9YnuOco24JC8xyhFmOLdOIntw3zujXEAP+nHy3N1bo+iblU2a+pijhdQvLwIKofFxX5p9+pCYgpqKAAD/+nIEBToAAIIfOtfRLzmkQ6d7CgmHFYho5V1EvUa5AZ6rqDecwgAiEYKd3459Kx6+8gdKZKlz09+EPEotlQQIuOCWen1bQa92xC/O5TlPM5v//q+jdfbKI2Oua9rSE4QqP1jV3B4WRXrXABQLARjkFgXoNqA1SdC4ZSvn8QBhIhEQRRoeVZqtobwf/wPAOepvOLc7v1bRP/oT1Mf/6PqWuOwOgWciZq1E3LZtNJADEBAgxwDklHRJVqDAzR+iUH5r8KLi8tkAJK5hOyz+Ly2RC0+JL5XqT8/nvobzuQf0+O2+Q9F0Ff4z4Yn+EFaH3cpokCCpt8NwRGyighTsixt1LmcjwODkmi1sebX34kPpxOCDa9W0LNjX20Wys38bZ576s07WJy/ebG5o8a3B8kK7NSYgpqKZlxybgP/6cAQAvQAAAhc7VdHtK6REp8rqPOVbyKDtYUGw5rEDnqt09IkyACEgAJS4DCv28QmSjoNNih2Tha6xLjiCoqcYRsQgZW3fK+NDNRfiHFBRo0BOO6j9H5On/6J29/4k+E2kY2bbPsIsLyAtIQCZIB8SOGs1uELgP7RY0gzzinEYGJmrgtdPzaG4V1DvGdW0PzdG5k70Ss4nqKOlXVkPoOFmYIledB5/6AGmi7/psQIiCADJIMFVHeOHtbNCcq5It4qfEpsFDUeZynKtqNO/KNzf8v/fz6f9C2q0kXZ9pUbUUWEXgipzM+DSmmiLDQqpLEuxYAEEwgIBPbgf+a8uLCGPUozPSV4r+0iz2NhyuoJIegSh/xuL4JuC4Po2o/J0b6L6vRQDm9/4dsGK4AB1vDjHA12JiCmooAD/+nIENU4ACAHzPdhQbDmcP2fayj1CfMiE92FHpOixJhyrqDec+xAzACJLUgwqn12AVG7wWzChbinUD+IA2+dxcWeVJpKA55vEBbR+U6P35vX/9W/nf5H+OE9mTEd/bGzK3MJ9UAJAIAuXgekOLbNrBpMzLi82cTuWoz1BkTUURgvTKhQrr0XjntoH3To2pOH/3//Lyf6cK3acoEUG4HhfqBb39cAAFySDvsxZtbqRmZnBp1Y0P8e4Qi9Y8GnoW5XqX0F3P4qLaLx/kmspDu+p3/mq6acyi+gkOlK0ILTNVQGhND9PG2IWgE4DBTu/HQZDIGcT2aNuDNjL23+GD/rmv1Yh6WzZSVg16eAc34mFPM6tqT7f1b/8hzuideOFta8sbbLuLRDxdvU9oTzfee+tUYmIKaimZccm4P/6cASEmQAAAg09WFHnE7RDZ2sKDeU0iMTrX0Msp9kRHaxolZWmABtAgp7fjxc28mNEgzvLe0Viv4XHeEAyuX4b/31JyPoXj/1b+nZE6bz0H12m/oLbsjoAlFCJc0H1sHxu4VS9OxV0AaAMAz/8cB4sFIUGWzTfFztxfjXwsAg+jgxP0bh3p0H8/8aM/o+6nUpkbtGBN3U23zpU4q6FFI14aqcxqE/G45am6xAxDQSd344eSG1FgFuaeRm/W/v4CoHBo4RjCtUHFf75ScAmxo/Un8a3J/t/9H0Nyt03O9BjiZFwtc4bC/5usPlOeK1r985qGxiAakkFRTbRShwTkrY3m8T+dM2sVr0RgQq9FFfxh+JcY2FBBsCcQHagvf/b0s/RNN/ej4ozMUCvSfrNraiisU7tHQmIKaD/+nIE+p4ACQIROVXR6hPWREcbGgUnGYeE71RHtOzRDZ3saJOJvgAmAAAduA6xRznXdXMMlliYXEaJBke8ebCGDQgkQU+gv5nUvoKOQ9R/6co2hf5xv68H0a22lahth3Jt7Wgcyb/4EiCbGIkuOQXZ5PIillbQ+/M7jYgfPbKA9MPcCZLvoH8xty2hP+jan8avq/N1/5Pvrr7qPOsaEVixBlSUh8PmqNC+EAgpgDdR2eRFyxsAE85QaHxEvn+O/WKpBVMwp+JXHm1fQA/+VD+nH+ON3681t028UmaetK2qbypbNJWLXsqQvQKgCAVJIPijSNPcBbfYHj+5vK8fbQPdoo/zuJ31bUNf1biObq+ifeyWkrTdmKRnuzFaUS442x5QebXYy5hZIpuVJxyYgpqKZlxybgAAAAAAAP/6cARsTgAEAhA41tDPUaxAp7sKIYVQyMjrVueoS9EJHesow5XLACEAAERtjmM6hGyJDEr151ip3FTiBfEcNnqS8jbMbKiR15UbaG++Ub/7TqTdVRiVloeqV0foNJZw/26GJUp17ujpQDoIgOffjsY4fI7hOy6UZdvxDhYZZQlxP/bjP4WHOKcraCvJ78pLoRT3JdET5T/xnY3GMexu1vWzgL2ct+wfV+goBO3Ydr1CS6SiWBQQ59D4OLUk4g+QOsEY2PaSvmvlG4s9OENoL7PhhuP36O01foCZnfqV0t08G5Gxg4ww5awu0UXMdlooAGwBICt2E0QoggLkLxQdv1Cj33/jdsQvnD5khz35eE20bUvf+j8/93Z5L39v0V//xX3S2Daj2X2jM9/d7GVd5Vv+JiCmooAAAAD/+nAEefIADEHaHNiZJhpUQyMrGiTDPIjw6WBnpE8RCBKsDPSJYgApb+PJcjaXIxOCdezw1sQU+mEPEP+wlCcQusTvFco4Ge7+CGVlqJQ4U9RzVwx+IIEL7XULRS9h8Xz6YAAAACev4gujbRzEbZ1eymtiCn0xDxBe/ljpxCKwnfJy8DNbZtjpCQiCuh6wQsnFSjvOKDJdTlK3jAIKip9ByZsdTQAApdx5YBLHBcjLbkDo0oK/LJBPae6GV+HoL7HVRx22fhC82CBK71BOzdm//H7b/ro+f6I9Rbb61IuCPoDTbLeeWeS9KTAaGAAu3YY67cDybw+zjlyVz9RJegUBuenWOchepwVDA2BMRZA5kKHGqYXBnxIjb7aD6CI/C8jJWCJ6XG7YEU9YY/2WXpiCmopmXHJuAAAA//pyBBngAAiCD0lZOaIttEOHmwc9Z3SIuOtaZ6xNkQkcbFzziioBAAUu3F4c9NYNJLjmMhEsRT+VPk5UqdB0qzRsg4NqAaQbKKA877jdv6D4Uarfpobb+/mRPor1AiTM/+v/z2EwT90AAAXZsMXlVKgawZJD4jaf2aWrqcVdY8t0hrfzoBxsaNVsicpVp4Yer5Mjt/x7QjmepzNktW513//+U5F/uZLvLvU9AACktHmlE8SzEI6B7JCkAw50SY+swLPTnKjMgmGvLFuAneY3qj16JljtB1ecvgZq/9DZG2/Jo2v6vk0f82omGhXi3XV99RAApruPiVme5P0a+IA8dYv8JlIUmu/ngktomF74P9WxtdcwEvwGH1r08U1HwxG7GGwHn62XBP/wUq8p/r1iBhTt8imIKaigAAD/+nAEgGkACAISOtg56VLEQadbKg3nHIjQ82BonE+ZFByrzYYpUgDAAqXYePgoWSwshDjsXZJrqBP85Oe07nGm3S6dNUTMhPxZyBkKho0i0H+t/9tG1/P0b/330f9cqXkK+3YgvI+5u1ACFAQnd+O0Q02MArqwiX7pBr9DVnzRsiEeWlw0+GHlWwErvUJe2TTdOjc3ma/q2Z3/JZz7/u+JbCvYrql3Fgn6UIEzX4UFiFidhKGQ60Q2hh+J56Qt6uJ3oxVsqWNigF5iXySymFgulWxc9H9fF5xsUXoyZnzPy359P4nBphxu20fS/VUt4X4ABS3Af3TjxigJBNSxjrMs862iYK9eKj8qJjRfKkHAubfEFlHkAX3bMNy/26PKCbqa3a2f061ZKvm/ml1hpZyosgvRYgMJiCmg//pyBLzsAAwCGUfZGW8pXEOnqzo9oj6INO1eZ6yrGQWdbOizihIAJyRjmA1L8To1IjUicJiFrt6C1RSeg5eoZxd6JWt6mA35A2Y7/0Fsq1VX1YmpMn1GkZQJUxbroHqlE8Yzev+2fQfJQAQAAS9/xmbDAy4I1JUF9ZM03FJpuUc9oZrxw6vg7pqDajaPv/yadf0b6M1M4+w7u17WQ1hAk0ToaPthColbYA262DAAXb8PjirV1D7Cwcn4HKMKkWwFYeIWBHWzAym19AOahDXorM1gPp0ft/viHInz6js7bPL//i/H9sm2VtA1+O/tiugBAoAM2/HcG5DyLhhbgbtVj8xJG0imzIqsTofwtqP9OI053//PyZDe1sn6HuzN6fh3wKqeFRdvFzqHJFBQU9UwYTEFNRTMuOTcAAD/+nAEiGwADAICHFiZ6TukQIcbFzxHhojMeWRnnNERAh2tKDSIegCXduPXJNEpQPsxXq5IRpuUO/UakrDL+cyGs84Z0EXH3oRVhyghBdo2Ws2USF9SJL5gvRrYUlDH/va1qefazPvAkADLth9YbGKsYhsrabnzi30Uu+nG+hg7DUEcK1R8JZGmC/9v/5HKB2cn2bN1/qqlfpsw1iQ0QyDlcUFCKfv1MADm34+8GW/2FK5xmwfutqi3XBw72d00TTpKJieoPs18X6PcO14WhzofbEMPdWTi62D3KV1hFTiyIkS5zZoCmqjJlzWYq3VAIopAV2/G8AT2DZlJQCc6Wc4K/yJBtMJw0EAvh3q+H+42vPo3+ivBB8J+jZu9L5Pv/tg8MC6OgpNsrFjhNnYYWmIKaimZccm4AAAA//pyBPX3AAyCFxjZGec0NECkarNh6liIzM9eZ6TwURwdbFzDlXMAmbfj6yiFqGO018Qhf7a3lOkkfnZ3xomL5Ggv4S6PUpqtzAedmvqcUcWbvWeVlTCznlYpMD0rSUQysWYpqE9iStMgACpIB+FdQJg9YsHkE9GiK9qJ4v2LVqUTtT6XmVtna6xhayagBRIxRoTEJuSAqxe+VSjf8pFJruTLQsslt5JIetAAV2w95BynBBL4Hb0iZ0NIrubxgsq2Lg95wXSw16foCWUOxJuVeglfq3/76dvV2/9WoWdCqsXEDD4OF2FiCKL2aFi1S/pQYECbfidkdTLxTUxZEDUFOVIYZNiYJ75USuE/bCPKvcuvZv/ztQEys/drSGZG5dEq2jfI2KOohmPf791fxC9JVqrncHx3f6yYgpr/+nAEE7YABAINOtmYLyhkQccrNx0lKIhw62TlPEORCZ1tKLKOMiCpv+JZNJ7IKYlq0HjqWXfb1VYDGoPdi06gn03vU+vO3b+pszb+j9Fan0QjTdW9hao0VO0rm0WnSKhG1SwBQ6x3RAgEC7/ikPDFU2UoCc6F9Jr4SNhqWqCNgr/EJ1fJr3//QRxhc/0T9G5LjkVGstWkcYKvIZoLL1GAIqlRkymsuKhxMBBV2/FYEKXhCDPHzcWO2m7fbUZ6qVlxwc1H0b897w4Frxtf+h9T53+fQf1oif76arqPPPOkFB1d2aGtSlcwYYTHti8AAqABz/8bcfE/LjdwzuTS9VKM3OxpkvQV4Df7q+P/BHq//6Plf9JZXzYvbZD5Z5ykeZVMcewVm6UsUlIurbWirYQTEFNRTMuOTcAA//pyBPTeAAACDTrZuWkRxEKjGyck6YSIjJVeYLzhEROO68wWHDqARAc3/HGJ1JFIIFUwNbS2eAR/ONVMFfQfgb71NrxvxI8r/6dH0/bQ390S7llbtqIVQZo57JhYuL0qYwwoutnzCkwGBKm348qBMTbVMqghFMgz0ObZDNyoYytCfCH1/69wt/SCPDi/snFaHWLe2NiScTUkq4geLDhdikElmEUOK2KsJ3pACl2wqUOgpWEkYqUQ2h4mcV4gHbkxJ44+cH6iDMfDOOtMH9OPP369DcphvyE6km0VILIvaLNULxthJto21ziVqF1tdNABu7YVggAw7Lh00CMl+qLUIr7+MUDKRRnPhZ8vjSWXUNadB9ypZanWqd5DW+8BChAzKkTLiriIsSJjIsPaoq9SKxXZYPTEFNRQAAD/+nAEkGYAAIImOtk55xLmQOcrBy2iZIiAY2LniOsRGJ5sTLOJq4AEBXb8feDTVfFPcWdqU+CpuUF2YdSk6pc+gEdHyaUbN+Z/6dTfT+56NmdJZ2m6p+jaCnmbicveCe/k+GTj9dj+6r/rkAEADLth3BBjzwXi9gNWIYizTFJs3WVGOdp33bfF9sTp2f/99D6f22a/qr0ZGZ2dfKOdPTAq5+PZhIoTatIBFe1ACCCptuPqp6KqwyVtYyXq4MM8MPh1oPlqcvgDPKaojJqPyEOq9brkvhlQnrNRCKPbViKIGnMjQlGlCwIuaAh2BHRUWOAgzb8bJBKGWoeDwqPmRUV4nB5YoRdHWbUZL687DFI2T83/9u2tu1t3leSymdKvsz+jlhMFRxhFO2Nq5GrFH9xssV/ZcbupMQU0//pyBGMwAAyR/STYmYU65Dily0cFIgyJQONgZ5xNmRkdrNyTijIBKbbibwAVImkkMz7R8qNDeL4CGx6zVj8C5uVNorCDPbLZ3/VMpKZGupsBF9R9yxPENNbu3NS5BJCLMe5z0iRMAiQ9/+I6DJJ1Ofknzq2+ARecNwdKkH25MVa2P+FWjf99CZV6a78c2HgAeKRTUH1E3mOqv3zt7sXBCm34+OLk25LobZ+0Tvq71HwbiW1RnHasXxK00ICazUDOvB//kGwTa+81Abam7IUdprm9KiC1mJySzFVVvBH3Cea3SzXLWQ+kjxRBe9cCiXh7hXGudJssaA39TX2KutAy+Kmo2t5aYGfznp/1FmUZpVGyrvJJshjPVZh3d+ZIV2gbER4lVSB+4uE4u3QYSb6UxBTUUzLjk3AAAAD/+nAEqTQADIIMOVeZ4jwkP2WLSgElDIi45WBnsEcRFp8sjJOV0wAntsPnQtKCsFJCM6Mltti1+vvtaQ28GKw2QeoD+TRymPBP+V0v/T//ZtW/elTa71XjSKhsw9S0AsnGpffuZntpkAACAC7/+OLD+nGAOm7pm88x7IMdHEHxg18J6Nte1wN/FP/y6HyGbU67iKzTGGBUa1umLgouxnW2pSJM2nYj2AAzYce8ch7AXwWRNDWxErLiMwiqEGYNWQ9CHUWCAxdEo9U0eoFzvU9f+ZtH3tse9X0p9Gsd5r9A9Yb//tSDaxQsInOicSgAzb8ebwLKBQKCwjSTbVE5JlhjKH3cojq4eH3UIJRd18pf1X/6E5P+T1m81Op8jXTG3ptKNPWn2m336na5xxf7+2+sNUZTEFNRQAAA//pyBAp1AAzB+xzYGe8R9EFiOyMxDHSIpONeZ6xNER0Ma8z0vZoAF67D3P0UhGivoWYgui20bSTAeOsMPyi6+HqixJLF28jVFfj7M+J71VXn3FL9dVybB5cO7yKrfTT/rsvR131AAzb8TpyBwfQLmYwHBG58nw+9KXyzZfGBSJVnI+Dt5G5zx3EX1vU5yc9N58qpwx1d5xPWkqskE2xqrTwGdFhW6IQAJtgPkzyVoM4Rb0CDDanBuSSGEPtQ27Hw9vrDmVtO3chwh5CcdW0YA4PVv/ovqye8ppJ5jl7IBIoMq818UJixZvuxl9wIDt2Hye58l7MkQtKjjjXhNSqES2kX6FFbUTgR24mWpb4yXeZp/JGzfz+87Z0T/tU9q7LgRC+RVXuzq6dqi7BzLNimzJTSpKYgpqKAAAD/+nAE6lgACKH5Mlk55iokQQZa42FnaIiQ62LnpKmZDg5r3PMp0gFAEzbgfSRXUIqj1ySSqZpwYAYUfh5tiBkLYOiboo+b1ZHmArYi8aO3/yP6uvpXRjbfUc9/WTVQRahCTFka9IADtoGrLRYIdIgMm/MrFitJySTyQFafBAKonJIiLbe4bg7AKmcs9CKpLzwl6vUzf/QnolPp0WmrW1l5PTQd+77VGBBm/A+3pvppnQl2P6ceIlyUh8wnlgLv/SPUdkQrn+CW9m6jkeYH/Ud/8STSpntnporU9dTVR363RqDH2/U1PLb1r0LSLth7tphloPUDZQYUJybctT4Oe0qq/XnR0oO1rQBuDsfvbpLXkQtdXoS/sVftPdlxU8IRwsGwyoaDVJmmjl3VdTFe5KYgpqKZlxybgAAA//pyBPBFAAgCCTHXmekTtEMEutNhKmaItMVjR6BO2ROJbLTAmcoAGbYD4RAczGMlsS4GqqS8KVJrK1ZfYswpn32ZmdRHSwvgBW/bGsroFBdGhy//K+lW/ZfJf3dBMvULPvQlaH2dNIADttH3LCRTWxmKtqhaCPKB5RJOoBMVFvZ3El5UiXTFQZApAnCby3Q3lRZvUtR//quheNVJMKDljZ8ecZf6vb/9e7QEAAN/4H5mHitMCehB9PmFuTMCfdUAdRN942klnAdcFD9OfWjF9rP/8O+6q4ifUcyykVWa00NesoM0cz1/elX6312vduH1AAAQAAFTb8TdCJJaASThkNlqhvMDtpQScDuWcs4mSD8CV0XPcCSbggr171bUzt2Scl3qsOBF04lQSCpUqNzKXPEIrXxqYgpqKAD/+nAEJ44ADAIFHNm5gzs0PaObIz2FNojQc2BnpKtRGpjsnPSVEgCAk3/8TheVuF61QL2G6GlePMEAnBumVN84IzRBlGzle1jOrTS/yryj3qiNjASPGzdEkUKUKPHBgzu2WdPF6qNAAd2/Hw3KpUBCB3LQTLkNzIRq3HNm4xjLBzD8Mx2/az/iQHUOq6hjbmCsYNz4hP9fnSUgzuEbVS1GPcpyOsAGbbj9+Ohlb3tx0J00EkqGwv1hfrL6nWJn4eqRHuHjFGarK+okC9dR93LMU96NE2eJrhkwZO5+yY55e67rAanPWITxCEZqBoIV//HrImoIwhgWM/jxr2AsFgvMYS4UdRodDOMDKBm1bY79Qb/8mh48ldHfms9kchIwms8de9CSTlzba1LQjYhPe5rJssmIKaigAAAA//pwBKbgAADCHjlZOYcTZEEHeyM84l6IsGNe56EtUQgMas2HpTKAgRd/4E+Xy+jOdjCo6BiVYLnWSpdTGKjJ9J7RWWiORqaVLlcwE3zcoqrf5S6P7fdQaF+WgZ1q+6s+w2PeKqJXxZinqCKn/A++0tIi5PFhWVUD10AKqEzTl5QvUsLWLUDkKEaErntMDXtKI/+vvb6mrvKapaJ0cNZr+jnoJLapIXFnuyAwBABM22HqVK6am5ITiOwAuy/Ly6Lyhc+LVIpQZBlZIF1qRKutfzc5R0aaqHF+mschNpwXdqiaNVrVS9t/JnNaihndeZNbggFbQPvR9PBoAIyXqJiK6d42R6qHcDR0uQiLQVOyb7hc1psvI8c1PBI7CW6uhVQJ52x1Is4Sr9bvxEw5ud/1U9P60xBTUUAAAP/6cgT9VAAI0e842VAsKGQ/gtsnBeUKiQTHYOC8oVEQHaxMF5Q6AAIAAi/8C2SuphE5QFiGPRdgsR40qzE8jjQ2NQPiDRX/oyqBV00b/7Ns6f2XP1pSrEtfT2i43eZJX2Cymx0AAi7/wLyzxRPDjNVc5QpS+dajABxUfIw0emYXDImGXFs0dgJOpbKs7PkyksbPi6IPJvNqdQ5SrRclW28cKOMMAKv/wkiCxM8ymTRP5AnUxttQwsMwAeR1DYWoDhAdH/9KuHMiNEAWrN/M2MMj09avHm0Uy2YXQF7AhBxwhCSCilJFFNRL7P7AUrBlWJYLUO1KvNHgmMMbR5Gn4XqyOFoNuoOGSjo199HUwfUydAzb/QnH3Knk573MSquw/szO5lXRLjWXDAuRN2Mc+WTEFNRTMuOTcAAA//pwBPrjAAjCAjLZUAwoRENmOxM9JTyIrLlk55xNkRWXaw2GFWoAAkAUb/8BoLLSEQ0SxCCtKpAtI7Dzbq8acTQdEXo23qEABSayI3/2P9qtoW1HPkOVdVHMQU+LC1gy/ePWtu1qwC7/wPfKneCfhJXaywXD9TP9cX/RwrvTdC9RjwyMavXR1cU7NCQTo//L2Wlt6OkioNO7OMqIgGZEInNJscg6cfquZJkCEv/4+F1t1DIzpmbyxMXsrnkKhXyruVPKlS0wvctRkfqM/nPBI3XoPq8ZfLutiOoQ+1RgJWn7zr3MvsZFCjK63oWro5IAB22j6ldojLRFcVWlnG5tXTPCkdQyQRwyocr8M1UJrGhtp6GsjR0iY7GSI8oP04t/+/ya9rtL4+JJjqOLlun1CPZV6PLpiCmooP/6cgQEqwAAAhAyWJnrKuRAxNsnNWI+iHi5ZUYkp/EQGSyc8xVyCJn/A/fnhDSJGUerYIoE7FjHULfKL/MbbNomNQRPTW44cph8BPz//jH72+lsx1KxzSNMHsRICDLlHWLShgSzdSAVAEEZt+BXKy0JoQxTbQxHtRKOBjeUXJucrlWi05WRKw42HsTMN7ztQzfwXK6A0IP+YoUfJGQtOPFC49VrnSVrVlsAAVJBlkAtYhoao4YDdY4DpFWCfpCe9bzw+PlHVQPboEghAJtUkfrAv43/8T91vf1RL1YODiBU48w4VqPrqgwtf0ChGAgRd3/Hthmag2ieG21QkPYALTeUA/oND4p9Z4lX4rPau8brZX/UH1f/HdZn/99jfx4YeL6oRNsrQsc3hIujlB6LKGl0xBTUUzLjk3AA//pwBFJPAAiB8hxYOesTTEBkatNhB3SIpMdi56BPcSGY612EndsAIALjgH5mvmQ3gxEdB2KJtkqnNMJVmtwzUhGH3S8iOtg9T36E1TBuZ5K0WW5aF3gbvqswvehwYJqciIipO8EuW2j8aBxXVLrhQzfR2u1p14MjsBcrsGSgyuYaGUyTA2VeBxa7ZBSXcf/HxRkm/lW1kmJzTCxu5x1rftrvu/9RAUnZAPttjvUWIIlNNZkQt6Ye1qyKEnCWMpuYTxX6vkUibuAcAttY8GWrf4N8rq3113oUZqpcYO1obFINmGx9gXWLra+wgJM2wH36zjO2KREZH4luKoYRBU64P0LmbF119D454syLv4w7ufUbq3QJPfI//Vu8xb91TR+OKx5qFaNX9B+Tq38yq3NlzXnrJiCmooAAAP/6cgQLPAAMggsu2NHsKeRAhdsHPOVmiMSZZHTygBEaEaxqnnAGAACAkO/8D9sRzmrSVFovYD12Hi1PBi9zP5kcHbtWBVvtQtu4b/erenEOlf9lW2wk1IFCBE3abGi61NwKEyrSyMDAGALv/A+pE0+FsEyKR9ZLrTVMhVBPocjowdsaNHzd8necH98fs/Toy2H1/Q2s9hIoHEYFD1Q+SQLsqa8sje1r0Aqb/8fCQzheLeUjyRBTy4ddsgkFxciO4U07ELnarYtbhYAN8gO6ftme6KouBnxjiYAKmSy58ACwsylTkC7drkrXTobuuSsqAC5JAPe0eIGM/IZFyvOMkQ8X8c6qGFZQsBgtSqiKSVHq9S7b0DHZ6Pmmv2zi0NCJR9aSS2jHxYLLULjyubQAkWjDVT7o9MQU1FAA//pwBAkhAAACIVxingVABEFrjEPAqADIBAN/vAGAARIB73eEMAAACsVCgUCj9d/////////3RlU93mW/nkjR4YZVT//z0YjC7EOWPf///kY/Fhh4AmcPAbwKf/t//xDg3j4WAK46PBcLFAAFYqFAoFHzX/////////3MZ1PfM/55I0eMZVT//3RiMRYtlj3///1IyckMHgF5xIIsG/////FsRZMPAvy48KEnBAEqN9v7aIoGnCUPGFh9D1FRMazqHlmgsrAqGhRpV4+TOpOgMBGgEg9O7uma48l/DZnhNzSpGtmIyzBy9x7TrGoBhmWWXfRFGYrQocqWmDxNa0iEXEATCz49L1yqD5omCrwqpp2dMgsaASD07u6ZrjyX86Z4TdApatmXLMHLRcHH05catyYgpqKZlxybgP/6cgS7QwAAAgEc4GgjEixDYtrgPMJMCJE1aOQEVdEeFu4okJTWJIEcbabqaANmpFUPpJFlHbVW34NHmR5De7+1AZ4RB6GvywiBoShoKGluBUKnVhIcPLLARb+R8K/7SOiS62lXdq5Vwlx62EchpIo1Z0ccNeUnPlfZm0l4hMwSlSKq2OKyyrSv/yyf4luu/0rcp4dPagZOlXZE7Oh0FYlhorUBQVKnciGgFAJb/+jWDdowGKFDh8Bgi4tY08bXX+TAKj4vgKeeL/zQX8N8l3ImcEpubQTBGMfNWY0HYx09bnDqzfRB1mT9SRpt/hkUG3iVLrbLikHlBEpRXJKNA2jFjiO1ndRAfMr7O1sVr7BQs90/HLdVRr90mKdUjIzZeLQnSTeNlx6B7zesoqZhQqf2AcRSnqTEFNRQ//pwBJgiAAgCCBxZOSMSfEPDmzowwiaIELlk5KxE0RqObzQwidYBIBTjbSrJBBsD2g4DLSUWMnZaGLSaoh7BgIlWQqUvxM2kOL0aqOtDBaVDBjikjDjw6rY+554VJrEe+ote/e9/7AAgIBUu34lEMCrcviBB+IQ01jVdJmBdtAWzHdkr3AvSgAr0PW9TaBdlh06vpQwzyQ1iiiB6CYWraKPQYPDzsakwCaRVAKl22vMDjNEgZkYSeoiayyskHnrO/Ehd3QiMj8wq1M4o5Nt92N0/avZiQW3nBkWDzK98fcfICtSxZ70b1s7/h4glxJNpLb7axHYlZAK9irpczVkganMJiktX8v4oBWHXYEHjQAswiFnNUKESW8chpZQGAC2CJeLqE1TB7Ug9BWWFhj1iRLfWmIKaigAAAP/6cgQSlgAEAh0c3NChHDxB5KtaJCKFiC1vdOGEV/kbkuxMwYjqAEa8mXa2tGPjAuOatn3eMsB2JFTJIGor9ChosT3FWJXCRksOwXXKPtGpTPBUofPHjb3lV6DbCiiFx1ws8i0M1Z9hH1gBLSClbJGkuJiWRybD++p41VvvpPYBtS70HG2P0N65//I5M4wYek1faBrxViey96hdB21UOSayAqMWmzOqNsCV/tBSk2tum/uFHGygOF6XfJ5sVhxqKGVlju4WbGsgz/xDM/w4t3wwspdLjpkHPy/6ym0/Wz+8tn2vzLy3OjDo27nd5AObbatH4UFGq5ng6s9gnFpYxykHZOjxOvWmQoYq72Y+v6GnNHJUokKklNS5g9Y5Y5aiVaBVr0zp8glsLlxwxNbgrxrBnqetMQU1FAAA//pwBJLqAAACCRLXGeETlELEi2cYJVmIzL15Qwym8QUMrEzxiPoEm22uRHnSzvihUg8liyFr6lcofiPRNI1uMW6QgMaEouuJEZn4QfB14jfmHVVmwunedU/KoeqtIUPMXZoGIXxv9QPQXLpY+kRGMWb24zCEzIyI3zSWixnvWK+AJuac2v+mNInYxZYHARDY4ihK2rsHsEtpQ2lBxKAEFGhkszUiS2sez9gwt/qO/7a/Z1wQqhPJHaYpZ76K66I4RxhKMcXJoX9Xr/utVW0qM2mi12iA42LBGWJmHtS0KhIlUuTqpPvhsUSKRDYYNE/ngU9ttWtVE3QuWsvzAzBUSQyXXOSiTP4wOkVugtNgL2fD4OhUVCzjChzaBZ5Ml45VKq0DcVHvrvuWIHiuoigd+a6ExBTUUAAAAP/6cgRSsAAIAgMcWrjMESxDQ5taDCIviKFtXGY8RRELBaxo8KBCAGA5bJG9EtgUJixjEVct+vQIzr4VXr99hhS2JhVnesOhAUPnRt1rZvm0EQ8DCKYu5InyZvQIgRRcadx7GE2nPaBglBKdskdVgiBhpRIguzCwkjpPsFCmY689Ee4C30G+1ggBkzB1Aut1LR0Pw2bBqKm2zTCY0c6L1sGB9yBUmt2rSM9YEdttiVEDyszFZ3CUXb442KWzTeznrqwIbqqBATlb13QWZwAwgaor/4OrdVVDF8+y3fV//fb017+1rN/Vbevtr+3X/8G4AAQAp3ba5aC0XbZkJAVbq5OKILFSYKAG4KBxzyg1JVmo/6ycl8XirWPGJ4dQMFFMlnjTStxIANSrNjXVi2tDVcz9aYgpqKZlxybg//pwBOJpAAACGg5a0SMxzD3GO2oMI7eIuGNi5LxksRsMbBzEjGYAAagnLZJLcwxAqC44IoFZpRM1VOyJ4bRy3HgMSkqWBep1+oFp0xYcNrLhcieYJUm0PNmGClSc9Y1dnS+weAsW1K+GQBEZJVukkxDhitiYvkTJe5Gyw/z1BIRlxouX82kJvlmRAnNehkZyM8ecXC/Ij05ZeKoe6wkEEKvfffmxb+oI4KcjbNDiZC0QCdzWofUB8LFG65egUlL0FgxvpDVsvgSlMyn1GyxAAdjQwPsFmlgkRXOmH2OUcUhbaJktQ+5hN5m5jm+oA4DcjbSElAcYwwPpbZRY5i8oq08s4P6tt+TZakvKgGFTrhGsx2i0LhzXTQGFixWrnXC6hdB4Y00WcRY5ili0LgOlrGY93qTEFNRQAP/6cgS7XwAMAgMdVxnsEbREIxtaGGU9iKBzYGeMStEchuvctIyeBNttrXEVRsTiXYLSWrqYD6Gl8+yyBg9JJqZoSx7T/O/4UdAfKhs0WREpXjAqhd9r7GB6far5TUY7H1kdt+tLvQCAP6Ml0kjWEVbF0+t1mORWrkK0fIapQI+3Z7MExjZwt88PRnnCzwRMlAKeMmg+hVTkqYhdAoRmBWRXAaL3rYcN6qPW4F3batqfyssI/2gtcXVULMas5yF2yXq0M1bW9XTo6HGhcUQTGANZA0VDQxoYUGnxqmvTDYhE5srShAkWImCrjSSfTR/+sBABbjbWCcBKOEsqEt0eStleoP8bvBmTgjXknQR4XHNc103oW5yTxoogNmbh44oZLsLAMRIMvh08FhgLjo9R0RAe4+9zpb9aYgpo//pwBN/PAACCEFtZuYYRrEPie0okYjmIqW1zQwxHcPsOLFw0iJoC4KsjbmI1qOFaNSDjhnZe2HwaWq7v3CB2+qNq3Xhjox7UZ2r7WarV+lPtSfS//+j/1oevXTN7U+39K10/wouihesAAVg1LZJLaAueUofYysIFqXuuQEMz3I9nhiCEdjuGKOsUEZkSGEkqRr4ywGZ9xEZOKnqnLQJUsaq9d4xiEOjnFf38IqC1clrtba6VwxQrxLARzU5l7pXXhrhb+6c4sKY5jiFCf/ebZSX5DUVmY31tK1ykq+re/R6odMu+i/Y6JfPon5tX5Kf8I5QndttAYUNK4KG50mlrCGg2F9Ff2VHh+3qt02gY5LGiqUzaSmYJLlou5acJTqVXFqXNFEkEuQhreiSSsim/2JiCmopmXHJuAP/6cgR4bgAIoh5bVzmGEdRCYjsqMMM5iHxxZPSSgDEHhyxqmCAGAEC3bbYlsZCOnMhMGJFAo8C+M6DvTbAaB60DMDV29UdWQ55qVDPT/d9lt7crtrMTXtr27/oS3/rTZtf19Nq/b20/4JgABYBTkbc1EMX40MTNbGsuR2lE/ZWKhoZfxgbSb3vsZcZV9Yq4E0Oalm58/P3Kxlk3qCMwpSyz2oIMvsq3XZZ2p80slOWNtN0CmB9cld7qs9baVV0HCA9l3BBAzqvRH7k/GP+LHUuYguPERMNkXgkASoiAx4YOMjwqHg10MXbNVaze1P2eHgIjbbBMbR0WB0e3r3Q8inXt2FIrD5dAdg4UdUG9J5HUXiRTjNagK1iGAzpLRYZaL8qYiEXatAuclnvkz7WOnh7PJJiCmopmXHJu//pwBJ54AAACBBnYPjBgBEJrvAPAlACI7ZV0GDKAARmyrkMMIAAAAdNS2i22AAA9LJ8eDgPwgvH5yGRtuKscmMKY0BdDpktB1szyN/y5gjR0ilFMWUkHDwIEzbulum1bb1irz1h6kADUaDQZjMiIjndf////////9/LOZf8UZjndSq5f/kJiAoLnc5CkNIU//3IR5BdGcQ9E2d//+KMsDh8XU9lF6CwD157EhIMci58/p///p/9bf/2NWv96XZ1c5Fdv/znIRXIQWRVGqIf/Y8hDnOc4hcUkEUuYBf/+wmdB7PF9RfExcaIMKmFzDDGF7OBCinGuuo3H7MU1bp///p/9bf/21r/dSbVc5Ftp37zuiK6MZFU6k/+x5CBzqdwmIkCpdAH//sc5BynU+ovOLBhGEoeQzjpiCP/6cAQ3awAAAhgrXu8UYABECGu94ogACDRlZ6MErsD9DK40UZXi6rZ9/3/tiAAwTsqs5KwiQt0LkOG59LWa/dfY91bjrcucMy/5V/046t0/QVczLUuPEhFWGmHvyyuLHdmjmt7aWEc7kQCV3JttrYiBMzRoWUkWdaoh0Ml86WZWNbW2bVkopaEpQzt6K3koctSvkFen5u+bZDdSskz/9OKU9sJHdmjv3tlmJzuRAIAUQEv9MRDLgdZZVIyFc2XxjePZvzaAyQ1SrVKgcygoIjxEJbF+JTsO/w6RgrUDR4r6agqQ5IkVO/WDWr/PSp1IAAJMbltjjSUwvkC3Zhzb3BleuSnSkQ4JV+X5TdcwIwwGtLW2fEp2Hf4dIuDVQNHhD6agZIckhbvrI//PSp0smIKaimZccm4AAAD/+nIEAZ4ACIIdWtexKxFMP8OaozzCdojFbVpmHE9RCAysXMCV3gEC3/aY9YRYQCpJZYISnU1aWpKuo/no3M1j7YB00X/5T+nMv9X8tnM3Rbs71R9OctK0MZlyHKS6FZrN9+n//f9IdzdYJckkZ6rkp3BzH0YoWSaOA34TdgxNItXTJDVWWBunmJXjVruTd36Af8V9hhoYpqTeqA3b/bZa4hZ7tugjyXX9YLV1tiV4tj/peGg4jXAPwp4z3VSxzqxV3W4/Uxn97S1AHpnd//abu+nUY0lX73yJZr/R0TkVPfot6t23df2tu/eZd9v/hXKApI25CpIq/2xQqPdVbONzqISEXNicWNWvT6gbw93LBNZLAZnmlnmTym3K9gcSaAjTaHsr62UL32E7lCd4CudFEJTEFNRTMuOTcP/6cAS8fQAAgiFa2DkhE0RCYxqTYMV2CEFrcUKEW3EPCOvcZgg2ASDFdtaq5lB1w8JCqYWmn9AEsQdGgTnNpJ9bOFi9DV/+qvX9EzUZqnp83dVSyFqlFWrMTLzU1Rtbqzf2//679PwhqrWAv//5eTUVLcisOjpH8e151mSigmWtV6jav5tKqNSmXKbJAshHeO+oZbLsqJoceuzhHY3sMNo2cAbrl/lXL9y4ur6gQGuTk2tssx5bJHVtfqhaq3YaxRMMMjVSLeIfkv/ABnA6OPPewEqYaSAMjDSI8p5RpPV0wGnqcrzspn58hpbuP/ghAFuNNiFAyP5gJRYUnye+ca9B+khXT0sgsBsvvJd3XeQRiPOjFAEohxMqwiIBCu4Qb6UUJ6qQoJmkRqTKGNQL+WOI+hMQU1FAAAD/+nIENxsAAAIaW1rQoRc8QoMbWhgjdYh9a3GihF0xEIxrGPYUbgAA+LdtkdRjJRRwvB333oQe6HVnoqsijqu6KpewT/YZr+zjKRy4WRlVKBZ/yJ53KW+0fOzLPG9S9P///t1yTjHrGWJBAGtKW2SVrAmqi0EOk36/C1GaLTTJVMRWv8+miDcaoK3LArXCy7A4tC9as3behb4xDqbFd2ymXzxJFrcwsZNNi0q8AgAtxuTbWWJMs41mVOSKmoJWOU8m5RQwmGIoa89+4V24rV008DPnNAWKGih5Hmp50U8kHGf/z/y+Yt4dpqZohfEX/BgBRfqYSWGqnXGQ1JBZNR0O9dMMda2JuNYe7q1QHR9rWXjuR/Y1ou0VS+k6o/0TIuScEzNCFDoeVac0JSLNuSfUekS3rTEFNRQAAP/6cASWBAAAAfdbX2ihFtxCoxttDCN3iMFtWueET9EKDipM9hVKKJBUtku/+29yJUbSXnQ7U3ZDuvrES49J76lAXpk2bP115FVH/94f88jbnOU//SFMvKjl+a/++567mVgEfoRUAQAU1E3brJaKA5pYRKmiw6yE9pwCGdlVbpQiLYXzdlVl7QPOCEXFQ0MDBoBSAnWwMrWFEDXUKGL0UYo4j/S/boN+4AoRlttmUdDVN04VZ9nzg1oeeuM7nKFgIV7VEWbkE94vxgL50U7GOz16MVvlullld+l+vnonmutba3Sh3al9vbrfaqf/gnATkkiPVXhZGTO3xDSeviPLmn4fnyOM2jUeLDJ0Z2aXQ2fupwM3aKOMbcVbC6FvjKJZ1u+xqKs3nHC7JFCh7FO9n+xMQU1FMy45NwD/+nIEp0UAAKH+W15oQRdMP8Oaoz0jGoi1a1rmDK5RFy1sKJGIfkQAi7JLf/rsrn0u0QmtmWV90brcMAlq5p5L3P+wOrNp7JeBgCWiPN0+jDWs7Jq4bf+vLv/TNgY/G8rICYv/4ZgXJJJFyWEMgh6xCIgSEpOgAwMrsJqoqpOO5w8yaUE+Zw+cpwoqL+QtBKTijcYjRfhoa366OTob/rf6/RZ00RrwHBOS22JVJVWyZiABo+sV7wcVqOq5rirQTeWcj0ty3UgJ/NT/+v12ctqKlHp9Of7m3Wn9aL5F6N1ut5nez1X0oq7f8Td0ItG28DAMklBMChqHJyxIyEdQuPlDMY7bI9uyBW24Yn/c927uqzkR/SltDtX6q2rulG1yWWRGZb5qorJd75Z+ddddZ8v/BpiCmopmXHJuAP/6cAQ77wAIAhFa21BBFtw945sqJCUZiN1taUKEXTEeDGscwwjiABX8qbWySwV4MMZZmOoDdnuwhSpa7Fren3tFt+EaJUXKAAjuQJIBYKm1zPnCRGApsSeIIgf8ry//+eSH6BxIjX/BgCIkE7I24k4Ok8ZwgYGx2DHTbRGeRqpom9+4v+JA7ghXKMuXimvn+NKP97atdgkT4q5VyZw2pLmUtGm1o+8nynbZJUY7oIBIwlK6qLOgf1iKLepnG6Ptr2sL19RBUdutQ4gaUKAREEsmvGbBmud/p5PL3UwTayWZLLvOSvP8su+rCkz60gBBTttsE4ewLCBYgwowT4DxtMDAc07HajMkpHQ+v/iOAviMBDBjDCnqfHPMLFjxitdzVnUDHqTGOKFtDAzAa20pEgvMv6n9KYgpqKD/+nIEz3kACAH8ENpRIRIMQgtrvRQi24i0dWtDBEaxIo5rJPGI9gAXuTctkiq1n8aGKfrTRKiQSyR1fdbgxCnkpF8Ccr8adKrY4WMEGwsQIpKN7FkytzGNITrCXWsA0vOXKvYp3qAKSajql2+u1xyyI59DWmDUdnd6n1KTbV9LdBXLye//R9l8AYgoqKSdlcBG/kCIzSL8j+/8+FSZq4gyGtkbIn9eCqP9Sa2ORtIfkxGDpyZnDtFMT+RSzPITNkaUjJmsIUmKvMoEFYDEsoHcrjQO7OjWTyXHCHQX+LBUSQ2AXhsE54RrNfNPOKWAAAwv01KS4ozewnkRQRYHBwXQYFbamajjr0INacr2TXc37jFD+bJCeSEKhdn9CjCJ8Rk3obdcmfd1jJAqHAks8fLNJTjOXcitMQU1FP/6cAQsRAAAAfxa3+hhF0xDa2u9FCLXiEFtZUSMqXEdj2xokJXeTBVcm1//+220TsJtlBmR5fdcpEpKfBLZp0OpwrQ5FTnBv//8jfyZe2fs5+mb6ET+uRFfWXOWa1/6znnS9S/4MgBtqOyb7XXK48tUY+l0lkKttH3EVDHM5E69o47646FlrK5PIVUef6m5/7v87ka91EudGKVaFru2nWn/X6/2+DcV7tIAK9pOyNtp6JndAbEpu4ajz3eozHIr+1kN+1uQZ+Egmysi3WWqlRCom+/e7t3+l9rf5msmmnpSlu2n3U/27fr/8a4AIVBOyNuJqiQekgaZddR+x9sMhs062Zo04mfftOG/qiR511rIMcwmeLGW2PFHHzjYojFgg4VvHkk1BOjJFfGVKQt33qdyJpMQU1FAAAD/+nIE+i8AAAIkW1ZVPEAIQeMrKqSUAYiYX2NY8oARFC7utwRQAAAAPMt/22XbKnFRYkRRk1j6xNAng/dCzWZ0OhnqqhW3zoerqF7biv/v1197y6tZtfX6L9v/yaVaTvsm/6tS/0X+nTqEcAKKynEySrEgMnpolYKfYKPf/HKz9KuyyDLkPb2qCEXuB9YWXV6w8HwwoEAIEWu5TEAnz8/1BhPWc2VHG6tQY7PyYAAAsn0jtmo1FAbCJcFP1YuyIgzO0fbfx896sIsSeiastwXOLG4ssXZKFFDJJYBY+Lm0Pc4O+cJoY4cdJK9UrHp//0/3gAAABiAWUD0eDsdDAcAiiw5Q4mVjfanL/uz+1P+jao1f9+81P/D6RN91p/877HV+6e3/yK5zmB6lD////+PQhAObIyMLpiCmgP/6cAQ3ogAAAgxd3xYEoARCwctdxIwACNENd7wxABEWFKwrljAGwM8888mOGu//////T///Oqo5v53nxiEIW39vzzKPOKsjC+v9XbPchLi8RExJSi4N/9iXFCVehGxooIjR4o7GYijgAAAFJJbB5BWAgAAACYEZEZKjlUrEm+1/l1TVLoQVY0wrLjBYRqQjh0NndfvoQ34rAvWz6yQ9yKn6LfXWMvilUiU7SSCm5LLI2klFM6lNCLCNWfPodasoUSVXMrVZ5EZOpbGV1Z02oZ/+yGNTsKff//7/1KYz/69BRV2oFRpVudoka3C0jRkcREgACaTkaABUMQ0IOhQJAgtzYiuXsvD+AiM9xBw28pAyP/WY7fi7MdX8/bil6qtCtBp8RfwVDv/iIGvUJQmd/Erv/dWCqgaTEED/+nIE+dwAAIIUGN7oYR0cRAtqg2GCNgita19GDEmxDBHs6JGI9iAUlLbL/a25DL5HIrzM2UzVW2cS1Igi0qViHbooCnZ2FZUA87hqWJZ6w7dDobdLD/5Z7GaywlI09QF4doth1T4lSWCm///uIpF/VFrivwsJS4DrYRnadAUdz7SDMzoMjIcJm2u5fcp/0TT67J/32//96+3v9PVFPdqe97fb/f/dUb/9Qh+igAAGinHGkpJyG1y8UEc10dwyDZH+gkpSLQleXfZ2dLFQBsqZ06WTz/19yluVHaxZWKtCb9Hk7mPJ6aX/vun/o36fa1P/BFeXLbI3bQdQxGQeQIaS/qEnGVqwxN0FlwV6X7u+z7yfOh1cSUBCEVWWvF7swIdMePCEDjgocSQNoYNEd7TqV776f9SYgpqKAP/6cAR3jQAIgg9bXehBFtxEIgqTPYM3CGltbaMIrjEKLWuc8Ip+JIBMcs2/1uyuKeoK1u5g/q9rHS8iIzO7sZy2lAtOh2RfrDGNnkdmv/80X2VzP+5bOfw/6TebaJBlCWdnMR/8M3bWFNbJKmS7jSEaUQVAEkBQPWh9L40vLrRFl6GIeyEf0MAg8pxS0QmlsuA7GjZFb7rmpxZbM81XRes9b28x+v7Ri67EempNyNz2ySlyAGYwUgrp38GdYzIEGSWJmQ5wx3Xr+X9TO4xylot1fXnfsT6XTvoXrpndaTJ3Ra3p/9P9OXv/bpZmL8JjlhONpJyrA2kBy7ZKSJpak1Pq0af/G7Y8il0Z7zmsiWF6Z05/GZmdepdbuZDdfPq5cpU/R2l/2b5Pf6ezk//4ey5GWTEFNRQAAAD/+nIE1YwAAAILW1dRgRT0QwI6Yz8JCAiRbXOihFmxHQyuNICN3gAAKDlttsZJgxUuJi8Hho4kpPH/0rMzd3/11Kj5NEBj6J+VNsG1tH3idESv//yykupO/35ZltVvuy/f6ff92/8G4b222zoDIX/L5xUZWS/DgPxFWUPiWH2piYJ+jlseeu9WkrUDpo6aK675EVeUPyj+kn0Ij/Ummr/1+aj1/rb8a8WVTcAgSW5Hb9rbrD3ONUhWLyWBuz3V1TZhqYm/OZ8xe5inebAV5LOOUGcud9H/+XIiXrLvyDLmA13J975XkRz9tWIPe/PDVkAIpxt3ba22FE8ahPCosJdTI/iVn4AIk8y3U6dDCGWKLcrqKpYY0LEZogLvS1ybXyMVYeSUTvIsFhdYDOnyK1jFjrsVFfZ1piCmgP/6cASC4wAIAgcY2FEhE8xBa1vdFCK/iHxJRsykzFEelS50IojeAAJYKWNtFIqKjMkwfClzHs9Ecm2OLnAXRFm/W8Q2MDp0AlhcwtLwuaHtus7UlCSVXj1ijlmHLIFy4UphLrV+/9RADJlsc3/23RSLRXZ3pyimEBZ3f6E0y6/fLMf/gjZnimbMjJReedRHm/KqAf4O5ge4vQPgh7ynad7g9pbYYZRtSlANVUo8n2TmszZI8BIuW4Hh1RhxEBROUoeEZASzxYKzR7wuB8dRwMsVNlU0Nx5L3kmaEU71PoUzQ/zP6PtRZb/12Bz0AAIpuyW/e264k12FXo5dgV5mc4700GJD4ojI9uja+PbZiyXumVKaq4Q7Ai0ACciJChBUnH2EyzUs+VoSxF/S/AVbK2B7wK+tMQU1FAD/+nIE8+oAAAIGGNPp5hMAQkILWiRiNYiFb3GhhLfxDghsaPGI9gAABAAFdttnBBjBCyCKVTBskuGeuBXz9e1KkwJlpAlTINu17Op7UtzAlk4cpJUKLjtIv4sT9vV9b9vRdSneX/+sASa2prZJLaNnudGCZotUY5uV+rroIWHV06j/WNNLFQkeAwjRJtFqypDYRItW4o4UrY0n0OVirlIcVLQLrCjrn9vqAYBOrjt21s1MdxXTbaTgoM3QwPshfy8BcVSPzH3gH/2KfKREyWtpC3DKZsjfJF9/c7mU58iZb//+Xlf022DobEsK7hGAAh5Usjbkr9RI+7wgTYaCwzIWz/Pd67FIEkrd6Dj5o/YyLHhzHWnm03pKtdc+SXhbheLYlGppisubKK7Fp3zhR4u8emIKaimZccm4AP/6cAReHgAAAhBbWNEhKsxAojudDCY5iM1tbaKEW3EWAyxoZKwGAAOdKWRttOIYWheNm8MpwBkHJuirI7IF/p3nABqvx5rtRdF/6rXfLb2rrSim0XIpqJf7dbr59H1+y9/6s7tS//EHIAJRd8m+1tlIYRwEIojP3m8zlBR+dICVB1W94mNC7zcKCgntAiDz3BFj6ylnFm3XMVUo06XJWsn2rSc7awFa/1AEoKpxya6WRRQVKU6HO7osSOcN7pHZtjVFHkfSe9Bv7BBiKpaI+oswGCEYLlbv665G6OcSZ2GOFBTM52xQyHl1OX/kf/gmAAl8tyxtyQhCI3puCD9ZAIwlMAMRBAVjHqQtxEuy3YDTKAQFQ8DoWDATeQpJNQ1Ts6GJ/M7lo636lUVoS+gZU0+YhyhBZMQU1FD/+nAEXZIACIILGVQZ4jNQQOMbCjxiM4jVa1tGGKTxHIfpDY0YmA5///mAmgJcWJLOKmNNDo5cE+wPWVg/jlRWZeRQb6I1jmtlitbhQhL2KQqe/NCzP0uUyyyVVY9bT66Bm+PeZb/DwACNBOyNN0dIM/u2lGgFMevEbbu7QdqstV3bfvieTJETUYBgTAb1ISX4WXj0MiiksQkfsWj2XLODLzlrULLOR4DDEJxtJIpFUchbhcYL2BKFo7ijmOj3snRFXbra0OgCtHaciEWRubJ3l7d2ddOt9bZXRKJptvnqi9E2957s/VKe6eu5tf10GgO222p+CgRQxsS6SlA7Ha43CRteeiW6a/sBPs1Fe2im/kYYCFtyx4jbidQYAr03IXld/Sa0Ndo6W5XO/2UJFbYo3xUAv+KoTEFN//pyBME9AACB7ltc6KEVfEFjGjNlImYI7WtjRARbcR6trCjBlP4gpE2zSzbWzI452dlLM68IAK8tNFNdhZqeZF8v4cvPeqF+ZnnSu5YZlwtEGnyF6rneU0n//0////qIH5WgWAckkkTuXsUmgbpnKdQI/DsmkwwvWAaCdXJ1CZLCN8E+W2qgJATBjNrn1oguo6zrY0ZZj/X/usU/7f//q5DqVzrEgAdepbI23h8HYeOJRkpI6hVE2O/pbh+Zq7Hd1K3NR7ulBarrHpc2M5HbA3y+DzQffPs+dl0XnJpnjtcyI5ryOfc658kf+ngiqTdkbck+MxhZDPQiSP3CxhrF1+fxAwiqRHE5P3T8jyt00+Y6NZKejMlem3L1p+X/dfTvtyr1s/fTSWvZrEUuisyhTJl0qoSmIKaigAD/+nAEMZkACAIKW1GTCTrEQ4IbXRgjdYixbUz1gQABFgkp2rAgAAaqpmI0sfsZ05skRWi1eHnXiwXxmDwn/37dnYQt04cyqidPmi1/2np+lK0b7W32////29P7en//3/0+vr/x4g1aAAEi425bbJK6xwW7AQ5PV2/SvUwCk63LRjbhtK4RstXbjGKU0ZSH3DCIfTilUVWUiCv6kkErWnU7s6VPoQAwi4NuNGiAPbbasbfFCxBAwwgE0R/IzeuUr4162/lh3Qu68rEZiPRv2oBUXkFrp27PvZf8/o//T3v9/r+/r6PNT/t/83/r8OViLWJAEX9Vt3bKgSAgFyL4KtmTQ3NznZmneW7cywbjpWi6srjFhViUr7UF4j4f1QycYsLl/y59ZTU6Q8/5RmTxP84RCJ8pav5xMQU0//pyBMOEAAACKCVa1hhABEJhK93CjACItA1vvDEAAOcMqWuwYAAABHr/uW7XabTY2M1RdiAtX7Bv3z6yHadalqp0Z37ZDdujNiMWdU0MqO24uGzu5EycIJkvPVXOMEKyl9+4/3ep36PPjOkAABuWO6uDf7ejXDUbZWVUO81okxYTKEjjkB8NvFZTrDTQ0F0GNisspCr/zuuZiIUAyfwyC9B9Lm/12CQKvHUaKuLgAmSX7aRoknIW0DiA+CIsFEj3rDcIF1Cpo5nVtQfS5EUble8XcXSAytnnzSqnPOSiKZlZtzyCKrXCrnnR4tF3GrDjWO4bAAAYTaQACIKwAJ4VAyiLIbEVYbl0W3HLcMTUdiUSlsmWXsa+6qhP76k1ZpXfTKKE0vgyBGu/bI1srTEFNRTMuOTcAAAAAAD/+nAE4yQAAAIcQ1tooRXUQMMrTRhGY4ikZWujCMexBobvNDEZzgACU65I2iSShUFXY9ytWQzh8HQaazbMrTKFDpqJ6+cnJ8TKFfzkITnyF//oXmvyES//1Eu4iJZHBWpbqJJ5JNaj2CqQCEmXK7tbGAxpxFjD1E8aBTH3GwpcZYGPvJeXLL3kUtkbma2QfYWegUpZB1Y0WUTSkGEQqBTo+mULi6nqPB765gAAouRy2xlFtoxLgRbmvu8oAkE8jqz92LFBjkpySmfvkI6hjXoPN5kiVOjjzw7t/hL2fiIkeDuPJf87lnw7NMcdWGiJUYggpJLrd/9tpSNTvNnnKGXTuDDxIMOPZuiRuuM8VipUBre9goUEaHqU4MoOPHEWrHMOR70NW1IurSFePKMoR5d1qUxBTUUzLjk3//pyBDlhAAACEVrdaEMXnEOBuvowYjWIcC1bRhhG8RCH7GiRlGYAlxTSXX7623shynO7drBzT97JyMUEynfVG6mb8Vr3p/S+isVWax2eUjmXmpEb2musMoFdt/trR/0+/o9v33yJ/8GAChxTjjaeFpDFi5kgox4MgJSo+vcKPKBgCXxzo/mXtIJRGqtOrCcQvYDgCG0mIeHExcC2nmJFgiRDcpIBIb2ebv78qACVRSjaSUJuEQVnIW1LcedbU6TFLCy7ysc4PCr6ajusVW98YCEWoGwiNLPpDeuPjGMeOXSZK3PUsqCSTUqhTdrv7Q2AAn6csjblnw6TqhDrvXX3xQz4QHqrCgcKmmxbAF16Sa5iKvQoX3uil8LoWlLEZaSVDjhKswpSCO/Yh7ECNbtw6JH9uhMQU1FAAAD/+nAE1h8AAAIVW1bRARX8Q+Macz0iNghha1VHjEfhBIDudBEMBgAS5KcbSSEEkAgAAoPAVB+RR89pwNVHPvif+7SRmpPVcbb3Mygx+58+q5/Oyb7Bsryf3mV3i2Z/8nvPzwGi5/0/wjhX//9lSf5LA4EMSRAFcIGyJIZQbRdppitgrKzOyO7pz90QTL3HpudS1gOoU6he99L6V32CouvFppHrvRICcq1kXIq+oAA6ip/9bW0yBikjVYpxGH4nESblQk2QsgZ3uy1+J1/f9fnRZ3TSz2W/51SyX9L3JSu3p/7fpv6r7V9W9v+zdPhnPqAKRblsu+1txoYTiYRoWHC7kFUh5YgNFHonFyzC8ikCjjQfj9yWNyoxbrHtels+z19LrZKjIlUJT2sQY0nXsYcVSmIKaimZccm4//pyBIZFAAgCEFrQEywSYEHCelNhIhoIjKdjpJRJsRcMbGiRlXYmoqaYEUHWOORD0AkeZEy1S/LrDiZqnXL4t/Mma6NL/7sKi9X/1bsRnR9N/fv36f0/3///////9///7v1///6BzOkQ7bXKGQwhb8ukCgVJy2UCZRcRom8FrGaWFetNAWYYwdPSHESr6Ci1qHF6mpMoADX3PfRLelmKMsZUuu2pK6koR6ynInbK20khRFXjJY8x3rTHrlMkIu5WtGLq2XkKLPlboZpqve331XnDgEwFWiIOA44Xe15RTiuKsqOva5+uePtZX3PX9YIJfqOyNuJEomZyCeywenctBPpOk5nOMUYoHGLi/wfnES7xBZhUTMWGkj0ESmf8iwe0SPM7WNc/Wsi8mbbpdWa42Nfxw1SYgpqKAAD/+nAEGIMACIIHEdCTOEDAQkMadzxlZQiwTVlHjQexBq1r6FCLbhv/8qhLHK7DNOT3BwAFYX4x17UxInGJdQoEoBkyCkYdNJJqlnFbxLPKkp3d777f0o27OxVPSmz/oVYzvFG8ob/SAEG62SQexugtCvbdummGyjEmYHy3hD5C6VftaOZ1/4TCRt2AkuLbQd1OuezTzJBDRiVKaRpeZ03ZvXr11beUXdT2A+m42khIcSnLCpB7LtAAODSc8bzpqVGWdCd4N4NGCV6FMrQeONFxMOuFmIWQPmWzahqbUMtmcVQeikGEP+wDK1UqDLlfYM5jkjTYwDAUEFoQH0DtJN222UNpFeeh0qpHtUV/6rh0o4TsXg4KERTntFpHDCA6fvl///nCR7YUVzhIHWBs/0gq0xBTUUzLjk3A//pyBJfYAAgB+BBZ6MJBzELBuuotAxuIXWtlpARX8Q0tbGgwizYAAgpWuXWtuOscOPTEoTj16Lqlwc6i3wGZ9uUWWflgKIUPNWHBYcdCoqPGkHCYZZu0Xbu/X6f+rSXvw412xBUAA6inW0k8ahmSxzAUlODPOXakHU+EDHiEgWKnbbiXNGgdBN4DFEahEnsUajHLRa110pcdsSqxLkULUfJ/705d2jc4hSuWyRtjZFBEgJQLge5mrx9/320GHJoGR2bwTTcyjiNVvIdFNZEcOfdb5yNn+SLYQDPiG9/701ylIty9vPWUw//8OCA9yksjblFigxUMIVwDLBZ27FwgZ/My1zbKQ7CmP+D0AjfnMM949+d8TH/m5d3e3wX+8WW+7kc6Zcjqttl9AeLbHi6YgpqKZlxybgAAAAD/+nAEFFQAAAH8WtxoYRX8QkMaijzCUwilbWmhBFtxGK1uNHCLbgCAU7a7ftbbUJzQGZ953sQkXpessvt5NQ/n+ij06AAKfXGXj15dFu4bqFSikyI9k/zrGYAZarVz5wS2V/+FfqAAAsTa2SU9XAVBkqt6nyGMjUOl1WCc0OleoN1Khw7ed9ruewAwFVIdAIp1Vd+zQ5rUezZ3Vmr/vcTzsUremliH6KAAAEo2rdbJJECwAoYo7saZOJPBG20Shy7SGaZV1YF7SPeKhBRkbPsiWjMcHWYG6w8/nZRepgzKeuw3mfWek+Ra2Vf8I9IJKTlvl32ttmK9CrGIG3WdKkqGNZB9RRU4f2sr9ee5F/hmOhkBEMjqd5KZjz24ymXutciZ3RI7U9n5eYUv/5akRSAj2v+CTEFNRQAA//pyBFOHAAgSExjY0MEa3EQCOx0ZIyGH5W1towRX8QatbHRQizYAAf1ZZG2WIkgdI9Aet1c+h2eQTZk00YljGSg53gNuJUi45oCNKUWD8YxU/sbL3oJFVn4regUQ1Tn0P9ZpNTyRy71AEAEtRy2RtxlB1TYy0ShimJ6hFX506hwzIHMxVxBiheRHMPNOvaOYbGvqUhaofSXRtux16KE1XIt7HtXY1b2uVqdrw2nbJdtZJW6mcsq91/G53qPK7bX3+rZ80NyFv5hGe1yQpn+VGOMhOV/8sr/8/tdRcp8t55ZfNeV88sir0TnGspAAIAkhcsGCLC7jREGVhXvjg8WZzlvv58ZzfjPvHrfPPPhv+Y6qUiwdbl5J4gJBmrPIsueZ+cz8o/a1INei0Du29aYgpqKZlxybgAAAAAD/+nAEQ+AACIIdINjpIhm8QytqrSwivwikST5MsMpBFojnyYyMoAAACY2pdI21RpMhgoNLipoJ3uwtWmTZpm04YxsRZmvXUEfZ5mZE5lRBuHtK2uRqePSsnZvH108o+ueKhUzTjlMTRZ9YAAAFLmv2FuMCHAlJjiQIgOnngT+aJmoimeRT/jfz2ySy5apB7l1xkwagUjb7Kn1/Pf////8//l//n/f6f2qiwb57raqqmEj2QnEbpwsWI6hTgwD0ckgR5WSBN5YWlwmmvoybnNLz5FHEhqAEBA6GfKJXelpCpNisrbqqp2f09cvTo/K6FVxYNg1VXAnRpCXzX4VUCK4FDLjYOnM/UB1FD/dImLEjhwJKPRARSHwGByLZ90D2wgEX2rmLH3ii6O0hze9pVP+Sb+/3VZ7lHoTA//pyBOXVAAACIVtabQhADD8jGuqkiAGJCTd5uBKAERoSbHcScAIABEqVu3WyOLECZwgCMz9902Mrpf3q2zoeW5UBv+RnKlln7bK1Gz3rMqoqn2dqVRF/oa7qdapT3W9Wfeqnq/////+cI4AAXJuNpJLnyKW2B0H4z7uXtpmyrVu5Ho7bujHPx+tiUT55BJsyRjKTw1WhwmXSooKnSin+e0w6b3VxSX/q+oAAAI0OW7UagYDAYDAWZjjOL5RtmwIKPuiHEAAQ+d8EHhwU/TiYfFxT/+fnT//I+xG///PcgcFBc7///+c52U4okMf/8vB/KOAAAABAKLjrdttttlAEPQhMRNv0eg4zeZSMnNd4kGLmLoNN0LvYMFxEEhUvXwfg/JiRuvWuhN2ntqRDFvbT793PoeT////qTEH/+nAEyYoAAAIUM11uJOAARAVLncNIAIi0c3NchQAxDYyqz7KwAgAAABgIBQOBwOBwOBwMY2OSOspLh43u/J0NglKqa4ac3m2d6/5wFBWYt/993//qdY7//8geAAb/6T5dZo7/6lMPooAAAAEARESxPA/oAAAAxDX6QlEosVdb8lY5wnE6tGS6ZipltmxuiYv1bEGAnAYTFkJU1tdBWQ4hyaRxf/9SnWcMQWEm4AJiilJEk4qHObEgcscoQfQQ1i6cicY9G0b9DMoBa1SI0shGbiE6HHBIwVWHTANSxM8dlXYc89+VcJXVPo/lcSrO69bpJYIJcklxvYDERbDJE+LZBcF2s4ilZTU2b/fpMrkZzWPbWtMjztxa2alrZPeYj7BiFXlpar/1yHX9yPv/7lJr9NKOG76kxBTQ//pwBARcAAACGTDe0ScTfEFEawc84laI4Pd3VIKAMRgW7SqWUAOcLn3Fdttq+RPz9H/qQVq5LifnNjj5xbUtiZ9OgJ9LRV7/qNy0ZG7ekobh3n1pnVHmQzYScqZFE6UMjixmxVtiPKP1AIBDs2wki8jJDILWVQ6dx4AFc0i9RL4Y4Bw3D8F14N9Bp4FwvbYHVYz5OI7Cva0owWWeWNwpOZBaKcKzTBGLCjXRBsO2tvsJB0yhMyI3TBv5rxLjeLahMdlWgQB3pxoAcIsOIKjsQH/VVt9dyEoZ3iekAB688rHYxPtVcg1rBGg4bDIaj2f//UgAwJIe//BQ9EihA4wQhB1cEPPlnUOcN3F2xNsr6fxpND0iv/36P/Q90ow0pQU+j9XU/fbsumeZ0Y3eGxa5CN9z6sP4p/ypgP/6cgSzvgAAAhgQWr4xIARDR8t6xhQAiLV3lngDgBELrvJPAHACAACAIY222AoAgU8+dpFJJGolm7730lAwuyB6arzXtj2e7RxYEwyE4tURQOf2/hABRpNYxRw9HO51qUmw+IP/uDBN6QAAEAGQAAL9/wMALHtSjCYDUjP7Dyp/fz6nDoTiJiMo545qvmar1xVOP8UX///+kiq0Xb2Gu1NLWRNO7GfFxB/+cJvAAoFdotFo////////7o3/9EPPNMfZvz3n6nkio2caGL/2PdrE3nuaaTHSg0Gw+SIhT/+hhhjKfV+okioah4kER8cMHxUcABQKLRaLR////////3dv/6Ieeaz/+e8/U84qNnGhh//Y92ybz3NNLmlBoPD5IiFP/6MhjT6v1EkTDUNCQRKjiD4qOTEFNRQA//pwBLuzAAACIBlgbxUABERIa83ilAAIPNt3Q5RvcRCbbGj1CeBoEtNuSttIkqRkbQrmEg/uG+BKqyJrbq9m5UJNRkCJDPfweutlIhqyU6xk9t63FaX4iPM/ESOJa5FklqNdrUIIypHErGCA3G59dokSroMHLEUsq6gTQC8U5cxylYUFqDHSvGP7lRWo6F5q6tUtIzMZ/L//7/zGo//XQWQewVr8lqNdtCCMqnErEAqrKVjIAs6NoTyoBz4SPiT4G4U34U2MHfqEOTRLqTTlWlD9mPnVKNxr/GP9mW7fwwruxL/EXzx4qAq+wOpnWf8RABICCPbEjLShUKmkNSlb5suG8yMOrgefE/kXTiQmLuvQnLVgpWMblb/8uahvv6OVWN0cKd2Jf4i+ePFS1fh3kfv4iQRTEFNRQP/6cgRXfgAIAiJbWTlnE2REBbs6MOUriLR1XnTDgBEBDKyqmHACAKEbvtrB1wjHa7E3ogxWgD0urYQ8bt+UGMQEf0EXl7VbDNqOu3Tl5ikQ584klBh/bb7ddruyXsn29VZe313629vf/hmABAIASSSDYvDR/CsOQ9BZqCXBDjuObE6Q8OzN+FCvHzQ8+Rsb76JyXb341ixqjjQItAoPj4WAeQaQcpCCcBsWVcfnukEO3axjTQFCe95fYmg8Dje52D0SEBqfiB8an5TobiY99+S5Zj3Kv1mhR8XuneOlhyrYdsWgqUZkGAMmeQoIqbsqp2Eb/DwACBBG3baTXAcF6+rS2cYL9+hJnUB/y/G/Z8Vh2e22gUCc+LIB9xdyH910H7bS5PNnbO8ItH+16Ew9/n7FLa/2piCmooAA//pwBCIVAAACE13ingFABEOLvKPAKACIsK17vGGAAQ+VrzeMUAAACgUCgUCj///////8kcww8g//khOhg8b///KBcCwcPC3//+LBIeLBgiAp0YLgCh////iEBsFgkAbEOYLhYYYAsA0AAYDgdjgcf///////tsen/8kJ0MFRv//1jAGwQhwqFt39v/iwSEYsEAXgK0g2AFP///8KQCoIgWACYX5ALhYYYAsAgalpxzZ+2sgDp2Wv1r+ZM1nHdYFyq/Byvf8V3JqX1QA+zKyk3/5NrTnFDHGL0Ho0arCS8Sih79Bp7Yq+rhXu67RERXPYVREDHXrlpbCSG1+tqqbc0lrOH5UAVS2rHstfFq2e2pUfSyos35HtdMqPRsg+VbK/JdAoe/QaedcKvSpGDNczQ/pI57CqExBTQP/6cgQOMwAAAgApYGhnGvw946taCScICSjBZ0Qco1kkGC1okoomZLdadprsRINPo/2YuC3iziNzHx3s+hI3fXlActpLLsa6qX7Xh7M0AuHf4axL+34SAz8jKnf+ocHTvW4NR506JUCx/bOzkRj6uHdSpONKhjUSGsNuS5HhR89+2vVf5Glt1mDSj0O/w1iX7m/CQGNTwdlTvdVwEOPO63Hazp0qACA4Qm3/soaB4uowYmFBfoDvcAeLdB7QrhQp/8G78P9fbWq3TK8iL9JuMs6i7oU3a/XYRehLvtPSlINQW2FGEM71K8p3cv//8gAC8giWySXtFx65U/4kKanQpyDbswLzczZB+ra8aD98MK5erGVbF2qiZLMbMLZnKINs9qEGWuCwYHgWcsKiNVTjVzHzLNV/D3YmIKaA//pwBJkeAAAB9x3Z1SzgBD/DO2qklAGJJZV8eCOAARkyr4sCcAIAABwFdttdZcXmjIKB95el7yJVQfGNNfMdY82Ji3/lQ5chuLS8MPEsNRpRXUtwlcSe92/XRGx3jNvnXV9/5d0AI/hh2yVnWOXahuH/qx/fx7WIK6D3wFXZsKAAUIIttwmJefs57KKSAhRVJ0rFUUpWir1bfturaEhYk8DT4r9YAAoFAolAsZD0udm1mlX//+//////j5xh7//40Q+Op///IIQJjccGg+v//38iPER8bigHg4QGoBf////7ikH48BYSRkqSU0Xg4ADDDDDgnECIzu///r9/7e/p//2HzjD3//xoh8qn//8ghAbjccGh6///+RHiI+NxQDwcLDUA/////9xSD8eCYSS5UkpoyDhMQU1FAP/6cgTKGwAAAfkZ3u8IQAA+Yyvd4YgACNxlc+KJEoEhna28sI8YqCGjUrkkQABC1GrHoZeK5uuZ+jdtO+iDXcRKGyZ1SHBwCIW+3+pKaDQrjewPCqc27w01bWzqgkgJrEC69AXMEIBvT9eyskjLqPamVG+AK5LsFzPJK+ozMlyGajiTHvZWascWcccTrT/ZfMjG0N1Fxh99Jr1jYuKysVri3bmUEAFGREl27WIlB6kDNRKWHCqoZqC8KYK4gqTDddH9eUAeUHzolw61Sxh6HORwVGhIOxKREWaCn9gmflSXw7rOyoLfuPVHg0YAACZkdrvWwgKHdUyp+OIfBNqYX/f+baJ7LOBZHop1oXuhc+p4Zh8T5TRC5L8mLhCE1jH9WHG/zIMn+Jn5VHyOs7WEv56o8GkxBTUUAAAA//pwBONDAAACFB3YUeY6FD7jKtoZ6haIzIdrpJyr0RwM73QzHRYAGAgBLbZe7nMArYEsMZmLoFipPi7itsvwuMak//UVdeVw3OWVOWiGyaxc5C2PtPP6Mx4hah6er++MSo1S7EwqZQBQAADACkkkAky0RQHxKNAF7dysN3qX4w4q3YZqRMCGKLM+UbtlRYitEtUVU7Rd1oXFE7DP/5kX+ie//xfWKIHgEBKFEBb/f29wPIa5YGd2w1qHEsjwePrxP5f/zQc8e3EDNPkcEFhZxVzT9BwDljowVT9nJtJvMEw890QPemIR8xUQUi1nrcIEms6Zu220HoYb4/GBX4kVFBDxtQ8IXHFAiuPvlC2nQY91twsiy3447PIeBQ4IV0FVmiD0zo9JNZE8WGpE8nIjDAJ6keulMQU1FP/6cgTVUgAAAh0V28jBM5xCoyrDPwwwiI0NdOOET1ETlu2cgRXiAAAcDtmeMEJvZILu9iO93t4qbsm3ld3Af+EOXdZhEHz96M/iC6IBoOAg6f4IOEAIeIBoOLB+TKOILBwMeoEJfwff/1gAuSAZVSkFkGoZ5zkeRrgkD5+weKISCT6tkc/+Xa3nL4wKEZ2+NJvfGDTrv9OicIrJQuf1QQdV+lihR9leo4XHLfqguAXZJJZ3LzSYfGljXlS8zcm5eTUnkuphHIs//zUuQlQ6FdGaz1ZPT7vvlp7kVjeVfSrDAcVc0TGtzGjr5gr3MFe/FWwSBLv3+lodUFQ7AkoePSJEnY2g6U97SC1VQr0bAgPImJvV/7uLrGPib95FoPaMA6BUnJrol2lIu/x6HljaErF3WdVtXqTEFNRQ//pwBDamAAACIjrcOGkQtD9kq0clZUSIlKltRLDkkQudrajAlUrAYIVmt0UhB+gcJzVEffgxGTQVn9I8E+nTV3+b/9OGM8nTo//9WbUqgLoZWsYSCv6gaDXOuuyp2LCXaBQVh1QKiICnZEAIAD13F5wpiw0CoYOBr03hItwZqgzudqTw/ChfUQ/AcGo2n/8mg/y7ybiGJd5XnZNZ263sbPFAeWTDO1I1rE4AQqCHfv8MM4jF+FxyohH65I/gicoy1CeYnH5CPDXrlOLPw6xj/8Zz3191Jng0ubSOdcR5y9DhbPG6UR+4XNLulKrvh6UBBQApv+KopVq7oSIi2GfoLp4ReNEtk41xRxh+XR8QBKcv/83N/eNfHafIpSb95G0HXD6Cq1MLPeaIllXPcwHfeeQmIKaigAAAAP/6cgTr1gAAAiE621GCK7RD51uKGEVoiIzrcUScrPEXjm30YpUogAgEAu7f2BHLKPSsajHRZ3VpsC24C+bOfldYcr1T4wInpeuM/6kWw//t3yNqyHbf/1oo559pC5HMipOGFyRNowZ1j/XAAogJd227Kcpb6EIuCfY2YmEeDG/3FZ+XR8Jifz6N/10XJ+tU+/v16fnZY4z+LE0VoA5ComaQpXvSEQ4DiA+7fBYyEMgcbBElkF4/R1FhcwZWEPxSMoM4W0GuNCDlHGTxEo+gF14MP+BLnbb7FwgPqLfQyUH52TarVTTfod40sl9Q3///pKD9dAACgCAUtjbKvlh8ByA9gBuMxgXiTYr9Ixw7U/Jo+FG+M3eLXqoCIoCZQwfRQqXFT5QPFJTHeGGMzhqnLhGlMIzSFrNK+pMA//pwBHaJAAACFEDeaMUTTEEoa50cIp4ImHFm5hRKkRwMrNyXnKKFBCFJolXXWGpe0OXA+w/yoqLYDtQ2c1HOUKZEeg+mpXrz//yZnxv9ETL0REa5K+nAtTb6dVDi6j5QkEAotvr6h36qAAIlUBF9dYgzcvC7EoakLXGcR2lCiT8opvj6zPan/9y4pQhksXQv/v7/hQQs/1SWhgoCbBoVDq1MwFWtm5v53JQRACm22ZpJJImik+IyURKuF4fTBXgl0LSWAw76CNNVb4r9cn4Oh4hNrOgtL0Fzo9q4q4N0s+TbL397DC3RUZQsOWCuUbBAAiWSS30AppcjKjiQSwaewf4LzB6Xx/M85irKHvkNOFxyWV9aZWakB5YS+Skagrm7P0FluyIiWGvx7qyzyNZISjD1QdUDSYgpoP/6cgR9JQAAAh9bWzknE3RA51tqHMKgiLTtYmecS9ETjKxc8x1SggAVf/9bubPfDZqAZ882a2NtH/5KVP5ur0Dm7W1/8VkHz/ukEV1SSWdaq5NdSKUQlDrIyl7l9Sft1SV9LyNr9rW/8O0AADIBz7+lUiOyhUEyBQIZfoGcQPjmTKN0tR9iB33/CF+v//319N2RnVDvarv2/vR8HKmhE1LTTISYKIAWpiP/rADm2sxNzwNrC8o6QiH3QTY62BHKC/P81wKxEJvmZV5QR3r8oV/9T4J837pQ+6FR2KZXQI1a6vSgIdFmc26TcNeKblf/8nAAAFW7CC4c5UphSDOZbjOfmduIAf0eqg+0if7DSJg8vUtrwuLqF2+un1y0tcTqc/ph6LlS5oVCIwWLNUokwAKnCY+3c5MQU1FA//pwBNXXAAACHCbY0ScT1EIjKxI9pVQIqLd3phxK8Q6VsHQjFYaAAAAArrhRhDwmEcA5UdOg5z25rbGKHoGsc8QGBuFjC9T/w0Z3jf/ZQC5xdRNZEJLOMEzCybV1S8hZhpt29Sf//XEg8CpV7q6vNpsDYLW8EmlCiy5mKTR90XEi92A8KHvimr4U1JS31Xbyg3nH/E59Z9b5+4T6svOXfBCID8ocEdpNlpzxBAmkg2yS7rbEJBlhTJ0pdwW0UrozqMth+HJwMoi4h6ppqw1OL1/8LhR6E9VM8GSwcNVN0Oy0QoPISoa82PNFpfZf5el31RAJORop2MkoEJUjQV0Bxe4sAWGPj9fW2/XV9WpwHNUqUZWm0etnYWo2g9Q4jSqjJcsLup0qEyXZY9VZFn1d7nyqNmFUxBTUUP/6cgRCzgAAAh8cV9GJOpREJsuKDWUSiFTrgaGgrTD3HK3okR4aCAAAAF22yUdmW3k7vJgrsPOfwQQ3Fuozih+C0u0UjmgZz9TDfUbU+Wkb9qy5Rwo888TUl6xDFGzvFzqESiOpZC+n/D0ggfAm6WSAnoEti4snA0ybArqHHhYbkfq5t+O152pwp0Qu3lKpcXZ3age5n//lL0cRDp53Z+Coh5Xb0Vlnyyg72+s6JYG2k7I03vt7R+C6uNRV548LqcH98x5a/gUwvp76tQEf7af9T5n09/UfsZfurLqhfl1HMNav7CI8qBBYkHjeMFiI71oCFUCrf/xT+cjREEI2Y+V7IhX9IsgyZlrRwtX4/4gJ0fHGcUPX+b3/3V+9Hz+m5+it5WgwKADVut9FcZVzKYgpqKZlxybgAAAA//pwBGl7AAACGCVfaGEoXEOnezc8o46IjOtk5ZxPUQGOLaiXlISAoAqRJvb7ag2pyJVXcGYgE1E9POwlOV40mQdGAclOKXFm/daYmPuVAVsiTi54Qj0hKpix5jIrqjhWkANOwdtFeW/pAIADN9x58LznzvIl3h/jN6/C1rxZsE+sKjI19S/hZPrp/6fr9qWNVHLs1eq/VkO7ETm42jt8CZkyMFRIePC7w6FB70wGAKu20RWw2HMKDwjCqiqt42oWJNUzVa2g9lXeVbRqgHG1Zo3/8TRh8T+7I29luWvfZ7KzRcqj3sFPfUk453dN2WMT6sAALANrSKTao4P2VbKaJXG4iTD+i660aym1N2wkX4hT6wfOCAYNBx3BA5ydQIQQcXWfUCGJ3nIgUcE4Ibvn+XPpiCmooAAAAP/6cAS8awAIAgVa3FDhFyxCxWwdFCOLiG1rb0SMSJEcC6xM1QjogBSYFSWAVi0WF8wvJS6ajCKKXz9NZ8Spg89S3yoSE5ugl3Hi3+XJ7/Ry1NspM2eiIl42Zn////////+mnaGdv0OkItNtJWMkpWOUfGqKWoV7lwx5Hl5o8Tn6w/mp8lfpXudME8yXUdYAdTpsBnmh7K28Avc6LHn3YUeqYW0Q3MY/fjWgWAXv//Bnit9p4L2lk8EJwXJtkllgk5/2f5tf/531p72IbKy2uqNT3JqoTR3s3602VF7U+yH/abe+1HkfP4NpFFUqWAEtqcnllYioGkMwQIP6iQ8x6BKbFbPq7MJeKeonQaB11W/qhJ4caHasRSoakioNBWZBd34iArvldZ3ulQWJQpEow8CtR4ktMQU1FAD/+nIEuOIAAAIRJNrQxRGEQQVbaixnNIh4ZWdGJKcRBpLtaKAfQoQAGALv/9KtwhJgMWDWjLhD4fi8+kFNcnTTQb/l/98G8pmBKIlovagYJVJMNKiJREu0ZFLAuMFKHgas+56Xn4CR9cASshF7/8KFuYr2dEy72Bhs7EJaoX2+w9o9R7RsodX9W/52ov09JVoVMOyi9Qo8uqYc8asgWY2lR5s3felLXCKLIAosQZNvrj+Cbnsj2gplZVW+kLx121Jhw/A4qWNZsV0egH6km9XJzutZpSHzLgEhylKL9LGgySQsECS1t4pqoxZrfDMAIkAgb/+lD1F6KEwFQnyOytRcq+LD09ZWvj38qW/2icNVpjDobQFQGkJw8BiCH1A4gzumSzUsvFRYo7XTOx6znqTEFNRTMuOTcAAAAP/6cAS8owAAAh4x3NCpFQxCxNsDQAjQiKCTYGeY6ZERjG2owxTylCW5EuayuPgMyocMnMPTUWwk1Xzkq2FZCjIF77vhj/bV/6ej5vWjtaVyWSgcj2jgIkmWBUSq+i3oLKkRscSoELhQ16gANbqFH4uyQQCMNMWJjBInyVTrJOogBtmzV6j5Knx0TrNl7vkob/hTyTAyKVJRFfmaXoxZFeKQ48JFTYrW+1H9qdygCpJJK7yK6x0NhRDpykWTCyiEoXxKuwQq82DUztAe22vK5gORnU8yfMf+3QfeXicBwfE4eD8/W+XPz/+8uP/9RDV/S76kBAWBUv/42OV7uBclVEundRjPwZD9WXDRDq56mbXj7BZEVBwGwI362rzLyZ4ibWHw455BhLpeK8vLtTFlpc5lhJ6VoyqYgpr/+nIEkWIAAAIcIdzRIRIkQuO7qiSreYfUwWrlnEqRF5gtnPGdNsAAADN22+jnb2kkA5SNtNqg2idavkqEaxk9FH5KmEhsrYe4CrNFCS4lfQ8o5jZaaTERQRJt8yxyYUfBtUFZUmQHtR64gEKBUdtsuudZ1ZCLfbqVVrsR0CRHibR6OWrt9V+HjO4lRiz5Ue23v9C63w2VCoscVICgU3qc/RLx5I1ECFOo6EK9YEABNvtescL2kGA+2fVGnFjYJcPyVfD1N21ehTaLHmXC/Q6WuVC63pUuqEww5owCoMB5akoJnm/3v6K+z/4bgMAY5JBmJ0nEzDSg1bIoBCwIPcOfCL/ooM06c31J0gZRBYp7uPF5rZVtmqn/9tTNx996z4qk6w+QDL25lgCI8m9qUfi7UJiCmopmXHJuAP/6cAQTFQAAAdIu2ZmHEtQ/5gtTCYUGiSzBavTzgBExly4qllACADu20zpCtHU7CQI9PuDOgrMqGOInPqXSI0cds+r4vx5FVNP7f7ciNa5wQ0RMY+bbrIJy2mQqf//v+oES/7iNg8jCo1hIJPnICrhgNhzh/60mzNQur6gXvGREz7a92qXT9mKVTKg1sECwGh0eBqZgOf7Z7W57AJFaCkfRQEO//2B8qyNldvCu1EpBlvdXamA/0FfbdWGsoe2ZrqhP5qmW//H2ad7qfW7vaiBtMsPNWBwqQYcQDChdI5Z5hwe22XMXcVv+yAAGCgrrdkDumOIIo8Cej0F8+bseH+CH9WSHd+n6C3atP/87+2R0IKKdyOKH1eOAa96k2nOJ++TsPmQQBc4QOCpA+XFRAZCDybiCKyCYgpr/+nIEpgMAAAIYYV2eJKAGQ6a7p8YIAIi8b3e8M4ABApLsj56AA0AAAIBAIBKclVPFE0mguxrtb7E5F3223r96REBF4r7//////IhJ0b//8jRQTF1P///9hMXZCHRh5P////7qLh8+To8ABwkiC3WiwUDznT6poEi+6sO0CPvZ5CK4AcMV7CmciIGfXp4yaH9/+n/2Q6vpMtNWuiIplmFVLi/4gMiJI+//rfPMQQQEgp79a20s8NAahwhP7cp6ZwrNNRnKRROsZjciu2IRojmEL5tTNh2MZNlDppn6Sp5Z2xGP3ND0yKqCz7MsiKJunU4zVoBV2AAxO+J8PFWnKLOTJwP2Kan8JWt7HAA1rB2/L/ii1gRfGmfiZZGhFUKuHBoqWbQr9KzMMq5HOjQbakurSrkGpiCmooAAAP/6cARaRQAAghM629DKEbRC5gt6LONviLh5YGwsq1EHGG1ctojqhAQIkxpEk2uAZNMJhBJLn46TYPhNVrFs0E2DbVqFG55jblYzVUu2nQ1XzG//0Nlzb8sMAv1hoFeee3lXfrcr1uliyACBAF3SSSviId4JYQteCuLi4ljTjmpZ7nsZi1qluWxwO6yv/yKluaYXt3ZS/p1mpjnAOEn1O8UTMCn2WdJnvqdf5EASSSS7hm0K/MKyN2Hkyp9duVtELDRtKr1B5+ENV5Jz2bijV6tgfqaEI4JuuUfkrNCdjSXwLj/3aFGuzLXdKMKHuKIZpas4Ae3/CrsXF8GBUFK5QkLkY0sPN8k9A1XB0qM2N21F9Ian/mRt9O/u/tsDllWtOT5UUdONQ95oYLWAFqUlSHkoxqExBTUUAAD/+nIEx/gAAAIkHFYdYYAAQWPLN6ecAIi8c2h484ARFghtTx6QAAAt+BqsUqIkxeSFVgUctHImFTt5ENPoylZvryAeUKRStKUr3E95NZFgW/FOZ01i0vnRhTddmJKdVFtv7It9HR/////hwAQBkkkmaYIATedIFQp3je3sFPRXxcUMYYGH1qXNfKlm2/OFDY7EWeOVhlX0xJIKODnfE4P/OS4f5D0l/h9FlThGAJBLNsBABAbm5C6mWbx/BI4BEoZpaiobhmJjQCJQKiURQyTNQgXd6DhZZOlRb/oQoRTf+2hD9kq9xYDlYoFhAFAyz/84bAOwv/++H4qWLBDF4lZdjyQ94SlWOLY0LvTc9gq4P7uQ2owtoGY9o6VYo9L0dlvTgFj6LGon1iKFNvWYigvCKv9jLEL9W9MQQP/6cATlbgAAAhNd4x4EoARDw5t6xgwAiKkPavzCgAELEq2rmJAGAAgtFotFo/Kyr/////////r/sc8+UWECFVf/5CSQIYziYkI/8jSYvIc5HD7CJhiEYM/+5zyEZT5CYw6DwUOWMcgkgAAAADCS1mw2GAAEIjaMcHceOJQ/EM8uyqXK1oU40Ysvp7HZimmZeYoz7w8xiZdqRIH5dtBZRo4o67vDQ97FRf6kwWRAEl2EonPsPdeC8ujtU/Zen5ZVUCy0fRqvfcv/QSB9HuWjzOpfvrM8q+hv/+3q3+hSlbR/QxhIex1dbvpbPMddHp3SWSLYAVeUbaGAkb0eRdbTksY2yJWvWDLspFnXBpqUIc6eUqXOC0/Sf9b/oG9/Z4r0Za/c++q+qn+nTyCJxiQEZi7v//s1piCmooD/+nIEUEEAAAIOHVrJDDksQ6S7Nz0FRIicZW1AsOExGAytKLGc6lAAGU/Vt8HjHGkllisGhhMfTL6F9RtnAYYdIFv01GTu9I4HP2NRDbA7UmhHnfZ+SIqVqESCJ34seqs1sLPIyoCUPgOAHttxiPYmaq8sQhuxbBhLk26maND2rsrGbQd/40K5pIzp9HsyjlDY0UPLaiydaZ6WiLeW7LwzcylLCZ9o9L0rLLIIAAuCVbJJfGMW1VjbDWAj1LTRI5e0aykR2SUIN+0qBAjRK+VAETPFhYi9LWB1QX2j03ipl5zet2lMXioKtIvFUM6b2eaAACAAy263tgFSzVIABxMcF7z1Xq+vFbakf/KAGuKrLmFV7iaReJAxcfB9vC61BhlRCmCBxwXFw+IxOPUvVXOZyiXPv6/k0xBTQP/6cATh1gAAAiAk21UkQARDw8vKoSgBiK13enhTgAERIK9rBlACQABsID//y/MmMKCAmEtI5G5/1lAPQAyGgol8OJbTXQY///zmWoMHiOPKT94nYEhCZeXbPI5q4YLqU08ftJque58uj+uAG7wnbG3Fw4jQe8U7SCdePDJxBWQvqNW376gqq509VUtYpKRQmo49syst8ZYBbobCB2klrKLe9iAy+NZF3VchoQ7ZrAAwHA4HA4tZBQNMUVDDvfbT6aaW7d+/t7/+r5//9BeZHDf//nnkypxYsjaf//UeIqPlBKYweI/+d//3FA0NCRyCDSaUFgAEszCtk/3A/HA4GJx8QE2MGisltMZEIjWOyPQWbH9zZ0bUYz4RdJBKjTiFXsn2Nlf//WNS5pcrf/9hF0gf/9TAmYepKYD/+nIEAN4AAIIXGV3vGKAIQWQrB+esAMiw22BnlE7BGQyrjPSpEoCADq67fEAC0dJFjkE/nQ/OAe4joTJuys1gd99KsNAx4qsck8ISLLrwKMNlQW/9l8qATv4kArHyr6+RqmdzKJLPZJABwlLgAMOoTsJ1lgm6nJoupYO/MqlqNa4AeHnGiOqcFTuFRpLd5DN5bwIoCV8EnJbUNfMP0h+Dosxu3hJuKCDeFwAfaNvFIInjjV2apTUhtSh/lUXoFYwHzpUcIMsKHevR6gfvIV0NTZL/M9SspdHK3l1R9DdHMBNxE78SuZz0RP+s7/5W0GgApbaJIj0KQStG+CKyzdJKbIRgw8fNhaa5mKBBFlmtqb31BJxZSU3KpqskHijCYcKhZwXtFgcNn3Q01ZkvfUhPcLtQshYMTEFNRf/6cARBzgAAAhElWtEjKZRBoysHJKd4iGS7b0OcbfEOk+yc9JRqhABZETf/1R+yboHyVsVA3gnAfRMceoYAYqaLPr07H+WRNf2bGxbS5seHyiA4oCIq3saHgKcLrjE3CFSdemLiu38PQhACl1wOPmAhLR0sKbhUUvYeJXhHgC8p4wMjiRBtTflAd0ade9HpRh1K3jSIWwiCbqzsSSi6I4q7PGkrHLPisyq1ACpyS7pI7wiLxCpayg+ygDw+4YfGcXDSVIgRJtV/00BaLu//1nHLi9rf9ptSNAoo4Os/zh5dzjL7EMVKUKqiZGyr1ggBC224xPOSI300Qv523udkfuJaBL6MIMeFBjxvtjRn//W6XZR2eMY5uu0qlCDEUYpSdwQtxAthSYcSKGmh402LBhDkxBTUUzLjk3D/+nIEboYAABIXHde56yrQQ2UrFyzlhIjRbXVBiKpRAhJsnLOO0gAgD///5niIEr4ZdUG0Ye1HDd41Htg7q5M/Ov1aFB05IPx+mgBgn1eIHykza6qo1n7L+sc4g0nEG596gx4z5RPU794BwA7tsNdgeAndLl3ExPk8nUw5WC9qZRxUMM4gBXqX+vyydOvIbf+L1CowuZU5DjLLzJ1YuJC+2tlCHi9JpSnqHoKjoCsrCV/3/sxQ/Bxo8CrjMiher5mrfoXt+oZ2vdP+pnlLKis6GZiC0pSuyb/TTb+nb9dKxZjLaVkOZWSqb6/oxu+/vcKfZkgbgGaXUCcF9Rc5tYQrbzhCbSfWq699caZk7MBa9TPzAccySl2LXbVpWVAYIupMipFcpaUZzCMhqL6upumiBvyiUxBTUUAAAP/6cATgSgAAwhsdW1EjEbQ8xKrDPUKyiFxxa0SYRtEhEOsM86LCQJAcFX//680lF6D2pHz+ONiNR8poSD7jPUv1HE6LJGJLrteS3DRhFZIhYlT7HDRCKRjljkUWVMyyA6HW941abO4OfeAHJJA2xHZaBPWOxXMP1I54/gp3e1TP5WD+LC1iog737YgU6jHltAX+fLAUGo223UZbF6Pp66V2GPf7fRTAEgIJv//uGaC4vxqAZzr7Rwa8fVvp8GzwfvjN8SI3KqeSMAgMCgBTJBsSVmkDxYICh4KY1E5cqpcpD88qVF/Y1V/tAEkkjqstwQ8rn5AMnlquXLH6eNvWEV/Vkm8KXyOiYpfG7hlqj3X2ADX8adHG7GcjHHkigkbl2HB7866jusFW7WsuZ2j/9Hb60xBTUUAAAAD/+nIEhDsAAAH9Mlk5JRvkRGM7OiEihIitbWVGhFtREJZr3POJaoAECb7awdgZNXIUM9j5ftgO4Lq/WMczvDr433yjv7nb/yTZIZGt5wj9O7JVWIDXaWj+TfQIhvWeRVO1fa4r8NwgoySHtvgQItGtQYNaTpM/WOS66QBd6+ak5AL/2XUf8o3s9wigAPKaduOUSLUl6n7SQlEJoOIAoghwVJgidCIzbK2S6oAEIBL221JjOHcoSeSBuoz0XUTyFUlrID1oTlDqNsxS1H8vEHv5/+MjazUPWsCbnvOZmQSIw9ovxev8Kk/2kXS+vZf/CPAQAy62yT7KN1RJH0lNMmCO5cjjV5QVZh0pCxfFA+DftgRut6lfa19VszL03drjMAQfWHK02PeLRRq70o9r7/rVdkmepMQU1FAAAP/6cARjZgAAAhtbXehHFgxEI+tqGEc9iIx1bUCkQZEOj+ycFhwaZABShJU29tqHdxq3j6YcQjCm3+kM2p9G4zUFBH1F1o5HryPm7PVHzgindTq97FK66WyzWv5dFp/y1r2Sn/6f+n/XBNAAkwS1skDJ6yvYQNWP25nwPMsEXusKBMXicaNrUwM86KDR8deROmnFxIQYLUsn4tK3uUL0cJD2saKIQmq6Z3kinnkh1ACfQlf//rxCLfrn7Y8qy8HXYLgynq0FD9h3o+vQfXaI6klFsDEe8+wIR5hVY5IraiTGqS57WNJmTh9BtjfQt5gnazw8AcM3bbW6HKKo1mCu4amdzNuIjzTaFxx2cQBMG4pFbb5gTvmx2ytHGobjyFJ5dhgB0GNpwetrVk2M/Q4X17StrkDa/rTEFND/+nAEteAAAIImQtg55yrkQSMrSjCnTIg4d21EmEURBIytaBSUGgCgmW7V5tU0iO8h7q6RCeTXKEbu9RdlBtKRgvhQo8qQ1agEJ9Zb371XX/9/nLqyvRlMytPdKXWyHy9XbFHlR0Bfd6KfrAAQsJXbb3c0yd0a7oOglV0XfgZowxrhVIUWL1IaPjcIJYutCXJch5incUAHn7I8+zFaZdav8H0KHHQZIIQIcPfbABzQK3//sa0gFvpozh+ZNc4PkwYnULw3MI16tR17UJCIF0IYcEcgISCAJiP7RaoyTaoB9yUyw++t6a0rL2joHT961hKf7+1p7OMitqhs16huObAXkdZmxgK+P/HAXLK/KgMOBkJCQ8DLyoFGm82vd0OyTiAVsVGEjJ0WFj6dY4UEX7ExBTUUzLjk3AAA//pyBB+GAAASClra0UEWZEKDm1okp0+I0RdzooRcsRgU7SiVFP+AAugQ9//mLIIIli9g4pBLdxtUT8qdoUrI2ypbV9eJ/3/9vti+Q76JxsExCmJL6k322qLJn86iX/EvI73Isr/u4RECQwlLZI+7k09VE45xadMaIZ+bdGGMEx2YFTB/wdA+7RrD3n1paLFWliaWqeoIyNsioJ3xynWPNvfqz9SJiL4jV6mEAFGyCrtbYQdCAPGiQLFYmkocXDrxo76u3EX766lBu8fbm6zCxER65olZM5DpB6Ti+RXkVLdkUiIx6fiDz0KYuqpKb4v6oAEWBLqh7CTbGB4NYomJxzHj1NypHKKJK8fw3V8o35v/0Y5G1S+IJU5fg6uL8fzqjsSl0lvH0yUbfrRpg36s+3v9Hq5/bNMQU0D/+nAEMJAAAIH7W15ooRdMRIM7Fz0nNoh0d3eiiRDxFQytXMKJFoAAAk4lPvrcRKD8SPPflfGcdzxo80lH066hF+8xCjEKX+JnN64jGYjVGbcAKhJWlBRWXyy//+9/l7l5/L7eHLAABS7bSTe1t3xsH8KZBervOUXxs22mjmvy+pftSEXCeSwmHFZG59ZeDJ8kiJ4eLCQDJFgWhSSSu0Ua174eZGlrhk2n1oAANOQufa2y8YGxhg1TKJNnSoB6isjQo5OcU85+nAVuOfncoItaVgEwxopC6xZAv+ti3jFNbkA0WL28bW9iD9rGjP6zgAWySY/7n4Jhwr0PXXOG4XQm6Y6PghWr9txP/EhMKCrAqYEl1LTggmkqBitShdh0sACBZF7ohUsPDiqyxsMhKxrmeG+xMQU1FAAA//pyBHhYAAACICVe0EcS/EMLW4oFJR2IkFNrRaTlEQmMbAz0lNqgCfyrv9trsUeCUe0Y9shyPO3O7HavxiJguddWkouT5ZNhiJIEhQvEtrBRjVpJGo1rR7KQVSONTZITFVMVXtjxDfOeGQQL7JItlltDlRwyf7vmbbYgrDr6D8rso87rChbX+Uf//+ZeTnS6d21PfSh/Xs1ul72NNVN1RXL6kRk0/9/Sv/14x+lACCQjf//JdRuF9YBSal6IZZ4OzuE3K0cryg80q+4t2hN77B6hd08HWPNlzzmkkHqRRguuH2ode+XPUN5R5FEDqehF3qALu20zGqwalZxilqFe2l+ZCLwhz6LV1HpCjdu2LHL2/1OPLxEDRNYtCUDIocoKupWLb31k1G3gySYkjdPi5cXcV9SYgpqKAAD/+nAENg4ACAIcE1rRJRKEQ6OLmhhFO8gQT2tGNEZREwyvdGEoPkABWBM3/+v6MhLjYVBjmtDdHPcvE7hKOR8whyaJzSn6a28JmQwpx8Dk0OGjYswVe8YKuWfD6VBAieFVSr5dzC2tlPmoQI7KW2tofNfJFojTZmmXFtUN2jYQfGG0ftiKfEFhfv/uc0NEpZ02Vz/fDdz4uzf6J6Yxr6/X0x+WBM51fFP+T/c6jSVAq//+bXBWq0TdFaUFhqRQ8gz4E9gOkNwYmp6nKfdF/kCFY4UMLiIwkcsU3iS4XGoMD6qLyDnofpkSZJVwtT9cLBMLlbe/21kbSgONGkO1nwupVtHKmBkWuc+hf9BRlF1ajKFAyG71OC0zp2IizhIPiNwiBKElhw80886sRk2QOVit932piCmooAAA//pyBFZKAAgCERlZ0YUSlESjK10sR1mITH1tQ4jLMRWMqp2ENVJAABgVd9vZioFrkjyIwtaS8GcMNm4njUg2wq6PpoI8oGRZy12PHaC8qKg+gFdMbSp6WpquV+0BgdhsPD0GC9rSfw2iQAQViS7ZJGGSxruFpDPgv64cmj4iMACAoBEcO5+hf84J9avLkC4oFHMcT4lcSFk6Lr1CzqkRMhxYnIU6W1vMC0moz6UbSdtkjRoKhiKAHE4yherZdDbCoNAMu8iv9j9t5QWhjtPhjzvy2lUMCoKhUnTGuc1TN0BO/RpMxzKLHuJn09Ebt8iAEAKSSK5yfadIomzkost6UvttFYsDgI3TXlJs4yRfDCavJ5f5t+TR81JZwBexj9iYo02qoxFPqEKFWsRu4acml7U5nb7ExBTUUAD/+nAElccAAAIgWtg9PEAERGMrGqeUAIfxF4U4E4ARERxwdwJwAABhG2220y/O8UCsD4Q4bbWwUd/e30XFaCXyqHI74czQq7agXvCnAis6UXe9f/Zf9KN+3/////X2Sn92/+uv79wwylXtAAWkAzbbDF7OO6F0Ju70qffGKK2PcjVAVc9XK+ov/4wO63sVWXGAkWaoHqVEXoYKZF0Q55gLCt6LyrWtJo7loJW0WDWAAAEEEGGGGGGVcArYf+YK68Jxuip883/55i2/+7mVT//QbuMC8///2OIDUXnmoZ///lCB8AAef//KCg1I8CAsAAAEBQGAgGBQKBQKBRkcnKn17hPbgAC4uKiXzTWT/sSNm//KR41V//og3lib//+xcgNSYOiD/xAfGAef//KCg0yPHAsmIKaigAAA//pyBOI4AAwB9w7dFyXgAEOBvArhlAEIkENgLJtHCR8r7mj1FOwCl/YSFYXARGhEqhLJCNGo18768us1E7SkLI0BGqxEK0EZ0goKjDyj2JlxFxFW7vwNIz3JLUm0S5UzXo//y1QABgFutpylA4GEMhkVlDHUqzVbGAU9GPlREeKkiM6QGBUBHhE/ExWIuDWm6+oGnkZ4NViVak2lcq6t1dMS0LtivLNAwuLDGMgC9mzssRXTFVKJ3QApOzDthN0exKHxiSmO7dW5f5dMCC041yAoghcEentyZF1kd801dEnFt2fb33Q35003///2ACgYi9rtz4ggOpRwFe4VOdTGfd721DIADhs9E1WykdF9MZR2KiHfTvGtX/8rajOZPfUrdMq6e9rsPvW3pla3on///QWf2e1CYgpqKAD/+nAE/oUADAICDtmbAsIwRGPLE2DTNggA12JszElBFKvvqNUIvgCU5aEqAEoaEyiBoLcF838yGDotHBafqxIyKTim4yFRh9QNrXmWtXkSOhJA7lkuSlQNZakiJDdaiw3xjxo1H/pAFqtFLmHUwtKApc2VyXascYerJZxTJNvD8gC3kyXmsZJ3Wpdd/15nn0WWYCZ9J0DNwke8ru2Bpy+jZKv+oz//rZrtZ1AmWS7MSE91lwxfemC3eiNMZscQ3M1Tq6ycAVkYmhNPSTMyZfq7zc31p0Pp/7br231/0G1t5TTb+9vo27N3qJdQgfwJzNtyJCmm9all2VWnma+MwVN0YttLpunMZwBpGPRRijIvo/7fp8W3evrLZymbR//oP9vqiv32p9r6sqn/EuVW+GOhMQU1FMy45NwA//pyBNvvAAAB8UDe0OEVXEFIC1c9ojyJFH1mbDRLISAb7eT0CL4FL0k1G23AqDwrLmDyLLnHTMtp1EUNHq3GUKDiP4f0+38n39P5/lv9TkK9Y1a9O98FihtVr1Ur6L/rduuDtIEAnHE5BORCIc/1dlQY/oKqR0L4QIWhM109Cvddy1qDBBdmdQTFqftNb1+ySaFH8j/79un/4Z/2v3t9OT1B7SLdai0ityspOF/ZPJ3qgzkaxv1113S1qakEkFutb7BizCXam3UbsrS6CTzwobqqflH3BJXlnw6nsfLsJNObrQHamWGsyKPoTVVaHwAlUV6azNB4Mm/I4Ji13HPMP7JSWCACLz5itoW3/JIK2daEdVN1oWnkb7vo013/KlHegVzx2tMFipVLdTI3xd+hmHaecDepjUxBTUX/+nAEe60AAAIaId1R6hpsQMsMDRwix4h9X3FHnEXxFpAsTYeMuABUyLdbSclG14+7U890Mt1mq+gVgQDLpbnzp8b79MjKqUiGx2QG15U5Y/BqkfDfWAILR29mfT/ccVdQa13oAVbt4bsCRSiRCjsklyhxS6KcfarbPr48M/ryL6fx6zg/5lMMua/Nenl96wzZBMGmj+ZN+blptXUvkb5WVd+WZ5IFnWfcHAAJQCUbSUYxUtUfbepq2vayZnYVAxP9RTK9/nak91wRmnaSa117Nvwb9iH0/vsR75E+/Sqv1l/b//6r/9CiOALiM8RUFPnP+yMjhAVTsrZCJUMSLeNj4esH+txpYiTBBTRP3KF0Sn/59oSo5nC6+SBnNDzy9XXp/KtvJewKExs83YM/UjqRu9KOpMQU1FAA//pyBPPSAAgCEBnZUwlo1EOD23o8yguHzFdcbJpnARCgcPSAiuYACAAk43JB0JRRuUTeAoDsseacGG+2/dbY0hCkzVX1H1bqX3SLIkIv2NLgFAs2DPXtZs29r82twm7tbbv306l0VZEAGEAVEknCjEqlOFnE498ErM16UoMQLEb6Vb+3NqZVjjkKkJqKllM2dOzkFBkDRhVa+t2jzd6ppTD66KKBG8fU0IG+kMS24YdrFkWFM5mF8O3bIdjMVDjke+uTizIT2A2wjD1UmOkdNajZxeg7gSdRWWdZ8N1G+dgVNI8O+un7///SE0lLapL9btxq9VVnN3mv2cHdcIsDQ/Ct/2Ws+v+ZiwuLzAI6OFhfyH+f357Uoot6I+Vf6EVUCshV9Mr/OuL2YNd2myhMQU1FMy45NwAAAAD/+nAEQLMAAAINEN1RYFOMQwl7hzxnDYhs53lDKG7xEqAtqLaKDgEeATckclClddtJHOerWq5p3GYap2LpWWuSliSUDtYptwu2hTqg7Q9gzHC4VQUH8sSUcadlXEph1Cu11FDuy61nSHAFVxSQjNaXHEV2r3zb+IgO79X7f6Z+m4sSYY5Vnur90+9tzD255vr/MbtRF9EVaGKO5lTDeyKifyg4deD5yvo/f1iDeEpNbJaiOucePnl067vf6hPBtRGqMfHXZbCL+as0RRc3pXt2dtnLhdh/xPyjzh//w1TuOeN66lAkmn/VxTkjVf/oBASAoxtNwdoTVukkC2aW4q9aHTU7D2D0irUotKCG+nk13cyPOzPZSK9vp9zfKmh9NWn2rZv8rIZgcV1z2U+lG3t8+rzyYgpqKAAA//pyBMvpAAACB0Ff6KEV3EIIG8ok5XWIAQVkR8BLMSEgLajDig4EJJwAuXXXYBB1aphZLzPUc/8BQ1V4wfuPJcoCMxGA4YiszdMPn39sayAvJx86GFm3n+dNrAlYROH4qW4ffiyF0infgmtbLaIGo7nrZVdOhdKkN38Thr1I+gI3/l0wWpRGiNupy7L+T0fxttdWokj5moOv7oejRN0GTOM7l9rdX+D30C91gZzgu7Uis7VfdrnnzVtdrhYkO5Z590aQ6ULXzPbsPqlVzNZNH/plWZDI/NkTVyPqJ3Vv26sJ7PpXI1p3kzAEtQAGOOQAWU1enWSsqmO7/9J1lepnA0Ai3Be3/o+rR3K62Vm0NNdd+8HZUa07ao9KmVRi0Qem3Uro/BsDjjqYfe8iyY1MxQcmIKaimZccm4D/+nAEdgQAAAIZP9q55jhsQmarFz4CWIf0ZWNGLElREaAtHJUNPgcBONuQDvWS6wCsZb7k72mMzjoPQZfo2UNPnlqVsYHuvtpVr1qqq9NP5R/+1pqXY52ZWlW+j1YyPiFo1CUio3dXlwCEQFSS3AB7cKT2llQL7NqunmqD0mZBMcwCyfXulKjf37b9Es5T+kpGROn918j/2cyQjBcOihBk9cKz9nnhZrF2ldywAACBUlmwAKbHp95WUJTatQf+Lns/8PCgey8+srP0EPt2THtQkUAQrkXL3WkzkHnD3fXR1uNkjNJzhvta5k11lQBVccgACPKN7c1XejPufr4tAXsdu5r1La+9qXo3qobBM2lsrNkT/mfnmdb7BtO3/14HU7S8p/5bcOB5l2uS2NZHkaLExBTUUzLjk3AA//pwBB5zAAgCDxjaUak4LEMICwc+AmaIjNVUbCBUkPUM7GjBNRoAAIQnHHIBUZA1pdgTjayvo7TyCZgUAuz818qQ0L8+aVOFjZ8eYEDVuAwafdo5K2hCz6RKoWdKF0A0di2lj9SEZ9YEAVJLsA1ixIjTva7RzL9XrTdjdqtahFRKRo1dE5e3FZmz4wz5pV9p1t1/q3R+vVnQrh+ve/uZVmUW8OXvz1G3UnTeCk3IAuuVpPTzvocXf20CtAlWKWfyo9ZZc1bzniUTPJZ/dsB28ip8bNUxep+8VU2fa/WTy9H9rP1Vtes90ECS1Xa1ZfsAICAJ227A/GAnOx4lgPshbYMARVJxNQpEj1dTdFW3TVUWu2CARChceESMDJlwEu2VZ60OWiP2yt+vlffxZMQU1FMy45NwAAAAAP/6cgRiVgAAAgM3Wj0lQAxDKAsapDQBiNTxcVhlABkZGa4rDFACA4kpHJA3hzGkDZkdqPZ/+xz+wtAcfqTlkYifTpdTS7p3qjGMvzFZN7XX6fb/VqmEbHSGNDOxg1J4UXxVPT9MtaABAARbbjAsIZQayKcLbw8twbuqggOwEy6b2c9qWgr3ZdaHrUkiuzr9tnv1f2buj/tWqp2XQu62bXTSRU888Uuj1f6QAKgALXJ9v/xwAACAIog5hOoQQu4Z060WgTySLIswyHds9e/M6ELZw6mVNLUNJHQ02apE76kJxqOxiU1U5mvY5rp/+hZR6oAEoIF8s3///4AACkJUMiug5fXWHu79jgc0BR0WZWXHGxzc6ElEGpHM+iI8RHHDpVF0OZCknno1YorqiNOqIPfaKnMuQOVJiCmg//pwBILJAAACGlfhHgTgAkQq/CPAnABIlOFwXPKAARUb7p+eIAAYDj8fv8fj54b/+s/XT///3f/5yOyGf6uQM1FI+MjpP/MmGMfnQ4Rip5xI/Wb2bPPcxswTmJZjTzL///mN+RifhHEoRQOPx+/x+D///7zM55Vv//+7//OR2Qz/VxoZqKR8ZHSf+ZMMY/OhwjFTziR+s3s2ee5jZgnMSzGnmX///Mb8jE/COJQgPX8UgYMc+EmaKnYnydebTrLS3mKxkdBIHBBI0xjK1HSVi9Stly1LQxv0TSlDGTREN1MdvWno6aipS11Yx6vwC7p//5UAQTjblujvFjXS+oX8KAw02rXuLZmLMY11FEeYz26SqWUylLtQpVLQxpf/No/0N6Hb9PR01Ezy8SiKz5UNSW+xT9d9CedTAP/6cgQovgAAwiQlWMsjijBB4dryaM9GCHjZYswoR6ECiesJpkEgACAABa8a8Qzkx7eOyr1oTewPTOVWsCDbIaSimE6Mi6aqdNqj7K09Va097X1af385Wsku5yXOmkEEBJT1zSSyA1dR6f7Rv39dRUgjx9oDopAridxr8uCmTwtgZB+UunqYYxIUyAIfGtlxrFxWRYIb5EudZtySFhzurAMjbw6VMBzTsv0V//1gIABq2GhK4utGdgxypWIiOj4nOdOztk4BAKdTdSrU3Qy80qO381HNy5v+hWfWbYpabWcpnjJJNnWbzMqVY9tbF8l+xAt/mFApzw6nwsFVojzbX2p5PvP497V979+ycKQJpPm7ZS0lnVDgZFVJqqNLDvLLRF1Ke5SxWjSvIVr6YMf///+tMQU1FMy45NwA//pwBNdKAAACBjVbUeMoTEMoG1pBAi+I6QFfTRxHgRqgsTRiiX4AGpEnGiU0mAVLCCEw2lGvi3LaYSN8KMWrmay7WUdNqRaTiP0bppVX+q+6LZf8QO3ifzlX+gqx3MpQ2vR3oEz+TECpAtxElMX4B8zoqGu0slGJwI3v/AOBH37HLqKo012qQkgyqyzva3+T1J/+M/ff66Q/uv/oN+k5oG6NKz/cveoPryIABABzW3bCAGUiW0fqBa0tdwLnDVi15cP0eaNCAYrWGbUV1b063Qt6K/anT8b+71dC0r+qKK5mV169/DjFO+17/aj9HYVEtQkktucu32l3cMEah5Rjoa59WlGdAkAxE2EU07M2+wJd/0K3/VfUT+yetmkf+r6jvdH/N4Ua+9lo9pY8aXjRJi+hF8s1j149MP/6cgTCaAAIgiIrVxssKPBDwzr3ZY0JCHxnXuyY5yEMlyvNligkBdse2IACPl9IGfFZJUjN1Bt18nrZ1K/MYOAPeqOy1Ho/3k7u1qSW9fkV3p/Gcf8s+dT32EhahtLrGUIljJ3Yv2aV1YYAIU9EnKQAEdCl8MXUI9MYHYUOpB56uwTMEArWeQ7qpfaxw1sjmCR4oa0teW4nxvz2LPe91bp7KeuK1YuYO5zx3OhzkyFTRpykiBGq3ap7RYnA4CczOvfKvdtUwBQ2n5pVlqL2Rf0cPdw8LnSh4qi+Tofq3Eg35iFVPfMMTOau7Yi+ghr6e/fQEpEZcVEBNERrIEC4M0XPwK+YVyeqOIUEF9WX3ZTWbbO/e//6zloqt+r+TuV6nWE/PPGvfksMihKZ1uZrQ55vtTtirkxBTUUA//pwBG31AAgCGEBYuwgR+EPm+601Ai+IdQFUbTxFwRMZb7SAidYBlV0ttxCgfLTSDOxL7ZDG9J8L6z0wTBT/HKLxSj0L8sKATO8d9Lel/T/7H6HJb/oQlkIzon9JKBHKm8MGK7/6/7ELABSjQbcsicoshQdmyUU7Zb12M8z/GBeb+QpaXq3UsNpkqSrk+jM606f2FdSJen+Xxl1alXJ+P0AZ7xRgScuKN9drH9IBVjtAJFDa1nSjDbFQEkHpBYx5yu969Xf8C+9PRTC82n17qbHLt+YTo1//WTs7muWnurZwx1b2+dfat/u8zQXTz1v5IJlSRkyz223MFURZzHxMrrzD41xP2BK9JaotORtNCCThQ5ykd1v29Cfu/iV+cZ5wDJNjz156pxla9cDKaMWnPsq/Ztd6kxBTQP/6cgRNDQAIAh8uWlHoEfxDiBuaJOVZiChnXsw0abEYqG1o84l/AAqJNVotgWcSqve7bmDa0mQfyd0tR+GACEP8iGVM0OWpVVm0TXIxItWZ1tPfX3/T4l2nsmE6TjzRQyynUNXc+tV1rjAb/Zu2yOQAuEYwa1RCqexJy2Yu5/KhF6hOkwIjsZJrkCQncTRvyOntZK+v7G6FrrV/Uflly29qMfMPqLUxpbL7YtKyJCvlgl2sVncZ943YuSrZru08u12WmLIVledT5u2I7kkYOYMHzoUZyhIuDI4ojIxYPmhj33EZYa/WjZp+RQh77W9AAKQNyxJwBTnDB38YW5P7omz5byxb3EkZypp1H1LUEwe7FldXUiMOrLe90MVn0/T5v6dYgvNorf6rM/zei9arl2kd1OCPyTEFNRQA//pwBJTzAAgCGS5WmwsS9EJIK2o84j+ICGdlR4zm8RmgLGj0CTYNyRyAQXGhqOyuTLZpnktdo7sXO93dfogfDWHcV38oO1Hsr21I/W2kqMeyFLRbOe8ifdPOTVQQJjHs7riXa+J2vu/2gg7Ao7Y5AUKDzC3uWMfZiZ7Ir0P8oHfFXUxWZn+778y3qy6XKkLoyIN+T4WyHoXpFv2W1euVeEHU9fh2WJuZVJUyVABuNJwCSDOXLm6is7iF5HXxk36AtL+yPqQPVjUdWVB8EWNhkY88/foi/bt7LU2XXpCzT4fVX3gogAl7MBHxTJAABSk42WwF4S5whRm+UpG3V/PEmQgzRbB8H7aoktkoYRJn1e53JLnEpK5vVKVo3/urcRfX/bsmNT1Q8zsEeYYcyL4A9aeRTEFNRQAAAP/6cgSnQQAMAg9A1hsoKbRCiBsXYUJLiNitTGy8R4EFGqppl5UoBTcdoEcTcKVmzmKKMjvBdTWhL3NqWOiQBAdWpGP2198YfN+t/Wtdf7LiA70JabX/zb/o7UoJvnDKIaqPlXVNFGIWDYNxtNgdJPXLV+lmn5mMSa6aDjst3YVQ2/bUZTq5oXiXVX3szufs6G7v/9jdxbro9eWVdRstvJ0SGHXT1MjEXtjdQIu2wBx7h6IgKQHKICpQUNYOIdO0nq8gU663ncP5izJ8Vb+P5DhmO6DGVlRvMbbddDFz9le5UzJduGCcso64WnBEFdv2iveAEAAM+24BoxoGLDNpAy0F5By6xp+Ht9CphXLn7gf0XAOwUjVdyTNryfmC0oSysIoRC3P25V6f5erlTv/EGW++0smIKaigAAAA//pwBIC7AAECDDlZUeoSXEKoGtJg5UuH4E9lR6UCsP8XLOjDldYAmkinZHIBg0PRiu/ZVXazGLzSLToJAKl72Sg2zN9A+9EbNFErd+ZjM8G3TUnQRSVrtvl6XjFO5gjfSfst2sZqZoG+qAs124aob8FQCr568mYsbi4Py/PAsAah1GPXFsidUQLDGPftSmuroqUK7nenqP8i9ftQP0oZmHI7ryJfEDI+n/WADeKbsHY0phgbA9QeM9WTWzkuC1+QeBCL9kz7g10BqOQHS4wHwMJWuWKPUcejcQco+33B5AieEiUykqNDvrWADUKbsjkAKCVel21o+Rd1FNZ1efWN6geCavqEXyOtV1rRvqY9JyVs3vZ6m/K/ILrXbg+7YW2PQUaaQy+ccr8kmIKaimZccm4AAAAAAAAAAP/6cgRJbwAAggM1WNGKEexDhvqTYec8iLDfY1SVADEKFuoesRACABrIp2RwA2aKRYVrRVAOXK2OWqmubxIDVydqg7XPonapyujciGBsV0N0vS2lF8Ivuuvf0GPQyojuD7BIBMNZ06EnHKAmETFhl2ZUgRgoOKUGNvo77yw1jH3u3hkQFku5cZhS1FZzDnafQhext533XTtXo9Pv7L+3satJIUW6ZeXsXvfWAE1RTlsgBtlsIEwgAZg33/b9pVmucccF0BeY75rvYn7/khNTnIdb+eqqYx5itLvXV1PzGRKGUu7rH5LnFws0DjkqXOK9hAVJJAFMl/tJjTiN6qYI+1+Tz/ZF3l3kCs60UFE4E8Gba6zMnj3XqR7pSug7u6R48ykV19fXWyq3//MESvWIonvd3JiCmopmXHJu//pwBLzMAAACHSPZVjEABD/Hm0rEiACIHDdzXGOAEQqb7reGIAIABVACqybXZisBgAWggJI+HbD1i1Ym0tTru9Fx/HBdQHDxJ3slar5AQpVQz1093lp86XQ5gKZsQbTIOQsH/0dZtn//SAAgqBfHP9s7oBAAOqRKBwiDXDMRNDP7yqNqapHU+3LLs1Rfp1E3kGuikKqkUzTFRqv/a/Of3/297K4tjv/7gOqLe6iRAJWyBm7szHzu3ab16DwIhRZZypEaEfU9qmtH9kV4hx9t22Xn9ISLsNJcX6DzzXx/dSYh4VFAicMpIKCrgSC26i40gSVHzgYhCeql8HO5D8QT6PoxUdrffmR66L3VK9FyH/Rvdf28wM1otwWbSrNJl+hlHx/dSYmlsFThmQUFXJiCmopmXHJuAAAAAP/6cgR7+QAAAhY5WFGBKjBB64tdGOJ0CKUDd6OEWrEaIGuoxQkoAADIUoAAJeehaV6DWNJ6RLHHx1T4WCp6toj5Wf/0+hq6/1KyG/v1Fei69WEi0UmJA0erDTlu25b0NV+V2mcOxFsUOBAK0Ae7qJTpwiMwWYNj60Z/d/wMBzfmbVnlK3/0emYyrZf9t//qJ6f8zcmZDP6lZtW+v/f9lzf/y+tnphniJthYcQADFTldoylLKeyra8w3szI/Kj4rG5+vQ11T3W4aPY690bw9Z6Dt+oUu+9X9RPlqX9DOhqAxJGJbxFd2f+xYSrIqHm4NLAAgAbbQMZCFbT68HTYjw6jszabcwGoOmHdOpzVL1waPNf77drq1l3f2qVsj96/q3l/6O96KMRiW8s93/9p5Z15cioebg0VTEFNA//pwBNkOAAACHR/b6Eko3EGoGpM9olgIpW9/o4RXMRUgbnQjlg4AAtyIu21pOEx3Vsm7UxmPiYCLu9ZA8P9slDm5fwTKikTxltFsaJOFdQbDHWFagOVyC3ySXfWxa+BGuFko9Kj1QIUKWGY2pIhIuUJGPhuAwwgx55dRT/KJfrN6qYCyFqZtAniv18//s/2/6vranoetF/0631/dX0DiGUuqEz693cn+/UzkESRLLJb/Y7MjI7IjvFsXvnH6dwjD16itz9/05nRMQ/Gc/X+f8z+PX/YfRqunuWzs0Wsx6b0TSuu/+1aL/qi68dSgf1kh4YBkUat2jScUpAsErDw93z8nQUBn4BlcVGJQxKLRAmARSsRXmxNrruv+op+/wYr7vb/kHPrT+2Jt9SD9aVfTOaqq2wzxCmIKaP/6cgQ84AAIAiAn1tHoOaBEaAq6MOJMB4Stb6GcqzEUCayokKlSACgAW6xW6O8HsYR9QjcRYPIW5EdiWtJMBgTHryr8ubVO3M23+9abftkXbHkCil+fl195YfOsmfy1WlZ6u+W4Eng7TU8AEApbY05UMIkpwvLQBAEjLkVnzCG26g6AqOJQqEJfcnG252mt9GbT/2VS6+nkO7W/1bTuv9LYlz/u2WVU6NDLrXqevQ0xHJIylKQII9lGygktqmniYHd+OB7oDNX27ej/L9fmYdRfxJ2NEzeSrcn0y2xlOg96kM9aU0RMgo9UaaEKiFy2NS2BiN8bGA2ipmR1SK/CeJ/uJD8tJZMgHjApcHXnfQXPaz9KvrqPJ97SLm39RxRozeMCL2mBLeGTZR6pEqyMUOTEFNRTMuOTcAAA//pwBEoLAABB+xvaaMcyfEQl2xcZQkyI3XtxoZRR8RMXasz1CdQAAAlGF2NApvZRAO+mDefJ+bp0Cw4JATKjFH0JN++2eMn+ijmnFH2DiqsWzbgW/RH8V2a/vYlbsn70prv22FlgkctjUtoL9YJJUrn/O3bqEkdXuN21EtX1Sw7HdUaVtXXp9Lpb9X8ZGM85Sv2jkmS2u4eMFRpcI9d089KZgalx0wtCwSIAJcjTd0ibu1EH6PWob9VLfxoQDnlCujt++QVyK5ea0io/+mzVtILtqBK0X/Tfyf+tUEVmlb7u/Vbf9X9fyNq93CPNKPYhBU0QTqTQzE0c3EeEwgcvvWnyzLed2C4DZWx2XW+/1FbXmuerWfs3WiW/Q3Qex/jokW5fWsimvWrWjZCtcJulcaxAw7bKpiCmgP/6cATrdQAIAiBBVWnoEshEBdqzPMc3CGkFY0SUSXEToC10I4j2AAAAgBMrQTki+RtoCfRS+yCJvUZECs+xV2n5AGAFhuFQv9vzv0VvT9//L/VtAZVp3+bqudf3fdVE20aQm0W/TcztXh1a1oqVtL++jq4jSZK1CQjFt3kdm8oFgnXjI8+ynsqTLVEghn071dv/2nGf38v78PRdbPjzn4BU5ykZ4XcMk6UyShz3m62TlRKTtoPoRNJVGJqLA75Tu514kDkNR2tWu/yhB50VB3MlWKiIZ///p8z/RuqqXVmUVbqS0mwzutpjqrtF/VvXprrDABTcTl0abkMohnCjvW61FKbzjCwlAkr4qWp+pfgig3QuWVHKxkmR/+i/kM2oJOrI3qjTSZU/kdKAuZvYz+pE6v9JJHemIID/+nIEkKEAAIITOFdRJRK8Qoaq5zDiPYidCWNEnKvxCCAqDPCK7AAAgCcaJQtCCoXbTKjZJTdm7qzq/CYJO/oM1W70j5VpRq6NiqslkYrs6+Z+pGW2jdpE1DjC11p1Rd27oS9yCSqnc0BQJxklLCbWV41jyPwTy8sby2OdAgBNV3V+Pqf6GH3oS9kN7TvzWZifmXyr+W9Yl4WHgwFexSnbt8hvYiUJTxD/p9oAc4JyRpsQP2jffcHORYLeyIc53FZH5W1yJ2d3yoSef2qvTVXz3SiL40j6CZtHdW9Ky7QXTs7uqoIlj6g9JJQhTqKLL6TJqxIG8ym+dmHyhzeroqMpFgW7I477fnfsEmQymPiO4z135oLcuasKHyO9YnP3kf0DlIcmr+Lykvr+oYay7rib66UxBTUUzLjk3P/6cAT3DAAAAhRR2VDDOV5CiBsKICLFiIUFTOe0sgEHICvok4l+BC/ZyWNOBkChYRMYT2DFJdoXmsPr4QOdUMPVWrxwf3VDKo6vtdr9qVX3m/M69etU5Bk/1atGf2T/RGZNt9aOxbR6MQBpU3YymJEymjgkA4khlx/wQJ6sZcag+CN3+BTTlj41v9Yz+3nLTkk3NkcDP3txyKs/+VuejW9b+CbT0ZkpZec1mgGAzVX8RzTEETYNXsYhQb8Wkq3n4oy4xP9WgosI9XCTLUdq3fYY/td6J6v81v+jf/RPjeh2O9/dyewvYPZxxJLk8ajNAABAVK2mxxP3zGgcczJ/OXuzkTONSfx/qT7dXQIQ1U6hId9LvV99rf+K5BK6l07Ic1FRUp5kUtcEde92yfRtXSzSmIKaimZccm7/+nIE7XkAAIIMONY5JxLMPsb6kzziXojRAWFGKK7xEKZr9IOKXwcBONJMRDAHHmatgGu7DDaW07gRD18OMlRPE90gTLeolWmddtpbdrk/cvq3aZbJZqUwoY0FixkEslp3Ht6iWLLc4JyOSBfNB5ZaN9xLgSV62cMM6EzrvfEQFhjOtuhN9+fiOEhqa79X6q1PLQv6l6Jf/zDHrEgIIj60gaQ1aO/6wh+iTukcgxtpgIFmMtY+8718y9z+4qBsRrwqrqObTukaJtziKIyRZUIVn/tVvykNqDlsVnv+TQOYZ+zszMooeznDqNLcaHjEAW62nBIhxNAaB4oRjtW3KT6e9AfDk92ziz1O01hnd1VUzPoi+1bb/qO2jkda/4En5Vf7I7Yppf/s1VdrOC271Gm+zpiCmopmXHJuAP/6cATQAgAAgho62FDIK+xDhcsaLQWXiIERW0QUdzkLGqsoxAnWADmJy6RwBjrGE2ZNXamr9e1mT+EwJd+LByGy+amZL6Q6mGgS110kmUVq/30afsX9CNqCqXb/Qt6aqLPLpLDjxVC64wAB81LrHINKN9D2kvb9e9QffplfisC3epv73Dx+et18SBVYoyAZSaO27JVWsYun7P3FWj+1R2cIo55IsGz1/CyUM95EAIVinY3AMORBsCR5BAi45duabofpwcRt50D00tV9PBOkxkPRX6H7Wd1RvxjfbXynlAFNiCLUsXyWhyXICGl7QI/uXCtQCdbTYgMcPYA0ZVE+r2vQZh/T2T4D0LOvvZpXSjtVndD8rmnaEqjNO370f+P4qj+9tgsWCwdIVE2MDQBTJe26mtTExBTUUAD/+nIEH9AAAAIjQNS9LKAMQgW6+qScAYh8h2dYgQAREorudwwiQgwAuNpgG4OhaOKENISLIPz91mpzFecUb3K56DiVdqNjgB0Y46tkb0evnsw/vnV8Tc1UvP8DKq2WdWI1HYsxFQcpUXZDggKttyyOQaalcSIKNyi63Z7Wjm9wdDY0xGJq5p5x58oe1c2ONzFd1q/03pVr9tT/KiaVnpcnEbwpuUmLi0xoKX/i4AiIsHTC/Ww+ABAIq4+VD/sNs/T/UJssdEK1Nyrc2nv/pc7h38EYMGHJqK0hwOyiHvBA8OaIRVT4Ab//k3OOf/8DxoHAAAARDAMdbmz2WxAYCCbTE44VwAufmffl0pXCPO1zbS3FnEb4YB8c3RQ6gqhqmBeUQfMgKtYhWUwB//Q8PuOBH//m7jaYgpqKAP/6cATjQQAIAiYFXk8YYAA/YVuR5IwACIjhakwMTcEUG/CkcYi+AAMIblv8YUCBBDLIAMLBh4gGjjgDQJz+IBp+H0Fz7ShARtPjKxqSh/UdlXLpy7lNSnWcYKufdpHsS1mswy9I2a2KrV4w59hFIEAwKGFxQ5duofegAIE4gGH4PvqDBO97hOuxTV2UNeCfEOGiJ+/DxYQ3vpEihYkmh1hsWj1dozv/2s6GAKNV0kAPgViqtq7lqYmZTEXJjMtEkJ9gR0qGARJM1UulxjL6VUslVu/GY5bzJmSrObpr83/6tzCrRcZXitVjJv/b/8RAAtdX01xMGCVk5qolVUiyjMZSKpM5Sl+aXLUralf0MamX6G2Q36+gp9n0+3QUtYlZaLgJsiWGCoiV74NLLPloVs4CPD3JiCmooAD/+nIENxIADgIQJVcLJhowQwga0mTCSggM11ZNGKbBD6Av9ICWtgMRDOmQNBJhpkQApwtaksiGgkn6axykdsjyTRsMQWlBLb4U90LS7ZoeUIOf+S5xwTxKWEaVsbMYYhZjFnGoR4vKEwWqgRhnncD0Sg6uFgUoXCm5ORBETUnGiEEfeVVo5W4aWqKatZjoxmmtR+V3+x3SZP96619NGT5tSssl/7MUKxH9/+saABnIibAUMWoy1gsiAVWWCh/KsQ7cUQMtKQxjOhjOpbqpZSlSvylaUrIY3+rcu6t7u6ZumYfrX6EO1IpVR8rywSLcckktkclA2ySa1tm5V/6+p+hUf/xSyFXh0KkS3MPy/rOHtEuYGeP/828p+u1giadYxosGnb2bXoHbZEqxT1tcxYTTEFNRTMuOTcAAAP/6cASTZgAIAhI11rNJKMhBSBrTaSIKCMTfTA2wR4EbICtplJwYARXyLF4T4Boq3ISh8aaTuuNtdJ7o1FGg98YrN2aqvqlivsb56Wq3+f+yrVnLMyf4iSge/QvPpPV67xf03M+/TmKaipUXLS8J8hAMllSFUVDOTpg9dFym4QnWMy60o++rUv1rGulvv9v/yp+/06hN0Zfr4Mf3TzonufS1yVN701yocskjCJI1hLYikC/rSVDmkFVIkOydKzNtWzd9xGPIbttX0peioWVEzpoyzt0ZKdWaur2Et8JSuKV3t9Y5eFHqqpyP/+1Wr++7aIABBUjZlogqNA9GZSZgEECbyb1MLs53cRg7sYlPd/MnOyUfvc1c+19Gq+b/T7OlW/o3IN/1vx5739Jt+SbjxDs6dYlSX70JiCD/+nIE1HYAAIIfH1jTBhAoQyIaommIFAigO4WjBKOxC5dqJbMJKBADjUdkEmIdKjBkjhAMofUh1la8uiIxXktoxjshv+ZVS3C2swKSLcRDcqDzeVdenS+xgy0/VkgbtJ0Pb1Utk+b3gQWSF1fiPUJM1HIZUNkIWiU8KKDbbVtKn2AeFY6uSw8qeLwDh19oSeLP0DXDn0F5AYQN88uQGvullQ0L0De39l+/TX3J6BE3ZtPdvrLcBLkqULghcmFkxcYExBAqFXKGkWjOx7XgBkuKPFA2EgKNxbMQl4m73RwjKjw8RlNZtzWKU6WDmq5Pvj00lQDAkOyxJC1Ola+MIocmbuXO2citVHhqZcNjgEminMMg70s7U57/Z6Ov/9Zdv7+KoZ4brW7iUqBWCZXcMPav93/P2R6YgpqKAP/6cATJxQAAAhtA2tIjEFxDYhv9JCIbh9xBTk2ZCMEIGSqJphRkAa+pJxIpMJYT8hm4CFBEJjEMzN1yoOvYqaNQ/S7l3mInOzOVkJ+a+36O2gtV//OzT7W9p6NDkgqWy6Gx+7V+Y98jTUIk7JXrrrHLgOeV1TZSEH04S/QECNw1ITChcbY95dw0uZISgCYwcxWIqzH4cpX3VXk6tI2fCJl7mNGR6hR9da2MHnO0WkvILgWCmlvBDiaLC4LajEk9odrVWySv3LBZE8kJPAyLC7Y89CL0FRRd75FFuhESMVRq2l/YhZ36GW/939NImi2SdF/Fv3edPnw2Ehc5hz20KrirZQ+GI2VyHqulvr9noyHdzb1t7TE/v479R0WJ1RItO9BZ354ld1KTYp36kI9qYgpqKZlxybgAAAD/+nIEM9YAAMISK1nR6BH8QkaqQW3iLgi9AW20sQAxFpvqCrJwBAAmubcaKTA7DocK5mvg2mWLrueIR/DgK98yEjJUZ3cr+ptXKartZ8yytQpc169Q2VSmTr58nrYcaXDOymZUq33f+objE7g1gqHhJJKIJHCnE5OGMdaYirLuzlGr3sOsKGJyLySt7IHKaVUmvsqPrY31L+m6ei/ob1/V/iD4SPCqfbd//rABTiarksabgJ4QMt0XCsPdbnzdzH7GChLFhhm3KY+erpe4V9FO8jb3NWQystn/+eWVf2lVcHUn611g7Z6+Z5FlS//TX8PC8tBwkh/BfRxqd/3odaW7l3ZrDvdcU5FmQyBV3eimNTm26GGtRP7HJWhhuqm/+7t5zXqk6tY+daRLDH6oXKIQWxb1effvTEFNRf/6cASIdgAAAg0N3W4tIAREBKt9x4wAiIhNd7ySgDERBy/3hiAGAAASUSpMlu221G22wACzCtwNOdft7Z3XHd9cESVYiuZSYrMGTBYuSSIjQB4nJx2HAzY3xwGJZgq1zwqKzD/P+p9wAQKTSSKtlt21ttlGAA1MrvYvpZNV9qaxr31TzkFzX2LK9zT8+krPDg78rbshHCJUhC4k49ooawE4hp41Ib0Kz//soGhNJVy2W1opQZP7s9y2szlc33DWjw6BBFlYqCq0Fk2JowjFQG70tn9Z6Xcv1Z0R9jmFTzZ/SBe3q3C2acppULpU1TjIMgtuW5+7atN0KbZloesU/V0ObwYEEY0N0LPMTpIiq6bQqMaWOgQ/rfFf1VHRXscERCxs/pAqEW9SMWzTizSoPkVNU5wMpiCmooD/+nIEnXgAAAIRE11pARDcQycK3WElCwixA11HnEzxEZrq5YQI/ggG4q5LdGnKAxledAIyEDqwTucv7JQUpgorWeERwoImQ62VaMfqdLdOV3li2WDr7V272BIO0HVPFwnuK7Q1hqGtlAAABCOcegCdBkla5oBcYUi391rbnR3buKmFwsz0kpV2+n78iIY21HXqpVZH//N/f69BaIk6Q69bf2OPdT/O+GsNTvoAAplJxopAAXETPh61vla0bjXvTUoszyoXF/nEHXehbslqqykK5V5lqesxZ1eyHJ+cr6s9kbT0dWq1W/qRSwphXe/2bvSoAFEleRQyqQMfWEy6tGJ8fJ5YjRWlXC/4YCF6+yXT6o39256IZUg7LZUZpjqzGWn26qnZv0D5K7FTpVYStK30Ydq1KpUmIKaigP/6cASNAAAIAggu0ptJElBDBYngceI8CIUBVMecTPEUIOxoxIg+BlkuwBtkgEu80FjzcGbsmX7mGEMV5Vya43n8iEJL3lKYmpqI/kU2zsxK1prSvRF1b8qdBLtdcJeLOCb+mzL+7+gwrIzMxJMDCckAKTY6GV2pfH+QMLJTg9kJW1wsCAwk+cTFaEiwVSBevtZghg9SzDoxGQ6IXS/6a/6Lq/8dqDX1V6Prq3y0AlBLPHdD4PVEKvcGkat77eoUGV0cEIyEUyCnxikO13U67lOm190lJahf0J5v/fRrKzR7dz32QSMJWMJMbusqHpWAdOzksakAtfQtvVXYiXQclz+W3otHlF+5nNkA6MyIdHv0Ds50zXnzTWWSpL+j9DMlf+EfVKP+012Q54ZZiKrFHNDF78umIKaigAD/+nIEjMEAAAINK9lpKBJsREcLHSTiS4hUn0h1hQAhGiAstoxwBgAAU2a5LI5ACT9zNiy/t0nMLpFfAkBYj3oy/AzIzrRy5ds1VVFb+ibKqN+41Rafv1wuLwle0yInirACmWmUJ9LPaAAQizHJZE4ASR63JKeN4WndC5sXvwoAQamZi5SK7ItEJEcy6Jq8qrXadVPR6umgvoyGsT/+NIp5NpZ7bOtiptusSLtSCpEZAQEMgHEtZMjgQtm82MC8s8vcx7OeecThKD5Ds1UaUGjI83071OVEdDm7fd1ZGYvwnULi6/Dup/KmJ9H+73EuwAAJGJSzWSQCAo5PAL3rG3X/fVHRRrEYOfVBsclyKljXVUrJc6rmFjZ42Rr09t5NG+f6XSj5s3dzKHEc66fMpkbL1dv5ZmhMQU1FAP/6cAS4XgAAAiBlWZYg4ABCrKtjwpQACLlBgbgigAkXKC+3AlABDCDCDDAKbcrV5kiMP/3fxoOLtmDcn/uTPf/57mN//mUM//2qTGgkf//3HCAOxLcxT////JozK9rmf////yGee5AxphACAQCgMCgUCIBIucjilT+r+Qi7ZDn/53//O5P/+SQn/+SofAACf//3EBQAw+5FP///4ujMc7tMT////+KZzuKEaICgAADAwEAgGAwGAwGAwH6UfE3pIO7h8RBmWhzjK16P0dl/85lZyf/soWoBDU//zCR2DwcUVJun/+IOtxkqOv///jonoclIYAAAAQBAgFAoFAoFAoDgDYJO1fRvOosyuh4U6ovT0dl/85lZyf/soWoBDU9v+YSOweDiipN0/6+IOtxkqOv///jonoclIYD/+nAEez0AAAIbHNrXLQAARAOLauQgAAhdBWtFtE6RDK3u6DOJJwASABrSMddUsbENFrLyshW1DPw4bgWD28JQwzMD5rX/jTtVFf1uFJMql3X9nF63WTuZlbNSS0jYnSVv//rLNiJ6qn1ABCADk0pc12Si3Vs2A8Rfg3+vgG4VagNEjX3+nyo/Vb9j6gkn3yPU/W2SqlawUfOq+eU/fvOiIOpVt8Kb2g3UHbEkc8ICQgKEjkvc2v7hQl2RSN6ifL7Vr7BkbEPa2DG0EcF7f19UxbdurKjt3XP0+r6CnW2SuZS0DCk5W389+9OWmg1I3MqkiOAKJMp2NSGja9RPqUP5LiHgxuI4ZuPqL4A2rcnC8z9+vW1H6e/f/oav/qWgYf/b/S1eb2pt3zX0Md3YK7j5sphEiYgpqKAA//pyBIEcAAACDDXeUGIpHEKIGycxqlCI0Nde58TpQR2vrahwnsf+NxUS1HJRiWJVi7QsdqG9uR8KAUlBJtfbn/oTgLy+P5f99V+RFEv5GUe26tpMKuSDbDri62lBzotx6TjP9FQR0rBACAm5JViFhehdgqj7Q6jfoX4s54NobqoxGtUEHuM20fUvz+jc3pyN+3v3/orbn0P/6Nalvn3xlMU609S/s/X1J5ABAABk26YZ8v6y5ehgqlli9KudLmoc7cSxqx1BXl6g/5XqfwcdW0FnHvbU7//p702TT3KlqY/rTOMf+4XR0vr+/MwgH6Yu8AIQQSAilLFZI9oR2c0P78lwMDSxWL9QXaTyWoSc18o3ft1f//t6EnZOhn08nel/Y++FGT3r1c4/z7f3/ovTR7o+p61Ylq8kmIL/+nAEjsEAAAH3IVeRL2mEQuSrWgUnGIjpjXWjHEsxHrFtqIOJYgFr8aJKafc6A7O3jFbUNvHbqCIICkhrCfLxsfW+dVxz9BsiE/otmZ6Sqq6nZ1HQWdONi++Xiv6zjOXltnT60oJEAAVI7aObYTugEvaKD3s/EnQIjMQncSf9tB33xAW/ndi/IIFDvZIECDdCq3pti5l60ve5rkzvZn04jbUlLovKP6UYRFECACmm6zs5gj6L80VzwsA/lOUGElRbxX+I5OjaD9ev+v+2rfdL9PlXtQdGpqTSt+rJ6ej++33M3/MinfRkruhOj0UMqIAxAAJct2WtlW8I2koQF9X424+2ISHH/xtDcFwXv16N//0X3Vbat1a+UGrDzlfzP0frV/1QrqY13r0Ozt7m3vO9LeUsrdlWOmII//pyBAS4AAACCTlYuG9RhELEa4oY4kqI2QN/oZxLMRYkLmhjiX5EgBSbkgMCDUoUJIynZbUTmqQcVgSKSo/4Nv8oJuhRsSxzQpxhzW5nTr/ZPd3Q1P9Up5eXjiR8Qv27WVXN1P2f9ckbqIFxzblMVWtrjcmAcZisbcSeQ5UMNUA/24vn531Ec3Qan6tH68421awkOCrDyZ5CDGgzpVyqVnUE3MQZEEYsYxDbY0rSJUlluomKWSNvGFmJwWc/j4xqnAv6LwPg+4Hzto2i8nX+ZW1EJvyfV1W1SWVOyyLILQGkOlxr4ul3Kvpa9ZDqedbZXgnahBKaktmN+2MDbbBEHZoY424qbhzZvX21FXI8G/L/Qb+383o+z/8zcsL23yyKcSyGvLtmKLRUkeSbGrbcKc7YKKUK9SYgpoD/+nAESxUAAAIaQVrRZxN0RCXLChmqPIiJBWlFnE9REJ6uKDSU2hApQAClm23XC07wpqoIMaOwt5HiEtmB3Hf6F+Q68qGu3tobj/58jNoDOj+7enpnSp6QaUVQQqfWGrECNAid8jneheRACEAgFtyQDwqET1WBpPKWZkbMBt49eJOUVRoCxMoVdrPlT9AUtvxeO6Hc7qalX5zfo3QuOI0+4UVP72Le93uT9St//WCGQKATt2GtLE/cBSXNTW9An8e4VaaIAfusRn06Jxn+DAO3VtG5f99yeK5rMranpfcObBhqOcQj7DlbJEk2rFJ+p5TvTwncJBU2/wVTEd4MeojZn8svwNwiL4UvFunV9Afk6v369X7dPfMZtRJtkonWKPMo2YquV9x4beEQIfFU7b8/0ouUe/QlMQU0//pyBMu2AAACHExaUScTNkLqK0oco7qIbOVjRZxNkQ+crBzTiloIMQEAXbuLndEVfAbaTmdd8lxPyvhofCNp0PxPXqJ7/0f/9de0ivErn6OVUejhSVU+WtcF7ytR6kXZUWFe96syId9SAAJECCpLuEqzsdAHONcU9C/I8aDKyj8MdeNDdQ9zN31N/IG/zt+vy6tx3JE881m3fKiHYdF/lV/+f7ylr67XrUTeTWAEIgBMkuEnFVDLh4ApbEitbqQ4EDsfbKpxP7ag3QRuV9+J6N34ZtW/Y3nvJoX0paWglImir5ZjJjQiOcg6LgVcYdauGJCDktBk5WikVJQpC6bUG1lLU+oIIbNCwkIpQAdPxODcDR/UIW0bt/BD9uXr5N7LPSpeaTuqEGWGyybVj2MCDW8z/2JiCmooAAD/+nAEIbsAAAIAJVg4zTmkQmgbGjDldojdBWlDqFhxFh2s6MOJugUAMOS0MfJIBs9Aljd1hjT5uwO2ypbUhwp7YnG1xJB+Rx0u2Q53Rv5Xocp9A8VJF3quYfJReImlmO3ku4pqvWiAJUCAHJcLuQ/0FaAGe6BCUT0Llo9wfCxImGGqET5+NDeBP6N/To/X+rdm7tTqb0+iK0yPkMm8o+tfqPpSchY0a7UIIkEAk2pAhRHF2oEzmgqX53I8qMIxoxwj/lQxoCC/fGAUrZjbltX6Pgm/Vvpm6v3drzU6irqErOgMZzBVKxVqpAupalPbcqEOQAU5buORbarddBXlOLyhNhjjvC5bEx/I8x9SWJgP6viYGaNyrYdtH4T/foV4J9T/dleyG4N4M2Xv90gdMJJreATyBiExBTUU//pyBIiDAAACEDlZ0UkshkBnKvcZ6jaIuO9c56hNERwgrOizidMBIQyApbuJVHGmomGllBkU9tB9xHHYOiFeAD9uK838YD8J9W1Hcn+2v7Yv79v5h3zb2ZVi5+ve55m/dVR9BPmB41kBAALktHxljwN4B2X3lJuPxI94gtQuT6iUG70O4y5A3EN28l5f+Ly389sz20mTfk3z60eXaZjXi5d73ucrWnR9IIQQFJcOzN0iH/HH05R7mCR+s/hV4IxPVjBEPjdsONxT4X+huE6eXv/Q/um9lJr6teiLZso+GHsbrouHhwmwCKMsA4QmiaIRCEFvX8TSmlVyB+KkQ3+kX8RdCj6B779eCG1G6cGuoLq+rd/6MkIdke5UB+vXfoqrh31ADlkoLL+cVj3HQjfxk4tC/rD+JiCmooD/+nAEQesAAAIdONlRZxNmP6cbKizlRIhk92dFnFoxCR2sqIOVugAhBIZdu4zsIj9xiLs1Q71Jcc4pDaxMHcj18hmCB8VvovP0bpy+/Tk6t1bdu5E+Cey7x2s15tMe7/Ux7bEclWWOq47gAImEIO3cdqMWU1wcNZQNDfJcBrFBn1BnxTl4UO4o2E21f+nt26/zepcq6G+j2rDjwI9R3Min0LWsXdefU/JmwAytRhKcg19pCfRIbcMDvyQl/ZzgbSPOYG7OENmjflAgaQPHGOxRHLdQT685tBv6P3//blbVDrpmfvRBcY6Vekh83WgEJBCTu3ENCO1QBJntQZ6+IvHS+qaFf4mbKDXtyof/PbGf6dX0fobRsYXRtkOpkUxjB8NtabJayRgxjoKhpgulK3piCmopmXHJuAAA//pyBLUXAAACFSVUHWGgAEEnaxelnADI3ZWMeAUAESAyrw8EcAAAhSQDjmU67E9ZfG7KvpHqAC+g9+H+zEO3UT1yoQANaKzYF43TTJfsfxYBvn1m48+gkyY/j8gyjz9s4d5hv/l6f09cACAnbuIe9p6WHAIVtAPg1v/zxGMoKC+pbPEr+U8v/HQ11/lPb+p/fm7p0T9vRXyL9m/3f1zFj/7R69DnNJ6rkKwABAMBgMBv////////2PPn//8wsBONgIzD///x4BIFIAUCwTjEL9P///AoBeEg+JQvyU5iQVP////wvCcjH5UmFgmVy5CF+AAGBgMBgNbwUEeYeSJ299dP//////zz6v//mMC8kBcw///8aAsCQAAWHlRLT///wGCQYcREsijGDn////4kEzyanDQ5XLmiWmD/+nAEuZMAAAIhLl3/FEAAREcLjeKUAAhc517EtKaA8AfvtGCJhiIDNThmNyVkpyRZKyZgofcolzNhuojm/qTm66OrTIrWZWt25lb6p40rRlmqeIyXY1h1Fm11Z1qOi4saWlyayANFmlq1UkhCGSxsgEpWVC7iaOYKbtzaB4FMikSgmXP5NF76GZpjLtVv+ZW+NTqObL2+LLoLxGSbsaw6ie2u7fRlkLTTkAMWlq1AGgkFujVEQI+9a8MBS47iPEG0BUwBL/HaArZeXqY1X//Q38raCz5tTeoqbdKCyprOhR4keKEtI3xkFfLaypY9anolc2xVG2koiCX0n0Oz1HrAIN6XC8GNqNVXunrYhtDosN7Ii699i34CqUe50KRJktLvGQV9WsSgI9aRcuiJUxBTUUzLjk3AAAAA//pyBFxkAAACEUFd6KI13EOoGuoxoloIiNdhpYjsQQ4ga2iWlOgkMwVtsFxluUmH0M10GNjAF5uJtjBx8G/24v+jcn7GpV7zP+Kf0TVv9epf9tau7SaNoJTu1s8AXPDUw8SfiVs7tsTACIKINZl0u8+eQc4Bo+d4fhW78fOLpGUy25P69X4X36cnVrTpnvR/eZ/d+3/6l/0oaqFKJAtfy1ySu76pUNOK1rpUbAABBgUBNu28IqJlW6D3wFQw8csoniEXrEwYZFGE/QtqPfynt/K9W6/2X7sz/rZhyKBV7pFeYuoRWtLUcDteP/ylwWoqgAAFgGSS29SaEs0CYEpLDefFHtrHG2LEdaCFxQ/8E4h76AnP/V+XqP7Z040e7yH2+XqfZHVv+Ijn76yb9CveSs/6OTTEFNRQAAD/+nAE8zgAAAIcNda5jzmgQkga5xmlOAi9A3OhnKoxDBwt8BKc9gGIAJqW6fJmAh7KImtNOQrNseS68YD1UTBl6W06C3Uj35T/9f9X/Rulasn+pGhw/Ci8+x79kqBy5XpIMO7d/hEQrTFAAkNViW+CqHnskBoMzJZWLX+RnxE9RgF4kT8YC7gO2JvhQjy9GmtenGm/p1HM///t/8/hNkUa2sl0X27Zrv1n1GukEoQuFtFtFqZSC+lNVC4xqzlU3CgaoUJmWE+Zu3J14l9NerdlyM3z/m//Em52/u960HMgFDO57Elq8+TO1WIY9bwELDxkiwSCYGa7muykbh8zRo7g/M2N8HdpXrxUE+6PicvoH9OjdecqOy/06nr/1sUfQi5E8zAdDn7FRrLDbX5LEepN6VlF11piCmoo//pyBIR7AAAB8xjaSUY8nENjWs0Z6zAIvXtrISir8QkxrKgWiPIAIZpmReXqNqnjO6gyb2ehDxKGMwEOTZwrdlf5QK5FRunlHT7n8JaEP87JqHdjrEUb8w5V/f2kxuPF1z9hQqAAAICEEUk7ewpQSYmNBurK+KzLqXfIizyrXAjgaXZaD51b+R+9hp/S9VNbu1yeQqOfamKXv09lXjI7qhLsatf2kaJIBImWrmuSURVwDUKpfTyLk45lCTjXs+vUDz/hb8n6jLZqlVm+noRm0/1aqsyD09FMrGqJNWSMf1Yu+jLT8qb6S/f6+0ZsTACojBLSkpBJMjIOx9KP57ZuYPYz50eEcghe+rcfo/P2/xv6/0+//9fff/VnYt0/7WV/sv+3Xr6XZSXsrXq+7SlC0piCmopmXHJuAAD/+nAEydcAAAIjXlnRBxPkQ4aq2g4FPAhxA2dBoEaRC6AuNDOJvhAzQBLckt3fX6EdncQQ33S8U8TtlCPfp1bUh/EPm8zp/r/ULogc6N6/OiGUzKRF+2rzDfv9n9lpr9Dt7lb3btsC8FFNiAJAEA227ZUSzBQMCKcOpky11o8+2Ps/mApUmB+j6n4F/qDc3RsxenX+rdTFZFXX4k0aeBc0nk0q+xlLWa0sUz3baCwAQCIJSS3aXH5Q9W7QN/4LhXwQt6hv6toXmbB/77+r2p/s2oE2TpqrkXV2Q6P7FNuhxl77irBxD63JRjHjSrlIk07CEKITaKBEbTd1pbqYArYW9H5vKl8TE+3TqnO/gqDw1/nltE68pP0ZNSN0onq73n0/3e8FfdzpLvqXtvWpC8DCMohth1MQU1FA//pwBFUsAAACHC5WOe0rMEKIC/0U4omIqQGBoKCocRUgbGhnnMoBAAdr3+FLWG8zaMFU5awUBjaqR5F1GCVEyBndorzNqGaeN69BPl3vqTt/Gs2o5S0I+QT2nDyg1kVZq/lkI7Eb7paquxwWyxt2Wy33C7rc0cguZd9zcUl9CXJf1GNgWty+onr1bQvb2/Pbhf0b4z6Eoq/yzOoLYcuazsqlmWd1NBIsJmXz6XVBXo1FNtdvFJRs6wGL6G4zn6j6Ibr7cfycYO1A3d8KH8trt/tuJk636GYr2nZU15K7IJhAYOJ0mHpuJMqUdtFn9XVrxeUJbAWpJbWsD3yVZMdS3NbdGsOPcKjOi83+gtzjy+ODOr8p7VQN/7fmr0JLZu/MjdkTRenaY9mHG+pVzl9Yietb/9ximUTEEP/6cgRJrwAAAgw52dDiLKxECBq3JeU8iMkFTmw0TcENoCuow4m6ECKRIkpOCaiUmAIV2FTbvxXxTwYotRa4kfBl4E5ug7tydH/r/Q3p31b8juppRWalki6wXBObfuegGZlizoITUyFAFEAJuQfSp9tIaoFxBtWtfWZ1/5d/DYg89jHmLNBunGC/CTaPwTr06i3RtW/U3uY0xtPkfZKknXKXrQJMWUu1CavxabABUWw9qVaJT1qMRUBTfbnvE1DNaS+ROGwR1pgwhfGrLmxO5mU84OPocqI3Q6Tbvov/zK+h2Svf6eSn+xasDjyNrMA9nkehQBgGAo5KNmhu9AhRATt2N5qSPM4mGcQhQamD4sGr087if+JgDee/argz4Xdm/s3Z//VKZBdRMyPpNtDO2fJvZllH9aYgpqKA//pwBAB/AAACE0FYUWcrlEKoCtctpXaImQVnpBxPcQwgbOhhFZYQJsAAlZsM5TNWHiVtyVmXtclgrxATGgvX+r83XmHaNy9X/+nCzIsxzorNRvm/O/yNK5GGlDbAC68PWlRrmuFCrMRQoQBUlomChcy4WCx11wt24/y3kQ2uiEKRk6zy/o3Cur4wH5OvUf/T05+wsU72aX0MW03snVdsZuOuuaZFHSCnrdYSCIDMRSLajkG7FWtsAtd5Ncmju/JhyIOs7GgFVbyozicIVxh6g10A+74Uf+vXt7O9bb2rXbO5v0RNlFDx63cXkclUmlxHUPhWAW3IMQK57vgC1pPGNU3Lwz4UEwg8HZfofr783P/GhzkLoXvoTz3+/wHI6KzFIjXzOeZIcFUhog7ScMVq5uLqvTEFNRQAAP/6cgRYEgAAghE62FBtKfRBI1raGe0yiMEPY0WcTfEUIitow4m7IDuFAGXbjCMIOOQVrHkh8Zp5uYcZ08nukqMDX9X5OnV+/J2H6i3L17mTglLZk33F0OcUO4YgoVCTqJzEP3PFh2RLIAsISCpLg2gUpOByMmJXCJR0yhx+apZ/MhSfLr3X5S7848VoqdOz/E0QuUAns7idsY3CNRpHylw0JC70PWMFIJrICcAApxyDV07U2Ao+8iLaX5/GpfKmcv16vlBF68qDdfLtQEI1Ec/vh3LoZ4RWydHEdUOAnQvkU1zIISV8XvFQvMQMyvvnWMABcuwzrRGnIdgEVtPmnsj8LmoeaXxWNuKv6mcadPBPp7am7e/9uhq1zdI5DaiFIyinPnBGdjIOW80F6h4GiXdxv7ma0xBTUUAA//pwBFG3AAACFDjY0QoS/EOnGtok4m6IiI1UdPaAERSa6+qScAIUN0SSTHINpVnRwAcLAeCZRS/IOLhssXiitnzHzG1Db24/Tl9v6fwq9Jpl7Ud0FvqhgA4PHFwM42aKvbljiafIi3FkAmAACduw802EdJyCTUbB8WtKcaPjj46McT/5vEv+IQdd+pbUR/R+nLrGbTvahELRFUENUwaKgYNkH1YoB8SXOQ0KdAAKktFFbeWKvtjUE4msw3ZDIlM/UT64kyCCY7wchogpuZc42socx6kua931nqOlxRySB9LdRTFmpdWTNJQwec2Bup/9MAJDEhS7cXq6ZN4UFc8y+eqZ5IzFQw54rGvFD/35DpxOHNm/4t7to+zZh/s++dfMNKEBGIXBgy9JJGmvqVNE4x5hJ1s0lMQU0P/6cgTccwAAAhkvWb4loAA+Zfthw0AAiKFpe1wlADEXJy2rktACABAAACAgEAgA2cYSkNDzzQlGzPH+WDd0VCfAA8XLEC4URJy6Nnd06knNij9vPGlJf/6RxNBFL//Onq2f8KkgMEv/9YBJDq98orlaEC8784LscPC6BPXl9ZmLmJk09003Jo6YE8ry+nN3QOGSi9/T8+klS//bWW0QVBT/ho8eT//5PgBQSVJJX5XAqAhi0nawPvB0Nkvo2o/6sqotEKF2quSau/fsVqYX3Wpxv31/us/m9fc3vboy2ttsuY3/X/1f9SA46sk+mAAACA46I6hZq0g8OXM1O5Za9KVJUT0AXCSSRSnDyrpoL9q1tu22xhTfm1qNn3VUtax+EwKT9X/pf7v0///////ojiDv+hMQU1FAAAAA//pwBFr5AAACAlLfUSoR/ENJy/0gLaeIiU1o5cyuWR0sLzSQKcbtHwklJrrab4lxJZySWHp+VbwfO60viZ3qKRwZHMI/jUf26CsE+2pqLryHvZL5H/rNQWd6dUo/6cz//gzfzVH6KiEkgGkXPbJDcXsOVRA5f1CnzjA9rMjGBG+pPNzW81jX7/bP/mYbIwVJA6MfRdnSQRN37a396taO399qv+//rG13+SoAAHLbxsxlKofxwS3CDsgT/y/NiyEujjZNjUM+B9OO7UD+vDB1X++bnH/UqK+3aTI3k/94gd9P///6t3cQHGWTbCw9zpIIaARCNkFsj9XDpulTPtvecDJwF5e2VEM+R98zmi8uDa9WzP/mSMesxMs5rkIIVuZ2q/9l/7Z3////Ibd3ZRGBtT+P/+VMTEFNRf/6cgQp4wAAAhJS3DlwE9RDKxsqPweAiNVNdUQoTbEVqS6odpWSgygndvtvyzkXrHJS0TP1Y2cOhGKy7VIayAau+pCXRxaL1AelunOTBvn1JJulJKI915+X6tFt7a/1ptXVf//tqN068AEAACTgdpEwxRciDrN7FPvTlvwin1bUZqo6yR7ZZUCj4id8b+rAu/Ed6MbT9H/6CxvfT+//9WzH//////l/9/+VLtmBKCBUktkxi5Shk9sTZAWrJM9wMCc3o2gktTppRRM04p6f6nE5B6kbCoX3q1d17Ot99ZKIfZv10R+mtW79nBVFp6SB9Z2TwF0AJTfj6Ts8QuxoiUErOl7uYApLaZefTimjo4A6cQajJRrbHVYP1aoCi6O+5BA6gz2dLX3b0Rjx7//////QLEPzhr/E6Ygg//pwBDifAAACG1Nd0EY4zEAFK60sSZuIqTtoZbSuURio73RwitbEBAkVLZbJYTGwPLH8pfoW2QFHpyh/Jfm2vQd+owMupk+7dS7R4kzroWEOps3TRzl9Fq3/Q6pn9KP/vpSzejsZ7cv9MBCDABALkldlncaK7EDXg98mXxRECf1o/G7b6O7Bu+CPRm2K2iD4pnO+wmebqkk3jBbUgVtS0vAXSxcC/K8n8mQXbtx3BA7gUGpBUNrIk+YJ3LzB2KDPaMB9RL9rcSJ+IBsjOr+5R+JvIRruDD00bWlyupWQvZfnmQVf/+v/rfVry0AtuLREMVEolK266sWNQujl445koNcfiAJGsZpxtkvk1feb/+QF2sRGoMpO11ZSsmNRHW1/Q6uzSG09Sm2zNStv/UX9AT9lZ7+pMQU1FP/6cgRySgAMAgpO2hmrKdQ+ScuaFOUfiK1LZmY86tEZJ25oEZwOACd34ssYY+sNCkZBiio5e4Z1lNiojLTNhugrpz2TIL/ij//vq+/hg+tPkvWxEO93/Sqd1N9L9C7tRUretdkB0Ro0AgEAtySyRQd4gLqokUTQZzTwsAW9np4jo30dDAv4oSeRap6aPp4lTZ+22hUTzfo+bN////u6/CIQfUM24nACk24n8D1yoPET4C7kJ5+3o7VFJQsnk3m1F3DWUap2jyo205PVv+WzH/OJP/b+xI4opypX2aVS1W///13R6UVEFSfqWzewvgkJyy0Q0AaCB5QFtz2iEAbKIcA57G5NM7Tv2UagFaNj61vWnQgbUXZngDFr7vMsxtJhxxhyN/c9Jx1Eb+v/9v/UMN2UpiCmopmXHJuA//pwBEMQAAQCHVLZuWk6tEDjytNhJ5CIFOtmZbylkQYnLWiWngKgAAcu3GzCSJoTwtbwatNDnOklYT2C2u86p3DGjZHV8Y/JFpv16Nu/8VMiKqafuZVT1Svobj5bv///+1qdkCFnp5YtUACjBB+rrI53iSzNI1PjzqPcT/V1qPcXfisPhvXsg1yVB8NNhA0o9Qy5zso8AToW1LyubREdt9H+QWOR2uO///lwEZd8OZDReCNrOJVBpre7/QPyy1yE3vMVr0A+rY/VyhABUlLUrf/lxr5U0s+pf/JlMv9DY0fct97VvsSJTPz2lEIFAAh3f6+PON9YdXiBHpU/ah32ViNYKuvqLGx/R8lrzu2WIV9/TofmpqHlk0Nrzr5xu60enukV876f2+v/9C3/SmIKaimZccm4AAAAAP/6cgQQrgAEwhc42bnyE7RDByrjPiKGiDTrZ0ZATLEVpyyM15YCgAgVLvh8YOeBcgLPeRCe1wN9qZbk1EZguR9qVazVjoxjrVtF8EbTq1f+o+UXm8/3m201ZzOn+LnEFr2i5wvXv+vsJAAp20fchMX2UmlVI2DlpRcW+32rF8RwciEcXzqacoDItjMPXydW5ZdFXjYsTsnT04/8rf9fqZ2dqfUOrBVW7/Z8R9MAAIKjiE10eFMJVI6ywv+q7pwk8sohPDfnVbPwHtVQZHewH+Jaj/69H184htR2rNU7juqLU36pIJZ+5gojYKq/+SACt24tGGPSKUDZwsnUTmqaw7pUCGz3dowHfDW+bGvQV/Dj0r07gzlGh53U743fVmtuizKi0r+XUUShv//7da9rxoDcXD6YgpqKAAAA//pwBIRbAAAB+znZGTArJECqW0olpWSIfOdo5pxzUSCpbRx3lgMguXeD7QKm+CgdtMt6XysetA+EGKnSY1X0O9Gxy1JFwNr7wlatt05+TxGbXdVb5pWs/fKiDDijapYDNaJVf/0JAAAAXNvx9oSl+qyhiDGVPM3zrDUEuUmmqLcO9vq6nALvjyRj/bvy6eLK7vT0Q/Wvc/9sxbd1X9P2Tf/2FW06jIIoJTf8WjDGyhHGxqw20jF51W6IPInTJ8q+oDtGz9SCmgzNfOf/9sq+viRd+WZfsUjVLYxcz9XAwEkT6Po5OmfFb3gjQgEyb/iZBouq4oQT0E+VErVLFRN61biXHZbK8gA99hrDBZHdieV8f/Dhqe7vMhEQejU6pWcrLOLXf1bV/InpolP1LXRImC9bTEFNRQAAAP/6cgSstgAAQgRS2blsFARD6ls3LgVkiEk5XmecUpkOqWzcloofgQAnL/h3AalkC4sNkif8Xokb2mwIHld6twHVs2gykA9HwTyX7e+z/wVvt/uYrotmLbYOyjOZqdX9PqWqW/+X/HgACXdvhzjAs1XH7EFqHV6yQ1HywBNLWIa9dB2Hk6OAXfIG0daU3j3jRbTqKTPI1plPVe3pp2bEetfSrK36vZP/RP4qwATt2HpYPTHmMahswUO7bGp8Rf7xAqiS+jnR0WthM8q2GbpzfmeZvf3zPv5LuyOrmye//rm/1+f82nv0vXeK1Ll0hQCEnJB64Fp9O0MRzpEG9Il3YWPgM777PpvUexMdBpQvfc1bbW1JqXVeP9//ejE3ZyvSu2/106X5F2srybfpdwg7GT9TEFNRTMuOTcAA//pwBEqJAAhCHinXOew6VEJKO0clBUiIgONYZ5y0kRacrMx3lgIBAASl2HvkLhLSmefRMJQg6E/7YVk47Y4G/tetnjHgzK6E5fniXpy2VN3t/Xt5Jv3l1gl61Nlt0owSGLFMqIGRrrolooAAXt/x7sKGudeWkXwRWwX+EAoABzaheT82g7FX+KtX/nXg7sVeNXt0rurT0JOrOz7tMlP/X2SpHfq7ZPpfoHkIkQAW7aPiEA7nRMH29FzXTft4tfwEp+xTFAOiP+xakRLzg3rz4yzRSIFpztb/0wt/5Wb//lRyd7viiyQXboyGtod/Ma2uAK3/4vAmQicZFcbZrR8JrD6lkX/GXTD/H98t0HUAL8z1/zHfEwNnO2UzOzsVHlZk2OYe21PGPMmzW17+pTYirQCLD16A0mIKaP/6cgTxVgAAAigpWj0k4ARCRlsDp7QAiMDzenhjgAEWrbEPAlADggiJt/x42CjNFXnsa4w/9KOVMCAffN1L1LP55104t/PZn3zXueQUxn1TYqToepaHpFHNSoUJODwYocoYnd2CVsWYtUobACd2w9YZMnGkyXVkVlzdW67WajsSJmHKAvkGdbLN0GQHqnOpdNJTUSRT+tXX/WrW23v9df/TNIMFF0xGz/t3en0ngAAhQKRQKmxp6ZW5qeb2lwWPECoZLf0////f//srP/54+TGhC1VOVP+fMG43cgY04uOsX+XSQE8Hdv/LqAYfBByIVe5xC8ACAVCoUiv7/5mqihByf//////4mdl//vExcAAI2VTKn/F0FADA4uBA4SjlTX/8ofc6EOeofX///+LyEOc4cEcMyn1rJ0qY//pwBOpuAAAB/xJcNzBAAEQD66rmlAAIxH97IyCjsRIP7ImEldgERVA1O6fYcMe5Ls5Vx0TwDCRLwIDMr9ivDj3L4pQ3Y/tJICgK8O0StGLHhKRIke6o8ue35qnkkYlDV7uKrLMVAACJjbITlaylJAmOhbmiKITAIipmGg9R+suFvs9bevsv0H+1CBwK89RK+o8thFPdUeXPb7jVPJIyQaU9zMQlQEeFKyvCQJNejbWaE1Ypt7euSCDLNqD1eiMjwEDatmRlfP7Rumg/e6uDQF2Zue8FZWe1maiOrUVU86sROAt55EqWahCjx09eu0SuN9L9dmYrOxZlFI7V/Otuo6OMoHXv4foQgticqrb03uB2yimEp1eQqcsq0jAbZ8RP2Zue8NSt6KzNRFVXK3uqr/To5760xBTUUP/6cARMZgAAAhdAXTjhFkRCiBvaFGJziKTXa0Y8rJEWoG/0UR42FCElbbtoLjxOcwpuqqSjgBt0ULhquhnpj7VZG4Qc1NffyX+/w/m34vIQMmrrpp4IKmPOSDnKHB1ZtCY6dRY1gaT3WgBJKEU4nLx7haMNk4eqAQjR4AlW9x6D7a2V7G/v0/o3/7Vp4s7/f0N6b59W9IIUAH+iVRERa202IjZW5RRxU7YtsngAAABUckmVLhYeWP0FG5ZDq5k9inrb4Kk++1vQguIvD2vpvMC9WqO33byftffzL9Lv/eHCrIEhz6FOyh+Xk9zU9V//4egAEIaYZdqcs6NZ2JFFxXAoRcSQEKeNUgOPv7ojK9y/xHR/mQ5b8px9WZDE5UNIbl2//M6zv25Sp/sNXpVoRStvequUIcmmIID/+nIEGhwAAAIVQOHQZxMsQegLVzzCeIig34WhoEyxFxwtqJgJWhtp/Ldtdu+OQKk+VreMmzYnDVdGOV5f3Sugv8GX3d/b/mb6+8eu/9353v/nIuCZ4gqsSswXUp9Dzgou2t+iPIycUHwADKjbk+GpX0Khdumbc+M5R28AFRLvliN//wvA/hfPH2zFW70De2Xp+yO/1U39OrM3/1fR+tvu3Dfxzb2d29v9sUpaCbcaUtsl2uih1y3ij3CcOtIYARqjspB4b/rUobpseuS/q/e1W/K/qy2v94gfOEHL/bWb11LNzL4MDW0PZ1vNmjrpOUgABAEqRy3xoUWTALEQ5C6yEwsNKMxWiNGt9jsPM9R89/L+hun9H76uX+61Z3Yv+9eg7b87K/70TAvWXBVUSZal9pkVfcPTEFNRQP/6cAQlXAAAAhgT2RgMiFBEQrrTBy8MCKznf0AMoTEOF23oZ4lSCB9n/L4gm483wCT58lYM7LNQeS2itEPyJGq1ExJQ2lI9M2S5CxPVDf3MthY5iLlg/+hS9woqH0Uar319CX02/5+lSwAWVBIs1U5GOqigLZ0tMHdjG8xIe1MniwioWrpIIuSrNWRldKlaqkXXlaf3mWDRppLwZq8309G78MS96tKtEt7v///6AsFrLcskmpsh0HlEtGHd8YC0vVRLVoWelWTEw5qjVDd6W6H61fZvn+S+1G+bWrmHqLY5YqTWEVP+qp2IvfM1CgUjkwysagAEAKVkt3xwLACFrXq9/VlJR+4euUIISjUs2rVfP2wL+P/5mc3ujs/1J4+zx8WW9F6SoqHmVbjWpT/7GVT15lwenoumIID/+nIE/gIAAAINQV5QZxNsRIXLaRnlH4hsuXtADKExFSBvtFSVjsMAqCTkblzqlKwTeyPhstVRQVYpatGlc19lZcoGW/Hrp+jfav9StuHMv173ZXcrsyo33poPRTkW0W+S2PsddI1PVAAABP7W1uC8oITt7Wk3wp7rglE/916XyDqXIOhY6Ii2HHc7KgF6NEQlZlZ/deqWc6/lXnFkgX11nU2ylguq73/p//m0goBGkpY5djvbvtMkaJMUQpZLoplLDHo+ytqQH/RKU39X63TZ/V24k1VOV0iNFIFsLPbTQ5RJDoBFEFg1a0uvWiRevqiABAZZjdjs0igaYjpK+noRmdwwJGoCzO6Do1pdB5SWlA1kTVv/0vbqT9W6kX9/trTT+hiDJBFxoxtLw0r+yjz1yS7HJk0xBTUUAP/6cATJvQAMghQuVxsDLKA9wttKPSNyiFEDaGYIc9EWIG5oZYmOAK2mw/rCG8i1SSxdjsZUg4NeWsN1FSfkDTsb9bEgg6PEQIPBngEPw/KlGUPgF7xAd/5PX7/s7VUcXWK/tT3Ox36O2AAIQi5JbffqmYgactfbk9zKpcbBRW4u2XFsSpMbwbBl29uNxDvSoe/sv+C7oS7sNV2mNa2htblf1+r/7vpBStuwmS8vc+qMyaSptqPzY0CDZ92RqzseisE3/fR4ctlR0Qb/4Prp0T0J4cr7/2ksqVT9hSWICdDir0DnuTjuStiUMQ0pHJO3W4KSyehTulyLXItTV6D9Qg1WkLcgIqPpb5Ov6HPqkmz9WN6GOsrvXpAnmQ0rXPu9qoUGKQZJW80GO2/u/3u6ExBTUUzLjk3AAAD/+nIEP1gAAAIcHV44AzhMQ+OrjQliHQiAc3tBpKyxDI5vKICKViBlttJOaWzuPchQN6UL0eisONZ6vVPuJGyPOLf6s5qORA7ZWOGgM+CAIajiS5/8Ph8ufBAM4fBDE46TqDDKwffhgPk4AAAGUW/pLvWIsRDZaOUZhvAYPcokQAeOuoANtR9733/MJoJ6tGc1HIgG6dDhp8AFMoclz7OmfD63zOHyGJ3+c5fnA+CCEwbrZTn1TScohvfpiaGF4lzXp6So1NXy31DgF0NmFQ6s8P/I8K1BaCvkqFP6ysOrr+tEZQbOuTAz1HlOUDYoFXSNOBCbAtxlObb5VV20bRwGX3JGTe1QALW9d5hOjvQffoLVCwdbhyoWhryUkp+iVg0uv2rxlAdOuBqDTyx5TlA2Igq6RpTEFNRQAP/6cARF/AAAwgci3lAJKEw+w5snPMV0iNjrZmS8qREYoK0Mx5WOBGBMouxS1PYLVk6EqHSNDLEcLejoJMIlHQk1H3v4/87/+wk9Yx4GK5bfZ4d79QFAViojyRUYFTLx6zs8OalX6zsBAE43BPXCGMo7z9aIfdNeY8HUcFlciYSqvlMggKaDt83dDgbx8XErkXqNLnwoj1HrLsOys9qllEdehbv//1hJzbYfSqrIjTPZSDfeiE+hNIXjVMGNq1bKItOOjD3dTI8TCHXER/V7evt/KiRJH1st2buhc6RgxjmkHqYVEohFEfv+5KrdQJbbcE/iG+HI5xdz7GXY8EqXmpJws7IyoyXjAzfEb6IHbewnUij9NlPqO8vvVPVV++uHqoJO6sp00NsWkW/0osIhqihx0UTEFNRQAAD/+nIEZTgAAAINFto4DDBkQMMa02Hidoikr2bnrK5RD6AtKMKKWgAhk278UIz5VFaK0KEzvFHy4Ev02jWx8Z5cv8TnKzXh1tEvb/KKcdsqzrd9wPJFQmMC0yDbzALXGlLvkE0MWJEvQCS3IB96uz6VCISc9eCs5qG5iXtLrRwyCe6fZeh4cd3o1VZmOfwmjD6BjpdIZDImufrP/xLUYd8FEkFDRT3B1X0wACUuu4+1xSEiDPj1ojbN+WSsIhzV0MKCxaWXYuvS4rVXkAnbGkYaqqzn8uiI22ypUWMCAsPv52XvAup/3bjYosvOr9S0AAkCb224m8mLgdGKtua0glJqYO17zLrSedH0VFfajaM3UHbvjb/1YclTRtqJeFZd3cldfarcXRrP9PDWftLNaSiDrSmIKaimZccm4P/6cASR6AAIAgc12rgJOGw/xdr3YSV2iQB1YHT0ABEYHu0qnnACAIEm25AHpuRCkflRPZ2SfXJrHoFiDKyZiNTN4bOZalBF14+bR03bomrJ2/T9tv+XaDKhvACUh8XtRFmiCqp1ltQBACnZaPwutOp092mwqQco6OgoHR1QhxjjuiIK1M161whf8d043XIC/13v/Ttf/lKr3HIiP9Rb4sbmdWpGhfxoRct2HynjohkZJQQ/TGhVmFHqLS+Uh7xQTBAohiJoMQ+sv0ulFCK/YLf/ofngTnpO76XSo8Rj+JW7J5E+1YHpFLDLGllO6U9UAAIgKXb8UzVZ2btnN9fVd3V0CIMUMGuPlBvKot0Hc7Tp3ni7rodnTmp183S/v/NvnTf5O999tMaG1HxSQDsVLKjEDKE/cmIKaij/+nIE4BEAAAIoW+DWFOACQqkcF8OUAAhIP3RcwYABCI9uB56AAAAAAACAAOBwOBwOLWoP7aqsE2r//uuv///5h4+ee//8xhHQbCR9f/jQdB2FwnMOId9//8H5M89FLE9P///8g6nqjMPvDAAAgwMBgMBgMOlH21Q9IVeLM3oZf5pyf9naIen4iNc41G//UofcwcP6//FCgcJg8wp33//w+L1RRx7P/yg8LkAG8CAUN9BwAwaDuIhKkkoeJS9G4VhHLZqGYnI65GGB0g9wVFqCLgETWRCT2HVjCxUsP5HfbfWYiK9OVXZle+mvqRqv+p4zwC6ByKAl50J6OpV94pUU/etofSwoVKOFVS1cyynJi/9pWLu/vnaW52EqPPOxMVEQ/kc9b1uiKPTlVlmZV///v+pMQU1FMy45N//6cATHiQAAAg07WzsPKGBBZzw6HCd7iLDveSOEdzEYoDJ0MR1+BAAmm7bDQicq103oQrmKSeNMweinDtfKKGUIJT0VSjNeW9EMn9/Rq3tfXs/VkRr5dzU/3/8VRxY6h1r4dcv7f99Ai4ttyRpyoFC6WOc6pvgRe83w8YEKXnE4J1ndMFz/2ubVF3//N53/+3K+pbLUUlSz50s19bhYlIiWojYEhVSx5asAUCkqmgBAChcSNuhtqNKgUGLYqEs50pS5g8xxfydhq8WBinyZkQQ3s0EtHPNCfbzWxr/Szyo5zEXPKErmlVStz442nlqwY7gXrJtvdvIZqjHFYX4Z7YUIOa/qR9Otpmmo2iJg5X/Vv7rZG6qrEGdUV/0Tr0qa82tFNUTBgko9w96WKoXJXrbrW49bUmIKaij/+nIEtoUAAAH1DNo7DHhQRGxMTRglFcjse2tMJE5hHK8xtDAVRgQhS+z/qVANYuTxDxYqQZfW1MJ2ArRe2d28NLbXtBkw96nwUKucCNgby1mH+/Sl/k+ucJFNyH0ddrej//dTUEiqAHlJbZN6C6RkVxVvCKehcq5aJ1Zmufetf6AZP/p53/J6FPe/+mlGxj3/5G+v62rerVTpvuZEI2bUixvUp7lavQSnEBAICayW5RMu9btbtYzFv8+7vclRQpp6r/EHz99GTlBrVb1v4m/qbOJBspbrjO3Kl1+BqI9J2lMPXPVc6gmVX7wpsrd4w11BtPAKWW7aXetRZ2b7FGbamq/aqFqt3ebRhj/tgCdtPvKq5W6bk6D99/9tBXiTfpfEx1v+isvmlq66bn3oVvq32Uiqo1wpUOTEEP/6cATIHwAIAhMQ2bovWGhC49sjYMVyCHhjZGwcboEFHO4k9pRuACBI2pKEuFa0byfOVxeDarHTRVifC1UtThabfz9PXIAJbxV7AHPDlz7Pscjmcil3hKJn563GtUY9Q37AJrc3dv9tYKer/5UWTVnn0lMpmp2g7+q+rd0Zi3Sg76Wnjv3RipRjvZj1aX++DfRCoYC/fjUfJRWp2J3Qi5Is3ojdvZt2OMdzukLPWf8qFJyWsbksl0ckW+Zwb9NOBpZQZdQhtV/UpoRUilmwCoNYopEMewqcc0Zg1yz/Xaoc6Z5FFOt5i5V+j9NVe7WSACAZb6rLMbWJVKdFO76EG4Oe2gEqPXSZhtUPZVSisv9FKCNb9DlclrGt6t1MS3+0rsyFqPrd5MhWrIDv6+/T+npTEFNRTMuOTcD/+nIEXGUAAAIRQ99RIyhMQmPLE2DFcgiVAX+khPLxFg9wKJGUPhFwrTksclGw4G+RH3LZA+GXdAE1R1ScrLplVezPfbPhb/oyNctyWo3X50V6/6aeZGvLMa9BnK8bpfmlq4v7rve5lqE2U/460nK3kGUs1Vsy3nMNVdygZSzBQVugQHUu56ajqbbFucogy9SujAKIDG9i03a9H5Zk81uu8VV679zeiqV/ToDRaIiiktjlw2edGiO4Ip7tfbNTajkC2fkrnGFz4zGpsH7vUQf5/TarbevmLf/069kLW960Hj/7BdDGLZpuc6R3yNM9iou4dtS2y3h9zr1Sm7/4SxX0NsTmMNp/eVGEf0sYDK58qUDFweXpNqCd1mCqWwdU99ag+wj0ApOVcKCtVND3gJSA8EEXJIpiCmooAP/6cAQgzgAIAgUeV5svEPBC6CvNMKI3iG0DYOwYTlEPF21c0IpeCbtv/KlLyztInx8jvkQvLqaPU5CYgFCvexIuFM0XlIW87hZj9SK72IX+9RLuyrsrNSrlUd02lm+1VqvX9Xt/00AKJAFJNtxyYNavfaF+isQngGKHNoBrZe5SuzNnUpLsqP/xP//7dM9tD01xusjSX2V0J6V0H1Jv925a0XXyxtOaSpqwhJRyQCjUgpXdsN0ksH2sq9/lyJrIDwruM284zS9XQ6BA2hLKDa7jN/dYH/Z61Wybr6P71r/SVD5B7mZHuSXwXXbv1CABSbKcBKjeXCxlJpWW5irAcgwdGpRS09MRNPQJmbJH79Rz/24tO1lfdjtwzrtfh+Qoe3UKkdOrixa3A5wtX+9zz9taYgpqKZlxybj/+nIENI4AAAH1Hl7QwzPcQggrmigi44h87VxsDO9RFyBsDYKd4iCwrbtslohKN9Rq3bP+UTDOZ9G7T/ltysP6W5VSk7xD4CI64NlRcoSq0Mz+ok8OiShR6Kh8NuWQlmp9Oo9qUBCBRTkjkA8Gw1LuhqHTvQBcxjtxY2MoforMuqWu5+9tnnhSftnNHPvI4vNBNhbm+E1U9mIZn3qxqEHbftSsxp6MiCk45AIuQE+VJDK21FK1vk7bq+2rtgOUlFhcgAS0+2GZT+GaZchDb3y6BCf/VlzXs27K92ObP1Ovf9P2vJ7EC9vtIf6AW5JaBiEadeGPt57lfZ2tnvmQIFkUCoVQvrmRDWdXc1T5B7elnhr/dVcxX1z26PbZL11bWe1UXLFD+6T9kKF0jhmwZp27UxBTUUzLjk3AAP/6cATAIwAIweklWRHiO9w/5vqzZSJ0CMkBaOeErjERD2sNlgnKGqbB6jxnvCZTeWI258ZytFmTAnZWaYX3TKlNbn1UueSd6Z4WN/o3Hmq68kvRrzqUqn8T6me0tDtzRiFRR4CFtoBjkBgKxmlyyllUrlV114BbHnHWwnTCwomDUqVBKEZwtzClpZXqhTVZA397jf7XuVSW99U8z//u+g8Y/5EISTikAncWN5UhZov6fHiMAHklyjSg+1bdGlnFClStCe+1QF/3PnS0btTCh5EyKVCWT9a+rrlqPuVsJnGKfF3MoGsRLoO6ACW3KBHC2FEh9qfCapZ6u7E1M2o9KToWLa1JsOtC2LX5VZyZ5WsJpdf1Ni/SJFMMmlrrwVf9tWhiKFkflq4rVSoXpiqkxBTUUzLjk3AAAAD/+nAE/JwACoIFQNm55xOcQuga42DifIhku2JnjFLxEp0s3POJzgAgtNqQIBLuswGdEusYrj2azYBVoyjsFwSyJtMloL7WBIXMzp4p/9dFVpX3PmR+1b///Y+PK7MzvF7OCucWXuzYJcjtCtAsZwZbzCtKcdRGh+mlM0bUp1IDpng11c5Lnsg49bVvWpn6sxoYS21bGZjqzqtG6+rf/0/7evfCG1aU6lr5ppwCRd7ZjRJV4zqfybsf4DUNS81OZq18ZWZdpc/k68CJN4f1EHv/RinVqN+1uKnaeIVCWNOHCW/ke60lLPQVErBU7MjBUjTgKJySzljF4tO5Yzvew4Hs1JUC7taquxwjsm6Lkm/rRBZVvLo5i2OMhDOduDL1en/0b6YlNTyIo933ONLfQiRbQhMQU1FAAAAA//pyBJf+AAiCG0DaOS0qTDxCOuM/C2CItOtWbLBOmRmdLNyXlZYAJSkrkA3beMKHTWRQnLDSCEEVTIyKfqLo7YnrZoxbZ/yGsIvRb7NZSn3bM2UNMVbFuRu/nJ+YoZLkX46iJPccX6NNIKckoBMzJL4gdNxmtrjVAStbtwjppC7C5sqswAyda5qZ1VDbHHDbTtspGegYyx523CZ5t+2YVe5HBplfSAVJIAyu/TKfkTtLkViyl0R1MWImmQcCYQkWRfbx8KPNRR77wRQxH9aOinU35nnAn37JImdzEyuR4Z11pYuv4RaK2rDIdoZTcrgEwgLb1w+2ZpfLe/gfF/UtexImvHGsVh0Scjy3PsJ2R92sxB7trlrqZXKtiEKsVHJmFiMzLT9bqQm6hpXnizTqT7xiYgpqKZlxybj/+nAErN4AAAHnLNm9POAMPQPbN6eUAYmBd4FYI4ARLC7xNwKgAgAgpuOAK2kSlZUg+zuf71DfAlmpzEMCxfZ1d0dW0OvTowt16PYSS/+qNvb51z5TCrfi1KhRsVPgaHMu9lYAQk5XAFhwW4mKwVuaPG3562EbRCGSoGoxVbobVKoer0N/ogjdpLRDMoU0GbnhcQObtdhk89hJThgipURSAAAAEAIBAGBAIBB+97NGnp/+df//6KpEz/44PGuOGf/2OFZ4nHjP//FwQhMCwHYjsJB///+JIyxEwTFzjC45////jdT2ViiFCA+glggAAAEAOQCgUCgQCAUYu/1daf957+//0VSUz/5ARGuQGf/yxwUZ4YisPP//LkJMPB+exIf///kpezFHOMcz////H6nsrFEKEhGgtpiC//pyBEvuAAACJDlcnzBAAERBG6PmCAAIqCV3J4jE8QSgsTSAiu4AJpuToQAx3HwdnDVK3F5jz2FhaYpWMhpXLKhnbqyO+ireletDWplLSiGlb+j+Xro3yrothRV2SPNcqHNz5Kbr2e35adBTpctohgmO5YJxwpWw0fMaLroIdBUsBlgUqWAtYwRQreeJviKIlnYmCglAPZne7CcVng1rAS0WFgVoTFXKfbLeN088VABCpqUbhMgzmejsSppvYWHSDTFRVTGl1iMMKHguSOi9AQcgw+lxZLYvlnK8s+epvWlpdyTbLKw0iGniNrMNXbHC5hmlyiAUko9bdYpKyCe+0244/DUwsNxxaospZ3MmNeLMzmjm8RJF7MaNqQnfP8XL/ifF/1XK0411gCe0FaLfw1dUzQzJpiCmooD/+nAEmLsACAIQH9YDKRuQQ6IawmkicghU12DtPEPhAKBvNPGUFjCKP9IFfKQX8yFLWX09PIYlG5HddsJCnmzE+SCS0rOYlQqKwY1olyrCjYCVun/BRClOqJFhCCu0k9BWs7TIkY5T6wn/MABDgkgFae1pzDZSvx/dy12nqmsmMExNHhtZFdgBiWddoMFEJGmVg0BHKVQh+4yAbSJ0xRWfcl6VW+vXkv0f//9YUm4VJWnk4J45ZhdJlb+75lj4Kkkslq1yxS/ux89ByF5Uk5RtafG6e+idlf/+u//DpJjT+1j6yrFFPefR9g31O0VkQSSSG4482nLcbbJwOV7I2quNoJgxovW1FXNK4gze35x9QH2uzf/f/6o3//z//9RMd9ZB82aGupRTTqch+VD9skmIKaimZccm4AAA//pyBMlFAACCGEBh6QEUrEFICvNkIp8IzDFYbT8BQQ8b7B2XlDQRuRiyy/7O3DTAnvtT2io7nSriIwv4tTlU9Aq6gjIVLHZsUz06o/pej/VXqslat/ZuT/5nbB19aZFbH94h2f2pKuuoBciBcFETiGb2k3TrZtfb1TU0hwoVVoxjhY78g/22RowEmWhnigciDm9BXy5hlHL+XMLef+XPsv6eFd/2P9GW/orBTbdtGBo29FDHVphlQWseqngsFkcxoKFTz7C6fcFqF7hryBU0BidGNTFjVXiNbtT9SG9blPTxqarmPp2hOghQ9+/7E668mENxxSVfJSydM27p1VWpd7K6zAExju5oQE6GZanojNRSp9urflTv/7z/1fsZna/0PTqK3qyxKaGKWM1Cl12z3+W7Q50JiCmooAD/+nAEq9cAAIIHOWBo5RBsQsIao2njcgiVBV5ssEdBDBysKZWIfAUXEWrLdrJdBl5BkqiRtoGe2DH1vlvGobBqybE3/sy6aWT3CLP8yP7oZyf7voag7O15S79CPmj+5Nxu0hQWcuw8AW0ZIsgn+jQ9x37qtwZNA0JvwpRuWyKOAYMr6MyMNdlPtdGRjOY4TOoSc09kPJ9Mcb7c634flFv3oNqivzLv/u//WHLtv+1obZcq5g8S2LGWKELWPhaAkOLUp4mm2bNuXVqTujvdovX+e31+9n3+pm0GtVf9VrTRP32oCERWzR2yepmrR7dywDSUrcud4jFlml3ipI3WpYi22gAQTd6kMBE4GZkKZ1D6ZlRlqxP02Z/1vbshf6p4r/+RtFg6KNyH/Y7aHenbbusTQlMQU1FAAAAA//pyBMT5AAACGEDd6YMoTEQD2zZhJQ2IpQODo4xUcQqgMLQzigYABEBuKTSOSpxs32FWFzwIKoyuHQ2pm9mZuLlK5uwh1ykKggQrKt/7Zf9W4vT5dNt0TLf6JNxVTVVRrbl+qhaatG+SIC181r7JknnxstBv7qcvQGhNY8YAv0YxYq8qKioq2l0XtceqDUWUYNBY+N0ml3KtXcgUW/pVOClNcDrY9a9inq/qz5gEppJS7b/3YCctarlnVF1QH518QEsuv1gYSceVaL8ztpqNIsrU3NevMzWXlMXUIKeR9/m6cUj+crK9mEJqc+pg9idnWomFHJFZrt/rtwan1DIU8Ki5iRq5ahNudAVJBVlZld3Shh/7TkVK91ZSehxv1RtRCe19VS959l/kV8NY3ft/Umdu/Ie5MQU1FAD/+nAEL7YACAILN9pKDChsPyaq8mHlDYgpAW1GHENxEyAtXMQUfgAAKL/vAklst7LBXRP47TJabAsO7NgMOOjp0ZS6S0kuRG1dpO/92r9f0obx1p1/Ii9Bg966hUq1DHKtmHEmIN99IVSwSffElTHey5ixrRbSb8gmzOhqlBd6IPWcRdV6XjlNvvU+ev6aP7N6SO2gmvXT3FjywyKN7BcU/d7kZQzekLi5JHIBxGT9qCG8uqTYBIqIa7u4Z96FU3qrRMxWFNU5ZksMuv+m6L+yEfju1b/72S6jXt2d6FBnXehyVSJ2u2+so5TkjkASlE0pSrZ8Pw3IZHy8pAX9+9lV68rVYyN9zvUQ9+1FZLupf0b3R3K7V5oqWjFsOSfIs8tQow1rqQohBSyV0pjExBTUUzLjk3AAAAAA//pyBDU2AAyCChlWEwYTvEQKCzc9Qh+IIGNObTBOwQoOa9mHlH4GYUAnhYTNYH+XqKwO5WbuUle/F2jTCIqUr4gNFa/tilYffVt5tWEcwdLk6tWaoA6wyaELSzfdN+46ZYOfPct/WkA5TcicASQ6mGCphj1Oo9QnFpZWjDfV2YgJ22LO6Ndb11fW/92OiZ7etBROGIiVanzPyY129X3hG+v0tPld9EqqMEZkAE7JQDHyAwRDikouz9ZIkDSvki8X/dh32cOGBs9heWjl5ILEX/fY3GIkzD+q2VWbIBq62fqQ9dg2oO0X6Is9/1JCV9WCFTeNPfrMKlI2XDwJfoTVnxPn3RvwPY5UuYINeSc49Kx/XGu5iNqC4GAhjqwmxfiBIhnXJWVVYj+p6PJjbSJBMQU1FMy45NwAAAD/+nAEKYEACIHxOVcTDyhsQearSjxlGYjY5WVGGE6xFxzs6MCWdh+qgLrThJVi0GQ45+zUXu3pMBsOuOEDhIVr0aqI+1TaKwmFOqOeYuNbO9f0vX+Mf3b/pqVJVjUorY9h67u0AgoEXLI5AMNhgOLB73WMTIAyL1H0eyqUUKJte7jHfdwxaq0qLUmzdbWu7Wr9W0MY5f/E88ohQxIWLDBHb2xbb+YtBYtyRyACL5UilhIfvTF9sswIJaityB/8RLLHejUmnMqMW2iZdFL03yuklvo2xmuiq5uvKvqd4yM1nDzJEXMscrijbTLxIJkqJdkkgBEXFBW3WH4Jj1nIpM32LXlaOm2fNKWf8yIfNSGciGJbkRSIT8OUyXEDnZU1OBXVHNYci03vgypxXcIXOqMOxiqUxBTUUAAA//pyBKo9AACCFTpaUego/EEIKuI9hQ+I2PVc5MCssQEMa6T2GG4AQEk5bZICyncJthALLieIUSCIby/IX9j5jn1iVFRXpEA65JnsHbo8lG69rkPW99CcAWJt+lh695UFXZ5BaQnf7d9i/9gCEEyHcdGC8ybJ9+1Lm/hQTL1Z8gT+qjUU7EL19daLQmor6JVZlV7um7caraDaTF0+j93Nd/zWZRQVUNpxsDABKTkTAA2JgBi1nRgZJOg0HMo2AnlCkpx1ijP1ZkOQOGzNLFLFUDfx9CE/MimLuhVIz7RAGKyI7EjP9xb70T49WigsdMX9IJNf3h1xRjIe3YwPjitezSZoBzPNKw8PrVWPvwxpf79UzfbsPqYKg103Kssc3PD2rvUFIEUP1otACvoxRd+Qo3JiCmopmXHJuAD/+nAE8gwAAAIgNdi9MKAMRGPLGqYgAYho9WtYkoAZCJvttxIgAwQlOSyMEvTN3qtJXPvyZ2iGBQqsXCIat/Se1K3dZmHAV61uSce9/Nb7Mjd2mfmIdmKUfa6Shg8XMkiQHtKi9A3si//oADAZOSSQB8dR8KlKoVo5W2ykOhIeXL5YKDOvpBzCw7jhmbqCzw7FK4SZv8wjltTGP52Vz4fEzRpgRiA2JWC62O22LpGgAAAU3yXbbcbAAAATopfunWFrUlajSJwyPDgsLMIi05XK6oKtj9w0xHR9UdscxaD/iz9bU9XX0pK50dbf/dV/IOdOoAAAAAACVHbdf//8AAAQnlYhh7cH4xaQrWYsvDqFEW3x9uDK8VwpEvnqVWqI+32qTdg8qWUs1ncktnQqqvVB6fc4ATEFNRQA//pyBMuvAAACG13hHgSgAEPLvHPAHACIPDVzvHOAERKJbiuGMAIcDj8fj8fjP/8q/////1a7//IrOpP/D54ooTIEA8O/sc4oRjncoiOCRxhxxP+d53+ocNdhGh5f/u3Rjv+Q5EPVCu7CIgFFotFotH/////3//MVnUz/xufIKFzAoNi39jyY0MY89yI1ICMeUPLDn/Pee++o0JXYdofN/+7dGPP/MPHEPqhruw6SSCAXHY2SkzyJclRyxt9sPIdAiIoUBYTTDVho4BHrllkrSK+yJRh98s+W/BvOiYO6h51fM63SXW5bvqaZjwkDRGsEYK5W2Sk2Cwmek5nPPFlfUKI8k5kzOJh4xZbrcss8S6qw0IyOHc9byx+VWDT8GhKpuVfkq3XSUSursFxGh5MSiIGn21JiCmooAAD/+nAECx8AAAIZQNxooR5MQYgb/SAjn4i4u29CjObxFaztqICWViAAQGo5IyCWijONI19qgAsrMg5kOy2IyCbyjrpfwl4Tby/mi0eIR/NeHl/3ycw/6pbbAyR3lix7qV+mRYsZGNHPlcgQyojJZbo03Ymrcmdu5njRm+FC/ozAzmeSk+/81sT1/55P+vDy/4lk5h/1S22CkiueERY91K7KkwaYszCTXvlZYOACo25ZGm4RBThaCDTo1lFXxwsyO+5yoQ2a79aB715/zP3/Mzv06XHyCfBRYoB0OkwGVAQKmHpWtwxRNhedgmhzf18ciW3AghcnIkVJRw0xCO76idceHst7gMnXnrGtegoK+3yr//9/f9CP32+vT1/7MgWrf+k7EKqnIJuhaVlrZ1mI3ZOMepihWxZNMQU0//pwBLadAAACEV7XUeEU6EMsW2okwhuIhQFzo4xScRWgr3RgiG4AAA1JUglJ2JLrGclmookkupXHcIt5t+u/CcN9qdQMHw+6/AH3vv/Mf5nx07L/MN9f7z+ft6f03W/996L//9t4s/WACE6cjJKibxefOjdx3fd4wcRbuJxnWVXj5ed+hhf/mdvsn7VFf/Rf7fXSvX/8O//927nvT13fer+0i3N0S4Yh6n2iiCCCC7JNGm4VvepKz1ZlOIWwoGXshl14Arq+7v0/Rr4yMuqv+SpH+r/H6fS4jzMv7bOuIekjboHjzkjjAtk0ZHtKJq2tAlIN2b6xSUDNMKmV2Uav1KPUaRMzB3r0UgWo4os7b8Zf/sslRv5Ohmvt/vx3vf8rlyDzt4pKhl9yVR8P4vyN0VLij15tMQU1FP/6cgSuAQAAggxA2lDKEkxDY8saMMMhiGxXW0Y9RSEQF2qM9ImQBABtNxIIstEbHYwK2SSrOwcFWfiUeiMfsKQzIrsxHq/+jP4P/+2X8Ef2ZEb/m8u/2ZdaCmQtGa0faj+9alHboclAAASJjJBK5QGJ75VBpF0RY3QjGuap3LhzYKd//LOsrKVpNwx03ehb9b48O+KqesRE7ci9YAYdI8Jn0MT0o0c3bjFs1gAAEJbGU4yPgSpXbEAOpLxkzCl08SBXHUyHzwV0ZnVUerd5SscTt6vdYFiT8dlZryk9e/SihFWp1zk3YE13fdHH6FqEsqctsPwTceToGYX5CUE0vWJMWfMnAPQKJUkF/njvBkeu1G2Z9el7f+bRzmp+vwStb5qJlsG5VBlgrr0LLU/7Xf6LZJMQU1FAAAAA//pwBEVzAACB0S7bUGYpLEMlyvowJXEIzQV5Q4RS8Rsa7GjAim4EAA1JW0lJi2sAmT1J1rD5/UN2TkdV0Z2SW9CfVC+1P/6/ymbUWW32qEE4f3pWSvW+r/i/4BprGsJ0AAA2rfWJdhyDI0kSkS1HG9WCuwB6TBtx4BOlkUzyBJ7vQbq12+hdHtb3/Tb8qeKu+jc3ta0TK+kL7XV0ItkduxSV+9sDr3v7JLkUoa6oz2rHioPjLYhDWvJ6wqiHgGNHoDphiclP9qt/VuMifNoqhaMllDqb8lagxPTpFCBqxSNtx50i9bIaoryQTFONEpwH5MM7ibCy9PW/vwIk/26/HnSbMJWyzzFwmR/O6EZHFr5wzBL3H4J0b2NrZBSXrFFdch+pZp6kWnbhlmbtuDJx/UmIKaimZccm4P/6cgQyVQAAghQuVhnoE5hDqBsaGQJNiKUBb0GYUDEPIGsc8IpUMusUlfIMiFrWnxeSucEPa8UZW0JkvPKsDoJJA3mNdntnVLFb+jNX9G+j+/2N0HahiNTJgTamucs4+lKabbqk+3+isAAFklEikygoJBGCCZY5TrKagQGaxkA14t+UHvR9V7fd/y7Mpf//L+qdBa9MbrFvaXLb1qYlIyopTF+U8JSsj3PqfbYOKnbl1jlGKFiFlV1DSEchm2YIHxm7mHdLJyPr++g6OjtkovbRmozehvNSql9Uuq5mUmnBIfJBhK9q9JoITQFWgk2eEAZCZ7m7bDRxFl9eCRK1TptUvo0u68r1PrH+zG3himTdJvRmsFclt17/0/tZH+j9Sf1+jUY1Et90dCWGUsjl/T4Gd/9/lkxBTUUA//pwBLxXAAACGUDX0SUTnEHIG2oMZQ+IYLlhRISycQ8gbGiUFWYIAAynGi0InwKRt2HyFbRKd+ZIA4nLoAxKPUp9Q90belUQ/81zL++30W6fI/Errr9ZL2xPfOzNUEJIh4bWF1G2a1b1IAJ2ntY5ADVhWHJ3xu2YmWpjQSjtoh6M37WEzCiT3YqqQQZ6tZW/O6x30b2MjqmbQ0R1tRrp2mTuGDUp71zP33rcCACxcjaTFnhwkYgdVTaPpfLTDRSmMuR7aGZpJlc47N1RgG36lda9tLdaW/jUfUScWJE9R9UijtlH53rNiZ7l6iCqkgABOU7GmwmeDgR4KtEb2kdeuDYtk4GhHwPVtBlHb125z7cqYfL21eutKt+7txp0SmvVTGqU1BWR9kKiWuCXDLKf2296YgpqKAAAAP/6cgTJDwAAgg9A1JsIK5RD6CsKMSUtiNkDZUSMovELIGwok4juCbjUgmVeLleGv2CYJfjF1INmpbWpRZIkEp5xwVvf0ojxrMyegkEaLrTf25LaWRDfkL1Kt5f9PNv+VvUQddn1s+rWAAExUjabHHkI/4KBou9s1P9rimsnG7EHS6DjRIrejc7h0Y8yUSjJ/XbW3/KM6DCaW/kLqOpr1WtcoLc5vGuDrno1diwgAHTmkkgg4TrGHrojxWoBkfiQ7c2iTEHxps9uJhLXpPETPVru5vM1f4Sf2dzXmW1ZjFphEUS/dkiTDQgLXsWwirdXnDFJUBi3Y04KZCQowNCAqiiztBcY6cCs4Z0QtwfVCsOnVROvTcOh9O7l0QtxD/r1ZCt/6JRFol/RdMoi+7wdIrGWKOy005MQU1FA//pwBCD0AAiCFUBW0S8TJEQoGxow4kmIZLthRIizcP8PaxzHiYYAAIg5bbsA8NkAVqJfRTEsudY9zzHXFs9iNjjrka6J3nd5OsgXo85t/t/2r/Vn2Dvcz/XKfRXjIydSP1AO3ShPbniAAALFyyKQQNnBf8aWjH6EeqANJofmjNWoi2ZNtSW0MLZmXMqzN/alHt/w4E2oUxEm/wq9/fynuTAxKlqbiY8wArHYwHQnSlscgSPjo9GlXw6GF1+SDXPZEPZ+XxB1DClV9eiht9JEx1+hbDZ5ViH+iNwGc8K6YWQInnz8gULPQqDTq7pVVzouEJRpJhCH4DIPMjoI4HdobZvvbDqRoWsGAVZ0Q6Mh81XLXzflTCqetarcIu2tlimrDoPQiKPZqjzS/ouv7Fns2mIKaimZccm4AP/6cgSt3wAAAfor1DHhFKxBJOr6LCKXiPTXX1SVADEWmqwqklAGACN0oNYtRNyGNj9ZRz+xrM8sHSQAHIL2FH3VPGXbuKJMY2XLZNQNZ0W60GZzN9evt7wgqsN2TuEaFhk9ozSqAAAmLekUYx+BPBFQD8KymJie2KjtEzpbqEv3KNqVjRE7eB5dgByo8z3Mz094IlrPS4qNcnVY8iiuHzgKjGNSzNVACAknHI5AXbBIT2mo3sWMuMUgNDs889Q691ecYsxzyB3eiWSLJpy+2//+pjIv5z9T1Wqp+xC0OvH8eEgnMtIJS5WHui1ZdhEAFN05bJIC7ZoGpkYhRx7NQ27UFhUVAdTAFmTOJsi2bU69Tne/Uczb67tfdW3f9m2Md95E0jhjBcPFiFlzyp0/bXfZtU5KYgpqKAAA//pwBGhAAAACDixdbhyghESkq73EjJCIjJ93vDEAEQgGbneKUAAAgAgAAh1Tzcf5/QBgQ20VCx5iLYegbPchAGZX22lU2Re2wuisZ60U+//4p29OowTnaX09ZEPi0TnzH//f//99DqACAAAAW5Jbd/7ndAIBQqVxCWYlEknvpOlvb0eFxaxBFJ7NGf3/Q+9nnVf0/655kUEI26FopfT7G3F3o//fv//9L0IcYQUjGglJEkAStCBuEmLWfrkxWwop6vl8jGcqUP/6a6HayS3Z63VBn4hrZ+GYrP6kKWZQ9DtB566uM7qTEGYDEzTSD5YqQAQnce+6ZAJck8QR3ndLKAyNxgFk9K0GxIg/6rVB5DO16OIayLTV+GYrCzakKWZQ9DtB566uM7qTEGYDEzTSD5YqQTEFNRQAAP/6cgRM7wAAAhU52ejAE4A9olvdBSOAiIkDaUGUUBEboGxoYIraAANABnyTAJbKHnZpkx5b/WCVEH26mWhU9Hb/zfMb9elDOTT/qAtl6eZzaCqCkDuBlrI5Z2uV91+udrKxCo8FQkWbSQQozJtpIU3KZWVUVOFWfQltvUvYCEi2NzbvLYiPVLqUHTivneyodLQoewMtZHLO1yvuv1ztZWIVHh4SLNpAFDeWyJNyWCYDipJ/xhqbEEibJyDIoyPPUldH+f75Wo+m6aP+X0N00b7l1EtX6ValQQolBrLa3KX/2nkhqBiQ8YGiuTABCdytopMo0CE0JOVX9tn6R/1TFhHfdb9tsRXtGInmVmKzXvDly2WT/K3QVzPRvuXUuv0q1KghyURZa5bq/+1kNSzVqcVyaYgpqKZlxybg//pwBDUFAACB+jXa6QEU7EAIGnI9InIIuN97oJiwsSGxalzzldgAAohOOyNEpxZo96uh0Lzrnv34wGKhBsqmrQpgR438IPQNZf3irxfgpP/YRrRs4dQ+rrOMJ8BseCdHql6w/Tvb0HzuK8CiPpSl0JWQ1c1kbNtD3Jqki3oudAHtvup7BpEfa/Rv/Qv7/339v1Ft3//V+v/4J/qE3J/kGs9ecHt6CQimZPt9I27Y1I0r4pOvhv7prOCwz4rS2qnsZZHuXzvVu/+p3+VeimKx0/aEg6ixw8LPni+cQ6vUuL+17n+LcotYILRFrgm6kguXFqEnJq2J4oXFgtFla4loHO9pWaIN92Jt99l8wvsu93Ocdd5//V/39RI/b/Xydf/wh/b0WfnP19fpqn6duhFsJtrqoqmIKaigAP/6cgR9QwAIgftA2VDhFPREKAraMOJWCHitY6SEUyEUiGpM9iBoAACt2xpRxS4uEtS4iy1uagPTG3QMc8mkHlCG+ZyOwC9FqV/vL8v1/+nz/3rMbFFFzRDG6W+hTa+WfcVnQ1ISwAAJTbVqWoZuG43unOX74/Ato3CA+iICP0YW9pkyMvzb/0ZZG/90k/p1Lb/6voIa5f5XfFXegYZTO2LaQ2toIopKuX6AIrPLYZMnEkQRA87J2zz99PHN/mke/U4WpZXd/+T+f/yqapfwzqTYY8s+8i2pdBUfQ+l0gI7UVpb1LSmxpBIHclzAslWxkpw+WUWhCFChpBUOtIadulSANaVcuEd/8QdAiQ21tuYoGPI+sKAu7E9Qql/OiKKXu1PpVKu4pGba791Bt4CwC4K0rQmIKaigAAAA//pwBNoEAAACE17Y6SEVeEQm+zoMIrWIhQ9zoaRQMQYXbfRwim4AAACO/byGXWSMrtGEbTHrf3WiQzl9Dzv89+dUbLr9/IvL8/nU7+ZfEaNf9Z5M/SjSo3X/Kmr003+i/W3f9F1vBaFgSB25IyCnRAznAxtlL+DCOcYGAfq3BXg1zeK/WPYYVI/NUUz1YbFfq/hd//M3DrfypApK+weMY4YM6q1H3pq1Xm0NQRABKIss1zbbmRzBExEpeehvDg0jrttkhNFZh69G+dbUls5f/r/R24yJ9/sRSqe4RT9rrMiMLo+CdKG3x6/VoQ+Y67iEkAqShJJLYinLF0Zz63VTtFdp4oA/dSYGFzev/wOx4UfEx8o4VP0N47/huWX61KDRD47CI2bF0HZJbWSsULnSI890JiCmooAAAP/6cgRxsgAAAgZBW20MoAxDRbrdpJQBCGiBdbgxABEcni13DCACAAIABk1kRTksXcQ1U/ygM/g71WrLYYVjIVxFmbUW+X6fVPnpK1P24uv7fX11X+lWRBayzSNaS1i1BBc9kt3XXkQAAAAJLNGm6UEIDEslV56ukko/MGWu6AbHnpeUYqEd/9/pq6qnr/6o35+NHvMJdCyVDVhT1ubp4BU77hR6aaU3rscYABBIAASet+32ttt2uwcOT9PuZc02kmdX21aLNcq5NikK725GtK4hxLqnsOHD667DzlKu2IxK1zGCxml0E6G/0N8gmsAAAkEqNNvWS1ySSSW0HuKiO+bub/jsykQ5iDzGs10yroTBmMUi729vVEXX3VqW2vPXIxKskh/odF14oQVmfV9vovs470bNiYgpqKAA//pwBDV6AAACHSjf7jEAAEKkjA3ErACIkDFgXZMAAQ8H64+0gAAAAAAAAAbDYbDUCgAAD6RGZcGPuHNW7dqD30RGaf97b/l3FyADB9/uWfUhwFwHFiJ/7ihmKCgs5NVP/6IYcDEa//ubSAAAQCQyJBIJAwAAAABsMJygR8CWK8s0/m2aMJ3xRxh9dL/klk8dZIAaBC/5TPskkEMB5E5/7zQ5mhoO5Etg1rjAwoBK8a0OPH8OX5aAie9TXno3VkO9VsI5NrjjZDhUmSIA2sDjqEjmFXLqqEMYSFDqDcRgsMApEqdVdvm0IQGo/pR0SU9duASbkgXMMMjsEwgqrQDQ7Rl1LwjNLFa1BUuwQ7tmMJjgjZ95NQsF7QpMNEAhJCobFD2oCLYQte1CCN6/htNTl6fq7U6ExBTUUP/6cgS7IAAIAhMfVyspQbBEQZwNGCY1iIQzZ6wIxWENnCnFt4h4ACBhkQAqkHkoqv877uRVVkydZW6QjCcLLBgqvheLdbbK2ZZr27Nn4WvsZZZkyJHvPO2H546UBnescs+ppBDatVTNQCSTdussjTcBERs1rSkjMn9SrNOPCEQkGgVBtKCI4aoEjZKvYHs4G06Ba8nhn8nFVOdQKqWCakJ3lFW7o8z2/vQPqatQGktdlcvEZBUsqprUlIQVHrxxDRaHBk5eKB06poNhrInalliDStYTDXnRC4WJTq356WU/iJ8UXKv8s+qSo8JO3llnVjA3MngjIYElBGRK4ozNIlWPVVHY0MYMM5k18NnXKJj3fc5xCE3eyM139f2o/syVKv39/fBts/+bxUj1B2LHs79n/Z/JJiCmooAA//pwBB1nAAiCGQzYMy8wDEGCGpZvCQUIaK9UbRhOURAa6w2liHoA6VTYvM/xibLDDApW6hReYXY37j0GorgRG9dAaaKpDmfM0PCzRCG1rLX3qI2RcZEt+KhpCjV4TNvI1RVASUsVLTi1gCBmfAaInjngk53IVSsjh0EROsyIEZDyAina3KkWzjn9PTETmGmTlph+AYvwFIBKXj6grZ1hiSTfSNQsO67+439QBSbkAjyp9Ns79lO69Dtunn901We2uiu9lVAbGSNZa+rzeq6FIRTvoKNzL5dLu29ECoNE5BD7lIba522GXn2zf0qq0glNyUDMd1la6dI2FuWEpFhbnrTHf22Maal31NrooTJ7c6PtuRrpsm41CuQujo8+sxbfyWBtJBAa91ZF4Z/5TFaRzmvSmIKaigAAAP/6cAStNAAAwhYUWtIGKCxD4ipjbekaCECXZUwgo/D1hmpNthhoAcBtNxtyAJ47Axm2RlJEdM4+9SBmyGRRl30ihkOlFuSkRIUAQKcCdQNGwu3UxZUIu5HJVls8hwkWKqvLWJVm5UinqABLewGccmXCYYbuWNJrkrjImPJDjSh+GdQ5DiGrRheofZ7dyZPpFwwfLmLdb3LdzJsPPUXJaA8nkV5xnih4QMaQO9tAAgMlFtpwEN1uTMpoJRDWqmdL2bXgJB0wyc1PkRjf63l5NzMqIQtyhKIURdAhdFSXrLV2Y9DZ1UbrqQI0rciI3Px6Bkt/ANjRZBRRYiqE86CIxUKEN9R2k0QcPuQMQ54mLrbQYHRVFqQu4mH2oStjFmpEVB/mfZiFSuKj7f+z60xBTUUzLjk3AAAAAAD/+nIEwmsAAAIVEdQdbQAAQcBrKqwIAYjIOZG48aQRDJ2sQzCgAAU5P+DCa1LhyUhoMXQvN34En3+5yZfi67qeEwRgYp6a18XA+pgSzkgwbBgNsFV1kCY+bnXxhrSiCq2qbc4q6mrs/2gAAummm3AM5gSbt43BEgJiSFGnwReJBOTPvKg4ZBhN6rkDWgR9ynubQldyEPHAzPhioSpelVx4tz3nrWMcsscUkuAAGBQG3ZZJbdtfqBQMPxwHTTfxqbfxrOcxInCvSjCQC8+VQOeH5aecX2G49inTiXCWlR8pGoNhmT2qkw///2HP//unz6z5qNOgLLuB541R6nLMIlt6rnTs7gdChaIsZvZsWwBAFT/EIBXFt//djxYHn/5h7x+f//uYrse///48Mh///hg5//92fTEFNRQAAP/6cASdNQAAAg4a3J8wQABDg6xt4ZQBiIyBc0YgQVEILm8olYjaRMQKTWj85B+407+2VAXd+fuHZZ6Fbs6GXvN7o04wrESgKHV9jkdGGr5F0MrEg4++lCBwMoeXfrd18X7q9NinmbZYuFEJuNOoglqK4LvF+6jtI1SyqUqEZupTGMYycrULAW00SNCMFg6oO+Z7pV1ym0PnBVPcksddN9v9bdG108pZFD10hUEAAmhYS2xQqUjWCgTXkoCI+74LdbOBlkcj2f0HX1g+5QbmDARgS24aa87ry3QSp2iLwaPETmTywUGB2mo8O7d50lg1cAAbbQsScgw1Gm/RyflygdMjVOZ6EWyuVWAKr6/+bqM+YCpUv1+X8vob+oro/rQ3+l6W//Q1kM/fobN+v3llauYVQmIKaigAAAD/+nIETzcAAAH2QNq55ysUPqgbI2BFZAjE13bkCPHRIDEw9DSdTgAgCCKCBrOsPtySN5eta0lAxqKRLYVq68R6/+/qHoifGlDrkq////ktqKpiTthf+j/9NS0UVFkgWNLFf//VpCGGdGLBJNPzS37m26KvxtYwIOQJjEFJUXiwLUvfjGoM69EgChMeK8b///tt6l6OxRrF/ob/6FbUdlf///p9BI0hd9vzzaQe2NToYNAfX14zSE6dX06Or/anLDPkdPI32o/aocHqUo4VIGXB6Db4srGgV9F0ledL9T5Pi84K7+VHF8MUquLKEOibZu7u7k1H7nxOB1B8y7Ehs3nde7Z3+07vL/9mbMsxyeYT0dJUJHPMF4uaoIt///oXfUgf/6W9FV2SlParL/+jczmfoUTEFNRTMuOTcP/6cAQg0gAAAf4y3FFpEhRD5vwdHQfxiNzLb0MkprEaIG/ocJaOABAZEW124KV7evO0ceWaCD5j4WaB3+z5+j2+9LzQjSBpnPs0nrO++ReNBrW+CfZT5Je/+qL+S2+p+JnjAXorvBILFbSh00dxXUvqdZ4WLCsRnoN6mjbr06tq+nTa9x8WT1a1f/35SNf0wWjnRTLcTCH13k1DffD36MugY/0LZqr6wzyIgADgCNhKAAelAfLMJf7JABQW+JbBjsxV041+9v55aIH2sMbvZb6M8z7UGMbVRbOhsZmZTzHO0d2aVvlZ6viI+l4FeEjdEXBGOtta1SYcer6kLZwc4wNcpxrynF0qJ5NjA2Y+VBU8x6mV/vRjMjVa8S0mV7n4b69X/3yvxVkUe3/fXwgOJqCYKlQ7IoSPTEH/+nIEjaMAAAINH1/oZxtsQSZbajyjfof4y2bnlE7RF5ltqPOV4gUAwU42NY5MxdHnDzCKD7crsJVo++PNYQjOiIqe9eLoWFgxjHl6Vyet2NQ+SljyrNOwteVZ/9L+tTEyc1U9bgicQEAAKQksop/X2vvn1bpq3+SBjVteNgXYPajerZ2oMFtPFrdpX1tT/7e5aP7EFiG7EQNLsS3u1DPSOxzv/+9Ha2mkAJAkjkh6rL1rtatzG/sgxXGnDHNiWUIndxR92eER9Gv8RR8GQEisjFbT73XfbZPszKNoH0SKh/Ts18f92n/+0AIQgja3dl+ul91mkJx99ZBDo261XDWpHUcP/Kk8QOu3zWqgNzMRLoit2dZv0+b/HbJS/YjSj5tZWMfsOT97CqaVjr0awAmIKaimZccm4AAAAP/6cASZHwAAggQz2JmPKbA/xlsHPyVWCSjLdUEcSzEZoC5olpTWgH/3HV4rB1RvF9u9GtJBAjwhn9V2GsjBDiTbCodKHQ3Rr+gfkMtu2y5L3rdte2h24uf9AiP0J2dalkNff///vpACgNtcAr1BcsyIipJFX/zSiko0PSNpW1i3mO9Q98j6BF2go/Ev6g9kPMrCqLKaTZfsv+ibFHF34wTfDcV/f/oQXC9EWxS0Q842o0GQiF2kbOs5hu1Qv0bBD6f6TUkmI0rUsVmO7qhwe6eRmygDLrqCfb2ukxwwAgVVdLliKHEXUjHgMstq8jv+sFwxHDJXO2xakoPqRHCKZBPcDviuR+nGAZZhx2TqPmvRk1M+jf/f8jJxQirKuLP///ncyxAq5pG5B8TIcdYr2rNqRVIqEN6Ygpr/+nIEYNwABAIMMtc54iukQGarCj8HUwjUy2BnnK9RGaAt6IgVehAABSKAMybWFCKiBvCG+dqNMEdHsHtysGoLCnWMPhPmfR44vUso4/E+n2NOx+Ri25TEZ5BWjOJcZ978WAFwh19p0AAAihrJAHyNhaFRfCocs2hQeKMI1Slg1SaBmSL8avUZpEYWPVO+IRyNTWtO5E3P+iW7fp8/ulio0fk3MPlWdtQIljtDeoqnMVuNWjfUNDgwkpsvDp4pxlLRVzXyp/yPoGGPbMYFBmU45UVzMjFszHYx32r0jyNV0xB3U9+tN5RsQ7wt1dyOkAQHg1dr+HXICql0f1JWDPBpLKi8mWrQ1nO3WVXpot86PLH/YdZHS9521t0beylbmWyusgE8i//3kKrnEDlblT7K7JWLEtCYgpqKAP/6cAT8jQAEwiJAWLpGL4RCaBrzZOV2iGj5YmY8qpETICwM/BVKkCAHNMA5JF4YwkVtVTWQUIbL6iBnrkOZZ/eneRnLJ//1EXt10lZ331fdf5dwaDaTY8Ncs5UXANvX6t1o29UEWnCwe0pBCddAzsW2YNWlD13aPeUdcgh0a5WidycDqjNqP6BYzaqlQ9PLFEJx/9dE//rUiOVBYScoij3B26+v+xnMuolAtG9QAdk2DtVgqP+lpF2UggqCugopz/UvUe15eJDlhEN+/qAXLsIJrLvUjIhbO39H0m5+Inf35/+pxGIhCPzjsXay3rML0BByy0TTwyyevlK/c/5GzCth6EvbabUo7FzvQZwm2JqhLjadSXrdRTr9Lsz9/vdhUVM5g663EGiIs3+So6vrR7xjv+/VUmIKaij/+nIEuakACIIHQVlRjyqUQygbJy8HUIh5A2VHnK7RCplsHYOZ+gAQCAEumAQ4oh4Gf4hQ7vswJpk6ExsBpDJFHmwmO1Df/UC83Adtq1d7UeZl/ZBMUQqFGnjhwrwx//v/dW5R/ZVWAUES67BU80WLp0S1NqVO95Qk8EEdCNUHND2zeJxnX/DFPYx8cOaj5jTdtfogyarnjxGqGKyhK3Z+bm39K0WU6P+76gDIEuvAa6x7EX8ZQd7Xggk4NkfjFMoxWawo4QstA3T20S8vDr59EapprN91VoiKulSuRBBpiARP/v9SxjFnYe15JxSsIAc0wG8sXic/k1TvJl+b3lVZEWtJc2cnU0R9yHFbrGgzp/KAjX3P5/7Odo3T8+YGy8vrJVqC+w3ZT63DVEESgoptiYgpqKZlxybgAP/6cAQKcwAAAhNAWVHnK8xDRltKPOV6iETVaUeVD9EUGa008qX6QAAJANxQCurylr8LlHfX0EMZqqxxxVsUnztCZYdUFA/TauMCNr3iD3G1mZr//pzI8WETow9MwHb+i7X9qZIoxkxEYAAphK73gY125pkkkSv+YgpqV6igVHOOTjydY0fExLRu+Cg3jy6xja3QQSZSZ9mbtjBcdiY0ysYYbB3292oJ2BxMp0ABAuALf+BjcVJpb+CyfN8DecPB3cR2CdHEeJ+/9+DfK9aMLrQdXb9PmuyF0KFj1NpccGQiEVvLOjmrB5TH6p66IqSQAQgIAbvuB4knWr4bIvvRxE7j1evcuwnGqU54DNiTf3xgtLbiLbOVBJqIjWrZ21udb/1Itd5sdpMRPrdecRpDpgW+Zm0xBTUUAAD/+nIEDmwAAAITMtjR5yvUQAZbKjzmfoj4zWNU84ARFxlsap5wBgAQWBFuuAknuYcbMrwrf8QROU1hidsw86MJDqwoXEJPlt3R06hilrlCTGRTIosWa/en3ZDiukUREIQlQFfCFf/xbegAIYBl94FMYhpeDtsR9Z5H4Qkh8R5hpLiaYUKFjlA9tW098TEL22Jc7v/b27y8KTZvY3NSmf3Iv/zwqXNddyVoAhCCbbuBZynIGcETJ2L+s7jGBN2ulBqtSTVPahYN4+GG20XKBHW9DxO9zDmZi9EOT9v69F32mD5eVOmT2JhZIkOxTGMErxKAAAQAbbgDZHyws2L4KbOOnjICfj3zUtVCtyZOYYCIbPPIFseLd5gUH3a9VJNrqzIxnT1/vql29Dio9BmJtEoo6LNZRHqQmIKaiv/6cASzugAAAh5lYL4E4ARErKxzwKgAiJlZk1gFABEUqzPPAtICABAAADAoFAoFq5EznHBEXun+n//////n///zxuTADEv//+NDSY3Jng8B4Q///8geeNCFBpY/////93MIHoYNxuTQaEAAAAOBwOB//ynX////////////j8VxbAFiHf//8WDScRY/JwbAvBp///4sEZOIgaSgsMxH////+5OQEhOhIIsRZOgqCwAABgAAgMBgMBgMAv////f/+TGER5r//jwjDFgwC6Gv/+PR6GgUQXQiIkj///8KElcseKxh5IaRf//+Njx8xrEKGlzmF4AoGAwGAwH/5f///////PzJ0X//JQzDqwfBLiD//kqPUPAmwlw5EBpJP//8TUouaG47kC+SiJl///48ky8yLGKBqfNmH0D/+nIEtZwAAAIeGN9PGEAMRCOL6uKUAYidcXlChFy5DSCvtHEOfoQEuFtG2zsNkDePfcgl2t3bAj7e89XfTRsOxNn61rQDIK4vhqdnqQrDU9qeKDzuzSgcDT5lZbyJUYTFQE8NgFi6w1S6JEfwDGQS13YquyCOFDmYeYweaEgO0h39av2/FE3xIYwsK6ZJoCBU7XlXFZbAzSp0tyKiIl1d5lKfZ+sc0OlTQStiV1EAKTAFRpOoWRoicMUPUAzYayBJpufxskr9tNQ1qcd//GoNvL5VxPE/5gLJvVfZNQrL5v/7LSre/TL1NepSlSgF1eN5+qoUAC2mEE23KaMSJZJzS+NgYcXIYRtjOYn0qbm/O31//r2//9De/6KMaqPnZ27KUOUKNXT3RgEYBwFES87J4lKT1vQMTEFNRf/6cAR0jAAAAggl4WkCO7xDqAs3JeVEiLEDbOWUTZEOo+/0UR5+xpBMThLurtzH7ElQ1Y7xvA0F2DzHyJWStE7d6hg/vJ0Pb+sluWVJbi+VSZ8D6+SFHRi2PWiUU4URxiHr/VTWFuuBAAUjklOofMUIBUTqlgrxIxU7mlwBHxMW6cIQHZTPiuj4AA++71/2o9T5DH9bo/A8tTbfT26f08Ij92LGu7937e13dAEAtuW3XkeC/Fg7nqEvwSHQsEwE3JnWsSZ5k5/oAqU6YP/21+f9PeT/7EFVTuvlLHLaHc+Cji0Uqi9ehzjuzftUsEbLR9BAIhkCTkclCnkal43C2MJhuMqNARqqyUZsj0bQfAyb6a/+TVNL/t6ijdv/0C0p/roFSXTa/cufk69FzVHe9FQQmrZJMQU1FAD/+nIEaNAAAAIbQN/ooRWsQyb7AxsHKAfgZWjmFExRDC+vqGKJxoAAUU4iVa5Ko+7xl4Ngw7HY9kRcgyrhZTV8su19//twtaqn0MxtEVTPf7w/hvTsysxaBHFyKFxmgkpecVpc3fvnnWVAA2S7FLoKDSrM8tyumtWmyHrb6vOo5oOxhRqDnTuNw4JmkNB7KF6mLzI7r/7tVvT+nqnf6vPbQm9/Xnkf/87V7/n+iAIAUbksP0QhvK4smzIedcFbxBcBXkEan7RMnQTp3Gy1vlYhQPbrdvNfdcRZr42lvpXZ0Heqw/IyCBPLONAyzdQrwSpHJmXzE4ODZ1fiQ/ONwjoEz+stWbXTkJ9P/5etrP+i+Cpf+1W0er3/ZlqVqUlZK1qZa0/1Sz/emafp+a490WTEFNRTMuOTcAAAAP/6cAR3aAAIAgYl2zjsKTRBw4wdDOZfiJUFYmecT1EalyyMx5yiAEAjZLcrIIQ+hrbQHM+KMo+EMQP/eG0bR+8aBtu3/0j84rFyfCHlfI6n6gEowaRRrHYotT81xag+4yUH3EVkCq4yUiG4i9Y7fWufRigHxwP3xo9j8gnWkqzadsRxvtgo0v+TW09qyv5R88T7gCHBk71IMWsJdfQlZ1AkkyIeE8Vg0eADbck821Wa2TNYViMRV+QtK7d0eC9ooIZnjSBSFyHbK8MNS1cI/7aYNsn+i+My/fvJtJUlvRXq6BxFMOZIn2eQ27XdtQAUblsNNKw3uNNAeUuWZAcQzuxsUwImy2d9xxhOQLVI6tiIPfXR/9kapDn/0R+X/WcW5tvaIrvQfi0gyOURsUKeGXQKLW5hMQU1FAD/+nAEBBkAAgH9QFiZ5yvEQOMrFz4FVIhNKW1DFE9ZGqCvdLSJjgC43IMRcDdjdc3aJTo06C0x6qyHhzoKBfn+NjAdwqSardowLbY6Cljf8r7NZbfozaC7//unTOn3dt406yzQI0ACABbkkzN1KfbWD1jz1I6ZQQREpUEwdEj2cJExFedwHhZB2TVsBxXVI+Xor6nSe0QLKl3CEcWSa5Ms7+n66bf+mEAFNdwVfAaDwlLBMcQvnh6B6gwfk8bJI7bfgz14v/+2jf62OIZp3KdUI8vSq8kwr8zNTOyejUrragI6TtKLP9uSqwAElxklO2W5At33qr8G/AuMtLDM3kr5Aenq4BXyathDb4Pv/y6vkUv5kJqIX8tuKLy0Kr+iKyOsbaHrp9+Q6E2K9NJEopikxBTUUzLjk3AA//pyBDoTAAACCTfb0SUUFEFG+3okRX6I6Odg57ClURclLSiVlKKBRpgU7tuKfzJ7nFoJG9PivdRj2YrlB+A5OSWh2ofV6q/xWn/qkO2/+vxJy1S+6Ho11DD2C1abmtlvQNYsZtI1zUIAKgFbbcJZRWUlQ7loviIOeR/DPcfP6OPty69O+n/82DEb0b2GlXRRDOix96Qwysg8VOM1zBBVOqt59Y21R08tQBACpJcPPsg5WUeSVhJTaomvUMQ8aq9KDw3Dh9gGIe4jxDG40W71DN2/5sRao7/Yz6OXLr/ftKOpkClYqiAOZwjW+eWfJJi8AgCgBy7cHDdDZiYoBKmjTyLwjlKWTUG28Kh2jcZrxN99W2/6cd/9SdNZbozylgw5UOxooheju76R1a319bqws+UnyNLzaYgpqKD/+nAEdn0ABCIIP9kZgivEQOgbSj0CZIitA2LmHE9Q/yBtKLeImgApbsN+o4DaYaV0i1LAWdmDF5ccmHbYGUVI3E/hn7a/++n/4w/QRnR0ehEVJehXRZvZ1ZSjSLXULXDihgtonn3OQAAoAK27jE+3qU0KYqt4f75VrNuGvaoDkzorZmDY3B/gBPr2/2fTn/2Zui/2TVlopsTfy2q1xrkfN1KeKnTrqzMABMs2DMWFUbay8jw/gLJPRaOTNyjKXJ5j7NDMTGNXo0oEeZa3/6kztr/h2atGXXa/Xoar093XY4IcqWFUruFJ6tzGv1LYEAO78SujjFAU5YpmF+Nl/CAOD1GqYj6stRtTacDX6f/2bJlf/dsion+2DNcozdi82hUqJm0hxaEJGjVqZhXpTEFNRTMuOTcAAAAA//pyBDcEAASCC0DZuYcT1D7mq0ol6iCIUOVgZ5xO0RygbNz1iOKAABku/GuaBZT0EaOXrKyTOrBnldzNdbxdPZq/wm04GkO9/7atm/yq+7/390VXSehUd7LLNUCfrKGQIQsFzzpnvWAAARBW23F+ygRowisLRzCSHWPG2VeduR6yGsXlmr0bHh22v/6s9BNVjj292Rm0VGpdkfyR91GYsu2/FM/oYsAKW7DE2CsiyJBrcsuEqFDKiYRZemjRyiC/pWOQxBUO1TatUT+bK//Ttl/XM91BT6bU8mhca64OjVUoeua7LMaELKUAF678Z1ddOOVOMw7nuBYMmuOePHZcifJBs1V0GpXbJ3/4S4ItiI32R+iemnIgYiUTZGBaOrsQ6AxgEku7EzhV96twZFFkEpiCmopmXHJuAAD/+nAE7hIABBHTFVk5gSukQwj7NzziOoiU1WTmHKyZEhqsqJecaoAgFS78QV0Bz+oS2mVhNAnUynWUKJA15mF5Q/oGULYuiR/ITnnJXgBczMJCYFcp7kmHWEYCZ23ii6lQOAMu347j0xHqJ2bQypSgHAkwithsYxVv2WOys1QDQbAxFWyNQf9s7aYL34a74UiTZPaYuhZN/JbMhyuu/+MxO7zlMIAd2/EEegYV626UM9g1BFXUGYrexPP4WpIw+gvpoGfksb/zZx1D/kjWba+aQp/jneq7ttp3K4+dwX6/8N2d27lBO3+gQBIQALJOgIkHgB/bU3qHWwsjlVVP6hVULCvNXlXGu3H/xof8/v/eugIXKs2uyP6ev1PcrEx5ht/IpmxwcdsPq2kVCBykxBTUUzLjk3AAAAAA//pyBN1SAAgCIRlXuedDhEJEqxc+J1CImGNgZ5jtEQwZbNzyieoAIATt2GJ4wpaCgGGpEDudqZwl0LLxUvAR1TgPJ5gUtO+8aV73jAXsuVnPyVmu5AkcgUgFaX3hxaMDHi9Sn+957FGJy8BgAdtgJJ5Goj9gyR/tlKZOBHDQVhIwkFyGkHolXHC4RUZ8afi877f/OqYxQCsMPTCL5I6UgiNWXeKIKRth2Va2/UATLthl88FsVaHkzQ7q9zulh9zQcPzk87+ITaC8JcX46m6vKg6cMOBxXzhRYX8UWkkiBA0EDLnAGTZA+4PLpa9/JbvSxwCgC7vwMWbJTXV58kWeEdvvQvxEbov4SnOLEI6EUeMhGjPn14KXTDCG/8gTRzw9vvz691jGGYZEBE5BYQOYJ++1ZQ2mIKaigAD/+nAE/0MACJH5GNi55hNEQ4XbNz0iPohMmWlHpEfRDBlsnPWVowCAAS7YZ2tlTHYSxpYk7AwtsAnol9PnNR3/GJ5RgHRM0B2BFbJ6KJclxD9qqOtgAnsVA4JcwBJHrX5bv0fap4BgCrvwPCXkJPPRzEmDJsnjAZDntrONuTvDmTqFrNYndBX+r0NXaQav/QpKCFMzvpSvxTsaVHA1DQBaNB8tU0K01VxhEAE7b8fTanrEAXTOcxtIwDwGEKJv1D0keGD/rE1H8Kv0Lz90kLq0F/9ZqKQGIAzpVSGoZUfuTt6TWkltbd6CnxUWQADM4fjdfC6BLi5F/7qlyjSjbI3mvLqKj+pqkAT6Fq0B3pq9zr2oXb/l46n0vZiat2M4RHk4SgzV5ia/3VIZ+t/9O0xBTUUzLjk3AAAA//pyBCuJAAyCASLXGecthD9DmxM9YmiJFMdebD1FERgZLOjzibIABy2jTqgcLwJEDYRZzWklb2MZOFTj4c/i2Wo+6z6Xygc+N5aSYQl6j2VeMBV00f/43W5Nnm9E007UuQ+79H/1AATbgZmGWW1GD6A2UIdp/i4UaGwdrhK5NNIcyYLqjwXyKdYC2fV7gHTj/uVCRemTAjzm9hWmlbytYrF2P4slYAUu4Gr0cUlQkLTOWRAaLKSclQJlZniaY6YLg/UrF4PDiheyE8vQa1KtOC3oUzjpG/+UfSr/Vd1d/+TgAWpags49ejUqXaomHxgALv348Usz3PowyoMlXXhX0fpTRcMO51GdQ1QvGheJLTeW/Efo8GGqn+Cf5jWzzM90VRavtjb/mszFpmzHCxUgaHL+ZYxMQU1FAAD/+nAERpwACYIRM9k55itEQCOq02GFWoh8x2TnlG/ZEIqsHPEaCoAAFT/gSbkKzmKax0HBuOv2UxSvp/9712UHf65II5NbGfH9kGhItRjTilX/7aVf71RXdpt53pIw5A5kTRJpbMW6NgALtoFyysCnkUBRtYsLFKC2mgSjhMG0SQR90rdi+xTPpgR0WG8gkgYzYG/ARZ0SonT7KC+t0s6hcCwj3GXEP/rYCTbOdjLhAPzehinuzL1UWhe1hSIoyo8iBtHiYrCA9zPVMY0oK+jVHf/N3ZU21zM4yS2fm4q/2ELZJimB7HPe7vutbZAAbvwMbRCOG0TAfY6CS2R8e8IJvC+XqJVSxY6Axl9l2AQzV/yNCGyCzyfFXiESTmoAvF1izA6Knkx7A2ScmSBk1cmI0xBTUUzLjk3A//pyBMa9AAyB9TrYmecTdEIGOwM9gnaINHNgbCStERYWLFz0naIIp78Dzwy4TE3HpkNuqoXPQ1uSCLqc16F6Og6RQDDGJvlsnUxH+odv/r/+j5b3U/NKCFMVc+77NUcq1KvqrMAgTbgecbAtxim8JChh5taHOMYmBCNbIZ/J/2Lt7J7W1MYaIoAwugLRrHPVHxv/001X0k7r9jYpCeic0yQNAVmxLUBFTbgb7i3e6SLW276zqklgrjPozYDEd6Q74Hq20t7FcmPKI8aC1WowMW96iv1PKTWVrDpjFk23U5ZIxIcMT8uXUgQJF/4GYxZEogneTF+Q7ppyiDKL/qAQ+Qo243c7rLXFGrCyQ2L5vawQNSSov/zCbbJR+k5WQvKABQhORdbEqGTpB3Nm/FExBTUUzLjk3AAAAAD/+nAEUVoADVIRGNmZihMUQkWLFz0ndohAZWhnmOtREROsDPSdogQr/+N7St4DB3EtZpsh1K/ywdaMKkGRkBVACQYyE76cXvDf7lR6aTSh4viwliMWY44SLI1POJQlp08dfSssYsax18AASb/wJYTEkEepV2c8SAr2WrsWzxjiyhU757fBLE0yTEhZI4vkfwjZvnf/KLoWn/roxZo8cNFwdVI0m3U0Uxu1E2EjvrCdeBtJxVoCqw1LUthZ3xPPCmU7d4Vze7Ppo8qERZZFb/iFNlzyKyuxIFEgKuESzQaLFqtJStprW85xZBgsvcCVczIslharh8tiMXbE9HZRJehHPYO58gnKSmpkIs6hapfT8KF5/Rdv7HH6Hqg5rUFCjhOH0Aq6YQqxETjmJPvdgIAqWmIKaimZccm4//pyBGmBAASCIx1XGwkrtEFDaxc9hyiIfLllR6TqUQQMq8z1nZIEGbYDVnBS6MgkpfxwkzYk+rYZcvp+PlanEoju8KOSFiiWB8ZFtrVXeo7V1UMbTKP/JQzfkiTBg8qtyLgnb6NxXUL1rWAwJC/8DxT5U5arouEosvE0q2fB6N6GoLRmjFSFHjUMtLIr5XR1hc1zXL3/FopPSqiK0xATKnVMFGiohdOTfrazdAAGpf+A1UqbUEEZLcfj+6CbnAud6glxtjlBhcqFWj9BKdQubY2qIvxOLmVv9C+Upb+6PtLlkpDRFa1qeiZuue8ohKaQCZtsMRS6vyKjG4iid6NtJPQN4YNHL8WOzj1QZQugkhzWvq/dXKl3h9Zz6Yh+VfYpJ0MQ8yuy1rZfS4Js7dnbqrTEFNRTMuOTcAD/+nAE0q0AAAIUFNi5jzjAQcMrvRgih4hQY3NEjO5xGAzsDJOJ0IABmbS3Y++Eb4CoWI4CTtqcYXFUIVKE8doOOgcmVCrxCXd8rVINW84v7N6uHy7xBR4YUCAIOxAGMQO//qBD//ygIOYIAEZBRciThV89bt0vD80dSYR+OVuvqTvPl0G0HfQKOYmcRLvD6gwp3KHAQBAMagQdUc/8piAEAxiB1nEHlBgIBhQAqS1EQS/tkkAWZia7KWrHsSP8TkHhbzS8sMqN80npwniqgwwedSz4ry2e/PV3P1pUeTLaAMedr91WszLKKTw+iIgFYlJaeBwG2DqIQi6wpEDy5Pik8K5MkUFp10cPCOgp6/wYHz8edTf4q7LYdj78KxVTn60ix5MtoAwVcHa+11XdLSk8PoiJMQU1FAAA//pyBFaQAAgB+RlY0eJLEEHl270FIg+IwHFQbL2tQRKgbNyXlUIAADBkvrx87iJ+UB7HypUdCR6+7PcyzHHx9Ioae/2qnwceoubgol7vemUdSW/donstiVy/QeniPTOin93//8iQSANCyS5E5bSVdHYs/Z/D6ANCTNIH7I4N9Q8FQz/9Dft/++jLo/5vUTnfAUsp/eVUz9EO0Y36zpYsVcHFB29YaiIACa4COzGhGIzszrTFRTTFiXRVWfpYizBQp7HVXqqa9XOdytYGp2dlolgfSMRSsd63D+ZXw8lGi2c/DtiPXraWf11u+MPKAYCldvxcizw4UlAMTYYwUDNIL5nsOR0tKLuMoLD6r+ERF96H/+j6Hl/0p0Xu8zfUuUrGp61KgeWIMFYljyKpXE111JVKYgpqKZlxybj/+nAE6y8ACAIaPdiYLxBUQkgbNyWiUIgMeWVBMKGRFyCs6JGVogQrt8DTEdL4DY7W0I6VerL4tsK2+YZRnBOCB5xlCPU2uhv0f/5P/7JS+hmuz+2zE3d8vU7OCuErxMJwIJBgGHWe9sXAUAlfvxeSDgcLMKI5TeoKJrgGC0pdGiR3BUd9u2Yj11f/6Po90vqU27LCKJI8yK1rfnelu7OSmCGIXDthFrfse1qyABcuu4jYWvAGEQtJWl6ZsTwieXsZKIq2SMEXqym1+hgDF9SXdBnwd2n31rYPWRYG+t1zPY5SqaOeSx7NgQvYKIIDICXtvxzKaIyRsQLxpnkQsaB8qNo8JZxRRz3ffVtC9tv/ZVzE1W3vToPH6N9PopNrdnYQkqPBd5whAQHYZcfI1qbQVHpiCmooAAAA//pwBOwRAAgB/zhZuek5RD+G+yck4lSI/OdgdPOAEREMrSqSgAaAgSd23GNQUrkTsQHMs1ZpAEMo9nsiVd0lDXo05vvlQm6tKt/83pVtv1voht3X9JRuVhRm50r6/GuJiqy7iVkvAYEO7bhI8VAJEQtsgNNlqoIVKVCPdATIkEoq+Zql16v877f6m0Wm39d0mpez9mR+IUVO/DSbtVDnvpXW4VS5YRN22Ej9jQtgHiU0RCobg/kO5D/Ag46eg8TUuROlCBdTi05ppOitUJG11b/7t6m7bdDk2k7sya7TVo5eha62mnOpkt+L7r2kIABlBSdkgOTm95QnivvVmIw5CRKiAwI+lEPxYw9urx9fHz8h0KLGTiPTSV8ucGCZRxz21HlCgqtzGgGqrUXa1qFL72aUxBTUUAAAAP/6cgQtvQAAAgZLW4YwoABDLKuiwxQACOTlatxjgAEYFy53kCAAbExkTNKi+qthRFEuLm6tMYkjd3c8itk5/bp6Vf/+qozt/8nhZwmIE/9JPoYMExQim///+OHoABhD//8oJ1jCkFAwwQQw7JYY+SpWNzWAnFyajWTX////o6v//URGM7f/bxpwmQn/7fQw4TFCKb///4IPjhwcYdY/////4oonVhhCKxQ5AW1ABfYC2CwCiZ9ZyBHySaP91JG9DVCpdB5vpo/700f/62qb9R1OpS87t0QmOmnKRoXFWZJwieMWY77KV9nTEVDjwmSo9cogAVlkOVpBN9w9lrYzUkkFJVzGHbe/ou6GbX6GTYszpo//1tUv1Cp1BJLUaTtxFnJOETxizHfZIlbRVk80OxFQ48JkqPXKTEFN//pwBN77AAgCEEDYEekR8EQIC0olZUIIcQFlRIh3YRYgLVxjlcKkgazhsL8yicnzkT7qx8NBLVRb9Bt29M/v222ZuF0gg1X05////69vpXqJbL36Ohtnob/qbBdHU9z5aIuhVC6NjugAIEAraVJblRZsZ16OXFuGdBXCL1dR/o1Ho2mj8r9Ppp9/XtN9epeXv0dDbPQ3/KagwXewxng6hOeUe7mTJ2E6ivErrBkKJ6MmvBEKYCgnbIY0noHmPJf9FnjSb//b0qIH6w1f1Ff3/+9aqzUX9/mTf/UvQ3/nVqzBD09rc9V/u5FosRLWT1DAWgqVuWvlAtECWo5fySsxOgQPVlRdEJR/rpxH8wvVF/ZTWytQnt+id3/qXobdfSpVKkIf0KDqVJepCUjlzWIkQaSLVPUNTEFNRf/6cgRagAAAAh5e21AoEFRDyAt6JKd/iDxjYOYM7oEKoG4oNJVGAAFkIyyy2oFsJkcSdRRD1FYPKcGOqVfNUrf8R+o/r+7fbT8i+jozJ7Is4RJTnKYF96f7e/+9yX/ZlvVf+r6VnrUdqK1ASgBMbRT7oGtLKUp5fjBP3lsDDtlboMIyP82nEm+Jgzssn3/sqH9anPoE5M6pjo37z0V0YrT/yhj8w/eR+/021tV5yAAM1q12RMhh5LMLkFo7bqWrfUseE90o8nRKaRqunR4oCsrNoH2U+X6Gi5VQT8NU38sKw40Xfsqe303Cnr5mETNlcAAslKFlJwaidwiC8vRpBYuNoQqs+geKyPfvpwF/Ggjdv//fTonYPTZX/p1FqVf8hODDk0XLMBaAlX9yZ7XRU5TLaExBTUUzLjk3//pwBJ+WAACCEkBdUEEWvEFG+wowxWYH3HNmYwROUQycrJyQiZqBAtjVIi3Eq7lMg10rmAXcXue6pii0xRJxTqLegr95a+c+OkYl8/3/+JZBl/dW8K0+QOUNY0GTqG0DzarBujQGloqAACAE6yXaDV478GqY9LdD+7iYJdtL0MW1V3NbWAxW0Xr+JfoWm9X/3t/6eq/9Fp1GOeP7phFPSiL+y3fscvsf0MCbjck9iyGmLMJWML0RnYfrZaJQdoJlM22udGqjQn96xaJ+MxYVEHWC8VU997Rzzaa8KOMGDu+wf7Oqq83YssgFttySm7FmUJsSgdNKGFAANhxbZyJxWtFbN1qQz/JT//9H/QT4Qiz/0u+Vajol7gzHsF3SbBMhNOoXToVaas1r3Cj0xBTUUzLjk3AAAAAAAP/6cgRExQAIAhtAWVApEFRDCBuaBMIbiJlxZOSMrJEXG++0UQ9WAACABORyS2QTkijIB4Kpa9uUUxBe50ZdDkmGZ2/1F9KEf/1J7US7b5iE1dmqv+v9/pYzqignl4Y0HqshlXYojZo1vUAAdRKkZblx4ZcqZX2wnUUMmzKSjqY9aN174VvoJo9Pq7NRaXN+YnQBrX/l9qv/o5yFBoS+OMUDn91kCNUrUMe8PW1EBSjctuUEVnc4WmEl+yp0b5yyMmh8aww7V/UE/R//+mzX/UrccqfX67Iu1/tkeEj70M3mkfIqbVtShmTv+Zv41iRR6skyAAbaU3bJLlNSi2vhZjOI4uOlMVvrXZtNXwr+L/9081p3/W+g/+97YGvgnKhZ9yVXiVKuBZ8UWMnggOeWtYt8obWgVTpQmIKa//pwBD/NAAAB9i5YuSUTZEOIK90NJXOIgQNvQIyh8RYxrrRwi14AILkbklwB4Z+FionsMKOGEOGb5Ywct0rLGB3XqyjAFFd9DOtPq/10/z+g7FvVUrOiPQGXMLHqiFEKxT//X6WgCRLUo9W7NSYH2w7Pqlz2NzrGxgd6q65Wqur4X/DyKys29/+pv7e/833eltLfkYY9he4YGlh1R5v603L4dRQESo16ggD2KUkTdoFjNFGgm5s4YYzATljGTVsrnFX+rUFvzN/1f121V+oRTleSYro33TnaP/VFvYY76vfYvd9MZmLiTidqS4XQIAEaZblbk0tSOq5OblS7qG8avWUIpz2f9X1fEz/a1//rCH8/r8lygFM3nXuia0HbRC+p6/19ea+/6pR91MVB7yLo/EIhMQU1FAAAAP/6cgTyrAAAAh9M3NBlE6xAKBtaJOVxiKDlaUMEVvELoGtM9JWaQQJNN2xy3LEluSY8OjFH09NHCwrxUKNo/bR/mG/X9d07uv/YSiV7alldcrKn7OZ6IZVvm7PtZ2N4J7D4q5YHATPu2dUgCohSRpuJHoinlJE7XN08Qt5kMQej9Tk9Xq/I4oOAj9BEn/29+qf1+Xfr3jWtJKyr7VqQsXVZoXd+l6F+O+7pQAGQoyNOBuL1UC4BXOP0QF9FLuLzvIF+3cTvN1kt5Zj/EkbP9SORXJr4IztQ3Yr2/JpZhPChlJUVclnRGPY2oSBtB4eVALjcgk25luZiZE8OcflTQa4AuhFYbT/FbvA8kLx3EziElTvX2oI/mDNv9GbNfM/1N6olV/xj76ilO13rmH3J783t2piCmopmXHJu//pwBM+hAAgCFjhaUekpLEIqDA0gJaOIVQFpTBhDUQsgLamDFGYAAcAjI04IhIxD0QoUehaUUHYThCdpwBF9k0qV91EWr/An52s6/vJqdWmL6Tn8vtv9PHstGx8S9zd50ARJZiYDHSZsAEIS6N3/XcZZ8S6dZtceKRjQdfZXOj5S0Y+uOsAw/4wbv/XbJZuvyNxjpr/slI6orL6S1SMH/b/TVzGP3fjDRGwwQAKn23ElFYlklYgEF6OHsFvJpGUl5uqaVR6VX/ggr/6f4xxB2U8ZlMr+FFeQ970aibstVKorbt3qDdu36xR7cxvSkABpgFJJIKEdOXeZMFCyRhHh9cpCNPcIPM8x0qUlWn/thFPqb/9PtX/P0Kx07lmlRxGpBr1z866Yg9N+0UShti43ehMQU1FMy45NwP/6cgQ2VAAIwg85WRsJKMRBhrrjZeIKiLUBYuYwQ1kXGuoNhYmgCL124lhwGzF2jliI2fTEyWlAalH4jyJJai0qLvvroDfo3/nu9H6P+n3fR962lvIhFGbbSQT/ig0i8g1IlDZMkotcAXZLRAh5LgdTQO4ex1Jwh8FOTQjASqih7ATYqzI6CSMDZD/2wT/Iu/9kKZDuait+hPbWf/B5Fbn6luDPflPS+wDECT1v4PiAB5mRoCxOfaMIffInNrMI85866Jsg776tBoN8jf+8zI7pdB3/Nooan/782yLyU0ZBLzqF9v3wNSs9Ic52Xzu9BB22wU2Q4D1kM28C4GaLChFphiMWpF/oiYMhpog+3OtgnuhdRY3oQyxQWNR9G4j9//p6aJ66CWzBEeRvu9xepz/ceT9frTEFNRQA//pwBN1DAAiB3xbWmeEThD/lewowooWI4NVi5hxvsRIV7FzCibIIOSXB66I0nnaHDkNGIvIls6tLndiMVywMQrE96wwI2TCW9CnfGst5Ot1PnNz3yC3jvqzAOoLhu9hE+lQAASBFtlwVROHnis5JPYqro0jrj9f88R1Y4bvMeFjpi1F985/qRv/e38n8asSh497KVdNUGgK71XCsRjcTqK5QJJdjcH8XEBWNdx9NLEa8SEm1Zuueys90NHWdj25+nCRvkNW/tK6BWqOpGmRav+/Mx/LIvMGaKB4MNOINxW9k7swX2l0ddzzBO7X8e463o8nboY1UWohm0Yegx7Qsf6PVqPV9B6uFJthCmrf77sjsQyl28SgRhTvpRbu3l3Co5o4hQ8stghF3JmkPapMQU1FMy45NwAAAAP/6cgQVmQAAAiMZWL0lAAxD5rr6phQAiHl3hVgjgBEQnHB3DlAAAKCnG5BCpsIiUA4sEV0FXMS+QvVtw46R1JcUdcVzft/wEgHm0F7eWMKLGFO1OlNKcs5DAgpmeaLMtQRQZNBQwm98oeIgABCCpttgxYjjkwIAlnCizcOjWRZW2Og1auExC2KD9lIP+j4Rftc3/q1d1ZUO2uMO9VJozNSiaDhgs9iJNabUU91ui4AAACAgACAQCAQCCki//UR+3/73////2zD0//x8eEgBASdf/5AqDsUg7MLjRt///j555ZxoMmM6////5MshiGkzXqg0AAAAAYAAoFAoFAoFAooies1c4gaGcNFF+r/xQCAOb/iw5wOnX+xFuT/+quKCAp1r/8ii4mLkTkd/g+aAhqr/8u4YXxAmIKaA//pwBPvhAAwCEBDclzxgAEGjm5LnjAAIrG9gLRhvAQgbcCjDlN4BUqobg/EYXNWMT9hUtnqegqQnMXdQU5LKqsZiUNDwoSNxYrHmFEgq6xUQiKIqioSz1t+6RU/YSKypVsA0to//+ioAVqoYA4FQdaoVzG9TuHqHQWwQNosDCATpZiQobNYZebUo2tmvmuWKet3sIxFrCWetvrMLIqeRyRWVQSkqW0f//1KYuYwUAvu2BuaIjNlC4VLtyyihmeGncXnJ5ZHEkPE9LTNx7EFfmVSspWudLBIlBOKGkoUIYecpoqqGm0uew3K7HOqWS+oROL002knLCj4LuR+lyDITCXXaZiNWlxmgszoj1/XQaDf/ez5f//L+hb8zlCRRT1EsstGS63Oevqioikk5IY/vVYtMQU1FMy45N//6cgRjOgAIgf4ZV5MGG7BBZttaPCd6CMhzYuyUbgEQoGxNkonwAFYDBgGwQHkx2WrwZdYk1SdguxSPGbEQ7DMsEbM27tHzuTjRQlPLKKsExMXpAJatwu8xGCYnPPktioxszt/QSAAAIiX28cIAHEdRtuK85oFwtqXTU1AB5wyYF2IyLsaZMu8J0G8gPL6mG/oct16zl3/Qz3/84anfqKrQv9///7P0hDrsG3C4R1nz7B7TCm4PbKO0ditUZ4CB6lTB5colZ2RDwYgP0zXPDAe1pqQ2e0Joi/Zihl/pq9pR2o/KJ1Pd6b3+rrqD3QA09dsFwjpJd6vZgqIROv23q22KGRBHng0A3rJ3SVr620vo9dAsUb//b1bua3ggMddK/piqey/pqFG+oBv/oo9VXUziNMQU1FMy45Nw//pwBIL5AAgCB0Ba0eET6EKIC1pEJ4kH/CF3RgmC8RQObaTxIe4AACgjLXbhsgHTElqv2Ct97teV+A1WuVdMXlMpmWj+L5EfS+nftjP85e089f907Nf/OTg1qc+rYqz0fs0vWWfdIgBAUXHK3eIqDbYnk002G0Zon9y4BTJc87OxdiuzaaKOE9fNDH//fbb7JfQ5m1/7WYtTb/4679+dYyuUy3ZdEMU3sfj6jbRboajZJ8GeXWGIkhA6xwAWiCzSgCYfMwzMI09txpXX1/K59mlz2lFvazbJsYhlaG5FSwwmTFjAGKS4wQqEXAVqobCjGYpIGZYKkkzEeVlig54u8NJZm8xHVAo7HZbPu+vQEUt/jE2MCERC5gz3koh+vPLp9jVb6Rc7nsy9M9OnaVpTEFNRTMuOTcAAAP/6cgREdAAAAiY43ukjKMxC5ys3YgU3CExPYGwUTgEHm+8ogJ3uAACILicsjboHhBLMnKaJwMAfvd+YRSldb2o96Jn1O63YtW9d0Hu9n30+pJnJ/RxtFVxJ5xdmc1/vXdp+lD1rPxhOGliUAREcbs5UGUUvSa9us3pYmmYpgyHtqjSF7e4grbt6XZ7X1EAU3/vM+71b+3o1l/tXolR9GFHDZzNSLG6f6NrvLa4DYEnbd+M3K9OE1akUaenOX6x7Vp4+FTdrkhkd0M68zPsPKwvIcl8umyh+BKwI0X8UVeP70ixBdDqu1S+ItRF9/XOpkhVkrSkjTlA8GLqoYXmF8tAFqzq3KRY0VWfLCLlch4OYzX9rPSZZv1Z+an/5x1Jdb92LEUK8C8Y/jrYVZXWtg8Ub1piCmopmXHJu//pwBN0KAAiCC0Dayw8QbEDjivNkZ3IIVE1cbJRugRugbF2CifQABBiuSslGuBvaNRnCd7/XL2AHTNbNI7I9KHTghYdSqRj8Xpo767/Trrt/t2r/9PTa33PZ0icUdLAc6t+zu+R68iC7bdsO0i1LE4Fwjl1e0psT+WD6v2DG68wTDmhSOt6yprS/9F3pqFA0MBg1vSLgGcTKukb6sqdHtL2dO77/1//6QlNZvyTNXq8ZZMqNrQi0BY0ko7EI8TTU9RFWGIxDcZQ6+wx5KnLDs2JQRT0tQeCjTZqvEbr0dznke2RQc0dy1///1iLjlevKmXRi9exapXUkmNrnzE+OopOGswq2/lZna/sktELRyIFP6/71Xy/c4yti2M+lvs277frREdQyrUVsUCv9jKaPx1+hMQU1FAAAAP/6cASCegAIAhBMW9HpEGxBA5s5PCh7iEkBZSeMT3EWIG1owJYmAASopNtOQoUsQ7qpbJ+OxsZEGOaMREmO+kpta1qjstvzAN/+lLyMn9S/et//S8r3/MpOiN8jb/sZcHD0kWqcz9PqABAYbprCEl6a7Oobxl3fF/ChlW4y202mKY7IjX1aAZN9MDiCD0r0FLlbQWwh34PlBBVqWqeyuTLdfgB6P+9z6JcBhqmwggvquXeKKJTz69T8AQaRbnnPVY6oG/7LxT09/lgQEZ3/9GRrH/6X7EWv/RVSd1J+duVQY3fppXhqWWdsEoBANCopRtNgQAzdertkbLaissWGz1HbV0jaARu0E0RjJJ6x+0FADZ6KqXulFzqtvavsNV16dJMbeVv9rlMMKqpMqla39m+tMQU1FMy45Nz/+nIEz1oACIIJJVUbRhOAP+cq02RldwhZBWtFhO8xEaAtKPGJ7gCrHQApGUucBp1xDomWiQ/dyOR5/pcnMAvaFTjuWDWiLQ7hph0sqaS0PRvuYQn19L0DqGiz0qZLM/YfSj2UvdZ1EqWOQCKRMagZ1rCG4lK7kVknGjURmiRiGmkpUxZMrGKcalGpr0kb4QDdJbe1mxROv5t6111+dpTaiiPap2//6gKKUbUgBGJqnaQ8na5neYgs89nlC0czDJGzOaMnyf5AI3Z+6+7bO2n6n+ay7bfXSQ3v5rtVUJl6klj15iIzRLmi2JweSaTTgLkeLZG8krrOnOHncUI7eD752gHr0FSiRXr8PY1F3kMzDp/+1FZv/EJ0Np/3eqC9r91mqsMdudoYhBJpLLtxRKYgpqKZlxybgAAAAP/6cAQvowAIgeAr1zMDE9xDA5qDaSNyCFzfWmfgrBEWoG1o9An2AEC0oDt4rDW76y3ddePW5FvCs/gPTB18GhHo75zrmdKmll25KKT8oYCf/0BTGYU3t0KOShkNdluNf11JAKkewCs95xEKbs0+IPW3NhD1s6fyVNFRsMTMprTmBJK01Dtd1VAEPf+5eYs9G+dDj79AqBQk4h04Kv+1X1VFRT//6Ai5JaAnCHvdccqqGsiaoyBliOYH3vyw7AF0SXK6xtA8VbOvUjfFVVVt+dliLkJGt+i+d2Y1+V9K1GuOcpGG9Hrf/9INJSyOQLC02RJ/l3fzf3gjD1j23vip/h0q4maWvykS3uNlB0VnlVWJnrMLd2ZL9WxDcqr/8IaHKRysv2Q20Iy6F6+pXM6kxBTUUzLjk3AAAAD/+nIEd8UAAAIHLlY7CROUQcgLNzwle4gwr2mnmK8xGY5pzaMV2ABAoySACrGoWaW2m4yp9IcqPzVoa6joUVIb12CuQcq5c2Es5lrotlMz+hn/92Rju7Ns9rKc1rgRVXco6IzqBN8sCMpNtyAgS3Roj+touIdcrvgZI1c4lrwXcmq0yIJfCIEL/9Wd1tp7bdU6N1ukhUUqTNbzGs9Bxlk/uIoVSHGKkplgQCBCUScccgb4s+qLpsaKQp/uZsEx1/BVNsaZ7y0x6jthTvFN9NMT8BQpkZ5vvvW9PJnFq3WeOqW8ZI1rILlnqTUABZvwKhxooMhxYaoUz1KSWsMpIaicdLyt+dkuuB27OIOEW5w2OlDMrLdXZ6OXnbtYM/gw08I7pLF2VQCo+gwAhnUUu7te1MQU1FMy45NwAP/6cASROgAIQhM21rmYEwY95usXMeU3iLjfXTWBADEUG2xOnnAHACCzLcATy0k5aBcwP24U0fpLB8pFcqKZx6MaScLTbbajGNMtll/+qdVZEesjoz+cjsvcrakWIqYcb/Ocpkm3j+jx5AyQo3GHiSD56u0vPTe9PRnBaueGXO9O6IJmR92v+j/CAdb/m53Q7FR/7dTN9GK2y4fHC1KELFj1rpB7nPoAYW+wabP1q19bLc22kf0tipKYNB0JZxCCaCmKFRYIoJ1oREHaYvI9Ng7d0+pzEOquzFNu/ZtSf2+qI7gxhx1C00JQQXaaKTccYWWo7Yc2mJg3eNuSV+ApKCIhhk4yheelj9FcuYVMR7Y+leFAnPrb9rWRdl/KHp0TRr/lXkceGUeGa2RU3gZt/ptdTEFNRTMuOTf/+nIEbjcAAAHjRdyOIKAEP8u8A8KcAAl4339cYQApL5vx64YgB0kkmXQeQln0xgYCoYFxgJ7Vf+r///7+3+x7X9P7Q+oWYmtLf92HhRZTM6mcj1T/4gVJUxF//0ikYoBiPQMBwMBwOBwt3vvZkFUPDf////9Uv/+zHzE//tG6ic4z//yZEXhYax0kZ///igi5zmkSDmDv///+SQw2w5KMeg/gCDRSrTl2SAyaBLEZeHltS+ytdH1axjK21DV7TdWlKQ0peunUq5n/Q3l/o96iS8SwG5fNM3yfG+IU/eucb+Ebj01/4/cadl/6OBf5DU16crSbhV3RiE2lVmEY5Wuj6tmMrehurtM9SlUqsaFE1KVvqVsz/oby/0fqJLxPA3L4oKVYnxmQhT9zfk/+Cu9NfrH7jbZf/nAv8P/6cATE3wAAgfQLWBMmiaBDRdt6YGI/COB9jaQET3EYCarJp4zgARKFzIABfJJOAE2VWOGDJNXJkAR0upooTYgWGgEFiMNDzZooEBTc9Yrba5b1lWCwoEzKXm19J6+hNKLH/p6wAAPa2sUljTaDSnnsQuHaoEbexAYnyY2vCLVXlynXZURCKlGoKMqp+mxtPX8pWxWea7BqIlP1HkFg2msjln9q+39GMBSUk2slsst8Md3NXXbXBQE+hJnKl9HxefxMh9+Vgox6HwafvBlR4KxE+dWNGB5zU+6alSoGnjxV9+SDT+00Bhs0AWXC7ZKgFqzMIQOieNeWVYwZ2UiW3HjZxV06keA+YjJqK/lhqpCvMOHswwI+0WqLJnjyxDwailZajid0/tY9VTGbK3+mHv//p2NrrTEFNRT/+nIE8WAACAIJNdibIRP4QoxbzTRiCYhlA19MBE+hGi4wtGCK5y6607nvH69x6fm6Z+Obqb+mj6m1nC0t14hIXC5ZAZgMiOwLxfrOnmf7r9d3/wYqmvlNv9bmJfUTOuJ7I52hSyPLgoloOVyaNOVwjsgh4Y2qzCQ3tGnUrUPRyXTX2x/2czMjq/+f1J+nqjf/p5+n/8e//S5O1nf69p6qf9G29UYp3r1BoBQUrRbgpU8GyltN8WeWF5S2zUr0RSGx7IrM4CeYwDzNyPUZFG4I+SR5W1+oz/Xrk9v9ujI/21vhHs+uivRf9XXed6w25CbtdtbJeWgeMflMhsV35v+bTTP8V3mG07JAiZrHGRfN7J3/l5r/4Qtuf+jZx3S7fv4Z//9fLSvr19X+czb5IkI99D+i0xBTUUAAAP/6cARHjQAAAg8RVxsskFBCIlqCbwYOCKBzZymkQbEWl21pJBQ2Kc1W2HBRv0DlFIOCWfkeUFcsYB4A9z6b017M1udXIKK9yGEA7T8mZU3QF1wUDhLlXTrNDHy7H3ewcy2sqzIo9nXWBS1CAnG1QDRfWNP8yJnTE4lQwDEmYqPEMHai/bkM5j9luqdo1GLVE6KVocdhaA/o0/QeentREj69edXV/+9//57oEAAorpbYKkJldGhDkcqPkVBbau21C0dVvxnK/1q9bqowxrlo2EnIfg1kSIW8DuEarsUpPpq+96b4sfdXPIOvqXGhy1IiBARtJtIJxYVEvqZR6uFcsF6+l5kUt1U6hbOWlG55Cp3su//2tq36t0EUJfJ1X8sNSQFtGBBwVHF2ZkHedLIFnvcrWhKxCmIKaij/+nIEkIoAAAIVQFlrDxBoQ+ELrTwmA4iZA3OnoEGxD5bw9DCN1gAAEE4ndbbvRj6Y9YjGnaqz1tChtQ1Kb7pfezMjrjZ/3ckiupj67/3y/f6p0GfVf+52pce2ielQb9un3dDtndydblgkJACRyWJSW5WNs3UdNZ/Q3mAUvLDzomaBQoIXEkXGVLe25y03a9P5Zk6m1NgNEwm5gGSoPg7SaMdRO0sjapiZgnVWwAAlBSt2RpyostHWhsj+7lDCgj/fHdQSKd0Z+Mb9iowlkZWO+36ub2pt/VuL39vp9rW+56tBm18djy1r2qfTqDtFj4a6w43Wbtf//dvRmaTdsj5BXnOm30cc36wYsj2KLlsW2MVkh386RmcOcTnl0/oNhUY12YiJR+7W55JfgBTlq51r0Uf593QmIKaigP/6cAQYiAAAAgMrXemDKFxDiBuNNCKJiLkRb0ekQbERKGwZgInuAQBQDkl1rlA1mt0VjuU2BhX92sq1ESPqOzJVVavfMRkJeZaOvu3d/URPLEgdAhtPbMCPU57S79PWgVY7Nzd4TUAgAA41JI3IAxjlaihPrtnFjteyZtQBlKkQJSYfNs5roKCfX9O6eX9fUQnso/oI1l0v+YiqgMYCqET0NFy2URceAdcgAIlyksjkA0Y6NrEi3h7uSagZz7uRGVvUyF6ld2VVuzYN6iG/9OzL0t0Q2rDWdV+qSpeZo/7qIokHXwdSIdLNeHnJqcuwwAMrpsJLyGOWrd3OpZwvd19Ik5az1QYU8opyRwtj0flgh2fJ/1Z/T9nbQOvXTpFvqaqs/oq0wrd0f1f9KHY60MqQ9S4smIKaigD/+nIEbiYAAIIUHdYbARukP8ga9mAie4itQ2VHhE85AqArCYCJ7gnI3AAQEe9cV9eX0m1B0GW4VSVJRLg8cn4nYCde+YTmuQJerYXtkQR1EBICBEwVYsTMk8g+bMpVkgPQNPa6hwJJ+bAGNc0IYjR6mFqxcajQVrd76ktLz3qTZDxpUSOfJj5AYUBXTD/+46ZmkpJ7m+6XW0ujyN2n09rUNBsqnK1W/frBAAIpttOAEYJspGa88BwzuJatnormt6vrUxBKQAFNnTOn3Fa/5rrTqv5ydGXdcnTJp2vbc6UaRlbl/yky0IxU+YKYg/So3IoBPAoUglddsEAOnQ2nznr9HIAMDtKRhsrXJAZ6NI0dQmAKQRBwAWgZivsqNorHfZf39E//f0qSqdUXaH7O+lMQU1FMy45NwAAAAP/6cAQuTQAMghQT1Rs4GGRBaAsqPWINiDj/WGw8Q1EIoGyo9JQ2CTbUgMORghKJ2oHYWp2uGC8JmxagmVCl5DsyF1qeRRYZ84IYJAibFykMCoJqIur2nopwnUeSj8t99YYVYvRuT4u6sAAGk0204AZyNrs8dDqLYrMVgbOcyKh0KM86ItqCZEcj6Xf6jf/8iP/6k8qpl/S6XjVa/tNfCu29zjrYiXLHkRQcE3JLQCdpMpPNMS5UiLuL7T5/ZguAPnPVWstm9T0oIOhXZNUPu2dPf9CVZS7/wQ+85Zuqe6tfx/6raDIXM0qqf7NQVFtxqQAz8azo2qcQW74rMDNPnOw7UcmiM6XHIaamis345v/ZFV5cYS316GMf/41fr+48m1AjWKC15CGDIt8bUhpFMQU1FMy45NwAAAD/+nIEKdQAAIISJ1lR4RPcQOgbOixld4h9A2FHjE85DJzs6PSINgAEbKkkcgJzGgNE8yLjyfVKxm0mk3OCMw1s5oUgMn4iFSKRxHt+uxmPg3LaWdFgmlppTVMakTZYm42JDtXo2SG9uoAEL01JHIAIS7nRcMLdTKl4ZNvSx1oMRrgwoj4o10N/K3xEM1t+q9bs/3qjcyJq36kSkzWt7MjO6BO2Ovp116VVgCAWW5E4AFJDX0a5HpBc2XH3iCvDvjc89iKd6teJwAZMyM50QnqBCXLdvV6LZ3TJ/+X/fyHWgZ1GUj6mrY0LxuidR5EK1JZJICoR0XQKLKlYbLYzB24Zo7ocQ7IwPN0dQbPRnI1EuUpHyVXpbRq3/Bl6FptOmrkCK6IcyDTuIzKw+qrQj17FJiCmopmXHJuAAP/6cATATgAIAf06WFHhE5xAw5rZPEN7iLjfW1WCgDEVlywqmKAGAAAtNuRwBV8z1bZTJxpe6zXyN4YW86pTdASuQsz497aKgc71ZSG/86+C4JkX2FdDJp39R0vR7sNSGjcRz92fQAAAxP9wjxGJVHU7lKHaf1d5fLiMJ5HaMHkVisxMPbQUh1pp/VhZrXNJFWNEdcSbD7bnpeosWc+TUASsXre6Z/3A0C5G2A79Cm3M0sBsAfqt/a35x0mJOaQmQtVvSrq93vXLUJIQcYqjEKxlVXNvrUYzoh1XzP5LVuZr0hw7KYWLBIpSpGqKAAg227I4AuLrP3K4pMlX91GpO2KvIpZXVSpxlwUDcXFzzHehrKc6Nto5yHpZ/26s856+zNSSilovrcKiSTpWfIoYxBNulMQU1FAAAAD/+nIEUm0AAAIMJNvuGKAEQkLrbcMYAMkBX4Z4E4AZGSvwzwJwAwAAAAkHI5Nt9/+BwAAAksAg55PdD9XMDASgJNUU+xZCnOhEPgxDwHVKFehnXuUtHRXZfi0YTWu04cMjgv/4CEB64AAAAIlp2zW///78AABATJ4AcbK2z5GDYu5n5/Qhu3hiW90VV0GbmX0vntcxZbu3WNb7x5v9/S4m7tf/pgz/maVlokEgkEgkEg/37s/////79DDPzOi1MOM/aYw3PsaKyAUGxD/HyY0MY95EdJHiKTjy/8xpjX5gyarHjyGo7//bmGMfv5ERnuC8FkEgkEgkEg/y//////79GM/M6LUw4z/MYbn5orIBQbEP8fJjQxj3kR0keIpOPL/zGmNfmDJqsePIajv/9uYYx+/kRGe4LwVMQf/6cASxdQABAhxb3m8UQAY8Rzu94YgAiJUFZ0QET9EcoK50UYoeJBaQEssjSbisgtQ9b1UVDPzGf3QMZplVuz/+m9Styr+j9/zG83Xr7lXUlHq3qVk2M/u3zPzP/Nzf/0ez1dQsIKe6/8BIVRkszjKTfEKAoYBwzMg/uvarkdVZnM09FRnT/o81Styr+htH/R/T+vmKupKDwV1jP9KSz+env0opKkQAUXUsmYGDqKB3aTuCQmCNOsF5E5GjZjX7CQRrYsOsK1/kJmfu3l6av6Cn5aV9DGetQQq/KzoKy23JfUemmFQiwjgqdUEQiTJZbYgU5Y9hlA67awE2dpn06tVSgUohRm+aStzI5WthW2ZHdNkM6l9y9RPM+/oby/6I71qCHv9xKInsa0iGv9FArTgGpMQU1FAAAAD/+nAE5DwAAAIHLtnRIROsQqgLvQ0CoYh1eVZnzEwBFaBtNGGJ5gBAbUjaJTYgHqwuQxq8+JKg1/rNmjuGVNR2al345G5W+v1d/tVLeT7hyCeoN1LHOsKm5G764s0jk8cn64yFAxTWSgm07rbo25SHqV6IXaEFwMj884ijk2oZHSvLsy632/j2mb2b+1X9/0J77ei+joa6/W+ENt+GcVhZkalY5XyMUina9W2tSWMcBVHA1lS2mW4Ye66vsCqPe9lbyOJ6rda9WX0X2Sras/9T/nXnsa6f5xGjb/TI/at/X+uiW/t7P/7/Zq4tt0mAAEQpFLGiE3Vd0Blopem57DX81y9I9dwoLhZdKudvHKb3H5H3z39LAyfp4IOnbb/cgzo11/8J/RgBC3UbbO9VTAMGbVEUxBTUUAAA//pyBCovAAQCFCfWUZETAD/IC20MwhOItOVg5KhMMRQb7bRgic4AIG1N61biutaBgqWkoMkWe9YukABvm3W1Y1uHdGk/TmNTVG7tsm30aw3bp3cMS9/LIhy53yAqviFThVfa/Ac4BkEAAAgI47K0Eni7Mmx49Xq4FZ0Z+jJc5B2tKx71J3f9Hnd5Cf9GVRP9uhm6/6dH6f0bQCsubERLa1fRkum6K1P7wiokQCjri4tAoWSZ+x5Y6BIu+Vs6GK6ldGQj/SyC3rkd/f627GPQtPIf2ojf6HLqNBfyt7NGgCWkJYsTVcpVNrARofc0MAgFAJxSVspTxBvMpdfZ7aC/MrTItaoDrpVDv0u/qgx3lam/9Hnf85tTgzHzfrR9AK9+kNLMj5fpFL/u7pdLwitphgtTJpiCmooAAAD/+nAEuq0AAAIcE9nRIRucQcvbnSAif4hdA3ehBFpxEBdrdPCJ1AAEvLdaJTuzrtSSY37LFyglzwAtZ2ycOFBD6qXVXgiChkLCjZkcBX4UysCeBolUOfQw3J69b3izynQzSUoMYs6OPySAQC2pI7rGW7znYwiq7bFigHQ+E5qvIDC6BIfTM3Bipev/3MH6t4JVr/z++/2qh3fq3++dv0/6fv6KzWbs6WYa1QkRSKYlru8jkqCgcUdCvRXExeigczHv0Im0Q9v4y+aFTjCmn/9fqZtRS6/9Vv3MnolJaRawkUdL8Ylb9Tnb66ZewnJVgAAAQxVWIuVrT1kq6L+Tw4VitM+A3j705BEKro7KBKZ1t3k7/me3r7ftt+t+Jb8MRJerUhRJHpADKk32C0R3Hq9W9vSmIKaigAAA//pyBCDgAAAB6y5b6GIpvEElu30UI7WJQQVxoKSm8SegbOhgic4gEhByKXRpOxQZla72wwGAbQR2YiH6EfaKP/w19Kipqpebd/75f6txwpp0VtQnS9goe/bT+jZSIjS2KDVVYICIFkktsSdq49Qi8tkpBNaGrbvIHSEqf5KMvkmTknnerTKwzYA5/uPPBDjgAvwzF7/VP/7vxSimQNuJKGhk4b6gUE2nY5tY5LZsYTTFKq4XBQFa6w9RToPrRH2jk/pBatzNWj0MhnX5pr/lN0H8j0bzGfsyD9tx2ptxdDq0IQsGFCt7WbrxYvXXorEPi9yVpNxh1jKIA7GHCRMkwgPmYonNKYIhTYPnrylbWivNQ7dH/8v6p4/6Deij0kyL9kBKZDgxQ9YloQFFnaa2w88fAXsqe5tFSYD/+nAEQdwACAIMOVKbCROgQ0a7TSAihYiJNWVBnEp5GqCtNDEafhG4nILzuMgTPfCGn3ZK1CPOfHrcUXOJ/efKlRmKxv8eu75n2sfR29H9DOv/fT6/6G96VXbXOtlDtH860UH5zo7OsAAghuOWxpyY7dAwTT9xaqT12N3tAczCzZBXtbqpiq60Intt/t2T9H6kb9ekUkjK+uJ1yWqoAJU9wZHgQeoY7+976KUeTtjTggsFBpj3Ox4qAI3ag6K/o5kzN6aqHd2drjqzJo6dOjOmZPRn4lUrr6rS9mUmvLd7KGMqN/qzW1zOFb6n36sFJQAAUG47dYpBX2osY8kpsIAPuE9S2ZqhWdMI9FJ2nY/gZUPiOjmdEpYxpC/Rk5DEcKjTfE60r/eefMsJHacaczBVYJ/JGVR6Ygpo//pyBMQ4AACCDEFa6WEVzEFoKwow4nuIYQVZJLxMMRSga2jDCT4ElJFSOTSOQcKUf2njaaZpkgDtV7ti9RVVfMos4a2vgXyAqJS3+Opn6mDmLiPP9C6CGjL91JuDUZTsK4lLV29BvpAABdu1spjTyXfC0uINprkp7GffrvUt7Dh7td+2hw+FmV0lQ42jr32T2b/oK8GciUb+Z9T1Gm9Z96j0/vLOP5VPJggAUf80TKrCQalEWF6y+yfEad7qWGSjG60SjiaoJKRqOetrF6t91c9SvRE/ZW1KqPLk9U6O09vunK4jXvjmlfFVEdAVlqNFMSS8eiUhNZdGucIuQvcmHzyB5U3/UT2RReryp4n8KjP/nW2clOnlCeCJorU6SNqlS28nbUUpPpLgUM1vOuGy8khMQU1FMy45NwD/+nAEtgsAAIIMQVloxRR8QcgauTDCTYjE5WNElE8xDSBsdGEWtgCAyGopLI5BynRRdpnydxRYAzXY3bUdZZ2dIwULur20TerO/07U9Hn/hQ/2dzE623XpqO/oWZj2Diddi7adlmlhIAAJ1+mhDHKxYfDQShiV9MJOIHiphojJti9l8rTbrXZ/0RFI9J1b/9PR5v9W1aVkt/P4llp92d3TFodfVqJV8dFrAAkrMmskgpCnT/3qbC8XUAXMsZ1Z7rZFSlAVmUn795wECVysNRD1abcjw7Kvu1cOa5n3RysrHwwBBkKXnQ0YLncj6P5xcqA3VXZHIObDrAjUczJ3sJ1+956sDTLxd7BKhHKp3IT29QomTX197pu/4ME2oVyJf/AVrswyfIyLaCjy6UHnyDqfvctaYgpqKAAA//pyBBu7AAECEDnYUWMr3EClavoZBVOIaRFdQyysMRWaq+iRleYEFL1LZHIIPGLTR2xPf52w26Krj6HtlFThYly5rEtp6HqUFau99/0Wzf06gM5lRf7KZNKiqBqlMJhsvJPY5Ola9A4QEG05q04OFOVlgsiqTXaYDk9tkV0RyIinYotabmztbY4KRMtnMV6Fd1I7xJm8bi1TvfcQTqe5j2W96Tboh0EoqACjSjt400cbNBkJOdhREI0vcm1mXQpZtDFVxRUbP+PF31dOjo94iyMMJXeIA3Ut1TdqZxZUyFmR31KonIjipp+P7yIAAfqOyOQULWy2Nqtr/bgcmB/xjTVwpO/GqsnBwsRG/k/QML9uv9KPT1TzorKTe24kwCBYONoSQPER9JhGZNrpZQbbSLpTEFNRTMuOTcD/+nAEwb0AAAICNdQ1PKAMP8XbCqQgAYkw4XG4wRIRI54utxIiQgBhfUEc23i2XqI7enFGi47GXQU9C3I5zysVG2QqsV5/7Kpvzpd1f0d2PrYqP+Z/Ouq12tAplLLHQOkDGBdRtHrABDty21wCz3dg5OEdVS1sUCRpMnSePSOFMrpqiUi2Z2EAM3f6Mr/1/7f33rF3//RtepzvLK0EFiOsxqRESIzoAABAACaqcd31td0AoEMGHi1mM9sU9gblcXQ4lGsLMY9EZ3fVsi9umW0QkJNK6l3V36ayek/7/v9XDHQ2bE5+n/482XAaP//cnoAAAABLbslc392m1AoFcgmU9CbJVXz6P3p6W2IxwwsyH3GLk9+o88piaqpyUVjOW/tVk1lrb39d57Ob9yiz4AT/95tP//0HE8QA//pyBDjIAAxCGQhclyRgAEMoC4XkiAAIpOFyZ4xLAQ6b7szBiOwCQWq52Iw2Ro0ZJi4rJ5uxOfqOA+fxOH9aYYP2i15wwUeMKN1H4kPDKcofFGiMVx5xAsLss4VrtbQluK7N4rSqlwiJQAGWk5rhcNk5GTihxGKxW3n9lOfyEnOc/qc/v37H+qXft3fkX9DtqINZF3+Q9EB0Oi+6kuyghdjWVwrX9Lf9+lVNRIApJSTUcNQdqpVT+NBjSvRNxzdgJoaqVZqpVL38vNM9VbKUqlK1V/Kq7m66P5ev/dNBURCJ9Zg6LCoSjTsFXKDv/Z/DUkCmSEpz4ABdPVS+MK1CibsZxmZqpKZ9Sl/qXm8pW6spS6lL+VWob8xvXr/3TQUo8mgSgqVFJGJcNRE//V0ptAp0q4tpTEFNRQD/+nAEMHAACsIlCdezKzGAQwga4mmCOAhgf1QNYGGBAqBrCaYIoAEAANCg5s1k/sGx9BEuKoG0ESCihYpM/QsaeIYqETqQ4LAmsmKhalkPhghMFRYNzovGoXfsdeVknNe9xpKmRr6Bdz/vSDU/ormjak5lo74ioJV9wD3LUSnjsQfU2/HpjmZyyo5bU0rMuu3TzM32qW337VK3FfQ38upWr/5pg1vO78qrvuusX0GA2nRenQ46pV7dkwou+HM5my80kTxwzYUqpfKH3YCFLQFS6vaqwMa//qTA1PBIOkYdGEioNWAGVDTS0OoBR+9Y/99K21+FJ5uVIGIl9mrwUGS4atxLEuVCdAtMZj3RW20TVS6l/K15aOsMrapo/6/b9Ry9/pr1frT+2GIP+Ku/Zt//5BMQU1FAAAAA//pyBKGuAAwCAzXUk08QYEHoGxpgwgkIsQFSTbBDQSCgLTWAiHxKQqMEtPlqFdCfnEWJ4yrl+5bucfBf3mW5EiYNUwyW1vr9npsZ/Sr6v/r/XyUs//DFojtZVE41lFu8Z7HXev7jAAQFt3NFyognaQSkGJSGPZzGBQ6PvoWXowzfV750b3ftSPZrfb1b/6kft/r5Ov9bYIZOutLoAq2MtU7W9blQCQvlZZSowouOeNRoJiweDImIDMRw2SkISwsowcquOiTEK9yu+ivOv2ad2O3Uy769PsJ/unQqNT/Y3V//fwblkenGPYzeLf/XqAADIm0++il60AkMhtBGRozGaqzaoTZG1Y32YlmYk9lOr27GNK9q6nyKon+3Vbb/6voIbb/8K5l+0jvPalJxRlLVvKCIoTrjEJiCmor/+nAEpk4AAAIOK1jrCRBoQ4CL/SQjEYigaYGkDEXxFAirtZGY7AAACFU4tGI6VcIwk2pgen5blygwO5Z8o5D7vtI2hSXT/3VlSpQ3p9nYs38EfpHBjyz72d9os97W17fqR69FLNNWaCbUcusl0jkwec1jBB2AgFEhJYHARp0iF8roM0YoIhLM96Entb4IC7+tR5SE055pa2EGUCd6Wp1MRpz6EuF0iUSvuUODSlav1u1rkwYpK1fONUcxfkvFXpO5MCs9ddKJszhT5VcHUHRasTgM4EtLJBICO+7I6BjXCCyzNabUa/zBiwgKIJPKOPNAAAAKsUjAUpJwprJrF+t8uF3pPFUR8LS0u0Kz6PfQPdFgw9pktr7g6A1u15f8s60n13Co9zL0xjXSbe3KIsMTdKV7hIeTEFNA//pyBOSnAAyB+kDVE0ET+ELF2v1lAg0IrQFMTbxBgRoT6p2kiDQHQGIfgcAiErnGYOnA8s+zypEK7KMdIhnMJEXRAU8yS8yyNAK4W9QV4fX5TdCon/5FKp7pf/fCdL93V69f+6RAAAASqjsZcpLk0AwSHyoULSoW6IqkIu62DtoC9SFdEqhn5NyK1GPrq+tnSZvyu+opPw3LLr9jkJ6i+gd56R206u7oEoLwTcAKZJitF7C+JYNdGJJD7tiiaA0WSKUPFuVmalAA6CtjFUn08t15jr0e/Vpcv7/FP+319dV/c1cE/T2SP6d2R3yVIQi0CVBHiCJCr0jLAIwJJJstYR1OCsyMzNecWh6PW5fk7SvVJGZN1LdVSWYR01u1uhYyUGwox1K4bJfHLZ94u5FFA6y5rTKYgpqKAAD/+nAE61EADYITOdKTRhOQQ6grHTwiSwgs4UhVsQABC5qsKqIgBgKWgzMIbwKrP6uhiYWBMmeF93WsP8vuVCxOL3OJBjHymRxNwSbfZelG3rsu9Sotv6X/I/UrbIyv+nPQVyiEXfdf/SAAABHZd7LtxHgQCex4Pc4XZI4M1aLVwKDm0WSmzOzUqZ6sVH/fanr+VPG9HV+sDS5A2VG/Z2PQfHMutdo6kWzv6UqqEo0ONN2CwUeKHJerLKoNDD9w84k1GHcaKWQmpMQINBFeaaoh2XLLaff0VtErf7+dte/obyua//feDk+06xX9Pb1BcUoiSkAmgxEMPOxmfMakaM24ieKHDtKzTSfdm/2R1c9ncBKp2IjaJt/0fo6fmtRwQTSdCwF7GCj9+7Y8NdAF1//7UxBTUUzLjk3A//pwBPNrAAACIh9ebhxgBEPCq33HiACIqXdtuNEAERUu8LcKIgIAEgkyWzbaj//bb7/gAEGEmDT1vajJR7x1q4S96qa6kMTIazv/+blKKINigsoInGAvaI6A4+ePSS+ODKExbBy+wAN8+kAEglJKNyW22622yUAAhkQ6cDXtXV8Y/p64Z3d7QrHJW9qWrDiQHTIcPBkVEwUIRjzsElm8XFFrJOOEmofjGnNPZ5fvAAAACSbcQEAgZDRjAgGZEGpYzprm163uu6FtKahqVO92//tRv/nmc7f6nJ6f/kIff2VX+/9XZTsfQvl/////upkO5qLgwAAgEIHaNp+OJBLNwOAph1ftvq23tTX895rN1vo11b+8xzk/7k9L/+Qh1n9lv9/54xxYwt3IAtZyqzub////dTIdzKi4MP/6cgTcmQAAghcIV29gYAhD5vpi7QgACEivRy1gQYD/imuk8xQ2AABIbku0Jboik6UquU8oifL3e6+pyWGVEU3DBEHDhBahRSanSKRLntKBh7Q6R6cZvDq8seOpRowC6V5bV+9qlBV33LUFRiNSA5CNk0Ps/f5toj2UQPNQBLHjjMOUBq51aXQ6NbRtM1l6305f/pbf8rdH/v9PFAY+SwaXPOpdKhqt1dmz6V5YAAAgvrA5ZAXGqvWulUSSCx2nSnsH3ZuPyxG+huhCAThjNK9EcOfTj06TdjuVdSp639Waf+Gd88kjZW6Sv6lKEv0KBurpsCaNQmsOYkOX5l6czZzK6HulRBwsrNmUejRFVSTWaZW4OcvUF3M4uGHkkir5c6WgzbcS3h0Sw4K4DklGVJiCmopmXHJuAAAA//pwBOIzAAASCjnUsyET/EMICx08Yg2IsPNfR6CgcREa6+jBiT4AZUqoMVrAwqMSF/2W0c/zmGpTTuVR+TNEQTEDjiOQwMbCo5HjFRF4c5Vwpqa66t4BS77SpUvUIyjJ17bl+7UvQkAAAhONytpwAhUN3YNkPmdcjhLG6I4ZZgocp2pL9Gdk1LKhbHC5ybWartV/sHN4JUzf4Xqtmv6dsMw/96FsIh3MJUVABCtuRtNgWwm1k2B8HFpVdIY1LWIi0KaLhCV6oz3aUo8QJZHOrOqJy3m5nlkb9EflIl/+r5Wat/HpUFQ6mBqmv07rzju0ERP1JGAwjpP6Oq23QlgwmGcDmRkUJjpgnX2TJpmoYr7vDkry2L3rdLaT+F9FRaN+8O0SKdoSRWYU9XgIy+QY6BDwxZJMQU1FAP/6cgQzLQAAAgQrWWkBE6xCKBqZPYINiKTjXVSRADEiICuqmDAGAIKabjlrbkALmns0WPTplFg6DPU2qOtEwaurq7nzjTspzkU3R0en+nRvwT9qqU4VWSLCY6gJxcSzRINbkJW7/oAAAZroUAzIYNC1WVWSvShrel7iErR6TtsGc0Idn3BEd3ZE0StyTG0ZarXdiJNBH/UU3IpVOu3/iaUb9X4lk/tt+KABBe2422wiFDDoA4RQd4q2nYaroh56SKzIo4grpsiF3ujyGdDomr2Ru1qWVn/PTkREa30gLVKkMdKTIlVOCtVDaCrF9iQAQrbdjbYEw2XVtDKuLGd2Hmb/kFreZFmaBcj+GcvMkX9OnBCHgNtc4uUNVLFsX/wv8Yr7dXKMSGcDLUNt/+ox8FXMP79dvKqTEFNA//pwBHVLAAACEB9c7jRgBELBC03DDACI1D97/IEAIQwH73+MUAAABMAOGU2L9/z4/oBAIoqWq66kAsE7W7kRy8K+Tp3L8zTyz3VjZC0oi58WabcOnT7lIRnKQzrLHdIfd//QT///f5MAAAkFBwWmxbO0WgAgEIRDSBoX/NeX0xrnjkCp9AfIlBGbAinoS4o4IjkrCrFh8yBM/WjOHKfW3Sf//uQTD9v//f5MyMwpFQhL0QBMiqiX0TbCcecf27uB8K/GNllEa3yaHJDbV1W6orxDUDurmViRTR9YKKUSVX3CsZVuFO+Wc47BckFBZRpBYeZEYQamJKeRKStes7NkfIA11Td3Q/GvRAw2WMh2uoyVcLBtq+3VFeIagd/MxIpr6wUUokqvuFYyrcKd9O2NJBQWvlnpiCmooP/6cgRptgAAAgwcW2hoUbBBw4vNIKZqiEEDb0QcT1EEoG6oMonqAAEDAANcJkpjFyvADtcAXMso3jzi9swFHElb+VTi59dSFk7RFcGlHvln5XI50RcVtFXKvzB3o3Feexh4OqcJUEaySRC0AlW0nI0TB7dRphkeyOD0N40foGIZVmc/G/FLryZ8NNf2SGxT/ln5XTnRF12irt+5vQ9Z0FXZ7GHg6pwlQRrEDEQGlty3eOt1D1WcM3mzfJtjvEwp4m/qWoaOM6gRWao39W0Tk/+3y9On7aG/+ZeFZMt8sbiMtUeklu2QpHRj0LqYTJUKqSW6Hu1uH9wo+1bm4CBqqFAvJxHjeMEuF9ffn5m7df5fl6c35dDf6pMrVCjBmIr0XK2K3/ayHXqWMeVKpSmIKaimZccm4AAAAAAA//pwBDnkAABCETtc6QoS7EDoGxololqI5Xto4bSl0RogbJxnnNJEIVAMEEppumJHcPQTXZYMilntzS9EbOD8vi9tPbl6f5uvBF5/9t/zLEv2+N7fFLLNlY/dpID1e5NnpvW8gQOrY4gAGAAACbcjNZMOEJHMDEcodgpMms9qJHUB+OY1PqIu4J9fP06Nqnbq+h/fT26COz0L/0Zkqxn/4P+niv3s/v1J6QREEJuygrFkm4d0EZiU9SPJJVi+WKURRxLUJutP8G5m5OX+gv//6/Xfp8Yfv/lZBE3D7q5lkvZVptRL1/9P/V9e1CRcy8VGAgEJuSWrDjRF2eVs2Kha6W4jcHwIIg8ZqDrZ+jZUSuvKN2/mF+d16f6KzqzUM+vk7sv7H+IS6aXVhI/KJX9ym63qqYozbIpiCP/6cgT9OAAAAhQdV7jPOeBEyPv9DOVRiHWNdUKEXFEOji2og5ViBAAApNsKBDlK6iQSBht6qrEXC3/lM6+iBoGlFKFiWmACNY5tTOEvXlBbnqJbq6tbE+VfOMkUQ7KPFf1q24UK7P9OqMoVNkkhOuzB7DOJV6EECT/AXjW0HcF/o3b+Mb+nGP/T/I+gw7lO/11fV6Vp3Uj6DB7G7UR1Vhzv2XVcXeajiBwMqbJnqQ5SILlu3iqtWwGdUQNupuThZbDAF4f/24t04wH6dW64v94vpHL/mNo0Ey7cnpa9Wf/pYm36WK/9JVdW3Iqp2bmasQ6AYgAEty7VETDpgdb4qLWtx7jvEwZ4P/GNoflfGO7Z6Tr99dNtGsJkUHqA6wqNa07oUA0tcFXCgZdQWU5xpLcvclQqtMQU1FAA//pwBH51AAACFxrY0M9phEJEu3ogR2WIwQNxQKTm8QcXbWiDlWIAIAAAE3JCw5faTjw2XTJunZZGfS1gwkNbKJbiYPX5wo8u8qaWrr3ztvFuNB23EMOqXeOFSqzWt2Yexqb81quo+MbRImpABSRbhURqNZQn6Rgr0bhdwnEBuo515QtoZxBzjeM9uralvyez8OSl5S8FQZB1jXvK3lQkWebdz+t1KHIx85bJIB3AQk2nIaPKIFLlict5QK8/ikZWjNgXb8oTzmfGwdobxz25Du+r/o7cl/b6NfztG7aNHyHdUZeUGGkMioLDHlzO6mt1tAgQgABOS7Twj06AKS0isYqhPirj74KBvl6dG4Pz8LH8n+38v9DPxWn4biFDy3VPNT1G7AsapAiQhF1Cj+0a1Iw/0piCmooAAP/6cgRBLQAAAh9KW1AoEeRD6PwdFCLjiIEFb0QcT9EOIG40hRXyADEgAGS76zbKgXsrDb7xP+t7CIG54fif8fm6tx//y8n//xq/Ft69EcEVWT1NVkBiZmqzavsdSBleHRHekkw4dFPyVFahQ7bZcUlu3WhaLehQsrpH8R4i+g96ePbMfUOduFhvXk9dcv98ua3H0dOrohh0lyqz7IGZnt6vxKmpUXte+WkKlXbXvIDoAAza/jSmr1BaoxQLDeYWVynKDOpfndf8RfbEIRde4xYcHOJbaeXBJ0cyRCa9P7x9GxRyS5xnO3pQo+tcMRZC9oquIgRIkEKa78GrJtMth7VlhLdKNbkXEA/HeX69XygXvP4xDbQzk7ZS4wT5C/onj/0bki5JnGzskJeZjFR45sv6cp515VMQU1FA//pwBHlLAAACHkBa0Gsp9EQoK1ohQmqIWO1YaSxNkQ4gbOjDiXsENQYApbuK8MPcwWXp5FI8TRl+V/lRn6pMH1ypJNYr5+Jf1DO3VtC8vVv/zEz9e7GS1Hj++rLrGoVOXUNGw4mmfUPaZUDQEgJ3biKL5LpgVW5BJV1Lc3hkJiSMbcTG/F76Cu3j8/L0fv3/m9WWj6jcMwAVHKaQxltqyZ1QRb7qQYeaGlkiodRSAAW5AYkOH6NJMzNgH+WEYvRD59RP4AIPeSiG7YDmuDtzsX8gwle0Ptfa315v8f+vrqj8v2lfrLa06aDWPoss2fb/RAEABDUs3Fth/2WvFPddHugt77CWDERUagP/4hfC5N8HzYImorvwQ/Py/7eyOQ/XrX1rwXVfSNrdH34/IKid1/ufaKwmIKaigP/6cgR5twAAEhQ5WBnqEvRDqBs6LaJ4iI0FXueoTdEHoGykk5W6Ahclww9aqqj5dDge25obzJNwtNkY7oO8fNo+Lx5NFgKMO3CqFkzEcvT05uzc23qmor3+/O4PIyzos9tnuW4M9RGylQKEQCXbuJ2l9oJQKD3Mcd6UfeYaxVKOgS+svfyvgw3Xo3bp0fv/v/g0V6VFetqyIFGltsKMiLcZcv9ZoCDmtJHdJkyCIAlObDD6ysa8Z0LEqq7bCm621BZL1Gofrk3ED75ocbEjq2UE3v0bn69G/Rv9vfVmfm30Jd0aeFCC73GqbZWWyMMtcRgSQAC4sJPotUQByZmNu9BnleBEXqqg3i3vxS3GP5QO/n879uT34l3RVGpz9bOmVDNcIdK34qG0K60jHkXPTCqioqmIKaigAAAA//pwBEcuAAACGDvY0S85pDtkWxcZhzKIqQFlRZxVMQcgLOhklGIAIQCAnbuOzaEJbZscs2bJA+9HV0AlwIB61ZsIvfQjx/+Jhvxz21F3P9te5vVuZaj90ovH+hXC0eDSqdFU6kowoUdqBQAQrNw+AXVc4NnbcIT8qJXEGgEA9YrBLQvyvXlAd8/27fyr/4lrGByo9sy9NBcrMKpfpkg1GuwlIqOgBiGQAmpBrptHzlQCkoYsI+yXfJB/AUA6kvgsawEdxx9CXHP6Euf/KizjTr7f59BPT39FhRtCaSsd4RgYFSa00sxhFuxSoaAIFy78OgKhc7fBKVIwPE/q3C+Az4WLFqZ1p0fUPdnxorjAH53wiP5OXq3N66dC9q1oisyxDmIu8TMudWmc6fJJiCmopmXHJuAAAAAAAP/6cgRepAAAAh49WTkqKsREZxsqMOJeiIjlYGY06XkQnayow4l6BCCjLvx2E6IfWAW1GgRE1bcePQHgsI8g6cK6AnF20bRuTp1Xi7af0J5tH4u2hDrdNAVTDBG7iYUMGHBKGUilm+bpv74AgIQSl345/3bV+4K90mh/LcQaiHicgbNFDZ3KF8qJLYWbBi+K5OyaH/q3fsmnRLJh2XPAHGXFBNOmhI8npKrQ8anatJLJagEvsow3+C4guvDRqUnzbjGFoqo81hq2FhiogCqMJYPT6hGWU1An08o2qc3p0IZ3/P5vRTPWVM1RyGl9OwP9eyNjagJiIIvb8ZYvrXMmwWX/xQH4mEXiHiQ+VGDZQAlNeUL8SGxW2o+v+CG79OT30Q9HfQBTR9XU0KyLGSCVojKNWiRv7taExBTQ//pwBOSrAAACICNVHWIAAELnuxqlnACIoZOaeAgAURaxrs8EcAECBXcDbsWXDWEopjaDVepLCqSK36vwMypiish4hcMwXjhDCDm7zNOtM9mQxXTYzK7rI00YvmZEun/nqOpvy0vU/hiIaEECBIKl3Ah5iw66MD7mICwvuFF+FkaFiGOhmp4Fm/KF9BO+3YPeo42bqPAxs/+U/76+Ym7XSqDkmkobdCyFrlIC2lQAAAHAwHA////////////+gfWbhlscaf//54gY4xZAzBoT5Nm////iyyugM2Qc3JYi5ueIH////+OMiFBImCcIORc3L6QAAAHAwHA91z2QoSPNG2Tc/bfbv26f//////+Y6ngBiW///7COE4DAeEBuNyf///g7LsD8bnlhu7CP////+JZDQcIDcbp0wP/6cgRh1wAAAgtWYh4BQAREqswTwKgAiKhha1yCgAEUDm1rjHAAADAoFAoFA///////RWdTP/xYKg+SAYBuEz//FkLkTAlCOMiMaCp//+C4LUnJAuSoyH7D3///xUH4uQ9iqD0u5oqgBgQCAQCAfk7Puv/TX////5is6mf/jQqGZIGAXQ0//xZECNAXheMiMaCpt//wuBak5gqqREbD3///yAfi5D2KoSl3NFUBZWW3GDJeYJIaYjRhCCvMPxPUT6DmUoCG7aNxB5dwqehqz6n/Ku01B10RB3onlPrTW40JQ1W4q7vChVwUeIippTGxKySwBFupyEyX61Kl3E5rUGz/ePqxpEEjVU1lNEQzTo3P7tRC3V+R/DV18RGqgM/uLSO18SJGgq6ShrpLMDRYio8G3CmsJVuTEFNA//pwBJ0GAAACGjfY0YcS8ESIK2kg4lmIiXlk5ZxM2QAgb3RUlkYAIBEGdJN8es4Le3E/bzhzJL8pqNS+JiLUFf9GygOeNejd+u2tK5n/T0M+Z/9G1Eq8sJYdIyv6VNWM+niUFWFo1ddb8E3GZpG5MGbxdA5sqgyymcc4mDS/gegJtE5OrdeH9unTp/+huZ/9OpZqfoZS0DCkfO9toGCrCQNPO3CQ9rBUYoK251owDpALckudlg//BQ+8XkK+qcd4UOwsL+Bf08f+C6p/K/P1/r+W7P+eYSmjs5X6vXzHue2l9lq/v/ZJKf+p9KTuk4Gdc5kIsRQrMtyNy6MZGU8saC0l78YGsbA4pwR/7cX6+Kcn9D9bZ+/+Dy1Nt826/b9fEnd6mmrRGrtcHKOfW5Shpq+STEFNRQAAAP/6cATs3AAAAh9e3lDlHf5DKBu9DCcXiLUDZ0QIrmEUm6vcB6gYUjvdlttN29TqT7RC023P5QWJHg/kW16dSduFeX/f+r/oveSi/8YP7Mn8zlviDXNfLqvvQl6ejvWv/7T+cs5XCLqS7DcQIhgUTkbScqUZUwyRUjK05r5QWI5QX8l7YmHdwl6NiYCrb9G5L+r/r6nHdtvr0LUa3/xR/MMgRKh3mlN1vRUEJq2SACOWKqRW7MlQpw4aWnDI/OiuAcDDEqDMiEZ7PjHxq8fxn+ml/tt+jei3e/8r6nZP3e7vj4oSTgfEYqu+WEB6Ov+BZ50hUAJBqubZriP13WAMqdoMjV0toC82RNkYf0ce9Og7x9z2yg369OrdLJ/09U/89yupQNw0SyLy7z1zZIJs26pHItoar1u4RTD/+nIETtQACAIKGNnIaTmsQAXcHRTlkYjhAWLnnEvRHZytqGOJNgARSBZF97AvwLNP6pvtfjfiFXQqNufynR9AxnZ6Ky9SJA6DvJZtJzwpap3YpYbZAG6tzPI3eKBdyj6nOC6TRcRjM0xHTrpZa5cjjbq9Z4Xqi9VyPiEUBiGD0lG1BsrNhbaG7f6+zWH/ofmGSD/xT6XyK5bqATgnG7BXnkqS5XPEehKSDbkmYGcJveakx1i7B9r8acRQTVVO1Ep89tDMoC/p19/6ty3UrN9k/1L/ouUrIql7OisegV4Ecbw42AWsRjm4qmtGR1brdAWJYCaKUaS7yN4yTonazjPHHxzqHC1J6Jtq3Xr0L3/qKK0t6p+R/CrYqf4ZePQdKFxY+FXP60m9rP5VMXAJSTEYeAbwrBp6YgpqKP/6cAQI+gAAAiBe29ApKaxC5dpQYe1MCIC5WGeo7kELoC5oU5ZGADpZEpJJycwaGNjAYHsf4f4UGrUXfBOX24e/jX1T+rf1/o7cdX5+8jIyoxTSfbJsL7ZX9NuiV1+pHlzlvyN242hox5L9RFqV0qi3Hjv5M5CZgoKHPo7apPWoLd1vhratcqA5aaw4+yx/LMxJ6sKBtF7OFm+avodbf1/0lPpH9nhKLKPK+780AA47/1deQvOu/AF7VfZGHeZq72C84TkwuDdB/v0GNSPM5V/6dW0b/5jNoTPXq16CCMaeYo9p4sXEr6FZG9zrXztvXimyfNshuNu2RzGZQudwi7Y7l4U2PBt8f16v1/hYG6cZ69OqfkUmovbU5E5UK0xT0HopvvTGvf9pL6Vb1VIPLsAJZCOhMQU1FAD/+nIEhOMACAIXQVWbDRLgQea6k2DKRAhhA1tHnElRGiBsNLOKjAELXtvgXCs6+eMiDkQzloMRAmy24wfMzWoR5lzLrfOFmicCG1fk67k3QX7/mJ1rmTb9ejGf5jyX466V4M19Pup91FQADLtuHYMtxVo8tuAR0MnYf9oQfoC0mGQmXKhfLj1teVEzio+V5UUdPb+vm/ont/b5doAjNN8nUUpq6NkX5H+9zKAIICbck7TjbN7vAPyiNYLB3bhIeppbQKgUXEtguCG5+D4IQ1AH3wTc3/1+JPr/WZFztH/aWpnCHZK7lR0sjt7P7LOlgAACxRtS3fxqejeEyc3nr12uph22EoyjlFsui/HAl0I2DfUvX/b+rfqXpJc/fuoA8hjuUxn9qVQwd10ZVU4kt4Fdv/vu5VMQU1FAAP/6cASr6AAAAhA5WlBMKYxBqBsqLCO6iJDfZ0G9RjETIGzoNZT2EDdRIlNuC0o2UCsRzOGWGF4vxnhgoqO2LNmLqHuTjB3f+jdev9TPoYu2vsWJzHRmGTci9ZZj0n2UprZBiSLtUdeXgAsZkKWbXuKZ3IHVr0y16hLJX6d+qRvVPOdJyXT5Pj/mXLlyDy5r8wLjhhuXx+U3Xe/20KOMHIsDI4hMe5jav/9IyKMIptpwb7BZGHtcuovHO/JORNoNSyTmyvKvmBC2/DIUZ7tkTVc3Ui6H+9H6G3Wm/1fjxYx7oBjkfVAaA+Ve1iBric9wl5TBKacFSACgjgqezIMhGbRj+aR5S36EgKdh5v78Ce/DP6dDd/btszcYuxP0WXi8aSq9ENvER95boQYEgFWxyAcXSCyYgpqKAAD/+nIEOnwAAAIPQVpQbCmURGPqc2HnTAjBDWWlnFGxEiBrnDWU+iQ2lYTm340QQPcwL47YItZTcX41sKFSLD/9egv04kO1fjer9v6tnHNolUtRvYr2TY90LldnSMcMiglIK19FzFNuIsApzYbz3OtnZ9FJkCvgJhoeqN+ZGX7lDTcbZrP8nwXVwvWJe1X0bgm274gEXjd6IrK1eehEU+JT08AU4Xaxb327XuN6AoA0mSQk2pBzCaRrMAGIw0pV963LVtRC+UfkP4nbUbPiPqpvLfzBn+/bhS++iP+lPzD66OYE4OGCidYNyR7QvKptirjapeBTIDtuGlUAcagwauys7bnEf8E1ftC/sJkemHSx2/PxXr0fk/oG9/b9hUubK7q5fwEVpxVo9duY9c4/U73BpSmMYVfUSTEFNP/6cATMugABAh462FHlElRDaisaDSI/iFzlYaG8phEQqKvo9RV6GCGVBJ3bjwsxHCndEOfKZCTy6CnCIOtBz4HT4NsGP2fUfRevR+3Xk9E1BTo6UIulhGog4dFFo5SFOC+jvjSq13sFb6YEiERBLbglsUGSAGfmHXfLS/4Wh/P/nUS8LH2a31JzdX0Jz9PN0/2yDtoltMnTEOlFciiPUl7KQev/22kRFSd73cN0AkCElAgK4m0TwOLbFI4wMcl+LXgIPxgJydOrcU/qG9+nMPxg3n6eTxVqpyaIUXVGVHFSiQlMsGl+iikXNveQqFSNKIAhAIKku4xTPaW2fI96V2HqTJuoY8q+Qho2S/7d20fEgP26NqvE+ifp9e/9mOXilGlYnvJUcaj2TR73orLSfrZYyp8PpiCmooD/+nIE1C4ABFIYNVdIb1EcQiWK1xnqMoiU41J1hoAREx7rXpagAgAgARXvMVWFzLMD6ndiMa1vyXiM+MBQSIYMTM9sq2Lglps2gp78/kb9//nlu15rprfUkOODIQQGJ40wNDmyG7u+3Q8FAILt2Hd7AAIlA+INqg+Tz/gOPkJfKCJ4+6vobyHp1E7me2pf+n9C+LMEsRNhd4QJB4iVW8yFkPPpJ1W70k6xVvQAApKBhIPj8qzoYwSxjl7bC4RzeeqzMSpDJKgYBQDlT1LzF9k60xhu7aj//yt/6XW+y+tBq2U1XdSa+uZnTNRUXutvx7IQS5KTFB0kFbQkZ7wpPU3ZuF+WoYO6DbYe9X52eEoxqvhmBULTxObNLZUWGzOnn6k/VjczRtVRX0WrpMTUqZeeFqGMJoTEFNRQAP/6cASmBQAAAiVN3D4c4AY/5ftix7QAiK0dbHzzgBkZIS0PsHACAABAIDAwGAwAreWMIiZxsN2rDe1gkH5TSg9/ybJX/olv/5gl7f/8bj4mCQaBGL///xLEwSDYgTIDQt///5ZCCCOXJkSQAQQQQHjeJS8GLFmjxaeLpDxc1oHmBqJZWtBiwkSSV2bLC6TjVfo2yVNmZ//yUMB7kgTiv//SNkAfBP/mn//5AAJjbj42x5g5S7n2rMu22qsCIOPFz5rVVC74/9FV/bTtpoRROzf///+1R0dIrR6sYyHHE2OB2BVmOOOVlU37nHKKDvSodAguO0fq5M2KHGFTVDLrc3L6WzLChoAswg0ijKbH7IPrdGHD2PPacReid6IKif3U3WdOzTamr1N26///wm6m2btYObLM//WmIID/+nIE6fQABgHvPNo47zlUQShLiiXnY4jhJ2Jn4OxRIqFsnFwcqoAAAua8IqgoXqw+1DRh8F34I9hbZ52XVRhKZN8adshdWr19vRm9OVbX/20/Xjzt9BNfQejoQr/xLkxV73fXAAkIILikHytjYwanaD2bv2D0xWY2XLW1FxfV6ls/Qtq9G3akoS/UzKkP85HsmnVV+/26PgVehrL3a4gArVbN/+u28fEhPYlg+FEf05X7bGn8ZkPjnZ6ansYH5uXegR57UG92aSBDMbZvRvq2a2d7pZFbv/zf2nMfoyR0Xf//08dcQLWypRNVa4AAAO3YFwIDogVJ8aju6+Rbwg0aJbszOcoLbZUWPml8ttaWmirTmN0/zNS2n91m6azmqjL2z8086IAPd7tWhjswWDHvAry3/JpiCmooAP/6cASDTwAKQhJG2JnnFERCSEsaPwdgiBEnYOecT1EJoSzcbByiABdtA9YZcoOgoSUTLahnTDlmhRgS0WaDFyI652z4gyptRtlGUUCPu2LanZ/V9G3TyKqq9U0yv1VJvVmzPRB//b/AYAAAAAcmAxTruFIFCRkLZFbkWvqPg0oFLlImEtWSUCXjz2bG3aYBHvnv0/y7Z+f+vT/+v6e+BZrOqi93R6hYM0Q6kMkwzvQ8XOhfiOXoBDaGxPXAyQXjkSR3HBcOyhJ8RWldRbjzyoBz1aorVtqaOTDtsvJJ+tenm/1k5Mj///xXoOiAAKW7BrcQNZ4moY3XI5co9bqGBAlux9BnWUG/C/473nir8olaNp5rz0281/O7dEt+Z9UqaBTShgz3aJmRqYJW/u1piCmopmXHJuAAAAD/+nIEA3gADMICO1kZ5yxERAg7Si4nYIf1G2JHpE7xDR7szYEqOgSpbOPrmPCwTM1XOEXuyYe/KkCdc7wc1iboL+dmoyF0u6D4IN6hjy6My8SK8IkmP/m/+jVT/z8Vq/90bpYsSLpgABAAKXbjdKJ/Kxi9QeYUX5wAHiM4ULVJZmS401fHu9B3+bXb7I71IX20n6r/5upuzU55aUBVWSaIz/lDfTYo6hWS5NBug++PxolB6srptL925KT4O4FW3IYr3knVSYdqSbc9Rs6OXOJ/IP2s3UERFYhUOlC1WqddFql5f9Ju//9F/xYBUuvGO901NdY3JsNs2+Y7/ysLGr8i9JVrHqNwNqvroPJevOPVrfoGdvZPl6a+m2VY1T1+zKRgqboS9T/rPZKFwJdMpiCmopmXHJuAAAAAAP/6cATjYAAMQfU62BsHK7Q/Z6s6GeUqiLTrZGeY7pEdHqzcso46ABctw/C4u+3dTdb+5xWm9Xfj7DphBmMAHyowJzTYhDWgYztU2mJBmvI9b2f59B2nqdu//zub69TY00j9/KdcAAAABTW8XXCRjj3zbJeNSb+3gbL6q5h7KzVXh3Gv7o9RL8QN9vqdK3ZPd3djFtN1W60b/o1QXxnp/e9r7yfvACd24+cnpGw4lrVuOfa/FrgzSSHIjL7hp9cDjhO8ry2jzBL1fJ96s6dU1bb56o6P/7szrR/U02xotQkb19KyCREpSVrTXAAFd9x1j8ngqQ6pgIe6dpUPBS4eFONY1Scz16XTIEfxNurfIPPKoJdm07oYyU26REDCCHPplJrgqJJYCWwnTq4MvdqGIWjcmIKaimZccm7/+nIEG2cADAIXOtiZ+DsEPgg7NyYlRMiw62BnnFEREyDtHMgJfgC3bcPjBPViwuh5eGWK6ZUuM3gHok1BcZhyOOyos4BmOvQnqXQRwCnlHxs0o8dZl6tq3+r2RtPcxO3+0zQX2fd1ZJcAEQpd8PtCxqoh1BYsT6SiPBlEGAJqxtaBgrkLGtha+cJfs9HX+rduz61//uvfM3xnAJruIlN8o1Lnj/eHYgAzXAetxcmvQJJ5K2i80mYG5RTgb93I7csgwe42xhqAL1L49ldQ7Tlb/1KIwQl3OTzuVEEupdVJV9u376BLCYyYHubUVWAEEN1wCew/cxIg30IOnLlEHB1h9o7HT6pYO8lXmZ7H5bLaxDdXEhKDhw6/hERjvBp0VnR2oQpkoiXk5f31EaDyduDfyiYgpqKZlxybgP/6cARzxAAMUgRG2ZlxE0RBKEsTLgdSiLUdYmecUTECoWxc9Z3SITu3A7xKZwKQ4PKEnVNqNCfBVGqBMJVNdHZqY6sj7WtQBT4UayK113QOSDZ1M/OxuntpZ6PT/PhFuYiv/tenjoBAm3A7wHisA8XImAZxNmqBfEaLRdQiaX0Br4QbcQq5Vs56c9v/We2Q6fVlR89FftNc52/zT6BXKG1NWnHWj6hcgACo4B9YHnA5/mrfZTW0z1kEHLkyOnOLSKJKvgEtOeiSx2NBZpoAtRj7+CRYVql90dlBtt0aR7V/4LQWk7OX2qUaTdFjKqAAqenL+tc9ojZDH9uAr63qDxMXC71Hxb9U2fBf+UWrVCjUfERff9TboMV+qlTq9X7b2879C9QmIMiW/UTkNxcymIKaimZccm4AAAD/+nIEuuMAAAIWPNiZ+CsGQseLNyXnRIho7WlHnE9xDR1s3JgdggApfwPvj8ccjTVMCVDPJD/X1DwGFQ6akJDi4Zo+vWy7gf8X3Zf6F0Nsvp1b/q7qQ6KOd2Z2sKjQAYojw0zXdX5fzDIAAh3fgedjBjC2L2ampG+9pQ7R6TahdK1GOJD1fZixXif8S0q1/scOnSg+dS+6q7P/9tDrb1Rb1BA2Vc5SwqSrXOmYBQVAJdcA9MIfPk9XODIdOsY+T0FtHnFSskbljqDGozq9SWpaPgOeP8M9f95NBEwtOyDs6lLa9zwf/+Xicee1KrWsVkMQHf+B64hV9sQSC+nK2waAK2EVajKUzUoEzb59l4m/YjVv6R4ypC7mPtVFY9WHGNdFOPdHpqttFd0GTJMCoU9ZngZMQU1FMy45N//6cAQcnAACAeo615gvOHRBJ2rTPwdiiNjzYUwkTtEbISvc8ooaACl2AnKOx9ZlLVWQjH3m2sjKApRY1ZGSBrJExvBDKl8MNMaaCOvHH7L/VqkTnT/3/V3ulGq/pKTUDf93agAGS0D1lEKKa8iMfN4m3LIzN4x4a/ErWFuVMVxXQvqC1avgsQvewVaV5Q+rGfucazIDDpjPXXdNPdNKc7/qNnX9CAASbcD8tMhotNEhVPdmPu298fcoeSGERDdUy+PSfn4/vvQDuCwR/wjw9nunj6t/KGBqaKuzPRSLb+uZR2nDWDGIL9a2AIlAQAKbcD2oLChGpHGDUhuj2bHU8MWFDaYqkcrtQVbEnq+Ad0zGevOP0fTw+CLnL5Ve5kppKdj2dmZPW7WATTj+yVghNKIfHJiCmooAAAD/+nAEbG8ACNISOti4LCh0Qcdq8zzFdMhs62DnnE+RAZ9sDPOWIyCABTb8VoBm8DSzvGs6/9g1AZZbe1IybDR2BNHw9d9G15cr/6Po1C/Qzoyf2dW6b/VsgrOj9MdrGmnCy6HDnSpE0kAp7cD64jTDSEsNUJC8LydmouQaSECCs8wuuxMdhD8ANGlGNXjko6f7/qvU+8tDkV9yp7297NQa2F5rjFdJdPFmDhAhzb4feB0uWD2WGaE57gOWtxQ8knE10Y6O1LlsR8eeaRKl23Cf8Nnyh3btfBNm+jZmppdqM89u3UjUHhbjuo/Mu/3gFT75DE3aVNQ8rrwkN34ICot1jwdxM61GS1RHenJ3RsIfzLR/+ZqAnf2O1FedmoVpVza/o2LNO8TBF/vErUve9TEFNRTMuOTcAAAA//pyBEWWAAACBTlXvTygBEOnyxennACItZVyOGOAERqyrosSUAAAQAcuwHrGFVEhs54MPYqJCe8huHuQXA5aD3YSqJjsQ/HKghUaEGpwtJnEmOq9edv+XT/7HKifph1AiDYjd7WoRAAAu7YDdbFjeQ0msOMRlztYn6kBnjQwKs9BM+VIEDDBIenW6NguMa2Ol6Fzn0559xwW5r9apMHf6JMVP19WeOLWo+WBBJLxxhgZWb4nQKrQFk3LuZ29z/1st7e3t//zzjKN/9tkH1FZhn//5w+QCo8YcNzP///GxF3cfc/Kf////lVPZWMYyho4AECEEEndqQPMbf2PKTGlCwq49wbsuiK6bLm9v///5zEo3/22QTUJkJ//+YTFAiJEMHyf//+HhV3cTc+M/////VT1YjEoUQTEFND/+nAEt3QAAAImK93vDEAAQ4V7meSIAAhUV3WgjKChBA5w9GGcbmACgXCXW0W5swHhweqE0IXhQjMCZFgmTs6Am3qbuphfopS9f+ntlb4Vq2X3YliyjrtAGWGi0i/xWtb976ztrFVLPPBqSgCAg/kr8lkcy4y+OOlvb8hGhjMlwTUd2yNQXntrN+UpbL9Ontlb4Vq2X3YliyjrtAGWGi0i/xWtb976ztrFVLPPBqSoADAaZT2Qd1wJgDmFbyiGC4miLQdno8Bh0j46ubrcGV52V8RP9Z27w7KqPazJVZLToKuDt5KBUuXkgVWIkNQw7lQ1myGlJE3YinK0oVABzmZ2hOgRsIzEWJiNHdMVFqF9vv6tYks5O17pH6zv4dlVHtZlayWnlXJ2wKl3QJViJGw7rDSYgpqKAAAA//pyBNNiAAACDEBfUAMofEHoG3csxSqIsNdk5jyqwR2xbugiib4BMdikq25YuCQw+YmkuCWmJvTQ6cIjolzp6j9V0f/3dvVqHX7/Mm/N6lfMLbt9qIbEQl/QLvw6x1V7mMh1sOlu+SBKCpZJb6hJWFxAODtkvxPLVWEduyC/VHoGUH5tXUwi7+FBlS+n9+qfZDehk3fN6l6GopX/RDY0pv/TjPpq1XFq/RAABD+7+a+QtHMwwKGeeTOEttiFrzaS3zpexhxARHwOtG6Pd1Amo3R//VG/u/5FaqEORmN/nH6rpBTu+j3DUcD3C//rNdeAIMhJxpOywziwAcrLw1AVYypmQfsogPx0z4ijK+A3V6AfT+6F21Vxf6+gs71Sn6axHv/8H//u/1b9d2rr/XnaiKrsZqJw4hMQU0D/+nAEyBcAAAIOGNkYDzhwQ6gbzRRllYc4cYOhhPKxFZwuaBMUIgEdZv12SW5dXLRxRpoq+7daSYtU1Ja8LNHpgtGYY7e61OEmuRX4u5Wl2VtKxvhqXHetEmz9bhAhXc2vrquhEM2VvQACAiQSUabl1FYWMpM+K48Y4MKSYrimg+heXGj15gF6tU3/tTtSpv/zX1/0bQ+yfz2oAr971G2TSFbdqyOuioao9bJAJhIyMqWxyb6ZhBT3NqZdEIMTS4ZIsDj/WbIcGumOW+Z0LMa+Ot5d0Ms0dWTf7HbJeR07kSbDBUxKQAiyCprLdTpYQBlH8Y+DVBSxGjPahSEe/b35RL2lBd//eahSnK9Oicilc1/pmbj2s7XFp65m7YnoI6swSSfJCgWEb0XAJMQU1FMy45NwAAAAAAAA//pyBD42AAgCFhHZmeNLpEOl3A0JImOINEFvQCRB0QmObaSQlg4EmNST7bXCIpotDMvIywJ4+2wao5sXTuc6INs685PqGCzBRxj5k9QbPuzuLFRB66LuKGZghVnZu9V1YYdT+9V5uhBaAAovRFy2OXT2grNOmNlhoipCjQ72pYj2fG1VrIhUzZ33/9KSPj/oXwSN9gfl1NultC1J8Y8boURK6jsuWcOftUpCgwGARVlt0B+18wjpzSWy5wRzE02p3W2oJwbU5Ni35Z3qOWKGo1MtMWoyp4PJLg1SG7mOINN6e1vWd+XOXgUWHXqcHYAA0G9K/CxPInFJ5bWp+Qn/Eq+mkrm5NkeMDVQdiVkZBgCmdfFIq1VT7mP5DO/ulFP7g0OIpVRlcut3bzz6Pe8/bUmIKaimZccm4AD/+nAEoTAAAAIIQNzRATa8QUXbjSzCR4isuWrgJKGREQ5uKBMUPoEAxDSaTc7GDLW8zPWMd4RG2fW/N36xWQP3r0in8sU+ZrEv/vl5//1/5aZYWin9dn9YxYzKvlS7lvV6n1JrPdeRgAAASARTaTkp5o0VkHSuqO4R23bMvUG+5uCHmGwsyMigWtqF//T+q/qz6D3PtXKypY2rxlPzM4NUulrHq/RoYzoACIXZLsHitJS5NNZqsJ+dRszZ2Gy0HK0JcVxVAxcgL+b/7FX2erN926D2aai2kQprXJFXaaUDBIL8Uw6o+1+bWSkll69cAA0hltJuyYnqAPsdO+5PLAc0UqrhY/ORWjB8S1CSiiNQV9qj/r3rFujf370i1/Ss6HExaiJVdWw/XRCYCe6AGhoN2RyYgpqKAAAA//pyBPydAAwCGytaMWgsrEOF2zc9YjiIBQNqYCRB8RcgruhhlsYArH7Z7iQ36HmiMTc6US0CRdqVTKyu1FIK1TBqGjLSKIogb+IvVvLsr9sxl/GnJMooN+yTI63HHLd1aB4q0OVkhV11MAAjJJbfXCkhCnKVbPSoWdEFqildFfWDNyqNBDzG1Rk1FaGyJRfvq/T/6o3EPWp1gdl1hPWVawhGi2KK6nddX/e9llQJTacC4l3JuKUjNXAU9IFX87FVGBtIYMEarex9O6aYP/7M+m5f/7CUSv9ta0eP+nucGjHFbHPMsrALnMIuCckQ0EHcxy2Sj6wnFoLnzvZ+4K2BJP/HQvG8hucvxCrwX5BFMzzn2/opdM9/pKvy5+uh4FIhEMqms323RB1q/gmTe1i5INIaSTEFNRQAAAD/+nAEuSoACAIMGNuwCRBsQ0OcPQTmX4iUcWRgsOHA/4xtXBSUMgCFNLZ19Ur52aZ/HvRNQIbZWpMEaEavJa6gxUWt/8pxSkcCAIKqDEuD4EOPwQwwEK+TLv+GBOfD/5Q4cLnycEC5+kGJ1uOX2uXVKZMrSoPUCDKJRlLNmERij7aXuphBOZFgA4dvV8p1UqDBh3wf9QIYICD2Ey7/UGBOfD/5RzC583BAuHw1anbrILjpXSoz1kfFU6DDk4/05+tmE7IkeYtOLU5+tUDH5VzUfVLEtT5X8s+4qS7QSKufO7QDTIsyycthqQcVCKCvgrAwFKJNy+S6SgGl7QNXbNJErxdpHsiSMOq+2/eoam4MfEr5E+FRuR3fkaI3rU8NJleht9XY/PSplQBgoLuO8qdTEFNRTMuOTcAA//pyBJfyAAAB2RjYmA9IZEJoG3csIoaISOdq4KThkSSga0z2CPoIqNySYx47DHcldInVnEIv9pRifGX/4dy0ukq7g+7qZ8q9hvUL8Pf6r/w7O3Ya1HtQ87V9l1/O/pp6j0TAk9btusugWEEs+GtmY3jX5AzvMoqm5LXeQT+7/+u+iNR3+j9DVl5epQFti0Nb0eV6qIPWZFbr7IT8skjLdR7WSrgEGptvxZRaQwc6fF2qsVWoJc5uVolB3FL1LTGV0e4a/FB9Ppx7oqd/2V+76P9UT0OZy7cSoHCVlNcqoa2PB2lj66wAZJKPeCHY6C+E0LRDqBoOkr5PqlCieHBVi96fYTLzKPpug6jYq95xXvIn/5Uo5hdm7pQR6oZTH/oJfDGe6ffbUo2p2vW8oVbQmIKaimZccm4AAAD/+nAELXYAAIIQLte55hO0QWgLJz0iKoio5WVHnFCxC6AsHPSo+gBAkuS4fA9mGY6ieRYDahizuIKTpJDrGrA0neI5bSxHeiHcGSjVDvDiPeR//r///1DWO8hXY6J4+zKfcwmPza60kIAAl23cemTzuMsqLKTMHbRIN6H8tk7dHEHeo9OVfYv4ID/+pNHov+jNwV7NT83RtkddqFtgJk74uRS283ESGTxF0AAAgkNtweq6YIZnwpr4RsbNEH28eU1KVsyOUWazwsXrz4PDr0ej//O3ZWr/VPKvzdagWqnoDW5c9X3e0OkAhLPMLFmMWQIuSbD5yXDk5G+0E8kYxZoL7Rz4D7rzKMftMzf4P6n0qW5xn6htRE/q33zP7dTTf/0fvt+sxFnDGx79oSrFSHczIuTEFNRQAAAA//pyBIaNAAiCDDXWmeYrpEQICwc85WaIoK1m9MKAMRWbLF6YcAIEGSWj9nEZciaEvSlHwv3TXBIRQzQQGIpEuoMUmW9SLYhqWpKcgJ+j0/5X0dW2/L+m/64o8QqYO1CsxcrRlGRvZpgICXLdh7YL/IHwTOVW1hMcBnRCqI6ONdpQOzi5R9B8a7zWVpx/vCYhRrd/+2vpq2c3V9/l6m2t9VYuPY9xPy16MXQm0gJbjkE6Q+cB4uqObOpriW9Ba51LZhB0uovnejUXegwJn+HhRrenEm0qv9IwpMDFM5FhShjYXaxZg4wiSoc83fGIQipJNwBd23FK6PVwbB6bl6VqxCjHle2qac+ggIplFL0evN7Iwkn/QjRlb+2j+/99TSzGoc30NnkEW8RnWbqOdusYSPiTZASb0piCmor/+nAEq2IAAAILW2SeBOAGREisJ8KUAAiwaX9ckQABAA0vW5gwAAAMBgMhkM///L///////++3//ZzDACxL//+g0IAPEtxIIGbTm//xoWPMKkzCGe/3sx///JjRXOGhA4yT/LAGoNowAAQIHA4HA4HUta5SKiQ4KAG2/8lPzs//0b3/1Y4uOP//c+AYH62/+4gKAcXihG2u3/+HAGNBw4X//g5EAZcUxYEIrEwABLNKNkkQUJFDyOMpzq1hvMk2isaqJIdmFKiexrdrYM2bGHQ0aCtUJPPIkRCsYKy3h1k9z35S7FWtucgbWFULuvrbs0WADG+VKHAkLFB/V5u9GfBVVP1lRls+OUO9/yal5lr4YRPGSUfqCTzwlyuKy3h1k8yg9+i7FWtucVG1j0Luvr7NCYgpqKAAAAA//pyBKBtAAACFiHjaMFMrEPmXC0kInmIpMt7Q5haMQKZbA2BnZgAu1OSNztIg8objaW1Fwjw2/Tqen9YdIR2YRKvJ8z+5EAvfKiFhZYgmP0mOtKRITIFz3nZasYeJREr/ER3nv+S//iUAJMRpJrNkg8oDLvsMhkc8T/WJhHPky7QHhzNF/C8E7GQEgoylL/+5XR0MpbzUUBbQU+MPIkVfBbEQa1Hvq4l6j3+JUgijegYkUmCwYDKocOyTMsIubzrOUIu6To4eRf9/4q9EvKSgVI+HdFR1dS95kPuavMrc0GGkhut1WhApUF3GUor3b79P6wBXSwABBGAISorEH3ay5O5bKs7QiEPmEtUoVyUzNSYZTxMutqsbrfAdxsLixscNvrs+v21d3qsqpjJJpUXf/MSaYgpqKAAAAD/+nAE6BkAAAISNl/Qpj6MQwgbR2BHbgite4WkmKHxDhltXYCV4hAxsJekbdCQDiyVK7zuoT5KJmdP2df9/48ceGI/ZKJS2a6nZr7X+1FbdSrsycKn9CDTAozspq/bV9K/oVJn4qxp2kIAA8+/6CILhcaNdjr40tJu9jkKKUZLAnhEzXWj0Rnfqt/4nlC6o9f/9/1vqLGsrKaVCRv8xtf6eeX+so7Z+37vUjpCRUL0ib8jk1iYl/LavLbob2FprNXUu69V2TsNf+NbFxhHGGBYw116/m//ktt35S/R/965HSqv//Tf+23/1RvT1QWLTFB4CgTXU5HjCRTE72pJa+Pct4Eqn8zxpcR6Jd5qgNS/fD3Fh6HiANPf9/U2NPYSuZTh0WHkIN4r6P3wz+hsqn7s73amySYgpqKA//pwBExbAAiCCiHaugwoVEFmW0dF4gyInIlkbBUtkRIZa82RiagAYBrSkoTyo5TykJl0d81sKqYGLbRNn7pn21b+Ij8PjToMhrMYwi3uR2/qz0srr45Ex8wKVvnk697qiwBniEioowBYJjiggT5EIK1I+guc1M7yCiDsn0sYPcgJtk13Ri/b4nmIqKrTvT7bLL9yJnUIRzLLQXkYr9ZGDykp///umO13aAK22IWJpwSmWU8vfCKwvvNR1P2LsIsRn1q7a2nm9Lfwp8nHFpqRPO9RIKbNrWjQneChyGIlYs4QJ+5f///rI59iCFV5IAeJWYsTkwjrvlH4/LHOvY27eDTybAcFs4wqFxkXLbpEadhTbSgE2ACBKuVBq9tG1W/9vPv8f2X+YVLrR3P/+/U/2J1oTEFNRQAAAP/6cgSjewAAgghAXlGBKCxAY6sDZKNgCLUBZmw8oVETGW0dgwmCAGGpJZxNsL0AamwnmVAEfHH9KrSplq7UzMq/iDYKB5Tjjsdii/5r/XmI2inIy0xNuv9/7o58a8VhrMU7Eamf9uog6xbYZjGgYw+8xVgqex7RVKojZGhZAPcNlHmqXteF8OaXLz4FL6460q0qUWT0mYs1+6V3p6yDVOfbkH9n9CP9ACsZkpVjGWX3Ui7Vr7ONyCfODoB0o1au+qzs/9/4MKJCgAdhERNGscVVdS2aqftbi1P1///77qIsqNaDm36Piz7wySYWjkEGSOWlVctgWMbuVv7q7u6SPi5xEGadqrvyoDPP+/k34IrmYyDo2rPns11/KxtRk3RcByNqvvW5P0WBNat4tV6ZZU4LPTEFNRTMuOTc//pwBFdQAAiCFzPb0egTfD3D2sNoZ4YIQM1i54hvURONbB2ECYoAAPQRWkmxOVuaPSM2w7+vyd83bLCwsTN/uN1j7/mW/78mvDgIWcx2dfK9PSzN6/Z/qVB2iOJhwK+U+7tCotV1u7PUAKyaAIIocKQvbDKZqMSeZlsajETAD0St0j9trluhou10lDCAYVD5b9VPn4Q8sucUCzQgu3KdG7pff/qkk2EBMkckEdA9Hg+nKtSqNstuzcJhE1K0jMQS2vnWCNfqiiOpMO6YJpMhy3zzMqxl5+fqFn8XTlSmCP0p7nV//6//o1EBMicESpIlNnf65N77KvvZ6aaU3ExwJOjG6K7Y8wI9WZ9lXdMU86fABEmNBBwSMuF+3frD74QiG12tyiWK6F///rvvppTEFNRTMuOTcAAAAP/6cgQHOQAAAhIg1ZsmK7BDqCwNGGV5iGzNfaSIcbEQoGxdgRWyAGEODAEAtMzykjLZnKZe7diETNoHJFAl97ULA6Dod6klm5XXaoZ62YMTtihlyZSGMJxn/vh215T/llLcZZLd3//6wE0g7Y4/tNQZueXbL/ziA+SMJAgmG2Z9bWLpLuhy/jmxUSR1Zi3mMU+r/f2n9RBGOjYEfr71/vK1lExytGRzDx1eR9IJRYdkblultD2Q9JLTzzlw/tKEaJo45Fq7Sujl22qhUenFSVP0af1aqb6csQFDWqDB8UHOAZZ3RyRFoo49MUbvy//kwBAmyygFRDqQzA3alBVq8yv5kHlJNA0dHpWuMmrKRm51/HTB0FZDCqsZhXejXN/SUSZrCEil3IdOj1tG6eSeV41qk6lJiCmooAAA//pwBJdOAACCDzNb0SMTTEEmWypgZWmIzMlxRSRBsRQZLA2AleYBUbkVJE5BOEbdc4XLZZQAGiYV0iN1ey5joQIj/dX9yueDFVaWuV0Yiy9D6anPOszpsyxbVZ1fbiB39q5LLr6EfDIAAMgGJBNN+0FzpN3sxZqbsY4kNG8rCKZ29PuhT4sSXp+cMbRxosZ7qQtW//+T1L14t6fqw1op1g8tDlhA+B/Z6gIB6SVkcrB2vdHW07+egDZ5n4CjSpfudphkWrUH7vnckOzMZq7JvVUdrrfyuvtP2BhupOR8kKCgT3RmsGhxIkYfIrlAq/2ACJFIEh4NfCH72eorjlZrXhCAiZnhD+EsjliV1VhoP8TbKGoKhyjC1nV1aq2vv7O2syvsw0rVYxfdYtaWM6lDXWOWjPUpiCmooP/6cgSBJgAAgh4y3ekBFKw+JlrTYKN6iOS7faQMTPEFGWwo8wmSBKBCkaj2rtoofMOKH3G/gbfIocbZts6MuNkagFEjB2bEutQ9FsxX1WUoVDVu6enoRkUjviP37WC91/TFXS2qiSi3y7gDJFAAYAmWlg5FvF43thPXnqx9J8jbPSmUNEHudXJjGqjX+/vhBMcXMlGVqV0Y7yTq88vAC+yS7l4239NEPAlpFWVy/a7QrV6KFJeW4BReVMIOdrwnhm84P90LRJXjjkuaqpOW05bkQtt1t5uQGx6oExknKrOPtWqBHwOzaZcXXtll3m/rFgJWWUAP5RKNfvDTMRwyrJFyH2Eyx5PMSQMw0WLX2WyMl37/ClaOcxBohTyM93TtnyNmGXYOqlYqUDvp7vravorTEFNRTMuOTcAA//pwBERCAACCCjNXOeMrxEHGWyo8ZW2IkMtg5jyoUQQZrag3iU4IAJssgA2Xrw6nY72CsuT6moALmTdFQIl+d5DPNQZ23ynfwg2Aom5AmIsFMrDEXRHdcvRomHnWx2kTLAzSm0un+gAAGAVWywDZepts1VdwouHuYIE2eJ4TQLqagx/zM0Yre2Q6dJOgR/AcVGnUcY5xxFR/rs+mreiLRmoLej9VymDUagDBK2yhmZaFTJ6eo6nzz/yjBaEcA0cQud2tvjBl3OVd+57CbEw0REiiZxFzaO93V0crJS8vO7KyGV8RdbR9qtfbs//+o3QlbXIHGGAeBWLHnGityVXeSLDoo/uydXfk9cQ+w73I17UarK3cltDK3IV/ewVqnARnU2Fyrk3PKJuQ9snsjnmUxBTUUzLjk3AAAP/6cgQWKwAAAhkzWDU8QAxCQ9s6p4gBCMF3j7gTgBEYlvJ3BCACAML/VITGTDNIfEPWdsWGsaDhNlkCg3tdGpkOcrkQ7PuvFeeRkVSpt1O5UnoZe95We52q12cK1TWP2S9K/79/PNR/yABgqlvtEAOlhSxrwojNSrq9jdK9/HtOlmS73Ldyy32Rf4ou2dAA4bfU7D5QH35eGPgg6sH5//B/U4MbFAh6gQcIAwAAAAAABABABQKBQKRT//q//6J//2///tz//8fLkAcMZ//54oFhYmYXb///jg0IF1MMWNye2vp//xoNB9xLG5N88mNNKb1NoAACya3a7QWWCxCMRtHVf6dau+qtmo8vP3eZbFnQ5UPW39ufc/qnq7CPqC1zyADI59n8o68uaKPakp/KDUh+s1JAIBzw5MQQ//pwBMQwAAACGgNg7whgAD/A7ArjCAAI9C1zR5hlARwL7zT0DLIAEERtuTWtIwggo4k2TGgM0aIgqo0h4lIusOsue0OpNPKNIlbWsNSEGbgc7CsilT0xE39I1mSM1PZOrGrOKSzobvzLAACecusaa5hB1o1r7l4BDjRE6ONEHiUi6ROkUzTTycSQ6V9FkGcSeilKnpiJv6RrNBmp7IbWNWEVJY3Kt35ljAAi3idlrSYgfBrPrrAUtCSnYRualHxVaAMBGTG5QowUeusXeeGPcUlrKkGlg0PbqKhpp5ky3qe0KnQ1/363VnWHetwlqCrhKAAQEo5LY20oQ7FVbboWZFrgCkoWGNexjtZStEzTJIcyM2BAiKDyYXSLiYsoO3ukRFs9vgq4sr25FZYSgV3/5LJSvkluiKtyYP/6cgQmDQAAgiEZWtHpGMhAI+rSZwIMCBTVb0wgQfEOoGvdoIngAAGNJpJSUMwGETY4nESK4bGXBpxVJuXlTOUvClsaHLPh3CqXlUPcvA3PGhKdOwWzw5p1si2xepRYqEkWblkdqV865HUF//mI8a9B3AGCgVrTEnlgew/cDRAQyJyX6jpmcOVpVEpcSZW73rabuwl6PeUw2QUt9Sj1GlPf+ztqf9HZ///6gBinKUaKTdoOee+NI1h6gC3qhR+mfVzhCmSrux/NTm1butymrdrpVS3a/6W9Nm/aEO/jCPFrHq05vma/7b5R99hAF41fwqAOWPVdLbMM0Mmi1uNXpQ5ZQcv7iSAAmTg4Rxl/r3wyYbKhdf/2v/+P7f/3//6An/OPlDI+hibVM1VceT1CFMQU1FMy45NwAAAA//pwBDrvAAgCDBzXuw8QaELoC+0kInuIgHNYbTxhwQyb7CmDCYAAQIrQLhUuZ7FjVbWwn8sKh1rKYB8RqQFasEVDOttDNPffNTtg+oyxM4i1PNluL6Yzzcpfy2z+o2I0ffV1a9gatqCSIDkjc1rcp0RI78Njvz86e2KERO5lSjaQk+iL3wVBPRwz63Sy3znm70S7JRma7fqr6tSdv08N/NUn3aoHyPZk011BKtq2jjsfAhwFuhF0UxaQo99qQN5KzxybY7Hgtr7EjQEx8Iz3bPz83m4UaSfZ7yXJ+/yNKuPbtoTitd+6w1p7n+4/xOCAYAU9t/5JMn6v2g527VtVI3ybIet6KQBtVI1GXmorUm03W+OrUuci6p/7z/6ec3/tV9RL7tJ1x1VHUyz/v3PT3s6UxBTUUAAAAP/6cgQ55wAIghcfW1IJEFxDgbrXayIMCLxPVm0kTEEIlesNoYnYAAC4JVopMS0KBBHNXcntegFrGUoU7mIzG6U6sl13+R1ghasUWCMAkTVeJAj+kXU7oOTzEbybQOOp5D6dRzd9rNTr1gIBerW2Kh0TCNzwimFNKZPdfq+9IWzGh4xyvcFZajBSOEq0xM9boL20kGuyEob6M5+QlL+ha6ZZahYWXGe/zn1btikgCyOyiFaPuVKpi6o2VQshgi1FaWCiQpBypIF/Zxrspbug1XYHufKz0EDx80hovw34Ecs5Lf4/a9Syanjn6NXxf9+oY56KQFXXbRGnGyKVTXrNSPx+N7cyQS9IUrNzlvJNy1oWQ/dbD/S62U+r7qzSI3/+vR9Rg+oHEi/i8R3/YHk1bDTKrv/9yYgpqKAA//pwBPaeAAACE0DfaOMVPELFywph5QsIhEda7LxhkQAgb/SBid4BEkpyy32uXBcOdrFXe+N2q++j19NTzBsSUjpZgXqT6CK5OGJyp/pdi/StuAov//ptb/XQf8Diy2Kt55S9ZGguZdJAAEWkdbJcO3EuoYyxsvX8bEPcEgQJZgc2zodimrOEzu7+26ttp1HSoRDo9Lu/vP/Kjti1PWk8o8I9OhP96fp7l/t0ABJEabiKvKBpkzvjAL6L+C9aIMIAlVvZ8xA6pXcgXyJhQGUcMyts2LAAKoa/qJK24V77Dsso+6kkub/Q5qEVEHZdSfrCbaNlll2stwZVvenfvgJDpgTVYXk3aZhRdhkl9aVYTkZatek3u1l1Vjt/bqZem/r8+ifyVoFfuRMf3Nrn/lb6ExBTUUzLjk3AAP/6cgSP9AAAAho4V1MPEGhBBqr3YCJ1COVrfaMMQ/EXoK2oxAh+AADQB+120RZGhE9g6jFsIeuITfSQonLxFgqKGqgOvuDRk6baUyIsxruhk0R/9f8qPyXp+r02gqeRayeRZRdp10e/5kBRS+kuopdLJj0gx3QU+uTnZollSZzl0ptmjqbQwIutUW3R92eFWZ6KvR8reVv1Ty/yfEua5ZH4u9lmf81Tea/7AQSw9NLPtbtA7d6EO7P3ycZAZnUs2qQr1SjO6+uvVV0erKe95a0/V0Z+DK9W/XJW7qaq92d7mT9v+j2S7Lul6auTe//Be3KgIFem7I5AaDeLbhnvfwHBRlPetURORA8zAZXr67b5Sbhl5Sqac9VR66090PvM7XbVaLHdGVarpS6KpHgxRzTdmDhPrtVF0xBA//pwBJegAAACDi5Y0wkZrEKoC3o9AhuIUQNtRIxMsRGc6o2BidAAAEilGykhhDrSim5SrasmMdXErcNPIYsskjpDIVkzlnZ/gHzvVNHhd0y72esKf35DPYIxCPfNo9yEOZf+76N5b6ApSvTdkcgIA+ZcU70u34aYXGNmLaXUbKj0Vnnf21NodHRm0pW/r/TN511TR/E9CQZLPul2sYO8wTCSx7tCOkbQwUMgAjcW7I5GCR5T9QaPQ+6kB0rHPNFMat2SiZh0apehdVJIIzWY/W3e/8tX9W0XP6X1I/8ikRqFjSguNqGN+o+lwr/WEPtvwaZEWFTu/MWI3SxLJ7cbAjGTlp6Z7W1GdLkZUbDPrczr99HwjkVFTyqlKNe3+TxBF3/eq0RajX1oycWTV+p/ioXTEFNRQAAAAP/6cgSK5QAMwg8T1RsvEdA/xrqzZMJiCHC5VGwkTIEUCOlNnRhoBO/38C+6LCxoxfWVRlBg4Yy9SCDHFDO9S1fLK7tfiXXdkCbjxV9M+0eeNbTgOvJg052ZyZcP+y2/J3LK3N/9P1/WEf//wF51AU6mTZzEAX4/qH6aDRmVXuggkvn2KPUirVZyaqn+bXkdh3ZdfXVZkdvbdtMna2uCRZZfVyf13X/TUAePx4PRXu7z20Sq5VahYtzljKZLktuSB8FluQLMbtMljC8z71S/8tLGdqEKWn//u7GLZFFxmpS1SYPNR//fzeTtT/YAJJJCNoFOZuFIsuw4aMj+J6QG17FnphLoc3lFRCozzcQwGGDMUWJuQc4H4hrbHuCgu1En6VqVKVN3d9Nzu9Kf7tQv//UmIKaimZccm4AA//pwBLRIAAACGVtc6QET3EJIG70gInmInNVtRKCmsQyOat2BieIABElOuPayShMq1qE8GReMA4zzXIw2TLEwwSvIJFwb4JLsl2TOQlrqekhb/5H9d277+y7r+9Xyr9v/o/Wmv/t/gm8KAkFF6Ryfa2gSMb3MU/VQobRy4maczT7MWntfTKuDIcp1ft5Ud3mRqv3byVfXTudlQ9EZSvVnfR5x01M3hRbE6rn9SAwvpvWyRiOZX0kB5o3eMH1dH1i2k+yZt3tlTjclIQB6WU2hdFSzsVS/dTPyv00spkFVSZMFBpJ+XuY1596upe5TbBX6ACAlySQKr0yEbqTj6O21GdxwsZvQRbvU8GKrpS0p5ECOl4ZtyvOubqN+X3Gp1KNRkisQJPHRpSCG1Q1lG+lGu7foqamIKaigAP/6cATaQgAAAhYc2VGBE5xD45rqPwJTCHSDa1SSgDEVhO62liACAEBpFWRtlejM48XGIMmJvtwOvT76aEjRETyqAvX/w2ae46TyHliDSoWFXWqeSLjxodtYw1Jqu16S0MOc2nLJR2dnrAACgo61yVXoahTXZmHvBUXtroiIk1j2Mmte6MmwaERnX7++TnGc63B/ygP1wG0f8QOKBhXLn05RxyIFg4c7UOosdOdYAQ/tvWRiEO0++FAab89sFviaJ0CAjnccjZ5wGTWlWXctbEij3UrkGFQHHtFg+4WdQu7WLDkrC4huGupUv67//+3EpsAgoKxyWyNptpZ0pq3PbMVfpCywYDZ9qEPMscKlHh9AOOeJ3g+CBeCGs+cBCGEg4J0fB8+IFvl5c2+71ODP1n/yjvIeIHJiCmj/+nIE0UIAAAIiW99uCEACRIcMTcCUAIhcuX28YYARDZct95IgAAAAAAAAAKBQKAwIBQKL1RBVomqkn///7//mVz//PJMT/+zOUdj62/+UwcMUOojmt//wNnRA4sOhhH/v//xYQhzueB5jwSQABbbRLbZa3C2Qw28cEvuGWVa9n+3Z2vu5mfFlFj9n6JD5xCMb6dkyIPFQYVPr+nJlGHEhcNU/5dVtX+z3s2z+uVagsgtO23WNpJPCVdis393CUmqWfa8hvGcy5CdB91+FDVSrGuW1L1///6Tfb7LPrDueqmpk6Rtb+IwW4aDX2d30yLLcYAACLdr/Yi5JMn33BR++/1hZDD5HpY1UXRf6t2dLGU7era/tmsi/VjdB3acSZ0ijUBWK4bw7CZ2sWH1B3bUeeZwrTU9MQU1FAP/6cAQ2jAAAAfA32+ihHVxDCAtdICO7CNlrZ0YYRrERpC/0IJ3+AABAjbbiIAKMHkwkGD6YSSj1RTWj3ndyBOvfvszCiKeFT/8QovF9HhXn81jFB69Z1K2REjzy3Ker/ETdn5EACAT23X1lzDA5IM2Ay2e/D8EWofe39TaL4wdb3nX/xZr+Ff75C/L9YV/h5Pn9qln0ErO8GrZayeXsrc182LNTOnoiAADlMxolNZU8d/rjEP47i7lFEmkRbttzvQrKYn20793D16Nt23p1KnyL/8N3Rzub119EM6eSn0bbR16Eu+lUdeb1g33FK6iCWg7NLvbLddtQTD2wfYcxc5mZ1T2vuJFAsnbFXP/W/Y35pC+Zt9HztDke3ZUdVEA4cc/ZE55cjgOaTHVv0pyEX4vFTKYgpqKAAAD/+nIEyJkAAAIZXl/oYiz+Q8gbKjDFYoi9a29EBEz4+YStaJCkRomEm9pd/Y5M7LzpD5+ATogcmN2Ov2bGufc895DsgVDqI/q3f/U//9Nm/2H77/VJ/J29f6apZvs+1v/V/Z32UNPvnWsACG6czLcmk4npOqfjG9f3AVj+piL2n1U5I0a3kdrq6urcPlyEMkcDMYacn/q//7f/+0/T/8Qb8W0IdpRes/TfUupPJAAh+5K2UoNs+qwyt7eUuF92byd1AmsrECJvdKuyIvIbVu/0bR/h15kC7I/8c3Rr/6Wzsnf+3vev6vRfv673SCQeTY5f82ABD/LUaBTt48rIH0LWVXTNzbJBcuOWqF1tSIRJbcjRYQZdkFhn1l0T31m7O6Xc/2GXpe1l8MZPpa6Joo8UzqYgpqKZlxybgP/6cASvWgAAAg8oXWjCGfxEQ0qTYeI0B/BHdaCgYzEPly30YYmmIJRDkjs1Zbhb/dcSe8bD4Q5XY7a23pB9J9z4f/r2KZ8IN0rIzfFZ8GJZsqfx6fRnWdaZVtrqNJHZQg3sX5NFKK8mEoyI5SspQMYNL5cMdSLZ51C2yhsgiilFtO5LP1dIQG7Nb+hcPSMQSV9hcPdlBL0Lcsj6w61WdEbcXbbVVTq0UN0EWcwQwghLJJpE5DSTLLoz+FX8y6j36/aawI8lbemeYSU4wwZ0jhY/swUJAn67FDs2XUhP8WEIdKLdSC7krSz19/EQABAUjcWaLceEVezR7bf4IvxDdZef5SGwgW89D/9OhRbMcEMifX33/t4/5TYnTDZ4a94qZcssdonMt4qq0NILNlmHcWTEFNRTMuOTcAD/+nIEjeoAAAInXtlRIRN8Qwb7rRgidYiRbW2jDEyxERcsqPSI1gAAeSVSJLgwjRugkTu6V0AyUZdizHxikSmvXg1zkIVoDEv9q3+cI3AydPbmMrUzIT760Bd1r+//r+ifr7v2eFplg86gTAIQiSx3aNOXwvZkpcP34F9g8btn9AwaPbOVe5DUEiHJmPvX/7Vb9WfVv/7vxK39ZUWamvLpVQmg/PPEcidphPbPz1QBAAKrbWjScaUiroq1Yi3Fv4icVTFqK2YEy22av678xzLVoZkRvvVCkdfb4I6/N2Q3S0ZF/0oCf/+nVP/00zfWneCcjQAIVRarRKcB+rmTtQ9r0wzKxEegT6rtrUmidGI707ZuQ4OgIr7WTXuh6fL6CSZFqtblPS1vIr3e4u5jdSA7OXPRXFFoSmIKaP/6cAS/kQAAgeAS0gsGS5RDCCrtPCJ9CRVra0MMTXEUG+10kYneGVbSPZkAQHYlaKqH9IkSwpO3d/g8AtK8zBBVI4HFbCfa/YDJTIqOzY+qco9Tv9uGu7JUDH5dCrP7///qAAAAMVq9atySWykP+NOJEYtZp4PL4bWbR82mjlMiEIRzKb/WvYcn3r/nG/VvX+3yNWXb+zOlUHspu2dGrarfw41K6wQAvUekTcZJE5WNiPQbg2ePxX7wikDhPMjjHQeU5daRAkEQzPJmXL+5vyo90A52RP3tvM19el60RPrVNt81vrpprTb/4dwyv2AyRx6NtztKpRVRjsbqXRC24k+S/TMWvEIiJnDOXZdO/cXUspDIdP/VD/Y3hk/P0hTuiiguVAlCs2UYzb+c3Pr1bC91nKJiCmooAAD/+nIEk1cAAYISWtnRARX8Q0I6QmNGGggFAWejBHdxEKCs6LCKVggBpSdjTbgRhHFS40HMTMge8OdWR/Uf9pN115frKcB6LJyiO6sRmfyNzZF0j+UzKkYMlJrhNhb7RFp9KpP/PH//4MT5zJAjwifwgHKloBV+DiqcLUlCGGrAmFxjSWmdFYNsJhX1jKivh2cwAij1ecpp5JPF+B8Fb3stPUXOy2R+j//q69ziCQAYmm5C7ocl+QjjmYAVjoMRJIf431pjKL3fO2H4tSsXL8jrajoviBzH0W/mX6vldcjodh5Luuch8D9tyUO8yP5csZbasoG1pC0+s57sFT/Jp7FqfqEGN5kryRP0oHRkR9jOr9nU10Jrb83k9P9W+rX7IqFvQdyqGLejMScXJiyLDn5FMQU1FMy45NwAAP/6cAT4VQAAAhBA2VEjFCxCqBvNJMIviL0DY0SETvEXjm50EwimBAD5N1tNpM8ZFjFbCOGL4qd6Z9lWbL5+tNQ49l1UR5amr1S6yJT9dP9Ttr0vt2rrFb/q3w+aPT+H3n0HZEgJ6aPqKIKM212+0t14LGcnO59ni19gD7BtvHPVLl6SzjJqtB9XF6dxt5/1m+S36iHvHfYxP7+2371toPNdXUjZbpcvx17vQEAHQcjabeGFxOmlaUMXgmB/9PjCm3rZyuxX2E2sZ0YyK51vT+f0ev6Ef02X//1d/mOiHRAZReRpUJEpFLRcV0XjCGPlnIAIJDcrt9ttsA3CUvMz80PlJbM6wehyaK0yWJflJl6iToIlHJZQUgTplRKlq0tKBNQqdteUC7hoM3glepOy9i0mADS5XRMpiCD/+nIETuIAAAIaW1lQwRP8Q6a7bSAieYh4uW+hhHgxFAaqDPyMnAgBvClkbbKRHABMm7XtWwjP01kgiBkzOV69ztY45g3DdEd6gzoq5l1IIydn9XItLqRUfq1Goe3ZL7/fb7/brfb/w/oAAJCUbk9skcKcljAxZtKuEgbrH5QAQXmjZnldufbDccSMOajC5E39Hrbu8A6kKu33qBJIoDC61scCRSrqPVLVxlb/1gJEl2t2fWSSVA4ReXUsWUBhNIrngi/wZmQ+5FGsnWcurtDpv1qZNmleKnV/2P4ONXiyYTcHSqNJAhPZCh+csOv9jfyZJ1kkBZGeCsHOdjOKhL8hFaBqPIRxMSDuAB0YYjYI9ABnWpFNUpQ49tZeaSwKNrcwysq9imGDpR5ADilLKnvfxRyF/TiiYgpqKP/6cAQ2DQAIEh5bVTnnEwhBi2r6PCJ5iKVrXPTBADERjS12jDAGACU/dbLAPI4RTl22W6K7tzbwp2fBjza7IkwerDovWqNn9a5O1qV2/L7Koo65ld/+yo+izJ6v2SR+nvt/X//v/6dwhqgABZKdbSTkZkNXoL1jSjL6flE63pnBaHl6J+coCfNwPlwTVkvZf0kK275NUZkfzey6XpU1///X/Xv/9P9qf8Fwwk5TjaSWlotAjG9nXmjlDBBlipKrSrUw7UdZ9HozRGSis0m7XayVZW2Yre93un0Rlrdw1t9nrW1aU1r6vdOv/2/V+z/+GUAQAG3HLW9B53ReZ8/OGEEQB6EcN9TRATeRFlZBe8ESUS8LHxYIg4CDk9TgcOPwQd7R4XlDjUsX9vlBB0wQyFOCDkdvoTEFNRT/+nIEV7YAAAIlJtjWMGAEQ6NbrcEMAIiNd4h4A4AA8aKwiwJQAgAAAOv5JrbXawIACoN4kEl0WxWrBJagJt2oTM8sKIkqXdWyFPnE5o/wxafd8RH1TR/9kgQJFw2lcJYfrOHte6b//qS82QAAAG25LbtbRZZBJbaEHSdGlvA4iBYkIiuZtXkWXR8jbgZWWuII/HLi9dmhYZ383itvT+pWyfX1PbmBZyFW8rc6Kpp/H4/H4/H4/////fzG/fz0OU07+5hg3JzHEdAyYf/mGc893Hi4hGovVjv+e+YyGajQ4w40u1jz//vzG+eflDzmPopzLKeGeeeeeX85X/////f0b/50NKn+QgfPIoTICof/ITnO5hIHCIiLqQn/OfIzkzigUWFBJ+nlIng2KGzxppxMQU1FMy45NwAAAP/6cAS/hgAAAiYK3O8gQARC64ut4YgAiGUDZ0SMVhETIGyocIsiARLSjbraJJOlvCk6xtAKGvE0oVrC2gs4OOLIklrBY8qdERamsJHssmVDPlSoIywGfK4atrMlaQaPWeebK9dblPOhrFRYEpMJ22tolJ6V8oz5XCmcV6VI4dtxlqzlQtLsv/Nvlaif8vX8z9SP+nlJ5nM/95S6t/9zP0fum3K3/6PL0RxIKkqs8AE38zjSSVReSRpVp3y0hNuoFdkpV1lbn35lJHqiOmJa9v2l6X0/9DKX3b1TLoX7GWolkdf1zYNq09Z1ySre9fkf+IgAhqdsiKTQiHjh48UtMxWMyis5/cq3LK6I9lbNJG2YTiFYIVW8iYzRwsyEf2Hw8K19PMMv9WmoDZERe7t7/kaTteMcWrTEFND/+nIEd/oAAAIcGNlQyCo8QugKpzHlUgiNe2NEjE1REDFtdIGJtgBRvUjRBKZyAPFwSVT8AbWx8s1UOjvmVkRoy5lSrUAMkMEpzPscQ7E85pMv8G+OdSsqMBU4FKbR49DSiMhc3+pkab6wQOSRJyDUKwOBKIC/sPoNs3guQn9dN5V3W9GlS9C3/ilm/aa3v25qP7/prk/06u6Pf9bUCw0ylqUkdxxLb6P6ZeL9QAQXuyNJuU0Sh5tUYIo6lOgo3uK8LUIzndgZeW+RIzVsJ6Ivs1G0/8//9e3+UV2/0ets9Le39dkt/f0X/q/vehIglNA8AIAFxqWIgJwPNaVfX/BwTlWfkkPLoK07PBr53eYQLDdi39n//6j/p6Dt+//k2//h//9n5CWr970Q1T/3sla7FOyJ4JUxBTUUAP/6cAS9ywAAAh433GjBK7w96AtKLCJsiLyvW0eET8EcBqqo9AmIAQSSdburRSf3TUOUzbPYPHdWBgNn7NTypIne4DllLKLe69UT9eZ/r7U2v+w/WJjh/UPmaEb3WEUrxMs9HvdvrHWmLagIh+dlrUkkuP1cTCnE4pRMYEYVmshm551Mfk8v50NrqzZmOhFBf29L6f9tG6f2lxntceoIkt729OL9PLVuUAAF7jlitzXHVxcrNhVHQq+7tkusHHdTJs0bZjIaJZfX/k/i/viCph/CHzBkLeGpNXcLrhxEBuswF9wSdVexdzh75q1YsMAADQWtkuVMo8cRe1FCgZPeJNqQNYrJUAj0SLPg3KCz2KLGtQxFq7b/QKkteXmOpblHqeHa3Mu0qYWbW5RHoua9B4UStTdg5MQU1FD/+nIE8CgAAAIJEdKLDxmwRCXbjRRieYjVe32ghE3xHS9r9PCJvHjZivwaQGdbC5S+gig20JHoZagPBDrphcq+WxX6YmlhpD+LM+1k00JPx2bEQr5eSUp+XRIau9961Pz/6HVY1lKlgFlh217VopxWHEFVuSuFDpAtjRzLWHWcFyfYWGZO0xFfdraoya+3bf9W8Q6PmZe/aVQlxHTlyxWVfmxe6fyQzXpUxICIQLSv1/8sk1WRmnPGtnbSYbMRpQYkUVmrHbZNVO0cy/9enCmbUIdXpvd3medT1FJ93WeqiM2b6u/9P+r36+ir80EyFYE0NgAgAyR/esu+CjHss9b4j7n9FsrZCNoSyGuVnm8dclh7+L6fW8tvhb6HWzf8yc2+n9ame/T9ktWzmp1Wh+3M/9EqhFkhKz0IgP/6cASeRAAIghla2+jBEaxBhcp2YMJiCE1ra7RigDEQoGkGsCACAIACtj1sZTjYtCZF4OLTqOzALxMIOu6adVQlbVT1VvXqdZfRmlT86toAtf2+nVdr/pWoMT//Vb31/9/N9HNq0OZ9lABC/q+GFEES3KeePZWHFp4cw0SXKIwUPh61ko757A+r9Pj76aPSb3T9qv/foKQ9pqwXcVaI2+0p/ahFtilov/1rQTHHbWklGeCbHAer+fgTd0BeMNSgOXRKo0yIrX7vrMnX+N/6tr7P4s/1fvUup4ge7aNa3JTp/6f//r/q2uImwtYpiXHR0QmptJirCS+HJxKqAmIytnY5dPmcpoQqhRT4Qd3vglfd6N6G27b0M3X/1b9U6mT6v8jTU2X/dNBqrtS9PucilrvHpiCmooAAAAD/+nAEhysAAAIZJNuuPSAAQgSLZce8AAiwV2DdkwABGAEx94QgBgAAskgIcZOD8G+BieTkHQpfLc8mIPcxnzcAEJlS0JtuFhA3efL2aY601VpQzw5YyYQrRqUc8P9pDsZIn137gbGf+3QAAFkABDudg/BJwDLVgkZpK0fSooQuOWquc4BQOT1TRH8eZNJCNmn97x4apbn284xr08J6yPba+P/T/4e7my+3+gsAIBK8aOOuk7q64mg4nS6smou4Rrt/KljTSw3yar4z49W3lUy8qcDQVnomVYpqkD0zLR4RAZcJGBFciyyJXPNuS7vd669QBKrl2t2223qNKBpwEW8JMBkGw8aEY0uE1FQ2hwo8KGCr19bgjeg2q8WexzMTqLGZofYOS+7sJi7Wmt9xCQadbdYiQ2KXQmII//pyBL0YAACCFyVXCy8Y0EOhu01gwxkIyEV7pgxgMP+IqqWnjGADjEUPbcBul9D3WSgTZxKllTu0xFrVmI3ZmKe0NnIzjkUfLYyUVE9S/cgmmdSy5Bnph9zWHVBLegfMRMgmmrXQxtvUAAAGVE44y3EbDQkSS+gvJJJI5PMH+PD2nGHNhQimaGPGEQVOBJe05lQTQeqTcqsKsX5CKzj62VBZEv0Ks3Wmvq9vuoBIiT1tkkabpWD0ZdG2czosuiCZ/1OkmwgUxcKtPEsCjOsNHnhoKA0qws1JU7K9qnlWzxXqDp1jVG/qw1/yanmbQaBUlUysAlqC8xcg8SFSL+QkMQ5ROEfDlOFqV1iTZ+LL8KDbCt4aTeKDceGlQ1DtflXUflc8V6g6dFTv/kvFM8gnpyJUkmIKaigAAAD/+nAEVD0ACAITEFObjxhgQcb73TxiBYhoe12ssEGhDiBw9FCLHgCWnJACFjhYJCKQDkMgPoCzViNR44iKx8NRNbkDPAnDGJJAwoxSAGHschCXVLatiqBKfdLG9q/TnSPzyDzkf/9/8mEkmppZLrZbrEXiSRGd5ZOLc4V80j5THQ7Xo82l6VK/YsNmfVtxO0qaN16GR5f3o76jJPbQVLKEmJbtrv11Lfr1dwAjKVluyFXwlYD4Y0Trxa5G800KI/gfXQQ71YXK706MyH/zLVTvUS5Yjz0PvuaHjoufTupFGEnah6d1QHd8B1Uo7P1ByVm7+/fff8aLB+eQhSPpqhUFWdXOVqOT6iATezuZinJaWEbgB3p7ddBAESyGBtl/hvbZN7ylcmBlRnX0XJ17Xuxf1JiCmopmXHJu//pyBESlAACCEglVm0w5qEEjSjFzCQoIqW15pYRR8Q6G6mW3jDQENSSUZqjU6aTPnX1WW3o6VJlFYVrKh4KLcoLuBwsiXP95RKqwaSZdgqle1gotAWH2vQiigrKsp06rH+zfp/SHNHoBgCycrT5zkjk+ijcTR4Z01lmEah4zkI8aD45KYLCrznV0w4hIUQpT34kt5v25393stHXvVe6fSafGCrqjKOhWcQCQU5LJLpbbgrMzrOJ287jV+85tmIAiZ8T1xuWlWILYZxo0BjF+Gj8tkz8F/rMhxnrdHZ0qy299rtaXtf2b/r/+2/hy/1gQB3/BeLEksUBdnZPEmaCriMsNjICL28JUzCgNJR8xOgs8sbvBkWXgcGpAkhdpIJhSSbrLB6Xd1gjGu1os1e+1H+760xBTUUAAAAD/+nAEf/oAAAICDlKVbMAARGFKuq0MAQjQYX+40ZIRFYiu9xowAgG/ILcYCz3rMJBWYoPCADdZLicZM/s0h2fqc04uqQvDqFOcOhY/h04wzEpJFaRj2nbAOHdBFLaDfv6P9ln///tAAAJEtuSUUrjzp4Xvi0PRmGJXYz3E4beTlSvAZYsWWhqjrUvYnKtoBCNes4PCBcl0Bqm6PUt8YBbShqw+jN/fp/rr8NgAAABJt2vSa37TabQCh8WA4Swl6mLr66HPUz5QTnXsFKKy7qcK4Sfahzxc8kyxUq9Q8FhqmmN8kNa1KqqHdlFFl3u///6UgAAACIxyy23bfz9/wCDUiG6iMGm1d6Mwp1U8+amUMyFuMUkS73mYhUgmoTg7gMWqOgHM03wBe+F+h3j88LPuRQh3///SlMQQ//pyBOQ7AAACDAzi7wigDEGCHF3hlAGI4J+HoZxtsQ6NsrQwno4JsxOSyuIgl3Q41nPe7WepwdcRNrChBBCp5Kt9b3rIlhdjeU4hwS/D0opzqSipkrXwOdcr4pRdLDXEVNKsMyKwkPDbMSllriIJecYa0zzpqeepwdqB0fq9BwOgxU8lW+NeaWRSJflOIcEvw9KKc6koqZK18Dud+Kd0s5xFTSrDMisJDwoA3HbItGUmC1xDdbKjzhhLA844SwpnP0bXr5nO6haoUByNaUObMzHRJaoGslfOyo2tT+8XWRLEkppLEC2ViLpXcgit1Z3sEcMd2129jksyYaJT+nmrRVNeIzZRoHktizaghEjKF1hNYK1ndQNPO6n1uTK4abaR6VnRZ4Vc/NnnHs6p/nSsY8lgr1JiCmooAAD/+nAEAMkAAAIOQN1o4RasQWgcPQzCcYis12ZnnK2RB6PvNGQIdgAAQgYSVGUoBww5rZUWWUhoJbY36ahss9fbV++gk9eX/U6qRqfwpcQ8P/Y2gqj/p0qCFITnup//7Cw9YKvdPCISqBILU0rkurl0Lg30VlUzo1Y0IzF8Vzc7avrwYnTeTp/Po1uq+pfNy/+bQ1P6dKoOjv6g6VUtbnqqryz1iV6lvLFUkiWSWivl5tGaK3gChteRQ1tKcATnNm8q+rZUv/DXaeDtib66NRnShnt5vymb/3F48ADnWLxHfR1NaQ54g8Vo9CykYGJWoAgESFhiSOSg6tKNnJTrqLo1JKBFMnAevU+Uf9X78D6/b/p9qNwY9FYn/6Wur/deoQXpY7d1MJukKu8g1n3PeMZyaYgpqKZlxybg//pyBAGuAAACFDXgbQjgDEJoHA2hlAGIiXeOeBUAERau8I8EcAIIgssqWP2OTBXUjaLcw/V8/jt5R5gsDZ55ZcTku+os49JKVaw1LmP29fX9V+m7f5U6lXeuh11PVFPT/vqFVKE8TUjAGQhG44rbJcFPlR6N85hKeD8H4ebZ9g3R9ONFGqmoz/v6U4/vv2Gijk2p+nOPa1//IHD/irIIMHdMgtnetSkwzYosABWKhQKBQ+v/////////75h576/88eGGEA8M0b/8xR+Lwvzi5A3//+aK6E5GUBWotj0Fv////G5cqcA2LQ0IyecFwDQABAIRAIBAO5CDFWVkv/0///////vmHn//zyBhg4QM7f/mKTKg/OLjjf//5o3QmPjgJqNxqA//0//4vJKgBY2Fg+TnAsCRMQU1FAD/+nAE6WkAAAIDDN5vCEAAQMMsDeGUAQis2XlBKFERAZsuqFOJ6gEBAW685CAAZqFWoqVhufQDLgaITLOUNDhUtucwmxps9fKsFSy2rdZ/V5EO/qB01sOuiIrQ+wWJUrwVKugXSdGJsS13TX/IABiMELfgOH9G06APuB107tRer9qBQdDShUCGiJZCQFCSiQdF5Kr/Z4Gf+RDbqiqKjzZmpYq6/Rbtx4o1XPS2NpBWYw+pLAgMNgBcA4Xj66q2IwsOaJA/IiZTdwV6ctJnVW1K1e3m5Sob/UrKX5gIBcN/qDoBTiL/lXVKPOriKeADZuN2xpII5g1ph9yvYYBdRVwk4+2aaqgsL4pL6NiYlpyjvM9S5St9vNylQ3O3lZS/MBGZGG/570/8q6pR51csmIKaimZccm4AAAAA//pyBI3wAAACHkDf6KE3LEEjqxo9ZyiIrW15VGKAMRKQrraMUAZkIROOpP2y3D0o+VrHBjsgW9w+2DM8EH/jG7/qBOe0O9R60b/Od/TWRy+vxy9/yHvszOPUHUOlyDowpbtdofdQgc6TpAAAIESNyRXtU1xtpIiWEq1aD/VjNegBLYl1caO1eIDNhnTlCHL6jSWHIYYfWvw7E1HlnKaIvigqM25p2z0f+ygqIlXbL9jlsdnX/GfSxinsBbYvDvCTaNhJu/bQI8QjRcXd1Eh6sMHP/fT/bUWpfv9OibX63StEFt+39Eo+9d2rO3mb/8Y6AACgSqktG5Lmdw9s9doye5DMG1FKDGdpHRA0E0H/QIB16kswdHMMHJQIMVpW/vkuXouLMEdibxIXLbO8nvq9969U8tPemIKaigD/+nAEy4cAAAIJZVwGGKAAQUyrocMUAIi4K3u8UQABHAzwd5AwArJMR4/aLZ4QGojmDNGK7D1yW/+3/9bJ/+vdP/9ipFHb//9Ti4gOMi///n+UeRhgkiuAH//vv/8OCYsBhwohXUXHAktowkRofLJUm0F3ToiLo6f+jf37+T1//690//2KkUdv//1OLiA4yL//+f5R5GGCSK4Af//3/9xQJmDw4UQrqPHAslhy7WSMgC6CJjPeVH3B2w6C8QjgdN3ZWse4wGlCy9kN1sKywTDVa7L80iWFgLr8MoqHBd1mB2NFTOBg1F3BNSQIyUF1oJxpyS1xtFF65aD1Zlx/dhvJgOlTP7wwE3IBFHtlrPorVBVQstlluxcsEw1rsvzSJYWAv8M9QHc4iKwbYLCEBhWBvCeUygumIKaA//pyBEJOAAACEhlbUMUzRELDK4oIxxqILLlzoojwsQ8gao2HnKhBlBjS0kAKT4tBq5j650dQE4h14S4Q4wdB05pFD/uEcu4RSLcqMes7DT3KfyriJZ2dEx78kIfJK8S1DH1hIDHvluSRVG5NxtkmkGo6bb8to0sE3HM4hx7hRseL9tGxOPRQyDY0BSJLKjHrOw1GKfxK4RFudEx78kIfJK8S1KfWEgMe+W4aQBSBKgqlrkoieIhmw1SW0CO4T5OHGwzYIHhBJHZsTAC9f9u+up7LM7c3pKxIxfdPo8697s9rFWvR/PzQUNcM9zGAB7XbQdI5Ki6REW24Kc8XjjUDwKdtSAfZMWYdYTLhXNAHGFlS/bRsTiPoM6j3TrRl9v//HFSzf0+ZVFT7fKv/p//I9SYgpqKZlxybgAD/+nAEGDQAAAIaK9rVIOAEQQPrOqScAIi9lYR4FQARHLKvTwZwAgAgHCM229EtS4MVNCfUh53Bd4m79WVR8tlC3/nBN0vKvq2rv2znn/eUIrUwRidRpiilqGuJ+BNvFL6yL3sFECoqlnqAABoM3W7CL7LhA9Khz6oruUE9odyLYmtHdRwN3Ktpyof81Ra2P0Gxz0pAdueidjD3Tc4lqhoHFhRVzscj2VdYo8AAQCgUCgWd156v/////////MPP///C8HhIhhn//48MsQCwIsRZP///5OQEjSQuYYXC4/////C8JCNwBYFMQ4zC/J2EGDWAAGBAIBAJlitHlJM0OnP//99G6evv//zL///g8GhBDG///GhmOEBLG5f///yY4WaY5hhMRP////wkID7gBgFiWKwfkzBWDtMA//pyBDS6AAACFlZingVABEPKzFPAKACIjGdvvIKAARCMrveMcAIAQCAQCAQDusv///////+eRs5zf/kJp549Vf/8jYjGg+iwMRv//+cTDcejIQAslBwWw2//9fkoXo8EUKAvziEpguj8AQCgUCgUD//////89bnN/+PSEjJxZVf/8ZmCuJg+cQgxE///8fEwhxZGQUAXowFAtge///8eg0iwDcFkGsoQi6wSRXQQDKlaqlJAAyICYauwa3jfkUYsRKWBXz8T6CpSlfV9AXqDsFcseFTsClZZ/5bGjXaSP8GgV1naes7IMg0sKhryQldLVkohBJ0RyJFHwxQSJfr9zj9k/nci5pxOlOpE0376hk6ix4lOhoOllArInWw7/PSpJvHP/LAy7K4/iWWBYXLB0TCV3w1sTEFNRQD/+nAEyE4AAAIdHVc57FEwRCOrzQxFZ4gkuW9UkoAxBxCq6p6wAACADbaur24gyiHyMAsHOvb9H4xOuPuIJsheoMjfIyf9Ds4Fb5vJ63Ft3ypav+ZFl5YJu+Vihr//xQ8sBWEQk8qROtCrKARSfMetkcCDcwnR+RbzcvAsS4MNAdsoPjR+r41dD3YWlXk3d7HDgKmPD23mQkKsqFzPYoCiooazH/sdCj6bJ2si5r4A1VANtkko1Rwf1JIfyP8oeWZeN6ccD6i/9saAnNIwKLvZtnZbVVn/nnPVY5wQsxMeQBM0dWL8/lGIR9zsWov8OAAAGAL/+AXRdwRoGWc0wLsWyOfTUWsdzlial46q2Dpe9hw+gwAEQb9x76P/3yVjoZWv9f6X51yehvJLYMjPd/Z/SmIKaimZccm4//pwBE1wAAACI1Bc1iSgAEDp+5fDFACI3QFxPJEAAQYgLhuMIAAABioAAFAQBgMDgPosHZE5yZZ4fy7rr6i1Zno1GjHNmHZz9fTs1v//t//6EYUJ///ni6Nb///5hSJgODnO5A5//0OnAwAkEAKBsdkAwFa/nLEokHFJ7yWn9dyXpjNHZLqu/L7+Tn///qn9OhGITt+3/PA6NJ///8xJw+LnO5BD//gCCBRyYAAWBoFvkbNcQISSj3uFeZtoPQrSNoCZMf9OHbXLXVts1W6tdnX0L46rZ6/mLzUKjr3lzKGe1zOKNXZpW3Fd+dYICqmlZYfAMmgWfXO3BCQSyD5m914fQbT3ZJR+mjUiW8tdW2zbdWuz/Qvjqtnr+YvNQt/e2ZQz22cRNX6VtxXfnWCAqppWoKpiCmooAP/6cgTe4AAAAh9A31BGEORCZcuaCGIUCMEBaGWcTtEVIC0cZZTaiA7oJORySSDWxyldfusYDBskT6Cd/1oO22jZD16f//KrZf5pVUbu2byiTaiaCkf7mMbBTzSisFdNURWHU55BYs/rctOAAFkH6qbA3hzjAngewLMTCYITunWWEfRe2KPXgplYrdNH0VWy/zdRqXJxK+dEXg0eV+g7ZihY8DSL5aMPB3pJWvsAAjbk1pIE5ksBgSmIidq+qELCjKNs/E54qiC1HwWhxX2//TvWK6eVm6tlb+g65ndH39WloGed9uDSgrc0SqztGGrDpEl6oAAANpyTDCg+gZAZqgas/1nDXAdowX2X3EoUdqDPwQWry//00fT95W8vK39H6PT/VpaCTzvnUQ6o9cJRKrO0VkmvCTCW5MQQ//pwBBZkAAACExxd0QI7REKMS5oJJQ6IgXtu5YirkRgMrfSQCkDADGQlNdt4XGjsMkPiLoZVBOB4Iu/klyLUfvUNfk7esD3IOv09hnSw0KiAYZBZyhhB6aM5yC2v70K+L2GjCSbLwoLwAVICXbbtGpXbOpmrHO1B3k19Cr8G/pG0O1F74OKV5v/76G0T6UvjGY22318lHVv/iTfb+961vT/XbX++tavrbXQZBMEuSW2L3kbFp4jwP+V6gj3EvR8hqw00GF5tXqBD15tn/6YztO/687PWv+cdo9KJ5Z2fJ1RtfplXv+verf9fq3PFC9dAABJQBGtckUd31EbJ2Ehvunnp35Temb8EwTVqH74hdJMj9zN58clQIDmu+GFO4fw+qt8piBj70NE8T2TgjYfP3sPiDcUQmIKaiv/6cgSo3wAAAh5AX1BFEvxEiGu9DSIrCJTfZuecqpEQCa1cw4lSgRy9FSNyW9hpRbqBZe+A3HZy1hMp5V7aaB0N7Ren/tm2eb9P2/6KUI3DUTSllbqHcoecpelYoeCZPCYsmaR6HMQhly6AAJZAAtm2mCfUpb3Wa80/3if8idBiP9j79OPjp8uj9ProtnKSvVv/9i5Zu/dWKj830WgCVPQ0RPVPwrXMrrdaRZvwKiAQAMjkks9lcgediF2qNrUcoKwmqB/HZE5YJ3wfR6Ob6f/29ioq/ixX7TEUjfdFXjlDe1y0oj+lRo5W+SAzqL9ylL321AaABjbkz9fUecGy3Q2qSbFITYh1EZPBsNUd5emJ8t+qRkiTYlfLr+oGr8h+wRW9Iu0lW546lYCAwdDSBEw7DQ4VaJRKmIKa//pwBLhMAAQCFhjXmG84cEPHK7oIwiuIbQFo5LymURWcLEzzleIADS7YLMhUGLQKEnzUPYmdsxqXY1j0NCeUBmq85wD3C5Lj2e9BHdu+KSjwuTftzcj4WVULr0pSZWlXMWVK5n9Gp1+3ACOiA405QYqQaBVrE+q/3QrhAfC5/CubHfJ+Qnwv/9HoScHN+r+IT/8O2pqD2Khp49X51iWgzyjbVIU8xhxpwD0Q0cgAKklttjkV0NGFm5NtwniorgZqN/aHJRfRddQZfn0/82MyEVf0M2ogiff565KmX857VCB9MOvSgP0HWo1Bvbo6GVPWQDG5JL+zJWgUJYVUpbrVbd5XkJPQDkxVkfQuewoDZeh/bA4jR/2/24wshT/0M+ivRv+ZdRWIp7bEqm2bANnSfMtu/ZpFkxBTQP/6cgSoOAAAAgZA2BnqE2A+4yt6JOJECRTldUMcS9Egla9oExSmAE33/1NKNBL+OhCpXIpE0SJOgzpAysXDXM8XGCDhUH7yiatUTGp0srf2rpz/qjWzAK+sn29KNb3dqVBG+3V//68CAqAM/tlixRzFBoWyIZuj4baHbLhk+tHfbtiBWqj1g+GawP1vB9VcnE4fWfUc4IHPM8uCHxO/bl3nA/ievP8AiyFJt/+ZPQX3jWALU3IYmxNz/WlCz0P+JgEiyPGvF//yavp+kEnin1Vzl6sgZ3iFDDzwFIOeUDzLFqUFlrOurGq7UbsNpqqSZrkExIAGpD2Gyk/B354CZg7oO1/GUfQmrZ2+EzqhUqa16fVL1M8w4kDSHMXVcRXixI9oogZxqdLByq2NcLisrENyRZm3CLUpiCmg//pwBGqnAAACHFreUEgSnEBiO80M4kWIvOF/oKSlcRCgbeixFXLAiqgSpZbQTzD1jmHu9YoGsO0E+Tyy1I9Ta6G/N//Lo//95YSab72dV1DVHo/PeqKCQzK13raysxK6Oiondl13R3/4JghImNAlRolghuD4ca0Rrc0MCmBPBC8npD3qPrcVts+5SiLREVlXZ3iV0cDNlQ39YKnud+W4lyR5QdxEJRE/uDaAZSlcScuu2ungl7UuV+hovNs56D8PpwozypoO14u9eGa/++Mfb/GkeqDUdXnX0sSzDoxWsMC4qg6KERSPu6ZEwYnZj5CqQQJAQ9t+FJk6R8K23RbumCATuG4v+CYJV+I15G+vf/cPs4wNytbu8d0Yt3XJ0jucsaejbPdVVRgsRWB2uj0lrsVWm1KYgpqKAP/6cgS/MgAAAhIZWjUk4AxE49v9oZwBiJz1d1gygBEUsq7fElACAIDff3uhse6VNwBm2M3kQTaOn0fM6l2JMiZQnsyisByZWZ8DtfHdAclH6q1NUGzWo2LnRVzIuTsY57g3a1aFqs+HoiEmZIE9dbsEF1xzBmcF8BFwcgjNHxc82ybPU7l23U8Nk6tqe5pee4TUbT1JrNMWjUBoPLCAytWX/VeeNLInhVjwmun1gAAINUkn3f/f/AAUJRP45pagRWIGdzSXZTnU7sOKZAeyM6Vbx3QdzzmRe7XRZ11s5kUrMR8YqFTdDeWZ3Iutoi8WaeoqAAgAFFAgEAoFvMZs0godTJ68MWqdXX11ao6u2V0qT29udP/I7kb//sTO////kaRv///uICjqcCNDjf////nIpwO9CWOLpiCA//pwBDcgAACCFCVbVyUADERjG23mFAAIHLtiZLzsEQ8a68mGFOjEACQVJGxFUFAvSacX2tJuWcqgrPrRI0v62EfJE9xZvszA8A9VU7wb/N9f/yOrIHvOP0tbMLF0qZqh1bu0q3///+GQAACEYCrYklv1T2hMBdbqzvxTqiUOvlZhCiRGlR7/R6Bp3Jax+t0UYsKlHAVhFXqWxNCS+M33TWcMUZdtOMUdSqMymtoBcttmOsoeVA8RqRCNmhu92Gq8JXofzasriyOO+LO2Dp6Pksc//jz639NU1JzlDitcVC6wfnk7Wni7NX81jSi/qA/wbutNVgS4OIUk/Ar+wr72DmbD1M4a/OtCFcYxQ0fiumoqlOTRv+vexUeptHf+j1lIaXGgERXBX+o8Hf/2lutyn/lkxBTUUAAAAP/6cgTRPAAAAigVWBnsKcRCh1sXLgIoiIzVb0KYQ3ELFyyc8xzaAClu0pFwMFASVZwwgb0sRPaKB1+wq6hPqwQg8JEfAeQWf0T3oh/pKqdJc6ezR9IKmQ7eyJngqk3W95gJqREdyziDz3eaAIAFLbbKyRFCEBGMDehkNN6RWaDGoBZdGO4fHeg2jZD0/Rv+TTo31Qztqyq10ZcvdtXazBk9jJLI3Ff+RMpV5dze+ABBwC5ZYxmBlcyg31kSs5Q/D6C8Q1bsSpOBaPBhv+rf8+w+9NDaE+yOzte7Y495MUQ9obCIUAEBQqKOFAR3MToIU3fbCwEqbbCk2IptVE6NRfbWEL8IVFfK5ROJy5DviVo2GxpTn9v+mnX6Jnby5JA7OgJZI8OsAR869CAFA66TzB4wj/SRamIKaigA//pwBLxWAAACGyVYueZRpEMFu0o85TaIHW11QoRdMQcOK8zzKPIAYAkuumN8hqVlNhEG2ugu8ngeZBCPQmy+Ywx5qaDXPfFGnNuY1v75R7vTE+KMqQ0yFA8BA3+et8qJlHTVBq8eba3ycABSAlb7cSV8ia0pi0eUoKtAnwQfiGuth4/I2HfwUvyf/6RgPiukxkuuKiLQuSBAXSwMccPf707ugxSLDYRE58XaXSiCLRJlskt48cQSI50mbGpYPNi57KdGEHX6PjdAO/x7z//ATedkQxuApugbk1faAM5s5e8f1jf+x19Sxm+l//4RwAnLa8WwPgrYAs4NUABemBAIM5YUhAEh+TvZPs9gYGwiZGYWxW1bFom+T0/pn/GSHQxIwW0RefzMX5Fv1PFditvsTEFNRTMuOTcAAP/6cgQn5QAAghcd2RnjLCRDhhsjNSI+iDSNiaGcS/ESGKwdhh0aBDckk+4aEI2FDeg/4rrxsTwn6ziGX+/Qpk1eCAlDbt7u2o8NdZZjIItOyU5f3qc4gdsteoslMkgC6sdONbWKbP/T1ABS7YXYXBeWB/E4CZGfoeYgsbCFUbz8hburmm45Rb2jZdWwpuWRN6/yfs6/5UovCWB4Klg6tbiSMnqatCsQo5i9ifHYwFuOtyf//+puj2isXST2DBHETUxtMtjWxz1f6hUaanRJQqPXhdA8E57lg5ZEeGloEUeG2JPOpkM1yrVLT++pnh4QBTttG+1WeKMXSwWTkR5lYlDeGzxKJv23ahALsx4cJmNZRzU7UtRgoyuXNSfffXp30/zLJu86W97yzWsIo7yHd9KunpTEFNRQAAAA//pwBF6GAAiCESTbUYcR9ELDayM9hUaImMFs9POAERMM7V6YUAKEBUCBdf/KotSRMieNYBlFGhsoD/OA9qk+fljIaonbXs3aSBO8F3zGwx8qjp891H1JWB9QCnRrKj45yFt7t0DzT/WAHLthmmTuNXicj1i1aZZHTp5QM+xn6E6UDEYTqR6m1fHgXjC6jN63Kp9UDxNLOfBUw+XOw45d7ItdBneKtRe1iK4oUATf/2jhRS6kbU+RO3HU2Pdi1UImyGxlWeWr5+vHfWPnqa9f/VzrXbc1a5g6YmPtRF8aQCgqzNZQWva5Sex9iaiZZfzIgArbbZpQaIbHIByZeWDG0yZtYeZov9GxCran75A3a5vya1VROs/WLJa1GIAy84OlHQffrNrfkj9dhQ48zaIFHxt6r/JpiCmooP/6cgSOcgAAAhct4FYwRIREI0wqwYwAiM13mngCgBEHD26fEjACAAACBSAMA2//A4G83hQdwG8AMTRi+u8rmgNGedXKPO+LMR0ZJ56MmgvzdP//8jEumgYjABJcRrTQSJc//+XP///o0AgIK/9tv/7/7/78b6RXQbhup5y9zMncuDmTpmnNynEnwWyee4/NvgrbMSorU08Kxdo3rYtXiMKBawG2MIrs/LjXi9fpAAA+H4/H4//////////a053Qp0N/1QlBRCRQYFh0VUOiQn/1POdw+hBAOFcOCcPjBjsD//ZUYOMdCUEGxphIeCjjIUOGGIAAuUrbtvxwABYvZ6CoN6KSUSF9XleMgjCLRhyzNCuIfI5cQq9v9S/rfBB+XJoQTSVgNtDKgKRY8bDS1wP6ELDZJCYgpqKA//pwBPrSAAAB8hlh7xSgDEOmG2rmFACIvFV/oZRGMQmOrMzzlcJiAFuPyStEhXvIl7XSoMDFKP4vFVFjDEBxUdV8gbvxAOgEJKNEg00O9agpZEv/ivnjvklhoDdX/u6uRz1Yo+AIBQVf/hYI/ZG7Q1F4d7n0CB1pXIKwFtAcsgzveMZqPolBgJritUKxuugyg1DxpyvSX6oa6B5ic00yH1sijf/o///XCwCi3C1tpJaPiCBRQ2t0xmsNqAaIwKXobBDyUjQgqGF1jrwfJOWcDRJqkRIAegGqFXflkLMJ1GjzvldQ48t0WArjsqMUPAE2248/fkfYznERKrhIx7vhQwsB3AmY7Kw20KZsfo2cX7QaN30FTQN2A0YJGnS9Rg4yGbL0J/8vqGuAZpQnqCVDDqYgpqKZlxybgP/6cgS9RgAAAe4hXdCiKnxBBKszMOJUiTz/d1RhADEYDq8qjFAGyASkta620bh+NUBTRWtFaY3D9aMCfBAGGHwTUBCcfRAUPirpxB6pGf7trgmr1Jrm7num+ecxba7XuodX+GQBbbBZ/gdDLS8CIlU24FrwnDsHPH1LjFfBDNv2xATvEIIO+LkCSSUOPLvLpnyDT5fwfPw+Bz9uzs///Ln+cU7wfmARpIzbbR8cQgaLEjLQxT/1lXbEaBKDOxEAxDVTTR1+0UX9OM6o5ZnN6d9fEZLKRa1M2e7iLnrRAqm9pYCw+4Xn8RgVJM+GEr9UQKOlG2NuP3PJGuaM2nQj+Hw9aAJ9Mcj6CjVN+cvariRZTSixR4mGBEgTeaUPTbmLiN8VmaUUKWozlGPVwo9UwuQuYx+/YmIKaigA//pwBNNfAAAB/l3elhigBEOoK5rEiACI7GVtXJKAARoSqw+wsAIAwwwwxkW7UJkawC3M9+D2dH///fv///VFf//kIeLs///uQggJoKI2v//3KPIRAYjCo/T///7C4gOBRZVF3Uw4AAABmZFbbYbDYACIMUX5MCphOAPLO8ZdUOwc7O7qRRQ0Z1afvzgV6jeXuhrKm6Ms/uXV2Z73/4J6vX3X//DUm512YVGABwSsZJNVYigkyAYCWg7r5ek33BtG0JRlfZ//EQZ5JLhK5Tyrs0FTpAsDOeo1Wpuhogn7hGAmtiUq5bdus9uZZDoiLNw0aJDkgA5LZwvuPMsqPoclU36X/TJxyqiutOoGrBkbOSJp35ggh2lhsZ2w9cQd/vhMn378vzh2tDrl/KM1K5Vsxu2ln1XJgpqTAP/6cAQOSAAAAiMbVhMPOtBEY7udGCcBiFBni6CIo/ELDu5ocZ3CAplKncBYLZpWOCCokc3hTEeldN+rnmovFKSvxS28uXu6L5avbj2p7YVIVlkf6qHHrT8EHakVjxAENH8Or5ao99d3/8OwAAEtkMyIpQwZQdKnB6opRWkdRJaUHfo6M1Cr0Kd8qInMkGFeQpIVgw4hxPKBj8nBB3KBigTn/if+sH+77g+sP4nB8H4aAnJtNdY5NdiSpeWNqUbD6Ab0FY1gCHPmaZu1ABG4sfFP9wrxaoX/Ddd2kqtZ1VL61BV3fufUSix+IoDKnArOywVgADoNWNyVHc4Ox8XSMiu9IGKhLRqV6Eeb+OBLz5OOIMNQN2OFeLVEvzMVjNJVazqqX6mO37XPqbCz4igMCnArOywVTEFNRQD/+nIEuo0AAAIUE15tDQAMRMarmqOUAIixgXx4IQAJGQwuawwgAkEAEksWFGpLtiLGUVjwLD0E8lftpANpEuGZ8hnJth/T/6n/LfkXTxXlgaIiUPRUmqBTqgaljoBOtDvoOw1DgKt2opQAaoI6y228UC3HyDnyd1YMOp+L7pxr6Cv/x/1OHgp6qdadP0f9vm/76CwSUeKgYuwswise1TjE0YYlKmuGPU42ijRe2kAgcfj8ej1HmnEkAjzPe//6aP3///f//zzy/358EMQQGT/3znfhA44MwoodP/ujZBbyM7uxgiHwv//OfEEbo3QUcmI0GRQAAIRAElRdvqLhgGB8OYJFoIGgSVW5bQodlS9SNaQdcWbFNqExPnLpHqzYXAIqRTSoEwCDh0uG7P5EFVbzNJn8s0+aBJ4dTP/6cARtGQAAAh0q2+8k4AA1pCuN5KgBiQzZZOWUTZEvoGyc84l6QAASagUkYBFxiIH2H0ItIzFafo3uX49dso5zbkW/NZRFDC77/nTtvOqhqUaxQ+w3u6Fu1ILf2I1NdaubSyy0b47RpGoEEoqMJW0AC0uNE9dYhkz2dj+P460qDeLLFCQ4ZRIOygFpLr2oLydcnlZ95zZFxYjuAu6kq4AgIjKJO2BHHLQpPCVIdeOK1CGUVegd30cRZoWHW307AvaAOFClspjUebluUpWDdDf/0M+hgYCedgy78NEP/O1qPZ4795Gs7LQPBM220rFs8LXemJB9p7lcF4RMwsbCV6nSkLM9CLUTtwKNkKSh8G2CfR6LuqIq26uqA2VXF7pv3aTq7evIYg731rFyw9S7kdpYw76kxBTUUAD/+nIEUtcAAAH1JNcZ6xH0Peb7Az1FRIkUlW9Uk4AxKJdtqpBwBwBJJI6RdDeI9uP0lyqobGBJSlANT/aHqDu+p1Fzaj+Uh9Oj0DrywE7rQG+7aswMYhHfaLV77a3f2dKvVd//UAHbbXhxiHEK3cBnL1UeWvHASeB+A3Wg9wxJz6F/GAH3nouvd3vWtkZv5uyH0+t66oKZoIVmrGJdnG0fZ/6oAFrBUtkkUrBS/qNPt3Pz8h99BuyDgpeSFRgUcTn99OiR8JwGEDEe/01dsxY7LNfHLKyFxVQ2WOMNh49RuKR929kw0rZqin8OQAKuSXLJAQZQchbGFprzD5Qc5Z8U3OKOODJpE9SwTk5T+Kwib+v6/q7T+voz1jppMlfU/luUHIIXPBUPWI9tvM/Qj38/VRn/b/bTEFNRQP/6cAQiNgAAAhJfXRYM4AJDbKvTwZwACNDhcbxSgAEYHC2zjFAAADDDDDjYcGOKs1U06UXnX3o8x+/vzffyF//892MN//7EyAYG///8wdEsXjRVMb///xLMMZyBrqxX////8nmmJNpaQABQKBQKBedop+aHzfr9v/v26//2//7nuxhv//YmQDAln///mDUJxeJB6jjf//+JZhjFyBF1Yr////+TnucahdTzSioEIAwxOIgApTwKZBIqXtoKjjGGNvoRBIwDNlH99MwQArbq/q32/6t+or0H9DbfIbisQkudYJbbNj1otb26nyUq5pBSjy8DAEFAJRlAB94ATqOUbsPG/mnM1qHbSqOOxgOah/6ff1b7ffq340V6Cz6Ge3yCz4rEIabnWCWSWzY9aLW9up8lKuaQUo8vAwD/+nIEa14AAAIXQNnQ4R6wQKgbNygj1gi1cXFBBFyZFqDv9FCblgBgHBViDcV2ADII4tPVEVaB6435fNPZ4XbFBP9OJif32ah/58w/h+ltb9Gu5C/w+0qDOoSZJ70OtiLbVBpwiQ0JN9QIQFWQ3LsEsTLjhNdCVcuKMrz+vEdsXF+nfcRfSUiZqH//D+f0v36NdyF/n1SoM6hJknvQ62IttUOuESGhJpvepAYGgldJblRRhqlFSfR8A1FfB2fDCXo/6G7RoyaXda8nUkI/F8zwqfyEshLXq6lmWtRO3ZKf/e9f0/V70MaiHCsCV2++oLILjLtDesUlVLNVmiiC/IuIPOGvQOulegO2f8oBcseyo7VTrstSXuL6WFfyFy//157PoupRnXuijtxZPpZKvlgOwrW6VmUxBTUUAP/6cASDlgAAAhw12BnmKRBDyCsHPSc4CJDXgaKcVDEGoLA0UR2eAF913rNGN8iKgYlBgyiUAEWwPqK/C4DXgOCvGP3w83zp7/Q///lt5TvJ+RyCrhOLD3Sakd3qvajgBjhf7CySlYcllqAqALtrtXUZFlppMvD3naL+icJZJrKjmpShcwZx59DPx8Jela9//9UX0V75QYY2hHb76Ie+jq3p1Kl/xQx3M9dX28W6oCym9I29I5LHUHVO09VwdnGgx6g2qQtzWSFWeCfvUAf5kRKNun/5/0XtTe38MPJjh+8sYht73lrUqeKrVovd91b7jNG6AlOOSpTWOSxWjVnmgivRLBeF2OyuUfV9PL4QBvvMpz/p+mhf9r6F377fXqS2T+lsYF10wmKAdim2dK6XaLpOrqNpiCmooAD/+nIEfIsAAAIlQNk5IxKUREb7OhkiHIhodVpnsUNBD47sHPMcaIIEiRqOQdAcHf0hazX1aoNiOJ0WDHDDLI+CJq+z9otFdlP1zNyJZTfmb5Vpb7Wflp+6KzJgTtMDM2MpLb7Or3VgY+/RXCAuoBkbchgaVCWaaMX7NI89ELPkSbQA5aubgx9H/f2uyn2qn+RJXRv06Mnb8kHwYQZVrdNrp6UQgl7eez+1yFPa43pSALGpbTbsDWZrYTDnQg6zH4au3FtsGrIyCJI3CfxXPfN1ehM+h9FJmlKm9K263Y8r4iYp5Bn0DdvuKWez7GWW0vXFTICyB7Lv8bjkiW8AMzhekm/Cr30GWqkrMc+Jw96HfqL+fNjh7rWdz2cVrPs9QYhNTX6HUFKhmFwqMV1oe7OaoNK3tpWTTEFNRf/6cASn7wAAgh5A2OgpKDBBY/tqFOZ7iIkBg6CIqfEJDKxoxJxqAAAgYIC/u/98ABv0WCdBc5CoZ5wTh3IeNcUfCxd9/oqcfPHNr0qn6Jf9E8/Q3+miPqns7Jag2uU1Dtq3OyOpGj7idqwAAtINZCUvDBeMhkluxMaNga/qYKJs0Z7/QsAZzI7xuSfrRBTewt2bv30X51SCxAh7QIE8a7v7XLRNLFQisPLS5EIYVldTn1tuuJ43Hj2o+DfFYcRWG5B3/aob+iMul5W39lz0+7cQZfnVXIqMpSCdhJG9C5ioEWVMyICoazw33Uckzet1ArgFxtyb7ouCUg8MEvIau/BEQrG+gmdjppVihbKB/b8XAry7lcCtJLL9RGIZqvEp5TyJL2I/tLiNrNSG2L+xrDSPemIKaigAAAD/+nIEIEIAAAIXW1aZ7SrQRAT7Ci2lNof9bXmjhFtxD6Bu9DKJpgBqndoNm0nAWd4pUlMrRwVKC603qHxp0ldA/VGOeUsNAx4U+rVAgPq9Uq/sl/9P/8v/9emhb/t5V//oqf////6rG9IAATgBxuSNh4WHMpDwFmdFtP6o3lUILidmV1iY5YUG/+OAL7Safv9pqi3ENRtylMqMFETz9TnAQbRRGL6drHFafdff0sAAFxtJWxyY1p5fUxDbctwXtU7U2jEXVyRB5RtWoNU7Xf/fr5/176OS/Pjk/k6rOXl/C5GfXXf/V/8g46VaOjWEAI1cSbm5NRtx7qPpgqcaEMa+52D5YXC+j9v3L95tP3QyNCGoUv9PN+jeh21epVf+xWqNUQ1er7i20VH2FBVxw5QgqmIKaimZccm4AP/6cASv+AAAAhRa3WjJEyw+5rtaJEV5iOUDeaKYUjESnHC0MJTWaBAMkYStjkuKg1fbA3X7bpHd8wjvOsZvRk6jdu1HG7WjP/9Lb5n+t9iH0b9HdLy4/5Ztzleyfvuv9aadf+v/guyyhIBCwVYklO7DCySYytSvdD/D2E1FbJBwo+cfQbvURATllY5dEWtSNq8+Zvr8qfnujRJ1lF9G8kr//u/VU67y0AAAbWTe1luEB0a+gqqX1DXwGbL1AgwoZpbLqNpzif2o6fYtyc9EdNdjO2DX9vz6WUl/QxNYaQln3PRRLVMe4onRmEhvFlaqYgZbonf/d+CG3GlquYlAIA5gDaNHfVkfV9G7YcAXvS3T/Ns6b+lSM1BRSslqdK/jXi9ZtUNadZq46tTT0KqFFf3XinQmIKaigAD/+nIEljIAAAIgWt7oZxNsQ0gbygziiYiRAW9MJELRGJcrTaSdagACYlInNrLtvCRLHQTUqS3K6lM8/LFGeezzF0eVD9PnBI/+vCdy+tH8j9d+2+o7W/9HQzfbv6FdVsi/tcs2d6P/wacXABj9N3WW7eAAAlgnEYty8yoV9tVyxzpE4flDe2NxK/v0+rrKpUctKezdhy6rf1uXjVabyWkdAYhUdzPVdOvJ7flb+kAAFghft/o+z8omw0GXRkHaN8bq2E5dmqy8G7wSadm/kt9LNZ7XVU/DsW9GtejfZ9Vo/TW89AzdyTknJTKnL1PMs+T/vACkkgSXEZsjxmIGhwFzZAqspfWYneUV9A+CHwkf6Szw/ozb18GQS0ftjQj/T/9ul1VdOS7kQYWSvQg8oPiUgKR7Kwl3atyYgv/6cATgagAIAgJa2jnhE/ZDhruqMOUdiJC7bUScSFERFe0okomqAiAJrts9AHAXRvrL2C3o6r6FL/31cfFspoLwvJf1/GevnPoiIn+VfZtfssrqsqM47L9S8yf+/RPJ/X/p+3e4RYBDPiXtZbKggP3qyRmdmnUZ4RNh/6RDqP/jsB/WSHjNJ0dDMrVNsjm22bkSmX+JFWld1oSKrDw9ewiUNPuVTAMqLX+oKwlvv/TpITcOZZdjA6M8QfBvULjHg6cEK42nAi94zBua/d2orXo6l+boLY+++LFGlhpAMNBJJeOeZQbWTGbaGmEPd/qBAhoFXbbgPigNKtVRVFg75pbY+FtgI9cZGNwbVu2E9Ozaf9rI9Gt4Qzi4OuC/gpxjVsJrhqpy3OSgaCptw4e5RUUhsUTEFNRQAAD/+nIENBAAAAIkNd7ooRcMQuV7bSDiX4io12TlnEvQ/5dr6POJMkgQUE4nP/dsVnMO3HVU3V8nNuSt2SJCmo7RsSDfvYb9Wom4A1Nobz1AFwZGUt/rg11tdGiUcLtaiKytErU2rLEbtcu6gBAAluEK2uRrTCzjZUKQf0y5Jo1L1DbKix5y4d37fmBt/1V9tdWc72nVOVYYsqCwZETuSpuflQNDSGnUsv2xNT27vWAcJ3bbSW4sGOUD0JbEFIY4vSw31I7P1tKi3Kn/ULO6X0Yrqv8iOhHJVPwredUsvSqKGI0yMFXxiGjVITrcSSRvYz82v1gAAOABbbRmIlDhtVvUg8bhEt2mjGPPiS9GcowHluzbd8OAf//NLVWttezSPzCrFT1BFYu8F42YcksirmGJ936UxBTUUAAAAP/6cATVNgAAAh41WT0kQAxEJrvdpBwBiE0njPgjgBELJPFrBFAAgMCHI21CCAPEvDwwTxJFEN+zJ51Cakb0e4qzNk/I/a8//WiVJtZdrSpoKJSrsdL3AQ2k57rgKdfcahsVZPq8FY9bPmUGCXrao7/tgid1HD0vc8GAI4s4RJmykUvqJBb/iIV+vf+a66mpRKv46ejmo7O70Rd2eTY1gI2vFc4ZRovtqSLuddD8OAAgCAQCAQCAQfD7uKfzfH0/nf//qfU//7sPkxoIn/8SAdg/JkBoQzf/4OxLee44NCH//++ePkxIY8aHP/4IQwIChwAAAAAwUDgcDgcDj4fnFP5vU7d8Q/Z//i7zv/+Jh8XFBD/+KAcPi4oHBTL//A4vfQQJ///85xcCMcOHP/4ISAgKHOlMQU1FAAD/+nIEKwMACAIgIOBPIOAIRAG8b+GYAQhs12wMBHVA/Ycy9CCkPgACx+lBpEgSszXNSqquzTedoFgyd2zkNZta/Nb/2aRsFBR99B9T155q9lTBiFrcR3+tr2GIYIooas41ZUiJGLqedYSoACQiJoU97ZI5cEF2Y71VUtmubLf0KH8seMGRcnkvmCtgoKP6Hqet081aLKiIxAXW5P9bXsMTBFG1bCS1kRIKD6nnZKisBOBgqOVznIpZy33nOa5+t73Y1YZ0t2UXMfl//3+c+qNneeFPC5LAwy7wnrLPFKW5Z0kBkIi9dMm8+rPB1tspDjLdYLcl27ssbSUhBQokGboNAAVp6wFz/li1HqAiw+PXoJ3rur5w6y8q8JKnrZJbmOr960rsllD6+pyxVdhLa4YmbTEFNRTMuOTcAP/6cATbPQABAh42XEnwE6BEBsv9IOJnCDTVZky8RcEOGq0JkIqgATVDqqqH2BDP6N1O9iRdX1m28XtqqUZhDkjS1N11fzfd7t8zqGqZ82yddsokqlmfozr6RKHbuzF67MtWjf057grR2awgVLKTpLG24HYAMOky0dO1a20SlMThNbMwcRVlX5u10/zPDOpnzbJ122ZpZn5mdV6RKNuyDMXrsyyFiXf0rh3grR2LrGYG2lmqCH52MsaqeVnvDn/vL/evcXwAoQtvZc9gZGrJfytsZ3/9S6E22720UxZabvo9vBM7PlFV9+1AxfTv/6G9YP/+FYQMNH69+rfh6tnu3ew7/45dt71ukZ6NCTVL+8lyvzW3f95hyJVrirSCaXe9WR7eC9M3yiq+/aYGLd5v6PIN416YgpqKAAD/+nAEdOgACAIXQNzrBjhoQmgLzTxqDQiY12LMQEsBFBCtaPWIuAAAI2CmbG5KMJZuKtjLSS/jfL2QqyaBsMJ5h6Uqxi+239tHq3M/6P9z3/5m/lC3/qqNOPSxW7Sq8wldvnF+JH/NyFQQBEtod31s2DFONnInEP6K5bvj25ZSUFL9kptUzzSzt02tTofZylHW369H9v97rKa/v+i8r+Ll3WhMW51e+tnxLXsCJXfmByP0od2QtKfjB+6th40k0lzucVYxBpmLll8Mh+kG/tRqfN2qXtojX+jWZFVWlU1Sf0F6l1v5n0KzCYe/V2P3+ysAQ0A5JJbQFAcSE0nXVD6rShLPdy1StSPaCWBtj/20agV+QS6u6CfShthHIMSs0uw5BMxuuU/s1j5J+MuZd1NzD1M0J+hMQU1F//pyBIOJAACCIxTYswhZ0EIDCvJhkTgIqF+FoJiqcRAV7OjGidABAt3+DUApcNwLDrC3YmTlNAEo/mhXkujrcAoG1dfiWrdfZdkFF2bXsK0NaNtD1Qd5NHYFq7j6hrk3+m7dRZsu6H+p3WBM1GGgOm2OTy/OdZuSHVIIUvXlBpOvURN4JcLhjQnD+izTh7NvnosLPUnLNebf68kREEa3VN9/vSa96tNCuqgvw2CEjrSnJZG3TIamKWMYYvVhSSH4wNVO6JJwy2guqwxSCZwyREdhJ7ZIHQ85rj4u5EOCTaqq9Vleu57G/TqTm33DtEgWFHmAAW5ZaJo1Du8+lSmUD0mbszjDedZJ3FiBOJFx7qgKg23ofWjbbouV9Ge3+mlRI+0gsmgkHCSlK1tb/2HY5X+zj/utSmIKaij/+nAEtKQAAAIgNdtpLRMwQSI7fSRqVQi9M3uhnE6xGZrvdGKJdgAAIUCHb7t+DqAr2Cj7jvMU/F17WeHYpTJta7YH6hp2ZHXp8yIytdyOU2nalfUq/zMlRLqJ645o/j5xYdY3JqsyPfWSAAABgALtdl4yStN1iZhfdQX5i0ke8XApc5vK3JxFezGjzT8ogqzy2/SCrxGAXAypWeY1nfVQlj1vt05rLur1PRSACQ4wklG0nElPZcvMx9VAN/FAZU5rkldWyew1O/5F3Lzp6t5S2lRuKN33bv6b/bPvq3/vz0RUog4UFKA3i9olG+owwmycCTghQLbjaTkWtKIOAzyO6xM/TxgHmarsIj1FcE+JRkZLXmkRD/Kzum1S7dQilZU19248V0uZ7N3OraoWZO5tTRDgEy/tuQmA//pyBKy+AADCF0bj6KksjkMliuI/B3QIhRl3oahPcRaWK8zHldBtuP6jTX227GMd6lplDSEVXs3oaoTO80Y+ihrbw+5O+rDDKet3W37+vzNW3rVUmknvf/ov1/+PshNTskVR3/8fbnfIBX/ochhPn8e1yPbG1gNGFXyvlZ3m9Tc7VLziXqKOUXsVcKPNxt6C+vP1jnou+bR/o6tow8WHiHbIzntd/v2+yr//6gQYUkCE20m5UDCursxyF0l16AwIQgRtafHvqMkR0T6OnJ3kZ0lTrH6W69dHa0te/+pQz9bJ/qKKErbR6+0qX1iF36H1gJtyWrTktWdOW1kGBsJjt81Ec/+uVmMD5VEzJN0GqEhTwj6hyzYymZ00RpLpdtu0fVsdIvXuadCLXua7X/Uyxkkr21cQuTEFNRT/+nAEgVAAAIIKNV1QZxLsQCO7AzIHkAixU2jENE9xApptZGaKVgxZAKTbSc3BGbTvuWbb91AeDERsxz2bipOYB9eqGf00pVrX6hBT2cjLIb1Mt7RT5jdFHV6MlKqS4NjfoWjGfRqkQBHLbUg5Dhyll8sttHZLv8y8r6d1hmwrAbCkXD2Y4rZXxB82i6+ULh6QHx++o81Tr4uAyMstqFyGR0fGevb9/SBgGZbeQwlwUP1zB6cKn3gG6QXUe0pPp878jvt6TOiGSyb/v4JFZVFlNI1bs7eozFRretLVbr+/xrf4Ry9k26VB3bPX2R8yATLYEEhCuV7fpIEofomPzapbhAIy6TapNe+T/m308E7fuiEb/oSjqQZ1t4X+MWkslc//dQkOlAatypa7s7Cu9MQU1FMy45NwAAAA//pyBOv0AADCFRjXGeU1EESDq0cZQmuIoS9nIxxPcQOaK8y3lggBMm3Yw1ozC8yHZaAjrxFpat8Wi78uJY57AOxb5W5o8sUcT4Q2lgjpG0qYIqjv9Toj+X0KUza2Z2Z6sb3F3dvbt/9QEASiCVKHKUpeB4nB8x7rpUuwrAJscWzowdCegLjeMxNPcIOlcBjPDmS+9CjrmOrpQ/dTtbOnXD3jKboXe9oi4Mxs7gmCAQAKKebMhMdQ1fwi29kielcKgkst0Ht8V+YGVMLv00dOn0+3oN3K/v/0BV3t5K9//v6/BNOyhBRKS6ViZQJBqwjfv8uAW5Z+sDgtJhNIrTg+TYx5jhf+pcWPwD9ElddXQBmGPgG3It7o/p7/Uye3oXwi+lf/GD7Xen6cnhivv8pqVTbZ70xBTUUAAAD/+nAEEO8ACIINS9mxJxL8QcXK8zIHdAh5UWlDHLLxEqus5JUJ7gCAyrZQ2HzMSPoXVhYt3TRlLOFwSlWVVcf3aKPHhLONf+ZUv3XGTo3oXwjrkT7dgR/Vvf//0f2+degkBW+oObv/QCJNdsvHMg0qXysxD54FXfvVczs6VEyaBcUSp6fULmo6wWN3JfbZEN/ejFk0T8nq4PHahYwOOuqHt6cpd+EPmP6dGiAlEkuFWLuSKaavYkWVb7f789GSNRPskx0ugV9wm/2q6cn19H9H833+vdB/r8vy/+nzfCq0Me61f1reIuit5LImac9UC0g2HxYjkeUWaN0qArZqfRF8s8kjACFxn6iyOnOkKN+VFqxSZ7B5Ol+L9H1o/m/+vx//L9fv6fDN5N6bNX/0L8f5H70xBTUUAAAA//pyBJvoAAACGlTdUGcrbEMqm0kk4l2IjSNxVDOAMROr73aMUAZQjo0m1GpeBTIFsM+CFlFtjnigbjcDxerOw8Z4jeOhJ6/v5/p9/i01Dl8U6ubWMB/V/N9//RvX43bFn/578Zw7uq7KAAMQFiH1TqbXOKW12gWUzvnULKJgD1H30QVIr4UblEXL94PXX6ff4/hW1Rvv6v9/T7/f0+vwO1VHMzUevZ3wz5z1fUAJ0BTaabgfg6sQi+DFiJlNV0CgbZXzYr9AenvPHAcnEzH10MLnVmP5qdX+/s+rH/+hf7+vqnv9FRcdd3ymUZ6M/i3/qJZIdBLbrjlzVlJp5MP3nPoGCemIA1XndQHZXwt3yNv9TB0Xfk3y+r/tor9n9W9X9H9fk2/oeuImXij+T/19VR6xF/V8smIKaij/+nAEfhIAAAIgSOE+FKAARKkMAsQUAAhUPXK9A4ABEZrxt4xQBgEABAIBAIBAIEqc4Q0Hh4KxCItsqUzo0v//7s5l/+HDOKOWv/wcPAjjziGnb/kFyITEzf//5kZii48oid/+o9eGwyroMMMMMMm7dwcmkOBsGLyMkzZTPaSwsv/rb7s6LX/hwzijlr/8XEgRx5yadv+Q5EJiZvun/8yMxRdxgcsK/+IqSpp7O65QBYANQtuA+FGQOk0Rp53PukkkldTfCAhcvsZbRbHWpzwoJViGsdUCZA207tZvQnW17CQww7rFyS6i73Vdus4eI+5CAEUmo3GkSQFwMG1nqUfu7VVXOpVV4iCmd+UQS+J2217p/5tFLVPX9HRrrL9TGujHHscDWLp0EPxfyaP9okLAZ7gpRc5KYgpo//pyBIGzAAECDjXl6GEVzEJCm908ZR0IkQGPoRxO8RUbLvTxFHwNuXbO2xtJJ4UBZirNqrZPCNjP8MJkH74JKsBCbmadf+zBjFCU0Hlz3/4bGdtwBdBZ5We0fjyK1rIor0NO4cWpP6AAA5aTYyAAE2BaVbYMoWH0SxlSm0PiA7faEhR+HBEbWSeZs0iSBls+1Iv50s+UXQWeue0frIrrSj0NbUHBdRa1fQoOJu6m3SNwgISU1m0CdQPb1II4z3HgyoyxxpM25L23fS50X2VS+hvvr/ab6A2R9r+VnfqDdkKhEtTa3KoxGGkYNPQnZrAIBkjUkbKBSvKhuilR4rDDLRz24mBAer+cFR8HboM5Ee27+50VOVVL2Q331/tMnoNYRXHpVOt2QqLLtrdoxGGkYNPR2a0xBTUUAAD/+nAExWQAAIIjNVpLJxHwQea7SWUKLAf415fjBO5xFJrrhbYoYAABguv+xVkXtf6Hb9StsYNiOZ40a+Te00vZIrDo2Da2Dy7epvR9Feardu7OWct3t5r3NBBU7W1dlCFoX9lBW//p/fqAwAAYP9fYq6P0u04t+gcs5YDMt+Kerw8gPBUxV9YvLImIn3GdNE9SbXe/pt29q1bT9L6UJlr9/MN9au46n/3+p3IkANDZpd1W/62W4vJzX+1sBCaQfMPwsxWtejBc2xqCxa1Ck5EV3TX93oy/9vmGdK+39Cf1zDiB/97WMs+tPW776nnEdCEPKmreHRBqJ5FddAo8pxYk7ow6eB0FB4VU0cPVdmQVjuwuR6LblbqtfQ9bdda72//tXl+l2tyA8/1nz3FP/FXetCYgpqKAAAAA//pyBAaEAAgB/TZXkwlQ8ESoGylhol0IbNdpR5ihIRGm7nTAlGwQaZOUwMldsWHg+2FAb8QDOYjUVP6tMxEPgnEtt2CaO9RW3oPfv6trv2Mr/9D9J6/X/cfeek+V+hlL3g52dz9AAFAkwgJkKpjBdelocs5BluRkq1ce1TTrwxD1k80utjE+vlTO+H/1gxmTKPa6f37XWH1Wr/3pU3vVf7NwvR7fJfX0O+SbAglJJwcwHIilkJDU/okxeI0THSmULbVgEDU4j8G9fr9PvS1kS1tiq1fXv7VFa4DdM3p+q4LCFkOn38sZtkTq2ZREwCSRF6IvZZNh6CKHYBJWFh0FUF0GigiBh9WvQIP43wmBnjp38aO9G87e3y+u5H+7/qO9tlbJ33//0k/nca2u1zMidX2rTEFNRTMuOTf/+nAEqDMAAAIdNlxpYylYQybLuSAnR4g4jWTmDOrBDBrsTMGdWAAENMQpZLLe0Ne9RBwi4Iw0o4F1amx43rCVOA3jQJR9vX4m2jr16albRTafUvboO9qyIBMVa35JOwi91bupaQ5G21S4Flbd9VchWnqIYmYiexWtopBJSvmg6LWwp5oY2qZ4r+69Sl9Pq3OSv+pk9jqsNexB4Xi7yOy8HhQiqwsf11PH8OfvqMoC3JZsKy6WnnOl6nul/G7F5LCjA1R3600En2GNP5nzFtNIOYGVR86Hd9Clxz9DqC7oi+ca2pgXgHdZdKnNDViwwJt2S0Hx6Ih8ho1Dy69BSw/Yuq4IoJEqWfxC/QU+cb5VvbXTzCzp3pmb3V3azzaFWO/L8k2KXBQ/6NzVAOl/2YA/fpTEFNRQAAAA//pyBK9BAAACASxcyQcVnEQmOzok4lsIyI9SB6VUARaZLqgyid4QSrp9lunFlqenjIpfV+PKCNzQSlL97BxI4z4mCWhFFM83w6Lp/9D7IEdBJ17o1zeq43sd76VlRUeAVfHJqTZ1AAagQCm1ceBsEPxtODWuByXy3SmKg6KH0iIX8f0J6/T3X0X+j0foFs39hKWIBzoPW6mV5edFKhOdYzOqtfY9+066g2WEeBRgpDqUxWuTW2sjOATz6i0qqsa9aldIB0BfwyMMns60BZ6jKgEGe5EFt71OfiOa/M2ZidqnyLyJa0vWSZq6/++jd0f+kEDtEki0nGNMrlZhwaLHmVbztGfAKvAG6L6+x29Pv9/V3RnCOm79TDGJLc4WwjSjFNj5zOsKg1MrWgYWsWs29JhLBNY6DCYgpoD/+nAEL8oAAAIJTVxgZRQMQ4ab2gTio4iopWkhmKUxE5TvtCOWxgAAIYCFLP0LCwWc8AA6mqLGq0ChUEN3C/D+plRat4V+x//v6v665PnamSxHRtN+v/0f09japGjT74UxY3LJ8vq1AUXooy43LMCkIwFA+IO9yHL4n3aA816Ibarev/yW791F0KWSqRCch0vYZahAtRHbvz8srYooTDwdjGKOEkRUKVfMOKgKagDIt2VDDkWreFjLnpTtVoaKRg7GRAf4Q+BGvjt6uq3CI+mZDLU3x+3Na6muFHe56itxbL4MipwGAo/jLEtU3NSmhVpJBNTJcsbclmO1LpcWAFPbt5hVG8AHc/CR2wgJspXOCCnCx9aK/O/r+P16nVNipahfe2ITglWQqCyp0woWadtK1ndCPFExBTUU//pwBNptAAACCC9YOYcTVD+l+0oY4pSI4Ul9oxyycRYpL7QjilYDgIASUkKj63B269PlR9fj/DaVmBgdCEaxMHaMCmuLy3gf19f39f/hOjNXKNczPPv+/14ixGXWSFKbyJGig/szrwAIgCKbbtMoH/LmC30a+O1pRX7ocWNJRvE3wXFvH/9Ter+Av7fCIurfGrKakU7NP27MSwkJW3n1X9b7VwK7oJkJtaSJSJty58f6YS2c5yOx9CH8cw/i8ousQFr4AtXAA9sZ6t9/K/n8x+rP9vR/J9f+8nOo/5rat4xfRzbp8XNkjTbnWqjvakQU1S023G7kHA9bNYMN3GOp1tKIqiYn4WLXwq3iX9Pjer/f1+D73vsJ1k+jdF/3ST//7eQzozBmV9NXQXCiBZZ/bvVJTyYgpqKAAP/6cgQ28AAAAf4kXWhoKpxDpev9BMdjiL1JY0ScTVEaJ6zkkoqWBBDrQJjbSbt6pgghTkCApNAvrvV4x9YSDI+gfH+EmphH4n5TbIg/W8BCTRpyFTNPz7AqyVdODZZ3r1aPU3bcRCSKkyTrljc1DcAjKhhK6AWYbl8cL0L9Tg5CTKIxejxGb1+b7HbIeeiI41euRf29ydpDIMkft3V63R251erRKkcnWd2AAYgICUnKVBqaPCcMXkog+JyytQwe6YslgnVxd5QOk7kxKR2h/p6m9W//6p9/p9vT6f/JT/t9vtylX0PyDYhoI1pqDO2GAAEwEQWw8YWn1rF5y6EX//MT1S4OpExVj3q5AxOAr9Qn8nxXyeX6fN6P8nu3givwT+3yU0X7fG1MjaSv0CH4BxDgnSmoM7UxBTUU//pwBMuGAAACBEbaSQcTXEKpK2og4nuIRN9tQwiu0RQqreRSjr4ADIDBCemJPkC4QngHDxesXoi0VEF0r1nByHGIEpdjUk9C+o3q3lf39V+329W83//dNHQz9EL4dp9u/dV8Dan+kIhcSSWUFMxL3U4usSN+wRo5Oh8Li2JztIUGPKegSPbN+I+3QYdNDMtDH+3y+VrYJiam2m///+vweTxDkNksHcjVzQAm4FhORyNLX5TGVnJkTb8jCsCx6CoPrDjSVBvv9/iv28Z9vIBvTyGHejevkHc1/kA+zDvCI4mQ1hZwgGhYMCQv+0ARsFGlpSk1QcbEx2pQEWKZoReN6R4Z4f+P+/oO/5BnoXyA3kbkT5O1fZv3T//yMJnJfQ3n8qV5TRPL64QQo4HxANOP+tMQU1FMy45NwP/6cgQRQgAAAgUkW4BmHJBEBHuZDCKwCMQ9hUGERkEMB7BkMIzocqZAnU64k4iEemiJvZAbhMmnrxemVDizAxZ+iJlELd3w7v/hBEEFg4YCwDE4gA/LlHB9jA+IMmin/v//8m3+J2jEoAAA1ExlDinXKxEI9NE/oAMQcDfqH1GNhxb6EJoTU55zn8IcKOJkBrHhYM+fDBccouOOVjnLf4/Yn6/9zFjy/1lwvgFIAAQKTlIIIrmESbOO0tFuy9wahM89VYDIFhNDxZDWGggI2CG+RcGRY+96HvEFChWl7vmVVdTmTRdy9lPGjx6pafNgPFoAwAARb4gQRXZEmNiUMjtDXYLXHhw5EBkCwmh4BbDTBGwQqfIuDIsfeaQ94g1L3u9xlVXU5jTSRq9jUr1rHqpn0rxZMQU1FAAA//pwBEgvAAACFRreAYYZcEQg3BoBgwAIuAuFgKQgIRIH71gWDAjuC4AVLcaUcMX9p/UdqZjzo00oci6WF+S70ihJ0wIlqCiRiQIkzF41hF4BEpcqZa+ce2MCho1Rvm+uyyn6zJZrv09AAbIIFptuEIYqgkmEClHBkUQWtSLBxRMzl0DEC5ZuqHRkCJMxeNFGHiouA0Gey1ww8aNdj+1i7H0J+NMlmnRbcKUitCAGQHUirMhzSCTDRIiOewWHwZgRK3FVFo8kNOtOEIjDwxaGx8o/GEAcNMAMsbaWWywqH1LO/2UdzLtTTtRpohF5gwfPHmDwMADKzCNA/4iNihC5Hny0+EhmCx7ioxTR50ad0Unwkt+BZQCUAoQDzWAGm0tZoJqWd6Dex6NjiNKWKPnVTUq1znNexhpMQf/6cgRPyAALAhEGXSkoGJBCIbuoCYMACIyPbqMEagEdFe5kkYmoC4AKbkQBmB2DmpCWBY2DTUAFyBaaOFQ+I3A2DQJsyIHOJKhNxtDxdLW41yLnrWdehbbamPYy5qbbCyO1Wht/HbaFAQAAAMWNRsEq8DqAjZzJu3qYwkM1gogNvQlQMDkVCwOYKgpHuW0fO0ErqbUUOa4rhRIsp6Bt61KU/r3Rsj0scvADCc80SeNHJy0pKsgESkOdUpYdTvbFQisMQywksQ9TU0YbvHgYBMNEZdDx10VrKxRbw2PWtsXF/adRWLd+u0laq18jXZEgAEIIArUsGB+b+9n9E7M6qFt9W96pYU+zmvI1cmS7dHatlZGpRkfU8o8KLdFayrCK3ht/i4uMs3IrExeffUXcTJXqtPNI19SYgpqK//pwBNk8AAgCHitaAecZ4ENCS60MI2YIXF9lJKBpgRCL7SSTCWjUHdWsjDM6fB6SrSiohhg1C+K/jJz1XaoSiVXiN7Up57Nsd5w4faRp4MJAI01Li4CAxIJNDtGCqqHZ1zH6qehmvX+3pAQAhgCBKbcmFjOZsfhaS2h6pE58jAn2vQJSvI5ZkqMCGLrDAFmTWwuAizRYXHKJLiUt547FH19WhFJXWhC48VMLVSiABAFZJ6QnmBQMGTOYAwtH2ctLw7A6G5Lpo2DBkHCh9RBiyhGfAy7GKhZ21C8WErib7tLiVdS++9UkyvI9G3W8o7uWABwCAt/1saSUYM7Pg8Sinkf8zlK4Eh3mGriMZ8E/cS568V15Y7rBXU5K1PnciPOhJRvLBBR9RQhsquFYiouivKOyU8lMQU1FAP/6cgSolgAAAe8oV4MMGfBEgvt9DMIdCPSxdaEYbSEdB630NIzg1veE6QjfmfU2fBWl+m6K+5A2MrQEaCIiT3WQjSmRBEsgAc2KJ5e8Oc86bmZQ/2FF5lNy3n+zbbX/v6///9oEDHWAQCKTUwtlrxGJ57DkPIy82UHnGyZ4EMnSuYU1GlLVg1lTqeTi7RhIh0zaEjlIFjlxETiO9z860Yl7Pzlx5vePbagADb4HX5ZJr1gItH3M3yFtE64Qzd+rszYGhclGtyDnP784ZykhkJKN+Wt/RSaLBsVXEjzZcECok4LG9Gm6dqHelytvRZdK6wAGa8AiJLdvj0Y13S+IT7X8g3JNdbbBlIMuQpbS3apb3DiU8QlZUk8CuETRSxTxWYuZQmlQxrA5UtW/uXLyw+YQaaelpN1b0xBA//pwBJH3AAgCGTLXKegVIECmSvU9JVoIaKVjJhhLQRwL7bRhlcQAgH2mSI14AOxVoGPp0dDzWvZGZ9LaUPwm8aKXFMg8KxXZfU8BxK1di112SX0RUfv3Rntr312x+CtYz+zToyOY1XfGAEF9rWanHEdItzUG5iAMP9oAMXXZldnV5jUc+0UYjRxe9BwAORZhl/RlvtqjfvmfmZvbfH5pszsNUf119H/2f64QEX/5ulOEAFjxO44+R89/B+PHZbslhg18/PDayuIfLF00LuiM+6PVkJdrp7iTwTa+E2b7N/VU5WItTSX0/endyEXqAYAiwBBjbUrr6Hq4U1g41/8Aco8mrjDeEhTyWvFzKkdqEuE4NJSeVas7QcSsmlQNAKdPA81fR+xxVzws2eZaZel9FlTb6JVMQU1FAP/6cgR2BgAIgh8mWEmLOlBAI0rRJedKCHhfY0WY6oETDW10MxSMAACAWr/5h2ExIKgZKauBm4vkss4/18wLHCxk1UDTeeHqZcqSP2KOZ0p3Siui3Ri1Bixi/VXHjSW5HIfaVRzdf6Gd394PshSAXaLEBZibkLReL4W0RnUl5WeCZ8a5mWtSBMMpng/Z2UfbsULLEClAI96L1DlYC1o0oKmv5DAu1npr+v/omAgpOS6EclQi0wbDQdINvoLa3tcTEYHM6vGr+MbVGA2wit4Ecxro9NyyGKMqPqf17mpQMpU48hFGMVEi0faR39Nj0bIyAQW4ndvIIgireDCkmA+saPssJdoRY3DPcU8g697UnIlAgWuQMHERQwMWHx6D0nkb6szdK5JtITJhZDBc46ZFZ742uSTEFNRQAAAA//pwBMcXAADCHUJb4EEXLEKjeqA9iJAIHF91RIxHMQ8L7Mz2FGwgACJkgQjdJmDuMO7gIkOGsCGK7qLRand+HfoX5fVsrDM7VdpZ1687RmADr/Mr/1f8t1Qa2C1r+gJkRtqTpEco0653Vs+D8I5ckjKBXFrWIBRcIeNR0lEnhcwYkgylbgA6unwXNrYFMw2Bgd99WH8n5MFjyQtW8eSdL4sA7D64tOJ3r+K7OkABeiSkiXLRkYbky1bHgTkZMS7LNUREH1o/BG8G2Zw6y9RIuZHaXqapmp+1a7uvRo2aA6FmnSazgth5KiFD6OL0klppytZ/p8TMyJputPnmRk/T62dXaVjpDo+7RbSEhfwiTDCJ+PelD5TLAJB1zie92pZLyy2kMCvsPKf1MQq69C2fq9KYgpqKZlxybv/6cgRgPAAIAhcX19NLKsBEKFsqZOJcCDhfX0ysqsERG6508wguAA4AAAOSBJcEhThlhoVFkyNz1nOdaCTZUk4TXX5hVG60A87q7PegPbCJqC2zf6ck17UJonCtqho50VmsSoo2/vXr1gASIAY5d+z9U5yiLW5QSWzX7xzCfmXygzYsgoJeb6BrwbdjLkyJYrpbo2/xlbVWsy//PtV/+5PVfUZPsZpK60eQ9TfowEEJK21TQyJFvvxaZc2aQQdq2uNW19xdHOVD/IvOoDUzE+LK6wsX95Jei6hsrkr+KPQxYBFHSMsNgLBGYOut0f+kAAisAIhlIuPBZA+fZefw8JJUF/eCztmRa6ivX69VCjVzejW2+99W7/mbsis8oSaukNmdr8NknNMPSZGDwFYa3SLybe9MQU1FAAAA//pwBOqOAACCH0Nb0eE5HD9DSzphigqIxRllTBxL0QeQbB2HqCABDoSSiiVBoEtpW57hSw4u562S+815V+43dNA7sJxn1fyl+b6NapT5x7UVj07ejP7eO/f7ereUbdn8//0goxxLTtgYPgARACkknISGDAurPKdY3J8MXMhzjdlLxkWgyb5G66ix2F5O9pC2WfRsRPbK27qnev5LEmDFYbw/Sbq7vKaP94ACyEFpNyEkIpPW/d2D8rf8ZsRtPDRlkE5BBx4XL88gMroM+oIX5DwR1woTWj26ev7+jf+d9FT1bwzci/X3Hw3Sz7YvVdvgEK3ZsQeVYtMNHTFGiXoPIsupfoVHiiOOQdE87li6nrKt1Fj1M7FS7tkRjhQ1/W9KnRGbl4sTyWQoO5/fQ/0f5rSmIKaigAAAAP/6cgRsnQAAAiVD2VU84ABCqIsdp5wACLEvcHiRABEYEG7fCiACAACAMyWb8CSTUCK4CmOcSelCEv7/6cKl6ixuCpLoFC93URXoYeL2830PTPX0Pb1+dr/t6v/5v26V56v1k40jQc/rf69QAAAEACTbdAAfRaODZtefWh4mMl/v/uCqedDpFjwVEptBATnnoJ3dEHwnILmlfVzH7eqef9/nfb1/f1+29F9U7S9CCC22223IBBKNH0SyMcGDBY9KXy3JutWUhXgt2MbBtkfEU39ptXtq/ZmmntZK2SmtJJ97pb06XR03stP8FM7foweUQ7//pAFHINkkkklg2BUF5sZGEvqyu9WyuwTdjOiA2wh8b38vnYhwYhNi5g68/GaErHGAfvHKsI2BYdeNV5G1tS1f+tTT+ulJZCkw//pwBAU1AAACJl3hHgSgAEILvHPAFACIuNd7vGOAAQWa73eKUAAAVCoWC0Wic5r///////+vYii5BA2i/xQQFDgiCjzdvpB0aeYjFZSq7p9vFN5xRTq5TjZQ8okWf/5D87qdyN1rUyspljQBEIhIJBIP//////2IouQQ6f4oIChwRBR5u38HRp5iMIspVcwl/4pvOKKdXKcbKHlEiz//IL87qdyN1YbGiSspljVE8+9HmgSiH6ryiunfmjob9M6yqLKNVt0Cwknbvvr0O3/5rTd3VHSn0NSbMbb6qd5BUcOmiLbW/U9vII/dYMJKeayoq56QnWfdJogSiHdLqJnPlFmTA5rOgLVqApdAkHTZu7T9E//Zpd3W6U+jJaRtvqpvFFRw6aI7W/vbyCP3WDCSnvyoq56YgpqKAP/6cgQmtwAAAhk13nhIFQBDRsttFCKyCFCHc6KM6uD/kO0kM5UQIQNhdZM/kimm7HJBGnaCEdRhKGVC7bd6OCqJUYztpTVunzGPuu3+/oVdn9DUe9YsXUoSu+36rS1J0NP8NoasSNs7NIACEMAjAAAAh0l1O64DM3FFvRqPGNvEgUhaJrARZrVPmMdLllGrX3utBKtY/oam/F2sWGtOX9MMlZ6VQZ8q8bFwjV1EBIedARskkJXO0whTCdf8clTg/NG8q16jb0DM7PZG/erSpJVrusjEFynsHRITcSRWKPqMolcWPM4i68JnX9o11VsiIQOCkFNQ4YjCGksDX7E6ko10jB3hY7qBm6N6/96s41vd5GQfU9g6JCbmlaxQ9UZKyuLHmahF10BM6/U0a6q2RTEFNRTMuOTcAAAA//pwBPI1AAAB7EBXAeoS4EKGuvk84lwJHQFvowDuIR6bLrBhFa6rLDXTO4Mk8TGA4C/NGm6FAaJxyj8TepKXtho06CEm3b9/VOjTf2+YV2p0fN4Mvvp/9hVK9bvLXpZ//WzgUACAIJAas137s+FJBntxoI+emsTFqkVEcjoVHGtUCvmAe5f762uoJqNpt7UH//m6Axv/ftxKitM8W9HRYp21ykxEG6SyNAQU5JI44Vs0moyfyaG/0pBbVrN5r9Br4+Q9/u+90aYTPtsr2N3WVJbKz7Jab4pAoiLbVsy2mo6bQLNRYOvr99j+GvlnTtpQJFaKC6Cu5V+4Tv8Wkc1BO+1HiB/I3QR4mCed/M9Lu6yPVXkLVVKzqhB72X6rr6jrMz2Sr/sQdWMNCcUXAKoOPasNes6NtpTEEP/6cAQd7AAIAh42VqnnEvA/qbuNDSInCDyvVgecS4EJGu28NZUMAIL9FZMfcMHeh7Iz12Nwjyg3uij41ikigRB3QfLruJD8Qr5jUe1K7Fo7s9/2XQh2cjLqun3hbbG2Ox3f0WOH/pdqIaQhMPGAS5I5JYtwa71o8YY3LD7V0zP0DtwQnyr7fs3M2rTpvdtvBFzI//3oWq+TtzpuCfp66UT2vUGXvj6Jh30KRrRFEuDkHXDOLLIBvDbLOCY2igqDhx0PULgj4GC5WqMawovqHdvfeOvMdWvbZiq+GPFKEdhFdSNLPXsXs0fps/qAQAQljICY42ZqXTRxk8XrKtj23hb3xN/P4wvoDfX2b3P0kOj9deqJzKv30oyC6xrh9r//n46HA127mEQKMXvYRtj0xBTUUzLjk3AAAAD/+nIEna0ACAIYNlbLChLwQWb73QzlN4icr2VGNOZBGKvtfDWU3AAIAAFaqkFVJmOEKnsldNqqgQHx2o7rIQspOVjdFG5ddSf1Dfub8z9EXKl939jdFP7enf4qS8JoxmvtRTQ7v3d/xalIojyNJNppKbS4JFbKCsjnh+kIikuIj/CLroDv4H7383k+jt3+bLUfffeqOj1XNUd6jmjtsinmP57SE0WoUCyHIUegMatmuusc6CZQfPXFVuP86ULUaUfxIfqJHlQmerq/lfm7b+v7zJg5WBGpvPExKgCLcTv5LbUh5OdMsJGoiJVHsKP35EAAACUUCZTEbuiQichR3xWfSrVQ22phAW1MBB66gN8Dffz/L9//XeQVHk/tv547Mm6f+8n/ppV1vI7NdPv0syWuMfc/NaEpiCmooP/6cARDAAAAgg4dWWhvOLBExXsaJaVMCFExd6CYpDEMFmtcx50AAAAqhADkdtoOMUMPmezj+VBFjsSD2WUWc8KegWIdBr6A8bp8qRiz4Lm4O5O0O+wWCthxJ1MchevV0kIpLP/frd+8AAYAFLZdkjcmIg+qRTtg0P2LHvUnMmjU3jU3UBfgre+sLZ81+87ZS/eswwe8ZUJRRyCKmadGe3YTcJ2LcbIN63cNJ/kAwUI6QWkkk5QVAOhsaptgHDkxb+nxIMTGB4f4B//bzP6epPt9P/WtmsfR/inT9v/mqjvNtGHjxW4k44Haie6sY+PqgACk1ImoypUCkngwG+ZDbDB/EczisGuSPhEN97gxNBEekqG//Kiz2fslEzPm82S741eCa2pYCn/xT3GiuzQYfn9+BkxBTUUAAAD/+nIEQe4ACAIZKVhR6xHwQwUrfAUnO4h8pVlHsadBCBTq5PYp0AAAwDLbt2xPvVAZTVEP9khf+hsP7fNNJbSGv/sOQ0Eg9Ydn6r5gpKzH6pXZPgnKHq8miHKwzUHbX4+tvTbWx/RX+zYQAFDEVqVuM8mF/0yPsz9IEfznVMg+M+PjHQbNWVAJJ1efdKEervn0c1aFG3lFT305BlNod+/1alY1SrioshPdTsd3pAAAQZI2NDfGjj/Ia3kZekAg7A5doBazY/YJO8UWOhDaUQcUTjThM8xIn1es+7ab9SD+v53E3yex/tjWRr9+T8p9P+wACIAAayVeP2d8KY2Bhp5+wAbHrU6vQTcw2/pSnUvIqEMGX8eihOSm+D3zPl/V7UT6+xG0ke3Kh7AMRZX7fs+cb+tMQU1FMy45N//6cAQ9DgAAAhkd2BsGUrBBJGwNGGUfiFiBbYwg4/EZK/D0g4pmAKbm/zC6TzNOZnLZYJpYams3LhI1AcIucq0uyyEP/BklRsW/QY/XyqZPZu2UnWKrLnsrRlo41S7I1GbXdjcs/xXZioBSMUabUkcu54DpWV42HgoLmjrtW7jfhNvE/if/ib+d/FkSuKD4vnKG7XsdU/dW6rEJ1SHuYtdUi4tYoWqJUH/FgASEogDM1vWMjY7SqSH7VeFDr1UQTzWy7leX8Kv408fCb0+aycg+IdGI1CgfRP19+dqzT2G8lkVFw1Ud3SmL5PJ/7gCyZfZHLLbfIh186V6N60E55oiRnqS8Tv4VLrxp8Rn6fFL/+3yfdOr+f1/FSLUlsIbZEfzt1KvNJytyMpv9G9X6ij1vq+QTEFNRQAD/+nIE8hIAAAHtHVjTDyhEQ2Oqw2oHSAj1UW+nmEWRHSswtFQV1gRFwIBSTkfgNihrlPM0x5s4KYdq4CMPqNerVFtIUGrxPxo70+M+K5DAu6tler6NOcqCe6Lj1K+Rz6Yx139QAKKlqCgudd15pewFxoEb3cFsqWoj9dPG0owJRqLrpAwZtKAFluVTxz44PlcE8hn85W+s7VoyGH92NzsJsqc/sxbdnAgYKmmyE5bsNAp2aH1xS3jhKZR/QNzf5H8t4JOQQK6ivVf/X/wRf/YnQvl+zVuRHaCcnMU3+tP+6eMnVtJfiqtjKGyj5f9EkGUzrrY3nJdwo4lo5j44FrgCtpCHd3AFCJ6MbqD+NP7/GFLdUP0/+3q3q/svttR/f6edNZie7ryN1YsvosPOvYrYgBaai+vamIKaiv/6cATtYgAAgg1WYehFEsxAKstZQOJPiK1nfbSRADEaF2uqsnAAFTq81jc1tl4JwQqGEXxQGPua+MBqvCfzep/BgXifOBjXdV7Hv29BK+R+C+e+jG7l8sv+v1+T7ev0+I9G8EAZr6NoAG6g0rYQQqSkJNImR/BIXumLjJqFGq6iOMeTficNeFJ43xb9X5TG8nglXq3gv16F+T2p/r9f/jerbU+N9vBOoDCp7rTajckw1tZ12M1TuLrO/fkICaR4X1Hbgx/Yfzeoj3P5Xfm9HT29fmV2k3LBovNT/Bu3I5+rc1+v/ovo3nZ+BN67SgwACGSbA2BiVexTtzgWQwqkliLM///VomuwqIGDhNgWkeooL7iEt5hnjZtlFk255P7+/3bqzcfN11fLI9u71fTiM1Roz39SYgpqKAD/+nIE1ZoAAAITL1qeMEAARGXrg8SIAIi0r3pckQABExXwZ4wgBChAZBQAAAlomoIn0dVlIoryiU4pigrdjODciLUm8qpSWse9rGslrz3uv+7POlbS3LSY45xtmv+kh/9yUBE3//+6kgUYy2ZDAZUhiaVnlh5Z64fdz3jt5XRDTg8nbrWae2ZCpa88xHV/vdtUrbR8245xt93XbpIKer/clARNlJX58Ve++jdSQpVWoCAAA4bEhpeKtMNR35qodb23IWZ53ZS3Wl+9jdrpeLrWj39H3c0SSFWLE0OgWnJ3UipO5PzzpKeTotUixJJzMqbc/cAAF38otBgAQTUu8llVP+aqHVjSZiFYx0u1hnLkfnlN29561o9/Tu5okkKsiaHUU5O6kKk7k9UOHZKeTotVsSSczKm79yYggP/6cATKCAAAge0P3IHmEMBC4fuVYMUaCQjZi6MURfEYmy1BhYi4VwGwTPZxjoGVZOR20IuIvCD1BHlX0rOJ2UtTyQKCxtmwVGA2HOcWmpS4DYQFqCbPjlPYEWAmnncNMvK7f1gEsGGhwS7+mBrgN3Tkdsthb+ENQoHix2lbk5GTUHbgoSBQWNs4qMDrOxaalLgOQFsmz45T2BFgJp7swyHSq3N6FyrgVAVUkzGkSVgCTrT0rkj7DAF73prDXwL/bCOisKZdPUqhamrFb+m+gpHIh07MyV8Lba2RRixzoHhEVedh0+BfOjjtYJxpvWut3DvBr7hU+jxxzpHwgBy3if4h8CkZQ0oGXL06tpTN5ralUtTK02/pvoKRyIdOzMlfC22tkUYsc6B5xbzsOnwL7Rx2sE/11piCmor/+nIE2N4ACAISQFmLKRJgQagcKSTiGYiAe2rMpaGhFxGszaaI+AHEDQHZa5F5xkNu01irwbKC/9E2bYp7GT3lQdBafsQz9aUbCk/y2l48qs3y/9X0T8v+K//LbTDu3VjnruX9f1/640nO+qumsYCJht1aswkIH0NwfDjbCtAr6wVef09rT95St+Xb++iH+X/Fbf8qlSmHbRkzwwkgc1HnQmd5J558rsOoJoAWJxi5kibMQONRHmrZWf/auonKRmJTrKIamzMtXXTq1HDPqfqVmURuFjtvNDWtsfGqxP/LIKnEtaXtvP55H/X2bKouAA2nIIoCEF+UxS7fviwdMrDQacz5OUarDCm8jJax1Prlq1sIL99G5v1CHlXv0FWIFi19lb/+UGetJgjaWhritlVsnwynWmIKaigAAP/6cARiugAAAgo14ukCK+xDRqtaYScGCETXi6KMXjEYpvN0M4neCjFdUabbkjpxtqcVHOyDL/Cbqg8g+ABej+nzgunjXr/d//tWg5KL7yqxaagZZRQvIG+SnhVUIOuZMfV6HVuwlIOAAIQAiSy0MEHNaZQsNUF6iQIwdqKuKUuJIvUwg/CR9OnPKdmyjXt/KHfSiXM9v29a+VPQjY3y7vUxaL0M6frR6h7+sSMJyMtOyuSjYtlZEzI/AvJxj7O+Uf7bZBvXhQir3dmcdVzaEb6vNA6GU6USWp/E1+B5+GvU3GiRKGJv0bAYduz/UNNJfrdJt9t7khyEnm43uF4q4meiE6oC9/07N/BDK/qb3Q6N+prpFuxwyMzppVTajaMSz1ZX2XhW/rSl0SnfBP3R9Mo5Xm0xBTUUAAD/+nIEgH0AAAIeHNvRgTiUQIa8PRQiuYjo13+hlEdxGBruKJKJKhAFwAJbbkdAy+7GoOzoindA9jkG3ED5E7ir59NFKPmPoXq6BgUHUj0pVSXmfITy1VOb/SuUO1hEO6bQ6lqDDlDtD5isyQk1MIqORysCj5Bccz5VbUDdVeb34S+5ZF4eIKdcPP/8uA9szsjEN60EmhxgFToKsoR/SPFmrOB32sO4KmFei0EIA1lkpttuAmM+vS6XxoS68KJDiQvB/Sg9ByNgn6Gw/bVW7p3sj5j+3rYjnnggNjniy15ckQ0NPi6jp4XpSyT0JTM91TZqsCAgAC25JRgJn7w7G0OQ8F9gzjFy6jQbiftR6IvBcG4J0PchnqW/T/0S2/0Yju+guE1KXbFSXpErmHa41H1Hu2pzFhVEOpTEEP/6cATsdwAAAewYWrjLEXBDiQtnJKJmiN0fiaGYTDEOEu7oI48OAYAFb9+FCEdcMvhlePKq/63O2fKjKKTM+Gf2qKpD1YXnLKN6hybG07l3jEsaoMvajV6GEUxBvAvtspDv1uAYAA3JI04/XyfBbeFVX+heFrhDKA4tzeg1Hoorr1f/52R6n0dO+rPv6Va/UJBd2pVE2q9a78zamUUUtYPF9FSHKkVJLMCedTSktt1kfiW+1H7oX0KJKO1FJwfS1WpJ06iv6NqvRvR9XtkqvWVA1mqZHLsM/NIm1kL6Li3K47tFRKEyfukW4a+s0xIkBAAFNpOA1HwSHD4FfR+l5moHbv0QvoeGOnKjKv1p6ptwRlnNUFHHXriISys5WXut/PrWUOrCYopcTDEMEFiDe7NSCYgpqKAAAAD/+nIE9dMAAAIKN1iZjSswRGkL6gziV4iU23tEBFcxD5uv9FSKTiA27dmFghVUE81kABbYRVgz6s25PlOVkG5JBYkwV8xB1T6gG340Udp6ltp6v9/V9KfZrt5Yjsi1vd1Ealt7TH+G7oHqQLbblgYNI5Gnqj8v1QvC41w43AG6atVF6+Ou3P01sf7+p/T6MTW9kBrtb59P6/TSog8QQ0YOKqDF5hCnCc5Qijx9mBtMFJtywLFDsaWjXtF+NT9NtR2gsEbcBN/zc65eHr+S9frm/JeN730JBj5sa0U0IT0PtUwoSBUJRCpdtDWHUOIC+ybRJBkabCrkcsj6pfDwDPqIc14RbtVGznSEOf6tVBXJwQokwPXNT5PstNfM2mymZkx4V3B+fYz/GSo1Vw4W30lN0X019KYgpqKAAP/6cAQ/EwAAghEr3NAmEERBxvsqMOJcCFEZeaEYTDERm63ocIrKAQEgFN23aGM0gvROcaCH43cG1GpFc/W9REGydPRc3Tp8352ZIAYLD2LF6Vl4Rxvr1vgF7xrGJi4L0OqvUVi74lVJAAYgADJv+Xz9Z8sWqFLsgdHy/LcxHiIHqKDz9Q18XROMVi+bo3ff0L7t7v5Pt1Vv1bG3hijUyvKrmpmkVb/072/SCGAISQEm25U1ICEQWKBP4gF5V0ahRHEfJjVEH6eM3/dB25V+/23UM3Uqe9KBtPbU+uX0P4bTasiKnj3JKcLEctpyjoEFOWbDqFrpQIniUVfEQWc+8TY8r8MfU1NA5rQZX+ni/b6v4oRegJupPvWQMLPSS8ppz2GDlgsETyn0FsuLNLUJrKZdMQU1FMy45Nz/+nIED+YAAAIhWF7QQSusQgjMPQyiX4jRX31BHLoxF6vuqGEVNhECURMbbmQpUY7T4kF4/I1JSS0AId56Lnb3woOmH1mEHbQT9E8r9Wb1b6f6R/2f/Q3yfdMlqat6/9bdjihAsg3K2F6VORgvJttySXeQSp976q+JAH0VJmxHoLNpeNbgvToNf69V+n39Rvdvb6U7/L1XTN8J7C2QYeSMOGHbD8VikAb62y+LeQGyUo5Jcc5WdUOXMC8A6y3ajew2stBWEN/KB6l5mn3fuvyPyq/KCV1emraRlHzl/2jvRvGOtnf5XfKT1ZrkTWwiyihO3J1oMCQGk3L0hsoZOCPMg+hutqtUKI5xvXfI/Xwxx5ATR+pn9f/l9R32yZFqqU5/+zDvZtlI+ssvKlVt6yaDPKIsghJXs2E0wP/6cAQJLQAAAiBg2hkqEtRDyMszJOJmh81hd0GUR/EQLXA0kJQeICakuOmZzUg1Aa2xSDA04t8xR2RjkopjZMKNZNtS9OiK090+CfRk/+b7ff/7dSev0b0bu+joT5v/trT5HXi/V21jVhsAJKW0oI0768LA3V2Uf8nxQhOblCoMbBtrJj507cGdSIsyL5vk/9Tfb9etKu7N27K25w1HZoncoJ1gElvksMWJz/3Vv1haAlNJx0KAyCYxDc4SDNQA7rq1H8LHavofEm5zaB0KjVQ3m+n/xPp/6/g69dG0WcF8i3rX3+X//+2C53dKRpBGOIlOSW8DkTbNtYinUBOJoJX0cd2/3y+bqPtfp0JexUpy/Hej+reteWta/9SOvOXLOVeavHff1b0a1wg/cV0ZxsmmIKaimZccm4D/+nAEASIAAAINOF3tIOAMQ6hr/aQcAYh5k3A4YQABDDJuRxJQAgSiA4AAW23JtDHw6VWMgd0QDqXHbV0d+V+u+X/1Dym2c7Tx84hzF/85Ogoe2O+pltSXf6tejP3gTPTITUo5ur+nQkoAa02U5JbbWJggezfAytMBvHLVeYJRtXCf771Obr5dPp56meVvs/jhDsUfzfp0PT5/TvnV5VJYhnzlHpCbpjdX9OVABJcCJcMDGL4OoAhLB9y+otGJMLZQslCPOpaInrpIQh1//cAIST//IBgbuEEf//gZ3bkb///z3Jc53J/////YDP74twASchjUoFbTITAZZp9T1Cmi4sQZc1iu2j3ta3r7EIdf/4oRpP/8gfF7kJ//+BzuTkb///zu4ornO5E/////YPn98XdMQU1FAAAA//pyBF6SAAACI1Zk1hSgBEUKzKrClACImTOHvLEAIRCmcb+OIAQBADSDBgQBgQCAQBCTBMpRyVk+UWTq39FMX/Kh2b/ylGCZ0//LCRiGL//iTi5USNL//+LO0aHlFVRP///MhDqLIqj0QaAABhBgwIAwKBQKBJMEy2Tk+UW+z/1M3+yKzf+VUE3T/9YkZjF//w8PFyoMMJl///Bhdo0BlFRqDP///MQhxoGMdR4xAmAK0rrra20nFRehEyoht3zX9rqjaXv0+ly7uTpp/u91uhqo715DPYrzI/N27IlK5/R/ov7dN9qkW4cghM+OP8RUNyyLjAAJISNCv/7ZJMoXSqoh7VVu65+mdm99VehU2JlM9P93ut0M7o715He1+/fdkyolK5/R/ov7dN9q1waMT6j/BqgllkXGEwD/+nAEQGAAAAIhNd7pgRGYPQbMbRgiFYjQ12SssKMBGaBxtGCIjgAqFLlKmSAVCMbrcgfoeg5eM7SOfyq+ggnU6Vrbu/p0MwgykNl3T29ROl26lb+Ak6Ovt96nkBLlFuvesJFjsSnlE3/IhptTSKRtIkp1DwVCHWQGWjl7Lpr6CRWoMVR9qZn/6M2Q2XdPbwYnS7dSt/AVfGdut/SsipbtyPErD2Ev9QCCHGHD3DXiy80DTI1t7F8TX5jGNxplRphpOGt4vR7CQjqKdG70R6slH3+32lmoZ6l16oLN04VdkSMjlr3Vsb7Q193LKz1oTjM0kbkcTcKHlpvDidQb3QncG223hn6r6OupPtnperJR9/t5SyuY6GeUB19BX/5lLosE4is0jnuKj2E08NhpnufqUuHWk0xBTUUA//pyBLeZAACCCTXbUwkQUEMICyJpYi4IpNdrTCThQRsa7FmmHCACAQCC7LbQp4PUotYjSTrbLFX+j/auDVtS/NvS/PwTPvp1KnZ2rIfqUyJ16qsj0oLO5f1JPsaepzJnO+5Xqd99FYDf+YScbMBEIahVO6RD8WB/meDv7Vb1VtMy+N4MNRaIn+M+n/t9c6OVnOhtLZVWT4/9LX9kuHN05oTL2lf/kLOe3LaAAYBBct2wN4nHyxtC3hf8Kxf5T2RpXNduZ9t5R/blGM/6mMy/tRZ80pnLRXm+3hpblrzGoMwoM1XlkzaKb1KXYMdu00vQh3XmLjDRNO0wkeXDhNhSJxnMF+yFXmkVCpqcl6jDRmh69eUK7OzquzWtXrKHO5p/5ypOT+pK6W/M/U9lMeQ7aupyuSM9DUxBTUX/+nAENdcACAIdNdiR5xNQQugLGUGFCgiAYWDMPWEBBZsw9HCLVgG/8B7AEkGKrCyjWUm9jCVib1cneeUITXlBc3A99VEvegGn9KbaPqRWRGLvRnqqHa788im/Ubjpcw3Dv9Q/Tr/iB/9gAAFANf4EcAcNoYWudQbaExVQZv6hECRpYCCwJqEx/QP1NQf6ePTfQ27zls379eqUt07ak0rv01Z9hJ/tFf3enwz+9CD1+5JAuKxIFmcMHXjhaRcP02qSvzihboXaA5+pMRrfsehcfBQtuW8YdS5QDFxUks4DEeS9vuvs3oo/JJVsu3a3ZH7BJCVKlG5I3LKmkyR7Muar8vsZb0+JAbXcyrZQzq3kqyXLea+DIbV3qBt141xO06v/1oDKGCFYSfWiXEFRF5LLXTKYgpqKAAAA//pyBPaWAACCGjZb0ScR+EQFe0os4qIIiQFpRJyqAQ+b7OiRFSACAgEi5JNUAmfCZ1Dy2Bwq8a8+9NBnwoMrTZGoHf+Dfvs3d3366nepSv2r6s/oD2UWLfHv9bUqEphIQMrmBSJUZv66gAD0LDl22GwAYsqm8B88enr9tLNt8G/FaR9fiAvo4jNNqHP/n5tG6o3p89cO3qKhnNxItRVbJxKUq1tSe4Wb+7S+7UaAAIQguzfcRKiZdamsJtwZCjcAuRSbTgy8ID/PRqDE68a5H/5X00bRid0N/5znt0OtGd2m3ZC1ZhtaxlCVqUiM/5Yz8XIAFOTX8FjI61MqrxXKJYfOK5VFbVBycK6RrdQ4O79QY4+Wtum3tbJSaYv/Kc+iqJLiQ1ddC/7WEjUMGvetj3Sp56vySYgpqKD/+nAEW68ADAIRKVkZhxNAQ8UrGjEFSgiM32BnnEuBGZvtKJMVUgA5Nti+JJkwp/JFPaftKJwl5OLZVohObiB+uCahm7dXkV+bmv6/FJYK+4Nqah7A811wetXZdKqCS2uKOr9/O6cV9QAAYICS22jWckT7Vj8sxYC7X6BWXxaZxo7ljWwMyarth43XjRBSVmQzda5RvqBhsSoqiUslSzs1O988dodW3tqr/2/WAE5dtMYbC4P8v7EAh8sBwZ0CbkOZgaZ4WLarlNArczeZik5+pPr8LXQVvUulnN1l4MxxI4ckiyyZGjXO0E0eaGYr+3qEQnBAJuOVlDUcEFaNbwwvf6Dxtr6tUd4KKe3xNf6ipGm7+0irK+8Rq5iG0/cl6VdrxowqzR/qpS5yWOf6nwTCO6KoNkZkYmII//pyBFxzAAACDSlbUOEdpEIjCuo9B1gI9N11QxxO8R8b7igiiQYAAgUQo5LUMaXHGYR0YPKtuEvdX2mkG5QYMncnt8+SusnvSpnmzX6KyW+sP7AyePb7Xd/StACFLXvT81gGh1S66mgAJACCk7bGgnjDfm8hlzDxUh5J0cGLW8VhO+QBAzQDi3iVl6CDuWBrb4BTVlqz/tbseplOqKJj+oKU7XGXeK9DP6KQLSaKTTdKTJzC86H5E/P2Hdl1zyzNUbhyUOd/YnToxN9Plfr+H2Dl5pS5Fb+fBnLcF3IBh92TJljjaU3x+o0osS81MDFhwaBBUJJSaTigDtDjRGCh32fct6vQQfihvB70KnXxCfp001VuQ7ArrcllbKcSXpUlAZQkhm3bJbB8RA2A3g6SM2tT2BUxvk6qBoD/+nAEpPUAAAITLFlR6xJUQkbbNyTifIiZWXWhHEwxDast6FGLwgACAABTckgM0WtSpvo6o2BEj8MGj95i/RXkg1q4VuoraCYvXozt9/Z/b1K7uUVGx8eLdjWbpzCW84o20SGert2dHWAgANuSQqybiyCrUCLeBpPf5GOhVKvHyIfyJbqd6V68oBVC6MZMKPolby9A7eZ+idzsmrraDFddGTJTmZXM+1Nen3AggOMokstOVJsQOcLgIDygT9FeEHjN4d+DarUR+TmKSo/X//ubqBN19/3fgz/9E86/+5V7O9EJWhmNqb+lwYlG9qbHwEIAwACjsu1x7uID4XgCNfQD9VbeQqc/r86CXbjAd0vEm+3/vy8FoZyzu5ffwbf9G89elO6+3q1EO73qGe3Rng4/k9m5MQU1FAAA//pyBE4MAAACDlDf6GUUfERKGvM9QlwIfN1zoySsMRAv7ihjiT4sMCzFRKuSXRHF4IQc9gJFzHWF9Mzc4QHrjRSrVL78a5HUjKOO+n09TNyg9f6f16jr0p39X/fs/q3ttVuo2IHRT6QBJLf0m0rbO2C7OqG5XIRoSRTqPuPI33hkOAk2RtaLxCbQYwXnbBBillxvR7afRfHf//XrBjt0+/q/r///TX42QdT/6iQQJWQSUW26waM5NHBf6KA2dGRF3QXnarej9QWrRo5O3V327e/r9jdQ1ftzez+IAxFgh3YidTSbWp1Gj5rJ55ij9WUQIAwxJRTbsjorLnr8HKl9T+6F/JlB1sd+Eu1W8vRX9WT3/8vqK+7+XuqMkGjUy0/0vdj/fzN117eb7t3PJe7eeS4JqDKYgpqKAAD/+nAEHTAAAAIdNFedPOAAQsOq06eoAAiJh2zYkQABFjEtSxhQAAADtv4ajjwXAJK50H49fi+gxP817HBo8DWiKOD7zBIfVybq0qWGOd7t9/v/8q2wwTXVGTN9Sp+ROVAKIP3IOZ3Z/F9IIAcuyInZMM4usr8tsFnADSGJu929iogiXFJ4zaC6FMG59B663KE9D8cHiaeXN06fm5QN2s216WRbEGj5zO7KtGNywEAAkQYYZf7AxEHAaC4nmIxBnN8e9aOYhHcM5QY46JKywTpTzt1+3/3v//9sv//+pkbU////7oEHZXVyf//yN/9YtSdQAYYY47232Bo+DU6KapN8P0VwM6jDkgIPi+As3ES4x923VqRv//zX///SWn//6mTqf//v/mQOD2K6nJ///b/3dWFxoi+KpiCA//pyBBAIAAACJxdgbwRgAESi7C3giAAIpJF8JiBpQRQSLwAWIBBQAAJIAEgpykNgIjINs3Nnv2KabmjsnKX/C0FnHwfQTy78TqcwTxO9RzXEawsA0S4WXrD5Dz/Lgglb/3dZ8swves+uPAsZKKYAQKKcuMDIEZKxXGx8516uxzM9SfaQWzJlErgd9QnFnME8TvUc1y6wsA8uFl7Q+GPD3WCBe/b3dZ8sES7HrPrjwZA/JKYVtHsC5bkI9JrUNFhyBOpqDXduAJEWTmzFozMdb5/2GxHSI8dGeakNHZ9q5hiBhUetijxZ+j7o6hNel/fQBqOdq1WNYD2lKEqB3xjrUkwxTWSACOtENPE7QxK1XUw11wUUdci1/fdrFxTErWw8lhVrwVOuPtAcAsKuQtco8s/R/Vp/78DfO1D/+nAEl1MABPIYFV4pLROQQsK7oD2CHgigSXanjacBFIyugPSM8AMCfdAeCk0Kqtlr2e5WzXTj+FMljmRroafUFOIDQPCoTmFtYBjiwGORP1DjIV64cdesnATg4tLjLnyWxG65aK9O6yjMhmixRH5VgjVOSj7urnXe3RZHkCSEmtB0pEA0ES4OC4shxdUOUB8UcB1xEQAyWIhss6Xi+HEuOmkBxNerrmrdu++oIEcSpEjpZ1/KOONMtV26JqKbIVG1HnVQ8dCABKkAiUAWDJmsIm2ohh9g0BHnoY/uvpe7em90xFUZcw1VovTSl4xW3a2v2LNQxWlfunNDBbx8D1SXw4GYfe+iXpcPNCpMi5YzHgLBky9YRNnbXHrFljwXQc1OdfS91Dy97picQ6Xc1VraSpZOrJ8mmIKa//pyBBBLAAACFgxeyGFjgEJoC6U8Ip4H8GFwIzxwARuIMPQhlCxAi4VG79jgdXCi2qUFmTrojgbuZdW9Bixwx7daxOuQa9MYFEBYPjTx6WNNNpFIe6aQ2DJ3aPe/1PUptCCTnOV3yKpkBAP5ypYks0GPAq7lxreZG//2uNvetxrGYiqRsoJE5h8zoxzyhfLV6bbWQuxOabiLtV9+6ugVuTNL2Jqq6LervpVOjv5sxSAgejyLRP7lY8sFCVUvLTvKs45SzJnBCYcwFSQBeBmqekDGHLe+fDqK001UlabDNhF2xj06m+h1NOv/Rii5aDZJZHJoek5Q1xERfA3qBPSPrCVBd5YLE0PJIpCgeVESnOYOHgoPNu00j1UlaWTKBUROtkRzkwM3yBhSVFr1ltkynOkUxBTUUzLjk3D/+nAEwV0ABAIGHFmBLzFgQKZLMD0Clgh4YWzDLEWhHJHt6LCKQLguJVgQEGbmLZj724X1RYtiPogRaZ/yFK7aOaqfLjO1bIV/Lt1A6ZUDLWnk2MQ9hZABDwKETxY85BtLlf///f+iuXqsetA0kFbCJlprPa1B6riU7AJ2NipyTJSAeb6lDK+jXIqk+FK16XZF3VW1nlMiPRNW6O+CPb0jKPr/Z//6PRgCszy5Y4kiDxkrJI02eod5ZaQx5qqDdWfy3uwMxFU44tTSqAg2QtDoGeLFhYQoVPKTZHnsSrQWa2iGkRvZ9EM99NT1QCYgFG5LbqsmlhYyWk5TZHBC3nzQQhVcwWuDuZt1qLDEKxDG0d0MtL/BH6DWxLA0NLBx+4m8NV1NS9Z1iEams0OTo9Mo9XoTEFNRQAAA//pwBDJqAAAB/iReyOEczEADKwE/CIAIqGNrQTCjwR0WbdxnigLAlVVqldUsYrljkFsquhPOoHTOS6vgURExkQy5PL8ldBRHR4iHmDYoIMUMFV0dOswpHtsW1V3pdprU9NTTEnUoHvldNxcooyis9CGIuPFxleOOmWFXpTBFZBYsejkc4DLcZU0tWNO1LYLGb4x+1LnkWboq8VUtcwtPRVR+jX/qpwAAAAmWW2zDrHg7QI7D/dKvmbjedS+hC3GiXROH7pkPpjAyTv71oIPIDwLGOAdpdhFzUSu9blPCJeyta3j1s6NXH8z/nkIEhJtuNLFiE9DJsFxcfuO7PjcU+5JbkbFM4Lkd74T9W0ulO8ztvM1UJzILF0nbhfQ4gwyZnYHjqu2zejvZnSxIyPf1HZVSExBTUUAAAP/6cgR5UgAEAhIY2hlvGPBEBJs3PGeUCIxjaOexCQEVEq60ExRkAMkkg7UDRmQoZMHUqepWN9L7N9JizUgZoZSsKLoBnOUV9NEA5KEBRh0QgqPPEBGckjKx6B9QowK6l0OZS3RTrV17gIAFJuWyY8kVNotxrBT0fUD+Vk+myQ7xuXuoatE6ucPxa8mGhHuqUdH1m/y+sE5djgkm+wfamKFu/hLFVWZxaPezSToCASblt+mo/4SPJiefsGLcB13pR8zj9kHDlEzarZ1cCDG1RZnzUuESBg8NO2JeBAmls6o11ebr35U1Cs07O73Rfs+xX1sgIdACN22zeCFNIvDMGylCGC7qUI18e2OM6PYEdlZYQyrr223349AqVcoVsatiFKDYFKk1qIrtqq/b2kVlLQstNjXN+xCYgpqK//pwBFSmAAACES9eSKgUPECm64oFZSyIyFVqYLIB4Q2SbugziWaABAqe6usYVWDaHRDtjtpQE67Zu6XBcydWxfSrVv1Zc1S6kfZHvppKZ6uuEoipE2NGoJ0PdVF3ZqdSipF69Kt/7YpAAoATTckhhNmzHmCe6lfg/6LkAapj6m5ksbDWVR0oWRZnZW//Z3TeZ2JVUq3p916XIzCKDaj5Crz2shQr1EvuqILbUuuSanPA0NtuWn4Gf9CZmybToLzHKOzLWVZ7z6FmHPiE4WdOEGEDla2mCCBsQRymOz5PlCajjnOc76UPe5/NvtQ79RrQBQQS0inLCGClTKtavppQMMnixsLb1WvO++EZa1c3lRWEE1hrt1WJFLVrpfuOikQdHTOhlD3Hw0GXDmpYVElbnz2lSYgpqKAAAP/6cgSBogAMAhsY2ZnhLKBEROu6DEN8iDhlZmeMsgEPjGzc8RpQBLkv/pmVBMyGDYpEgoBwhPLahH3rDfAlBFV6EQDXNZBFRpIGV2iHck+gu9oprS5SnSMwcK1/WiJQ6gqtt6/vZ8ptrRgAwISllu5LR3GzbEpwygfdgQmjVRunbSy0hNeH1fbq7WhQPNyFNNDFKCUQY5Qlnghqbijrrm5/EVSh8gh6VIdEvwFpSAXLf/jFSkVEUzkpGhN7vMLW4aW165iBZfJLGFtur6QTsxgPVLPHcWWee2sDxVYMyFNqnEZL4sNvekOIpXUp+vp9EAAC5d//ap4NThJhvwj4+Gr7ytfTXRnEu57GWgpsOWja2y8g8hYmricRWOSWkzjlVRcZanpScgikg+C15KOvZsU9PTuTEFNRQAAA//pwBJ1tAAACHCTZmY0rlEKny3oNpXKItQ97Q4RcsPYfLqhwjn4AJtySesT2SuVxszo/Oa33IbFUnBgGGWvQ4a2KR6bh/Rqh78Q0/o20EAzQ4MZQ16iJU7Dr1qW6mSIIaRaklSr0/8ph6ABAAU1JbbwDG5hKESQbojZyKE4nWJo210a69HgI+2hVu//u3v6K1bSUbqyO972Tu+1d0EhrTm4WQe3F3vZwNVOU9GAHTAmOOTKdMdyrJag67qR7Fw7dJ5fvdNSWllLflD5qGK9lV0Rx9LidoK9Uqll4dTll4cpnY3xkBV77lrqV9uEXTeJotkMAFBAKTTdUvHHPu83Ks6C3qOAtq2et2WYu8813//IYeYPeQz1NfLH1XXy7KDBacS+jQ6SVf0b0pFnVE17KkxBTUUzLjk3AAP/6cgTcIwAAAiAVWzkDKrRCwqrzPSWSCEz7d0KEfnD6Hy5oM4mSgaCm5Lt1AP0UGXLgmomU9w8UB4iqF22TbDIdAjtAdY2eTLn5CBDaQS2l7otqQm69DBI/citJ0sF7WsEpA1bdQmvKRaoAJS7bzLgjaHJaSMnZiveQk7PFXjrzZtXBAgztKQ+r5i7v7UaTgDIz5hxQ9oelYjfW/9sfkr2KO4eUVfMbkW9K7f+iABgSU23JbxhUK5L03QU0oTZKs2HzNayrvQM149aNT7qruGvsLv1hM/Tv17Dc1nyQigpTAJoJAWLucrDMopTKnf/IYAACEo7dvlgQzTSpxDqLtDxMCNKVJ1TfDXThPx1mahPf39/bqVqqX19e13/+ihnRatxFHlH2WLs7BBjC1xByYgpqKZlxybgAAAAA//pwBNVLAAACH0NavSTgCEPn25einAGIQTV2GGKAARQmrwcQcAACDJst//qAZNuIDb9YzXfKdUuYIgIsVqqlkngsHiK0MdaXODf5prmzXatXpc5+d5ujGed7P16X2/N8avzX5U9+V3V/0QIATTTlkip4xzoKrTCwJqw8TdfLcxProbOEN25t7bdvOOfV+y+nobWvm/fr+t46fJPKhvxKDZYPkgRNST3nBVUcz1ellufJicw0m59coIUeURDQgO31L0/yK/P5Ti5n/k8TlIYb/q/iAiEDjxgj/yf5xVyFdWN/9//M73D48A//9IVnxZBQEknH7yc7MZJeNmSgADLEwJD7pd5Bi/mdlTfr6yZ3/8q46OHFf/8cHRMeXKDv/b/PIuxrqx3///nO9xuPAP//SFXnxZBRMQU1FP/6cgSpGAAAAh0S4m8UoAhDRBvZ5ZQACGkxdyQEV4EOJi/0gIr04jlh210aRJVLRBGOR3VEwfGOJAjTLSroMFEQFgRFVoBYwtegTMGsE5DwuPMRHI1ZQbXPLMFiyOj3aZD7mqhlN+Im2VYBJS6qq8a1IRU0y9lbhOAxjKUTq7ZuRsiob1yvbluiMyiTBrBOQ8LjzCxHI1LlBtc8swWLGG6Ol2WkPuaqGU34ibZVgKAXwAtpB9n1RmnOPgMgu6fGC/NczEXniZiBYSZlvLn/rE9Gq/5jUP9OV8/zLVsvCT/Pxd8rO4YDa+HV1j8PLrCdN1BBYGUrZIAC65t6pPj78NEzIyBG5TnYp6FwQasycqFlX/rELRow6r41D/TMrLP1MnDcvCT/Pxd8rO4YDa+HV4/JaxtKYgpqKAAA//pwBOqMAAACEjXeyGcTKEPmu/0UIscIxTN9QZRL4RUmb/RQi1QAMES+Vf6IQK5N2XSVBB2ZXD6t24Vqtv9FKxW6+YxXxK+j7ZfmeqfRH+kMn2LI7E1vPCJrRfDf1AYS5IiWFtTWJJJwAKQrj0bSSlVIQadjarFMHIacwpUd2YfCx0o6UZQ/JIK/yaeH5PqH4nMvkT/SGT01kdmt7BRrRfM/UBhLkiJZvaxJJIGaIu7Wu7fXZA/SZ7rQFeaQNq1bPibxuZr7Q3VmmH9arKTmRHr38Glyvs1O/qJ/r9SrS4/2f07WPc0a2ZGRJuxQGvMZ0kQ4ACQsJv2zuyKqmIJ10WH8BAekQVkbyPGttQ+rIYLqVkkfMPmmiEAnMn+PYW6139RP/+2lx//9dlu8a2ZVJPu2PfqdtJEEwP/6cgRamwAAAgk121ErErBByAv5DOJViPEBg6OFGrEdIDE0FImWwAAAFuSSSPKiIGTjLymOhD0sQhB7kcqGWqzKYQd4TLvuqooAtlaj/u+hHt63aXSzb/tl8GI6Iu/u2fTv/8Wd83XgMmzTzXvYMGpoRvjmEYkbHF7JdCDwbedrq+CXtv/8j1e8qP2aUv/9vUb/Zt9XbEJeW0iIduNtduuVrISKGQ8aY1dIZSCSSkbbUYhV1YkfPdF5dqIKDq6nIpeP269aGBKZP1A5Z5SJe5GPiwEeaqfja/lQI/PDyMj5evX0KGXOdQ1nd+yuEdPZfbyC0RWzHZG3Qgm2FipZ/qEtKplQx/q5L146ruprL/JRVRymur6X1q+z4N6k305lVqCffLf0bwzvnivqduqZlRC88igwBmugsmII//pwBOcXAAACCTXamSsSsEDpy2oxolYI0NdzQyxMkQ+a7VyXiVgJSS236cBpAu1yVlpyjKoQwoWp+NxG/UwYIPDn9X0ag/5G+7ErZrbMtyOvVWr0a1v0H1tdIW2/L600I/5R92p1NWAAQAOSXa9eScejTAgl2mtVjuoAZZpeVD19TI48l779Mz6POtPifqr9U66zPoy/99H/t1fOXdWX/6oT/qN17LoZAQSANNtyX8oNBVZXVrGXdCViKFSZffQI9ng2hXsiqTemYautm7tg+Xb7K/b7f4/XSG0PTletdg29Gm1THvKHy50fARKkmRAIDK7bb6UEpYEAGNKW0n5ENo3jKZcZsvPt9jLDOPTV5fKxQSvmpz1z1+vt1pI+Rm5VV1dNYp4y5aXXp/tN88322aPzCYgpqKAAAP/6cgRq9QAAQiIk3lBlFAxCw5szAesMCDC7gaCYo7EJEm0o8ZXQwQDURNJJPsMOZGD3LswShx7WBd20CYJ4NAbHDMfUX0wwnX3V03g7N6i2d0OGrFhzShVwTYaijsjx7rJcg6nXxG/fW2REcckkhHkYY0CAWZ3NYbOxli22okqax5BBzUOMVao68w9M97uIWk4SfmK4OmS6DcUQNNnjNLQ4HlfdKdm/Z/J//t1vALJIiRckjbnRkzUXWvk/h0Dg2o260o01PdNFOEFlXM1Sd+U/EQ4zXYjyMdhJkWPzcmn5GQa4X+2p9QTpf3aX2/koAA25Nds46aJgry+TNrk/v3p45SAJQDPGMPwrSQZhTIfOeyEdQG1Tv9b9dhrI6i4O30yaohMsReKYvJbNbNaDP++1MQU1FMy45NwA//pwBEQYAACCEyXZGYkrsEPjG1oF6QwIWJWBQCChsQySrVyWiZIFR2W2akMcwnUZU/LQwPYWA3wNQec5bBgIZR+Hhal+2LV8iUSOAui7ZS/7PoK1NkQK97Ras6tsb+b7PvPHW5Nv0p0AABAJLNd/XVZvlmxLDY5vqdqTem4DmcfF4fU37XzqZzmXO72qyoU4hgs8NxZTUKztsvNNhKmt1PVv69no9jbKZLX+2kFUa23HJJZgxo6h79OgResYGMgaVbhINiJOtdDUf9eZe3N49/WtK9YwOjlpPBp6DSShCKQ5RH7J1Arf7hr6yW6pdeogSSckn2wwGFBOLSoWnejk6G4St1sT6x10CYJr5GR0ZjASvO/0Tp08frGrDNT5h49Z08FYo8cOOB2498hDJH8jRdknpiCmooAAAP/6cgSiIwAAghMzWRnmO6BAR+uJJCWZiQiXiaCY6PEOJu8ocJZmCL12w/opyTSvoimSqiZcx0jDIwSUetXKI4BB6fpDwzSic623peO/5u5RDUHwlL2Ygj069kObVNi1Yf5KXa8Kf1p9uACIC1te50YaA467mG8tdH7DqmUlTWd8gO5C/0c/oX9aXuUAX2bmV66UOurbN1av7JnXixhCaT3EqkVPGfI+71gllE1tSW23fJhtHVveuw8tIVZ4zuq0fWjbFtVY8v01bt/OdLAczxHuYapreMMxI0xPOY2hA90wyt4JJcsIs74XayYd1LQxh5CbEpJtyz6CK4pzTnnpQGCug6gw1hNkQKYr12mH9L/6PoFf6bPvvxt6KllPmSY1K/pzt29q69ivIYa2RfPhjQzpFStFKYgpqKAA//pwBN79AAwCGCVaGeM8hEJGa5olYlSIhLlaTCSyQQwgbEz0idgFJNy39ecDkfLnVUsxxfo8aJMXKuoel8tdZHSvHLRO83MfuxYJM0zVtfbo/Fe+2YVPU0/oXqcXOyztc07U9y+ycmnIwASEJNy3fFOmDwOlX226My/gN0l1YPrRXMZt97i9XYxve66/6PoH//R+j/pHsPdG1zrf6Dhsuu7qP3KIOIKL6npmAK7DmLMpEELVVY3PzRQJC51rrYBodpJ1CGknakvpSJX2A6K1TZRGEbIRchGvUiteQO393/txj6gI/fQuX4x99NPn/2UgBKb8fOzmCHl5Os19lMhau0kqdvLghzvD5O9PG05pETPw/NnqndlLf0N/9PEe//0R+fqOtUWi+fzpVG0BZ7uSrb+1KYgpqKAAAP/6cgShRAAAAgwl1pMPK7BARKvaBSIfiLEBg6GkTPEcHO/0NAmuEn8OdeNQhGlvYbc9kF5Ph+Y/DyXm30G6Q7xnhdCmqvRljLuz9o52NLxReNHVV2Eg38g/U3N18dkY6KvvSmrd/6KsAa5LUbcmihQHK2vUe1tJgki6WRLb7LYNx61idXob8xc8ubn6Db7+nPvYwoxecqUZDZAMKOU652g79/raMQOeaiBBKjilkst+oOtIcJfq84vcldGjHrDdb0aGujyF1KZXsq0R9P9uEE83Xo3VOR31bO3T29ObUGdKH+txR720/tlYSSOOxWEAEmJKySSXEWKHZDhLOlwFRHkuoN5u58Z+dKwoetdLIUttcfb+g2wIT/Vu/VH0eiK1Q7LgugnyMLxV3pBKPPHD11DQD3aKkxBTUUAA//pwBPONAAACCS3cuOEUnEGIWxM8wngIQQN3VGOAMRwhbuqQUAYA4SbabuJ0FpMcyxfkpwNNow+CLUDiWiQ1BuyqLT3R51SZH0dsVrfrfR8GMWHZGjdJUtLhNZiI3J1z3V+mrCaz1Isrd/Htg1Q5Q2yghR0Att+JDz2vAnwy50ohgGkqcplfEoM2KbFcRYBMzXOUCVRaNJ/nJ1//X/Ty+3t7fqTgnp9/VAAOpbabkvZ7SNrPLdtxxYMXIGnlSzW6j/VT3jq3pHQcGGdk1T2081v7J/W+3TsSvtzF5x1DWqpBYWq8xylXt9fZTACHxTbikvOeLGnVJsJ2nIYAM5ooAIuE2LZBNt2R6r2PAYjVdnaqfyLqAI3m7dWTKWyii6e//+9iDc453j0NoRSsx0fu/O9KYgpqKAAAAP/6cATqggAAAgRI4BYgoARECRvyxBQACNyDlbwxgDEMhvK3kDAGAIIIII/OhXq4Be1rSSjWjP5Gf/2/+jp/0IKIHEZl/4oHw+cUUXyq//w4KN5GT9E/+9nQgHcuHt6/9IYhgHy/rpAEMMMMlMZBt1bAvO5QksDXQUYCKux3Rv29Df/VFT/IyEMzL/xQPh84op7FV//ihG9GT9E/++6ED7rD29f/OQwHy+uukEppK2uRpElcEszHZIzNwWr1VCUnGbt/aKVKdzszUMx88z8zx3EzpU1VnxEbQHVCWp8nqHHmE6TF9HLOvm2s23N2Frn3MkVglNJuWSNIkrg1ma1iIZmgkMCRSusIxg66WBkQuNJbBYOqmzxtLiZ0qa8+KPQRUJanyeocxiaTF9HLOvm2/t2FrtzJFMQU1FD/+nIEcsAAAAIRNmfoYRTsQmbMPSgjfQiM12gMIE5BGJsuJPCVwArJZNdtbI27xsKLmXOfRQ9Ijk6lmbCMCfEF/wf92SJlvh/I0ZNMl5lVFZca49PIO7l/VFGNoAv3AZxUQg+SglsClQIZcL020sblhMH1XOdr7rGK3mPXkgHy6fI8H9d80OD8VeefKVhkcL/s7Mr43PQ5buX9UUY0igCti+4DDEFQuSluOqLzmWbhVY9BL/QzS4Yb7OdpSsj9IbLCQK9Msy3IHbZmf9W+/ytCtL2pbddsolpSfVKJ1jlpHiz6w3pxRp5mJHVsqLXf1iAAXOzXjnAP25wXtt2ouafFbZDapR4NGM9DzjGvbe3qza/PbLl3Z0tuv1K23109Y+9VtbMX76hUspMaGADFq2B0x3tRi2pMQU1FAP/6cATAAwAAAgYT2ANMG5BBRrvKPUIbCHUBkaGAqjEYpHH0YZQeMLNNOQay6K/FkQ+ymQUncKSpF1FAYDL8H124isL/0pmUJhL4IXCb5Se7GCK6EmCiUVlUGtbk5P4NP7Po95L//6gAwvU2sklEXH9eGyIzTX1hiOmNaIbbyI1UP2fZGqZ/wjJmlK71RJkX+jWe/qzTNwb9cRPfs+hbVJeGn/f7PWORkQ49QRLL9bJKFNjMwaqtaPbzhuVm5xvOlE5nEABKJCJ2a+itQ2hUnf5Hf3O9VmSy9ew0N/o1mbnSw2n6X4G7kW9ZD6N4TkYMmlutclEptCpiLH+MYo6s+uxXKpJjjGQrIrPentRQHlnJoV6C6ZLZ2yfT/W2v+vtzpYa7No9eiOOepJ4/3acTOXVUQTEFNRQAAAD/+nIEaOEAAMIbNdzR6yhoQaa7EmTCcge4MX9DBSTxF4isCYMlwAAgCJmllmGAdU9mJi87VMnyKGBe7LUN713QztL7eP2Lv0ZyTUQezea3RrIJclqb79FFLaY9jWwzQgjjRptln6tlvr2gtf4EgLlKsnXmpYDnIdq8rcmqTMQr0aQ7F8frNqtgpnKpX62eX/yF5++dsyLZF0mbJ/eY5lqIodfkz8M//k/inS0oYK0pE0mwYmXsIGwTZdCXc3Mx6wMs442fPFSSwqgmgvQNsaymhOImaj79SjtjGztE3+2QMwWPvrzTdYZ+TCGvgK4tG2WNUUoeOWP9PUV2CqavoCYT5LCXjRq13elcprCLw6opceJ8q7nGEZCus6w03r3SaSKykbAz8amsIuRTflqOsMpiCmopmXHJuAAAAP/6cAQkEgAAwh4r31DFGPxDBGsTPwJkCJiLaUS8TKEJle1MwInsECCdNxtJRwK6dEct2TQDjMzjG27Rp6fA+R1by+cy5fFofaSYovjEupH5XBmNK2ArD7nCvvUe7NPuayn9SdR+obpkySwEm45DZHcM+JKzImc9bP4C6g0qBcbFrlHX7g8vN3qzvP10rX3T1xfrWORwEWK6isTLxQKvNKe2m1v/rLMQ2O/Kf9OsAIAAmii3AVMFmwaEYONhhth/PUpx/FxzNWj515GbGzpQwV2RXZpBuX8Zu14Pe/apdbk9sRdWMOrn8tonbbSe3pwq/1ESdFG5R5HFo/I8K2NJtm7ddoCnGGiZFWAyN1DzcnCMSMBPqAbt4Pdd0f9I5lD1qZxNTPxcUhdmryaz8eZs++xb9+7amIKaigD/+nIETnIAAAINK13JARTMQQa7QyWiUgjcl2ZnjE8BGxcuGGYVhgBQpX7qyGSUorLHvXXIQJcI5Ymu1ksxUYN+UzKS8hPlNgouyI1eU8sKWWRCwsfWHHRfpQkHnNXqeuwQRaty/9f8eDLLt+IAaVZFDM3hehaFqYIWTjPYIMiNkELR+lUVp0kVN9sDqnWd+Tqv6ytv67GRlqJbIqwvF7PzyGXi1u9VH/9ILm2375DodEJTDU6TV5N4dWMAQEuaGZA1Ol4K0oZchZQtbm9tqmJr2Bok4HKiwx5VjgVrHRo9yO80lC9u2iFGiuaIe1P/poEON216wewlq1DU9+fgCE1iyhQvsaSqIuggyvKr3e32uRWXZoTJWva+9yCbuWOSYE8Srr2IQBgkyE3VPuYltJZaKNN3f5R60xBTQP/6cAQjqQAAAh5XYNAgEoxDKZv6FCP1iDCNgUWEb7ETkaxM8wnYEiD9Ox2S6TuFFDQWjOZgbd5h1y97qj87KPXtt6+Nob33OegJspdWMiv+l6VTTdW09/Tv0dnM/VkZ12bWrHbwpLNb3l0JUqTccctV3QfuZ02w+aaim/HnaV6I3e+W/y41+etQzk+2RooK/Yu/dznMvNTAa+77mCdKnU6LE4CcI0ighY/Gb6fXAi3bdckl0v5iZ575q0w2JrCtfiDN1LdkOJGwf2f5uDCodz+sNcuxw0dawOssiNMb3UrUC6HlErpSisw1dydSe8Jgpu7/wFgyESSiDK5m7Q4bL7jTYBo4JotchsL3tJTw+bioyPS70/rgl9nuHQn1z/LmxGdzhWw6KS7xQy5/F96q/C6Kf+WTEFNRQAD/+nIEiRYAAIHgHleZ4xOwP0RrIzyneQkk93NFlE7xIBYtXMeVTgWS5sGN0W1TCAkoY7ksMlSVXKyrBdQBgXhoIoYBnDhhamynd6+zr26Y2iqV9sXUiUYvnm9RUo9Z3V03aAXG3IF5Gsq6NHXfG5RB1guttoDmc5BKqA9XpGtVSPM1Dl9KIrWTKtfLNhSCps8/tQSqSs1e5tiUIQ2z+l/3egBACimy03Z6kh0jluzvNSgESffGC6KQ2LcLY0zmR9IrKjv8WnMq4v+vkkBKPN2jszTp7SW3t3hiRc9iOI7Q5P0tT6uG0a2Jc3HiBSKKUtkbozjgs96zLzfhoIZLRw5XRTNibZp0d9SCKFRufGDPbgGeqvr6IkIjxEQl037zllJMjrcGl9l70xi9m99TDtLb+oLJiCmooAAAAP/6cAQrDgAAggw93TlhFiw9hHtSPEh7iOz1XmeMsgEQH+3okorekOm2mnLTeyYmyVc/zVqBuXTceQ/1X0m6Fbljl7Z8wo++9Vt6Lg9fuENRH43au3P/nswWo4/12YZIislL2qRaR6aEtVvGWR+pOvWXNIOYc3wLGxzXIXbiSOcR09rIdDP+Utitqu0XyEm8yryj1PnIZFkIT752yn8zZIMyfc+/6wXHduDMNM3C+DTL6q8nQnzuyvumQqwaSscnHENtGd8jHUcadb6O573R3bfkoUb0fEB3f2+qhJiyG0fM+9tCe3fi0mrv9X3dIDItlJOT0lQBWkoJW1Vu9gAl4/JN/i0LWJ9bTJvtH7zGHe/FWmEb06AVtP/qG6dFyClqZsie2h+Px/ynCFLZKreUfd0piCmopmXHJuD/+nIE0SgAAIIIP2BQJyssRIZbhySnfYig+2ZmHLGREiTtWJeJllNEubmllwJlZRiamsgWfSH/ucepX0b+/TjB1R4lQ6MsKHcr6tq+w4UejJRmZG2VtF/rsQ+Jmkd1y5545IYbDjUpAEU62lAnhtsTUkw13f57A3dMoeq3c714Uy1o0yI6cKWVUmcSawKEHdhb1Nb6FmSfmIjUIXUCvfKV/o0icYKg3qCI4WjQk3JKLnVjQwXOactFu6HF5AAFP8j/QoSr1JK6HcuRZUZdv5Q/IGzZXhQjx7r3M6DAm1xVs3jrHNob1p9+Nb99EQ9Y2b7CFZV5WZqIkVkeLwRwXZ+BSFi9ffaz89Xuj8MJQaVCGZd+hchWVA/Bf/+CXl6F3EsyitzGt9CVAnwfX07/3E1LWA1DuylMQU1FAP/6cARq0AAAAhJJ3tUg4AxC56uHpggBiMkjgFiSgAEWLXErCnABFxCpK2Ru2d6FRtKM25+wiHzJgqa3KkDXlub/U9nse0wX57HVRz1DbVVf6c42evmtmNr5v/1bmf/t/QgYL1tAGu9zATktppyZVRrutxQam+Ht8Z+hkSLlHaDIjmGfC/sSZpH5+XU3FNy9OjYYTOvhUeC3Xy9/bjNOXb0cYAJsSOEAJ0tW9RhhhhhmanVg5uZMPzxib0GrHy/IxT/z/+Ock1P+RnFDiBl/+UOgchEFHldv/igno0YOSy2X/+1Wh8chJ6tv+UCKwswCeLUgAAAAGDAYDAYDAYapCtjjdjuTvN/m/57/8s5BTv/kCDkCYo//kRqE5hiENf/5AfSjKULf//5ZFaNxhFLP////mGLUmkFImIL/+nIE6e0AAAIED2TvDGAMRQWMLeQIAQjA2YGlhE/hEJst5YYIMAEU2pdI2kSFgwLVaUWqtuRRjZlViWAzuFERAmxwBGoVNHhQSj0JVrBxJQKNLBTnHn1tYpjkrVb60+5/+VY9Hv6QAAU5f9ImkXIFgBpVRtRMzU1J+iXMrIlKsxTzJ6EvpaxWW3uxnRFZ9un1OSOUCjSwU5x59bWKY6tU76y3U5/9ZVj0e/kQSZUqt/GkgmoAqifc2t542vvg3kJ1zFmsyTBiEaFxKNf5kwbFim/rSpDW/RWYztuGVzhxKe4p/LjsBgJ88yZKvkQQyXXUkAAE/QABogIeUz9o9Wq3noJtP0DFAUtoqqOl00yd9+Zrt39Ucm5s1X+2jqQ1v0VmR/DK5zyVhXFL+phfmRj/Mr2BjR9SYgpqKP/6cATgWwAAAhE2XNHpEGA+pCxtICKXiOzVd0gYQaEgpjM0MIpGAQAt6NpJw0xlBZyFzWodz1vTApruxGI+rcpc3v0zqVXr7YpDo+WtN5epS6KX0Wn4D+WJauQyK5Gp/yIaJaltZtNVBpGNySaRopNygbGrXc63HwFrhruR2r6LCeNGoj+6PX2xSBZhVarzu5SMV878sSyznSFRErI1AbXiMNEtVvagCMM5drXHgweFGkUzk3K68b8sbaZLU8Qjz8Pqvsdpf7s9Fuheha63s7GKh2Vs1HS9zQTHVM2nCIBWhfdQpJZ7v9CSLupzzUcG2rL97f9rbqM5G05jlC7QudSvxsiSZuv5bcKv7tlW6O1zaz+7wREfL5HRnYxoIVaj9aWL0distq+0p0Ta7uGHu67tCeLSd+OTEED/+nIE8SMAAAIOQGVoYS3MQCgK8WzCdAhE2VwsPKXBHyavcJGV5g5VYtrt9bbcDIiACLulheUy1BU2/gyPTe7rxwux/zWbki5+v2N6vldi+FCaN92yK7uuwk5KuwwGnOALd+1/DfyuTf0gkQhBabIHilLPLqq8oilPFoEtTK2mDTDVYr1ubSmyMyK9e+vftlXq+RND+2j2R/+vL4I/+/3Twzvy///J29gPniIQltHCQG4rpPEkesdWpoemcDLFt7naEuDHjS6xpeZ0NEHNT35U79vfvszVT21qRDc1v/4N7ZDrd/ZcEe7t/+oAAIlKX9tYZQ5rPruyOW7KIc7bBPpCBAcOZbELnf1fZ9G1bU3M16aKmlkUKu5/e/0x/6J+hz7iY6trd6JZu0jxB/oJcJehT60xBTUUzLjk3P/6cAQOeAAAghwR3eljOThAQorRZekoCIDZdyScQ/EOGy209JQ0AAEQUtn+22w9DPYZ8jGx0Y4Pn6nA3vKC5KFa33PYou8VvcUeg0K2FTyUgw9NKCOMD0wF0PWHmO/rU5+Gjmxak8VdfrH4CqD50tmBpuNTrAM5aQU7CQxybgVSZYgkssT1cYeQ99sR5WU+hBSt8PpIGHSIYarVWSnW/X0Pr/9Qx+hX6tRIQFKnu2ux1dMaUlZrSLKRLtU5A/9U1ehB1evRtfT1uxqCl5bZe6oDCCaXTe39RDdkSFmMG4TftN1NQ59UVSdTh6yyt6gEo65JHNBKwmOghdsgf4VZ6HFKj3RQ9GraN4CuqQ3n6e/J7UPodletvpsVstvW096lFYvtaKd39b7UHE/Xw9Ip0uWAUxBTUUAAAAD/+nIEYS0ACAIZK9iZjyogQ2sLMzDicwhwsW7kiG9hFprt5MSUthE3JJAfEkCaYAVj0RgtZrdoftQ8DIZZgEFqDkw1Mj0E6tTU/Umx8pfZpba/tzRriip11rCqs2+9kVZt6j3Yl/+Md8mU0m1KQz8fCAAn26lB2s1QNwQk9FPUGN6irWd535J7jL+frfbo7/p7SCmb/Unpn/Y3l6/6f6shmWtHZabfXb8E3oQusqs92m7CNuYSXvC2uj3VoG2b1BduNwT6iFq/VEy5/Wp2hFmz/b+pTlIyBkRe14oQIOd1w88A6uzSXtJKAyPjkyJy1GsAEGVZltx9AqEXx41uwpj0hS+1lBvabhnCSa+vX21EOqoqNlLy929K7uj+i2otlFubc5iphunXLPWMYLk6qnx0HEOXOHjaYgpqKP/6cAS6kQAAAh0U3tBlEpxB45sXPYUoCGjVcSSIb3EHGu4kYonOACC9uRtt2OhiCvcW1qhU7biKaRbvHZ6DK9yQGdEMRRBVWdVUjErJHW1bzJbUUNaKD1+pwqQRhlRrtMWKORq3MJNJJIggIUmu2P460yxhKK6+K3r+hGvWACJshzIFOMbMB0ZhNtW/lfRtA/Q9UtF9GTBM4PFLWC8miwUJ7uRy2hZzuT/0/uABCprurudUXSX7GJXWVxSLke6lZOqHaXqJomsF07j/WLoQpImW5feESUwQj2hXpK+nOD1r+1orIqXW7U1DLXu3d9RECMGluqt00oBf3VjT0e5a72CAjCuoJGhX1b+bpzBtjcNSjyTe3rMrU/swh33YYs8stdSAk1KKK1LFH1kGBqjt1G60xBTUUzLjk3D/+nAEH40AAAIeEdiZ7ClIQaa72g0iU4hI13tBhG/xCg5sDMeUoCCU3L2tiYpxYnwpPhMePWEyAyUh/KljrJTwyyixle+2Qor3KDdtu8WhtVaT9iCykcupRRjop51yZUk5jdCXyDzIFzWzCOu0nJG5SGIxXFmUSPexg05Ex4LoJJo+rd/6NxtC9NHI6wrNtoyOptUVdufpjl2o8zsyXoFzb2CmaRWd4F3PeR3wBIS245JMDGlK53HsG6LLzUuSEoUzC+WZc1w67rH4v8L1Hy3/9ZWlIjgGJVEiZZwXpJiQt2rYnj2197sRm4vUpGlQS0u/6QclLIMGfdJxL1Xkp5BXdXoEwJCmaJMR6bhe6/bO2NFAwcSCqaRTZFWCzjVaxqoatq5vkU3XLYYUibY5aP/1JiCmopmXHJuA//pyBJzRAAwCDytYGeErhEKkWxM8Q3qH3K1kRJRQcRsV7yhQjf4ElNyRJoQkYDg2tR1HQ9iUY0pRtBuHiw0gZdsKa0zZKZeXkbV//sr09tllQYfK5FIs0UeqvcpmDuo8mXGjk3u/JdIJjckkisK6541a505eNu6a9kOZEaU4e7YISR78nX25OJGQodmxNAoZGEIYn4RG+NYBgQKhw83gvHvI/tNMLpSj6q6QUls7grQKlAeIglS8wPM8gEJPCVtkaN4Vspmzt+gy0bhH/kfMb/5nxzgruC7Fn3n7rs9+/wZVjFX19kbTtd0AxRqo27JchXQXRXPZgsV3YeOKfIZMx8aRmunnyeX+I+sva2sFQE2jkiY41RkYUKPHjxgU2dT1UNtB8e8sBdDxSIjqtCzTNCYgpqKZlxybgAD/+nAElxwAAAH4I1zQwRUMQ2Y7WRkCKYigsVh1hoABER4sTp4gAgBUbTMcbg67cPRUGzXEv2Zs/gCT5ldxSqMHHZNU278Wmb/EFk79QuER8rF3OOJDTWTNLHqX3bJE66weLmnqkgAgBcrvBQQMBQ9JYUV3Ev4eN0lkfccyKNhQKhCDdu/OTRunmXNoZ6dVK2f0UogQmjq6tiA8DYlWwWS9Vi2vxDclNaUgAu7ARCD1YWVo4PS9TiIVU9itRUcvl5FOhSQKhkiyA5lMhoaZvqNLKPe/bWl0G/9SCX61dmZkD8rqWGVCeRsdcYc3/UKgpOS0bmN060RBXCjFyW59RHd+tysjCY7jkwYgm3E+2ozQYbgm1/oIO7o2vt2WdSPtqMlnXBdL7cSfYDowUbSd6cqYqJpiCmooAAAA//pyBI/jAAACJCxe1ghgBEOC65rEjACImW+AeBKAGQWt8Q8CUAMBAARm6N23bbf/gCs0KFi2RnETsE4Q73pynbvPj/b9u/5T8rryZbZcNavnGS9ihXLDiZ4ezScy4jFRdjSyD165isGKHoAAAAVabckttt2AAUQxk2ZEbBczhl6Cm3IDkY3VSjqt+fGLSlgyTQyABMYPpIoQ1Vo9AVUfXWhkVH22s0ut1TzXvaYrAAAjFgtFoQoGwRN5s507f2///+3///+d9v/3IxNyXbZBR//oQBAIwu5yEE1UaQUBHKYX/+2KDBwuV5FPpGjRosKAdBKIAACAbDYbC/KTJz///////////3//9yMTckXHWYgIL//QgCARjuchBNYmwoCOUwv/9sUGDhcryKfSJjRosKB9BIEmIKaigAD/+nAEot0ABgIOK9+vGGAAQyDMTeGIAAhYk3gHmGXBFwfvpBeMCAQAu/MxyYPUU0Qj/vrVjZbsBqJNsXn39kBmQlhva8PvkyTlbpFlzyPuYOGRRxkmMnVNzr62CqEdlOblP+TybvVspIAEJSJBablnELd9GgQA2BsWB5yhdx4+/NEypqFb2kAYYKsJOdQ9o8gOYh8UKxV0qk1FlNd1JvpNQyM3D9631gT7Kt+qnsG7MtliRgpWO7OnfnG2yD0Bwki6VlNVIIaQc6dhqMf/+3mvnVGVj1LHNCYzcJUnUJNJdeQFUpp/rvvOEv3dP0dYAQAAIs1KYZkzshhmUESzenS9xFYPGGUkF2hIokeRFCRvVSdS0A345oTARqZEqWlZW5htgqB0qS/ZuCbn3ub+65yduhWtiYgpqKAA//pyBJbkAAoBpQ7eAWUYgEKj27AwwyIJkGlyobBggTUGcLQxDQ21QoBsNqFakT/RCXiGn7CCZVaFrODGDh9oRpepZ8e0JNW7FV0Sp3mW6HKUlGytttPI9aborao0A1O0A2wYToLpUrW0xJe6Y/SPLuqNyZwdS6SX8d4SQfPiYJHy5yoVW1EqdscT6GKUwlbra5tO8jpHm1OQK9m75ngaMSgNNKWj9VYw7IWpLTkk/607EVZ00PGRSDkCxmRA5yBRh4wWaFHCjA69Y1BUsTHPAMO0BUkWUeAp4K7EEeWCuPSmlsVqTUPn1IepBI2OxJttFKSguDb0ph25oXXxUtrf9krIq711Go/frBn7JptKDEUD+Ljbze5xy9vvt/5RXPCtg2U22vY/nrBVX7969DeHDb2fuU1mxVVMQU3/+nAEjA0AAgIeIFuBhhlAP2R7cDDDIggsYWymGGTBFARuJBYYCMbdSgZWCCYJec/d6xx2zjK02NS81IOTcj4tYgp2cj70yYQgWSJUlVsFUCTNVOQKnFuIJQiSYsSk+LFvXp1aKky3fbcizZIRi3zEwSnoc/AHc8qk2Im1LVlVybY7iyiR2cj7/DG1qbgzLyYkoGnGEzs1UJHlVONUrGRc6HfPW7//79P3AkP1o0IK3hVG7sCdisPzmgMjzlky50nIoZsODRtqyLrwaFIlW5wpalykVqWeGpcy0rgq6hyGsPNdb+SenfYv2V6wA4mRRq43kIX3OWbdYJ+9IGjweHkY0UHVuRHDnsMkjWFBTDbamFZJiXVMEzWigtCam53bGqrElaPrMz1lRokrUJXOehzExBTUUzLjk3AA//pyBBlfAAACHg/cSSEbiD/kqxA9IjwIhNdix4xOgRMSLiiRiaQAMHZ5KutRLiDVsQ1DU92hK57YY37MUooy6kPAErYzYRHCi3Xplra3E3oCoiWCY4i56RQe5SiNNDPYlGgAnZWqxn9kUyxqIf4bEAu1GbcqIXdNAb/2oImp/9caQzBKZHuqCAzqSjtqjs2q64NxMzCjDyDM+SYEwkOWqRLJ+wUff2L9fMQAUFqs5YDeENgNywbkW+EzSjUgrxtQxSsESqzTCK2vdTyOz7S+6esxpXlfiXZtOq670tzqPnB1S/1lY21zko9LzPK/uACnNFOJKW4mSgIwpyUtkdd+JxLl2XQBJ4/7d5WCPfbN0rTchVDAINiZzqmXOcbV3vcTbcr7qMpNpAiDFiwAIEXQ1y1pNMQU1FAAAAD/+nAEowMADMIXO9eLDClwQ4R7Ej0iZgigfWZFhFLhEgosjMWNEANxlkUdkedTEMmD1zVhrsvbHePt2BCH+m9feRkZAViUYrE3ZCpd9F+3SZLlR+907t9NVslrVNui7WjcpZ//0/p6N1EDN3K8VrCDynX01XFtHu0ZSDL62oa+juVPjyqabBNu0ZZEprblvK1HGBnCLDYd3xVzFIZOC2uB23gSerT7/6tr7/7dAmq3Cy4nAYkxYRVm6ssVJS+zmCPzSPkaUoZFxt4J+SDQNPhIJA6Yx1jFPWlyy0i5zH7S6HrqWLt6zQfjzFCLGPQoOU9VAJSaktYhurAYtC112KnCGW+HW5V6/SPcRnpPBDTKRVoacp6lgOq1Zi4mgQucAceupTlH37KHot0ihfk/YlwoVi8g+RfSmIKa//pyBNJyAADCHxPYEekrkENiCwI9gzoIcK1rRARTYQoM6sD0mcgBq/kevz0J7ZEtSgvPld6h6H6rJV1jg9Z3eGM09Y3AyBCu49PZUJKGAEmowhKsaLF4iT96VGWMc2KYTU/FVFndGl1HHAd3mG9qQBbWQq1o8evUVxZoxtk93Ezk5EkG1EFmodcmTMlB18HCQNCVw1DYSeoJuJnmjiA8WHJRbRUh199bVIk2dPrABDBJtROWHEVwtY1jrTEtxBxveoy+uQYTKQl2JUpMAKIXeMEkcDVi7nyg2rN3MV72Q7EPKv2talFjVqRScdYtqKtkWXksE6IEGkbahFEIK+y7B3wrYGuWm7Z9YE9BW07MCPDk3jkcvqHsQfMxYIHDyyKBEAgMeEkOgS22tlrzmG73////+pMQU1FAAAD/+nAEGBoACAIZE9m4ZhKYQsNLmQyiUYgoWVYkvSGBDgesqMSI3AAkFpJywVUHyFbWiQ6PH7ehmgkwQ26XhqnnAwZe+4jueSAhhZZNQZtny1oA6sKQCxfS246KDcNIegUHGpluUfmanscAEsq1S3sZPQTCOqFvUS6VzQnRrSro3ezsjOLC88uUjSaDx0WD7Fih46pZs+XIKSZuAinkaP6tyGuF1KU0znUSOnRSDhcfJMJuEomYFEKtxfQHhFvlyT4h9RcVKiWhMe3AeDm/TNZScfqSyxRIo8A8JuDKVnmy0aOjzrFqHiy5GRR2a/QAAEABJScuvlqEmOBowGkWaq/2RMqS0HDM0EdnJJxEXQ5T18K4FQQeS++Lw+skm2lFRuEnC++vxpKPKpQLUBO7dSSocmIKaimZccm4//pyBIjQAADR9RHZuSUTmEJCOxM8o0kIoOFcZ6BOgRoW7Aj0iSQEJBablycVAcDPrSFnJlD+cMknMrQvghPHAiqo5rBlizL7oBe67KhZ3UoVL0PFn1el0Y9Dr2ajyLGrVcvs/0ghJlSywUMZEJhHsqB8umQQH4Ai1+DQntYxlRccZNiF9jTorrcZKlFuY5CGRZGMPitqVsdT9tXXFoWHZ9w0Vv3/3dAATbtErg0ELCyh2sItPPQjFryHEqe4CFxyCjckH8UdZ3W+Z3W7slW3fWy/BoXLQ3rQvy9e6d+PjppL1Xhh7HJUtHr6SVkC5VCErY84bGZAYkrWgc70Fp/PiZimB9CSA0Y0PezVR9zf0etNMPB1W26PmDLUKkih7bGnrdQ5iWi9X0i4heLbYRzIt9VVSYgpqKAAAAD/+nAEO3EABAIcK1jRiRJAQCNLigTCD4goV1xnpK5BFg1uqBMIZgAgABUl//xw/EYRHcqHdoaNMezSG95/mOWG0IuI0Z8H4rl6H76pquoM7unTwyj4zMuYwqTi15wgLwg51A9P7abmp7awAl2JTSSUpPTOLwH8QyvwV0L1OsEkyDaCeS1dJ2xmF3reUY55JJtj9QY2w2ZSbg4wowa5JKxbejvpW5/T2x7siAk5NtI9fDNJa3YgnRX620bqcha5UTEBay47CgVXQa9zNjYDKzyPPrcfCgVvsHjx99V/OzUkutahA9LbLKevfY/UAHNaLiacpG0ixeq888N4ECfaadkQIIvDvm/UL2FSoamR7bB8ffGzUvqejOHWo6iCzokhBqqSpMyGhQMFT0fIG3Pt19+lCYgpqKZlxybg//pyBA7nAAHCERXc0EERLEKiSvc8JXAIDJVzNDOAMRQcbE6ecAJBMFim005UsOLcF0+gX1Rq8EfmewdsGcFQcIBp77StcLqf0Oa9D1GrEsolY0wuPOKFEpr0lR91yBHQODripm3jn8qAQASW7+SOWi2P9nN06hJlVH57rNpVCmUdjBWEaxh+JP0VqXThEmQUJ0K3qcVyKhHagZUYU666SWKMe9PSmUo2Rn+hANHZvjVQxTXW5eZPnBADsn6Fz2PHrzeralj8/qZzk01UeU1yjr7ShGI6nL9shIWPS/sUzuNLOAI/pPFzFjyzdKBTbkEBmX2suzQrFodutZSbhNmRuLpHUJ18Pl3kM/2uhj5UzYhzvzR0orSTPVOfzex65xuf9WrqU3WzbRWWhStjfEa1dqYgpqKZlxybgAD/+nAEWgAAAAIUDlkePSAAQmAMCsGIAIjda454EoAJG65yzwBQAwQhJXagIBT4H7gUTGXiFZRRnjPFTLmnbIpgQDq7xDVn3CYeBYGEABIDsMNU65HkAOyy0H/e8ab0GGjU///vT//0vsAABru/cls+v2/wHEJZB5gZGtPOcNWolGtAjS9WQeoZhifFHUwIwGYsxu8cDIfIHBjGuL1Ipb3jekzJvT//y5qyYE+A9Hw/H4/F1//////9G//nFFOuriUv5A+LziAoKlDo0WMn8XRuwmLiJWZzjmD//Z7oQeQjkdxsgejTU/+z/I1X5lGowb4SlgEQsFotFo////6N//cUV/p/kA4fcXEAIKlDo0WMn8HRuUPi4iVmFjjmD//Z50IPECOQePGyAaJmp/8j8khKvzKNIUN0FRko//pwBHTnAAACBg/gfwSgAEKhi73iiAAIzNlvgKRBQRibLzRQilhAAxkyeH9iSaTgmWjmA1hbVQIC/qoIsJH1Pc2oqGyHSVcGyLzz+JhZ7anxc9EiTp0JtMiTT+oXdZJp/dg0rdlkrIAFQtMZABAMjCIQd2G3Ya2r9EDgHOIBORQX2CV7rrw0VHB8snrCpFdhGsTVnAKVKi6B5zHfipKpa3v+jLHjyyHPPaAA+giuQACplgM2nikWqTPdHbqbtwPgrb7n5yko/L9EFpcuTdenTFGd1t5n78Gznjg7odF3dKwwJHNPAIDP8GY2JRDaHdesgFZrZLNstt2cwFqzMaNHbtaqh5vj5e7Y1mak+X6ILJcuTdW0/Ml/5n78Gznjg6qYdF3IpSsMCQY0OgIHjXgzGxKIbQ7r1piCmv/6cgTkPQAAAhhA2dnmEWBBaBs5GaIYCO0Bc6QMSOEMIC20k4kUAAHAaFA4rGVoiVxfBtnSw3dNVfwWjkNqbmJ3/qt2df6VE1K226dHvQ/BrrzU+cC/m6Vb4NtG3tiX8SnX0EQHfXelqQAwEiUAWkKGAWzIrUP1CdLMT6S0G1Bc5O/flLdjLobmRRKAylNbdOZ5teDX839RX9Oiq3wbfbfxL+JTr6CPrvS1JAAZHjm0hMViacII3Ly6N6WkTA2YE3GZ4zYIUuXhKEd3/fUmxiTFvzunlb/+bwa//vnodQUsXGPCMg10rd90qknvqFjp05BkAAIvhBykhF1bBGBjx1fVqMUYMFQzXoNoJ5eEH1a1Mk2XoatHOtqb9/epKbe3N4Nv6e/8EtK9YJl2ucVu/rk/UkXsrTEFNRQA//pwBEqxAAACGUBXkekpcEJoCvY9hSYIsQF/oaRKMRemrTSSiPQYGmwqnpNCM2YNghGfLhyVCkTZv23ebq5Qzz6DG4p1vZK/8mh1p/1pIVKpSln18IjE6VV9b8yuNShvRjJb+/KnfofNAlCZSZFUrS+CasOPGO2lONvjx5nWNDkwYkKFu74x+3NPK/7E6aHZlW7L+i21b/rTGtSvRv18b+wpvMlNSVVa22889gIxmJySJlp0czPhttjbsvRu1eCH1V3UDGo62mspj1t67FVDnqnr0epjHQGtun/E/06uc5kogkekRviMSqQuMf+doUjR6gAAAKGVGkEnbR+YvSWmZoTBcYy7qWNXChfguK8l1P0d6+2gDZWZTI11v7YpkZaX/+N7Z/sqNy/r+SqfZDAn1KeLErqPljSYgv/6cgSjNwAAwhE1WrjHEbBDZCqwPSJ0CLDXaOSYoyEFGyuI9hyYhMDrbrq9wMvDnEWWiG9Bh9o0nBD9u3VqHH0Kj05vOyOb30PM6NqmrdUZfVIJhFZIGYhlpn9qS7migU1vaSerBWn3plPp5aGMxLqASyPHyFOgcZU6CrfkSbTJ/2Hg6ahWxPic76N0/Zx6Yq9pt9wqLzMeQeqoTMSKuAyKrv1FegDP+jWCvrADBIm2pbDI2CFFYZ74lcQ4vOjRLjA14wX5uvE3Sxkof2ag+5S33trmqqD2qj+j5tqBoViNrgEwDqazbtH7Zvt1+5U5U2wGWva3RtE5HhLrg7Knrg7H0DAzaPoMQO5QNaH8v/V3s7bq+qMqTqOztdv51Ep/3vbQvb/FfVRER25L/XA3R8mdepMQU1FAAAAA//pwBMNuAAACDSvZMYMTmEMGuwox4jQIyHFroaxDYRSgbyhwj14EK/c1Li8gBTekRzLWMR7j5vTrR8O+Yfj8J04OtXfJe9KkVjOtvTuaecGPAzHkw88O0i1BDTqRt1XWPaRd2zeBv2gAAGA03JJpYTiMHtd0XV1N2C/lH1Mh4GsxgXNwnTUGmq8n516Hdf1N7Xstm9d99RDZYX12BJrE9YeOuvej87cBv+xIAAAAEJmjkmiigodV2EBdalRuLuJQeR2hyapwf9TuY9ni3JafC7Dqc9YUFDq0bBgVfZoRnJK6MbkShQrW795qJTlrMTjV3IJU8422m5Z2L6CifFTaHdleRfltegR89NSVF965VxaD4T8pbOb5UQkVQ79ffp1XBZ1Dcq0IxQOhpLHNACVoVBRexx1vF0xBTf/6cgTxKgAIgh8tV5npElBB41sGJeU3CKCVYuSUUiEMDSxoZ4hsAcckkbpVQtEso/iDveG6eLJ2QjnyjaQdoiz9Mc43BNrwZtxBKs7UPTVbuib7dvswMBqqaSdJ1V1HVnFjPTAttbv/d9YAYtqtYqKAHAbUsC/hRtTrHy/UbUPFIS8E2HNjON40fuO0IVEhlcdujI9YlfUAwG+RKwqh888W9ur3yPGoo5vdmKQsJttO0aVFhKkqoABVKxt0qMM/2oo/l34YzAgPh21fUR3bRkz9ej1BNEKnrWgeqsAGNQCaQM3KPW7rfXwnqorxpF0lrlQwIabbcZIaCFbrExHMsTKTdb7petNBw7cbQL3bBvgxDIwuvfOirELipCskGiosyZfIQoQXOZ6lXdiWPc5Y7vRKbiJxMQU1FAAA//pwBG7JAAgCDzzXmesRcEPGu0ogwpCIgNdpIqRQsRcNqtz5HNACRubbLSm0EOt+ioDZ47Qt2wT39wiVOzVf1BxxXBvq+oV0UP1atKGIqTEcyN0637bf7d86//h1wOSyPee129yHazQAIZINtyS5x8gHaUAGaaDK/JMRfJ2Sv8wJ5uD69QOilfb30S1CZNpuv1zfJOJK18ZrCD0yGr9akrdazMyHXIZ95QtuQA2tK2pyAUBlUwC3jXxYW5rd0he7LCtFgfQzuw+DTiPfV/0tOQt39WRvTav1ZXTuoyK87fDKf6O4NthzhJKh5p1yN+hgBAWU24F9HmUaYI0xnzUCEyXcRE2y4Qaj5UWsOBvEwJnPN1IhvHRZqOVcwlEZ/mpYwbrrGDnmNocSnJVPukaFL+7ceR+5SYgpoP/6cgQBSQAAAgQr15nnE6BBhXsDPWUmCPzlb0SIrTEYnrB0EojWIMVu3lcFlnB0PG98PqPaq7c9ywXXIlS04vwmdojx+vP0fY3Noojvr/2KU4V7zkzMuI9VRxBAqFE6dU1Z/Ut3RpQelv/l04MgNWKVuBR7KF6/IvIovQfwa8Z0H8urtjHypZEKixzo7nt527ajRi3QhdSrvuCrFm0J2VXpQ8Q9NSia7AAkeJbbSd+TSBPY6jxmR1/iG7gxnV+J0DNnfm4TPmfdu7UtU6u5XPWXpseqsK83R3abVPH2Vuvf6d3fbQpJJRtTnB+w9cqgJpBqO6xua2XekIezywt+MrxQMAgn7czZn6cH07nahmawnVj78jaeDfl5kaj3pWnv+YO55p23QUokxKhskcJgsONgYeMfrStMQU1F//pwBDqGAAACF1FWuww5QEMF240VBWOImWFvRaBMsQko7SjyiXoEgCJlv9BLX+YyCRQWcD+lIp+yGo33C2W50w0hvwT5ctkU67K9Wfp19+Zp9+f5qJJdVt1r/U7+vsn5v+j//+W1qduQAACVCUo00lbOUBqOVnhb9gulQYpfqPfCLZScIynCIuJFDzPZG19cyN/9WoNI9g9OkrkLKYq+yS+v2dXu7BwZOojv0gBJ6C2km7E0kGtKWlMOMV/i1TdArMPr6tmG0Jzjrd+v0qndqL5Ovt267fTk3jd9jvVS/z+up7fr0fQvf2ZSDodNWJR4oAGKhEbkm2K+gvawsSVk4XxECc6j6+KcT9ulSi4aqlatevVKV19G/xtC9fTp/t11X/9X/7ZpnHehHuEE5VAMoNrM1JiCmooAAP/6cgQiEwAAAhI9XFUsoAxC5yvtpBQBiNmBf1gxABkOGGxDHlAAgCn4lxNthAunDsKSvbZIrmyjDkIyBmbGg3G9G0XiA+ltE6s2C9tPIXpqJto/I+dlYjS1WowFoRNBxpfup/fffaoEUQk3K3HbLJdDzB/e3LhOZUsLmw4zVfi94cF9B2g3hQ9kS+nP7dnr6LqmdRzOgtyJZW0a/nitkQy/naaECbfz1qSFFYACq1X9btWy2f7AkTD2X8tLYcbV9aKz2RNU6YJ70/kW+qel3969nRdbNYiv3pey3pa932fpddL/o1PdGazf////yCHwjtfg7BMRRF28SKDdXtUGGgJk++QqjUGuo4RFhgTYTFRxkYykYogjOn2/Iv/5x7OdP/8go5BQT/94HQZ/+USFjBR3//MZvcmIKaig//pwBEf6AAACGCveBzBAAEQle/bkiAAIiFmHpARJIRKNMLSQjeSg4AQHQczh8xPlj7RdHi++4gUNWaVUq196zKzze0cv+a6LWh0tS9klKgpZ1yxYOkQLTis1gIom35pzi37mq8nfpdZtADRLVQIAQJCNSarUH8qIj33EVKzOVZaGUtWoZWT9kdevmvPWjp97bFQUu5YsHQ6BacVeawEUTb805xa33NV2Bu/S6zaAGkardrG0lGAwQeKvMYNiTb1Exdf57PRR3R6gJTYNOlQCQIk7ayIlY2hUXfPTMasc3FPesGSqUuLnBlXQql+S2iz2RKAAkbNtrEik5AoVuWe6lHzTPPVizVyzNXmzWRG0UffA3UuYNOioBIEfWRErOqfffM1rU18U63rBkqlIRLnHPV0DKejt6ExBTf/6cgRa3AAAAiQ14GmFEOhCxrxtJCJ5iHTZasw8ocEDGuzVkYmYAAEDl8yjaKvBV8a3YVqeF5Uw10ehoRf1m0U3qanX7rB5u9HXq1tRlqyN0Rkr4kqWQqu/Z9rqmrMIf4bFcsGxwBdQxlCgkjFbK42kSXybW7zbh4z42e03hoDx56ls35r1NEdfutc3ejr1a2oy1v6I1/iSqkKrv2Wdrm2rMIf5MVyxNQBdQxlCgAqStQwob6fjG+mU7Uo4ttNBMHLUut9o0kXlm5bV9lHN4Ldub2uHnESl9H+320LvorUfoLflg7gJf1UHqvZK/Cv9bhoCBHGECOV3G5y2kp7jLdZ8fksIwzQaniyooC7tDlpR8uVvoDU2b6uGdSt6P9vtoX9Wt6D/g0/PN/q2dVp37ftTQmIKaimZccm4//pwBCkwAAwCAUBZky8QcEAGy0lkwmII7NdiTD1BgSEgLej3iDBT87FOCYQ3UN768MeUHDU/Hk4Zs476b9gk0qNK705xkZ+guDYlv6vVU2W/J7m2K+vTp6P/+mz7iff767/+T+v1gADAR/fjFRWJBrgw5rskjNjuFKODrfBNmLBrsDVELeXgqzs5H8XBW1f9tfX7cjzFXt76N8f/TR+K+u/3en4e9YHT+C1BvR1EBKa9zDH9amTiBgw5Zr4JOrEzLdD4u7NyVGv0rI+mQrV2OV0rbt2oil1t6ucb28v1boihzq3D04H03KT0d9R6kAMgkhdbfw3gKKqa3WPCVW4WOoRtzRXmJ4KsVwXt/6j19GRl5+mRr0Lorc/99XQWnUfT1b+74Z3xChukX1Cy5qKLJ2MrOmWLrTEFNP/6cgR2rwAAAhwr42imFoxBxBuaPWUNCJBlaGew6IEVDu1o+BUYBRNDUuk8rko3UmrWLTbvxHkixkYhQcdoyOnU8IGJ5wfNtrNlV3J27084RQyJVsLsXlKPMTiCSfF13pQn2wLse75yRqADMFp5ySYNmFSo+REl/DMKAlR5PH8by8T6cjaNhZIxDUZGddRRVTXX2jSi0bBQmLRaiuqwUQS663M0SXdPd7t9hgMSyW1lDztWmr5KEdi6EyBM1xvKjGYFmuczUbKjFi7mu/kCzpeiGmECKXIYUcZbdHgw5rlOU9Olu37EtKF8rr79YaleaABMAMay3ZxHj6DVzRrgVafCUO4ok0XsiGwVzcnjiMLhjKnUdXzD3gkVCJd4193Psc9qyBRFFa1Mt12/Yf1NJdmdtEp36UxBTUUA//pwBLhyAACCGCFa0W8qkEJCqwI/K0IIVFVvRLCoURIZrSjHlUAAIAQnpbthYHp7xheUFt7ksSFz76LQfsE9lQ9WSNbEzKnwDoXlNaszC49tFVbioZhHEgwi5RO+2y5ntooqfd3fKftqAp/xIBLleZgTqljK0uE+puXDMA8BjBO6A+6XCHNQ13n6jTq8irNRaUKSz1wOsY9BotTW31DCzpvbjq/7VJ6f6vo6AAAmJEbckaUcoDP5xv/CkDh6K5x2wCOiqY0vLxtGpeFkl4NHWRFXXF2VRQY4KbLukyFECjwvXQeXE6Y0zWUtspCv1AGAI5d+rMC0NBQ6GhJt+ui3AlBYA6qEuEthJGlfG9f6kqjal1Htp6b9ev3Tb0GLFkrDm7F4GQRXtY0u5NNRb1XKWe29SYgpqKAAAP/6cgRBewAIQhINW7ElEwxDRruaJWIuiIDJdUKEWLEMmu7cV5VGBOpKNlVMOGP6HoygeE2QZ6CGRh84OYSJTpA5KzconDVqESlUY+jHDBB1O9toy6yguIC7Swo4vvTHpoLNEaMgaWp6gABbLEjltVx4w6p31wdCsIvSvo14kp6tG7guQdZB6G9u9VGapF2+5m5E0//6YZOo0hVnkDxp1ESaClygpKvjn7Ob/pBYItMpxCGChur4xwFeO4vRy1mKSvUtBiovkD8wUa7P5N56A9KPtkMymGiTcr7z8KLfbPJPIEURK7Fhk46XGzeVUOWqkUIxpJxEhQvK5tLlW4oCti3NqErz9KxhjsnGCtUfcczqWp1fLX0Pdvp+nRiEEPHLIKV0001yya1x1DVmXpR5P5tSampiCmooAAAA//pwBB2MAACCETFYGfg6kEAGeuJlB3IIoHtvRATwkRSZ7J2DidoAONwCQym89Ae8OjXJfMrLhhlTJx7DwDGKC5j3iLIoDxzJhna5/QHc4odqzyrPa9Xoqal2sZtW3vuVcJ66v3+t90yBl4ZTj/raJhrsqhunyv06/yD1S+PMvnqcNPoaNxFkoABzULLP28TBuPMT1Nor1ZTt/79q2f9+uePtB+aR6f6FgEAiEm5JtrjA340b1JgBw05C9B/ZN8RYljGNd/KhEqofq2Vx163DXucPDT73F1izo37FMstt30JJE1hwnZmDT2FaP2EVlBBSbkoXfsTTV6Ca29vPmZwcSRDqxa8xjvNJtQH14absstSehpaavr/K9rak//5N0VXEPBadUd5FTEt7NEOUEvgH2EKq0xBTUUAAAP/6cASF4AAEAgsy2Jn4OiBAxmrzYQJ2CJTPYuwoTREQmfA0Y5ZOCETu4bKSrsJvFWFG03yWhYdPg/tpDgkQo5BmkLzRiwVGO9inhLnmZrY6+a/fXq36M+/bs0cbUs9PW3zUjoPx3/1ABpygSmXPxPER60cgBvP+w1FXhOqdU9z7qRODROA9W5AVXyBtH04Jo5y7O0K+t63q+pe2lt+2UmQW+4t9Mvf0yIIIRSck1Ds66avu15Qtz9XJQOCpobDuBfOMHqZ8yGbYVxtbDH6Csh8yYJsnT+vt016NkdqC9933SE/bdbY8tR2fSQT/oRJSDkbjrjl33HS/Ze63AlwTz7TuYC/fS6QjBTTs0/ChexHua0IPo70/q3rSlejaNsPebnPnLrbZ2dQPFkkTzbOfVZTQmIKaimZccm7/+nIEd6kAAAH9M1m5bCm0QabrAzMHNgkVb29EsEcZHi3u6DOJfiABBJN2prULV6VFhF+8EQAZ7c0clAB1YN5rUfERVo25wr13bGru2nRu+r9v/T3ZFEpT+Xrq6vDSoBDHW4xG03hhK7cQxqBp2JewrHjlNPcSob6nf6jsalw/U5Xl3mAaL6ifcpzgbc7q+c2nT31f9LfT0c4qj6LQqxvAdfVVVdUr+4AAJAi3Jd02dLu9jRb38fCWPN6bF3pQDty8G2u5+orB9uDT9G/UftpXbR+bOVtf/+V7WzN+rabfNq3Xpof0lYjKRLhnc1zuKzACRVhKJJzS2jy0VlXChii3mcacW2jnYZMm++oTTCqzEM8P1ANG76v2/+/R5hPX//s+To39mo1t+fbn2bn7paRogZIaLIi5U0mIIP/6cATvAgAIAiAb1hsseyBBg9t3DYJQiMDNZvTygBEZmaxenqACAALkAqyaCF8vL95hwuXGblCr4cOalgWXr5O/XC7T9LYd8ubn6sFKU3N7zT4tyk1PBZ+p69ynXrZe4tdw2HkQTv+R5KkFYRjku4KGDY1oEdfweIiDGz7B+PPFtghT0urder4J8G+hTlnl++66TEgc5L6bKJOv0lljeTlNwBMlgweGilQL3uOAW3JI2M2284NUU46tVb5FfH3DjTlZWBlVgKc0AQd7tr04wA3Kz8fjeQuILx2rWvqib9+jsgDnCzB59efm+5b7/S2T7f6AAgBJKSRm2c74j2lkn9t7eWMJhgj6qMmU8VtBNPHyASk7ybke6eTzuyK8q+i5Q96ty39P78zkblf1DlihQ6tl9lyW06G/0Jj/+nIEru0AAAH/TdyOJKAEREnrksYUAAi0sXg88QABHZXxN4wwBAACZELyZw4whA422aElNxFqrim4xlGMcSH900ZdE6rnc9P/5CNX//cONIhP//xd/////h88lgO8H//+Bw+IDNyAAAAYdUVF/pGq/2XGmRI0+49UFHZxBoy4kLWmZLVzN25xc7J//IRq//7hxpLf//i7/////A54oRgO8XP//6QfAgIHyiHURYatNoxNq17CmhVbn0XV5pgpd3Uxd0rRpqtdfaj/5UPdEqyN7fUKJuKwo+FHqbLsMhJrzxkZf0Fi6Z4svpXVw6720awAAQE47G0knzwGmgucrGdrz/M6XsdZS2PvMm6UO9sXQf//1j5yFShf1f/qiUTqiMsRiPHIHiqUzTE+4895ae+2zLGlN17nSaYggP/6cATdZAAIghhMXSnmEbA84RuAYSsWCGTZbqekQ0ETGu4k9ggwAMl6vA3lyiONnyHBmC6snnwNUuqdOklBui1fVvq7/vuUzmNoxW92ZHdv6btTHptk3qT0Z2t26KmndnhBd/yOkdZ7qX/Ea0wqIOPFRNJIvWLy/NTokiSKSpQscVW8SuW8m+lLwKHQNIBrDww2N7LmJVlLxJoQijVZfFSbfvzf6N6mDYQ0DjZCI0ICx9U5eineWXuGoHL17sLhxHPn1dqsv/KJeQ2Wj/e2g+xUr2b9hrb+x8V9MVLT2fI+JTgM1hlyjvVpAcBFaehGB3PTyPou02mNaN5VPYvUaZT8FWo3N+rVXT+UTk5aP97aD5SpXs37DLpVqTU33izTxbLljS9YKFjsSGkiH2ZtMQU1FMy45NwAAAD/+nIEbqcAAIIjGVmLD0BgQUMraWGFDghU13knpEFxCRrvKPSIegvHOAt6EmUm1MQKg16E1qyFBbthtrT1DmfDdSErkYSx83OTXh5kWo6jxKRUBU+8cdxKAj44Wf9G0C/qerFVdyyrv+3xoABKgW/5AoOOemUSazn1HyLAsDKrsXVPfkDOTrEI0dquvEh9q/DpJhFQFT7xx3EqT8Wf8rtAv44rsFVesr9qO3xoAKFjXKwLgeSSz8lF9CeUSPKmy0OegMHwlaCmq+TBAJKh9dfRdS1dHdDkF2a5jHUp/ypk0gpC5H70z1Kq2Z7/7vu6ztBMckkA2EmgPp14TpesA7VDt3RSVkSPdeSfJq/bBpr6rVbrff9UfZjPNbL69k4IcVXuR7yM9IqDi6nnvo+67KxuhMQU1FMy45NwAP/6cATJ8wAAAgw12csvKHBAqxytFEV/iIjXg0KEb7EXFvF0cI5uAAAIC//MGZK98URr0RZ0tTWaUJ+dM/fqj0U2kUZmEqsB2ejtjs3Azader59SHsnVuTuO/6l/oDj9Wb7f9mj6ldDgk2lrPJLbLcJaPW98KYw8GwJs1DgkLFjDfp+GXbRevXs+njdrIxP/9PH/1/RH2CLt77Yxb/2S/6+b9VHN4iD1lBCLNIqONuCYgrPGlaGXGbCuPrrMMr9/MDxbXB86EPzLwYnyLjBYaxc1sStn36GvVOlAPcsj6AmqACI1D7HfiA7froa5QSKCj0LdlklUx2Lqqva4WGKDGK80RX1zikuPDw8WEDOB4tkzUONdesQtFFUeQQEEoYJlpcFVLes4dIt+TauNp+FGZQ6vySYgpqKAAAD/+nIEBbEAAIIENd7Q4RY8Qya7qiTCYohw13kjFE7xEBXuHMEd4hAROCLSSUNCGSLyrsg3uP5nMqYR0ftNsuVHM9TbwqIc4+XvrDZlWAutK+qdRhssvMZan1765vZargBg0VypifFAABmBMcjliCvef6a9UvciMI+L+hC6EwS9q0bk/U+uCFvu7IzL9e3RdQmhHNTYzVddRCGwaU7/9i9gi9t+CjL9oHhkgEAfKu9XCWzU5s8c/Q2EksWHVF+/dwaYnLkbEc2M1nI0pXevTp4tpHOWS6larWd93AQOyuuQVZsuW3YaMp2buRfX2McEBNSSVIJTDr2avRafa4Fprmo8Fs+d+r6F5/wsUmF+QdJnNd62p06uyXKIGrSqZfHrwPULVM/ezhwb9V1hEd2DQSY9FyYgpqKZlxybgP/6cAShAgAAghAc2RnoOyBCY5tnMeJUiLFhe0GUTJEMmq4oZgkaAKMttP5ZRyj3ZJK5KsI6IKoEiiZCcA75IpVSHCzB05mqdnZQVn461ygxNVVW6+K69IHbAyDlu5zFo+rhekz/xz/sAMAEW5IuPVL7+sK0CRtzos64hyY0xzTh1s3M5IIrafhS65wylGVPVVU69sBV9emY9ZwUBN2jWeY5Zoul+yx+p6/9AYAvBpy671TQXyFFhUrnzYiCpRet6nbF3Jx307tq2nN06F5rA7f2lN61EizIZt10ej/X/+9/1d0RtP6Ja9hE8QQUiSpyFBKCU5JaBm5i3AklUvx0TVc+a/pdvhsx+Vz1Zt8nDPpkVtW0/ofBF5qKmhlPbVv4NzM98t+5IxpETCoVkG7G5lLGoiBMQU1FAAD/+nIEFU0AAAIKKVtRhhKkPiVrVxmCGoj1OXNDFExxFqXuKDSUZgQAgACTkloh7C99kjPjJo9DOsPM3krak4NySH0NpqXpi1bVu/6PoPoJr3cyOehi3ccL1O7teyyj7sUDdLVy1C3ESAAApxyYE2LeYE5It0nQaejfWm8I9W1JwTgFLZ8FwuqZ1bVv0fvx+J2Ckl03QIzB1zT1/d/2JYNbQ6/ybv1rgBKAEUk4VjHD4eE3M66yV2IC8bQPwj6eN316YpBHG6d+I08f3wnbnegTmd1jdu9L6K+f/2pKoyUGZQpKG3TSUDImepYt1AVIQJAAiilGUaqpHGGrFqPh4XsDNJoGaLxkf+TM+5tNIZx2nvo3JzaeTrqz/oL6t/9Myo3t/bSrJGBkucmMUbiEO7jy2SFaYgpqKAAAAP/6cASUTwAAAiI/3FElE8w9ZStaGYUaiN07ZuYMrpEaLG9oMolfSAAUEIIpSnzQoJ3MXwzci/pUxvQy6iHNWJnqNA91fG6bIVddF/n6cK+CfT+r//L/u7K9wBt6d74u7WdF3/gU+vDyLIkAAAgAJNySwSzXNaRG+Fts2ExViVjIyhATbJxjFt1Pq+MbR7kLz6f0H6eNHlPk6s9d0uJpOO9LWzDr1rT/WAYAlNuSB7lRE1YXy1gz0iOR6Whi1pghy+DlJGSw/Gi2otGAOarj9R/JzduXjWnR2aXQ2ua+jdO/9PfGdf/+i81kFmhuzsk1SAfKKcRmueN1TWVgrds+QN1DMjIMah3lJrwb9+P06clYC6QfYfDahn13/7f/T+n//8yzqZUMBM9j6VlbCalH9fk3BtlZ5kxBTUX/+nIE9VEAAAIEKV7QYivMQemLEz1ldgj1aXlClE3xHSwuKJQVioAApCSbTeBNoNxRHMm9LUNR/Lz1gh2sL11Bm1Syk5eXZtAG1EXqM6q3amqfOdrj01+rTRRLfStAMidDnPYwioFQASrt5WlmahqQCNnKZGEJqsjmMSU0FfydXz739GO6QXbOUHeQNarYUmryKmhev9OXp///voft361m5m///vx1FP1AABUCUmnMhJh2wLa43cfMBaR2glyuaJNjE7VHNQ7cM0fvp4Xm4vmT9W/v30b/0Lwt7//2VnzEFqjGg1araeUrRT2ESDiEwisAEIgSVLdk5SOHuH9PSN/rnsa2wKG0xgylnxi4x9eNbX27//Xm4s2nTn603rfRtXSpuW8Itv/9dUeokXRnjbVtr7XHvKUfUlMQQP/6cARTDgAAAd5MXtCjEMRCSYuaGKJ1iQEHc0MYoVEfG6xM85WiQFGoNSW7+OhAbsE72IccegFxfD1p1AcMHwnL10QSlH/+btoI6df99PfTr/vsObJy//BPg2h5Ymt+TkOgEEVwCSincSYCGuakj2fgatHTh5qnwmF7AXcuidulLdu/8/N24np/T+nvp1/o+z9O3/eG1Bot7xMptWoTSBwjRH0LgABoEuW7exfKVwM4f9dQNkqVPUoE1CE9WeVdTb6Duuwm2R//j9CtOD6Nxnf9D83OTbr0Pc58T0xFxDU8XSBqXm+WDwjVl6uMABSbkatZKhV00WKN4eVkNtxUGNKgTNwxsPcBLnSxWzLThA9BrYnSR/1Nq+MVq9G6d+vP0bD9FeeXW6/1HR6BU6k9fP9El3+hMQU1FAD/+nIE6I8AAIIdL13oJigoQkTrqi0iZIiUj3rDBEkBBBIvZFMM0EGAQFYi5u3IT4q2NWG6FC8rVbIBcg7gO5d9W10A/Jy9OnRj6YxdWJkZXOk48gH/t5d5d+jLh8MYPn9b/lC6gxWfD/9KAB2hFSNtuqlF4UPL/DRWfUG2qslfaDIbVo676ifzv+z6f0Y/wTxIxg03D5AmlXby58u/5cPhjB8P6/ylQIVnw//SQwAAra9j2ZY9BYGRJ5IboBoIDmAOk+ewcQuer6n+dXqc7hDAfcUBB4IAgGD6OH6jmUTwgUAH/nBOD7l8/8wcDxQvd1jyAAAKl0nCqaGNho+RH5Cc+AbEBsEs5uf5BxF9+v9f/3r9dzCEAzUEDhQEAQG3Zd8pzj8owMHP/WD4ety/4ATZ+TTEFNRTMuOTcP/6cARKegAGYhoa3wgsGDBBg1vlBSMGCLhTeKGwYIEDjG7AOBgAAsqGFb7hkX3HBtAIszVQZBa55BXeeJiziVVPdVXxg3yIceKA8HEUUOIkUpAK7HUCEucGEugq79j3K2Modb2Hn9VxmsEAHxnQN6YFSrDi4GH2xKuqgzPUK4sy1hThVV+hQZeJM65B7AeDiKKDJEimYXZoK3KbFKCrkdlj3DH7N32Mf1XOrBOo49gBNmCY5odWgMPecaKgYzFDxMXJ07i0hMLDhrOJ0JHJUVF5Qxl3HnoEaOPstLjk32Vmk0w9aUBYhn9Rt6TbFpspZpCssFIIGELoSrMOS28v42Yl0iVZGL8bOvl6lM23KCPicYHwolQFF6kY9wVWVLo67LVqTffWhNPaUCXbzab2U2bNKYgpqKAAAAD/+nIEiqgACoHsLV2pgxLwQKH7xQUjCgkQgXKkjE2BIYPu2GEYQAEAjRkHQwif51FXPFQCNerPpWLJa32dfKrzNTNiXR+ZW8tnp0r2tqUFNFNa1sotnW6ZPff7tBN0qmint67gEJvYy0gHos5jctOS7usu/T2glIwDFQPDYo8PmmkajuGwXUPFC60T5Jibu1W6krYA48u+Zhmtaz3WaQ069X1+1DbyeuOqAuNkrVOutMw3++aA0wtUQeyuV0cl8XBTtG56yhTrUphkrHlYo8mw8NbLGEBOcS2mkBhVK7BiptbUEalJTSZe/3PskVsFRa3mQaBgZHKp/QwQsKHAASTMz62goLmYsIhZVoKvFoxFqSTVMKkBFbhyAZS87fAoPBt6FAFh4dXGPaxj3qQnz7BPAR5QEalMQU1FAP/6cASRkAAAAhERXMmGEaBA5AtgCYMKCOBjeaQEayEbEG4okIogQACAAVala3rygV6X7lA0fZTm9FyVCAU6BWtFyoIiEqNoGwM1RgKrS9VguhKOhgUuQW3MDf3WufFVVdFZUuSsV8j0wsIND8xFbJY1A5EzCxg3y9UxsIUwKw2q9S0lRWGwpU6gkvjAgsBioNrvSNIiUXSY2TgUzRa10NoZ3UOfX++tFGqAAABhBJpFubS1hgSnpnMgFOqeZyfLt3PWz8mGe+Sa8sNK1VrKA6VjRcmNFDd5aBVma3Z8VgWbMFmNobQ5EqAVHq1WN5UZkkAA0CLaccyJ0gHxgR6ZkbkZiSfW/Jj2GrkZsU1Chi2RG39VvpGKiwEBLmiqi5JtSUmkysMnRKdrkVh6HeuzRQ1Y39jdBVxFMQT/+nAEpWcAAIH+LV3owRRoQgJrMjzmYgjEr3kjCHExHJbtXBYIOIAAAIUCWo5NO8j00B88Nm1P3N6s5qDILaN955N92hcovOTn5IzPo9wx1QJAY9z3bgmhwlYjd2RZXe9abtlHEawBqrEMzUoNZPKlCpe1KRjRaZWKsF7+vWeV1yCwPmpSLhQ6HXk4GUq/Bhzq1GhGRW0+HUiHLGlJsOnItF1f/u0Us7MAA0GmGrXyfAxC8aa0vfE6+1104SDaPxSbsZvZVQb2v5NvL9pWlPKKDrWCAgYpoF93bcrW9ci9bgwBwg2JVEGKjdiqM/tQIAlSSWMpjmRiueGyqhZsURNK0qTX7lSamugSpACDqKtdOpvUmVti/s5Gysv9Qd3OOeJ0E1TtKlFrrrKKHs9bm/c/sVUpRilMQU1F//pyBB1RAAgCBDXbUewpsEFCa1ckRnQIoCt05IxGURWMbyQwig7AAQASI7aIc/RqUThInjnjdlxZ2rlcxa30dkZ4W6i/fc2Pb+/97ohG3apk26fp7tvYaIGy4aVUO5Gy3vIstxQywAQALcttTVBOioHGyah+KzI3zRPgdWXuxzIwX3Sk6J3geWlptjDS3ineaFc1Es4seRsJrWq9LH1KbsEK/kc7/Fe8oSTckqaHqjg0cMLQBmNkHaKpnRgRjg7fQFnrHLZOwkLiU2esIvUqmKME7ybhDKSKbSrOt+HXvIumrf8HniqUsKix0hUyAgeRpluSAxEDUTUPYVjBuhCxVNSFg6Cn3SpsCYp6z0607VPUVKExoMura8mZEMk/K0Mt+LsvdfeLH4ucmGiZ7djEmmkUpiCmooAAAAD/+nAE5soADAITGNqYLxBgQuOrEj0iaAjI1WpnjEuBFpRwKGCKHgipLtq6sIk30SjGeiNex1/OGvFRNSoj7IEESOZDfXjG8u7pUsUF2XnVutsmWnVmn3KrWcA+qoUTee8XZptr+tymsQA9+ZiGctBYpI7mgxJDlVqPmOlQjEvmCu+4qHvJmAoDnO60Z4J2pwFXe9AdxGpQsRGhIVJFatesygJFmTAt+d79HpoCblv/9e3GKTEd8y3hBxhSTHG4nrYagPhDzNZMiLlCA9E7vvl5rl2dk69mY1P0fR+MKGEckRxd+OxdqGrEplaSJ6PprFlZLiku0nG03XUcgruhGr3tvrewJ9kiAiAPp5JyIiBlf79yUv9SJZHZwZFirO8Y+dnPGBVpJ+RnD2OrsvEVNd6xVjWPhw+ityYA//pyBG6TAAAB6xdYEekS0ELGW3ckYmkImFtzRJhE0RmUbaiTCJoQbwxuCFERASQGWZZpJk7jBEhOO2eFvSPLQtHDSze4qJJumRim7oORQOyU3dxVrcsNeuAnikUUcHIyCe2xICSU5LN/lHA5aaBPoCrA0EKicMteHJzp5Ab52TbrrnM9Ss7/e57Ud7Lv09OswRSTKoFXjYpV9elLmkltGvhQ/6N6tEAAoBluS3JP6BwiDD5YaypXQbH1o99S0zopmxcvVALyz3LX0Sxx6lsfxPKlFlBvRJlWsFzCqH8WfWtT+8miHUmr2jiLEwCAACS25KtZ0YFrFmVhZQ1psuHz0HU1HEWa9XbO2p9H2//VbEe2ooiiIzlmTiqYvpqgaWFk8lZ0NaxzVuqShD8oG3ihoNoTEFNRTMuOTcD/+nAEniIAAAIdMtq4LChYQmZbVzBFeohco2zGFEyxBZpsjMeU2AChsqOS3CIVQXHAtImifWIpLJFqJY1hg9X1FXCQoqOqlaIljQp3Qc1Gf/zJZrX4onNVF/IrRyBNGK8y8q3RX/Ns+39UCASW3JMfL5cQObgLV1TaK4szkejztHbos7IKq3KyRzP9H176IhJFS+UEM25KaeTjnFnKT4D6KJeW+prKWq+qq5frgAAqV7NR84kh0hK7nzUT8KR9V1BEdUzGgYxRWVsnE/gh371bVD7MsnBnclsFaZWtXmgOEgWVCDN7CVLdrMFtaUqd3AFK3/7GVQUGyS/mXVKH44i7q2StvnIo/tO40cQ2z/gor2odP2+av85VoqJtXnshiyiIuP3aHqMKjKmgWu5+r/3bkxBTUUzLjk3A//pyBBmLAAACBTNaUeMrJEMGa7oMwkKIbNVlRLykwRMarEzzCdgAAIAAU3JM/peIQA/nRxwUpSr1XQ2UKXMTHim4vCJSG76aO2+nv/X+vRtaquvreIvI0s8RpEDRHKPH26bqSPT8vAgEoSks39PDDRXbvbE+7YLVtW5vFb9u/fMZav3Nq6U/hOfk/lfB5H+XsMCslEcWUCgBQNExcRlgvI1sEkVNVrBmzG4AAAAGrv+rVA44CmVDRasNa6nSR4oVcEpdHEGaLglTgMZqPp37bnbK7KrpSlOvQq179G1u40ZWPelPYIOW6q+v+v3LABdv/zRdDuWUW2rsvV2JkSytPOrAodYvPCPwwvGAwTC5JPzbvuuTn5ep8nk1fj2Xpanu8KIbFyfXER2cLKvLKyUr/L+5MQU1FMy45Nz/+nAEHAoAAAIVV9tRaRFEQSZ7AzzCdgjpQWz0kQARGywwtoYgBoAAQBCSduzkK0D4fEIT5rttt63lDshX3HocX1tRte/Tg+C99PbXoT//ty8nX/spW2Lq3Im16f/2p+reVkHOC4Y7lVAANzbZmSRVPztT5dzLqlWdpXkF4Zr5pP/EunqSmBwfAPMdJC6vsO8Fy9Oj6Onwadu38/LwF5aE/uuejWY992r/rgKCU5LtCPWYMBcH0FsU8qxaTqLq1mWh0vMzT9Ozdqs/f8ypQ+pdz9U+l8FoJHoR17/np0m68vf36vS6EtYQ5Tn1i5VbuvQMgJTKrajssu+J4crzcLw6cOUMfW9c5OZsyaOt27XOm4+6emfUvXrZPbUFqgqgY/d/369+vbv99XPmREKsQTa521U+JaV6aKkw//pyBDGSAAACAU5dBhigAD1py6DDFAAIdCuBvBEAARoQcD+GIADgBgTgTmhCe3SIu0P19GWyFZtn2/7czBxk//Fx5Udv/d8XEgsSQgn/2/xcc9KCf//7eOd0RRUgG//5QVUMFQfKOS4QyfkIP9nGgqYAMZSKalEyk+3I9P2XMwcZP/zj1R2//xcSCxiEE///zjnpn///b2vRRUgG//5QVjBUDqqACT2krSTSVzjUN0MRDeBDDQaTFZcYklmwZSq10WYNIC7/CAw2AzsBVArFhU+JAo0sXJFGt0amMvRJ/c1VgVVyRCZ2QICDATmrsaTSTC9zdDLsKceu4YT1SZlrRtLq2yO4YrvRJbZVCERpAXf4wY8BnYCqCdq2iQKNLLQprdGpjL8n+3YFVckQu2JiCmopmXHJuAAAAAD/+nAEA+0AAAIYTF/oQRRgQamLmRQijAhE2WqklE0BB5rt6JOI4OAVEo2rUmm4ry1RG0OVqcHw1DzlMRTTCFxBiuq+6xOYNF/fCOjrtoV6X5afdprV91Trr01VSrNhTD/SDS82eYAlb5RAwADQAC6hEeIhISXGKopNxvRqfyRsTMDCsg/19pCiaMGi/vhHR120K6pfiaV0dpn1919demqyrNhTD/SbXvYxP1QAQC/YDjggIoH/0zQ02hVwrSo6bQIXKztr/dzOtbdHisq5dkrX6iWmczpoj/xVV7a2ZJ+TSoqJeDTlUJl1ncRUdS8AgoEBJBKuURGYUGoB+cLkNkdBEdUSNTm2V2lDo/8ztydsVl5dta/iWmc3oj/xX4trM63vYdO6jXc/Euo8x2tjbFJiCmopmXHJuAAA//pyBP3bAAwCC0DYAekTsD+GqwA8wnYI7NdiR4ywgRogLeg0iCzNMsJDCfRHH6cKrCHwNoJp+Hc6kEz1IenMN5cQ8VqJROIoh27fVakshpq+3gi0m+hnurYMvvp/+Gf6M9v/3f7+ghnD8uoeDCKg4Sn1GzAJGcFlty8BWOwdhtI96B/o0p9Ef5mRI2dutStZ2mvVbfporp6Ge68G7/ln/GS2n//5PxgVNf5mou41TDYYjyFDqqU7WrqT0deJaMwbBuI7uK6NvoImLGlxratU+hF/VFv1u/p05fCPloo6cQd2eYYTQd03KG1ybvJGI7AABQ0mynKJpcOL8lW8KZGUcg2D2VKvUxtvBM1+FfVtfbXRK06k1V3dH6lpv18K39X7cmwV9XFEuPHgCx9VVWsPUJbDJm+lMQU1FAD/+nAEpuMABAIQQNkxiSlwQmgLAjzFaAi8bVgHsLCBFCvu9GCK9IAAV/+RJC8GJJI4HbHi3Gt6ot6f6k+3ZLExhNjtq23Hvo+nZcj0W6+XtzlEkRD12pu/KGV+vk/jH0IrvI6fxnu/khVmqkzAnBQmaTMkp4Wq1Q3p1NJP0VrYFNuZh5QXlwmu47Lwk+R8nZsQ1f+Wlrto1z2b/XUd/R/17jH/ej3v0ZHo/dTDUGiKEUx9dqYR+ONlHNZMMPubfgPxvC6uNYimKkHl6x0lXY6aKCNQOooxNzjTCiMTpXGMe97QlZU3YTremn9tvf/1s/r5ABG7ajttu5kM6oF4r62vbLBu1Z3v/53Y/55Pa1w88r/Cspmj6UXKFQ76uRC4VkViyJYmWL9fnJ26PVB/u70ofbUE3drTEFNA//pyBLoAAAyCEzZaOCYQSENGyzo8wmgIZNlmZLBFAQ6Oa8j0jZCAAQiSKl2OyoswoJFSsjD1ah+y1DBYO1lvT10doI66A4Rztfq7NVVIup7aW0Nzpd0F2zZGn9vSlJai+vSKdx5rMZLgAGiEm3XLje25wSRUvln0+GA4uef2zPa2zMx4CvWqWql+h2ZPm04/q77Mj7edpN/VXU6K8w8YhrSCf/r5VX6u5/pfWEFDLtj1RLzvYMcGeq/vj71R9TK5tXDVUSujZunfi35vdOU76oeX6//yN4a/I4AawbqfxZA1KwTB+Kuk1ZYN2LTFFlQB+/ucxcVhViejqmgNcSOGsw82/0T/lRMoTGVAReEdKVVPP8CbNi8d1myZwwXsMgmPoQTQKIUlaxylujEav+1H7e/9CYgpqKAAAAD/+nAExWEAAAIXHNi56RJQQia7aiRieIhEy2JmDEzBDJlriPSJoICAiKSSZ22lvn1HRBESgJIeOtd8Oqgy7Zt8T/4Mmc67vpoVsRqImQ8VC/qI2lAu8KulInSpZXQue5P1bYo9H9OTf9KBBQBJttyXsXuO4JmcfryzU4GMgL6s9vq83pdOWNKF5F2Pqd62lqypomi95SPb01dXpEr7vs/PFzqgMaD7t98fAQ4IOS23cqh41oaEWAGJy8x1onoXqCKO6cKJmhr1ffj878mE0J1dczTo+9jTUv+XBnRMhei1VanMn7484vQEH9VL/8iA3/y2ZywmIrRIhjICqoXMhgDU7iXzO72QYoXXY+6DL0qatvdWwB8O9VfmyF3Z6dWzvyu/Uj9ng3WXTHXt2e72/o/7ExBTUUzLjk3A//pyBPLzAAACGw9YmY8xID6Gu3okIkSIxNVw4JhBsRQOsDQzCVYAOW3beQgXi3JAm+EPOfQZO0s7Bt5llrwZxA9TqbJyyTnpC8VVBSZUAADNpUdYbsPIb7Ynr3rTMTxZynXXRr6ztutVUAAChFxyW2z2zxQDkTqDiDb051H607dDZP7cK/6I66MZaqX+i/vy1r490rfrP9Pw3DiRHJCMkScjKiPf5Ko9Q8IpJJSjHVDAQcs54LZ8vAtHV0ehBPMI1ZYf+Zsg/bVrs7GnepK3v2z0d8EqMCPcMFyx0S3OCJzu+uEM8GIt+0C0uydd7IQGU42opI5NjaFJFc/BaH1fkfnV0erCOZngj3P/Nx3lJ2TSEoKUqv1PFGls4pyHUWwGt18w1jlUDArvpTL0BlJsnLOukkxBTUUAAAD/+nAE5tUADAINHViZjzkwPcDrygQjAYjMd2BjPOUBIqttnGSIigA5Nv85MEqJps4e5hqwHFkbZp2EPLlXn7DlDB03TtzG/HxmVmYUilrpuSm8WcDD0noXuF2o1UU0JnyK6xHQmlf/ZgAKxKTbbpAsrEPSEDaAJ2PGnUEJKy2+ck/qOFJJwq8tFn58aLHwSFwyfEo2L2WUU0OuXQMXX+trmNgNlOKgAyXf1oXti5DBH8vFzlW0UjtzzYW2UrUUVHT2Q0Tn5R+1DW76kNlyDXINIrSoN8DipYsSii0WPuAx5TmauyaaRczVUlf/qgOEVJLc26IuXmR6iot5xOGzuoxCNKvKrwS69vfYRzdqFDkqfK5VkF7LRj/sLTSlNfTzJdrgl0ZV7Zt20srskj6P/0jlXWN7somIKaig//pwBCS2AAAB/jRXmeYTQEHJ290MI72I8Yt9oZhOeRQoL2gwiIYABS2jxz3JaebCryQikN44nbcd5iOCE+wnGwp/GqcFE6fJ47wTfiuvn5enXVerYR8yPN/kliYs+asskLLP+uqpAAkhxFJNtzZ9HlVNobnw+CfcA/Moo37jyX4ubyPh5dcLs+B/kv4JQ8ehfyney5f/y94alIQJlMoumyomFY6XFuG2AACZGW205PnXOXGxsifp5ofWYjNSstK0Fafqj521Fc3Xp25+n//slH16Cf82t0NzPu31va/NoJ5fye1wTHszd6IRUVUcGSTgCf0km3JqxOOxoa1xI8pqATY11EskI3fRteXq24/frzLs+vBdL0b6oMiI+pOXt0oyzzLV+nXy/xRpC8Sj0AQhI8YAsu5MQU1FAP/6cgRWwAAAAiU1XtBjEPxDBTw9BOV3iJ0ldUMESnELFK2cFIgu1Ab8xSOSZ2occ7ClmzVhHQXGOUH4/MAOp2sXk68G2q6pb0F8zzlbX/6jUcWsKPJmEunh0odWzp3HgGedD5q7lYOkV0SVBIRUtldlt3GKbMyUeTtZXQqtCdDwB6INmxMFxPp11Gvqmvl6dOI4TqsoSOhODkovDMIxJ1WIAzgM1ZNLkRM5U27uXgAHRJabct5xay3OvjuzVJ1D0cdKl43K2r6fy8J0NhXz9OTqlbLR2r7vgibkdUPV0r0XTq3Zv91ETQaAYuTLlXX+Gk2eoAoJJaTlUI9VXGlba3UoUbBPmtJqCdIxMLzMk/ZsnRdeqaLkTUS97wyYAshJH2Hg9N0xWyL/wI0wCJdofH3q6JLo+xMQU1FA//pwBMtRAAACFTbd1QygDEPFKyqnlACIATV4GPKAARCmrwMeUABEBuSi225co7DbD1JoPMWzglDmTJxV3Ur7my/3yFH/3yE14+5zLtavb50EVWaeKlCkrSss4rX8uNLmVLGqbXRjVf9SAAYAFOOQYdVJCqERIbxxzLMGMuH/1OQPmHRqMOk2Jr6dmykzZBp+bf21K8es4SFxwSNhcYGr1Onp2c4cnmDN/to1NRh5JpsNyFnxiSQd4xRLRasqjvznT75Ji/+k//P0d2Hmf/IRmxUwDlVH/5/8SdWZyGHf//JkvvHBNv//WsewsNL+J54BuN0fWySL+/Sh1JnDjKGdmibp7ORimFi/56XI/8+6OZHMb+xCM2KmAcqo//P/iTqzOQzf//JtfeOCbf/+ta2KWXTEFNRTMuOTcP/6cgSKUAAAAichXjc8QABERCyt4YwBh7ghfaeIwqEODq3ViAjYAFSqKwzYCsVW3s1PBbYvs+Q+LgpWv1Q2bN7o9Xpmf67XoFlgyE2uTeBBCG4CoDtu/JNVEtu+7gtvS8f1oXlVjOoK0HouAmk07HG0iSuCPUp1SJqGLpLykSsxzOMdzND8+ex/8f/PgWqMi4s5N4EEIbgKgOzrFvyTVRLbvu4m6Xj/QvKrd1BWg9WAAJDJFGkQAskadUEPKlTlmHg0edhJjwhk1xmdL/efQ0FJWSa1TjrLhWddOMbM29X50XWJaj2rcldJjEXG0BB4GPjrFe1Ixawp6jznmhrXOld7S02IhjWO/0/nXj6Xn0NEkrRapx0hcKzrpxzYCtZq6s6LrEtR4fFCtyVqSYZEXG0JiCmopmXHJuAA//pwBLgSAAACHTXk6MEovEKmy0BgYnAIqCtrLDHhAQ0a7WWQiegRNuq3W2NpKOWmGkxKjc4CAvjnayI2UyujFHtX8rOqoRK+yuTNy0/f05Wl6LX1i+ji2pmnW5ovi3uUWEusNFeBCUwRQIQiL7YZDKIKm3apal7DsF2RAq1ejiPZrNWgVKv/96tRX9lclTctP3m05SS8i19Yvu1sxR2FKyRVba2CmiwKhrW7+oAKADvv8QzBekgMWjLSoc091++YgV88THjUiphQscUDYspug7GdTB08FGMVaRkNR4oWQTqrqeeTP7Uey8NEu5v2gH18iTAIBwfv/EJ4HCuOTla07n1N67u2XfnbyJhChM1kzIqZ9fcIdAx//99O/rQU6pX0Nk7KK+p702/UrS86S86/2u6lsyJNMQU1FP/6cgT0WAAAgh5AYejFEUxCiAudPCV7CLjXhaSUZfEIIK6o9ZQ0BZaZdbkkibgKPerFyGuqREBPLkQltVddxJH98r9n2bttg7SZilRLJdWF6r6E7+o//7Zle4Lzq2om4Nd67eWM6M29LLAAAEDA3dInKFSG/dmteBqN4/zB5ST/DcYKzaGBpXV2QYKKMLI32fWyt1b0d3Mbzf7+r/09tFfGd/LyLga+t3UZo4FAKKZkbkrjbgwiuts99SKFADuRzSd8ksEdOtmuXwv2KIf5PxStI+5xO/57hVe6fKUk7l9CV1MWcS9AePL67zOIfrLee3eSSlbWWWYNgyoWqbXP7t0LgJV0Jpq+iJJdGK9ikdBKqMmNDOXt++ibtR2VBDp7bVpqTp/TV1exx3I4u13U/TUzmbeVTEFNRQAA//pwBL4BAAAB5w/WgxlIYD5jqxJgwnQIxNWBooRR8SgTbWjHiYA06Kr15sneZeDK2PsTonMlEIqxsFzNfgRaY1307VCaHExgFbTpBc0oqNL9ND4EcSbR5Crla1qr2f1a//4qEXfg0SbsRZfesvvNXZTOc3BVkEXm5xuKiWhoJZ0iOjVEt6d2pasb34ukgeapYpSUvd5iydoqrG/vopV9HUSABKQcTjjbbiiU1LpLaIglsRiaCSzMRFYIQZln3zA6Tm8j0ch5adiBw+QpJOwIwhbTCgyMSeH3te+RSWKY7uXChbfszzi9wAMACF26/kECV0KLNQEkxa+00CEjOFQd422rETUFzsia9/EP+nS+iCzKsSHC4cCRNZJE6KuGKda9ptP9gTLLh0UXsbPM138bSJEpiCmooAAAAP/6cgQKYAAMAhkm2RmPEwBAomsjPCJ0CORnYGe8YYEOGS2ol4mEQccdtBVSOD19PPsmVxZcrBH3kTDKRyKy5BTtIDL+rVS6XSxvuktNFZBUWcRWG3LczcOiL3WmVzn1LPXrJpbs+EnetASLktpdUYhS/d8umNajO79dTAC9UNExDFVn3LeDARr7ljqVGPpUgDLDl62SIvF0Gd6TprJSs/7op5Ji/r5DZke4GNOSQoFwPwgzOkT4qw6VUKM8SIJpmewwpCKJhBJgmFroDU+fgraVHCcBu0jkyF100F3DHyUCLcvTpS9MjsZW4/dbZs05D9AQAaItuR3jEkQqakofJ7feSZJJi6Hayrpo7MjhW/TV2dejK/ej/RsuVquvs1/THCCFurk1+L/4FtBWaylnSCpuKI0d6YgpqKAA//pwBEI8AAACChzZOS8TAEEDu4kZIhuI5KFtRYR2oRoI7ViGGKYAgDbt2BpIkAg+qFmMskTYImAaisd5UqMyg8Eiy2N+rSu9F4ggpiX3ohy4VASrQMydalxSvckWQWWx7PF2C+S/SkEUKVtmujqG2n3PVEnxOAmx5dFBiMHmqhLG367pmI1CD4I3rlLFOPRMZaMlIx7imufoYmbJ06YzXWec+l5L/WakgAQKSll13aVZsvS9XqsysqADtf3dytyTviEGH9gs+kRnmByKpLamR9Ew1+iR4op4CMKUdFDBCIBdqOdTQj9FdbgjojV6mf1AHVRW1MkGbfTFrCLS5T0NX2u52bingVHbJxJb0BgSua5so/0dtzyJcbGyDiCjpeJiiouvLp+upzQgJwSNWuGGEahKi9/RKpiCmv/6cgRmrQAIAg8OWLkvEGBBocszMeJhCKj/XmZgrAEKE28ogInuAEAnJd/gzIdDMeiny76DklVJ3gfludlAWsJoJoTFeeW4dSzhy8uMjg88yOY3Uo01lzLOcdPqfoihWmHljOv/7NcaC2XLcwbvBA9iczs0O9vZaB8e7sD42JoJsAvYJwyONiJnReuOkrvcpiJ5bCizIVuFXxI96xWoO1IrHAHOUEff4r7wAk5v16pOJbxkEvL0MkJ2guIY2iM1LEtFKAx2E2OpNtuXXtazP27UZtsm3Z6v/13qupq/+vdYhreJ6nAdb0Gel23TZ/akJRKS1G05YHb3A0+ZaeLB+axdbzlWBol8+rodWyZhnRrltGgEMFWlOy5+Jb/blEssXVMD0AVRBy1FicMLpsXGirm6ExBTUUzLjk3A//pwBIE/AAgB8QlaOShgpEJFa0Il4kWIlKVkZLxMEReUr2iAjfYAgKSclpc2uIMB5qohr3QlwAfkBj1ylrDqZ69BsR1DnJn9XPFzAllr8TPYVaHw5WpL8aWt6ttIuB2TlLOn9QKy3NcQkD6hGOH1T+MNxzcNtWyanOmVd++UXQ7VvbPq22itLOZKiSwxoEiBtCLwqt5NpPJsxTbrpcGQtOZV2fugbaCSm5bBdUCWQqibkWI1rgIQkWk0jnwfE4KqHuTtVtF3Tp/XTq9CbJBtVBjW9q6jzW8xrWVk42TWgqqBnhws5Jxkdce/74ypWKcccm0kk6o6GDWyJDaVvPhyolMHWWXFRG2plh563KKUE+DBZRg8h6zTWnFMQ9nawekfawArakxVInXpdYp6AIyrhBSYgpqKZlxybv/6cgQRlwAAAgUaWRGQEww/xouqGOIpiPVDdUQEezkjk+90gwoGBQGyovbCKg8Llf+ba88uCiVCrgGFeo+crEBGV922kfjmBMyjvQkg8/ft8lOr/1LHlt71Lr1Bgg1QAW1k8pSf+LhCAsS2m3K5ch08SyHaIDl2y6vV2nojZ9eVZlqP1T2771a6Mk/fZpe/sDagemEntyF63qpj9ADdErTXtLsYkZ1KMpc0mWnaRyEFMtSpnlkAv68Q/Q6Ea8mTSCIHf3+/jZ5j4N1vL8suDJyMpOfea3h8pftze7NTeBS+/tlo7EHu48qFbb39yd0hqNCZpuOOXDYg1nLWLLrgcD37dwodHUrnIfEZ2ZD4PXorH4t6NvVEpaIiNQTKLJvNrcLSV2y9cGkn1vKWXTrAqLrk6G1ngRuWmIKa//pwBG2gAAQCCTJXmeYTpENGu+0kolOIbTdm1PEAORmmbSaeUAYAkpuC6mXQ/1SXdlRqxIofFZDvBDOSw3BOxS2oM3MuQ9n/Js+NrLbbtpq1HweGfTTttxmnI4ejtk+fVRptu2t5ZyESaDScjbkouNNuw7HLHpRhQj3Np+hhYY6qB2O2bK9RDM8tsnbJqSj59H//8qo2otQfc48vfYmcuIXoIrBMBLYg/mlBAorzf5T4cGXD/VnPeV0fgmMaKri6IPc+ZmmZ+XqzdFLVaF3/ftQ270f/n651k/9tO6//9s2opUVjo8ATNoc/8hU/6BABYWJchO25QPKVZ3WViudsgwGborXCj51MJSPZ57Nn06rVBrF30L9WzavV9T63Xn3WbV7+2mj/T9e37nQRU8e4JFVNfuSmIKaigP/6cgRdQwAAAhtYYR4IoAJCySwjwxQACLghct2BAAD8hDJ3jCAGEAoFAoFAoRtAN9gAl44CPvI/7m//b6uR2/+Lggm5Btf/gcVAMHOICml//hwUOxGUTF/t//qjTzoKO3y///7ijsCAgeGBQKBQKBQ2V4J6/YEhuyyK95H/dP/2+rkdv/nBDuQbX/4HFQDFziAppf/4cFDsRlExf7f/6o086ChrK/+BHFAgGA+ASgAAlK8yLauX5qWH6u5YZdu7hxIiDN6wm6HVi1ZCxwxScqRFBg4nW/LHjYTZCnQ5ibFMe9Q+35KbfUYr0Ny0OsyzHKTSAS3FZJW0iSu4+Z/02qqrdGRZesFUQ6PFonIWOGKTlSIoMHE635Y8kJsinuZsVe9Q+35K/UY9DctDp7LCjlJpTEFNRTMuOTcA//pwBO14AAACDTZjaGEcfELGu809ggQInNeFoxxI8RcbLrTzCGwAppKWRtEkhQSKt2JivxgfqQYp8fezOvYliU9F+q7kOX6yZiZ/yGRNykQODhdRKSCV5k8bVeLi4tyrv75IDtkurNgAAgOytkppNeJqDJK3jVXKXmArKwJq7F1c92b90bTXZP2t3of/6OpnX+h2Y1FZgZFjRV0As0/F5HUGvoemtIXrd2UgJtEWxxpEAoHCkm3FS8naTuoXDNSKgTdcKt2orM7aXXb/mzHZaJV3/q2qG9F/w2j35HLa5Nh6DJs9PPUDoifLJKjPfIpAASBkicSIAQ+w6jybl5OeMnmaLx0hxL2kIbfLk08bRHfb+jGzOy0Srv+ys0qG9Fp+Ba3N04o/PZJfUdy2Iwkbzzw0zMpqTEFNRf/6cgSuPQAAAhU1XmnhKLhDxsxdJGIJiKzZXC3goYEULCyNpghoAAgAf220jkoMsiGuLNCM1jGAjcRql5NWGsjmPoaqt2kvfVsyqR/K6fvZ6lt/R9dWGnX7FCLanX30KNm7+u7qnendUA26TNbbY23BsxSgVctUpJ4WwZjKndGBWDvQz1E66Nv23UrOiZXTy332tv+lUdWBiVe3URZJJH9ccmw2bQ/lXeqjsu1G+YBTmEA7ER4zqU19nFuPO/qq66xCoXOQWEhZxh7BO7Kbkd9cQvd1H3vlvPO+R/tlbelt3t07eNFF76IxoE/a+6h/2v5oBttSQKW0VXtWde2UW3I8fEpwD/IsFZNgf7NV/kat1dKn9N16UTtr3tK1/3+2o39X5dUW4A9sz/O0z69pG/7T/3Gku8YpMQU0//pwBAm6AAACEjXdaYY4SELpvH0EwkWIqWF7Q4RVcQYHK4mGPGAACsCux62W3DFQpUt0TjwqonUHVp5xtHxUtT33r23dl5B9/pquboy3oq10Q7t/unsEtkVrM89R9g9ETIl9aLbXf2uCsmkstl1slqpAKCYEbJboNmH72Dazxap7ZteTV/KlUeTsTdXZ6WsLazW/Tvje9E/sThv2+up7uTKaJpQlQRYqly/JAnBMi4mkmIwToTorl1FjTVGEbN75HuYq/rtkHITIeaoZdFeQpM8Y0FhBmRICN0oP1qyu0pTLfYv/6Z731ms/+l/1BPTzICs0CZBK2dDwz+x367QxgeEDhzIZr2dT7w7/ZbgyfEzlwGt4hhgSElrCU8FGCU25UXalj6mLqfoPSH/zXatzv6w6mIKaigAAAP/6cATmPAAAAgA2X+hmEwxDJss6MeJhCJSxb0SIb6EarG4kkInGAQSKkUTjablEGhPexfjCysu7wgrfQ8GEr7Z9H330fNRp0XeZWorV0Kf22p1+L9kBGMvaE37JVTUXb1xvDPZFKwAgAICKScIIDEYEHuS2vAn7GpI3CvQNkfH0R1EPX8+j77tvmo/ay9v7sxqmv62Q87XFdlyab/dUWfaJV+VgUjkaffWAIhyMtttwfZaGbXzKo/7XZApbdMFwmZljadtsE1ZpXS+D0RrM6rIMcuVzbBnP1xQqxzG4NRUu5ZV/0EpFrBhjf9Ib06grQSlmSzqbB1TI4tI/+ngHU/CqaBkma40idtNG01Wg+vXfVdnRtStQ/u16qT+I/m6t0//+lq0V0Kt3onoW6W9R0FdjDzKDKYgpqKD/+nIE2gUAAIIUGl5QxRpMQQO7Az3iDAjIrXujBFCxFKataMSIbBVgrabTScjQ4N5q6cZscER8T0a4CeDVmVLS8vvmXRDWXqnMpIaHh4Moc+ECGP63TY7a+roUgkFlHQSfqPDnSQGmuMACcksPwkb0R5zyemoDe+xGbGsM+Ka6w+GGxOquiXXtl7baJjyCqCwMqOiKErTDtTQytoI6zbr9bfqsPLrIM3/S8gEkmEouONO1dI9q22V+UYF6ufi5LfeL/pqJ1ujVNnZL5dDuj/M6sCHC5ihsXACDq8yFHu/6yqg28Wo0rRTE4+Hj6oUHRrwSSctl3Ol9Qpawt945ffgELn2kejY+p1qlX2zPVs+9lN9H1LSqOuyauEV9FX0uVq7zr1+1vr+/efqvqWCAaFJvv2v0JEyYgpqKAP/6cATYpQAMAh8cWRmJKNA/5krzPKJyCHhfYGeIrmEMpnB0YRSuQU2//bdXjLjSFyagbqxGD4Ai5NYQoMHYY1HZyafnao7TiizucVFhQKRI1g9C150YbS5l+dU/j3IOIrKPY0k09f+sl9RATltsNDXYHpVPH8SGvKWBBGm/DKRLFIEiUDMAbHM6ZPweraatVsuiabtdNO81un2YgR5G8ZW3+z+uSYeV0L5IAAtuVnZlyPDL9WwV01mVfl1QoEqwrFhbOGEzgd7yICrGPR+OlaLldqnum9ZsmNDi8y3SebNsrEdKfZvbHMBL6k5vRqRxiIkrkkkl1VIYHpWSPlctqgbOjYHad8qs/NjWy5G10/XvIub+n/6UP+Op7027eV//t6q90QcSOjWIgVK48ay6pGxCYgpqKZlxybj/+nIELBgAAAIYLFWTAywgQiR66mHiHAjRPW9EhFExGqbu9GKIngA/xy6zRy/b3sbbLCp+uixRYIIGLi76r8/tmMXHMW6EeJDIEDIU+L2IGYl20zbcj3f/p8giy2MtsuOWVql9hz+ylhEAACAAFLKHSgiyHPdtZgvYS+oNOQkBTAa1FD+crmkWZArsU1CaPn2Gzdt9P2egq+1UvAcnwAoYpNSlNUtu36rd6g8AkCpJSTTicFUGp63dB/eB2kl7+QPR8GSOJGueSz/BoyadHsd7PYlfKiLp+d0RrGYh29FtR6E8n6P/+5mc4IUyA9+d1IMfWQwSAYki225m3SBom55cKg/BOzolnxj+vXP1/BpTtRHsq7mb+uF3b+lp+9/roqUt71/9OhlOM9x1BEjrUiLAw4Yw3EofXPKTAP/6cATeVQAMAgUcVpsPEyBBxUudGEN/iHSFXGfgrAEejSrNgZYYABb1wcGtSBx5HqLQfjpoWrhKRJAfukZ6FFiuXWGCYI71ZUGWbQfJo+EstvFhxMVax4fX9DCSBZNt2T7v/HpLnlAUIgFpEtNN3CHAdGMu4Q/AsIMrBSxiQzWKaDntp3yfgsI+fO+Z7Ptfjj2po9j2/QB2khZq5HTfTD6zf8udMaLh/cQG7tgslrkXB8fCuZoUryeY4hqjS78twGDHQqQ+2D1FKq2uo+of0fOSn4gbKJlmDbYUhpNqHKQm5SVJJauy9dUitxTtAALsokjFIaMCMHggqtATxPHlZZ0ngmvADt7yhqcuBzo2zejKGWwD+htQfEJVzMo9AoxVdqzdC5IgZCRpKQmfYWY3U/318u5CYgpqKAD/+nIE0b8AAAIkKlcdYKAAQombmqMUAYiUH31YYYAZGJOu6wwwAiCndsKkDMgSrsulLJmRaltWVjpVcoAMaMjBEdg5zhjogOqKy30qNeJiG7Ys87V5tGr0z6CtjdNuxzezOCknzu5egq5+sJUK0U1G4LyBZ6UIvo78vEdXjWx9RE+PPJa/zj6imbQW115tG+jeoq0yWKdui20dddNaaYosnd6/+9V5MCsKOsJqmwEAKZv21bdt//+APt6+P489ShRFLOWzevtanuFOfilcVHCvwugRiXfT6zcL2TfGt3+nnwH+s0r/bae5/7jd+V6bf8uowAABKrUm5LdtttgAaHfyGtCLU0btT1z4TmxmTTP7yXvkRepEmqdkf4R35pLIO4deJBATD4cFVjpRM6RYbMzSSHeiWpAvQGExBP/6cARX7wAAAh9IW5YkQABDqQtyxIgACHgDibwhgAESgnC3hjAAAAJM88MCVYlCys4MRhoKE5omQhIlyjlMzmZxatqnTuvr57/////q/q5lf/syNjH/28hP9XKw6wqz6hgY+wHwYMDu254AApnnjQMoBKHkzUYNSZWQ2oofKwedTM7iUFnJr+in/89/////1formV/+zWxj/7eQn+rlYdYVZ9QwMfYD4MGFcXuejAAREkEFlNyhTh8MCcyoLiJzzTYxYSL9CmBZpZwM3tJKatiqhZJkNpSs0pRyxr49XtS7Z2Koexra3u6UB9r7hOkLawQgABJABAIKbkAKLiJQ5lIeLGHmmwWCoSA6cqOYFjqXL2klNWzULJAQbSmhSjljXx6vaBnJs4qKUD2NbX9KA+19xNJ/WCCYgpr/+nIEgbEAAAIiFObgYRqMOsNbwDzDPAjsOXpGDEMBKQfwpJGInIG5HI390zkmgKMwoKOwnClTsk39lR1SsplmLDZJKaH8qDSmBMAEnN2jZih1Wbt2sBlbSDRRb0xdju78bsJEl31mSBGIdaMMWB9OtO+ilQ68t5147OnkijYJ6bq9898iZTaUY/rQIlMCZQk5uSXMNNO8nbnWGl2N+vVsT8bFdG9AVa+EpANgo2eM7TFavSYtxj7CR04LgMyVQAo26AFmROlrURG/GJcPvce3azp5zCVa5kfW9DggsO3i0IwGHntFhe5yXpSKrVFk0+0yvuKgTE+DKxHZiLa/NmYrhpwYPg7GhgK0IoOFFnwuucUTigcFDSaZbRdFywAGTC7UGz8nABSLPTQhsPmS6xVqCE2g+8+0imIKaP/6cARDtQACwhYK3UEjEbBAhsuxICKkCFghcsGIyAEHki3AwY0oAAwAAbgRBJdZPE1zdRBFg3GCYCFzljjSA8XCYLmA42YCLnhoRYSKrqpIJvpSwiQYMc+y9C7KLO46/Fn933ix57l2oGuBtgELpbizZobsmms6z3FjaApltqqEMMI0mt7HIDa8qu/q8s9N+jX393dKDoMQQTbTGdlSNBIyrI10V9NKTWPKAqxUMA0WSLB8YcJCjzazoLxfOk2rLg2PHnjKFLWtSWjSys4DgxjW3wmGV1FUsYSZaKVYG98tvScrOs0pWt0lMKPqy6EBFx3Hn+fIhwYYEZyR11MyB3mypTvSjx/jsaGZWd3O1NweE1KD46DimBJ6HxcM6itckxDSPQBk81+cStzOld0kmIKaimZccm4AAAD/+nIEJ4UABIIXF9qBKRjQQOGreSUGNAjYIWzEBSRhDaAtaLCKeHKIgNFk0DOTSnxjgakZjJEoAeiZHDX82I5SUSCR4KsERmJTzw6tqQFJpgVgHRZAhppsaMLXrFzo5RvQqp/u17vu+5oAANAArWIZAGqBR0Wc9BhBBT1r4xaHBRh0jGJvMHQMrPamZWOe0cbGwG3aXIjbyRZhnKpFOe+x7BLzqSskepK+ToFEByLKAKQjY56vLBKEXsuLnRElweAxpzg0hptOtZYUItcaFwhrcVcFA0OQo804CrhftyirjwwftWsr3h1PEKxFpTDT1naAwBJSUc13AGtUSWOvORnzIlqL1f8rdxExVrBWaE1jXp9vnMRXw/oGbkzQhQ/NIIYapfNWoStxytg5ibL8j9YipU/HWpiCmooAAP/6cATMWgAOAhEUWJHsEOBDRbuaIEN/CKhdWgwwbMEOjGvE8ZoQABqrUkIrkIrnD8BPnxNoL4GsskWY1Tu7Pslx70GAgE3hpG43hRC8G3QtU5TiApYHhnORGGl25N7b28w7OuMfDvdKh6ik04427rgSsp4uXjJohmwjbZMw7o+706ikIU7/XZV2bRorIyXvCpyXIUAiazvMOn1iNG5cN921a0OI/bvLPVuFQ1Up5UVohFp3W1mM3Cr+rK/YAzS3qktAtLeWEBduCJBuAoZvVoxcRLD1xVQFhoGnpW1JZ8IqKC1xR8pi1Ukmg3p+rR//31g/dH7BHQT/MFVNMVgg4HfHZnj3W2dY8XDWGXYyScypnlj90xgVopRiMabNYxgcDwWPPJ5FJy0QTGzM5b+u5n2NT+5aExBTUUD/+nIE7NQAAIIiDVt5aRjYQ2FrOj2pJgg8O2DGNMqBAxrsjPCKcDAAIAAIIpJMp2tgzdG23dn8ZPiXv8KVQdhO04FGuMFBehiDg3U6w64mbvASWByTFlU6j1HF6P9EJL3jolT3VgtkaiK6gAAIIKTctlgbIfHa1zJiVVFUdQrrXsqyIDynBccLWoYKLeSrrYMutpn7iDHJ2JqSeudaIaql96RResjyGGq2wW04l1ABAi/32fA5SFeFS2VeMQAshSNkHj/jq8hUF3rNMWxDhq1jBzmywu6ZRxU2tylCtRmnG04yUk6rXDnOfpn/TyPW+SQCckku/0OjTBJLarTnuBrJ6FE/a//N7DGPCM7mQdYZ/32dVEaslkttmhSRAS9nyq4yp5JDWUSlCE5OvY/998j7ExBTUUzLjk3AAP/6cAS9jQAEgd0Y1gMJQxBCo4s9MMJiCJg5Y0Y8yIEjjGuM9hRwnrWAse5bg1yHbp5RS5ECGvkEhHlgKLxR7sCmZc55IeCmSVHZw57GGRQZQgkQFhoADjW1MKt9e9xR6+pIAAAChAMctts1QDfQZdzTzuDQ2ExfcSPoHWF7tTR9eXS6cKwiseyHdcP3C5CN0FUlUMFTQJNrlVbn6ibvleps8z6KAwAC3JbbO0AhDbzl/n9sECVhW9N5Qjf19zi4HbHHNgGCeTNrkCBsDgmEFMCZw4DBYpKSe6g8hCHVChMHile3TtT0Vq1IACLTkkX44JxWrDU2q0DWnAs0tjG8+cPLDbBwthZJVpndGR2jiYjgyMOTO8A8qsFEgo4oIXOHIZQnVfFQ7r2a3HhtKWPq9ZjV2piCmooAAAD/+nIEnygACMIUF9WLCDOiQ0MLAz0iWAh44WznmENhGoxrSYYgqAcp+rYEY5dqojXEptp1v2JqsGlt3daVC5jQF9yc0zZYOzBQaC02daWRknZEEyLaVCsHJf/6tNFMqO2s0Wzv/+5/7yIKblt0+oTOI1pl9W5X75fA0VHrgtt4/2Xh8HVha3ar6PBuvW98FsLlRR3qvimKsD9ilMI7EljnOVnKiKalaclb8j+8zD0ku8NIBeEIB0iCyGjWcDNKA+6y0db6Ozi5G1fRoOk5s1b3tMjbO136v1V9P20Ur3UycYjfaqMXv/UlctbL0aN3XuAa/H3EQDx46MXm/wHDYqQiQwhrCmpB0bxujU6yCMuCVsIR1dvBldPWVyNtg8wxNQ+YUIbyySsp2FbhjSsPXqq+tmN/Zuf1JiCmgP/6cATbYAAEAg0YVxsvENBAgfszYWMaiMxhYuywo2EdDG/0kImmACTcgZGI3g+MMZVUE+Cto3KH5a5CsTLKrCGhmhBVRZ/YW9hysUqBEPGLYVTOKOhQacrQSIvcfvtGqrkNltTvTV9ZBSbkkrISBy0+TzDBq6gRjPQgYr0qXPkuHi0+p6jRxPTNgRAhLoUcqSkGjoAZlTTWGfRDq3giM80e96mN3y39dRACablzOATApclHVxdC3AjtLMYflN/D+46gJQUejZmo/ZRcJsk3MsY4Ixi/TMpvkRVz2SFZlbRzX0Xm+2oPOeqG62097/6gLE2VU1JI5dAFpH3QRM6kd+qzGax7CRLTZOaYLzkHNgFpFdMsNA1qFoehkUrVVRl+aasjmc7nEHwT2MBcmkkfWOR1hhY9NyUxBTT/+nIE7p4ABAIIPdqzBhDcQYDrmjzFFYi4yWDsJKNhH5krzYMJ2igJTNa2BJMZdxVlpbK/2cEL3JrqJzCXQXcujag5UZD01sWzW2bd099nRTzvtNTPZSN/9PdwoaMl2EkTDe6r8rRoCUAgkpNN1ICMvVQRPA5ig3GFCU4LuRReVkLireqj2mCOtCQAPsOtC66qvJjnEg6BlnULvVI1HXWasAOgmHnrK9KyCU3HLiCBNS2smBGdccTUzhegqMt5rjcRBsIibTO4t27oFCiX2R7E3VkVEjTS++7NVN6Uq9R0Yj54AuIMAup3rvR/yqWjyCk25ByRSyRIJ6Ou9Nudkrjb+B4TF4J/wSzS3wt+4nbLraFatQ5YKiu/2zpCaNp3yb2XJnsrMkHuuvOtocKfpV7Ebr4qnn31piCmgP/6cATD4AAAAg0y2T084AQ9onwdowgBiPSbeVhhgBEjEq8rECADIAgm3IAOIPNy/PHjY33ws/sLml2+NozLoSaKA2xrKcvbR7izS81jDrm6PV0VV9U7UTvvz4+PzMHmKW1MQCgXKn9YMjQMajltl31XKIBBiWFq/f9+6uNvvoKqhYmvciqjOKhMDidsanEJZlqVn8n+hR8r+zWNTtAITa8hn2JJBkACRWr9t2223b/AAaqsO1PcjvEngmYROT7I5kb3ysNurZTGL8v4R+wIiLtjfKYeGFjS+2LWmlUIGWgChJFQox0KmUuD59NyEgAAIq3yS27XXf8ABUTRJQ+54pUd4qCss93c2+yLdiWviGawjox5qMXmmewXehI6v82SV9d5oB652/9n/uPtgc/dbwlx3OSGsrpiCmj/+nAEtaMAAAIVWuGeBKACRMtcc8CUAMiQ13WcMQABDYXuZ4wgAAhKLRaLRaLv6suT/////97fk5BFzC5jfkD4fOSx2AQSFRgtp3D538jM7rchif8/VyEY58rHECqdP//ncn9FV4pxAdhBsSCQSCQX////////v/k5CzHMb8UD4fFyIQ7AIJAokLfw+d+hCFdzu5BIU/4u9lchGOd5WOHCjTp/+/ncn9Cq7inEB2gggA6vQEFDxPcpRHsM0LGHlTL+7cs6mb5Wf790VrOblkp9XZCsTvzqt2ogtTz4naEUtkGfWFGzjQTR9ZlrQ0hSnYqgRB0j9yAAe9APNPxnQ/bCm3IPRJXpdOnyEoVMspakVcZDk6cZkxgaDF8+00oRqe8XaxLZBn1hRs40EyGR1hlrQ0hXrQRTEFNA//pyBJvpAAACHzZdaMUSMEGGy40kZTYIqNeDooRzsQ2a7fSAjqgBppBfSNAoleHCrLubYTwsMZGwp0DFnBrZum1Cs3qSGa1PViDkvo+3+lD7J+Z0dPCOPIyMi3V9RNews93rXiImyHdXMgUIBLyskAgLx0NPVTx2v4wdCoFOzh08B+2Ul2otvtT09WYeS+htv9KPsn6Ojp4g49lMi3V/XsLP+teDQLkYd1PzLDTRTskaRJKOa0h5ZsbOnEmRA2beEoCgPZ/DMH/WGZ5o6PZC+8i+ZHJnnArolVaYPVmup56wQtWDydVgTO4iatz6mOeSSBAQAnY2kW23dwIwVPPHhPVb+Mrm9DB+60Xk/zwf9YZnmtHsn95F+RyZ5wK6JVWmH1mveywVasHk6rAmdxE1bn1XPalMQU1FAAD/+nAEn4MAAAIQQFep5ROgQugLTSwimgjFA2tFhKYhFhrs9LSIbABzu5qRRBN4STa4QMqmmRIUec1nJpwypMJNU7tXfXRt7L/zPIlJJTWvW3VhXbbXm8Eb/68lbiXJXq0OSU7//5lj6wIAAGhEpGnYulhJzGF8Qa8gqu6T3R4y85wfkmbkeSwZ8XxN2/N0TMz7yiuz217+Cf//64zq+LTTjIEWz6maKOQU+tcRgKNOppprnsNJcjTY/DgOGSY1ts+gatKIlH5atUr5fZlimjFv+vZMOaLLuxml8JB+v1ekzcqILNOsogY8wjKfl073fDkWAgIAAFJbQCdtcDDNW1sgn+4XjZjBtRs+gGiHRDaXo2XXt3XotFKiEtMq6Mmzez/r6D9XUiHPrgrTUjS8ivsct2VIC4o2xMQQ//pyBIgyAAGCEEBfUEMuPENJu0oNgg0IPGl7QwTrMRGQawWHlHDMof45G0k0JOYGtYjBT2yaNgGhMzM42/PTKspbNP2KfYzmVzL1p51Q9J0mfGTI/0Az10ppensNdvoZYuInfS7s0esEAMEY2ik6OwFcetuV043Cla2Ikg9GyaD0eytm0bepkqnRNAbymkPu0qX6QdXTfYvtxXrn/RX4Nvt+eb/nG7livU36IzxrjccI8pEDfcMBfEhEseSmPYc7GpY2rfRcscyCJoibERKdOqcHHqqNCdLCazqiKD0UCiPbyBSdaUOu8kO1A1s2Ae7r2SALEdbessikCJidaaIZmIHG0zPGBlQ2gSeimdh0TXGLXjtm/x9CRtDaFMpGUJriRwuwSx+1NnyZ9mcJfccREX6UxBTUUzLjk3D/+nAEE1kAAAH2Nd9owSzcQea7rCAiuYkJM3VCjF5xHZsqQYSJ4Ia2gXU242kogOwF0SUOql/v8b/l7h07eYZ8bnzzKkry26c13CnhXZTDerdEfdn1DPdJVp/3W/Ro1k32nNkNtIoBSRO3TXVwHJiRfAf+SOoML2l/X383EnbvP8zpbFS7NHkZdPWdydr/dH9Rv2wiR+sKXKQ8VJs3InzEmfzlQmQeSkulSbiaKatAyBAJO6h7KR04cer0JkH1fDjSro0RPJW20lnUqsbf+e6G80H4tvrDpx/0kp+SLfu/0//X9L3FIfKp1raGnqurIww4DRT6x55Qm8+0vdMW1P8Z3FZEok1O2iCViONnCT0Sv1Vudi2+Jr3uUkh9l22f0ZWdEbZNrSltd+yzv9hfY2R2J/KdKf/mUxBA//pyBIyAAAACCyfa0WYQqEIiuzo9IhsI0MdlR5hDYRYT7Kj2CGwEgIHJrJLpuQsiKKy5EZ1MCe4E1e2ZMfBtky4JDR0vrbede1q+CUwUU9g6hZFEVbTydVmzWMcprxMS+kjh1dWojQGCCCSajk0kNtEqehoQ06KM4Hvik/H1fbRqm30H3MYftyS6fVYFGOeOWhZE5Vyu5QsOM6dMmeUeFCDX/w6bdYx22VCCACmVxuWrhgk4dM2eYEt0WGlB8Lq+fBiQUBD70f5izmT1rbOq3udLXrodVVTIajbJDj0sV7pT3W7hZ4VzpazIKNnZn8jsDIASF2NyWTEMN96pfLMujaZMEcED+AvBj14cS4Mp2Lgn7XLm1Z/R7XvcGvw5zC46dqE6xNKJctD91hH5We3u4tRR+7Bq1MQU1FD/+nAEhr0AAAIfIVhR7CjYQ0Kqw2HlHAi013FDDELw84ctZJMkbgAACbELbctfImjhDLqu1hf8idEgtI8Y9R9hDDoO7A52CAtZ3Xqm3d1b4mOY1FVKgZvAUQRAELlkoZT2xb+tPnH/Q+okQEm5Jbm4iCsTWGiOuAkkX8AdsMexr1wmJ40NlGNAUNl6PGPQtyKfJBVqqqY+CQGghMloSnV5zfFtvkabmvVp/JHbNCIKdFJppuJRwehXPoa3G3ORMGPh3mfNm1Jk5dOTasmFSr/tURsdkDzlM0uRK3rjfgUDRQqlhFt4RdxZM4RTUfGU2JdT2FkiAtCmW3tKADazR00l/qoergInc61+T02KkNAaGVeP0UuKTTGlXCtzzq77xZKv/satFY8ygqs84FXd8v9aYgpqKZlxybgA//pyBGljAAACECbYGw8Q6ENmW7okYh+IrMtxR5xF8REZq92XiHQBtMO6VoCzO0medkqSY2B4gXGELu0bivrddxlKiog6yfR6pobH2LddPv2bXg4cuNFEyKXK0/imgl/bsPMKRF693+wAEXklG25bIwTF0s0yXBkal4PdZUZ8lOj0Jp3/Lp32e1nkWDyE8zv9M24I+LCQWQnr7NmlxBZZQq0SKJPITS85nrXdIAI3lFppursc4Gko9QjHdBviFy8nUiMpI0FGq/OlTtL/fCfXtT7YKQg3ZSZa03XBDUW+wTUi4S049jbo08Ooyu0OAm9/sAEAAU3ch3Aq59vFv4r5IGLGwq/xpucXav/mdzlwB6CfobXtr+T/6bbWS0ZJvVU/e4TOi6/WENTP5hgJDxppiDqpBbFoTEFNRQD/+nAEsx0AAAIOMtWbLBMgQoabJmGFG4ihAYOjHEPxGIxqzaeIcAAgpaC1h9eKjIBpfPtIfrKbZNzMcDIgxYXzKuCgjdhvLiGoPyUG+fRO+drdzOrftn0ZLU0/2oGpsy80glMpp9//uIWCqttcMTHzZhZImv+rJkLUcFUmMH4+oSar6Jjql+pHt3/6tdXe2epmSXT9NGsMdY8ou1N6emn6LguJEkNDodwx+gEmNSax6y3bgUsbWzKWzBk1VwscfE2LzvmWjaajwz6Ll/bVt9dXedsfO1f9HCvv/3V9Vn5gcUQcmFrZDdoSSvncpah+m4AoSjYUnGkKywRCXkJIqpzkSf0gRVrux035mRsJQCGg9HLIJqC1yinE8e9a23rpnxM9JomEnqGiGGyelv/+iQhljLU3t/0JiCmg//pyBGePAABCDTLc0ak4bEElCtdp5RwIrSltVPKAOSCZKo6ycAAI8e4W03JQwPqLGay0k74xs73GxahbI5VGKBKpi9tTMtpzv+2r05r11tU169/jg8beUeN3TzlDrnMvxj1G6qSe/0kAQDluwFIDuO+W67Gymp5S9ZkI0hTBhH/rqagZgK0aDoh1Z9VxugrpztJ+0xVmZoqeLx7VptTeODiZz91jnPd/qBFBZFotOAd2qI3MlvvGP0VEpn2EdGkA2x62OdNsgJQVxfRf9FyaNm1Wo7a91ppoPSXv/+pc2T9nWzU9zh4LLiFAntv/eKAQc2Bn6CxCzDOBlcfWwsXGOpmcm2/eSbg11lMKiyUFhjAHGRCNB01SMq8or1ajb+1fzdu2c8q2y1T6dMlDdv0VWMG9yMDT+hMQU0D/+nAEqU0AAAILB2JuGESEQaErR8YMAIjEQXg8kYABFhYvB5IgAAAQgCTEWnI63K7bv8B1Vof7l725+BA8MHAwGBxhEYKA3MJM0Zh1RLRoPMo5i82/t5e53NaXoWSe/cn//3OJnwfEAAAKJJLJkQiAYxG4/huSyMP8KY609OeKSQrtgACFAwYMGKq1gggm9GY1I0JoOZDmL3m33PUqA7v6fv3f//c4m8+Q0dQUBgKGR8ysqXSYbY/mxCO5rCyo6iMLk0hJApnGwIhoq8+TNX2KKgIXGJzI4fuz6aBzT5ldW0WH55C377l1lyJhOonXivrqBQMBQyLnkSq6TDZjfNhGdblRnZR2Q9V3VLUfkRfSy0V61d236aSsGDqCSbwyOH7s+mhTT7l1cWXnkLfv15dLk6ideKpiCmoo//pyBKRKAAACICFcywYowEPELI0UInuIHNmdooRccRaa7hmDiLgAACBgACGhgAQ2GXTRNCH3mLmUuopzCr1A7Ycf0qR5lqVbbs9rKKsVjiRTrPEwMyVVvnYtWg+7G9+AiKSY1b36clonaAElHJVFGkSUggC3dSreIPjS5rCNZsKOH+tqYTqrs9rKJYQxxIpyoNkweYdKq3zqRatB92E++gBESxMIre/N3EtDjtGsORya/O6yNuXHCxbGe1Dzq2jsrBMUJCxLv9MdpVJHvf0dxrKwqGaL1WYrJp8hUzcVfUxVmzzakrHjaaPJPG4b/1AMCBmn0EluY/UhzInFJZgor1LbGVhdsQt26+2hS5Hvf913Ll2+1tTXJZ+hqPMmBJoET2K1fex5Mk1D53eKguhsqard22KTEFNRQAD/+nAEgscAAAIaQN9p4ygoQ8gLZmEnGghw12AtMKlBFqNwqJKJpgCQ2xSprGo6egvGCu0hZ4V1Hcr1VuCcV6bJ7lud1dRVtOaVS0erL7erd/o/9RX/6f3GOSo1Wi1Yl/OnX0khw7W1iGLAQOy9oeBLJU4KsjjRGszweCnuzOeuoQi9YnDGhHRNraI1z3v9u9TTaPVl2/Vud85/8j///lB7rtiLIud+grx7iPapKgvGJgC8uivaGn9lEUSml4RPDU1MXGs6yNWajKilHcLBBIVACzI1kfhjav/EdVbk7s23fKX/pub47/dP7fHJzv/3fd7Qlf/TjbTUA4I3J+V8+9Y87HViG4CBqxo7g+5tlbBALYl9G69tUbv/3qNlv6bm9Bv/3XZaB3W9aOiwbDnO/R23X63m1yILJiCA//pyBCjAAAACFzXcUecpeEQmu8o8xQMISNdmbDClwRMm7V2ElLgMAQIgHXJRMwRM6xeSoRqQlB2tuP7lCxlA2ZlBm7aeL/7dP7tt/0SKDcy1nYYuvwXqrJySAy1i09bc951Rf3b8JWvWECKbqP+v5pkNSGlNY01RHRufjEaND/ADYayr8X/o3f9DDltIZEpOyLZ9LPlWdbaCq7S7uiGflXn9TwHtdWzuVywfsesAJySQKXceRN/a6l8WRFnFxVV5r8z9P3C8UPxOZcCvVejcCfxAF5Wejbvzuzei1Ndpuquv9BfXr898UrpQR/RtV/tIAADLbsIGvPMN/RVU4grF3B8lP5U/11P9e5rh+WgX/R9SdOMHfya2t1b1tUSspq96d9D/1pfsvEn/+j6/JRB/bH0QB+lMQU1FAAD/+nAEnF8AAAIMNd7IwylsQqa8DRQj1Yi42XFEnFERFy9wNFCLpgwGdUvq4COhhtwxn6DrQD6ZUgMM1HLgN/Q/fr0F+vtcyFKiGbo6NjU77IcnTaoviEe5RJtPt3OAT5ZOQ02REz1+oCEIMkgtRtuRSKDQtaXreghqfrxpOb+jzKKpj2yX89m0IwPRk/M91qy+SFMcaoNDDwVdSko3VU08106R992Ij121FIYQwAtuSSDDWE7Yp8wPxafOb6Jd1Q3xWMNrx39YzqNT6hUZ1AuESilcrL29OVTsG50rW1jeDBN2wptX+1iiku99XVlzf9ZMIQZQCccbdGHcPiwpdbVSqCOidvHdv1QN0DnAV8TVsdTXJ5K+WmyAQf5lA55gWPWTN8kXryy5hFy26//F+7ps/1BLPBpSYgpo//pwBEr8AAACGBzb0McpaEQpi+oIIuOImJdzQxxt0QsOq4j2ndAIAAglLZLh8EABR0uCxWoBzUJ5R9wVBSVRsJbH0EwNxTx+IFl6mnjQs1zAxFz7rWsaBmJEul+7WT+u9b3kk9GnMP+SDSJwgNtuR0chAiOSfDcfQ+4/jN/9+fpwY+nrxihJHk/Viu3+ro73QK99WVX/WZUe1+vkcxzMRDxRsvlxg4qMF5nboXWFEIRAVltpbIH2VVB6umOUsloZxOecyBhsr/Vu/+X/pspbMJZIoPQKCrkvlhoia5Cllc+aqk+yPIHx4SPGGM30p+91aNIB/4TFiWwU7q8R8L52ouYBLj6na1Zj8JY9g/nx+h8ClIYvWVE9HKIsqA47P5UZ7zlhVCuGYUIEdgmIKUe1pZ2L/fyKYgpqKP/6cgQGPwAAAh8g22kJKNhDhstnJEVkiLB1e0MUQPEWjm5oYonKAAGAAJAt2u5Rig4/asArtmkNntp2p9giPGxitp76rwL/GgatNBjSweERBiUJpF0satA8xLawC47tkX9eeFtBX47kfyRAiAnLbViLiCaf0U4e6RfMbrsJHrCjtk/245v4b/fo1Eemj70Nbl87K/dlHrCIRIm3RPXrZWZz6b31pnjDs+7yrluisaIvMhuOS0HlIqOFXoF0Xs6MDHNQXoEJROqc39RsOckWusSsa4DvZeKBzJWkrEiJIsdHstgVYsOfedCddp0qyWDVCddOKpgFCgnLbtbcTYn6Ka+06wEjlJsFOkEO1BP+2h+H8VFpWTdtGUC4IgpqLChsZKFbFGFkq1/lqBrUiYMvlWFXnHVja3YforTA//pwBAf0AAACEj/g6CMoXELjW5oY5nmIJH1kZ7DjEReObAz2LGAmIUlRJOSy31RQnm+lhHvoZo7bV8/TxThFv68R/o1EW+P1vKlrt3VUt9P+mzzlxjESKIvDbyV53S+MzTLiuhEmTWdCAGIhlNJ17oKgptF9f5WuUncf7DwYLIUd8Z/oMcKAb90VqIUdDgZyNuOOY4YVxXFOxf+qhhCSY2V+xhNinPQigOKOgNJuQMGZSOf9M6NlqihCNI3i7hDWPqtTOJH1YoLckNQ0x46Xso049nLooxZwZEDR7bTpHt0RGVErlKXW90XT6/oAhlv4ZVpWBYxJkywCBdHToTf9IJzbSfzIpj7eRBAXyafDtXgje8vvkwdbIMqaK3Boo5TTgopxrIDOnTvcAECIn9bqtzP3piCmooAAAP/6cgTLxgABAg5BXdDHE+RDx5v9HCLzyDihXmeob4EYlK1okIrSSRMJESbf+EeFlY+HMrsrcYf06y/bF/9H6f0DPZtPb0bGaLQrTmTXsU2zentaiJYrnpFxL2DojFr0rHhXU2Ktn8+hCkBFJoqSy3IsmLx9We1DOX5lYpUvExza9a7cz+gd7Z3R3OWcZkGsMvZJM0lX6/yLg27py4zzzmv5fGof2R/u5+U/b7ADydb4Yjh47XMcU1sUeOOi2pEusmEW2GItIyhVeooWXTnDbiX/Fw/5H/oF49UpAEgLELgdL3N3exbETwvb836/+oIARUSHbcIJqgA0RnZilRfnEWdNF/rfvWWm7LBt/tFHiTs/GsvXf8E/rszQpcRx1LgI2bWebYtRCeEo1/2C5IJKedW5+4BJiCmooAAA//pwBBWIAAgB8ilauSI8pEOkWwc9YmaIqP9k5hROkQ8gLeiRFaYBQSTtuBWFDJvc0gs1y4l3Umv45taVHE4Tj4gEyZOED5V9CXM9+/ZeXx6wMjWwA5J3+KJHny4XG/1ufanbhMgABSbkB2InhjxILamxzRiP2ehQx9szmo7OF0jY00gaNUGkYFSDH5OHbDl1AOfgn1uTZe5dRUaIUeVi0Li6Fhpun//WIApSXBlxoDx1KFQjXHziGED3QK1nNGOQHXO1RPsmfimyvo3O2j/3kaN3JQzf+38n0fRZGQKiFDHRHTSMuHFCxWYS996VBggQQkmnAlLQWMiBLQ/a3WMPx+Lgo8tRTVEFutI1+nAr40X4q23v6ijELYy+2u+330T7P9DsVGKMHPP/m0GjZ5mZtzyYgpqKZlxybv/6cgRingAAAhBAWhUwoAxCiZtDpggAiOTtdPhhABEbHa9rDCAChSrLPLApPZg4qrD25DEjplB2PRAgPTGhinEkdVZDnFtUXGD+Dv+M6P9e9k//Vd1lbX2MaiGmdFK1RFCb9ldy+uiWABcu4f5IBFfZNESKmuxjhk69kEUYp3g1bX6V8nN4//774OiD5u3/5tibfMiSbuqgxrBTMR6vaezdWIl48Ikgk115cAR9IRmQbG0AKh4dKb6zoZNGHOCnZHpNg2kfW3O+iu5HZ6Uz5kdGbktJkZ/v+xFc+mtmUqNUxGPFhiUcz0q92f//+Ay73pDAAACCtSKkWx2VBAbDus+nXbPM5rV03uisHaQ8mRWZXoZ9K+U7h3bey7o2TIQ6+LKtkyK53/bL1pxYYlDjP/dn///gMu96QwmA//pwBIEjAAACGyDgzyRACETEHI3giAEIaTeDpgxDoRAmsLTBiHwAATuoAHzC5GiZqTO5OL/m5VropERy72fqU0tW7Tfe9XFl5gV2NlQkWnkFklj1CZB5q8qu5NO8NnpbZXts0EfEj72ybBRak2+2kklRjGIMQaGntVZHqiI5d7P1KktW7TL7zTuLLzArip2VCRaeQWSRPUJkDQFQ8qu5NO8NhWW2V5KzQR8SPvbJgAGCX7yNEhK6GBqNJVuRW53YgZd2JV3wyF9UWkp+U1LfSlWlOXZ/z0Rrp+Tf3aXWhNd/V0//XstyZhgllGprYBTe7UAhKLb/bEkW/TgyjRKrXK3axiv+HYJ3qiF6qi0lXUpqW+lKtZS/+eiNdP03bZ2l1oTVL+rp/+vZbkzDEU1zrhUrQdelCYgpoP/6cgSdhgAIAgM128sGEMBCZruJYSIYCIEBaSywQUD9muxBphRgAAE1kEBigwRg7eB8KSrV4LPD1Jq221W5e+1+65Ut+pVLp0qyftUxUmMqdmbVuKUyrX/1vYqtzHeEiOKnJV21skACIfIKtDgwRk75CbEmiP7iZd+Nq3Bi7KIJld/a/Vcv/XLp0q39tCpQy/ZtW3FKZVr/2iryCq3Md1hIjipyVdtbJFiQBEphSRBGAgXqjNy175MJ8bQZWhX63xDZv9+I0Vv7aGq2pat9vR9X15WMesiifK1dFlLmSDG1RSsz1BX1lXdXyLJWNC0IgHo8NSPCeTD8OLmnwlobtD7RESu4RDTMtc13atE5Fon+RMWq11ard/0eqmy8rOvqO+tbsl+1/ETfaGv/9CYgpqKZlxybgAAAAAAA//pwBH44AAACADXZs0wQUENrzJ0YIhWINQFzSAyhYRKa64WknZgAkb3+BJgkaAkKDVEzh5rMpASspyYUfqdaCBO39Xuh0sRtP6dZ73ZWp9DPN09tLrBsJtnzBzfRuDKr21aPV+/rCcdWntt1skwjaZbcOalzgWqnUhR1yNl5c6LeL6dRHf9Ve+ysr9jUHQ2b36eoyp/f/yff9WdfTfb/t/sRdLVXCTbD0qAiPNMkr0oDwouHG7T0wgEW2IeSlGbJ/V+7Z1xJu/fVNNGyeiCYzlujVL/UM/p0QrssqiYxRVTnmU319uxnDvrbmB3NTeLO8VveeIzcIUBirNq8FMGsexzaKDXtygSQYUDBFI217w891Itv0I9nz2sj67LZ6sztbT7f8t1/h762M8h9XFH/SmIKaimZccm4AP/6cgSELAAIAhxY2tHpEFhCBXsDYYJKCMDZYkeMTyERKjG0MIneAACCAtNpsKU+LETdxfGDPBxG6Qaj9gUsEfIyNTdX1P26f6NV2dbpvt3a1zdKqleqB69Htpv8v/zJ0teq09H/X+4//qJhKSZKmUQIHZhSY18FHK4siKgj99FOTh/09iNL0BmTE5KoYGGXHZVBDlIg/6p+n5vqgVQSmauK9fQ2Gv9fIff7/pFlVkJACWIlMneRzmsq7GcbrrtVc4XqLGFcA/iFuxU4xYhLArb6m5US3mR6s21k9+1/3x61sJRY9u28glWXOu3rbTm5LkaAqzHJXZtbJbrrZm+Fkc+DZLTvGMiBX40qh3kI7OQ1psjfdaGM/2qhM6lW9TN/HrV1mm3/b/9p2KRD1xyyovpXg25v9y0xBTUU//pwBEBjAAACHjXauYkQWEPle1cxJRsISNl1IxRMMRGmbiiTFFQBgLTckpAComBo9Atw/Frlj2wT7Lwd6E0XYu6dCvher9u2qvr6v9FC+1Na3PSUCAFQRiUkfWSZs63oIvIMfTr5/X20JILC5JHMQANNB8aWhR6Iv0mn9J1yCVamSoCBsYztWn4olPq2t0NdWVLKQ1fo4icWfbYRLRnL1OyX1Ws3ob6ksd/gXPCgULUq91aTaMPzJMdD0N1QrQOm5XGo+hvpTxX8GPo7bq0jvWnL7qiDrujtZZzr1C3xe5RRaRT9q4nSLKR2NuoDUU4uEALkeu2v7SFkOK28BrNS8YfOtau2zOMOzKtKeBtDeD9P5lKbtcl9ZCoXN6VOemYVf//rev/6NUi86jmc5l0Ujohc/+RTEFNRQP/6cgQCWQAAghEz2JnsENBAZsuZJMUviM0zf6CgQXEerG1YkRWmICk22RayV5Cz7kPQPpiLxfLH4hfcAPwRxMEBvQT3pR9RPXq/8npvTr7FUz7/XpGoO0j0zJigh6R4RY3I3+lGlyvJAgC9N1Vpq0J5pluJxXYr8qv4bYXiaPKBXjESmjI3EfvGga+pVR9r7W7fVdqfXR60FrawpJcJd29GRS9X/NbcvoJaCbcTTkkks3IMqbrfDK8ETclKXoOagv6UfkfTzcXqu63RXIun0T308hnJSh75MvxDI8srFfTVGehSyJ5h2+1ABwE75KneQKVbdFcAGrnXiCmfTToLhtKO9A2gvXRkPuFGoYmNDGeqb873R5a/syv/0S6Ku9NDTdLb1//1fd1e+QYZult+avQUP6wDRUG0xBTQ//pwBDv/AAACGU1cUKEV7EJjivc8xXYIvHNcZ6TswRea7Fz0lGRJAaCEkknCCDhEVRHDzj40a8YW5S0lpUeO0/k8k8SyWZf5AX/ui5/2SLea+tL1hCW8/P01tTMCUV0SpYNvUBbX66sCkAYUktoevTcB9vKUjF7Ykf7lXF7ct+z55XAk47HDGYxsvsobxA6RjY0+FTjHh0Gka9c9xiJAnOKlUN5UpZ7PFHJ+wAmTbZWMqlC+W8HpODNVpYOfJvn3Rw39xB6egaFeAeJjdevR0dQ983jhaV2noEUeSy8fura1stcQak4VpGh5Hucatu/QzSBAAtOy5IpBWi7NqAdYAWUHsqboDY5oxxjg+DlxfY9akvO/bKIc+7Py7abp12yfnQq3U9BFoFE50sduu/GKImkRGhP09HqTAP/6cgTMrgAAAgs120knE+xB45tXJGVLiHkBbUGkQVEejmwM9Z2aDAKBqqrUlAUonrcHKQ/nHxpLJkd0RpVidCCZ9T0IHHKEha9s4YrTpz2UyXooOmgV7L+bQumFdS/npkX0btXT8p1EpIBSScXdYBUqSgD2hbQI+J7J5yCTBgQnt9duRu3DOqHw8cYTt1vbrUQ0i99EvmGfW4uWEzTZ1CSJsYJ3aharb2oCAIQhOS7YFMFTNfAvT/QPbMB4Po7EqyZv9tAvvxtl5+jNtrOojyJv9WfNa3yHX1w1jIgJw+hDrxLGrWbASDkf8sXd6yClJJIyudhxOpcrBM7Kr6RVuDfkilPMRLF2hBsLN3VfjdNOODsAILUGLhqAAFWHCwGAagVcw7KFEiXgCqz3LsF1PEzRBf34smIKaigA//pwBNUnAAACDxpYUeVDFD6kayowwmiJAGtg57DjURonbeiSiZoIAAQEi5IJFZQcLapWuIWB6rPCI0sYNhb+oHknMSSO5av+bx0qMFrrGMUdY9jpDruk3NT1CAcxVSBvYx3dqZqpn9YAAEMEOSUadaBF9DKbqBEomAVOfkvy47zXS0CANTBgzprtye2j//iE0CrQlBYIDNYvY4iO2otf/+pSCIBfrtIQCi3LRBPGESZHsMQMnmZ0eg22Et5UvuEJM5lTU1uhp7h1zxeuK2WH1etgGOmUGoQB91ubqILNLFn6GmzQCZJ1pNVWieHroUCAIwJy3biB+g0Ko30Wt+mnPFdXZw1qUDX117oJvMXBtmEUteTWHvZ52jpRp766+pN0oV6h5vZNW3oiG/6drHCFuDeH3GFJTEFNRf/6cgQCZQAAAgdOWj0koARBxQtWpIgBiPWVcnhjgBEfsq+fBqACIIQlJbhNNUGX14BWFdMRjlW9kAR8gg4nOR8v+11EDbdh8305zys6I9BXRql/+iJV6rxRF/m7f9/p+RMhonHvA9YBhG7xbWAJZSqBW0oKEefvoIDnVkrUM+EyaQQs7zpr0Pf6bKm1COhBM4kNvOn0nihHfOYiosjSS+7KLQGz82s8sNEAAAQCAQCA20HdZlnJNpGBDA7pO6OzMyM3/6tSv//////0PKGCQv//9hvLULj7////Mh4BkSLkzv////yxpUbA8EsYEk6cVcCehBACigYDAVjMUZ6w0zk1qOdtXsYzf/rp+mv//837f2VGFj///YV0HDjB0V3///+PFDoBCF5PFs7////9jSo+FQblhqdQq6YA//pwBBcDAAAB7ABhbwRAAECknArhCAAJNG1/IJhgwR6FMKgRiChKQgIoElFOTCokCwLl3icCNE4neTKDSjviMQVnykhIswQt4IBgoc8Ry6tYfoLtB/6HZcU//xObWQ1h4QRhyOEBAASU5V2HRs50u9XV1O9XkEK5G/6uh0I2p8mjaP+EES+HwACDgQ9R9Yf4nfhcuf+Y4OHP/VUsPnlDcTjDiBAAEAAALUUQRPSNMTuAqxkYApejTNxWqNiYUh8ZlP6pYyqTFSASJCocSeCgUIIe1qXPcgKPWb3pdNWpVsvEpSpDl914gy4w/VUSpgCxAkFNyRMINWVHOAlN2gAliqqXjDwSUlQddEt3FUBIkxCXsBoOIedCaXPcVCj3m95lyjTGm1eeEpSWQZ33TYgy4wPKVFiVKYgpoP/6cAS32AAA8foOXqnoGNBCwxuwBegGCNA9dqCkwAENDK7AxIxoIBI+bxhVZQwZAx+55IBE87HciGBUNHnunXEDo1ovcOOJChyDByo/WpIVPSpejOUxWXWPmn9Oixil9Wne5n9CrKoRuMyKZZoxGQfVNYA82VOc+orKiLTadX6wttRMmF1txBDpCBhBgd8kKgYTKS4+l0oKi49QbKNbSZ/3qsb2b9H/kAKACSD4JQgBBnSrCm/AgsvzmTeS6NPguRDIUEQIiwPA8ASzUgsxCyDWB1oplh5kcqvGrF5ugZsahdyKk8obfQllNZi1WQ6/umBkaHUnTez2rzpjFKjEpkVIEL6XYZHtsi7QUPAKWpEwrWQaeS0UyY8yaGJXWNtvXGNqaUuOoSpKMobeqcZTW75DrTEFNRQAAAD/+nIELucAAoHhCV0pJligQUgLpSxiegkEgWwGDM8BH5Au9GUVgLDAu9FoI3B4yDTmpzDQaGMWfFhEJY9qR45gIhU4e1Fjy6mLVGJSx4BHVUC1GfRpxVT+ds/yOSazV3TKiIBAfe0lBeLTLKJJ7hX0R5U1jGX5MKU97cmPeTQqqZr60Rza9gXpsl9Ee1/TSqNt52t9/RYbwlI2fURyTWau6lRFnxHYyP2RXVeUm5xR8jzBZyOFCm2nJROyoPWkKkbGG5OO83tV5hSFEg4YNHUTK0ETudkQos2TKsQ1IbvDT9T3NqXs71JosT9OkCAohJJOd3SnRatKQ7K2wTgZLUKXlREUj1vfuWTWu6O8rCoFLIAt7kQEVWl21TAot4mK+G44ltShGqRsz4dUmtzElhe1RPhxKYgpqKAAAP/6cATzCQAEAhkY2zGPGwA+IxtmMgNgCNRjaGWMToEYiq+0YJzMgAiq1auX5FcHVdCug4ncm7KViKYP2BeaKUYXewzL4xhgbwMwbc1a3TAiHSJuLhoAqHmINDr3Iq9dmuz92tH5FvT0RwBABFrvbI0dwVfsnO5ZWWmADkTk/DhZ0ayCr/6r6SizY+Sc6KrGIIkcMr2oryIixdDJ7rs/f9td+3PN0lk7QASnHG5iOEXB9C6BGHxov2QNgCNHAWSmOUoSq93Ze970FUXzB1y1qpWgHqod2hoYE3NKtY8XqHanovRVK2JRppZqkT2/fzNFCAFccltu3U/DGOXqQq+A5HFBiXs6mXxq9VuPPEoddqrvPWg5e0Jc9GMDWJSyTpVxhzwXj4iGzsjDopQp633v9qi09kWuTEFNRQD/+nIEVtwABAIJD9mQz0lAQkR7NjzHdAis12tHpEsA+wVv6DCYnga/9VCAkPRMDWel2Mnnu5+BoPWyGunele9VQ+8o5DXjkgMSbNLRy0PSOTNAEY5MmftoEFjxtZ1n1Jsda729/T01wAAL/3V2BOrOoB95UxHaXcLDY9ADNuA+f9F4g8VxAzLzTa3Yxd+/dfzI45o4+NJCNY4XyxJrYhddHXf7xRDf9t79MAAKTktsWr5HwMkAfSLsx9pmukNyrC/he5sc587RZ8lXKZMOG5N/TpWlPTa/Rza669Xe2O6+9B4IlZ2tanOu/oaxfb+6SiATsotJJRChi8nMc2uxS7BRJBcKuQt5J0e8vycXQAFEVGUMITtRHZJE1hsnYYGovlWmKvf/V7lkYu9eUalRVMQU1FMy45NwAAAAAP/6cAREjAAAAhIZXbgIODhEAxwKFCOFiHRxcOCkQaEHmi1clpUQAC0tfbeScuExmpTJNcEMzWlZrqqigyvINV1YYqaUSFwhEoZOgOaTY0MB4BmRouLFrkt66199+wnWYZAuSuqRhq3lslEqSTbTcRlc0ZVjVfcDs4UbkzobDg1filnwA7MhhQGgmtwqpAkFgteduCrAyUiK6t0RPPGlFS7muxxdOTtvYNQtzPogIQ25JdJMIT8g5pYI9QnyxEFRcVrRklu8BdF664IDf4l1aoNMB48RohoLlLF2Qg1xCXMJ+UfbooUKBW9VTHVIb3WW0AEBLk222iEMHuQZEsJBln8ngdDsDh1bjKSmd5HqXEZV3A/6v6vXRd2ZHsj9NK395HIzqMLGJZCvru6jSMV9OnZ/oTEFNRQAAAD/+nIENYAAAMICGODQoSu8QqObUz0lcgjMYWhjvENBEYxsTPMV0NhkeLUcblrKarVA9D6BtnI0U5qosuoGRiti3a66PIlXF10hrPqY5rSzDoqy+9YZjFEeotp3tNNHMdeXZu1VMdqCcmu/v7I1/4USzGo9wGb1iiz1MZglepNlHPnapZgk2KtAlFLja3EUUzi5eOFblV2izD7rXMV1J/NNnBz0c2j92/fQAXLf/eDXDInMtFyZXXnc8AwRahIgE1WRjXzuJT1GRqwd0RnFtVYFScM0Gkhkgci66r5iTRaUek0ONxqE1rU9Lt6vNM/qUoAFN2iPpIHmrtTLMM5SDZPQ4poCVAnHIAAPcoFKfnBN4Kl9hrsrUGGBaQwIKVCw8p6nucm2KOp06knTzk+8WfZDybvrWmIKaigAAP/6cAQwOAAMAgEY2ZnhHDBDijwaFCP1iJxjYGfhDEEMjGyM8xXQAjl2Ax2NLSyj3QcE/CIiphXbaiYgnI0F/gdGgxtlI/osMYmHz4gDyiqTcTjXl45TgMpr7ZEDgpTWp2xy0kUo9OMC9lOSN3O12n0aj0gORQky2skwvzfo+yqB+zztT798WcoijKqEs0pBfPnvLDl/xeffX8vOcl2I/alsNHiqvgGVYq5IIKjtE856p4+7l4FEuC3FXYoFrVNBQLTP36WuZ0NzsXuF3rjpiWuR3QUSSh1V67BpwnpUISQPHGV0IaiLu7Xvodfev/6wA3bv9bXzmV+HtnzSedkwr6tS2CYXovCinL5U2StEWq97vawE7FR4BkGPuZKoAyTMyu11mrnRRzSe7Xfa3ZcQW53/cmIKaigAAAD/+nIEz9MADAIHGNmZ4xOQQYNMLRwph4jMY1xsJQ6BGZ4t6JCKIgBJt+L6ZBUskAbCWjlmQ7Z8TeUfR9uOCZ+3UD3iHquKSjTAdk4VGtqErR4SeaFZNYeAs/cWfVW6ieQ5xKnQs6vRAmCVE05JZLzdjq6pTnBGjDs7NmY1ycx4/lSDedKpzOkFAILl6ZNYmGHkzl4RQczd4i0W/2zwJ+p0so0cDrsjK21AAJ20ZYtKTDeWaLDm8h1YiVl5hTidelS4NGGWMiYvkGp2RoN5AXTLrUFi4ytQDEmWIXRQoVEpt63uYhniIBI1mE06jVn2LQACABNyXbbOFTHr0IEPWY29BQzcmwWTm96nwc2EFF1VWqV2G20arVf/TQu26yc2/e345I+Io6eantDJAXctsJLlT4ajfTWmIKaigP/6cARbbwAMAf4lWbgsEHRBwrs3MMJ0iGzJaGMwRVEVkqxc8wnSAIAEnJADURi3JKGB8Iwe5YsahlcCSlnbD4ZkhyPhFq+rX1L+StmZX75hoMirULClyKSxgA0KQHFTLJoRs/r/tAIASnJRTngqT43j5sRvuVJQ+Cug8GX4Oyn2FNrXh2vVZDLfPDHNHmdinEGuKTQCe5wHRwIlMslPVQfEw8MU5y4ioAlyWhWiwGdI9kWEUlGU7vIZiGz8Tt2w7LcQ+DNvva8oA33+31sQYgNtl3lSCLfuxCNBnM1+GjgCDpq8ygM+RdseqhcAAAU3IHGCuzLPK97vSYqDR7tzhDYTcyCnR+Hql6GHGnNVsbvh/3qv01NQRBuRrWIXHS1hdy79up4bNnjT3pJJ7JjtKVrTEFNRTMuOTcD/+nIEoTsAAAHqNds9MOAEP6MbuqMUAYjdNXIYkoABJKauQxJwAIAApOW0RShiQo0NYmC41yHFMJ4WdL5rpmceaY9VVkePA5PnNt6fpVTFKmWX867epnz3z6EbQwfNUF7l0/uQRBIJNNuB+WQQ5tsYXy7zwARQp2fXiDu1Gk5y0eUBOZE9qLI0kpqJltqzhVAnU9Dz6HkFiVcNLiP9zTD6EeeXQlOEiSMwVAU0PtIIKis2VHsSHWS59nYvfdtEfmfo5BZf/0Bii5BP//QBgQPqoH///D4xpKCh////WeUXBwHWf/6AiPCzCisBk4OFkM7KtpmmAcYEROXEtw2XLnmS7u5quqaPcrUxPOfpMOX/9CRpMwf//0GwsG6qN///x8o1Mw////1nzSYOA6z//QEYWYUUmIKaigAAAP/6cAS63AAAAgkSYG8MYABEJCv94wgACDBJdSMFDkEKiS5UZB2IiAlIs0rSSKR1x9yNwos6adYlI4XBcPO8xkARw7LHSVKWwgUTZtlBQOGRE4VZRU/DYqre1/4uim4d00Me9d6ezdVAC+U8pE0UUu+r2425VT63YJeEgmYI+yHq5bPo/5mq1VNOs8AMDhdTNtSg5IuXoqfhsVVva/8XRTOgTpoY96708R0uUqAAHaAAHZLVhAUuW3XSN7jMVJsf8Ur0D5q2kTLZ1VfeAizw2RH1400imKv3HmomkqHYq7vpj2vQFNWkjxrSKdbnoANwPcDuAQ6kWXvWbsEXslpxfZnPhfelKlNUeW3eAizw2RH1400hiYqP3Hmomkiw7FXbnrTHtNAEKatId406RTrOvQmIKaimZccm4AD/+nIE0+UAAAIVNdy5IRYwQ8a7rSQixgiFAWhEmE7BDRstCPGV0IRqIkktbdn+NDFS3ap5rUjP9+vtPuGZW8hnePu3Xv6Exa/M2LXX+vCinPy+9Ynr6duu1r0tQocWJKF8DEg7KnbvZIgACAplxlJJv3Eewsp1vBSnmum3nO3V7T7r+L+Du3j7t2++MTFr/Ytdb+vNFDD5fesT19O3Xa15OiOTVwM18qdu0NZETBDU/hRo2Ixg9doMOEYQiHgTmkPH09eRDr7FjnBD0dX6dE0f6qXImVVm9ft/0M8/oK/+jqX2GXvx5hWIxvq+pPnsDRjgmqqfLArGc5046l3FmwzoHMUkhkIePh2LjFDYW9nza7AJRESjfWWpH1ZZv+wqzs3zGefxg/8iS0L+ParEY31fp9mqMTEFNRQAAP/6cARTPgAAAgpA38hhE4xDSAt6LSUqCEjXc0MI8cEXJG60YJYMwXS4n5rFNH65WCTmvR1ZqzhR8in0wlO69/0HpsxZ8hb1Zc7G62+vTt4J/6emx5TC3uPJ1AVJNDEdyjC8QP00h2YQABIKWyW1fJCuKJo8vkn1M/6G605S40iYgcfXJy3XYB7WzC+9lZT0Rr16dXfL0f/30H/1f+fi+ntAlj1Tu/9lnPGulAAKA9tv99Y4IwQfhmrZo1gMRmF8uj7TvLpReoyBwBdsjv0RmvZ/ZP9UNVSU6Jp48GtZc6tFcF/qqThH8Z1P3WNsrwAACjQklssyR1IYQJqM+Q3Q9AW89QdLAJR8uxLpq9w5yPm+jEuvOltvdkn5PtzM2gb76P9CKuHTt7t7GiPrIfFOLB/yobTEFNRQAAD/+nIE9SUACAIVGVoZ4zPAQqV8LRRidYg012+khFIBFBssqPCO4Am3JbfmMcMdTPbp+WA9xEjWgjaign2z4asHHzPXlL5L4ExJi1tCj52h0diIqNdeO9yKScSOMRl92znVH/yVaif1qLRgFJysuRxtxiwHjFvc6OD4HmFrzbhkx3p2v7rP1HqXtQhW9kt/uyKMaCqhzMjFh2Wzi3IEf1FDlazZDnlKqiF+6KKArAruu22cbakXzTGHpWyY5wMrn3KJNWnmiR4R5nmswaqbb0Zt6k0h+jO+7V6q+vzc/xHe9Ru+6u3c5jKejHW019QAAQAFNxyUwiT+hE7EdVZPYrI8kqQimkI+IlKT7lnz4ryPv4m7VnRbHX3+nZKLv+zmWSD8PXOXo3Y1rF//LckP/T32+5aYgpqKZlxybv/6cARL1wAIAfkTWThvSGBDBXsjPMVmCJyvaGYY66EckW20lBVsAICC5HINRlsKUTrNEOpXIWwr7pX9TB6pbLSafnlkUiz+BzHS2VOpUZ2x1zOtIzEh/lEw1oI//tRXWO7tut/1hpOSSTUTJ0qkHCRs2x7XVsHDUYbQLZkdKX83DMUL4wMiBVokZVwgm09n9V33da0/vQTNKTW3rvXz0eTuR7dUZ/O/94iTbbnaSQy0pfwcpTJoSUDc8PMX21YFk3NCP1hmER1aZf3Cfc56Ml23q6n6nf9TkUeeEq0S+rXzToFJfXdFJI9/w45XkoAABESm3LJaZfrUyQ0HQvR/fB5jjViKi/HbI5Q6Cg1F1HHaMZVCet2qn33ioFFiUtjD5EYbvicnLaFO1z7G2dzGqi1/vTlXJiCmooD/+nIEHzsAAAIOGNzowjPIP0MbzQUiBIfAY3MgJKHxHZruJBYIPmCAAaEJrrrv4MpLX0CgMa+o8sUGYC3dBkVZsO0M3R0fTwJJ3FjajinIPwo5UNse1/plnS7H06ms+MOi+Sa70s/1IYABAUIjltt0NXIPKPGiC4moo0dEFCwQ1bp2meXgtAVrzNKGFAiKtRIqnQwJJFymLY0kvR9Vlmv9jf+Z5gaHrAAAiaqaw8bhcdXkul5v/Es1z93dAQrHERPAXtFnmR8CsLybStyxUYpT2IsTD2K0vr/NZOf9wQeyPJN+hOKOgACQmqrjsbRPhxY6yubqqdPayX4buCG3oNUaJJFFdwTVC6r65dWlq19s3yTI8/8u6quAiwcHAFy0PaWcyzVVvLimL0OQuoBJiCmopmXHJuAAAAAAAP/6cARppgAAYhg2XtBhHcxDYbtqMQM0iFR1bmAkoRDpDm0MwwjaBwSlNJuOXwSKQZioq+I4Z0TK5wv6Y+G/oBI3Lzz8tdwgN+XfGFyU/A7FN0xmFSQOlXZ9a5n6bpwT/SbPH7nXVMqXFIAAABSckt3IB8hC0LjQM5xe4iXAvSV8EMKPIXHu5gcWtMdYljQF5AOXhEWiiUPl3tmEelCKdV7DaCZBlp1Q5vK73qJhJyS7LBF9FO1Y4pQA6gfJXRqSjHrl3zr10XERRHB5Lg29o8yhzS8Y7n4kuJ7udtCtx0Y6SQ8tPG3qeapgZhb8cMWNckH7AhqkADwYGSEAjHBfkNguMCZ0wJtrFyXWjAkerqwhhdzUChsUQ920TT76RizjRYerxlakGP28khMQU1FMy45NwAAAAAAAAAD/+nAE/RYAAAIBK11Q4R6EQugcHQgiu4iwq2hnjK7RFBvuHJGI1ggRQDbtu3x8sNpSqCKflJULY+y6NTO1eU0dX54z+aPDrchXLp8KqWtBAwUi+puR31WqGAgukbi/pHP+zUx3PNXAAUmk01JZb32grPOdDJYPnazI5UYobJi48+f8Lw63h01+oezIqXWangmsv3PLakY0s61RVuhxQlna1tTLLirYViuVBRclt3ks2V1TbqNk1tS9DfopT0e3DDTKjjaD5GsytygRtKN777HdmsqvfRnGQIqRWL1sJJyt8UNhZZENKUnOqWyn7FckiBgRSbbnuaPvBUzdkhibBNXt0spsM1HsALB7ButEdrJqy9mdrb9VDVJudjeucyqsUxB5Uq9gIFVFmoTTVOi6ZBv7NP7OlMQU1FAA//pyBO/NAAACEBlYGC9IYEMmu7ogQ3+IbKlzRIR2sRuVLejBlO4JKXcCMEKM5+xmelVC2lwW2uOLVc7EbryZi783Wdy12aByXad9y2b9US4qHjdybTBaxd5VDfbOOnD6pNC6DB1NXUrAAlSk3HIJ5IsMhnTp4k+4G6d7VZWeEaZ5z2V7hfZ7fj1ajKmm2/AvTlmjFSMjNOTxi9i3uJoFhoKh+x0som+0L/TJyAAuUk23BPoTWpndlTMPpj0P5as7+p2oh2C+dFnSNlR5POkJryzRMVhKt6d/HUfUqzKPQcapIiLAR0upee98vFD+2UQADEFJNOD+1Lx6HaC8FiIoB4pgoENc61lthIdJV6Do9UM9dkOTajpa7eu/x6hEDQsoI0E3ZJRFnlz51ymeBgULlD79IwXAiYgpqKD/+nAEawUADIIDI1s5IRxUPiMbRz0iOIjk4W50w4AxI5Crzp6wAgChNSXYfg4PsKJnyvH26kR0mRZFSB2bGVBzVKnUJs6Q0xM/5qgyAaAyVnVqNsLuxz8kLSdCQYbBw2XW1FjUYBMgECSclwtmyu0NhtNtm5CtIoM+Jm7VxdRcFYuCGrUZAS9AlyRM2qSF1ylyLBAKXk/leltrrBpI+nTpKucpvoBSTbg6gmPamWI0K7SyD12QonVnVau2xTXWs5G1PDXWURzqvtVDlf117HnJc+2+62VbZxRZNCsmWcKFBBhe5TKAgoiPzumVUASm5A8ohhyqsTYDAG+dWTpNB9AHqePI9efMX0aDOXS0+ztaH81bHfOV331V06O+K1RjRdZJeDwwaali+eSq3aqwg4e0Xe1WUqTEFNRQ//pyBMZyAAACJ2JeBiSgAjqGC9DGFAAJREl7PPGAAReWMXeMIAQ5hxCcPPxVsSCZNfLFlq4UJ+9Bcn+iH/tJdil/ydClKRDf2ISTDqCZSoX/k/3FUvYSL//8+97uokLGdE/////us4utPIGNyn6J+0WmhGZzvAg7pEAtM9yDRn+cyt9vVrf5OhZSIb+xCSZUGlOnfOeEkxD/l95ssDSmf/9a3iqigAAH/KoFUwOCOrWWuZtQnz5e9rZZvRcjLY+wUxtJZB88dCbOgaeQLTWyUEJkeROixWKtGAi9x4qp+LdEdaI0sWY2HbTE8ASvfu6AAglZ//LIklygjZnHztRyQ2KlDZFs7InQMbQ02f5WN77sym2VN29PIhWHHkTosVj6gRzxW98W6L7RGlizGImoaYngCV79yYgpqKD/+nAE8OYAAAIUNd3J4yigRENMfQjCKYiI2XeHmEMhAw0tAYGJoAACvuplZRdhwsgTWLHeBy4to6Fse9y2P7Ub/C/m7NIyObbMuta6E3/s6pe8XlpWRDX89UoUCh5wlrdovsJC7rexAlAKbVtrjaRJUMJpZi6388MuJaRXVkcWfZvS1Wf+PUWocGCJ5EK8+oMmUVoHnnvPz1qgK7V9bBQRCJBUJyXcmqNDVfVGtAABAjf+KCb4Yq5e5k0hmY4BTqzMO6syu7bTM9SI2s2juUe7z8yMHLL33+/RCl2L8lHbRQCeVrSnETvpLWZ3TkQqdwVpFQgTrLYLltidlF7uWVKQnXC1BFlLlZV9VcIfmazTaO4z7JaHitN956KArAOUssOPV0pxE7TpLWZ3IokQqdwVp70xBTUUAAAA//pyBIj+AAjCFkBaEy8oUDsmyzJkInQI3QFm7DxBgRqgLEmTCYADi/C6oHeXFmZTqRXNGPGag7oR6Sq6q16eNbXYt9r4U6y22XdSt03T/p0b/nZWeK1+n0T40YnWp7l0Flb63UdvuCusCRqCq59hOk0DKb5WtZ0taqQpw3Uzt6GFo2i0QGju9GX2vj/b75f7p//M3/L6Cf5HWr48lmEq8q7/2iogBE1JDA0X2wwIKcM6AeN4bi3iPrPwrSoGdUbe1JEyXV1faTM3nSz0FPRnt+n8+qyre2X0Ef/RrGPuC1bmJeYgr6bekM/MOA9bzKPDY2d3ZfJL1Bqxak2xRMmPpxtDVM28ZzhSirJMHKz7S4jynreZtE5L9L37+7fof0b//ou4J3uokno9Nya4ZttjHN0JiCmopmXHJuD/+nAEROcACIH6NlozDBB0QqZbJmRlZAiYd2ZnjExBE5BsaYMVkACAVVQ5YFCNk2LhOJh5aVzIwR959lm8fTP3Me/SujS5+/uWUzmRW1drr9jg0XT69H2QH7Yg6xlvYhPf9rOn+oBwH1+OWAIqXw5Yp9yiT6xi3HkKExsFY5q3MJtQwx2e03o02/uXdEER9bsxF0RXqnjP/4s2KxWrkz8l6VX+T+z3794AjjkgNoQlJOMKXKEPbbYW4cyocKdyqk8G+xSWqwyqNTfAX3cNJHgYWRcNYMEjWkCEVSoxVaWi3W1rEHf9VKezdwZf/1AQAGinIWeLgrVeOfm+ztffu7KhhQ8pTh897ZDceEXzojNaVm01yfR7NUc67AVWVr8lpeumyhiyXqoDzHkZPnbnIgy9XpTEFNRQAAAA//pyBAWsAACCGCxb0YMTGEGlewJlImQI5TOFoRhaMQqWLJz8CUAAAdURZHHUMEoP7i+45WWtB93UAQg0YvKr0z22H239H7Kx2JVlSZu2u9nSgJp0A2wItb+KVDPmeSQsvf/FNL3WsnXqA6/2qlnmVv9SQxa7BVfTtSQYihpN4slctiZ8LR6Oj+wsxNB+wpuR2NuMeXf/3Twi7s2VFbX71KPenxkGNJ3+z/9hSEQejbekblKxbotCQcr4UWLDiUaruR+oW+xKcnhPWltEeSn4/v5JUu2VlbafrwZXLKJt/5667fN61ounJGa+RfWuszrGVEUAhyO2n6fRXQbN1aK+20TQDLkLBQgO6qkbdQQ+6BXVcQ7L/ZupyWsbdGkZMleTzDWYoo/c6/lVj1mryPXbs/r7/o1JiCmooAD/+nAEGN8ACAIgLF1RARWcQQQL7SCik4hMo2JnhE4BE5pu6MGVlgAA5RMSJTEmbSyNOXsZAGV20sUkPzeMvqiBJeLHuQaxLIznHVVFiDK17+/tdHBtY69bbt6+rVltCbU3mF9R6bw05rOPAKIETTaraTgy0uXWq6jsNHKKTrUPMu6PZjAQG2QexlrfduwVhaQrn2r486haSTiOpZtQw/3/Xq6ILUB2vRZM1hoANyXZmnJriE22OJsbXS7uLff3V4FSC4M0M6WMCpxiruP9tFX1PR2X7/B0rcgdXh5K7fQIdn3CjwpWQYKbplOjVlUicDyWmmkp8HdOv3g+99FZFzFoiCMe16PucK2SAMrYtLiraKfui/an+vxrO1DddStQebpi5K5lXObo9sva75Lprc5QZWcTEFNRQAAA//pyBE9YAACCEDTd0MkSjEKGmwNlJWgIwNNxILCjsQEabBzMnRAEVdSjGknHrNcQVVs8WJGrBJQg3V96lV2ZqAW2S+vSDZWWWR2dJ66anH9e381HqEtKX9v+5homxZ5j+SQ3Cjq3e2oASO22Mw820K+Yt5Vfjz+R8lrSBSQgpLJam/74pPrMVazOEvjps/nVj7qavqn1x/jW1/vZ0iChZC1ku2z4y3X/o1DABB0V/qzG3li5nfH+A85riX4IOjmXR6uwTZ+Ivogn/uwi3IhUU8qoWtWuMBiXRKmTaqixYszw6ThjRcSbPZyp9mUA3/9esgAcVlDiUfhhANTFGedWLSkgnUnSbd1XRSpCeyFC93is90uFX8/xz2Ktstr3a/+j+v/z3JPlStCXh7b/vpvUj6UxBTUUzLjk3AD/+nAEjCQAAAH0NNjRODqAQmOrihnnV4j1a3+hlFExEplr3MwdSAAAgAEttwOtETa4HgsGGlxQVmRCV5Ug6lEoR3iYv0HX8pvkv9XVjrorXnPTZtJTep70VvZ2Wzy6F9rbPu3+kBAaEU0ilGGov7RhuPmxQm3qNCpamKuvXTkSg0D1qgU9Sl6KZq4+AnQC4s7fDDbfbIXvbrrRq2aW357+rUrI1sUS0lAohyJt2SS62J0ypFMSZPZoQUQvTRYU7cJ0dYJ7dUClZMHlZE0S1cMN1R7/54C7qab//fyF8Fbo96LtieX9+6JfulsoJ9VuoAgAblu1UAazJaHKdcjoCA0SpDApFZpoizSooGGOaClFoBz1sD/0I0zm8rt/sZ/0LVuu+3nLLRbFNtj85Vq05Xb9n1f1JiCmooAA//pyBMvHAACCF1dc0Cgo/EPliuMzB0QIxWtvQzyqMQgWbCjMFRAAgvkU2UnUqYY9IQPeAHVeQ5c2IUo+EGq7u9MQ8gijZ68tWr7o9Mx+iN6f/szMv0+n2/9Pp9t8//9tF0jhFIgxRpoaAI7JasC0MhLa0ezYzxuLP5gdsRSxvP8rGxUelmBQhtB+18HnsT81OprrRzHvvbR/Q1noIjpJMtedWNerVo+RyGzf/6BABqJLTSUApXuUb/ZUZKOthFIgyOiXCQJWpczQm/hv/qj9W6p8f6F2USHLnXo6GFkdSX0rz9P5/S/OtM/r//9fR2k3Gu/QQAIs135mHYGDLCRkbyG6zoSQqkfqlt3ckSDY3EgzHQiL+A6e/v7M7k1Znqnq/x9JRYcldZou8l8/t9WX09NH25/WmIKaigD/+nAERGEACIIVNVmZhxNoP8abSTxnd4iZbXFUYQAxFaLtpphwBipW7ty9dPVcX0vrZLL1w9N2jz33pqWq5gmRyrKKuaVCz2jrn7EY6OxFrt6/R/VvT9QSpGvLulKy2H9mG2Wbv6tO0NgAFANKNr7c9cdWgR5+sK/AfEWPdsOAQmj99xulAAerQID6yo0rkW6lL+vm+v0fx1v//LJt3f1OofKZR1vzmz1iySW0k7qU7MvrMubAW4zshZ+0ONnOOREoF8o3m+rJz1e4u1QZGZqP6t5fvUlRpdfv9v/t6fX0/t1+dqMwyX2COFW17gwMpPbfC8bR39h++L4rjkHixw9Ssqd4rFjM0eSOSAHseZNLef8oZui/J+pnoSbY596/erU21+d/9/RvKZcIiQxor2NdO6q0xBTUUAAA//pyBEtsAAACJEHfVgxABEQhG+rDCACItYeAWJEACPykb8cSIAIAFDpmpya3bXb7YDiw5gvU1kwuY5pklkCZ5aTVqyT9gVZwtrkldWMxWs5bO19JldqSTpQmv9GpSmk9a2lgwEneOvynttAAAmRVjdk1u01gFjWls7M86L4zTktEB46q+y1rAdeLISdOx73LmbkllKdigYLvJklrS08zL366e/tcXnP/+DgnB95woASUUUVXy0hvxtGTNpdvrQ2h2/In/y/t+n+djuQhTq038QYIR91CoyP/yE10ZJ1WdSf/fqcQrtciZVQs////n/qhGcbjEdJJK/U1Bvp62TNp9Lk2VaDbvkRP/X8jOy6P/Ox3IELXN/EGACPvLZH/5Ca6E3VdW/+/nEKHoQhMgX/+3qYaxOmIKaigAAD/+nAEDrcAAAIbB97PPEAAQqI73eeIAAh02YekBFcRBgqutPSIaAAC++pQILGHWjfGgQ5s1rWkwiKuhqt9VtBgd2i9ck0QCgo+xtQKxVFxG5ZI8dEolKFd/h5m12ylQisQ2owjTi52YF1AAACuWtpEkiCxhXo3woEs2c1ermtLYK3GJwq8gIVlEOHLTa2uSkBRj7NQTiqNly9xIXQtF/h5m12ylQisQ2qjTt0LAKSTldjbTScCIHyzcpfbyIdfbX7hda8bfrRwi0eMhcyPL2uSILdyO195yWj/bTOnEma4eZkeL/09T3+lCywtN3XjalgAEBmywoAgJJCkCBMNvuFbS9g6fymH4YWssG1sUMTgaPvarNVDA294rd1qchulx7BZrnmaeL/09T3+lC1C03djdaYgpqKAAAAA//pwBCOXAAACEyDeUeNY9EMmvD0lQoeInNVgLTzhgQwa7vWElCwAlvpWkiki/kmVVmcoCUkMQZVnP1DUvV/jHsYspmdN2OvQ2yxlzf/9w02Nh2eKsksjTz/LfkXZnLc8d4LflSL5YO3gptKaR1pIEojExqUpquyshsB/EisW0UmJtpUs3F5LMuHDzHyV+//NmfNlTdW7o/P9j/4r8i7MrlueO8Fu7EpF8sHY9rQrlEnAIVDG0oXkjE4YU2RHoloLw5dQwyYnGN4HkbNKbY/6J9vVboy22+vo6Oa/766you1OsNNsA1i9T2LoRd/pTo0dYABAUsl0sjlIRwIMytW/D7LjBpIeRT1FGpGjt4T9A98Hvsb7eradls9X9qGRy/+urDYuqqJXc98ipjYBca9Hvfur8kmIKaigAP/6cgQoxwAAgiNAXOpHKfhEyAu9YUULCIEBd+SMpWEJoCtBlJWYAAAC2S01kkoJeSrG7U63KqIVxaXmMKiqFOW8Kl8kHLXFKcvkah3U/Jde2z7VvoSvvl8LFv5T9/pEfsoCd6TP3v1EvX2AAACWua/WXYgPK0dq2ysGHZQ6slxyJx3hEfao1rY639hBvOfdCJ7VZNuZ/9+1sb/oz75m4ia34uAxrhxO+rcadUSt7vSAgQAKSpTX7W2BBJ0cSvLpiDzFiURkDXTjvHBqrcOvNQP+vNGPkVV1KaiNNtpRRpT579F/RQT+nR/4g+d05B0LHdnO9AR4spYk5KGxQyyaE4yxVR6iHJJh+A1C5Ys30o2kEd3DgaWkLHvxefTpGUfUm01uZF7XuR7r/+tR3//+Mf6ftv/6PqTEFNRQ//pwBPtlAAACDR1WiwZTIEHDqsBlKmYI1NeFoJRL8RWvsTRiie8HzDI8xa+/fH6c2Rtvaflt6IvFeeo155Y7WnHBCzKoODbxK2qPvPfymUdAI3mRJUIlvc6/Ud7Idcqn5NnsO/v4F0mS8bpLMHWpY01h3rMajaY7+AFIXANOFM7V8An1seiDnuaGV3wwZKGmTs8/yj6mwKb10RQjMslOp3O01f6ldin/80EEiZq5JZI5TAPO6qN55CGE5PHcoN6AK3gv7VYE2l90O77+WtqmCNpfpSfKwYDD6UzgnMv31k+57Aso2mhtoFJYxyhRcqTvCbaOtslksc3i414rN8ctSVjqkRQbaor2D7eE28Fvnf0/fsLZv+Vrn5d/berIYfqy0m/9//RqVdds1L/1635TMlNujxq2SYgpoP/6cgSOMAAOAhw11gsJOzBBhAu8DSdFiGRhViwlbEESGu30ZZ1cJ8gKoRTuPdZ6pVk3aPwArFNCBD39GJMjHCsIfYg+Z1XiK+6gHbygwnqfz/mbZ3r2mPtVv+r5vj3u2VNr1rm+lP/JftBAYFTV3VWDBm+s2fH8ZcTCNjePpK5V7s4PS6rguL3VH81/X45cLOiacZXcjFwFetmpci0qWI/PwkHIoJrvarhn6NpyM6GhNamhgTInlaPptUwcyzTuLkwVtcrFEYaFvHk+59wQTtl0EK/MB32PgxKMgd0HduRkQ8wk6N7/Nuo1/ZT7//SAAALIXFG5fxAPMZCUO0jcH5g4tG3VWa3Uv4HPbFjdG++s1+q+aW9e05/T/1b7yNtb7hjFHtMo06WqPAIXbrvcuIn9v0JiCmooAAAA//pwBGgrAAACDzJb6McsmEOGO/0Mo7eIhKVWTDRSgRcTL/QTFDYAIAHtNxy2/HWApwGQQjrJORR/SpHNgb2zC/hH8Dv3Bran8Z8nyv1/elS95vjkhK5Wh7n4/ZtQLBNiQPDPocZpf9YATAkakbcbllL5ohm2FNEItuiP+D9KgZQ1AD9B99H9Pp8tNfqfJXKdOfwhgq5NbLP21EZddw1RZ6U3UsTWl/HqCVDy4CNVM27VSlEY1KYch7r+IYwYSCl3X1pMKuXZqn3NYfCPnkhyltqxJGXYojzv4YbxZHJp6t5R4wjndqb6vs+xlv2Xf/6CnEDYy4pG5LY+q0Aod/PwOgcENPQd6P1EXrgBVv9/Cmrqz0QSn1FJgN5/btkXvFKhTGGChbXgsI1MhAg1DL8e+gm+RtT6UxBTQP/6cgSNCQAIAhQpVxnsUrBDaZwdJKJ5iGSlZUYg7JENl2zclpVCAJJltayxSuqhikreKeZWjKwD4xyws/ba0b800Kw7sgYJ4g26DXzTPt0Ifn/b5fTszmcP3+8MKEr5bEkqQ2/+p+z+lpxg2VOSOSXVVZEPqvj/XSfTSN9QELNo/QIaYP7C/X6Dev1+3x66P6PpRepGJrpf9Plb39acjXtYcRD+ke7EaKY1adwaBEttyIfvueHBnG1yoT9BUt7FmOahIW4ygvpiosjtDi9MXfN9F9X7sX201KDSsGhsjDx7Oss9rrteJc3kl0afv+zuAICk3HIU0GN8WqSk2NwoCEpw1WogbTMGvwod4CvzgNXKX5d2V9HEnXv6CjLzXxjyxS4aNiyUkch87toeWuZdv/8SfamIKaigAAAA//pwBPfUAACCDFre6ScrTEBHu4os4pWI9SVzoxxRMRMjbCj0FagkEJKNKNptyp079KO73QkFJZzuqKJHxnx4OnWARq4df39H9X9P/i/WttB/oZedl69/5/f7v6PrK/pXl++tfRW8brAAHgklJJzQbWpdTxWKjnJMdxvSmNS82rhycTP4P7/f19ie6ff5dcfyJ4Nn0P/1j4k04noCZSBCEWvepeLury+kAAEmImJpJOvGbh+LvvEBT9x8Rz0En5f1DkRh8IDLugm9S3wjpUrPzP/1G6U+no1qs3r/9qUt/1v3CPj9WU22NdDdgQcJXLGhAIFVN+8Mgm63pmixYlXSSoPojroFbUXeXzwGQ+mfGiaL7FTFW4Q+X5qZW6nFu526l+nyfb1+v/uSnL7erj8jtfZulcqmIKaigP/6cgQxQAAAwhYsX+kJLYxDY2saJedWiHVra0SUTVEGKOzIs4omSBJK0r8ajkyD3vmHb7yfxEEHKRAt96Cn+WCMEDkYMMaaVg7eLehft//1EPjcrow1k/vYx6SrqzzxT69ejP0kX2WYcAACQIhFp1YcaSkVGZtSbC8EqyOx0Ie31uCqvV4pFi3sWn4LmZFl26ku7dUer1a8royTLd2/f98U0a/opJRWOYyAj00AAOhgTcsyjan6RZdv4h4KbQAwGmMoE+O8LDJCwMOO+328EL+3gvt6h/m+/lb1+T6bsv1/L0CIlZ//v6f9Ua3BDaMPPUUC1R2jhrUTFdXstwfED5PSc6FDEzUO3YkDE1E5tcHvyPxfo28J9vn+n3/8H8n//r/8I/UDY1iD+pnvCY87K/RkkxBTUUzLjk3A//pwBKf5AAACHS9Y1TDgAEOFKuOsKAAIiSFyeGEAAROkLg8WIAAAAMAwLdv2uid5MQEOlZHrAiLHUWDs9kI+eCBmeKy0wwcEk+1TvT9/tqeXM9eg4NvU3oOPhjZKN9b/RiHJ/dWe9Xb/SABHLbKX+icDZV7Mjll2q5UbFWw5eyhw1ytzZFUnCAkueFGOWMMHnuF8nR/l/TqeTt0foQE2F85nK2VN9f0bMns3b/UUQCGhU4M0Z3xb24o+SwliTgbvq2HyPAma81qs9r57ZP//7/+9zfR1f/nnciq/vKYvnf9KCTQiwr+t4fDHtFCH2AFLxOAAAERQIKiVIoLTRcnqpqiVcMmguq/Ijh8Q8ClvM7VZ7X/ZP////3379f/POciq/9C//pQSZwiwZ9Vbw+GPaKEPkSqXidMQQP/6cgQq6gAAAiMjYc4UwABDxTw5wpgACJyReBzygAENhy+bniAAAQAoAg88wAIAJzmWhAJSdyNOk47wYr42Ntw6fZ3fPIbUKgQiTdm3P+hyA+GlI0SvCdwWND0FwIxl37l0Vf7g+n0///3AAAQAQeeYAEAe4k9CASIXipZ2/5gxXxprbx0+z2+PIbYqBCJN2bcfuhyA+GlIt53zn8e2WZa4PqVR+i3s/oLv+///9FdHqSB9EiyUbMXrnPtTKxjiRFCgsUEykUfVDGEd3ctlmEUf+sy0s4sDJuPQLCMqxEUQtO8hZHte+705KhA97viTuPJ9yBQIKs3mLNTRqZu+gb3NSfGMElw6VBCQ+cPuLHXG3GCx1+dhVSDZ4DXlRYuVYhKkL70WLW95sk79ZLIF3u+La3DpTuKpiCmg//pwBBQVAA+iEB7dgG8YEEJA+8UF6QAICDt0BIxDwReH7oDEDJhwuQfsWV3GYhFC/qxRGikauc18m0wLbjQ3WTfT+DHBIPB4UIAyfWzE65NslIMAjJhJoQyIzSpbrHp6epirSimL/5IBAGmjNErQnalSeKWo84sEgyYsAy0llFiw4KFwFacocDkHpACn1sy60JpJSDCl6izRC5jrEqGuex6en2bSiq2b9uSuDQ3gQOIsF84Z2/DJcELnINiJoGIlBwEL73BEYbGBU28VLK3pkxQcSYPRJqeLlRRx4ehCnpS6eVSGjVKrr7v+vGSuMLKnqiaPODHHt/3JZgQMScWHRQHjaQADAELk0zIRGJUDaQKcLK3pKk1DmsHopS9pUROePQhRpKXEnqw0ilV19zPQ/WmIKaimZccm4P/6cgTvVAAIIiEaXCjFGYBCpWuFMCN2CEitbkMIbUETjS2UYQ2YAJCbSKOAgY3QXhHD3IQj3zpWwtUnlpidI6BUVxLLSgctY9TjKlrEMPgyGnNah6GAVT1pS3ZkO4A+zza0TVph6u61bgMAgE7QrRbQ4bxTaNZ9kX1d6sKOJTwPIamh2WWrlc4vO97ZDh871FLM4JEixY+gPxQC3rSnsqKu0gGnZQ7onrdVd1GkAEmg6EjhZGnfD8m8i9XL2G0cJ0cNo7AVqkpL2Z/qzNMpG5mv5c8ESLH3FqwqhFl2SEpkUS+09grar5l3bOr70tRpIIePMPQSVbsFw6NSAPU7eMvDBQuGCl9g1UjCgEKhmsFgowsDQxbkDWFgkk/TW9GRRFsqZFC22m2Lln+ZcpimxZbukkjSmIKaigAA//pwBH5CAAyCHR/ZkeYTQD7km1kkwigI7IdcB7BHwQuL7OTCjaiBBqkjO1i47W1izJdWYTFPXzfWkK31KsquKDPRy3u5XN2Shn4OTAzHP1kivF4hrPFYGLB3QuMWtt65IZ2zmWN2+l3/UAAWiLNfjx4Ssh8J2HkJ9hu4WiO6gqcRMaCG9b6By309/3y5I65EkuLxPHpHsNSxY8FH1VVtp+321Fn/uXT+3K2To/9D8NtRNOloZqReWdz65kMBZ+914nDCKZlKULIiAKThxQFvUM0uztc2DLBAgCcQgk+dkpAwcdQjOF0s/o366Oizrp/cGCFf/sJ0M6HKprsfKzIPu2AinZ1BL4wVM7ocM7rLRQz6sxoS8RH0rQYD7w7LH0i9hJiKHARg13JJQSvZOZSi36afrTEFNRQAAP/6cgRudwAAgfAa1wHpNQBB4wrgPYZmCSBhc6GY6yEci+ukwxaAg6OgnGQZR7Mzk6SjJDLazeU19b3h8Y9v4A9mXCW2KQ5JmqtzXWThpzAyl5GoiSASjlx2K+gbsK1Hcs7//+hvmK0nGzAL8zbzYv7CEJQdPAn2Qd8ObMJAH/LfXG130qmZgwvB5gy248KGlBoRrKTZI0oOqJrWdGUzt6Zz/b0s/9IIAAdzl0kjmyQBzGj73Ss7hGyfh4E5X0VsXhUWI04epdwlJhoGAofvUZe0BiMi7e4mKzasuwYSfzEksw1X+97F3W1WUlQjYjVWEACI0lHAexW8BYVJmtUh6glgM+8BD19X4o2edQhl/1gQNVYpGnuBQIWLWLacDrYQeihDHgUxdKNP2k5e+jCN6es51d9yeqhMQU1F//pwBPh5AAyCExrXkexRYEJDWuU9iFIIbIFgZizrAQ+L7CSXoQgASqo0Hka+hdbUP7iUGmgnacS5mfync0C2c2E9jNSdc5RadKISHygmbSs6thtsIHYo+riI5THTVFmz1BN+qScr/6lgEB55y1Ls6Q9OMkrk5izafjKuBfNmunUbC4J2/AfhqImxeY6wbLdxRp5zXKJucg8lq5+KCqFIicRCtu7V7v3av+lIIRbUjCVIeZEfMn3EUF8EGuGTcajd0SwZvRYQnzVYOvNUg2yM/W7ZRxBaDyMpN1ovE6r0ooLIYR/F9lGRjFUL1WX/FgwEr/CiYIALYhVQduxV2ORLVQHWLDIGB4v1Qbi0kDV7nBprxRtCIEJPm0lmiqjilisclA54+cJsNLNBfv+tMpd/dbfWmIKaigAAAP/6cgTKvQAJghYYWUmHRDg+QvrRMwhECHR1WAewbUETDSzoZJUMAACBFmrz1N/s/qpg/CTBq/pV3puJ8UB9coMrxmqSAqqTpuUfWQiqDYZgwytq5SXFzS2qDTL5tW91lSTHU9xvJf9vqDwvG4DQDK0Mvpvy8qpGio6MY4Oiu6gYENfADAJVdUKj19wTp/Q15UHE3MLuQoTlFNWp5UkiVwVfa6YXr6vrb0clkq8QxVqVnTp6Ap20T9Ylsy7K8UhZ08wIz8WTZajy+l8+7cmFVKqdKkHAs24E2Dp6prxh97toVFxmhnYeqGV6f2huSUmVNxwWExFnVGgPYDBy4GSWFHpcAhi3QlnhMfatH4iYWnBbY8ee21H3xiyTiS79vfUMr1dqIvMLSKuIHWMPQlkZAwmIKaimZccm4AAA//pwBAwVAAgCE0BZaYkpMEKqy8oUItvIhMVg5hyuoRMhq9zziagAAACIACOS/wxnzzgmOT3koIUSt1DFZLh2rQsf0M/Gl85fRu6s5aqS93S9vnTjLf7PTp6qj/6fHZXQrj0CwoxN++KMUvykmkk6pwZKEM7QgzBlRXS6eN0x/hYxuJjlrMnl7HuUfX2uX/hMy5amutZr68P18y9a7ZxGt3ZgtNJfhVf2S3urcACETLDUmDCQTQIWXiiAtoVZrB6uwrHHNxoG/GGbGgTwmTtP4jXm1r8376mdF7XxKq6TTutztb3q+i9D6Vp2tUh7FfSAggJOS1rimIgbRz8bHOtRF9oeNV3Gh1Z4YrcYIUzK46CLLg28GX2Pu7/k3f7K2idvyV+ZYNn/UtX8qax3qW1NhDRp1RyYgpqKAP/6cARsfwAIAhdC18nnE2hESivNBKJPiKUDWEwZTIETmOtM9onoAACARErqyPDLhg4VXDiXVgslUnWcHrqVBA26BYkp9RzrB6Ytguf29JslKdU8H9vjo/b7d/q1/8r+GvwiZh9urbvr1AAoBxFJxNKWkOZExxCFtINU6LHeMHb19AXwdtiilvFDlZYo3Wj6/+hvpz29drDf+30+T0b1Oj6GRmn+O6TUwOF9qPsOgDX+EHN+yKoWUW9bsUkMip82sWokGfO5AJjsiBY8Yl1PlQ66YxFRuxP5X5utTKrN+c7Np6r9n+vs//O9S2XDM3l9H/3awC004NJoX5fllNJdQKqegE3kCZS+MQlbnCVUppLhlfkU+7zg535NPe5T1YpOZkSiiEd3X6PwRHr+sam1RLbLHs5WckcTpgD/+nIER7YAAAIUPVm55xNkQyhsHQxFhYida32gnEvxCZetNIOKIgCAmNtyUiwHthlRM61GLpI3aqfbQv54x4o9Q71B7552IYGYjTgBHXEoeh16qX0+z9ae2qRHrlwr+wS4ArO4c+2rLPSCZTzSr0jl0WXl74To/vaVQX4Mbwj1qEh6HaK+pvQEYiDAoQV3Ur9f/jPz9F+329fiP/nDtCMF12PQ+Hm1PqP5vchCIACdRUcjblJoUa8bD7YSDdzsv6lvEBa+ESTFUFzdTdaiH1P8K5mlBNWqffzP5T+ZrUP8vyfEdkP0VvK/d//V/V/T4L6QAAAIAEW23JFho7FhVivB+sT+KOURSkvHQx4ma80QGUaKm6G7UEGK1m2ld+EbobzPtQfJ5B/u3boo/dW+z2k/9m3emIKaigAAAP/6cARHbwAAAhJD2lU8oARDw7v9oZwBiLF5flgSgAEUpC9HBlAAAADQottN32vF5MYce/y0R4u/h+JIoyHE5QzwkGXUTFhyagLfm2mE1WolfsburdS+7ff7+n1+novy/H/Z//U6kx6qUgki/Co44nLGdeqb+NhDPAwfCIOC00h8t48WupppZOBH3O8orVs05LbCZxk590V1a8VtI7Y/YpBUTdgcHHVLm3OIZSkSggiigjIpMmT8i9f+n+iV/yLru22NcwnLZ/ITxVwHETsLXV0Vz/7GciMdjZbrMb8532IVjiUhFb7fk//lVXmRXV4h1tjwkto/vnOHGwsCT3W+ule2iV/yLZXv+NcwnLI7chGbFXCYidha6uiud/7O7OxyG2urGf85+zLOJQQbornPwm0WWKvZrJx6Ygj/+nIEM9oAAAInEl3vDKAAPoHbvOEUAAjM2XHChHcBExsuNFCK2IA0Cw3EQAAAuUgIAZvfD83+D+ZrUMCsJHF3tDqHJI1AVAdMEn9RkTLOnSRelymrQLHBVjolv30JQpKksexeSJPosq3HkFIEAaMgABWMIMESbPM2czmjQZ3xh/ddanfjnyLjJoaZ8g88LrXF74qpo0pais7+57mPYOTt1utDQtczLLOyoAAEAjBt4AgyMdTCjnxzinHMp2Gim0jeEx3iJ1B5+vpCK8TnNdZzAQmnPOcZr+BUkOCx6Suij+hSSjhp0qZf4qvjYuS1qliAgAOlYACQCpBVYwZXFLDshVeN+MH+A4ImERNqtz9fSEV4nOa61gyIZ2PLqz3ReBWjhzJK6KfUko4adKuf4qvjYuS1qqTEFNRQAP/6cAS/GgAAAgpAYWjpFgxCxstsGOKGCK0BYkYcTYEGmquA84mwaLhU0TkSIJVnSo0mZ1NCaSqqR9PhC9aCSa2UEtS2Dfvf+Wq66s3l+2rfVP8T/+8vuCe/xEouReIDvTWCvPDjfbqAJQD6HTK34TOzUMZ+H+htw9qP5V/KP4QmdBJ9CTUO6/2y1XXVm8tdRtW+VH/iW+lGr8RJkXiA701grw6BDZDaloyMAZasWtFV4a3nczQR0h9Uk3Khvwifzi/QNvxN/+2kyaHymUht3H6RNH0683gif/TrsQHava5AuKIL8USp7paqR4ECbnrATYnUOQ12Z7XNG3YUOQXapRR7xCXo0Si/UkvKAWfihm3R/9DGe8+V1Y2821BjIajdebqCd2uZoXr511kvb7/V8ndpTEFNRTMuOTf/+nIE08oAAAIYQNcJ5xNgQogMPQRFEYjU12uhMKFhGqbuNDQIXBfkczxNKCT01lTF+xbp0I3PFmkKgW8i/UgX7A+9xh/+zazE0Zq/3poi6mY9TKmXwwmj+nl6cFtZq0Il/ctPW7fhuipp1hT6RyyNSEacKB1XeIvipaQFH1wiP6s3g/uGffyb7kzDSoiWrTWu1VVvRMt6jNH9f/xn8oyEnktZRTwtw1bzxvjAAQAbSiYWknFXU4KI4u6hZ5rqCr1iluFP44f0F/Gt/0XmVDHVDXq/7WQyEdFK6LREb7IDQgNL2ubmkU3imNpDerleEHVv9AAjAnaIVkcj2o2QL2nhDUeCFI+E+j+F9RXqf0f2tqYPYqMRVe57s26gwVCMZbWZszFoP7a+lUJuZqU19Hsie+Qfa2K00O+lMP/6cATCEQAAAiMr3OjFEvg9wvrRPwdCCODXbaYcS+EJmvB0U4sGYAYN0Tllsl0p6vgyPxnDxuMe7RgPTN8JP0H+Fh/qU79/u+7zVn1mZdlESksXeq8mtw1dZLT9NzxZ8HhXZmWPh3frTMVlO2n2RRe4Z/LFLWpw53xFTlhwqvBQnMxHDHg/L6CENeourRn8DTQDFR7vEtQeyfJuc8tuonfqIne8j2XOyKQJCWhpjbblZlEWOB85yv+axx6F2e5wapq+4+C9+hniEC3dH8v27Sdk9zLoxOlPfdqVABp2t6CL6G7dd1lCdmVY1Yfam/bNNmsF9uORttxTkEKgrLtI+PacqD/C2rUBQ0+oEblQnfOT6v3l6NbRfNtlo/6XurJUROxuepR72vFA4tWhv3up+shc1MQU1FAAAAD/+nIEmf8AAAIeF1npiymQQ2WLfRwi0wiss3GknEvhAg6ttIEVbAAAAIwFFLbbrjpbuCI/27OBK7VHtoFi3qP8BG4wD9gi1gEfUYrpRKimC6GgRZNdJkiMGnXOIo55t9e0mVl9w6r8oR+sAIAW2JSSSTKxZY6SpYNQs8q7NxKPXCEX+EHoIvjoo7nfJ8j5ACDrue9wzNtvNsqJlppElUWGfqWqhF3ytKgyK26B1YAKCeha2ts3+PU4OPlKpmZU+o0dsqQ9S/QeFicbfDFzUf5m+PyJbRuivVWKNQh5VZFNEOxqWeR2ecFXNEJFHp6HrW6slpAAIByBTjjm+Y5Wj7w4QwTYh5EiPQfwIboA3xH7+Mw0+DSfTKoehOo1eessenr+WEs7KjRcDShZDFlazJ9TWe6STEFNRQAAAP/6cARxmwAAAhkX32imUqxD47ttGEdhCJSlZaeg6oEMlKuol50QQCQbzCkbabiFrMCGtMxlfiUHPyS/fyMcRpU0tSoEQZQJ9ObpDjVC+dUySVUteW6CkutyCNbYHOhINh7PI0XTZ2R19oAQAPZSUskuaUkdAnbPQC4ErDkGRigtpuM+FRhON26ipvfWhOkTUsDTz7lNigvVg+utqxdFX07clVuenvMSmk36g1RsAAABpAgldv8l3FdNpA3CePpxTqt6A4f0yhP4+LvC4wjSst1Hf/YefqXaiOyKxqv5TAHyedb5ux2r16D1z3V7L5bkah9QAAQAAJJ2nCokCcCIdpbrJZvXaB3gvheViAS5lxCNvEIx1GvqBv1+d7v5jP77RxUlrVTtdbv/9bI9bxoxeZqpfS7/uTEFNRT/+nIENG8AAKIhMlxoxxQsQ2L6/THnRgiox2WmHFEA/5GrqMepCAQACcgCQkUp9sjyO/fr2J/4OEpBSKjlsr8Qt1D+oA//t/zyl0I/gdqodHbfRFHdacszt7YQdU+Vlhmpeun0NSlmdVvIgAAAQAABKXaBUWSxwriebs/Rscjaz8G/SzzBQcpjoIeVb3fwePq0bdGyg/It30mqyNloB06Pk3MYiQYyhJpYafX3UKAAAAxACl2/+o1ROjE5dWO7AmUOPnU3cntI+Ifgq/jT4P/ZvBN0CvnKvo/kJWSaQjlfNDnql2YilXaM25Y+YdW1vFNOnFQwjttyMUV7YpPl2XZhHqF1j4YGy8oLPkYa1qI79BUblQp/t5jef8qZmdA9BYw9Mi3Z6shretODzZq1qvI06x1SYgpqKAAAAP/6cAS59QAIAhMgWGmPOgBBo7tNGWU4iIR3XGY85METKG1wkxWOAAAAoACcmu0OngtRNYleoyjLcRmeD0vIHlH8Qt4kjK6CP5UQeUM851uhGoS2JdI3nEw7WbKXLUyTH+f2/du8pt91pAAAEIQTbkkLTqGEJResLZTv1T0lNIKeNHVeCBq8P+IjvRvDwZNGEsDmfqfVpfD+ISp9GDTS7az1YvRrdR8hV//oACal2ZPCyG6ocGFhtMlgtFz1zQ7SJm8QlugRDPUTPyoZbub46ZcH4TM5+k7J6MT5Jz+Y3UIhZNNp/+IGuKJrdUOf2YuAEQDCQYVu8dHnCRja4Oc633BpgpJAXwtvEn6hx+op5m0hEu1ft9/duo/7eq+rUdF9WXk+z/+/qbqtD6eIHZQ28q6pX9CYgpqKAAD/+nIE954AAAIfWt3oKSlMQ2or3RxFqYfko32jHK5xFatvNIOKVgAmFY0iWm3LTUSFO49fFPhYQ7sJBnkbw58V8avr8SL7+cnp9idTeNt02lPbQxeNZ/8/1T0VNWb/nu+L/OVOZNlxmV8eS2gFrGVXE5bK8bjF8fo+RJqcaXGr6ACFqYz+I8H9fOLFGMuOXOMnv4gnYXJoE29Pv6P6/X1+v/qjee/+Ltgdij1cgpENp2VxqNxytPvliT82QTrzSktY+Eqcw7oYcmgI/G/T2Dj9l9U9fQdpzm56wKIBMJJQr7dm3dU+h8WbTs2J/USYwXo4TG05en8ShKx85GVfAnTVQy0RhEMUcoJIyfoLfVvTygDcr2ml9fQv38R9V6m+3p/6/Sf/jW5mpZ7VCfdOC7F3bqkxBTUUzLjk3P/6cARZ4QAAAhY21x084ABCBzrnp5wACMQjcviRABEWjm1PGCAAACR1ubIapRFB0kvOt5OrjDhom/7m2eKzqtCoc3G/oMeOkvH/Rfc/0TzPQ/7+/3PeyuzK5V0uJvJk57PbqtWV06GWrAIAGnZs2Nrx3AWEPzPaAFDuvM+9UbPEJO9RCOvwXPxB6mM/H26FaZ5f0X09D26H+c3utqvt/Kt9ue+705VFOQOWrABwSMbbcitdtkQYc4iepBLJSq6daRDrSr2d9w+BEEztQNdeARxoehLGD5ho9y7bq39NpG0cNvAHMZ5zWblpodRNhgHEDiBKzEc/f+/77TiFRNiThodKw/UQ/EjXZwUPCYuwm0Wp4K0Bj+S1xKBNI4vr6lLss4uhGLXuW/0II2vcu5UWMTjHN9bk/L6ExBD/+nIEyhgAAAInMuEWJEAARMZcM8KIAAiYK4W8sYAg+4yyt4YgBgNNPPPPuCvB34KGNl+evYnOQyeZ/9f/9f8iuxDaLo7vYjHEIIdAybdqRaGIEKjTKRXdKYOE6WiYsd/Acu95ysaKsceX1sCMQiQSCQbAOwLYODnNgquU8hk8z/6//Jr/iFOxDLRdPYIxxCCHmTb+LQxAhUaZSId0pg4H6SQmLHfwHLj3nKxqxUaeWACEnrbYkiS4HgGal+xty5/HUSAJ4ydU6tr0QLGjDzvKuOkXpr2MOlZbWmhSHjmAQjKhtrqVyihYWbIIZaxF3JXv1sANICZblt0jSJK4J65IXY/rIiDby6WM5xQVdW3K1ogRHX0thoqp8dbULFbva+5hmKXPiUf+cFRUVqc6tVyOt1PFkxBTUUAAAP/6cAT7TgAAAfw2ZuhGEow9Rrw/JCIfCMDZmaEsWDEOmKyBlhUgCbd1n91rbTk4zk0PhQqqu2ahSlZg7F6U7oqPoYs5HqnsZw7K5dq//Qvv81HmRYu0OsOr9f/CiDSE7YFeNwT+oAQCNDeJ31bjeoNu0TkHMR6kTmSCpdqWYnRPdARdv+afcu1f/oX3+ajzJi1llFbb8X+UyzO/6GxIfezszKhHVHrfbtZI6ggciGS+Zs4RML75Weuqdm7n7hl23r1I9SGBlbosrL+qPo370ullEKaWdG2kcQvdCUqIn9R76/WsrpChWp5scQPhSD44taf9sDF6VUdmgr21szVf7WN7AQe89yjGyt1eMNdif9WqVvWVvrKj2GM9tBZWsI/anEP1iLxQ92wCJU9fsWmIKaimZccm4AAAAAD/+nIENpQAAAIdQFy9ROAIRMgbd6wIAAgVlXYZgoABD7KuQzBQAAGJyxJuBboEZHAcMk1llVR5FPZqiN06N3NO8TexzlQk2M206psc160fY1Oc//vndyrW//s6WJLStVbtqC1+i31fFfAoDAlpZbi8wt11KkWt5+93O0mNXv97gG4nmtXQ7Yb10EWluwv1s7rb6PbnagtdUvpzeo3/7fxVvlTVaXHi3o+y7EzrYiJhwZ4ePkxKUymkpN1qfurf5eJsNS7POtEJ+vX+nfp//9//+x7f//6EIJo5EJ//8/u7OQhpzCf////7uQ4azTyFFgjL7DV6FK6G4xqgn5Xyk/uFXKKCcaJj2ceZEUn/8/m8v//b3//7Lb///QhBNHIhP//n93ZyENOYT/////uQ4aw6eQosmIKaigAAAP/6cARTVAAIAhsrXtcoQABC5Ww94wgBCHjZcUSErsEUDK1cwZVIMALuK2MgAJonnqefQl2M5/DcjtRrWzv376Ud9CXmomqX7dWMhNxhKAHUbr2P1pO/l3D8VbU99j1kXKvYskVP1Nw8kiHA3p7/t9USRKeFp33jMzSt+bUM9hnamxysno/TQXelG76F9+3VjIR7jAqMdRmL2PxVJ38u5+Ktqe+x60uVey1epuSMNqWOFFQAh1pvbSR+emY/EDOfYRytUYhj9X7PcHpTu9VSrfsaytmvK379v6sYWBVzAEG/7AW8krgq6seMiLPe49qDqyABGRtElwCDonlsyuhjazK82OA0wzYNEcb/Poj6JwZHiVh8N5Y0ieHhpgxnfUgGhoayfc7BsFuHZbgrrfiLPI1nT2oOlUxBTUX/+nAEWUkAAAIZNleR6CtQQ4KbIz2HDAh0iYFUVAAxEpst3pIgBAJqoSBdmIWZfy2Rnwb0FNqwm6QnoMp2MuTwahhtLEvNDGYN8ipn5T1ACdV0RNM97fTbrRJfZtU704t6Y1H/aind3/WCG3ZcUC/cHSSXnLBDc2WeXqabbg7ewwOauVvWPl8RmK73KfIOUo0hV3F03pWGQyiWQVE+p1iKOqzuMJ9s92r6g3cVUI0/aMrbbj2PIymUhzYmAXSv2au7n/sZf1X40iiALtKWpubnZKTMt87W3URpPZsRPnXSZt4mSty0b8zgjU9376iOR1LCCUrJLcTjTxslPKJZcJwk62MqAdzNV13wQ9XG0V8W+t1v1Lo53PW1Fbrszk6cqPv0GuelV8vQLOY9sTV6KveVcuRyOlKYgpqK//pyBOD+AAACD2LdlhjgBEGsO6LEFAAI8VuLuAOAER+rcKsCcAAAEEIIKQlVnGSbFtmKxKVdM1qHT13/+n///8z///2MMb//+rmDQgef///8mPuTQaBOhAH4P/////xLG5QgDsH5PE4AAQYYZgLSRdQwdJ8IYpe2SpRNhExaEst1fR+3T291//////yE///7oIEP///+c7nQUB0IHw/////+Li6CgHD74nAAAAEAAAAYDAYEAgEANb////RjP/9v/92Knmqv/+OHg8EY4wWFRE///JmiQNjgIMJiDAM//9fjwTA+CQHwD0PCUXsEQlf/6AAEAOCGBgKBwOBwGrglLur/bf/3/mf/1e3/+7Keaq//5h4PBscxAqIn7//JmiQPHBAxQxgcf/+vx4NCMJA2B+Y4tJxULv3f0Jj/+nAEJSgAAAIfGdvvGOAAQcM7WeSUAAicZVpHsO7BAhPt5GKJzgAQAQYVJGQAx8mDGS7vDrt5BPmUxwlpccTZ9zPawOHLBWNBV3ERolSiIqtGV3nvDv6QVBp9bqeDT4bDUqCpU70ITjxEAAFIv6BeJlgITjHbUTPl4xJPQVLPEB63VyOiCvZujVDPcvvLIR4iaWeslleHPI/pEoNPrdTxE+GyUqCpU70U4VEQF//Fis5IiaBqqO4VB4sJ0ON6Eh+PgP5CR2IVMhil4VGk6Yh9Thq2mUA2nDq1aGH6HmiCWgRLYljM8zr1P6hfT1///1EgBBGE5W3JpAQQm/7T4N1gVWfwpIUcO4E2M9Dl0mxPbn+mRiGQ4ekwipHr0lcgrdXkbcv61PJiot8Vd2P49IqZlExBTUUzLjk3//pyBCE2AAACHiTZPTDgAEPF6xemHAAIxCF2+GEAERMYr6sGIAKAQA9e3+QUBROMnJUWH9o9yfEbkMDJ5R2JFqMp4jjEqgvzS/54TdtUTHVR1Q+bQlLnie3W5diUlV74q+R8Te3a9j//qACAY7thEUS4KDUfk69V4/MjFiNoXPzRo6EDRGFjjzlXCqqo++52k1R8yqH62+9NLHW3/arvqVLDLEqva6u2p6+zO/6gAAgC25JbLlQ8C0oYDeHNinR1ZoUIB5osLjhK8ocL7qcI6l1O3IFSTSy3McFiobPqSWS8miEQ/eSSjFpoI6b9f/+cA5N8gAALyt0UpJbbbrBoZlNdCPBaG7ydAbsdZMxnUxZmw4tNvP5P0r0ZL7JodSK+ZaMjQ6XZZOtynGxttxH2ZtJ//6e6w2l9CYD/+nAEuYoAAAIdE9/vFEAARAMrqeGIAAg9I32hhFrBBqQuqCQKyiSSJc87rYiQug9kQ9qM4kB2eAvsruS1dDD4xbT4tO4DUPYLsRCREXCQpFmmfdWxEowW19oftXFTdGXBaKXB5RlF6fywCCr3fAG/H0yiuOhxXz9Qd2ZXPmDCsjZdUG+0i1bVuQyJRRixVH8WuhBbKm1rA712iQm9LrAdPCzEF3EHmTI4/ah9wMpihSJ23/RtAGRUdJT3VUKhP5+yH3h05Aw/P/XwQzHDMpC+GviOJiEIg5P/NeTymv5CMLdW8z5nMarZQvb+R2fyuWQAEIRtEkAIJZB2oNHKiXHTJ0j1ZpOGMWBZ9mvwwa3f5nR9ys330cxjej//6PpVv6GKVurebmcxq8oX/qfV6NZ3CiYgpqKAAAAA//pyBKdsAACCECTd0GcT3EMjO0okJWII4W13VDKAMQeTrA6YoABAbsQlE0itOim7KBCnD8Cnq2qelXohejvz3wTdb2Ko1wT6J8fZqNPOizpLUBXSREk3OUFRFBVSMkj+R1HfAqcRAWwAAMwHX/9UlJ8XQUUHU0Dz0skOhrEQo1HGR7EQw7++FD7GIRFIpp3ajcNq0ah5mSInd5ySKiIkCtuSQP+sidqO/TqKoIdqSom5KEeg2cqNkVE2ivK+h26MUeRF7+hYmABCWxiczURPv7e3Ivnbr6Prf39mZXAMl2GEaomnR1ylfv5ft1q/6v3GfbWALbd+vLKuEjE+IUAzMBagIcMWuwUKvPFYmJNCjBNUuUGJbRu7UMra5E5rTms/yAmuyFqHV7a8jv903sr/lt1O3+xMQU1FAAD/+nAEwHAAAAISDN9WDGAEQ4Obc8SIAAjUhX7cYQABFBCwp4YgBAAKppVClYrTqtkA8Qbkd6gKtq3Y02IBCQUBsoHI+mvDsCoC7EhTJmrdCGqNjjCSePsqQDiJ8YV9DKpTS7//wA0LABgeD8bABALKUKroyg82UG0L7ucujgkcgllR0OwQ1YruyXDm5u4xgXcHT1hN8kOHoKRMkLGCadbmaH//5Ss2c//8AXgCAd1NXmiwdMMOP81JI3+Nodt2lMubQ6OVJbs/eXWvu48+SASaxg+DQTcSi5F4GrtWQYxF5A8v4sbS9texFytZYaQtUHoxMvAAJf+qMR0OOMKBxYrL6HQ/csplzVd9st2Mk7ymrX3cefQZTWMHwaCbiUJkXgatjViFjH3zy/ixsi8lXsRcrWWcjUSqTWmA//pyBMyNAAiiGDXfSeET8EEmu4BhIhYIaNl7p5hDQRKbLZWEiGgADczuv+PBECWqTQdR9b9Mb9nMM+ynnym+qN5PeYteimqO5TXkqd3Jp5N2fiBS4m4VRlgyvyB5ijLg21vqbmCO/QyYi90HuZs0wKSESwLyIthTtCamT1ZaQa6n1fHHp5tvbVlZWd0lZOR1O7kVDeTdvEM21oyxFfsPVJcTa31NzCd+hkwpWgo04pHg9lXRFEu66H7fBYMxAWrqya1rV+mu6mu10N9YXTm//xW12TRLVbMBq86PDTHM1fpUodEUi6tQFCA58FrdHFO4Q2V/IFyWTwhx7xqeOMUFtB1ZFryoFF6k/I920N9XC6czLTp+K2uyaJarZgNXnQuGmOY+JPzJZQ5ZGRclagKEBz4STEFNRTMuOTf/+nAEBp4ACAIhGVmLL1ggQyWsPCAlGYhs1WrsrKfBDYxuKYMUOAtFLgQCJ4ikAkz9J2JDAPs22spCDmp+rmO9lzovKpND/yt6tcl9EqPPCWGllan1xzthY/EYVK7lsI4CX6GesJfW7/klglESNNr+nAwVIacSq0K4jhfZdK9LH/8TJ9f81SspWrLSY1dWuMAaXBUsfaIwqVzthHARd0VLFWFes79fU8WRoJFb5kwJkbkgg0Uk/T6S2jiEXCzg66VvQEt2jcvarcRc0pmukNeo7tUCLTn2eZ76z/6v6qVW/p01jR/+RRSWxrrXTX1t8h9AAEwAHa2SDHH6ClBuVSbE7avjN2BF0HK/skT35ezQgfRPKYBjCT5p7ZEu46SF5ByZSgFHLaj1Jd8X2dJQENGoH4316ExBTUUA//pyBBn8AAgCHDXcaekoYEMDHA0kYg0IlNdq54zwgRYbMrQSicYAABEQgvbbbAHxkt03o0kZTQTnvs4vhVSiDD1WrPe5X39xgApOz0umpndpX3b0fedSCPWnMX+NFOutsUkP9Gz9XU7+kVEz5RKP/yWgGZ3KTPAooIrrhmoqv7IxLq2Tthz2xVKZzz0yBzBYVpe3lHJoYAsdZQdYy82ZkVpWxIqx7E0OWjSikUgAq5baDtQ66kZ4CSUj/CNeT59LJv9P664IefjBacVnt7Kge0dPyc99UW1bVT1/Z7snRK09Ma8JpXrt+O3IUzK/xR+/yIcUWc0duusuOwIiurH3hNphYJ/ZWftQurLBFrSXR+pPs6dPvYoEhXSS6O9H3VhL0WEPXiI91oES63iXGGxRkNBl9mCrsRJiCmj/+nAELlYAAIIdNd3JARXMQ4MrAmEiPggQ13DkhKpRCAxtqMOJyCAERFqmlBacLnpmD4Puau5bxW/iLmfqMd0TkuitS+/VbBIifN5uzIlLuJZ6HghwOsBgobUi3/dDwAQCSahaVGNfLP9YDX/CAhMupvs0DQQYLAHLyMc2XE2cCRfNFQOGk71IIjNnqIWejdjSACx6u5pa/U/n4dwtrUfnGNb7V/WUponumvUDVIAQS3JJCgaakPMnWTSZNkqNYVqdGT8eq+ujOr6cqU0HV6pq/Tnopg8Rkqt6tZ3oyBrq1SwqQV3I138ndb+Tf9lgBBGa//jwETyZU2hminT7Mzu40oIOomIEuCGuaFR83bDtxYnQRlSyWvZxQg6iwylhapRDfLE+jcn0s8xOOpffzOlSYgpqKZlxybgA//pyBFbsAAgCGhjZGekq0EMjK1cxIjgIuHlm5JyowRcM7mhkCKAAOWy1XKRzOUvTaeqRs9F5m5uaXz2CbPTWk6Hz9TBRYjO9XxmoEvcOHyEqYZfampYwaw8Qm1NXo1ZVzPr1Ktb/wCz7AGAhW2yqiStdVPjXE+I5eXkkiHdFbsgYZ1gt0YGZ9fwgr92aAROt4k3xO9tJ+tw5yc4o41K1vKahOGM5lDny73ezxAEBW3bYHTANgHEwSOiswWBWwpkV4SvGqEyq+wuQJkHj3jX7nGAKSW+8RrjUjkBmFs2FAV81YCVjpDuW1RrfqSplbmf9tvUQADISvtkgGb1roMu2/zqwnwHiw7MtOs7vpo+HG4Qu4YFA5E4eKoFnGhX0MLqeQiwDBPb2mijVLEZijUKyqJ5SU3mFVsqUmIL/+nAEmkIADMIYJNoZ5jrQQ8MbEjFidgfo52pklE7RBQusDPSV2AFNt/6aclK1qqk7q5TWmNrBn4C/Lw3Wfu5unFCkexDuyBNj15NfKv1dtyL2nUxcWYzXSqtG9KC8U22Lvd5Ig7/I6OwAvqdUnZBHc+B0M7Qk72re0SS9DB6+ZFr0lN1oWPCh93qXR8I9jxfb5Ld7Edi3Sx7/XrDR3WE3VZV08JcSoLanpLPrqAKbkkSUeC5DBTspgRj0vFIdw61WCDrjXCEoxCcv1R6tVErs/V72E+I9rqi0/elJiU08fDo5ItDP7TnWK/9jqgCFJNrqlIF/juLoi3XLzqK1x3txWbzvu1DB9O8TecwLnJcoZjHcIQK5TPThuQ3QV2KNKYKWOps1svq7O0XPDt/6ExBTUUzLjk3AAAAA//pyBFQdAAACFT7ZHTygAD/nO1OklACIsF94+GEAESGQbt8MMAMAN38dsy2t6ElZAxWAdN596smFezi2pQGFBhyCIuPFGGFJnEtHdTfjj2eb7+3t08xv9VVSqfv6r0V0NiyFyGS///9IJTkki6TJc8OCAf2Y/5x2RAdJGdglrVzEFEUdqZqcTFp21G739q5S9G5Cok4sRbW3JXflRuotpPDx1e3T9qf+sACdAySuW3bAXSLXgFO7lW5kW5p+0KMckq1uDtZxVJxl3SDlmSAMIIExUQGC4ZPyWLHZJzgRpUtDtgyBTinMaLWp9n//6QAt0jG7JbbsAJIuTwQHhrGwxX+FmTouDI0ok5WK4l6cadL+nw8QXrnBinyV/JopbWtX35vNFJSLHGO4/q+GGuu/xha9946V1gmIKaD/+nAECk8AAAIkHVqeMMAARIu7w8GIAAh4+XFcY4ARCiGt55IgBAA29ZrhgMD9fCDdaBCKi2JcucOLEJaPfoK4jAyaQ7wRjMepWt5Q0tysz8rd/hPcc5l1ziBdK3vQgn484jY+pZL9f//+gAAAUej4fhlIPRAuhmGWThGt3R9za1+fv//f////1VCL/06nvOqGeZSf9CWSIzvi0RlVkAf/yEILRl5BGohAASEsVHIIAACEJRybbxCBBOEr/2yv3GMw3I6FEXKd63yWt1CYvNdpMV2ea/O9TfTz/Z+Z6t/39fM7DoXoQk/EjLabE1upObgDnqwABxG/prRUkY1YddprySz4ZM4Z9whlwUruY5tT63UIaR9V//1LlonWZ//9tEqlW8paP1L0SgYUt2Rr89rI1SP6MKITEFNA//pwBGpAAACCFENb0SUVREKny3cZIgSIlW2FoKRMsRKfq8z0iaAAAKQipLrrM6TYLnKP92S33AdlMrOrXETFV0qWlFnzeRS6dNO/+oP1X1fR34L7dPlf/DENjko1uDBvFf07qvVor27gCgluTXQH6TD1xYqiN2mPk8EITBPkQqPtrb06M9qv/qP6+R970p9v+3/QiIocMChQqGQymwqLsaj1VZUaWbJXCXJVsElqStKWW3cwwGyTjGjrDRf6xtWgGxul51Ib+zlINNyq6tV/+3l8IP4/v/o3gnv/flAV7+f7Nqnn6J1T0bZNbUwZfUmkAGu79MbbTObUuH2gJ8H/DybMFRyL9H75QsEN+aE19RpCUE9Q2Dmhyf3t/a8wi0h82/V/f3/6P3fovKJ1Zvd/2bvVF/1piCmooP/6cgSZXAAAAhIf3lBoO5xCZJtqLKJWCHhff0GEcMEZja7UERhAQJT4FRokpPN0cSlYih2DqwTJsSGS3WvOTtqyoGWlexVzdCwpeU1J32iRztoId+s5D+t/E4PvLg/w+s+rUGInPhgH8AB8CW9ttcdowPyK9fSZK41+CvhYTG6j3a18B1rCFr1Z7Yyd9oRIonflHO2hjv1nIf1+D4PvLg/w+s+p1QYic+GBPAAIAAJJcoJ+GQPJC4mdNEiI3gQ2AF3WQEMRQ/KHC46o+t4niB0MQfD5wllFB+s/+84TEfpwf4gd5c/fl0BBRc+mJ6UgIAc9i4GvkJ8GZvsyGjD03kgomTMIOtriM5RDCYRJPY7EEHh8g0E4PrD4WDEQOE4PgQaNA7wwI3xO91WTKVH+nLn9X/8ptTEFNRQA//pwBJ8eAAAB4SRfACkYMEMArAYFJgIJICuT4YRmISWK75gWDAgogAjEZKRCWBVMqpMDpLy44FqjYzb++qsRRm5abf8pJT42DEvtBU6bYFDxI6+rgW06/rkVfm9tX/Wr+rWBABGqsQBi2ww4jgqEHNHlToVLHg0wFnuaJaiNpvFUE1MEvBU6bFAoeJNehRpUCw8dHcXQRVX5trSSBivTsWpPsQrKqCmdszIksssl1eC4PWTU5GSLMBYIkBoNJKlKlDSy5ZmIKVQbARH9zxRJ0xdAXGlHG1H0azXeSF3CMHBQXa6oUNzUiu8NsSVJzAmBFqpMHiu+04IZxsYzJFhL4IeqFVRiPLhEcwRUsIz5biCbVBsBB2rcxxsKGzrk3loQjShQGlH0Oi6Lr2iVxcPMF1uqUb6ed0aEwP/6cgTKnwAK4fUZXimBGiA+YyuwBeMICQRNciMsZIEijK5EkQ0AAQDOUwGX4w0fs2LbwwI+layxv65cIl1rSoJMAdT0hIVOoHOahgve1oSCjluXHKDT0ant99VfzfZr0272V3R7GewsSraGqRUyuduGwjTj2nbkgosq6cJFKm2uMYeOekTCp0kOcJTDBe9p0YOd46do3H1K76q9tj+x8r/sr4++0TULwRHhLLJELFZLifc1PaQj2xOQLAGDwwNEdRYOrFGzS7xQW2jj47IhBJ6GGCJgtsCUSuPA3EqaSzdzCqFBk+6uq5ZGwdXNOIiDo+ZDxQ/QaIpqPFwXmyj5QMJP6NZk6WlmYokRDmk8sUafNLjxATQgJKg0yRGJPTjFW7GuDU8I7dKa5liCpEy3XUp0RY6tMQU1FAAA//pwBPZ1AAACDyRcCMsZQD/h+4UFIwoIvGVxJJhnARcKsTAxDOYZ8iyAlQuBJEFpxTODKIgv6qbPsXlbzn1iaG2drPSYNqgg0PIgqJcwi9pEwnW5DDqpAj7du4Xc9mbcVZk9ctar5FYEAXhRBITUNOCfMwTSzRGI/pofcEOUfLKDsBCrDxYk5hIy54ROSaniMBPgUbEr1FdR1/XYxtFBt+l87rN39dmlACUSGVW5cnSMtABCOS2a+D3vmaWLYqx0kh1eeL7fUEWslWu1wkmGipyVWgkvTJWLe83eurUXeXyaHq5k4KuMpkn2If+0pQSCklLumsSnW4r3j9WyfsLj5yGpszY7EnxXkjBVTaHnTERih5YudO2sSdKz00wUxLOlHqeFNwsdffGjiIqVili01xx6rWmIKaigAP/6cgRfFAAAAhgr4+hpEwxDIWvKDMIbCMynaGekSQEUie0c9hUQrRBli0kjaSRMYKWqWWd9CxuRm2U6gz1DM6MtDPhVdRAuQqXbsv91Jt5E6jZG11ssoh1d6n1hklWsa7GywK2BXb/LasAKCLcccni4erciM8OY+aS3D4RoMwkXkqUCyBLMrz2G3uYetSdWMYYOlVVhks/fmWCLFWbrUTynaaWYwE1nkEyxerUAEo3J94YV0jw+0wEzCBOHj8AlFnI6/HyBzFgnB6Lztr0Fd5fs66JOy9qs1hzY9pFwLD6aSD2uVWO+vthUgWVR9VjfTvtYuAAASbkmN3L6yNJ3sgzYRJYGPdxq9gA+DhzGuMjaDEH3qU8e9sT0tQpRdqXXsoh95DJpmjSVLqbidodQi5reppL2uf/qQmII//pwBJi+AAACGBVjaGEZzEFCazMt6RYIzMlzQLBC0RaJ7agVjHiohFqOuSSOS0XtnYG8sfB9GQGjNlnFbwYzGjDqSVlXCoXSRIPc9pmsXj0am0M3JJypxSnmxG1IotabkiwB6MP7kIKrACSbcU7Bc1VUVZ7LnmtCE880Nwvtb6ZRZBLUmI7dJPMrahggqnKnSEFZEQvbW0e0W0VXrnrVd7rLv0r0adlNvIugAAEItuSQ5qFUsJwz1FWlQw1AnFg3NoychmhSa872drd7tVUKUsrWdFy7LRNXZV8d+RElLhiWBrz6kIpHNqctbas7U74vXAAAIQkt2xCoS2Yk4LtHxpOTvQryivSMbsQIaHwEOShi6ztpdpJj4we6OByVvNJXSt6zwSknvzvYlKr2BkJeXtdu2I+lOhMQQP/6cgSSiwAAgh0PX+hlEUhD43sSPSKECGhlf+MI1GEJDGxc9Yj46QCcWqk9rbi9gOBThtbLYMLi9YgdQdGGHItHm+dacMpfY+pJFggUYKTiuwH/5QMcQJKOlCnXD7AfOS58SQwQa9h9nLgL/kutZEZguBCwunh6wzErbSdn9xZ/7D4pN2r0jy2+iB2zaHMUMK4aQ2tHrhu4fY4XJGSaVs3iV9w9qNirkMyXtd/WtUEAFDMom3sJT8yp1ARqXrzO6vf1b/pQ44ypa7qFK2L3yeCmT20raZCYdxZ7IcUW/0+49+sbypKt2Ku3Yx7kFtTcJEQkBpuQQdZKgkqoSBdwGMDSA8xdj9c4BhnT0hYZq8pHbd2ZWqD8LCycSvad7SOLSpNTxQjztx6e6kSLLfAXqse1dO9MQU1FAAAA//pwBKTRAAyCGxlZkekqsEICW1olqDAIjP1gbDCpgRSfbIzECWgj/+xnpd8ahzBqRaMnBYfUlFEL3Q1XuQ0OnJs9Rqrhy5poiGUpSuoiyk1Aht0tdAvDSZHXPfVDpMllnejUVcorO/6jwAABsB27f/JDCpg5ganBBgtGom/BUnLCQybkq4qSxr5yqzflGWJBBNmVIgSrnpAmxhodUbg0yzuZVWBUXR2rf73doAajcFz7LsOlFUd08St0eFrJ8moqJq6ai5Fm0KSBlNajKXdx8/D/axQWlrK6buq5Vuq9U75Fmuaq96rddejb+i8bV++oAFyXbKuDUM0ReFAGWkrIpGLC/4ZE3KhgTJGMPeqmx3wPtghNHNs//LTWbT29D9111XX693tp1B4cqy9B66i8pe5/7b/WmIKaiv/6cgQdJwAAAg8V2ZnsOiA/xFtdMOVCCMxni6QYtDEgEm1cso3iCDm22xvZzqJRhrmALS+F+mjfLYXJoCWq5QYvQuI7UH34sSNL8b88heQEiDYJCZqUKWIG1PULIKT8CgX1UXa9nt/VAAAAAABdtuIEfnxG6xcCPQ+RsIpwvjhg69B4m+wv27LYN+Vy+7cSSNNPNJWwgDXvMLataatsV6yl5Spz14ibTaglG7ZHZEk5gkFV3MPtmrbde98mDA1HeHAK9ax/BtLYp3j2iB8Jpm4gAAPwQFx9F92awQlKw+/E459agzieH9BTwQ9Yf5SAwBbbkuXvcJmskGkVN86U1cVDaKMHdA0o/IL83WGYTnP0l+dqqsC4JLjQBVUpXs2dCZ6WYy4FGcQohq99VfHSp0QmQ4pFQqhMQU1F//pwBKFhAAACGEPc0GkRIEJFmxo9hT4IDGV9oQkp8RMm7uhQiRLAACgzq0iVlAX2DQdw9x0lfhOc43VwXR+J95BvozryIjzTXLstoJWkWtLf796s1uqUO3a/fgpN1QX+slamxdK6NGkUAAAwASabCs+jlJtmEdqSgxranodH9whW6B55FmT5GIEe4kB2oHfNFRH69K2owslasm/quQ45Clzf6yVbJJ9lO2z7YAAEUGU40QUXPYEcVWKdkJw/rOlXO1ARdDfsQGkKxHOutETCw+DRueg0j8O4l6A5/EqvJLGaj3xEDKbpFZ0Gq3QAqsEZLr+rRpsoYh0UhvD9w7KtXTRB2oT2oT9n6H0TqvzfeWw3/MV3n0W1jkVpm8PT/+/tZkKysgmktvEQhap0JXGjfSmIKaimZccm4P/6cgQpTAAAwiI+2FHmKtBC6csjMOJ6iM1raPTCgBEUHuzOknACgAEEAK13as3OU/DaBxF9XGzihjC3Fi49Ib3BwkOhPxeZgFY56L4vCg2a/879KGUpaTq3V+P/5v/+3Sz1Fnk/R6tj//qAKTbk2K4pMEHDAdfNp+/N0UUB52EUvbLuWlB4voT6DOA+klnX3qiL1fqnl8ctd30/1X1bkZ9TP39PJ/0NegrnV/7EAFBDcctysnYkHZ8TRtdyjuTRduPepgkguiu58ykxhtLzJbqKuyzasb2Jyn6tuS7VK3J/07t3fyr2rrbT/odOby+3p/TjGJBKbcttnRGEBPMKG735fQMszwn6Cp6T0HWjxM7QhznlRAY03I9TvczU1uavXo1/9v9081ufdXI406vYniNUc/qep6HYpoTA//pwBIN9AAACE07chiSgAESJ26HDFAAIlIN5nDEAARWQb/+GIACxBYniQhXH4RNeHb3MZRllIhd152ztu/pzjlHP/+HBYouRv/8PiIHILiYv//+HzE2ib///+PY+HwwXIa//4ePlwPJgkkuM47CJnT229abfO5HEOOouPtqXV0uvm52Vn//DgsUXFG//w+EQOIC4mL///h8xGtD7///+PY+HwwXIHP/+Hj5cDyeQBMIlpQBIfRI4eLCzv311ZLtCIE2WtPRLKaj6s/eIBs4fFHeFyTxorJbpAbFl2vDRY4xFvSxssQD7ZOKEkl4bBu7KosgwBCYUJ3GkkkgTQ17sg2ZHkdB26EQqttYG56M2iWU1HTb3jGzh8Ud4XJDxorJbpAa8WXa8NFjjEW9LMsgn+lMNg3dlSFiYgv/6cgS08AAAAhI13WihFcBDpsucCCLGCGTZb4ScTsEZmy2kFIggQAKQIkYAAAN4wOiw0yIH3ZN2zdlseZgccogEzCqIw8nP0teKJ0Pa36ZAZH/H/j8q8YSbIa+oe2Pgy30gZ+8tneSn4AAQGaQAAoO4IBMDHZA9kztUrcOJvPB6AzriUVeWfpa8UTp7W/TIDI3+OvXH8NoFRtRl1t6yIlpaWCfRNh3TPSvHuEZAAEADeABb3oBCYxrLNfJQuDst2Md2msFdHH4zd6ylu/6lcM9ulH+/mfczt5/fjX/ZkXbciVUySaqlEXFTNYSeLcdrAAQIYBFukjtxP+YdkXhBODfYZ7Vg3VAY+pPeqFLV1b1KYM9t0o6e/o7XM7efo9Yynr2KsyLtuRKqZJNVSiLipmsJPFnY7WmIKaig//pwBLQzAADCIExZKekq0EIoCyElQnoIiQOJooRY8RMgLEj0nWgAIH1xo3k6NpcK7MEmmtvyYv6BUPUBUxn6+ED8oa6BnknqX/pUtScrX/6IP1N+Z5/GN7/qyPTiLff7+/qIDvUv1MbzqgvuogsFzIwE5lQMWavLVP3DDSFYkpGY6YO0PL6kuszE1mZv5stScu+rf3epn9zPP4Iv9OrI9OFd95ZOJB3q+piO1VIadkdUljTcK1LDDOgjldB4bgK2oWWkIzaIKZi6scAyLx9ZheBSV5BZ8iZy/3/BE/r5dWJMBbj2KRhBlv1LXjnV8wpdQErVXW5ELRhGGdkbB06QZ1jTXwLu9NAOehfGTQHP6kH1J6z5QJ3//rdLbf6M2d1/vbSU/09dUMx51XLG9q+vf5D7jLKkpiCmgP/6cgRXlgAAAhJAX0hDL4xCyButGOJJCJkzeaKEWuEUmyxYxJVo0Q2dbertMaivHkBVvgx9AY7rUjNiW5O5kEv/9Mrpb//Y0Bjab5GhhmzeJAdFbJIbQj/jVpazYlrXlxX6GPoZZrtm0wCCIWm7bHN89LUBQphfN1ZqhCT3FO0GQNoJbUJ3JDpT75tCardfWTRHwjMdt9bUboPt6N27cG2nU93UT76uLV30mowAMUmrNtN/ZhAWeplFN2QvCD5VHrxzt24D+iC/8/vxIUZpI8fMwkKYtFZF/FrKDkLdq9t/+/KyUkCkN0HFxcxgw+/0pgCBXfQc4YjsWQuChahjRcXviBuPoFH+8B83GuqEPnouTgP6Z+nP7ofM8iLsW3Z1R2UoxKdk9cWdGdl4Nyv61chf5FlkMpiCmooA//pwBGLyAAQCExvZUWY6kELkG40Yo18IJNdmZoRYwQmOrzQjjX6AAAAFEo5WXkCFw7IvHwwDGJLcnYbCItjYqXdZQWga+c2g557WfLVLoLPG7ux4o7qWXe0SXU7v7a72tkk7LB1u+zK0AAMsIFeSO4pPnkp0YP43iCQ+nAZtBo/oGE43oHPXBOWSRc7HAioa9ueIKbfal7RIac48Vs0/2tXmhTsYw83ffzdjABJJLbpEmMtJaIQjhxPRcoJI5PPZdOn1oVR2Pk9uU/M3/rzEKMRVpv2VuM3Yaytk6iVaXX9jPWvyBPIv+Zeq7ZIoAAJQoFRIpOPVHBsPeLctapMO1KjK5QsjZV9CfdbiPsmUQaUs6Bkm5vpWQelEX86zfENSnqsVSPeXtJhL8rw/SjQtMQU1FMy45NwAAP/6cATxNgAAAhdM3UhHKSxDJrs3PKI8CHx9g6CcTvD/FO80NBUegACpHpaY8K40oGZDS/hHQzD56GAIc8JvxT0k/2V5HlVSr3TpWjVjVZ7y0382lfZ+RL+tl/+9Ue6kYY505SeJLrv/IgCAEdl2rui+8aSzUoxAIgvh5BfC2xAcGrhRTjVDu9CekgFX3vydmbf5PYy2CkdaMuc/vj9O+Lr/F2azARu2aclr2+mAhFqGJuyRyhzJSGR04fFHocpsURDqg+jdioJbTldnggo97Dj5DeNKXgxYDqz8PMiM0AzTuz6kKgEUHfYFH51pIzUJHpAAIwkEttJSy5ODcbfO9ZZEBy+EH4xxvV9G9aiv1b0X3OlBd9Ctxd58QwFgktZ9qttLpiKbKlxni/7dbOl4J0piCmopmXHJuAD/+nIEphIAAAIcQOFoJRLsQmKrmQTFB4f0ZX2jhEixFZNuaDOJ7qCQEpIo5JJMFTA5O9ZvD+jizIkzEeokbGAul0FN+bz+voa8J6/Wu3OYCczs9bl9+sy0amJyONQ4dkTn0JfeZK6nkWklAAWOmq62UdLSGuLWzWxPsAwIiRJxF+Z4wyJEElmYdl321ZGtaA85hKbPPDYFDK1lyJoVPsUoAQM+9ikO3PzH5p8hQAAlCSm025SAdVi9RlQWoXqJH5fWbV+Cb0hjkVXemvfRUwbxI0ZsfPxEihRd3XWKlVnUqRWpF54+VDWHNr9wyACUAW0klCG2VkAlojpGFRcUeVbq5jyttS3KJOEJx8yonqROidX4+Z9EuFyB56xrC6lB1i7DTFmbVDESVdtGO4TMblLR0piCmopmXHJuAP/6cARrcgAEAgUm2lGCOtRDR7uaGKJciHCvXueY6kEWnvD0UI+WgAAAQltyRhT0QehvorJzAxq6TNwDnDeEdsIMWY0ZBwzlUNU479v/2VF4sOXuS1Ta9kq7qgZLSgK+xddtf3UPv16AK6EpLbcUGlB9EElsj3SZw3Cejp5KZI5oTbotTfMI8rX0eutxvHZJC5q+p603it87PQl8VnProrioZ91G9CVvG+RRAAA5bbA/bAMJxUGWC8RQ444y5QEoYe2PDrMkS5JpIcMyo/0aNGv7e307M+XTdleVNnScqbYw+WCW21yJQn/b3GP0/XUgVbI05Zbbxm5zpeL0MUaNCWIPnXz+Y7NGt30FbtoK+X3XXj8Pmqmfto1M4ER/OEjlEEVHyS22UGpN6zn60zhg01YMBKpMQU1FAAD/+nIEddsACAH3FVvRKxEkQoYreiAimIi5bXlBFF4xIZ9vaFCPVkAAlBTcluhulgRYhgReP23M1QBtQXjQeDdsGKwg5KVNp3S2G3FmFOiTap/8TgohYua6zle6C+yrZWEXTIq70IALICnJbdF5hdiEakh9rOihq8ivQZ4PM1mPvMVrj9/erOnCnfR/bS/jxx9/bFw6LB6Les4yYmB/RVUxbGWHEjRe7ZNARakct4IoyAhMRPqTh+gf0Ymq8GL07v+Kft5/P4Z+nMlMhExECMRigDvVlNdyCaXJxk4//X7ksfZVem3trd9G3mBWnfTIjzRTakk151Qw5lFLrcSXP5/Cqajeot3LElV24vXn5rk+Vyh6A8wFCwgU1Ls+L8Ot9GQLJCV9dOXhAN1OlFlnsDzTYWLrsjqExBTUUP/6cATZjgAAAgs+YWihFcxBCBtaFCLkiOxVXGwx6oEQH290YRXegACjkqTslt6vS1HicfOivMCPnAj3jBxmqrzfnuvXJ+BcK9+/+fjS8ES+nUb2PxLz4QZ2S9jZJ++2qkKHIgHHUK1IAQICUnLcppAsMCwIKoBaFPYNwV8o7xjGapeoP+ry+O8/p9cn43/nzrktl9fdkhX0EeLePYhvrU488YEIuRvcrYACXbvd1XYQtZtZXICbUlGjU7nYoKJG2gATnxIO5Ypn7TSR/lYJ/4LBY5qc5W6zbTuksM1uctjXSuqIm+fp8XfbvR8vh65/uaASiJhbbbk05p/UXibB/keLbDeCEcCGO1StkCXZMOTeb19Pdeoe9uv79SWoVLW9fsmEinBbEe/ycrptZTeFiqThJOmhMQU1FAD/+nIElW4AAAIhP1kdMOAERMfrF6YcAIhFN3YYwoABErEvBxRwAAQk3JN7CkDInj2JoGi+AxYimTN3HUIS2PjcMJlRgx6kn0CG/9fOby3/Tof6NRXfzuavlG6ffzXmy9wiwxR85RjN3W6LwAAKSck13kxwEIa6WoTrsmV8CmoGltRsLKWFo7qXbQf82PCc8r0Zuc3t1Tob6/fzupvco3/9S3HibVUGK6yOy/dW78vVq6YtHN05OD4GYw3doZ0WeYODWo6BwGFNOlVEF/9aGf/8jEVjf/bFA6Hhw4TN/3/w8X1dv///a6ooqIwL//xETKXOJJJ6xmSGs4F4hPH5mroDxcxkG4IFNOk1Rz/5xE45//yxEcVjv/8gOjYsWHx7//8bG6Vdv///a9FIoe5v////6HK5hChCYgpqKP/6cARnVAAA8hYsY/8MoAhEpYug55wACEzZlaKEVXD7hy2BgrxwgCJBJFM99rG3HYIpGx9vG2ZTW6jgf3o9DK3V/GtL1zOyIcrOimMjLvrqqiNhGt1OYzpGRAp11e7FFUztfovpu8jVrvcSQvqVYmZmcpVLM3KKNCjQTlbqFQAhJGrXo6pt/1avXd6TzboqHI3/VVHbCNYyZzGdIyIFCbq92KKphOP9F9NyOHatYCbltLlkbSTnFbs7jrKyWvy8QBvFowbEVcy18qSTaNBiHs8yyJkaC/e2sduHyUKqBl2K7XOUaF1izyqOWtIv1MleNbuWvBXdgOyixILLlIIavneCpQNA2RTZRrqQvMGTSDIjhWLRR7Q2i9LgG6fkoVUDLsV9yjQvFnlUPy2Rf2V8amIKaimZccm4AAD/+nIEhgYAAAIcNl5h7RLIQ4bL7TECLwiA2WQtLFJBDJrsyZCKqAAEImD2qrIsW1hrrWqV9bWUkmfbfYNojKZer2Rn15mbyon/Lm6f/0FbM3z121CKe+RxK/e5QFKyNJgtamRCpnPOT3awgAZFQ7U0kodgRMf1tKnl3raX+NtiQg/3lojnU7cxWeRgZCf8ubpovT6CtmL89dtQiv+zW/1AUrTuVamRCqcOuLcbjwnEIkJJNnhGFaHJA29yRy2pvlLuEY8pPm45NkpcInx/X5iEdNmU6VlbutOf6G65eT3+bQrb/TvE/y2pbV90tQn0/r+bAVakJ4n81qCJK9Mqr19wNSZZ65nnjzXPsy9RQMNl12/ywBYmCPzX17+xbzXLf7eJB99O8Tf1jH5EX9ApCm/3of7XdSYgpqKAAP/6cAQ30QAAAgM12ZMvOXBB6BuaPMoNCOzvi6QIsPEYoHC0gQ62BLvygQpDXjcjC89Pz7r30K+d3h+19ZdZCENt1a7w7qJmW8s/UJDOr+r+nU7fm6fS7f7v/kOqr23Ud2h3/r789WABYAjsjbheAeRrGjEx1Ogmru0ma0ZgyAvkz6qi3mljPJGd8/zf01Xc2z+9ma795unPtoW29e69nyr/Ux9KP/rr5YFNqSpySOSOiQ6bVR7lTFaakhZKVrQwhI96HZHJDpsgFu869Ef/VPo/+jiq+ypSno5A6/RbrQSItPLDOihbnp0/WrvOeg7TUEEjVEk3W23AKFh95Eo0VT6T15NxUCQBZO/uXxJ+hdsfkmYyMUD0XXtnqrbM/b6d3wRej1X26aHug/lUzHSX7oQ54Mb6DrExBTT/+nIEVnwACIIUF9gzD0jwQ4WLWj2ldAigi2dHpLIBExXszPeIuACACtRcYSjBTrKETiqGo1sK0l8KbEd6ERvTZ41XoLsEUA0L1lJs5vuZ+/DZIFjyefija9z7svyafLde2i/0r+n/0uAAgAhty3UlwpLZqEvKhGvZHN3PreKXzCYNNgQCSnRRIi187+CLv+v0N9E7/XVBaeJ6q1KevI11OLP94up7bwsO10eaDAFpySRIhXIifRvrL1GyN5fNX9WpJ+ePrFoIgovZBscxhBkaVOMCKLoK+Iv5vHPrN4YUQeVNtOnlSBOzrR9a+9/9P6PqICTktPUWpscWId6whcSrkwW3nG1vXh6brQUgMWzdrJE0vhPRdaP6v7N0/f/oFzhARIrYJnquijG0ennbrmIXa2eyrv+pMQU1FP/6cAT/ewAAAiE13WkGKShEBrwKDEWHh+jTeYGY6LEMGm0Ml4pAABQQoBU9l25MkNhlbIUsW7cGKYVW8Ihqp9UZNV7iSrpfq7VlZC/RT+lvN6N+rLRlHIqvvchZKLpxywrasl2tyGa7tEiQx3CbjjcllLD0VL2H9nUpnhBdPqe9RbPmNm/q5lejaq1+32bUIOlBxTa6tocg6Kl3MnWM0dUMlx4xB1AffaNnStX79IABEKAr6qwVSNDh3iIQS5Zmf4+G1M0Sjozqc9aAgzsYYndWTcu/T6n+7a0bsn7py+Rr59FuzAy2rYpFmd+R/QCrbtsBxBbprIwYb5kHnb+2e3347RcX00jr+t8SV+J2z6uR0SyrR0dH01baisCdaOnXciNHEbrVVvUyyk5zy9T/T8RJiCmopmXHJuD/+nIE1MoAAAIfGFxpjRnoQCV60mCnogiIpWVHhFUBHJSsqMedKAAwMAAna9rv9VHeKssnjHQN9Zro55iKKSzjc40VU8/wQeDWulB7RLszegyAwwaSF7Fthlrj7WrMDdf0pEls05nx3f8iALUQOyp4Zt20XIMazMzGIwON9wm5Q1/Ln6ifx6YB1Mp1s1SrhQyckPGepQN10JPWUf++ttfr7lHT3yifo/8d6QAIAAElu2eu2GK/W1ncaWcXSWtsar/C2plmyBBrN6SWPMkgws/i3+4im2OQ6kUxfFrQaGDBdA9jp1l/i3X6VmLLa/36QBIACbk22dPFN1pZ4Z9SxAsQfplia/zDxtvB+yZccNlDLrCr9RRS6n9SlPPtpdrdcusQkIl33Ch08eey5iL+In7fb+jzLv0JiCmooP/6cAQTsgAAAg004WhgO4xB5puaGOVZiOEveaGcTnEYCm0clonSKaYiAajssl3QeDVdr0p5JxQXoMJuvcfCJnPRF9etHXv9XN1f0JO1LKmu5za8kbo0IYQ37q3WyJUDdnAz5FPinYtoDkQAlpJKNd9wUmUydiyzbssx1woHbaTt4U3Uv07I79/I9tXvobyN2/orXEhCgwJFmLiOS+E+9bWN9TeBuVFGz7b0SiIkCSmo26Y9eqivMIeSBgG+jw0IQw1kg76rfUf1XoZex/L9TehFYqGYyKlama7rKMxK2trPorc/Zr/Gv/gn/vbvfZ88yPAgAUm5bokaesCB8W1NIQN18g3uorPw7Apay8y6QsvB4JZPDLtK2VuqOMUZc+pGhPkt+a9LnkCoCKA6zNkBzyXbZPFnTVbExBD/+nIEFR4AAIIgRt1QxRRMQ4U6wmEKoAh80W1EGFIxEBpsnPaV0gEJgJbJTde31lUnv0XOf0PzZq6gU8a3YY78i8aADO6lEjanemJfm+rejev/z37dE/6IzqbTqgreo268v/UTrAJF7dcVAC/Imqq8VG8AVwuhw8eqhhvuX0b/t/z53JscX274HtIGYPZhTGAYUKkmAoX3tR9+5T7+l+zehbDboppxDkNsZ3b6/pQAhAAohFwg6HMCSj2lAcZPLFqoZGd2hxOs7IiJwadBPr7EH50eejP5fRvX/7awS0ljDVKan6duxSwiL36qDzVXpz3x8ACm45KubVaUk59KquFXmemqyMm8itEKG46yyykGNI8P+x9n/GN7fMj6VVaFthHqx+110EhYXo2Ct+3fW+UxYUp+zR1piCmooP/6cARsUQAAAgRXXVDFFDxDiuwtDAVxiLElg6KgtHEUK65oY5XWAQzQlJNt1u24nisbC09z8Yubx8RHVfz0bCB+Yev+n17uzdPoVqIEdc/6dyH9WTj+tf9kd+3z+jf//6xOTup8NMSCajBTcstu9WTB6daFfJ10EatKsJlbhz4t9+cYVtSv0Yn/n5gE926SVsh1161f5Pn/1iiLscr8t1q/9UZ/jlapEO+dTBJbibkskm6ECiVdmujvOMCNxF+NDtx/q0uVugy30cNVczNyOmzeIn629H8Z9/r6P5/r8v/p83wp4CPlSr67SuXniWz5oQDkCY0kpS0ilOGSneieubwmFZ+WjwfWh6FdeH24DgZ1WQPD1szNx/z+j+j+b/6/H+r3y///+b4hpIpT66frL8Xe9uR+9MQU1FD/+nIEf6EAAAIbVN5RBRPcQ2U7FzzloIjBI3+0U4AxF6uvdoxwBlEGhKbcbtmxlUKHNXc19QCTRm8aD1fSMYtoeXjQJXQ/ofq/oP7fR32M3KT5vQXbv6ff/0b1+Dk1FW6dzs9WCQe91X0AQBTTTkivIThIMhufz7WiURI33pNf63hA0RYvtLm8j0CyPwENwFBGnjH7KOqqlelU9H+LJHuUjTp+zfvq15fT/sznqZARbhLccslwRWSZkdWQV40BD4+beJxdMfeC8gyTBq71HAez3c76fTyhv9ee2x97uf/6F70v1Vuqa/0+a/lLin0Z/DdWr6SiEFEEXI43c1pqj50L9zvYEI6eVGh5r3We0xkEJ5+OBK56fbbqm9CPu6+7a/v8z1f0f1b0/9Pq/jR7Zj9e/6qz8dN14r96YP/6cAQbmQAAAiJiYb4UQAJC7EwjwogACKyDi7xSgCEWhq5DsIAAAABAApFAqFYsqyoKEXUMq8QJ6u2jzt/6ov///Y53U6vT/3Odld5f/5CfO6lavf/5N0Y53Ja7wZkRf//+Rjv+qI0X2HdAApFIqFYs9A4ysQoI94cJ6v10X/6f/3/2Oc6nV6U/3OdlcylV7/8hPndStXv/8m5CHO5LXeDMiL///yMd/1RGi8HlgABuW/a2NpSgDoa6Pd2KyGeYxqKJg8SFjcY6/ulNnQrIiPTZRwsDBom5xPhA2OI2pmJvKhNtSVN6NTHcMM+vkVPdqDm6+q3QQlbo16Fy+AJFq1L6Wls6y7BTXDMDwmBpkiSv2XhoUI40JiIMjUIW/KEQOEHrGPQ6/ErdjxWl92KI3FBn0N7yKOItFID/+nAEY+oAAAHmEmRoQzjsQSKrmWDFKAk9A5WkiKexGprvNPMUNACo7bJG2kSVCCshOi4VifTO8lokBqjvoUNZOVjGqjE4OE7RJfXKuDJM5lnaTzMzYEsKnfVU9ksW+Tq3tmQAOodQVZIIDIVTkQoZt5kauLfAzqXRVB9/GNugcdL2tVUnBwnaUv5VwZJs0uXNhxmZsCWFTvqqHMiYBbMnVWPbMgzNaZ22xtNyb99D6gnbQpHxd0a0MSo7aGBEpMS0yV2Lb9MSeb9/l8pW2/sn4r/+ZTPeoiy3JPHXhMmpCS4GPWuYDoNYlOiXoPZIBAjaESxpAlk/GeP2qX9/Oytko+4yjSC1Xvi4I1IHJZ0GVyt99MSebbuvl7lFS2b87J+K/hV+SXTzrVaTe10kIsSnRLyr8kmIKaig//pyBE6tAAiCIjVbywkR+EQmu0ZlZS4IONdgLTznwQsgLqmBlBQAAYPZlYd2zqUyfFstu9HcO3nyZB83Kb6Fnr782yo5ynIc+0vX9E1ZV3Jmzf6KiuxmL/rVHBBR/u6FMRrdK0DB/3O/5sCBf/+K6hAUpanp6DZvZ4Fdt+l96j4oSZAnt8IsisoFXwH6lb1H6zlRzMWbft5KNKjL79a3GGT928wKeSZdMEW+j7l9QPmI0go2vpr8PpgM2livgRGCWtM83b+u2tBYcXPRUz51TKLiWZXJNogFFfl/lvM+b8vm7annvn/v/KL11ef/+3/1Xg7dbJKMHdcELgYRWsFOasP9rxMVruqKpGUhm6AJfI/QTauU9phHzpl7aP6P0vamMPr6X+r8P6X4oretv7dbLeed0piCmooAAAD/+nAEPtIACIIONdxRhylIPqgbaj0iLwi0jV7MPKXBExXriYYJ0AAIwDRcclAEEJiviqXOuhZ1qGiMldSQv4RfqIHd0EBH1+renU91y/pRZuT6P/MG9FRm6G5jjaiSYeRKsrRdKH/ZsAAaAFNMtwLwhTJaOn2LdVjbv6sg9p+b+QhPq/87n47dD+j+DZfJ2T66bM6bWT//j//9rYC6atzSfkPr6j/yRAAqoCoC7ZdLZsvO2xGDuzWYaV6lC4s9h0qqcmaeOyppSjAAD2RAFLzCT9x/kZ832H6KkSwHutHvpGs7Oun7KU63f+Zp6QAZoxwBIXKxtFULvvpObe6JW6mPIgyW94PIb8BCA1KeLWBRDn1Afk2o/g2fgl86/220ULSEc5TEz2RpJm3/a5+5H78SPTEFNRTMuOTc//pyBFb2AACCHjXfUMUS7EMGuxo9gi4IbNdiTDRLEQ8V7Jz2FLgWTtU020nHFLVqI9HPih64wATvP1eN+EsUKQID6Ft9vMN5uh/J0WUmU5G1b0fV+oj3Lt2Ue9DhxZCNC2mp2h6q3qZOLAADAAFOOQzRFk3Ea2wIzxXpYIlOnGgj61MQ4sMyJyXZbAl6BPRvT6t5Tej/+VtSVNX0800oymVGVM0fW+e9vtu7/mwFqphMIszsoICVrdLcqq+n6y8EdF7lx3H1UxLb5SuR0VHug3q3kLpZLSCPR/J0nfUr+elqmhV9s2Hs15RS3djP+I/2ECIdstMEwYzDHKg+XvsQgz/tlsy6lpq7pDKGHH14ROtRgCp0+r+n09RVm70aj3QgsGZtC0HmNL84fnb7/RS5yr/rsy6YgpqKAAD/+nAENY0AAAH7IFzJATysQ0L7Gj1lWAipNY2hFHXxEpjt9IKKJBCIgDmWyCi/FhMzVYQmt8nKbHD41U/JVt8LBLqeVd+d8viTbkWCp2RduvOq47VkXW72KQWEY42W9kxEKLU4meAAmACKUmxdD5N9/M+ZUvCq5B0Q+86HD6ym7yxPB5jKxB14anGDtmyXbU3exT5X6OButli2qZXpnhR6iYrZ77rXf5tDQUWgLlttt2Mh9WmxQHyjferwT9Am0UKNAQm4kDn6t1G+pPQ7cteZ9SGbavSOSs+/+U/Lfovzr23YpaaPEHprfQCIB3fUAGBTgFLHbdynfUJxpkdAHKH/i6QW/Eh8Ka+bMWBDO5QoHeRRN+wdvT7v0+r9g3t6IMesRRS1DtWojKKoy1m2lnKmfs6UxBTUUAAA//pyBIbqAAACGUXg6EcVnEBmzC0IwlWIXKVpQz1IURwUraSwiwZENjQEJySSWqi7CmXGFCrhS+r109Qr2divmh78dbwvx62KqPV/lH6FbyJ52vUktLa79/e9NH8FPmXCiEk07CpVHd/1tIoeUlSSSS1hB3ldl2TFdrAUYXvV6N6vvA/Qyosr+Df1byS/6vzK3p5jkddo1rmBR9dKc3nmyNgAGFnrZMH3toAAxAFJqSTr6zgEVsvotX0tskR6DEtFTBgaNSj9BUTqPGbZm6uq5r6nnMi0T2L/PZyKNcr4u+AbSxOI6Q5/926nU70gCRAxK1qWaLsChR9NXE6110ZXdRF6LOFvnQX5qx82xNfWqhR60O9sWz9PjrtyGxNp8ROh2gJsgo5IsRkWpIJikxTUnod6gymIKaigAAD/+nAEbAQAAIIaL1eZ6xUQQwl7ihwj2Yica2TkvSgRFaSszPOJcgQQ5P293pROzDizQvHBq7r9wDFt7dtNqRXYwNItN7jgs3TpE9n6gfoX1N7v0X7fCdVL5Rtmebf6X+vEWDekNU30q0OAArAIklFwoYQsIwwXSwceuPCFXOVMoW/ijqYJRalCdO/m/vw+S+D6+vBqIUu7yf3/5/L+wzoIDEr3qe9Og5MjbPLExKBootyh+QCI2yDMluCTJSlZY7e3loap3Igjvuhp0JNqIWLs4qGN3+RFoSoN1hvP1H/uqyiq/lKU/JZzDDLKnr4m17HXgpNOW2u83UNtQ4zZxVR3OEryh7jJNhQTtiotd4XbhmddAXxF84+rrpn9F9f28n0b1/2yev1/J4g+cj6lkxG08iUZtcp+hMQQ//pyBDh5AASCCUna0MwqJEIF6yM84lsIhUlq5CRLcRInrSjxlVIFTIA0k5LZ0Re0jcvw+nq7Z0ZChKnHzDo0usYOQysGubUPeo31Xr9Uf3+Kd0b5/RvT6P/5//t0t0GNQjbhzdVryoQkbd1Ug2UqDNQaUe4vRn2L6xwSoKkoqBN2qgwmUCHyKG9D/ByvAUlZjnJs3xL9m9R6iGQY0j9u6vW6Owm2v6NnzowIIIKhB4xVJRFrC5EvnTAjWZezFnb42//0sLQdEA76hfp6iuytyr/6p9/p9vT6f/J/9vt0tyr9PQTUIZUMnGmczDLbJQKBakthrOdSEgR1ZPGTL3tAboYQaHGnBugcHJeEX8b9PU3/iL9CfN9/k926CRV6P7fJ6Lpt8V1MjaM3oMzCFEMjpyO1MQU1FMy45Nz/+nAE5eoAAAIVYNzQxxQsQ0o7iizFWYiVJWNHrKsBExSsaPOJ7BMNwLJJTVbTkcMBl79QqfygDtn+1Wjr+gcgpnEgW1vQvq3r6L6fr9vt5W6m+b6emlDGqqW8O1Xr/9vT/vDX4J+nzjkAZiUUig4rL5YGTDO/IivvcPGJVSfAlMXr6BAMrQIGtQCP0Xyin21s/Uf6H+329W9GTVOv///6/G+o9uQvjHVYU1fSABCAYJk/+3je0OCieXd/lqx3wM6dVpHMklmLy/rOGIiqMZu/39W926J/6B77ejen1+3/p/9XVJxnxb75l/UZCIEa/76QAIABBSLlbVmD8jfQl9H1Un9dfMEmm2BY9wQQL7NFwYNTB+f4z8l6DereG9fkFYY2Zzbu/9eHtOmsKUn6qjIRAgsa8ppTEFNA//pyBE/QAAryJBhaqSNCoD/Da0AxIz4IsJF8IYR6AQ+SL0BjDPgCgHl5zYZB+bvUQiivKBJTiLaKQBoRkDnYQVr9YotjyKPhELPAaGggA1DQIICbvLkLUa3k3QsZpDBDc/j+//6rc4y74YYfd/QvbRQywpVzpQh831GeuZQDJOo5uRWOflCDsBhCfSi4OMkAsCADAgRAggJhG/LkLUa91ukMI3fX//7ez+QXcOdDgYd7ioJOLumyKb4oBYUqYdyzI4DcMGgzzwj4g+VnKuoJ4ZKgybMgmCI8mGHuAFImAD0tPvfjyf97JAg9ej+vS75T4friwsnb/WnWnT2lJUr9kgllzGl3mZHAbggyCXnhH2X9JmvK6gg48MlQZeGQTBEeD4YeZKUiynpafvx5P+/IX//X/0JiCmooAAD/+nAEYEgAAKHyIN8J4xJQQuMbwDzDLgjkr30lhE/BGorvFLMMUBP+j5KZtpVtewzRjbaalJA9igTmVorNYmrTmbQr/o9gw4VBIDstZiWMYkDVOmJoi9jRVZsmwxRyLmu2/G9iPUqQ9tqoGKAJPTEnpqryr8xNyjTi1kkH0mqMxCCoKnRCby0FgMFQSA7JJciJXAJiQM9+jvY0VXSwxRyLrttfG1WI9YBDAQDtc0I4wRadp8Wu5vUtqVlsiUaUXOHViMinrNnqZsiZS5dizBQgeOHC1OX6itVlk0bTQ4xlQaO3jaAvRAdAiedZPmWFkXlgvFIoNMlnYsU+h6ocrCgYxsg2aHtzXCjRoGCQo0pJn0Dw+WULVYus4wg5T+XuqRVZzRvQ4AHYhBpso57AfcUmaCN22xKYgpqK//pyBBfaAAISAANdwGxIAEAga7UFYgAItOFwoJhgwRSR7hSTDFACDAADoY5ClYZFQeQHmPBUkVLLACIiD5I+ExgaARw+N1h2gUbLwl4GVRpes4MRQo9t29btpZvKpegWYz3Iok1KEgIcIrJoQDFAMVNBILtEpUcLgAxEzzAecMgI4TQ4mAw6WiJtMZapIGGUaazikKRPbVxamt20sS6EvqYr4ekJZQFE0KJBSCWjviiK5DI9NFRpKpH94cINnYY5cVkpHzvCH7wqmbcszlvC+ZGZ/M6RstMvhjFYuxihELo42/bRm/Ov/fQuB7ACAk0IzGpVIVMFEd6Nnz4KFKqw/UzOEG3KGOTdhVM53ZT7ZMEbeNFTzVOFEPi2WO7HOZVFAq52g6qte6WWXoEsV76bnSyYgpqKZlxybgD/+nAEJNcAAOHWGFsBJhkgQwKbZSQiVAkgYW2kjKzBEpvshMMJ2MSkmjDy1w3BaXxrDwihgRYP+Gh20gpFSsHEpUNKAiFEMYIluZe5SEpYVWkRmEHEMejK6HbHzsRfs9GsCAH4YLilUsaRnqcMhvQdRnBooWfcEatgYshB46Gg2l1R3Bo8SLXknHmNfXCweMlGuBpoSTO2ogZSRyKiK1K1PiX3NQBApIJIRTbqWczazGOno0btUjaecdQ4iOl4V81pI7ywkqIlmSNkRPfaISIuDdFrCYldCjWotL5isBYmUrzZJXLFi2hCEPCh5SP8rugcJ1FSKDQc7qzCbNW9hqkLs9C9OcCadAUuwR2epu1PQ1+RVUnYxl52opksisjlXyVhShMaB9JW2hHV9KK3XP79EYmIKaigAAAA//pyBHnWAACB9yhXgesa0EPCS10ZBlYIOF9pIwUGIRUMLGSRmojDzZyqkw1Q6g5oUcs+hw1RzLCTwKkZQpBib8qIzoShuIESLYVRcuHnST32WN5czC5Bqq3uRpPftR+5f/Z/T9AAAEECYKTjk6B06eNMl9AYqb+Hj5Dw+Bm8oNl3AoMcnmEwpcylVQaqBm5hsvUDaBzXWxiTY8vAYx9H3V9Nl/r6xF0wKgAWgEIuzloIgp6TqQ/9ATR3aQ8kbMkhT70E1+q1+hKIhq2vQRJb1TC2WIPpWywm9ddR1+69+JNAsWft/qbofbS9AYAJVeo2iegCxJEeGAZNZ+oCbK1KUpEROSODsXXMY/OrengbsRVunp05N60CxNh5VqntpuJJUU4zZMLX2G1fUi2u2mSdWmIKaimZccm4AAD/+nAEZi0AAIIHF9eR5llAQ2ZLFjECXgjUX3WijK6hCg2rAMeY+ABVr2uVrQrwGKHUyF+m4AfCPAgYK0mIhGruKISK9GpnYgHUlNE6BxyBZphySbxqnxjU35Kt39mnbchltDDbu+vSBQVa/0rScOOQxm/aKhi8KGOaUYsaWExtzEClfFvKVwCi520/slXI/0/nqb2VpnT8etDlDXTyb3NS6KVx/b27atvyWtAFCXGSyy27y2Yjjn3ZtgPEhz6NoJbh8MIzoHOyC4AeKNeGAoRLC51aOSCRSkcXbmalQHZttkJQ9kIxs+2Fxaim6d6nKJMSsIPDSqBmLxecT1BhA5o1cq858eDt9vFkDQezPnOsG8lZGbQA1slIDutow1eTlmIMERYpDhdwiLKEd7EWsfSh1lf9SYgpqKAA//pwBOVJAACCJRTe4EMpPECCqvkw5aAIRGFvoYiyYP6V7ahhFWJgJmOMtd1cXNBI5DmJEhSxQpxm9cZthbWuPpE1AZziWBmYA51LJE0bQfrJTrHiiyIXuNTbF3Yuwu41TS1my1RK59W6d0gAQAANVMFrTryAfQqKrAYKnsmg6T8H0X2OiJ40dWkLi1OAdglCbhlanzKb9MXJPnJhqKNKRG9n5DNYv/X7b2/0AAAdRBJVyS/KpihhyrAQWiPHOPbODwW2JfwSnFMYHVpQ8Hz0LoKojRHWDSqbWsyMkpL07CC7t2Ratgpt6r1oX6vFJABMTbkaXzAIzEO4cQmwfWo8OsM3l+CW3J6cersqyKLD+e/bydVEkMSmVCk/pRrX8ro1hCJkk73IrHuw1Ver0piCmopmXHJuAAAAAP/6cgSgqwAIghUaVgsMQsBChFqgPYtYCIStc0MMp3ENDW4kkpz+B6bt9pI2Orjr+Vaj5JUqsnKqd12qGL7jk9CfK3VVgfP5wwbaTi4rfTiGdMCAtU8NmSTG4fQZv6BsZe0kjs3Va0fbZsp1eI2eYj4sCaOmGogexF7OPhyysFhpSQhKN4gFvvxbB72PxY23RjvN2dPJa1fdVFQ939omypfL4kL7VObaj/b2di1ElFAqaCAAhAKPjRwInSsiZXArBWAhqaD3aMD3Qa3s3mfVX03RsnzmzDWMvif3NR78V9XMxVGlbK3KUAQwmxxlZVd60SWmW9FBIgQCFupChpTkVGJT0JAEe5CubxMTfiFdZAsGr3vPJhWrjBYTnb2pWNUwis1sp0ZrY+PvbIkrznEbN+5r+VTEFNRQAAAA//pwBOjsAAgCDytXsy0qwEGDSrVpjVgILHdbLKTrAQCKrrTDFC4AgBf5NFWI5Iw5GIDhDjRePYy5xTR2c0LFrZyoosPjKFR11oZswP0CI/sZ/Snf071b03KNUYXcUx9JNG2zO7q//9oCAcbISyjwSTMAWoRRUdGw6PSseEFqPI1kWbXzgX1Xp8Cy1UHTR8yDM6siqtYyapQYsS4Yce3c4qeQK6Pk/s9bvVAAA3mkQeKzVGDxgCgJ4SLm4qqJ/rpCee1weAtYFhbhZm6xodW42pYQnpSvytdmZ3yDoKzfFNy4o6nG04B2ut//pBCQgYARaSTiwDRgfgUvW6HS2dz5Eerzt/xMDLGvrrQAmhlMOHnEe+c9WaQSElvIZ6g7W7oWlSHqAWccQfuse9SYgpqKZlxybgAAAAAAAP/6cgTrbgAIAhpDV7snEvBDKRvdKMcNiLx3WmylqsEUoazphhyiAgQAnJs2IXpcWFRERnv7OU/21ukptRKW9wSisOYLrW4Py/QM9hSLl6v6fb0a2GfzfK6f6M/RvC2/7ereCbdh/P0/5IIpCuElpppywSG30g2y/Uga/unrvMeJ/n+PhI2yn+r+c/t6MnT6fP+3o3/mv/sreeQ8eIdSL+TPwPnq848tOPW6x7lgBlNWouEcty5DpZFLKl7umEgnPul1vmcPy0hNVhRlr1OaEN3mQvtXTGQz808yW9Z5agpnszJPqd5d44WyWHNu3fW+O+v6AEGAKDabkmCiuqaq1WkfdwRO02V6r1MirA0Z8WjKqsfZWqQ9W7Hk33HC1aEk0pXX6v6N0Pfz/V/X6N1N9B/Tmcju2r6TiYgg//pwBAYfAAACCUPZuecS9EOrbC0VIsGIxQ+FtGOAMRigbPaecAQDgRaTchbwatpqBLFq+PUnJd9xafOqW8DS/hR64ik0RITsuq60FkSWxuf0P//9vK/35yv5ele6v1i62VnN/3169QSbFnjUtccvOOdpRamwvWwIL3NDPGj/GP4W9KobZ35ErubC2fKROvz/f5vRv/v6/b0X19o/k9Cej/X7+u1l+GZt+MYsMJmzWOyyOTvlf0hmfnCFd7DlzCKqK1H0icxtBBl3HGJqrKLT01KbVIJZTm9U636t9fT9/u/T53VTvUzupJvkP/XWI8v70gAgI1AIxpO4XVGxLwCgZrX/Q6Fr+7Oclig3Yg6iEaNxBufHiarYHJ/Q7aPmIZVDdNPZ+/0N+/r9H8x/T53y2bbZkN2c9etMQf/6cgQ51gAAAgYwXgYcQABCxfuwwYgACPxLd5xSgAEfiW9/hGAAYqonMCpZWWay9G0da6WS/L0p+WlHVZb+5jMxrOj5L8rhUIpjGBOGyPkTLEnTahYS/oUCIQvYKJR/tkJRzKVPHDMFW6VKpSy+No9wdiSAr9elKeWlHVZf3MZmNZ0fJflcKCIpjGBAUc/qNDoFS8Qip36HAgCgolSiJo5/XU6GCm8iRIECAIRO16Cgo91CIs9VO+hgcxkFyAnnboMHYNuILlieNoIuItmrKwYCwkFBFBUXU+JEHhcAMaLVJb+KPTYH2fWRYweVnWYqofKggKYMcGeiJSJVQ54c4yLV34uql2V9f0r5THBvPSBeWayNoIuItvsrBgLCQUEUJi6nxIg8LgC0WqS38UfsD7PrI2DytzMVUPrA//pwBEHeAAACHzXeeKEVwEOmy60EIgYInNdvhZRLgRWa7rRTilQRATCDYy0aJRToQziA+ZogLMlARbqMLR5PjwSUwmFTI7hOvyORWUwyl/PE6/p0NbhVKSItf/2iolGu+oXO5dSxd1zG0gBMVQGNIFFKTcLTwxecRm9Xo3mGftZqkW8q2vfRVBJdKl6fy1FPb9WdDOyYWKkRx606+v6yQqoXDRIWT4qJX59guS60AAMoAhTVtunWFRZXyN7IkSANpXQf4wcmoW3m9Bv/J3WFRXdkejv/3VqP9SP6LAg4d6hLqIpquWd3yvXtW7DS1ajzkkURiIS+FxNIEst1jCnXCx/RaFUZkyjdRWWoeoTd0+r+3dYVDulHoZ9+tFsyO1dSP/FXeJdRFNVyzrJuV4/at1YaWrUeckihMP/6cgQiWgAAggtMW+DhFpg/qYtsDUUzCP0DcaakpaEbIGxkyBzIEAAxxGOI3KKVcXgzaCw+fUbECKGFE8oWtihV0GPKFvRvEvE2yX6/4P/17+D0//94rpX9+pSJZgrqDr1P9CfSKP0gAAWcrOkb2hVxOmDg7kBNHM91iwrmhBk1DPGAv/p83R3o1y+b1b19+/Qa3/7/x/Sv5tqpswdGsQ7f6PUwUeyWQDYx7AbbcjtO6xVKv38VwuHgLE+262NrGDvG9BITfM4mnt4mT0fSltC/9melE52yrNCxKYtEp1R9c4rULs12IRPfr2lu+shXoAH9/tPdqNL7jPBqqbjYf3YQknKl40N8gWTKBjzy3n/syXNMWh7qqoURF2Wrs2iJ9NvKt/X+i7kX05sEg1RPFO6vlq+kh0JiCmoo//pwBEEuAACCA0DhaGYSXERpy00loj4ISIFcrDBOwQem7TjGlOyQGGrRtttpubWaj8fmDdbsFqHG8/Ir84N9AY7cQ+hRXoTyt6P26MZ/buhtH//1E1/63T3BWM2j4xdZHX0bmvr9IAAFRAAjkktsnlnCjUV1goDzWRHltH9Ct5s/lbcEA/E+FEfbqcXlXZLlsluoIdp3X9t+o1ddf9eCf/60T/oJ6am6Tf5kCAX1ITQxGDCCz9WYryNEjbX/g9KXznW05NHG6clGNPy6ZVNTidVMB9VZ+CboLPJJGiIxoxsa/39Ula3/1f3u9HWRkDAIao/ZrWwqcfekDCgcSyA95FPVYWPtMjrjBT0B/VuzN6ekrpI9dfc7at87/kK6sqWN////5+2h3Ud9xwtFj3tSmIKaimZccm4AAP/6cgTWLwAAAg414GjBPJxCRut9LOJfCKxre4SEsDEKlesVhApYUKg5rabiabnttwkM2c5wZMj+GTlptXdWubekiftBL/zhayUMz53/6+T9fX2vQndXUaoRVbqU5xGbRk9BpND1T+yxgACuEptOSTMej0blt3yCxflBTfE4svlX8q/Hgdn+L33Nb0b/78/02oKuWv1pZHoO8Yl1p/qQtHSPVpy/M2Jy4V3NSCIqxLddfeXKxtmt/GTVpc4jm9WaXk+KPVogurhq+LZLbQ+K6GjQK84ALEPa0sXKIeC8yejG+pQwTxw0kQP1IUWIKj/yIEAfmLDo7gklu/r5Z5ZADNrusOquvfz5av3dCPyA38Zg+XRwHTe+EUfcLfY4jkb53bl0rvMFKSXfSUjv6jnq+N/2a0xBTUUzLjk3//pwBIGaAAACCyxd4Ek6PEEDqy08x1QJATNzJRiwcRyWLrRjlX4kAimMzVVanXAR5ugswyt6NRx1CpWvHm8IiHiT5w43T6/OfnPejP0d60QvmsSPUw8ZOoa8yVoXq2IISUVd+O79HeAABRQAQrJdpHtZ46dcntc2CBLemLBNRvB7l5xfws3im9Xb1+VdPvUJpZ66ahHFWan53sevp0ZURLTrUv8/v9fyRAljq1L9HSGA6rWBdLZ4vH/3vdUhSN8KBEug9/A6e/SO6u65BlpjunctDOZKOq/Wvy6e0U7f//m7ucpkZDDhMQiqwiUHm5/9JISBURSSTRUZm3lDh+NgK3QHuOLLuj5etkDknOIhdGNKh/mt41vZ/N6p8p0uNBmIJy58kopNRcQnP/V6CytW6zMOuwWPKfFkwP/6cgSDZQAIQf4X1gsPStQ/JSrjMWdYCNDtYUecVAEhlOvc9Z1YAypKZbqeGZL8vN7QVzCpa+aNacvuTZQikUiVyFDHnPnyMRWSfEkD53g2k+FcKsabyL7Nlra9ZuWGRI+Wf6fsvAAKct9kenqkZsyds+AEZ16g7pvmjak44CcUjcMpaNtVNBy/dfQ37/denyipj5N+9vDv3xfUd9Z4Zyo5adDfIQgACnN/4GL0Wyto4+waLJv+Go6e/gnDqSfmaWuOKwYmCALohQnp9R+qvq9up/g7ao//Uf0n/4XF2Rd++Wkq4kNemwP7Kf1hEgly/+OuomShS54Pe+IQc+ExOz7QHXRw+TCZ+NRhEyDdxB8zzi/RX7p6r8fa1+7D1RiWvK0dvyC0DyEwS0ihoAgN9Rl92zYlMQU1FAAA//pwBMffAAgB+RhYGw9RcEJi/E0YZXOInF9nTBjqkSKkbmjzlX4AoqXaJtgMBEoGdqOoSnobjOF1aLaLHJJr1teJn22pSP1nhmOJxF30FVt1ffdtxlQl2ZPsuz1O/VoYtGj7P/UQYzHWnHJZd+0ZphUTnvZr8Yssg/NfifoE96h/Rk9tD6ztQZaXvEGvFajWRgNuS2UBKKmFqDRIXcGQSfNPsdZkbUbEgBBLkkeNQ4XtTUb+yPtrXU+Vy3KBUlzsj4iAl1CF6s4idFJbMlnanbs+eCr3F6Bc/LLPiUdZmd+ZqfErqG+cr1nk3/0AAdoimkk5DO4RKm26Je32yE2SYEaF9Bf5X1AiMroDz4RP7eEQeyRiJMg5ORPT437//eioreQyrb8/qXzfEXJMIajDFWt9b+/oTEFNRf/6cgQWtQAIgg0i2dMKMvRCA8tKYUI6iOh3Vk09awEFlGw1k4ngAAiAExNuR3B2TbWMI9PWdb0pcSHvh1kKMfpiMTej9CPxeNG5TtASW+SbJJr5gaq0nZQdE2F9m6UMejK6Mn/Vr0d4AEQAJJOWu4Kue7DNmcVvusC/PuMjmdYprhX8E/QW3DiCcjeg+V2bN+6vWd3tRoze7G52JxVShhheEXyaVOvOep1QAP+KNDqGVw3Yyvh1pK8lWMmaF6pfBZkiki31E1JCmwGMsRqc0yPNttiZWpxHIF/5/v7K3eknSyg5IfdUKVASkt7fv3f06NNCAIGcu2IDAOi4trNLWNyG9ucS4qXt7uOqT4iA2xiDhnmPzwn8Gfzt85ZiqYuCN/42Sw3jcypT6dWLYg7YY/ltGxMQU1FAAAAA//pwBDHsAAACEyDZawsS2EMKy1w9ZTuIbHdztJQAMRmrbraSUAYAABCAEBkxzFRShFvmUEVJ6z+adS7PaFj9U4oW5rK1uuCqvBi/BgXlfw/dQ+t+6Q+za9QSpOypX27c5u9WUyGQ3f1gAAQMkBVLCkG1HvZIPqrRIG4zXyOs6FLI9vaA47qLvwsG9W8v3fqbqZ/J6Kvt4z7r0L9PZ0//1/+3q3p8d9vGDaP9oBKELKSLKScCwtV625ScICUVt/pIzLhBIvNZJkclgrM+BK3c+f/t/5jLNm98s6ROUZ/PGla2SyvJ5n6a9OK5DJPnv6gQCImSiWmlKTCym61DcrmB9SHXWVulcreHB64W3o3m+KH5z+V/N926kbh9vden1+a3+v2/9L6KlLnq1A+7bjkbcUIQY9eKJiCmgP/6cgQAVwAAAiccXB4koARDBIv6wQgAiLVrhHhhAAENmS/HEiACSTSbckkkABku2NtB9wPPe2D7BQ9KJdzDwtmYTyVEmZcyNW81vKjDklxERQxkhYxpw6aLFhZjRdk6t5Bcn+/Sumw/6koQCABTK04rpLN9t9gJvM8MqwbW7HcroyXXJ2vPLNF2wyFs1+6blcdiD5G1yngZW1rzh2aNCFzTj9Udp9ht5VSxJK/P3gBsRiQSCQd2k8Ba4XSYr++llCKusjItv//7f3J5aObR+cAABAtCGQXQ3XwM/u+5HacGv/X87td7IJmSn/5/8/mVXQSsLLG22qqiuCWWG0URrbRzRZhFXPIVFVtffa/vbL7k3lo5rI67kABB0IZBdDda3Fn+kMwvrd60JeMOhRX7+nPNEMuxupkcmIKa//pwBBFRAAACCBHdtzxAAEOGu6XoFAAIeNeDpgRHoROa8HTAiFQBAQKtIrQXkaEuLOXrt/Ai6xjSbXgxVSy4LTcmWe7pKuS9qPDiyIGjn56mXYOY1Espny972sKC3chZnJLnn4sLagHUWcICCMLykVHz9InzA1QqUmysUJi1YJZTI3ejUfo/6s95W3NVLf5mre/R/m8fEbBzBZFKv13mibAILdyFhnrsfizQDQA0wJY20qwK0Vcaq6WiWS4rnnXP/RtKLB+/ql2nK81Kdd1qNpb5nRzPaMpCCtYzX+XkxasNXdIDFkxGQUGNRK4WQwABhfUl6ttusA+iqK6yAhtG5OM+g3Xp0bQhjwaXn9UQW05c1Kdd1q3t8zo6PaMpCCtYzX00l4PCzFhq7pBcWTEZBTtSNqYgpqKAAP/6cASZlgAAAgtAZuhBFqxCiAtyYWIuCKDXbGwsSUEWGu908IgsDOTmkkutkksh0qMJXdGwx+W3qhez9URHoDme7Zr81M1NH7hX1m/xaXog6N8q0mN1sDcdxEkSrqlG7ESpX3e1UfNgVf7RgAKNyypKT+ldF8GXwz9+yBY7ooO9n3gqc/o2r2vlaq3VqX6t9rq7X5tL0QdPy+Y34N3EUSleU9EAlfd5JUfeQEpLasoNrSRrN9YXlGMZLYW/M68wg7vvWcEx2QDH1U+9E/5UPTW2b6H++huz/ut9WQfqq3GziHtyMcB6D7JjX927d0gsAEAMSlFdpv24RlCQGg4TVwgXktDCEQI/FvrDsjZCdfI2TdehOT+e0Nyy+3f0H/2mwxi+AjIoBwGYPirjC5hQpZd68UpDaYgpqKD/+nIESNgACAITNdobDzgwQ8gLd0kihQhY13FHnEfBEKbuXPKI+gAFHJALkfYzR3gQhashUZHJB2gDHxHFBOK8QBCA5ajoz3Ib0F5Hq3o9dfN/JfIPybe1LOzfQZo872571py/vr5b/SAFghRNwB9g6C258jEJQuZEq3NuX6ziM9RRSvzoif++9RQXl6Ne387eqe4zqhB30S275fj8nv+vh3fCwHRxd+nbqZZ1hAUDN/+DNOfddHNNUIWiIraCdXkEeVeUGoc2IhfSzI2Q3Xxr+nQ5//roQpt6ppI31BLENbLjud6VHVVEFk6fNbGv3opIKbjkDNNOFDtHm2H9gt9RfkTCKxIQFOBX8m9BIr3wQ10fdtb3zH1qnkeq29UaYupmk8jqUn+X/9fX146WSKwrzb/pTEFNRQAAAP/6cAS2nwAAQiAcYukiOOxDxst6KCLGCBjZc0SYSxEJG63MkoliNEBqsaSkrktJGR+ozpnAxPFCKAT3CpehhHkvvvQ832yhJ8iakSxUhic+MMrPhYSO9yvJhNT3XbzzIARLsfXtfwao5KkhAAAAd//xGBwQpc0PZw0NjMqO6jzk6F5j5AOcWvn1Lya65K/vYUzhRdnc2TjDdmBW74i0rQCIrvq96y0nTPfKcGv3KAAjgApySQoQu/JTek+8KOr9xi0YIqzords3ifm3zBudtBQZ59na1l5PstEKEfdyk0oR/QS4YsTt/7BWW3a/X/6gE245CgldLkJ/DUsGGDdALxS8z4QLxvtUejDcds4kMKUepS3oXun7u0q22+kj2VZXQSfbv/+JVWxRzNbhH0sRxJepMQU1FMy45Nz/+nIETaUACAIWHtrozzCwQGkb6iAie4hUYWzmHElRHqTt3IUI8gAAAKAErLbaAzTJsJA+0dM3LfGflEUqVd5xAd4aP+Fb2zhmFmOdmZ+Jz9olekJ8NAYzTYU1OoY1qssr//dvto/9f6DACQIDbbccoY+MvXy4v2C/Hz5Pj+t9c+DfVp28lXz5FTvmV/6z07KKguVERmUvLKi037NoxxS40wtvsdYuy/7E0ACTkkOV8XMBMROGr4ABQhlBlnjkjEBeFkP4C/RqjUg+o/e1kNYa0LJEpXYEjT3HxLIn1HTN91kuhAvERVTlff3/uUQQC5JaKwloDxmHjwwF3NGHJJeY+RCnp0Qv3F9Or//M/cn3asGhkZA+R5qGI+rfS99976f8+k8gmNOrFmRMEi5xp1hsZ1SFKYgpqKAAAP/6cAQOiQAAAg0227npKTRChcvaFCKViKh1bOWgRdEKG22oJgjyAgAE5JbiZl9SrHdBeqC6Tvm5yC8JNEihvGP0Ha8Z/iz/VOa+R292ok+rmbyUX7RNmunkmcUrYrPCvGILAa0Wd9FpkDIIJNtSo7NEyxPC0bQC8/GZ32uWSgyguD8f+nR6qQW3ulM3hNBRpWcF7EEv16rSTyaKxyEBiww5TjBybRELnG63oAIDbkt02ueKJ8BAuBjDvl/zChPlVuaKFw3Ui+pA/8GAESdLEJFBYxoVPrKbjz3YmbQ2nu3QFWoXnHgrU3MPjA8u9TtdIAESABJyWrE4CPMN8VL1Ozivv1Zk8ZIphu9yNqiwTbF6dB1a/Vv8jfWm/yf3+eDeQX/9GGlJKIAaqRZdElGtvNX8DJiCmooAAAD/+nIE9BwAAAIKK9zQwiukQuOLeg1nGohFF3DklEuRGpdrqPOKkBACAALkt28bbEF4S3ier8WS5r+DGE9fvtVie2pTvNv0m1hIU/tUUx+hCFlYR3OVl9b7nxpAisbTQ6MTU6L3ktTKQIJxALbltrj7gUWzO4HlVvqmzEH2hcWoIhPx/5eUarv35QQjWFnu1BxU3nPWjWeJKYhtmKOhPOglGEXxgXffod+ttagAEnJdqjq2B5ixnOIlHajuIq9MLIL8nyUaohGzv65dPNbWX/sOzrUM1JS/ebTX/X1b0fw2OVEx2W8BoigOZ7fY6gACAAAC4wGAtVrsgdQeLg5WVlBTPsfvRmbrd+3rNw2XPmdFjtUCG0qGIKFopGhPiltBle3XoP6L9+wZugJyhbSiK73K7t8smIKaigAAAP/6cASbrQAAAflYXDjHEgRDyMtXPWIoiOlfcOSUS1EerDA0M4luQAgU3btqCTWtWgnhCHG0G4JUgss2hhHno+c/Xxkf2Tqvo/s/29W9rf0jy9i6/Y3yfPyO1bNZar+mtviL7/L0gYAJblttL7VGWdgMnYeUK9QgeRREQOrjC+zfpwpuh+N+idS6PT2f2G929vZL0j/LVn/FNpAPYXhxpIoOU3bPDazm/y6iUQ5Ldk7luh+MDcuNBbYwP8TKsKHRijmuAwZqpcVkb+CAY8lSG4cQ/OtW26lXyiOy//TbP9/t6N6dnPTlTsT1b01weij6CaYIChCaTck26yaj99kbKBF3tR6sX0GRhLqFpxDdfGv5PQj+v/WXyiNGZ90a1VT6tt+zfbyP2KjssYd1BsSySy5jUyhb5xdvrTD/+nIEtscAAAIdYN5Q4RX8Q6x8DQyiE4hpX4ehnEbxDpwsTPSIunYBAJKSbmKnCggWLDJpuVK9Q/speUaUf1LZwaxv1kzUeXgLmRf+Yvr7X/o+D/yWpLieTIBLiL19YZdGBOfn7XZ9BrSZgQKZSSTckvhBnjA2TLbQB5EaZqK/DtrJXnfp1a1/6E+T5PUV6Ceh7Uqr1d1b2/5v23VPT8f/urJ0bMa/Y+lbb2XODdwYItIuOuXXkm3GHTZeVBxzK8zUfw41mFNUPsXh+iOJQZ1e+ien7fE+n7/6we2vKjO9QS8hC0VVVU3+3//9sI+V0bKgAUpJG6FbUAfRhwFraThc9/IJf8Czhjpu6xG1xLacG2J8/V4mbQ/teQKb6/G9H9fhbcLOLO9okfnZd1OhFOjIZHdu9aYgpqKAAP/6cAT5iwAAAh5JX1UUQAxDJHsnpJwAiKwBg1gBgBEEBm5PDDACZQZAIxuS1DspjtXMNfEAB6rq1H8HyOB1NubryB0ZkoGMfMVuQ+39E6Avg+ykm1Fep279IL6/a+Hrg4mHYDOqnN1f06EAAApSSLubpkDxJgMu0QB9j2uamQkHlmNQbmcV/PUqQY85Ob2J2m6J5PWf3xA6rKnogTFkwH99rYo6tG7wLo0bfvwkCqAwDU45ZI/p9B6VHggJSoqMcPHgSLKPgQCFDIjApQELA8Hyzj5ZECtm0voZjKbxwlDZgZVp0pvfclHvTeb/2oQyx1yLgQ2kjaaEK2Lw08DAqYmmLGSiUukzHMd2DQFOGJtrjqr3sXHvQtZgdcoZo3pSPKodnjTX9BMiQR+m9N/9caMq0XITEFNRQAD/+nIEx0wACPIdLF2HMGAAQYWLsOYMAAhk13cnpEKBBxruAYSIYOLgBBEIZKWkhWqdbOD/vp+wmGRPTCH/w/45Nel+aof8yXlbfu1hfxSO8ajZ4Rx5l73KshFj9yTPfQKqvb/czUSf5ny9CAAAOhDMlpIOz53Tg/76XNgFDpHXQy/Q/45H9Q/NUP+evK2/drC/i53jUbPCOPMve5XFWP3JM9+Kq2/3dRJ/mfWKQqKtHFkB1Mw+ms01MzsPmds6NWtm/+npydC/ZipWVCdf5FZLHT9s/eLF53YLKkCa+iNcLJgmR0rw1myynviwsLHqaMYEUWh4TxDRMRTkTwayYdln4V1O3r6vr05Oi/ZikqQqENIt/kVkedPo2fuwsXndgsyQJr6GjXKTBMixKV7s3VftTEFNRTMuOTcAAP/6cATM3gAAAfY2ZejBE9xEhsxtGKI/iJiHcSwkQGEdi7A8sZQ0DUrvmssjaSfOSSzmzm/KS+SBdMc8KXB5hO5hltdfS4PMutP39B0u7P0N18Rc8qGsNOs/KKceiIca8qKc1V+oJJmaN1tpElQDJJTUyfIEha0Cci6PEe8BQddU3qWm7cvpMDqhdafv6Dpd2fobr4hHPAoaw1sfXwIUceh0ca8qKNzVXXqA0AZTRDHGDRRhhkoQqPOCfjd1NI9yeO3i3X06fXdZzODDUKih6zJyep55CQTY8AopALsijx7ns2/kuSIjQm6gLBRAdc8CNhBHMp8+s0vCgGQiaQWXuCfkvddAaMFG4DD+q+MAzAU8L2CVyVFr+9ZrDpY+ZAgWSVOnaCWp7vJJU2X/AJ3WMNIJZ0swZSmIKaD/+nIEJ1wAAAIfNVrTKShQQubLnWDCCwh5YXmmCOfhFKBs6YWI+AIAAILkclGNGggmKT4J0peWWGN9C9lFIU0T+X1Fq6FbrxjHyd9Ua371ylV5p+83Txiuz2j1pTjnoElCirvb1k9Grao0AgBGAC1JG5SCT2kwC26xX4gQS9G5VemdvCdAYbPRi/6cm3WyFdOrzq5ZRSG/p08EP93aXWlOoVtaaUVd7vTRwztUPADAO6jT0m+4EhjHX2X7JhQTYKvtcaUX5G9q6Hf5RmzVq/f/6e5D/pavjwbWbpXzj9aGDjrZGX59Xp9jPtrpT/L/Er6QACAYBLckNZhbsWnb0xfpR7qBWbV6Bl9lUr7qzFq8cEMt8ao9Dm6+N31+3/pc7KIbRvr/Qfb2f9W2CPozLjnUzu/f7E9KYgpqKP/6cASyVwAAAhw2XmmFEfg/yBs6YYUICPh5ZMecqyERGvB0Y4j+DDAFgbc2224pfXZzD1VxJG3B+a1Wxfwn1gddAN/4Jk+jNW1LLuVUfgzNO9O6kr6gUpFTtIHy30N2OjKn8J6xNSnXXpAA0AAFuSQUfCoZeXU88roqCAEzAekJiJvIdOFn6B+pKOv9FT8nIsvf76RdpV7dK31NpbT9UXiT+9bPs/4PnvpAQBMNOAPByeOZvOsoZ9iKisEcqJuaUIxS9gTbUBQc+oCOrUCn7E0qOZqnMY9FchNIWfjwx1uv9g55r8ilKKgWxZ0NAUVrBWo1MhtIxNxtuYMHDMfKO1Q3UXcxW9LNhICa8j2w3r0dG/7yorKz0WTkVsqe7L01EJEbx5VbGkUtRfrQEjz5w927cSnb+hMQU0D/+nIEzbEABIIfNdo5jSpQP8L7Iz2HCAi0YWBnrOsBE6xtpGOI/0ABB2a/CgRnpVDmgSryHYfz3LvSm+8wI/jB+iEbxNunV++hdVRPSq1E+Y6/X1ufwUeqpYj8n9S5141LH2To0tkHeps6AE5ZaTAeZvrta1C1zgWHm1BnLqP0aPEhbxAX0jzJqPs6CBaFIkIunM+0Oiz3OWRDOVmqPs/fPdzfMRTQ74cvaAA1JIhSYUsRNCcpeMWHNCYgq38dUbySYpal64dvhQvfG7T8VDety3vdqEWp0QWAt8ne08wgh7EIsF318OL0MAlX2VmtOsgAVQdYLuUfnETGpmpQJ+ap3qSfhbpBteoAfm6HvLa3d0VNGokj7FQv9kV/x+b////1rVG7I7ran9NG6ixkDTX+O274maYgpqKAAP/6cATetgAAghEpWzEnEmxCBTscMScnCNzfbMQcR/EWHCwo8p1gAQCqrlSiP2gP4IpS5V9gl5ylqZV34gf33oKDdOr2+3d/r8dLwj4cBZjJ+HjfYxg/umEFECm+vx5GRCtAiwG5Q16nAAAQgBFqoB0qm7rgpF9CbWhs2k/IcVDuVyqC58VFkeatOC4h1P4y/vTpbVV6KNrTzXaiQUbJF0uOepSMxi2zZY31CKAlW0Q6x4HQoKtFil9BdyqtP1Xws3vj0Cl69XRpGdBXoy2qvcuqoauvXd+qpMoNxZdHIvkKXMMmVWtQ9tAcKHlCSzewk2nwAAm7dojc/tQYDlyezaMgwo7oDdSpK6hYKTs3lsnhG/8qYY22d7W6+5u6qnX7buqH23K3h7uufuz885FQcLbtmYfN5Jy0xBD/+nIEwjQAAIIYKWDoYShcQsL62j0HVgiYx2LnnEtREiZuKFCK5m0QFU5E5JJdAzGhKWwOqivRXtsKjuHgxP3qLE7fvfMvXd9/h6PDy1o60LWcPHUvijhZT5V9JZZlzwmNX3ewjT1tA+sACEAACm5GOKZTZCA3HhECkn2PM0tNArPMuJsgvLAi2gULeJUqdQFGyMSsXdn3z2eYJ3V1LVrLCjVvq/4rjtLH+xOoBBAKackizEsskze7IXCuQUIjuNuJyrxxp4cMdxjzvewg/ToyJNp4IP6k+fQx2SpFbUK3RJettQZ0+DFKqyz0n8jY3v6uhIKTScGuOdgHHQ/iou+4bydNBXw8O89SNfzZOWXgb5+QKEQA2X5IpW+bBsy5PqnlfXfSdqIZ7vSOIQzF23JsezuGc0hMQU1FAP/6cASIMgAIAgE22TjPOLRB5ts3PEVeiL1ZdaCYozEbqy1oZIiSAQACm5II5y7jtW5EDF4+hMnI8oVJ3aFxYW463UlTKGkuvRna3bo7/9TbxWllXXNVfXOj/R1FWU4ZHTHVy+rv9oGACm5IG2I4RoZGY/ca9Cij6gHMDSZocSI5RuptoIo/P1BEH+yamK77l6CbPYw/ahvanfaJiFKV+ls5iJUivlE16ZCiCWWnKSXgi8JbyIhWUA3UIg0Lykfn8bo0YQd26BhGk5v/+76CJt6+sv340//t6u37b1rbzEVHmZ9TaNl0Gkg+5NtbFzIgDAApOSbNUToQPwEWyDKD8B4VW9iF4duD+dBuz6Cf/rT/inSiAbJVr8rdX1g2X/l/9Pv9qpcYqtu71DPTnzB5l2JlizMBMTEFNRT/+nAE9G4ACAIMUNrRgjt0Q2rsDRQlxYg83WtGHKwRD6+wdBEUTgAKQIKckuv1OoCyjRT0iUfu/iea0r3EPwj9Wo1S+/sm2xeV+nqcb0DzF1+a//UuvSnf7/v/1b/q3lso6EE24D9I5IQBQwtqSyXzExgQOuZH0TpF66XfO2o0CZJp/yGetebrE/JeMDXvd37Tf6j26fd+r31+3+/qylbxye3kFqwimikphVXIEFp23Qor2qC6l4gbJZUv/UP7DDxmFt7v5aY0nk6uPoL5V9/X7J4t+363VmyAxFZBqdDHdL6hlGj5rbGGmKP1JlDEiFU2U7bbeQ2osTJlToB+dH91N1+K+rF6dEO96suqndqjfQO/Det32jeWjPVGvt1b2S+y/fzN+nbzff3U3vtnpjBswmIKaimZccm4//pyBCujAAACHSvZVTygBECiO3qmCAGI8YlqeMEACRsxLg8ScAEAAgAAS27YrLmFQV1C35px7l6eb929g5xgM4k0RRuHH96alN/u33b3/+vUVP1fbUBMhU7+rKuSLOVZYoqJ6AKzi1TM2IgSBARSTsH3v4011dfycx062fcRfynZiIIfIqejZBENlVv31YifBF9+vbjqyFTsZ5RErhbZQ6g7JGjxV0cD3UbAAAAADAgEPilwoUhcP4SiMYioen9VdHQrlI4gGEYgZnWOhJWPBOkujp6a/////////+jex////5w4G5GnP////++LDmEDNwCUABQBQOB7IZT0gE4yToTaCdmq9GHzjykca7Tb83ZHu+lPfX////7////+hnmH////RiYrB240YwmP/////u5o3FZISBmcBMD/+nAES6sAAAIWMN6WGOAAQ6eLwcMcAAigj3ockwABEw1vl5KAAAQQMMcLAqOUzHZSK/8sjW9htMoYpUJwwv8ZLqJf+8Jx0oUP/7NGgsLgvNGn/+w3cgwQ5T4cAgnPn7P8q2UMr5r/66yQAOVIFRVFNTB9/sjU1zWEamYo6E4YXvxeXUS/94TjooHD/+zRoLC4Dx0af/7DcuQYxP//zix4RB8v/8J4Ye3//ttDlcoBggIWjqKe1jU9VrkZoIum8EGxW9Cqd4yfU7c5WsbNV/ndnm2eNw0VMFhEFI4OioALSMyxbGUwFcq92mi05/9mQM+l0AAP+lAwpFpUs/pZLfHuSuMAmbXSQOq7pmnrmGU51puCQ29C2C4qRAIqYUWPVB0VJFqYLPfISMzcMvdp1NOWv/2ZAz6XJiCA//pyBBU3AA/x9RtdgW8cAEHCu6A8wjwI9G10B7xjwRwMroDGmciZEIAlRWbdR+43Rb4hzUkQ+WzCmZybpTyF95kZyQUb6wdVFVC59KhepZ8GbSxaaN2tTFpNb33O/2rH/r3f6U+53A1aqNxW1GYzsvMuoM0WDItRKDj2MrQTbuDAyCyhFxOG2CjRUTi8K4qA1BtqHvS561zy2Le1M0ju71ybejb/83CiGcM9Xu8uUe2IltYtXGMw6m4Q7ewSDrNSXtcihsimOSkTgyx0QgyacPHOWZaFHY8B1111Mm7XvqcXuaQrbdL2Ooa5s0ev6bNhYuKaPFS61YI4rZx5RmrB4uzO47/H2Z3LrajKuUQAs2dICE06FrizREGseCZ9dbl1MKm7R5qpxe5uttkTuuoa5s0e+T0ITEFNRQD/+nAEO/oAAAIZDF/IYVsYQ2GLxhhpOAg0ZXAktGrBCxWuFJaJyMDGCImEjYwIGYI0IauUwZRYZroH3mSThzHjmsRWYMRguSmJ0NIF2AEXcVAKA2MhOfFspKgQrVQRhryrrkYlb799tgsBARKrfg3FkAKnBpXKcW7xYB1wKEa3AUgOY8RNYixBi5re50S3BdyA1DYxJ02fFislKlCvICO2xFbriWSbRml3pFrBYF+lTIygA6BpINpHc7uoioASowUcMptOjS7kogsjRR2Ni6Cy1LK3HR+hRoOmCKakWNU6hG1tpFXdRofR3S3aW/XKwABesETzrAOMtQQ1cIHqHnljE8EKlVshqgkbaRs9ReZjvd66nRb63WmrblQcaR1GrG3SqO20RbajfMIo3Bqmi0sn2piCmopmXHJu//pyBHRqAAACCE/iSKEWvEKDHA0M43MIvI9/JJRrcRMMbnSRidTRWm6ultTXkRiqarzW0jQxzF2Xuj7IXmuKbdMTyio1mczZTLWYQqnRTxCJhpT+az///5fzPR/wQdU+Cu0/559NzYgaYQIq7I3MTNaTETD1SBHZ6hlpfKfvM9VfntBg06dvCq10iQXFB5BUXJEKlyM7WYFZ3G7frd1csbc+CvPqYhpuztwJRVbpnjL5Rm3JRFoT2tlhYLVq3987J9AnQL5ueOZ2uZW+fg3H4MC4dRDrx7DwsE8s8IeQXWAmQoWtPFZJDP9cT8Wz0lHUAAMAEEMpNzPOTYtM9iZBSrNtc99kFmFsJxb4l/iNS4qfkFiNo5UqHsC01SEs+tTTLg1LcjWZlVXCUUZQSt0OXqp+tHSmIKaigAD/+nAEX3AAAAIhDVxQbFiIQQU7ejGicgh4X22npErBFBatnPYIsMAEAQCUScrQWPAMRXSqhjqLwfUf7spI2maWeGKfBFpYcFBaqiMiiRRzSJYDmwqVLvUEulh9SW4qr+6xSH3aHqk1p/dJIASAAo7bbOXqmDwpmu4WW2+hdgbyqsFhU7tmwL6stUO61NSnRjK+jJd9t+o7bCqGmjTUy2NgFp36d2f0W9N5//RQAAAAAAnJrvnxCqqZ7uFkptnFvui/7udCOscGBPnJ6V45e6Pva5ZZb7tq1ftLym/WcICSFIuhLx0509LPahy42t0N0AQIEdt3xTOEMLQeezxmxe+RebTCPdYAVnCKx9YD3qupzW26Dv0d/1ftM3RlsQecXjXqgIZdeWrOJ6E+jui15oosrMNZ1piCmooA//pyBBA3AAACEBxfSKEUvEKjG8kk4leI0GN3oLEBIRKMbUw3lHjAQRVe6us4SBmHuUUZN1FtY0Mc0pksTaZR50xZPEB1pQ2PAbiFyIHesgZOCgjFi2VZS9T3iO9btiGvrUSFV6f0I+qgCAVZmuoeiw6nu2pqny68ccqE3S6MsNo+ayuWPxSw6x9Y7BGXhYLPvzyzS4iW6WdLMGAFRC88d1gDjab6plzLd45kAAAACSUt23lrSQZpI8w9+FzJM8jIDXNzE0nAi9x9nfO6mc6q+lIwJoPHSAWHexBQYgsVJuPjghK8PRdWBXOrcSntOV09dYJUm21Iw4yjFhtMGiExc6lidZ+09BilJrJ1oRtCIj1ORVngqERPqxVabwk1TZWq4YAA8SARO8qSXnrNOEq3Lu7ukXq/uTEFNRT/+nAEVjMABIIJGNs47yjwP8Mbej0nJAhEZWZlvMPBGhJvKJQUvggAFXX/2g5hi/Kq1rEXjzQlfRK/qPJVHTjT2lbDSU7dkQbfP/ICYO1zEadZVECNiiF7qk2TrZaAYzuRat1uLfX3YAAACZd//LXUxMHdNlNCcqGMh+FUBQAt0fc3qdvkOyISvkyly7mqrHxGTU5KqDwWY4DDiRxa77Orjfdob8r/2ggyW0RbgkSsJ4WYxKr5QP4bBk6l03/KAjg4Ai5ExT+dzXCKxs5hNu2SyW5f5B4oMaERjzp+ckI1UP3yatlR1t/06NlCKTKcx/04Pxc6TBBFbbY+wetG+zuIO3GAfldS6N2s+rejWHOnZQERU8NUcNMFHMRpvvgzpo1y5h4ZLXOQEnjqqjVV62aUxBTUUzLjk3AA//pyBCSRAAACGC9g0KEU3EHiqyNh5iwIUGN04CThsRYlsLRQi17AnA0lJHJk2Gi1jkBWGnK194wfgKgrHXAL7H1tVYfmdiPCyKSJaj+CaMe0CzjCfxZyzIxR19En6V8luWKooGIOyo2NABct21y5AjOho6mclihPZPBgpez3eUxp6DVaF0hZdtn45FTZ2QlWnBCTW76lhaT3LCjUiC60Kqer+667u9fSV//aAEFJJJykYOqB91UJ9u++347BQPqRpburrRzb1kRUUCZVhd3PDgeoCaGpYOOK6BZiQXFww54rnqct+0j2bVLTRev9MIRCSTTckkurPHuQ18qpfPOE7teObFVoSm8pYut+L835TQiLRQMsitZfmGYeZiy7RrwRE29n+bps4KkHpYqXNIvuu5C3OpiCmooAAAD/+nAE+eoAAAIXJODoZjwcQCMbIx3mHgjlD4GjhNrxFh8v9GOI7oACWUWW5I5dZwGoP6BESGV/kFL732pXS90Q7RnYO1fLf/NN0LFZog7JFFJkqD1S9NkqplJKbtXelM6i4a6YsDlW194Bit2FFAkNEUq0U+YoZESVw2oZYH7jYe4KbKR8PisxgZFbY3W/8Q4wVN6zJV2VNb7rWJLOoZePV0XyElouX//ooBQISRTkjtxp9VIjpc00tpe2jBGQmvovW6brpYeEt78S++ZxEMiPCtPQzRX/4/rX5TWwLiGhmf91Cqx7liq3DlDtydV6KfVUUCQCgVXHJmTeRfy0QdqZe3mgjU65ut01bzoLyvsUqiUcpzr6uvt6e3q2iMmXpbmf02UOhYYa8ZtuWg20900WQlhZuhlSYgpo//pyBFgNAA4CERjXmw9BcD9Hy1c8wlaIoLNiZ5hPESAiLVzGlcoAFSbDWFdlSxRpBUB2ocdw72S94o8IEX9n5fRe0MyeHK2Jn4FgxkeWdozvIfUxEu5EesPj7pl89XT8ndrVMMOZXrWAEkFOW34y+ZT68R/spuW0oNf8scA1ggoufsCFI+BNulv7/+/V/Xv/6+7deq+z0z2SJcXcBS7iMirSat2itPKtyT30YrmFYGwql+4lmeWDzLk/4Rt2xJAzIAa/zTobOaGbHcirsm/zafU49aPw11dAaPQeNtauaT6mwpXQajzreGZT1fMwABDclt3pRIhhBUspRE9Dhp2qsnOdDcYvMmLvqlHcoF7tT86bq1+dWVBZNBbmbsrSO1qn9fv1+zGRRKtVL1FafgB9ncR8Qv0piCmooAD/+nAE5PgAAAIcGNedYeAAQCfb6qCUAYjpj4J4JQAJGbHwTwRwAQA3LQNdzXEtJJkcE9j6R1WCMXcNuJcvfDn20WhwRMWWsBvW73V2K+n1Xf99wWA4eiUCLJXVlqz0I2slsNyuZs0YL77cMFWBMkcuG7EiQoZmRmDVoFrVVd+nqbu9/2KZHffZLHGGLjTevYnoVv6t7/9X4ieSes75JASNwC9RJ7XG9SdPWAAMBwOBwOuFsqjoxU/Ps6f///////x4SMPxF//4X4kgFwawpFP///C7C/J3MMOEQSf//+PDScs8Zi25GT/////nse8+PyfIAAKBgOBwPSDkK6s+/JSh4//Yz/av////xoQYbjf//EsdAeE4sU///8F4OybmGFBoQ///8aGkyzxWJbnn/////nse8+NyeRMA//pyBEdSAAACKFVi1gjgAkRKXALEFAAIiTGDvFKAIQ2a7peeIAAADAAAAAQDgcDgcC/dr3dB4NSva3/////85kORb/+WcKKTHRJ//ziZA4aCQPCYz//8splAWnEB2f///5pQcPQYY0qSHQwBDDDDCI29Nraxgf4HuIIM45wRHl2T//37+vRkNWavZFeOcQU5RXSlu3dyGIKIM+/7/srUDqMv/9vT5UR0HRM052L9mvEAEBKuJEgFWoY42eTtUGK2MHUK90XC3pyXXpr1v+5kdaNo/9DGSrdufs3b2yv5vqn9PWlyzKjiYx+6bDo6gAnWFdYykEBA/+zk/g7Y7bx9xKvR8AIkMgIXjPMfg320v3Jrfv2a5kdaNo/9DGSrM1ufs3G3CF6S2tnZWpMfDxH0gbmAVVO5Jl6Ygpr/+nAEgZkAAAIKNd1JIS3AQ0a7mSwitgiVAYeBnEyxBprusICLHMAAB+V6vdJoE1ZssM14LyD4Y+VZxv1lbup7zL9JYxanDuv9lg71/8JUl8+miBpCkUraGdmixbJbQndWWLEpE7/kkAgmqVquVE0DFNcyWYrOm9QVn7MdxKljYty6t9F01n0cg7av+WB4tf/EpZm+v6RyH2hnZoSxbCZbQlt1ZYsSkTvr4lhKKQSb/1XtJUg7IdzbLPWFBdRs79HZWzLtcmW5r/eWpEeiFZPb0XQv94ebDt9b6s6l+Omp+4wWFYmXS5il08sS4jGyt8AAADCJ/q9hjGilnFcxuM8AYWom4N4uo4m/E/1wR+t/JeawzBckaLPfKmQVDy1pg26pIe7fs0C8KVsbqkf3u1W2pTEFNRTMuOTc//pwBMv0AAACDkDe6GYpmENoC50ZYjgIrNdu5LysgRIa8jSBDm6AAMAhmS6y33glihgaqMszjC3SEnpz3xlC7sr0kFVoj0fby6s2rNUy69Da6f/jf9fJpWUduflZpmd96vGv9odWmIqAAQGAErrds3KUKEGLItb/JM4AqLiqw4ffqd8fzrdeRaet6/upEU9nVHyM0z///g2/pa/usoi5LsSlNpW33+mjse3NwACcm2u+T0YJTnPJ4v5bSEV+d5ovW8lUconllWs11ng+e9HrrUtFb/971DyLSu6Pv4iCOyDxQ5ZDk9TXURzf4zpf6zsk4VJxGyKy3SS+zTKaUte3lFZVCCEW9qnO+8rqDItTNF+9B0y6NqXJei+GQ1xSGjax955pPUZWIjOo3Dn1ub3kPinY9d+TTEFNRf/6cgRdogAAAh8ZWzgPMGBAiaw9BSJliMDXc0WEVmEVmu5oFgg6AIG5Lba1ukX40x/pukSG17lXY/oRywsZ2fPsnr9eW+3r+M3TtSwgkxN1mUMvD414kApQ6o4e87sYo1x/7uxj/28/9ddIBRMTTkjbcSNTKKxavhVDy+Uezzo7B3meV97UMYQ7oqOdv0fNUymPnQ2vlzL/9dBLfn09Sbq1O/6f6YLokUWz+CAQluy2XdqrgPCc1pq6rmtyIwOVFGK30vGyTPQp0NUZG6NzzCKd1vl0Xdb6Pd/a2rPneWgvAQigc99fVc8/b02Dka6+o1AAEASbccl7YrHi6KHqVlhG14EmLLfRe9yq9cqSj92perKh3et1NoloN72/u2knurKdbKgsypZ4qIn//DornfsYf0ov6KUxBTUU//pwBKcnAAACBiTZGi85YEADHA0gJpWI3LF5JJR28Rka8GhQi1YIJMyTKJsBNjJsNjeSiPcJT+iqk7A8/d7rlAVvGKDgmD2cikw1lR6pM1XMbXsl78r5FYK+ry3+hHZqu5r9fNO/txARAbSkjbbjVKlldJSx9VJJiRjQWXve5gCamIRfWg5OAxM9YfDWwa/5dF205galb6ziqMpRch1c+392h27DrKYIU3b7q/tWDDdc7FLfstonVauVu9xZn0hh/O5uy0exyADK6ps8lluuN5SaeUKnB0ERzB0Lua09G5FCmK6rCSqSShn91CPrcAAmxkjjcvcBHK131O8OC6mVS7rdyasjK5wbSLGATRXz+v2KZdi6Kwgcn5nVe8Sqovn4pJfU0iG1jA4JX1GF02CNw3uU+xKYgpqKAP/6cgR0dgAAAhYY3UljE6xDgxvqJOIpiKiVaUegrsEYErC0gIsOgACIWar2KKDJ0VVrVzTFRBDJPQ6orpAng3q217tHnluU2mrOqbIqLPQdlheI4AkYEcLqgtOUu3gs1dwYu9LHU7uas0ADQpRttzwqAJnSuXM6MgRhDAEKPv4TBsvcbS5w/BODhE2gk9R1B2m0LQtLJh1hwayLUPUkgA9GlJpi51LbunLJrvowAAAE277/5amUC6eXiu0KQt3hehzpEWiFaXBgeNRZx0PkMq9Zsip/Kzoe/07BqmVrUSUMHLQl6xTf7twp+HVEdf3u7P6sSASGo45LJMz7hTe4tf58YuXxXNkzyEx+JUrXIm7g5F6oWAuumsVKuWAxxOJDyxHNRZ9K44o5YXnt2G9yKtTKOsIsoyUa5MQQ//pwBKUAAAACFCVcUewpZEHkq+okI7OIhJVsZ4zukRoXruj0FO6AAEAk5Jbd2vgfWW9qiXdn5KQY5eHaov1kyx/fzaxRRX6HTZnT3SdHAoXEg2cUhSND0be2XYPeoq6/UJqbQr9N8sHcAA3Lccbd9eg0Ut9Qf/6zl4aztT3uaj7Wf7vTbg6RnaqTqmny7MnTUBfdPPNSgXm4YYs4uixe9CP204xXpZTIt0VgpNy2/LW4gydN++qUpvErjVMG/TR0B4cn2rHXt2I6sahl3Vq9/ff4gqbQtu/3LPXSSSrXmQ2HkIOWoMD0Urv7bH3/ZpgACkFNNN358o3Y7yeDUvinwULiS3FIsd1MbZF5epWP9Bt/o/R+Fb/InOi4/EfWd0ZVgvYDqEcUWsY2ddS/8Gn0y4cOqF4DTEFNRf/6cgTblQAIAgkl3DgPKGxBBLrzZYV2CODNbGA84REbILAocwpOAICkUUpLGJBArO3u5deV3rZNiJIIA8axJlEB0jv57UoHbIrIo56p9aeE9NczYOSjf+6I5AmDt5rqq6fq4wOdu3QACm6BvK6soE5DhKTjfYTrOYFylc1GqwXLdoZKRWIKipZWUulOEXQLFRXnBtbMIZrTjtabdHfD+b63xFKb6fz1FYSjct2pwv3Fsnim2lp5g44XlmxADZs9HPKFpSqtUxGo5wbRVXNRLu+e7UfURPfRtPo/+Sk68lstCpGmLxfYor+/qFq99zE34AT2m45JfaaEbD27vNXEx8Q1dr39cEntDl1Xu0PVLULVP9H4h+/bt4J2/Udqm6tefqVtC6Ast97AkJAqM9ZaxYyVqqWlKSKYgpqK//pwBFZNAAACEyXYGwkckEIIHBoYIpeIsQFs5hhO8RUSrmj0iLYJtyUDmUAP2AmQKo1xhTrM0nKjdpLMPqXQtcxwaJCv3hIo9cYFqLOQVWqS1qFDbYb1P6xaGfmfwYpGO8+lNW7o/RKaA90W7HLf99AAps+5PiIcHrDNZfbuDojUgZuivyIsXZ0M+6JQR7//v79PI+vp6OhHVCMIUUr0Cy8j9MbWQYimq7KQCBJKKUnEKwM1iZEhD4t/Lrm0gGV3XASGv7bucG5+UqULXMonImZ9P9uCG5uvraqcj9WLVunt09GqDOqa/1Hun3W8W3fVAAEgSaabvp5h/zuPJVa1Xt8SZ0WTJNq94TjVsx0rcyt3VCqxE5u1KCdizi2xYHZ62HnFM71FS7ShL6j1ct7ra1qSoxZ1JiCmgP/6cgRHBQAAAgEk2RnsE5RCiEsnYMJ2iQkBbTSzgDEekqvOsLAABJbcg/bmUiI7egV0Rx5UkdJXB7Dzq8zwalB/og240Lcr5NWyJZNH36dHY0VbknurGQo4i1G7paty3RH9wU6vycBAik3IPz3A5RlDm1V7XwYpWjuohU1JGjgayeOjTDFTrUM8xBykWZ6V2bsLqe1P85L1//X36eW+3t/tqTgn39Ps+LwABCFNeS97CcUiOsiPqH67XhtQ4mFWoTZaFXzzeab0QeHTkzKPU7uunmt/NTtzU7WorrJV27L3dUNMWeQaXFRtoUoAEtOWVSoBNs2GHvXbPoEWAuGdi6LM7BWVNMzbtmMAzgk2SRopg+rTcksaKfK0e9m77hnXFd/LP/9Ce3jtValCKsFUCRat/6pWSpZ9vuTA//pwBNYYAAAB8U5dhiRAAEOpy7DGFAAI+K+DXGGAIR2GMbeGMAS0NqJycyrBE1HJDQUorDxYGbWZwAb//9+DcOVv/3FFdTf/4gKKGGQP//+HCLPOjf//fxp8IKUU//5QVkGmSki3AFNnMusqbKnS/AyJBYHQwaGAOtRtBIKHf7uQ/78aYTKIf/3K6m//xwiLMOQ///4mRZ6o3///tPiAsop//ygrY0yoAAG3IygCXPEBJRKjvLVs9iebNwvzpSGx316V+HSVqTFz8jtCNlmuj//83tCgZQsKAJt1ORGsaoqg/28luLEOzGJyQKet1tTAAik7/dpI3JvFZjbpMa7gQYoxsKeAg0pQVedJC8eNPAJzBG6xCToDfqLrDZ4VFCDKM3UaLLsOgPr63UQoZ1aGPesAnG8lXiiYgv/6cgQb+wAAAiEE5GhiGBxDwSwNMMUXCHzXbCwgTkEOGy0BgIpoAbiBkkkaRJdDiY7xBYUxMH4WcKDVOBEPmoqqdKo3vNmFGyp69dRYVQP1E2plFlpR4wlxb6FBRR2DLfzc6IEDRrssKQgAGGRNVakiU2xDD3QPtO6ltMASg24YoODkZeoywaFGu1Je5LwWSnsCjTLcVH32T0MxV2v9wiEQhMlgmdy1pF67kSXfKFWsGaAvzanXrkcxL/7N6rapVQOcyS2FOJZu6cululpW/2qbZ8rqQ/T5hS0X6XTfgyFCtj8N69bD3Wt1zxQDNfS2O/YpLc6Fb2NrYjEPXp7Ggq3LmNUAklnZyh+5b/TZiozI3W61NQ+WdYtPCzXl9BS0Vn6X78GjriNScq/bixb2toSoiFU4+lMQU1FA//pwBD8nAAgCDzXaky8Q8ERJnG0gIomINQN3R6xFoQ+gbyj0CDRZmqXOTpON1qKhD5I2rtm4NF0EhOik9/IqfvBCtgboxj0VqqttW9PqXY3lK6d/vYzfQzz9kFP1x6uR/zt5Fn//+hgRjTLrcsjTcsNs3Fm3VN8Yq9H9EK8uAxzbgJDm4Z6+G6lqR22ViZv72Mzehs/xX/1ZH1wTr0+tZZV3UI7NlHxe/WrlSAOF2yu3GgKJ76KZFy9czO4Gya/5MC/9H3Qm5I5dgYSOqRvBE/fEt5U07Uv2kSyf8X/bafNPYH37ZtqEt03fY/4aqAARxPXeXc5CHE0Oee6tZecBIiySnrpuXoqbMadEQrtvmg/6S55KehWZTLy+7++o/t2/Twb79ZIyhCWp2chkH3qlSCyyYgpqKAAAAP/6cgSGKwAAgh9A5WhhKMxDiAt3YeUMCG1fgUMcR3ENDyyJhgnIFKbjkckt1twJg7xp1IDjQo9mcYG+mrmM2tvitv+3nbrXZRW3a5HFT0MnaRJujgbs2Me6Gdb8QPX9wez/1gJNiX/DtFYBwjLrth1ydSPck8fL+su+xV/2H6pYujiQM6XRuialvZstf9FEvJ33aWlez4po9Kb7+pv6t0q98Yf37S3r6JRmlOjAwiCVpJppuUGr82qfNThzXsUF1ZjLsttE7V21b2+VuE/2/OV9F61/4np7ZnldEobdHs20mQzuj5SL6Hm0KX9A7ruxragH/9fwdWGIEbHDjr09qenWI0EqxsCsH41Qh0Oxn+yKVXFpIiJInkpr/Eyx2caKvIVuaeYJ2dRq6ThNaapb9Rd3X/4eTEFNRQAA//pwBAEcAAjCFDXbOewRaEFCOyM/CWAIsI1qZjysAQ4bLYzAilwAoJLbbi5MZWajlAKmG+8dsr5eAeG9XrxtU+jsRfo1zNssb/wU/TkFpot69OiVS9MzJO/QbjXBcbo19VznUfqt/r6gAm45D1EaK12ZgWrLOiYzmS5ue28UOteJpl4fFF5k4Ic39VbFscIdtjA9DdKXpe3iq361EzUr1C9mj+xl9/9//7QY5dvzFbE2RhKMDx5/Aplzm8ioFkIFhkYzOVlqfUph/u6H2eujk7bxuZQnvDkzeiio/pJUir69XisUiwvrG0tdWGLUt3rRcUklWlRvA/MjS5xew+5FsAPFT+zquziI0gpKCEVrG54QehQTf1fr379UpK3rf2nF54UxVhW7CFNjnZMW0crVIfKurTEFNRQAAP/6cgR19AAMAgsjWhnoE6BBJFvqGCOZiLA9ZGeNDgENke0Mx5WIATd22PhhhStA73rQr9zFi3Ec1yPA8IrcJx21URRhdNSIydyXejdkMvM3Bj7M0hpKjdNvr3n6C2v125WoejX9SP3gIZySm404V6dRea/xaSq4t6TdTK7KdJS5X9efyQdh8Cj3BFolxU6BREdDQ5hMHokPnWGnsFez86g8Prtzf2O9voALlt2nUqgWFSK6N9ofLexSWtxiG4CJJSo8IMvzWweAGHJ55i51NxeJEGhNA4ca9iRoTQdxdC12bCWd/0FU9T1egc/f8k8B7bf99MNkkSl6ymT8bXd2YJIsRYuqjAXaForVVtG7UUjW6GFBrC2VVagTFLWy18HXDR/2o6rgVZWSyFGP+y2mLfv9CYgpqKZlxybg//pwBO/+AAgB+DdcuSEUzEDHO3ch5WCINI1sxLysMRMV7+gTFBYAwJJSKnbZ5OkR9bePXljQFgVW7hU16xpnR1oBlkGsi6138RkMj/9TJT6LjmemWPOC9jEf+lfHfufKCMZTTeoQQRTklqsqgLiA1CzsJ5M8EzZr0vXWPVlXVGvff/stv8oZyI/9ZRJrF8jF0r2Wi1QYxVy2/Tx2Yb3oMI1CSvvoqEgst+1bGsGzaOFhHuWlIcmjsSERZ+FlQccYh2oi7oj+jac6/3GsDoC0CVDQ+H4HFoxTzFhHl93d1V6l1e98Gv+9isENNuOOTWnoilWtmVsKaySClXtQqKilK45Oo03t7tMtHYm4n3vf31GpbjnEn0qseqgPrQf01qem6xrZdqVNtUb3YroKpiCmopmXHJuAAAAAAP/6cgREMQAAwiUQWBnlW4BCJXsTYSJ0CMCDd0MsaXESEeuNhBXYARcuwhqJbdCMjAPwtU4lYgz190mhphqBtBpMrJk5zCs+rqWfHBcaDZp40+x4Ul0QI4SiO+2TU2UaPHXvb1VPWS5zR/9IIat39ds01OL5VT+/AGovC7sV1Nhcbtsk3sDiXpv1bFVZN5mPXt9tmNK3tmCff+mFudZbUhdT7rpT9VKjUjJW7/+gQAJimy25Xu0g9GbXzKatgkT3RWtyzHkzddZ9BG1z7EP6I/0gP4IaVP79qhK8dvir6AAhQdnZ6tmivBm6ECqbNSW565dPpAJTtog1tXFhScatq3rzOtzEOSl7J+yaKI3MFbGA8H2sKUaUTDPFRjnNuh0XVuquZN/KA8XPsbGVvTn3JQSXYMz7n7f6UxBA//pwBA7nAAAB8xBXmfhLAEKkawNhJXQI9MmDQaRM8Ryf72hinP4AFK2hgbGx0C0BJjKmI2/aRE29NKxfRrGHCcaRRDxLUYQEAdMxYFmkTW3dBoBZFZardzllFFjp+xKZWU/avQAE7fxhLWn0rCBQDXqVstFSyXchpKYKpU10+q2MDtU7nWB0tFdZDvtv6+iu3ZOoU0pymGYZaUqfcw/0xDKL7bejb+qliv5LXbNwQzM6MSHlSKYNOtnLKG/85SXU6myXOjZwKq1tlCsy6o+Ebv/pmDSqCfQ+g5gTOUc4ieBv/lmNTWdkaRUtQpm+z1ISBU0445dZ9c04ZJNLYUPZiMFNsizqQgl0vvMObr5FZxtTE5gStj19unFTaH6HrQ7pqifX8u9Sc+mmUyIIWFJKrFSb9lwZrTEFNP/6cAS2gwAAAfQ/WpkvKiRAptsjPQJyiNkne0QEtrEhKC3clhVPATTlwOQlAfAZlzTVWmWtlyOhGozIFA1cSj716U9RVHf6MNubZ2xg7sr1/26dPbdejf15DrGotPuseaHJa2HACk46G1RICEOo30rNCtESLeqJGsEbjU1oUAfytUd9LUt7/Xkn+5uCZ9v69GZNJETQQ9VdSAi0LsKV/o4bbAmp1EDE9JOOSUWfEoWTU3dFrIwAr574YMxvotzbkX2gfF95DCRKFnOMFazXr/RWuUmm6BnEku6JRayd+NPr/V9LZhWUrGpNxrhEIcpItGTGuqwIV3Fc05C7uACFhyqeA1Xer0OMZ49jFkjTs+v1P8/Ge9volBi7suhbOVqm3M/02zdf//7mDDXI7RrPQfQh1rntJiCmooD/+nIELREAAAHHJdsdPKAERCebE6eUAIi5NXgYwQABJiawJxBQAAU5JcKx6yJ1WsfbM0c5fewqozJmDCFeMKTm5a+jez91dtLVgddSjXOVitsrEFed+Dko9Hu5CmuAHgEtuURmImzAROEP0pILYZqTdPoAOc9Q4HmQgXUe0aKXUja0+Smi91f6ZDFyso5E7tozKvlTGdU5f09Rzinsu4Y6IIPvsxFSAJGoatAYvJhi8K/OpGcZDs5Ys6L06q5P8iFZjv/O+RzTOb+zox7hUFMZf/J/qVjo8GX/7fIT5nYSC4t/p/igxrAwXAAAABDBFLLPPq4SAcuHgIHyNw/XS7fnOn+VR4p/7W/5+jvM5v7OjHxEYBiGVP+T/UVY6Og0v/23xQjeZ2FQXB7/To6RQAC7AwXTEFNRQAAAAP/6cAQ+0gABAiIsZW8MQAxDJYyd4YgBh/DZkaKEVXEPGzG0cIn2AKaacsrbRJXAbHa2tq/rytUrVQMnW5SPOVmu+7XfV7dyqhiOJKcjNTN5wrDBdkNUOuYtINSR2dHX3cW5ce/7dSXt4lqARaackjaRJXArCusVqrtvylqJs0xugoxfKzX0drvr+5VQxJVVun84VhguyGnodsWkGnho646Ovu4tdLgX9uo29vEtQBSdclsbUIC1CmV2RiC1BMVva9XiZWjgYM2YnzLPx/aG80YDRnLeJkahv4t9Lj+ekwreKXe0X2lSvw+hWB9f1AEpNV2RokkpAoSpSdo1osMAbUc05i4GrMcTeZZnf9x9ap/9Hq1v6bq6GceLXhVAmBm8UuFuJROPtEIFvdh8kqgD4r9SYgpqKZlxybj/+nIEfi8ADIIQNduTDBBgQIbLVWGCHgisT2xMJEGhGQvtXZYIMKpFYRhFliWdnoSW6GvRwfTQZILbqPI8Gu6U3t9mQ0w/+yuEdzPlo/38xnrN62T8bT3OzWm5bjovo78qItRFT302aABKCFzhJ3HoQanKzxx6p3B90IK0vf/oa766nbom9v6aP/ssE7m5aP83Ywp6ub10/G79CO7fkituY08JHtTyKd6lYVYZ+HCSCRMAIdgRdtyXw6EMKzEBkgsGONmO+DNXC+TwfoKkVxzDnFhMRBbWoaVMkE3ktdYYDTHtRaoVw6SZ9HsV0DPaRCRuOQVnDFiQMyMW2GBo/zCDu9pgGrQpUTYdqQN9b+QG5RJyXoQBS1wUEVOfJkQWfnRtIhTeulfDTPqYuDRI97Eey/X7CaYgpqKAAP/6cAR/bwAIAgY2WZMsKWA/YjsCaYZECK0Bd0YEpqEZrC5c8wg0Br/wJqGJvVD8IQ7s8OdljaaL3BibZmbARJ/KDBmoh8c/UCT6p2UXe1C6v9BXvZ6EZqaUq9m0jB3st5b+iOq/Z6EFWoxlsFT2EP2/kCvnOJz37APYIusgkEbRNLS5yuUOGeZIPFxz5RNLkOXdEjt+T3r4tyHX0uhzN++jJ/9rr8qBskj214SCVMES1/QYCVAFTwoMroLPdTMyIgvY0gd9V7Ef3bt9H/ZUUR5f9E6jAPp1eka5bvxoxx121KGSj3393RRre9aBDIu22XDALkxQSIvEPCFK3hKHWI2obdA3ye6r76MqO3JkO3QxEPbstnnu36IVWUELfMWpf/wbdv6tK321H/+v9xrE0jn10JiCmooAAAD/+nIEXTMAAAIdV93RBxK4Qwm8PQyioYik1XFFpEGhFCQvJGGItgIAsTltu3BUGXxHDDy3ZdsJhY+omJ/d+Xqe1KhE6m89tCqfUjpfSx68pdU+6/W4r6T+ipfJ+n/XVEqh7/+7/qEfd3dYYRJMcklkbkaspxdvPL2K/IN0kOCs4TakUBragj1H5+mGd+5SzkUz+9UNyf/c1R3/T03N+v/3Qy6VOdB+oUeaVFn/SAACRKctlxiGTekBOzauQCnd0zBHdkBD7MRy6HfY7/EoureFfze3y+8uYjLmJepXar7g95EmkIq+1uxKtr31aXFcM9kXrAATqnqvLBndAtTD+lG8YePtA/WpW0Kc6Tgnu6KnvybWhUfQr+//Rdv0TmzlJdSt61Lb/N/nmg3ngIJAsR3JcBQZzlHurTEFNP/6cATYGQAAAhQr3+jBFhxAaxwaCCKdiOCZYmeYroEcmyzM9hSgCCQQiTbjabj4qDRItaG8/o58SU0Qc3+et1paN1pkNPbREPl7NzKEksnU+vbWGyn4l/OMVYj1LFbIsTfZWrir1xm2kjhPm3JI5VKLIVXcha232eoTyiqB8Z2pC9vjPOhcxi+Cf0Zqkvyeicgkja3bzdq/p/pR01oz2/rXN+o9FzHnUzYQTllpuJBxZwuiwEU0op3CDgw0R3wM9Ae7LOGZ8wE6rQptShoqr0ZfJ2VvN4k+uLxs+LIY16qL5FkBNbXjP0JiqFu/ulTf1gtzX/mAxptnEs8akcn4Ro4iThFcrKgFo1wwM4l9md5hVSJKupOt6dlXKbO3uu7rZuu1UTH9hyVshN71MwyZAsksVW+y3rNoTEH/+nIEoaIADMIgElmZ4TuAQiMLEz0idAh4Y2JnrE6BDprtDMMJSgA3t/2JqS8pJAolM5t+Jh90dRJRvGqwqwxSggIE9CbRA6p24s98kes09YjOse5bK7z7fhQ5jXD20LJDTVowi7VGmP8XBSmt2OZ6ejCEEKJcZT60tFT0ZrBjMP8pJCbUvhcWJ3aHbs90UEOzVYW24sPh+Z0mCIvWu1RufsOTfV39Hat/7e/1gJy3bVXT6EYKHEEjQHGOOyAsx3ojJO6XqzAhYU8XDiLGAeWL8ELDZ6mWhRUIR55rUWvEwXgKcemlmuSbo/F3LrR0fi4KTcknHzMAcn6L61cIUDfYKSrLGgngxIa0r0VvmMG6k5gTU27XtMqVb7lTp93UQ+zlEIYTAI1dFNqetbObmKKEp1929MQU1FAAAP/6cATKgwAAAfIX3NCmFAxCptw9DCI1iECXfUGEVPEPh+wM8a3AAACgkpNJyPIoMohNhaNRw8morGK4zav3V9UQfKUOWsUjDrxPbYbBJ4SvFmvssorq9NHYTQ1RY06f90XO0urTDRSjbkttt8gpjouEpksM1kqemtuG6s/qT08Z2rR+hN1U/bSj/Su1Iu68HFH0Ai6bWsmx0W6EMcppH4/csA940zvTIhHqSjjlwMfpWFfBMNcWRmRBHk+jFjC3mC8/meNEHxmsK2jgz2Qqi2obuKt5aNMJLNfRqlwy2Ags5jovFESTvejSAG7v+1IlgL6YguBaulespsvzclL4GKr8ewWk7MIrccDKhi1YI5+SOO0lz6lu2EE31qfmCzfwSWgUfdaqYSuLkcx/770xBTUUzLjk3AAAAAD/+nIE9FgAAMIVQ9w4aSosQ2WLSj0iKoi014NCmEwxCpGsyMSJLgCApFJugzgyqyp0dt1WILtZPhZPN0luVBFCtkNrP6N4kv29y9S7f6oVX7/XZlq6r5acYfUwmEaXkqOvEouGrobUf0gABACY5JbeiVgNAghJ4RYJaPehMJtpLAY12hATJnbJbzP7crBhplcXKkhE6tZpmrYCtDZ5hEWiC7636v8ZjhjruzUY8oqpyW63crBjUcr6DWwC73TXBm8+hyFS+LZdeEdn6mLWGBVcqKjPMTkPys9u5M94OUZe0RByAXJFfek7BZq2FtnkQ1s/WAs11Ji3D8dggeJdGR4u72XCD05rzn082Sk0wb7o3q7Pq+joycrdw4SKpFZBqmoPcCRzA95dQSehK9+TgGGhQIfr5hMQU1FAAP/6cASuEwAMggoPV5njW4A/5GtSPMIpiIzzbGYkRbEFGO5oYwiOACm/4SDOncitNdQqk8NQiNlS14RR6B0DrAAWELnOySJrjV4RrIU0iiq5cChBode9liR5LWpzk3sabkQilH2V//tFqrIczt2tinowM+jRL/AQn0Bkt2fiHtke2ZuO+dCU7PnQMSSJCzx1TLNhwyCLnHmHAlLqahW+zHgPLvIOe+aHElJpwQe/UsDSvGVQKZv0JkP2onG/uCHeziOMy/UuqcjAvO3cBHLzjL0Gskmy/yd30fTr1ELzwVoWpSWvLQAMUGR1xl6EGJTccgLImSYEj1EtjkfDp2KNcjECk09r/VtUfCFBV79BAeiMRl6TTNuqE2dHEioHJFoxKMBpdEBtLlxbe1ZnFakxBTUUzLjk3AAAAAD/+nIED0oAAEIgKV04oRTcQGrLxxQij4jMt3B0kQAhFRbtipggBJBgpKOSGqhQ4H1DrTjNQgDdwoNlchi5dx/rMDxy+L2/S+ILqCqtlyRW2HGol2BhTxmoRGQTSnpcoLJFi4RIAdgEFjIEgGUm5JYrtFQo8RaycjfDPn5cSRc6645ea9izImf+xdfkV2hrytdGSjL7b/25HZ0XR3Z8p3ZKHRXUjORTML4skcVbIiUnsdPiKOnNtKUPiF3ICHpofx2o1PRWfa8lCeReRvr9X6SEAy8QBgQAPUMdQIBAA/xCHBWUJ9hB/paLOCwYKUX5QTher9BpZthUZKfGjbo7OUxl1nVIJAnC+Hba3yt/I7t6ejNJQ/zv0kIBrlDggM6gg6gEBAX/FQ4KyhPsIfJtU4LBgpRflC4XTEFNRf/6cATZygAAAhMi4AYYYABCRWw5wYwACNhJi7yBgCEFja8DnjAAbJez58qAYbObh1G5qJCu7kBATK35taqlgIgv1jPQYkL/0xBAzFnEA6I/MC6AEOB4DeoVQs+wchz/+6omdb/9n2CtYgACEEEEFBBBH5GHLUgMblsjvqQVw7kwplY+m1qkWogvyjPaMv/XEJTcvLJv/L8YVaGCJE98UDzhOqx367ko6//VVAkKk0UkkUlJ5CMmzmalVed72v73suRY7OOvj3Dpr5FSiobGUYGZ54GZ5Jw5IAAscePU/95Kk3U612LUCJoUYTQWCjhY28RZisIgatuzOTc0Vuy0rmHlY/A4uoHDAz0jMWpHu56Vm8dGZ6I1BIqGwBPxUDCrIrPCs9cygAMOPH/9FJvd8Wy1n6kxBTUUAAD/+nIESbUADvIDGV2BhhjQQKLrsDzDLgkQJ3QjPMAJFQeugBYMIL0B8hBYFziZ2qNrbIROEbL7JFKzzK0Z2PHyGYF0lHLA8Nic0k02TqWxtUOujUNSjHnxl/lNt7a3UxfWs+17aP1ZopwrlWlFBrqECMbaE1LRTf99gN6NuVpOCNB0GQ20o5YNqMpNJAjXE6itrlJIujZ9OpZ8Zevu2zeVdTXrWfb/6hyKP0morGQyap9/lGmnioSEx3n4lKJak7K4vn6Dml4Uykrt186h38tblra3Lbl/y/zg3e9a376Xc/o0juXrB7nt41f/+qb/vaJCs7WOwU5D559rQzSHA9zowYSOLjwk5IFmg+aebgMOkRwcDAw1HtYSOvGSQDVc8dL1nmJLnXvDbjSVNsiZ7dqadfzWlLExBTUUAP/6cAR/2QAK8iAgXKkhG7A+AwuAJEM4B+hhbCYYZQEIDK1A8wzYBIC9OekDLqUludfa1ps8FhZAweYt98pSGPxRL0+d4otdBBaaJgBolg6ugUCy3LSlkRHIiJvuWOb/m0JoIjWOV3ySyJWXI0gZLrEg9yB2wASzSw1bIsMflWtKkp1BnGBwNB8CLLBtYshC0iqFGAGiAq8KBZblpI5FkiJn8t/9PXq76C0t1QPnSsHr8l69pBcXw+EM94/DI/CkWbHKJEQdFVhwCyBYVcJb2rtvNm2IrNPTTSywCuaja91+t1Gx6PX9RKkitlGPZjaB60JFu1Bws/NFT/w/HJjtS555qwoBLCpB8QqEJEFRw86JVB8KAQ2TySzT3OSiXdFZVG+Yv7mIzzyu6xMQU1FMy45NwAAAAAAAAAD/+nIEtBIAAIIlDN5oJhCYQ4ULED0FNggUY2JHoE6BEYrsWPSU6CCwkJHHIkpaUMQfcrmC0jIu4cZkkyyhY+8zSWEbgZsVFGnty6zqlEbXtVZgEYHfOBIqpd4FkaEvGiIP6SpgSootO/dTXpXKdYCEJgn9aclgNpXEgJMXCg0Vd8VAxjQqxlbRUFhrHa7OtnT0MljGe121EQkHUhZdAhFFJuOli3stFPyS/9v5XuQALVdmOtUCGqVX6XU1FaufVqONhv1wpUohNLzHr6urwSOFHnaSVZUJkz7ocffKWvAQzMzZwwgbd+mzQ9PVjExkWJABVZjE2VISGg7UuKnD4pGYb0wI35OTWJotQVZCjks2NHBsMpOUxSVrApyvrbLUIMCjXAPACSpazZR13aXku5Vi5b31piCmooAAAP/6cAQPxgAMghM12JHpEXBEIwuNDSIVCJBjYkexBQEMjGyMwwnQRWqsx3aXBrGAdXBK5hYP9aZpq/6DyH/zKtNGNETr3WroKVH3o3r6Ipb3tq29CvpstDafj1HUmhAiuzT93zpb6kdFQAAAEJRbbTetQUEl1T78YwNsYO82euqjus/RuyCEAhhtJ91s8WCRRIB3JQxSVWqrvvZJUrlt8jiJn6RSlciKmemE3qjBFr+WZNIEUgmSgzDu9jFD1lDH5+lyQMC+XVI+15vcR/EfRNTa+2PFkjU6AMlb0xZNrb1qTqeHkQ32OYuwXoR9W4e/7JsFJNORlgilkAhgVCjarK0hqJkXnWN7cPy4rmoLsuCGzvBwblWII2NCOLu1KQ4RmEhSufsKS6teQu01TmA7q76LNY71piCmooD/+nAEZ1oAAIIZIlxQxRK4QiH7Aj2GNgiwk2BnpEXBCowtqJCN5ABAriVkcl8Oo0HVasylvBUrMFBsbmcRzu8/ua7r7zGajo9IWeWQTEg5RJK0j2OUOdGJsvN1kVvhQ8NSS7Z2urV39nUDNeUgIWlx8mELo0iUjQToTVNmx0hefhpLD1YVI1BStyDTwRMB44Gb1gQahx29GtK9KmsIHkMuN71iz3o87Yxf7OaAISbkgMprKI1xdXroqRqh9pVInEtVeDVXFuqkYIsxio2rGZzvI6r9r5ORHiitdDVRG110Vi8koYh60f67jq1jWJrV6x+gMAXE3Jqi0oTCMDsR7Czsf0cJDcM+lV8PNlBCPHrT1hl5WxCWakxtCAAgug0Djko0YVc4WfFD1ttUoSASU5Bt7OJB6YgpqKAA//pyBE+NAAgCIhha0SUTOENB+9oMInOIqD9kZgzOQRSErWiEmIwAAEmm3HJathC0IEUXeqTfK5K0XGEoMDN2RoZsiajPCp31MjHhA80ccckwsXE6jq3jrLLWJFpxh68VQhdcreP2bV1t+pAJtik20nJSpqxTJAf4i1O5cg0pUm8AnEkt1B80XFBgxYqukqBYmSibHOHMDQVPkbop/0NHhJTTxU4FBkWVWKVirLrgCo7t/ePZHwLll23omam/Qk4z5KDLPOUUGkK6p1w5jTtlhQHmCYJC8JVtSjtF2vLPVyRh7pOhiqmG7nAL96XhJvRVa1pYAAMWkpHLoZWFAcRjomkuzV/pDDKEFnCwuFijCTjpQVqr46WHMIBEkb0Xx5YgtpSZpD+GnDXLffatMwbQQcYQwTP7ftscmIL/+nAEUzYADMIOD9kZJROQQoSbIzzFRQik4WZGJEixCJJsjPMJIAFJbv7NCuAZEYtgZtsiBw1+DJEmZWha4AJl6uWPsGyKiZ8ULCpeZuxR1FAjcxDUG1PyKXrLMaUR3EbLWX98u/Z/oQLTKly5IXhGncbqtKGLECFzwg13ugvC9UexgOy7LtczUZrum+6JuiuRBrrkmiCW8y5ne53120dUWKmH2tUPS5//+kBEX270AzC29CB9KgdD0bM8SuTkfgWp3+dn5n1J301o+qo9ls3BTLvld6rsz71j75Cb3Ag9LkCy07NcXS1IADhIwpUXVagnNt/Le0Ixz/JC/IBOjoFG8BD8Zss50Hqg11Ddeuju+/+9N/MwNVxRMAh5CiLQVbaG6SVgqMZLNWrd6/vS+GEfVvTEFNRQAAAA//pyBLC0AAACBRhYGeYqUEMCS1YFggeIfOGDoQRXMRsH7ahkmI4hJ3bjOkATUuCMS2DDoikKGexyv+HPrBWd0IV4I23G5486BXVXqTCyybmUYobFdEkfh7YNBFzY2dmqvfuNqa3XeAEGJrNKCysGlOfiHY9BsBFd1aC1Qa8bOPaWB6apHmXyNpKjuHCps4hZB0BSVD1jUPcAVh81eISobMPpGPfbN7n3O0oiFIxtqWWW+16ECNdraOjKIUarVQq4P+fCXfxbtZ1RELvk2VEZVNQ3drr2XHdc4k1hEM1vFq6hzmWtZ1QmFVhj2lm5sAEGgEiilHQtMMCIjY5HYdW8PW586dE1NzlS0tXBZEOC7mk2ueIXu387FltW6+xrUOqBQhMizyzxY8u0iP0PrnrCuJNlfSTTEFNRQAD/+nAEk/kAAAIVK9gZiRF0Q6R60z0idAg1EYehnKwxDhHuaGOJ1gCk25OREcgBGI4coTPkbs9iQBTmbVqhDlf7ON83DtqTEnZGLzb9nutTqyL16M0wNRpgWP2IyErW7W6zv5Wt11NCn1gBuS0QFkhx2CZgohFEuCmZnFvKtNMDeeI/fcZUJJjMbiWTrDdWXJwtGVt7U5CPdRauNPyGnCD1vTiCVkq91iUz37v6WRk5JJHNdNvL3rj3JKNoHrzDAX1MD2UzZercj68TIzzdHTNKYy/1ZUV6Xprr09enRvd0ml9RkSMIkrkOmT78gX8sACUzKaacrPBZx5nA+7F4+MFdZtdHDxzIQYlfaeR8L5LTZUfUJv65B7LApP7VvFnTjD/PVRlecNElCOomFGiUYaqZtSmIKaigAAAA//pyBD7WAAACAERaPSzgBEDkevOniAAJKXeVuAOAERYu8GsEUAAA4BbktEOOmAWWM4MLtNOuchKywUH3vQ8bc3p79G/Y289q3z11Vv5laPq7Zq1Te9VvOm3rnGVPZ587ldie7kPrAEc34XoyNNgCyQFXNA+IsZfMNwbniGsrXKpqvIoRcPyt30N1J1XPZFdcKqDXVKV52ZQJmjniJywlAcmXS5cZ/igAAAAAAgEAgFAgFAoFH/r/o7Gf9HU1v/iYbEgkQV//xoaJQ6RHxz//xEGSzj80aF///8XMazAsJC8qNf///+XJGuYDxTjRuVQaAAAAAGBgUBgMCAUXiJ7u62////R2T/pUrUT/GCwsCIJ//xAoeESnENb2/5HauR/f//i1mYOGHqX////uLFcgcmKHxqCHWmIKaij/+nAEYwAAAAIeK9/vDEAARKV7ueKIAAhs2XMkhE/BChsuZJCJkKQVEbFqyWinTlBMuv2l5HyJ/bIqaHVVPvykZf/No66f8xTkChUNCw4Si6nxMq43YQ2f7mWhgnsxoZYeAokXPxoLCpGuBAgvgUFTucIGKo3ZWyv9vydE0rU93pKRl928UtHLo9PzFOwUKhoWHCVanxMo043YQ2fj2HWWhgnsxpFjAKJFz9YLMI1oQJwcAjXkHBkPodhnlbvW45mO+V8uT0ayErA9fZgKxQrX+bGmdvy0M7JiUeOWGrDzov+1ThERIzXiolwED+ZUlYtKowBW75BGTxzAE2r4Z203ZYOZDfSL4b3tK2hmu11b7qES5qtt/pjTP/LmduJR6lhqw86L/tU4RESN/ipXUD+ZUlYtWhMQU1FA//pyBEICAAgCAUBawekpQEOoC1I8JWoIEK9gDCRLQRMmbXQmCCgAApHn9OzkOTon1LcIU3ebyNaQvRqDn18o/W6esvrzKg1D0p68rdWbkT7f4/0f9HL8b2ItUvnuiKholhVRHt13M0q2aQXYzqS7br9kzN6HyqSki48kym/1UvS2LKg1D3o/LsVpq2d0Ib3Zzfg3oa3VHL8bZokiy+e6Kw0SwqoOtxKtK7tTsDRVCgRGh6jgm9aa8uHKssHvlSGWOk7lg4e/iKMxQQd24IQTQn1e606B8zVFevhnUHO5OT/1pZt0/Pf//MeHAAQAYAG21HHGV8HiSl720t6imoBu5C0GnGL24Ic9WoROp/XMrJv6dM3vR6Gv/R00Biq9N3s37p9f3Rv3cM679P/rXyIcTEFNRTMuOTcAAAD/+nAEgocACAICQNtJgynIQua7jwyiMwhY12+npEVBDJBrQYYVmAAAwRma82kbITFh4A8TRrjCXOnHbRMEbQDtow7xgr6H9D+j8reraP859V9rZfUN/r0XouN2s8c1M771p5F3ycWAQIAAoIErYmZYsUBDoZcEUbcPhUBCNqk6AmwTv5grlOpD370uUmYpFUqOumnv//L4N62lH/b9czLQmn4n4o6/JJeKAUAFF7bbG4skYmZiS3sfXz02EzFK3auDEP1638IKfp9H9OjN2t9nUhSulpqqqJ91EYTAtRJtBn96zCCgl/RuFvs21UFtjrpKIkhXm5KuVxo7R3tyvYGmdnigdakPbEFJHwm2PKAGKUGH4xurObnF5lxxy7owpFKaL0iTcr6DOjW36fivf+lMQU1FMy45NwAA//pyBOY7AAACHDXfYMIpvEJmu2oZJUEIpNdxpYSw4QmKbiiBDdxkoAmM1d1fHa7kQqFxAPqAIyAh0k1I/bk59C+h9ITdtBqcS9kXv5hLuTqqI9Msg5z3AR8CpFp5PqXLYaTtzNss9V9ZkAAZCVI03OlZGiAfI3QYCLZAK+wUPs+j6F5YntQvkbTfyEbIrKdkLd6+iczJzv/jt6zLz8NTzfk2twwR7bjhaWf7FQAgAQBB2SSZMvdagUGDHeCejfmggT959KWMm8L3foLvtfPKDdEs5EN9+9Hzk9upe7cYr7FRUzFUs2+5j5hOS/GUq1tk64MAhLOkrnk85zAMGZ14fk35T3qCemrvonToJCU5rJrQLnViNF773iVT35IiMkXsCrnixzT/hyuYZ+f1Be7WCJAieSmIKaigAAD/+nAEhUcAAIIVLFxRIhvoQYKLRzEiRQjcl2rnsKUhCxstKMeI2EBMhacmkuvGMJWj2T/AhR+dIfV5jNXZH5tUjWzF87er+uxuZcYi+aRzZGGUJ1WmRVSjzfSjY76ESrVPHVu2OrDeR1gQBTTTl3XDg9HIoXGmKIACDf4hLmMPPq/m4QrziicSHSk3GZw0KKvShBEWTiqWVv265Zh7PcQLVJkWe7ohvTlR1QASHHJJc0qzH2NNmI63wNpu+YwiZoqIZMJr1P1j/Mr03afb3N2e+IO7Am4++XQGTEuFqBE5JiJHklIY1UYcnpH9LHw6u9+SMAEzbb/XHfNxrU4UF4xzrqb2fqTso9noON162Xzv070N3X7MxXUsq+a8zv6bSOnUT5LrIju/ZBWpQ9+hjGm2Fnn/oTEFNRQA//pyBNLCAACCCC3aGYkSwEIDu3kJIhuI1PuHQZRK8ROV7XSAjxQBS7baYlS0wHUd1o50iAZNj8qg+dsY3rZy7bHEvmtXQ777a/UKzYM7IltszVVE2GsHkxVND9Wnt27xZotp/vyf7QABkBlW5BeRDvBOsINd/MhWh1HgsE7dunXu7bfBMoqU45V3GDSXetzBAbmhV++xGnXUueQTOrOuENSWSdT6g1a+GPJv/blltuiofH08DGtlLyBTxuUdvfWer1Hb/rnz2SCd3Sj/1cFuhXTu6iHypIZ1Rrbcz1DpmUgCee01yyNkYsyqKmKe2+lgEASUmluhWUNA4zgiuD4lHfQRJWMG1v8NVfV/dXQ25rxPO0Tw5IpBoTLzt2HCR0pJuS9xcVb6a2a+h9uVTpcp2hZjfv60xBTUUAD/+nAElJYAAAIdNmBQQRy8QWL7WiQicIiA2WJnrEsBEB5v6DKI7pZKpKdlkmR2QXU75AfUnUM9cmuXM3Pz8fdIUwdLmEymUPnkpC9gToYRbl7GpXfwZ+uOOkwvq9VthPVVvSffpqbF1OCQAAQEJOSSVbmhOaZ4Bt0odz9M8SJA8K90L34PrnGPyFzdUUcOe89PiJx8DKl2hpR8SCzBAAiO9b0fVTN2MZyF3+sgxX//uTmk09Jh+hMVxFfMT/JKH2/dJdqL4oD8bmYgJudFLqxOhOdWZLOTn+j/alR9PyVd63UMXapCcpH/K3aVrq09iJIR+bdkkmz1gN6uq+z8LFKPW/XsLSpLH8nOcEnlkNdXu8VX6ZGZl+Wz1zqMVHRN1bcEPWCZDZWWa9WRZ+aqnK3POvfoTEFNRQAA//pyBAmtAACCID3asYcS7ELkavM9ImYITP9iZ6xM0QsV7GmEiLqBgCzXr70AQXz0LLhIVeqD+jKXo8LMS5FtHXVFv0fmW3La5R9WQV/KQgeZ1bkZ7Uo9f+hKKCS8OpeR4QNaxC31D0vs6SBHZsMtKXbgPIzRr5LL5VRsz45wN5/A6O9Z8SrZrOR9B2y3d+C5Zxu5mY1hc4QTIOnxUowoAkb3+hA7Hjft2paOX9gBKbkkumhgEL03ZTObDDX3fpYL1vc133lRGW5GzuuayH6P4J/p0FrmZvsbcHWpeZno/N/v1u4dthScT4F7Ka90f2MCQAJtuS7b5MJ9lUhE2K/o6B7/6D096wy7rb6Px4ZtH6VZejc8amVG3ZSI9ZGo3hYsEo95FBT32oJTP+c5Out172UMTEFNRTMuOTf/+nAEMucAAAIfUd1QwRPMQCc73SAinYiZD29FmEUxGSHu6BSUTlAA0JTTbleJPBqj5mCH38uCRo340+1qDPE37+jV7PcEj0RX30PqXRTdWdqSV28pmolkB+RNOv60Pa2gJdX0GryCEo/QgCQXUUnJJJZqpKYzX7G37A+vUDTPyb4V6zhCfJaIlPCR8jUyFb5rCni5hatc6lm8hRVb5I8YpQt8ZPch7rrLKAEAkBSSTc6s4GpXKAj6Dl7/Q09FUfwreVtxlZAWZ3/qjd1pgy6VHycvRu3X7rSr943e5T7Hpvdwr59b3Ml4o9R1b6JvqAgXpGRtyWpCU6exMo3qLNnIC10cE1EfbpMxx60BXWph1auVHQqqyrbbX47UvXb5JaPSz66jZiOzzrxN/bZG1PUd3sQTWmpMQU1F//pwBM/BAAACHCNYVTzgAD+nKzenlACI/HF5WJKAERYCL6sMMAMAAIAC5d+JG20YEWSk09R3Ocui/i2WQvY0OqIRs05RHIi7cbtoWP11R+2mbXOtUqQsPoO2TUjRL7uIbEEL/bTR7++2gBbCTclGKxpBLsrnqFe6Oa4nuxuPhQKo0fRAOeVhft1qOGP2f26rbQuqxSnpztzdNlaqNeudB3ENftp5P89bDAAACKrMcklt12GAEyDv4xO5q1F0LWfaldDGcRoTOzcxtbQGs5Sdx5UWSLizEqIMJQ9re0eI3PC72PclSIoISJswRH2P+8NXGwEAGav5Jbdbtv+AOQY/lWBvaZPKO69lo8uxYrAQDE34ye3+TMs2EoBuUZ0d1Rv7/tz9N2WQ75NSQgnbm9v7Lv+3ytE/ZXTEEP/6cgQ+5AAAAhIyYLYkQABERlwCxggACJxrm7wRgDEQjW5nniAAAACAooooo/ogXCWQVMJP9hiCf1MG1q2/6oqf+9tu3Iroc2R7WOBn7qIDFQ2ncWfwwm8IHKM/gg7nBfo/dxVpSpv7gJKKKKJrhwnHbqQqYH+lJEKfopgztrN/Wip+fjp/boro5kke1gMDPs6iAxQw+ncWfBAy9JQQbpfKBjlFt7vo6heciv6CHHXNLbY2knMEg2MjMwuzNgM4Vz6ZF/zIytiEUh0MMd1RVwCNQUZw2MDRChmeiPeskxOQZ0VhQWacaXQzatbpIFRL9bFAAANQAARxdQHBRNjBizfJmkNcFNGh5Yy0yoq7eit1M3uUT6oq4BGoaZw2MDRDZL4j3raxOQZ9YUFmnGl2dtbpINEvrqTEFNRQ//pwBIZnAAACEiDg6YEQmEJlfH0YInmIrNdzR6REwRMbLQWBlZAAAQJ75utpKsBepiaZj5jAtkwUEyIkEddX+230qz6J3ZhwifnkacE0DORFDzSiS3UP4p8iOrw7f1RbCp82WfWy0OPBbKMlsaiZJbAaORnrf90rXTjEyBBMqgv6165upvdmHIp+bb7sVJBZIYXi5FJ6USW6h/FPkX14dv6othU+8s+tlp54AAhNBtSOHsDAY82MyBdj4mgC7kh8PTrSkqtVla8vxpfs3qJqurbL0f76J9TUBXagV0S7Q1rNM4UPYq2LdW07liS3ap2SS9mQMk6cYh2zYhtqe7r0yglVKgYkaL8Fizxl9VrVmeXeIisM+32LVdW2XdH9HsifKmS+gj3aOTd0ltTXLFexSxLx9HYhMQU1FP/6cgQlRgAAAf81WhMpEzBDA0uKYeIKCPTXk6SETnEKD3L0NIlOQ7v4aBar6RuWtvFKjFM8KCTMtn91b8Y+vbuFEFRqYMysyO6PQmLk63bpR9vxHt1Ka1vr3bgmf2EgLr+jR/9H0gAEAEvqr9TiWVZGcJYze61qSYaSAKgnbMnIz1pVz0f9Xjm5NBMGjbz40XocOIlX0+6p/9krr6kGpk049m1JV0UdllYgtzqkjltluXdi/snKJzb+kXtAO5za4ZtIf7v0PVKTc7V/R/oFRi5dZNF9y2hfQX1vnoFEAcY6VdHEGEZSkpYJQPw66vWDMWFjTek01+0lwMjCOh1PuSFmsP5914oTpyDsQioueDJamhmcG4ARUYwoOOyDAccRl0nxVkj1dSIc+uNkakfX2O9YYaxMQU1FAAAA//pwBHqWAAACETZbmeUb8D/mu8kxIg2I3ItxRhROYRGGbYzyiYAkSa3YDVL/WDEQxVYVf9sHvXFFXcKqQS4xn7OjbPohtbsuhWekZ0N7Gf+dW6nIfJIyan/iu5jFJ4138iuKO/dV6PWIgaKnSuBUlokqSqBL+lYPjrRPuEq6crZ4Oal+njXtsqm7h9VzqztOj1tQPV03vtFGa0ein3oJfz3o7or4eWvoEAAkIORyUpAtVeiFVIT7sOXCZHkJ1jZWAVczNS9Bl2K1F2hWThm2Q7tcOEjwfmDrOykIiAUQxFlVRm1dHUWdK41GjM8se9RQeu2x7EiitVbRWxK+0GODvVMgEwsGwOWYhQ8AGCii6jrfhnASxzzBUgpljmlECRFRhcHWHmJamzXkz4o24l+cxEe+mlMQU1FAAP/6cgQx+wAAQhg2XFGIEwg/QptCMwVQiOjPdySIb/EYma3cl5VKAABZAOR25gSYoIg7ihc/SV4qc2BHBTwmyMs5TOEajbZs86e43kdbJXa1G31cHp/dkMRuNvordayQqlJAqvf3al/R6wKr8uAZNS8rFZyQD7/J1GVDr/IQBVcAo1hDjb3pJxE3LKeOyETkgsWddYo1Np22EDFH7XgiyR6/jpP9/P+pukQAZRO6ud1CxC3jP5C0Xj0C71s5+Rteg2c7t9JPVhPc6u1vMuq3r8Ih9LZtwEMY4RSSmCQrrYHQtPa6wHepjzaH2214Kv9ZFAEtuSRGKoqHxlNYT5QZ6dGvpzWsFay8/Xo1Ge+tEsw/UY1R9PqzzdW18tN/UYdx6Vn7CIvEU8RIqfa5pQ2uki9OjRVDf0piCmoo//pwBJO4AAACFhVaGewRQEAjq0M84nQI0M2HooTY8RgVryg2FUoERS79hb3sKQTwjMN74TFgAscloZMhdxPFiXo4O41xnSx5UsyVqAxqyhOqKEacYEgcfqd7WsV2SSGVmZ5ei176h344EWK/9OtThZ2wXjpuvOJlBkA8qvHlg3Rk4pL4SsO2GM/ejX7KJeJDpibSiUqq+qDG67ohFhWyzekdLRRf17r/1pmIIttN2SS5GFUUSHGH5WCW3NOwzibZ91K8W33IUifDmghZnETk79jzPlykRYxoEyxVi73Ph2+fhxZEsSiEqrqsAu+3qfGSAC4G5bbsGFE48J28TIWKuddxSlV75+ovjLtWwkB44heV6tT1We410O2esTJighVESAkNuKbuO75+ebaxO6vc52V0YmetyYgpoP/6cgS6GwAAAgs1W5HnEzxAhOw9BMJZiLxneUMI7LEeGu6otIqGAlW+26s7a3V336icyjygl82rG8K8AdHJaTevF6PqjVa/qayotX3J1/9lJUW2DqMp96ut9yzK1OPuDu9VtqiVpj0pEwhSUxWRy40A2OYXFwLOteMf22X1iI5dug5Z7Mvj3TyD1pqlX0rUdNfpjYnrpd96q/OsclTj5sU02vdiVvo3WasBQS2mnKwjvDzObyNBlybAsZ+r4jMko23qRlQUUROW1fE740Xe5xoE07lMLGJj9DIpTbIPFUJKlh0m4jmGuirEPdVtIlkABaSKSSc22rlJ6TvmrhSGiTiHUbF3fBKvoATo8mNk61gzSI2rvX+d7SKykt8n8nM6jgHYedXfkbLbbFUNcpJL0AENIrSh+pMQU1FA//pwBGbTAAACDz3YuTg6IETGfAoE4l2IARV3QKCjUQKZrA2DldgQAARW3CWvaIzuUCMbTTm8EZqmnRhE4IaBkWLg9BLjDxQW1dKWoEt1XUzXS+31r7f3/sWcoV0vnPU6luXzTSPP1daiBftOuOXE2cAktseMSItzWKC8zkGqBEist06KKqtVfZTwr6q8v1ZS7DUSd79tEwQt+es1vnyLpG/ZBpq25/Y/nLcXd0gOAIEY7d/TSI8MmaoP1nLmyF6PjfDP/j9k3NjGlJ0/ZfJsVlXo2lmYXfft/8U5Ofrx0k+JJoDVKU625So661VQIRkoEqid94X0ITPY+kHS7FnDNSGJEXJ5pfcXOgFjeJehFzIgIju2vUHmZtLxB6E6f1bstK1pTpqw96ZT5SiuuhMQU1FMy45NwAAAAP/6cgQ7tAAAwhpT4NChFjxDBvsDYOVoCMFPe0METLD8me0M8xWCYoT4pxyTddGShszij2eorVh/CnbfQd+kEwYg5mrRkXJd5+svzLk2QKBak/+nP1fT30tTSy0edSMqdKUEtQ2RLy9ausANO7CpG5fVW6sOs1sDw8l8ih8gW/KiARppiRklqVvDTj0RgzdtLRMGiKF1O8xFZOnvdX06f06cRM+nG+yft+yzs/oFoDUktNy525knudt+eKyYoEqLgm2fUbrrXfX3eD/nb9X7f278xpxInXv0/lfZjuZubONozMZ7oatn37aB9jgiSMls1ah7wSknLWpk8konLWmGmDGYFsabPg1HFZAfy8E4THJelbQsUuN8711to3fV//+/M9xXb/M3SF9FiLMr0RHTsRV9CYgpqKZlxybg//pwBDtUAAACCTLd6MEsrECGewNhJXYICM9u1JKAMSIZsHaMcAYgAACEgFpty5HZM3WT/qxedgw9Q9JlT9p28ll9MEqr4kr1VVVv1dWVOtp12/lnGEJFZq3keSk/1qjaqYE60qdq94IStoFakxuS51G/gomNXsNzh8cGnncpnP9G6LjzNfm8nwDO+FXSSc5UxoYxSvpeua2nulffv27Uq6oJNZJf9FNYDRdWhKMKJg9JIJu5DRjGEAjY5mQotw4OMgFBxOpXUr6D1oAZzIeqDwZxqsicQP2Tb//+nGtPf0vysnTbRH1v1mBBlRRN5yW98uu2uvuhED7tj1HGW/IzoVVal9Xvdqmz75RiZ4rPVTD0ovo9u//35h5sVuLV/UO1NW+9bLkt9DexIkMokm4hpWmIKaimZccm4P/6cgRJegAAAhIIXr4YYARDgRvaxIwAiKyxejzBAAERhW8HmCAAAAHJLcslr2gFc7YY8E0pITllBuIz484AyAwsReWCCzsUrw5URyIqKpWcUi6sCojzHJDri/pDVmLfSXej//pehE2AwAAAYFkk3JJa7oBar1fQWUXVsYs7jJ4XhwPOIKew1C4TNEXtiYkhT33iw82HsshkTmXE2jOnY/3+VQ73m7v/+budNH//lIBAdFpbUWdho4rPf7XGYUVEVnl2TdHuyKz3szGO3vsRUnrMW3VDUqUELuBaeDZ425Tnrn27526/Km2SPtTb1BL5qnTpdCoAADQxH8iNGsLlFZIjm9LCgkIzIFeAkPeaFTKUGC+mABYutN2eYVEaF0lSyTUgm0BiumVo+dHRZ/XeK+IWNdpdfvTEFNRQ//pwBLNlAACCHhbmaGEcnEPiG5U9IhoIcNl3J4RxwQKa7mWEiDAFpRy6uyNpJ8fOMpY55H0Y2BkjPyrrKPtmQeXc2megq4SBIVc4NYWHpYVqaXpBMrFRtohwm341lhh4OEk26jutN2tGbAFp7kBMBzxGkz7IZ5O/QcY1WLmE7D9OFEymKYNkHaXHoKuFmsc4lhYeli+XpLlYqXtEOEz7vGssMPBwkm3Ud1pu1ozYAD8Lq7asCTMU7gxSPGjxNdip4TLvN9lHz9B+SzU0dHZP7EPLh0XrzR1/EZWw9QTHjqg6LNZr/aq2JRrvCopqByLntRDAqQz+BUQWFQ4i5Rm0Va0HNNPzVUel+Clomhfx2kPo/o5jtRsuye3q238z1KmUEFMRDxVXb9di6zDfULMxGciZMQU1FAAAAP/6cgTh4gAIAgg128sJKGA+xszNDCKriNzXeyeYoXEYIC+k9Ag2AAAYCn/HFDVVEAs86ARNY74g/fLG8QoU2RnaFxKjadW767W+rVK1H23/20T80y87B3I8DPqcjWdCp3JWddhL/50SNJ/fuSySSwkQm4eXgxgYkV0H0BDPoIpz5Fy/A88NfqYVv//WRfIU3nYBap7JYGqiF2JSxVe5XklLO+r5XjkbJ6Vw+EqTzMLhPr8hG1H56KfOd2iEtw4ezrI1Q3rnoauq6p+ypdGmdml6aobS6OMIJrl/c+99LLEXtDtVy+8WbTaLAspMsAAdZPTWKYlRerWcfn9BmkGkYV5WrPyWoR7BcnAya5vXUt1R7bVTkY1Gl3v+ngqm//psw60pmhxp7nGRpZ7v8VteVh5TH3piCmooAAAA//pwBBs7AAACIUDeSWYobELoHJ0UIp2IYWFuZ6ShQRIscbQjFVYEApwdaYHQR2Jo2xzx+djnmWFHQ5shnSPZ41O+moc0bMqPV7L3f+3bsfr9LIrvUJBa5nRb3lZrrKIvOmdb2ozf6up/9AbTS0tUdlsuMzsPVVMqYW5gMNxlWDOXaLJyfWAP5vN/k/6+qN/89XZYY6qrrUzMf/hX+tgNSMeT1NIibiJ5XiVDdJAF67bYMVRtAs9eIXHqEsmwf8oZizXHatqvmbHdKAV8zXYiyUilZjU9RXtxC6obKs0lfMDV6ze39H//pVl9X+n9f1OPDJROrhdsskoOop87W6WtBzu4dUjcvWu+huKsQDgagsSxD2I+O5HtbVu3UZY6TtW2q9x/pr06vuMf/fXXT9X/Tp/nH/kkxBTUUP/6cgSCmwAIwhkfWpnpEOA/ZBtDPMJiCLTXdyMIrfEYGuzM8wmICFl22eD82bh9UaAckWBtl4BPVGemd1QR9DPiXEWO0wHu1B3ytQ6YfDjHrElzwYjeSc6pa0NtfadQhn7K9JD6K9Mj9AAjltrAKRlwjPlIq0rgx3qqJ4KrMGdxboEA9F4zkdn1Pu2c+XMd+8ouk0uVqfbSE8zVCwTRqtZVR8PHOfV+3vEJLarMS52mAThnq+vJFwDvqG0M4HDvmNkaY7WAiRLIeIaZbyv2096qUZp7U2c+4mCITHuKJECyjNjXCl7Do5DdPvo+tk0AE7bsYBY1Wz6glltUwT8U6oB3FM5xb8IWgR87vcG56DtcFo2AJvlbk6vlTTp1eisVCtferPd+4nVxTR+9vYFmeSs0fmUUJiCmooAA//pwBPWKAAAB8BzbmYIrRELDK2ox5VIJFH15QxTp8R8a8Shgib4ApOSSNCdLiM6YoMcNYnRpuhDYtkYfp0hIT2zXUN67jFplEKq3XrAzr+BzceROKIbzEKhptvdipVDdX4u77AAACCCm//V1jSPrBqccoWXXRUWxlNsgeZJth1Yk+FD2kG5+LlnZ6yyyQkHpWTPHpN6jYPSx8o5DbVSxL8hVaGpv/khAArKTTScLZ2Dn8xTHsR77jXQRfINZ5WC4wY21yLUJOUL55ZJVpGyKxDLFAkSGG2uZXKt0dOv9BOJXCziE41TQqZiIC28QDlUN+N+2pLbc/3roCGKrfhWRgxynsfCwsmpF+B5c1msunRcE3NRW20XolEClRQoqfGkh55Y4kaDmGqywjENA8kqUG6mZswO3TkgmAP/6cARoDQAAAh1MXrhlE2xCyYt3JCO8iKk3dUCYQXEJn+9oMI5+imEmm07AhsOrhLRUUfo+mr9Oeag5ogtxJ6AKmmdRtRu+nvoPoJ5q/13M5Owq6t5d7/b+3/qSsEYYj7qVkEplVlnx7kAEABTktxakj51ccM2gqaIOWoJd7try/v/+ut0qlyePlnXX9998OpL74XZSm5H6cn/X9dz+QZCYEaw29jlua/CFPI0AAAwCEkk5LTQ4E5Sboat9lHwdApNF5XLBiWxWr0fpog3G/nvEaaIP7af5snMdmjb+/fq9S9f9tyNcQ7gzjwD0XmdFllliBAcghJuO0mo6h0aLHBjZORmcf5Hyn+eT/LKuv764OKfy5zK5AWQ5mrYO9a3qHNCrzkJmjzgm4sYFzl2KeqqecyyZTEFNRQD/+nIE7kMACIIaP9q5hRO0Q4fLejCicohpY3ThhE8xAyctTBeIMgAgBJuS46wJZaOooxljLp8XOxDgxLLxMjrCuIrqNq+f8ZCprovfn6cLrTR/yv/8v/StYA29O91bl51b+zEx9u1db2pAAAkANuW2G8SGqSDXqDKOGMT3cbNskwmGZyPgmJV9U1fTo9SFtPmXvx9E4Pp/6X08V23/wtoRGp8UvJVtWsnXv1/bqGASkU7PAUDoDFYpxBluNnI+Q+JPfHm7KKfd9R+Tpqq0Evh2qj4T3/9P9/6e+GTX//1yoqIKZ2KdafyXQGLpoYInSRIAFtq0KmX4jEXVR2q8rEKuP37JYPxunMMeo+oHrzj9+P06dnWAvg/fQmn9v9v7dP6f//ujudTKQwEZQfVRVFX/oTEFNRTMuOTcAP/6cATuXAAMAhxO25klE/RCScu6BMIEiEkBZGew5REEn+2cxIiiAKbluSuIjCBWZWFsQZK/kgtjKK2ojxtqPza8MalsaKaF5f0AbVXqnTp/9Oj8R/VCw00cnfb6/m2LxqT3beKKafx3ePQAC4EpO7cm7hj5HiJbBR5BLwVgQhaA3w/i+n7E/VOXr/T+n//+fRe39UoGpDN+nStXe5tBkxkplq5aKoM6lvWkKGACEk5IfocgeD8My6G0BZeVjR2Lk6uIWQ9tB50kS5OUO0XUgxQLtcq3I6f5vGup2h///+/fRv/+O3/6Zoex7XEuimgAYACdluPdQYlUaCQRk/6QC/kfuFwID0BPiXSCF6Pq2cuvt3/t15uP/Tr7piqxSOEaiyKpuXhXJ+uiJgGoyv/PziYgpqKZlxybgAD/+nIEdoIAACIiWt9owxQcQ8gLNzzidogpB3dAmECxA5vsjPQJokgQAImSW23MBovAZp6vv289LROlKm/0OchpL+TTu+uRBno//xXJoI6df99H0fBdevvx0ycv/0fBtnbm1/+rpQaLJudjQRgAJOSrhR5XBWl0GUhzDmJfns05duWI1TwmePruIz5wtpm7bf7d/5+bQbQT///T306/0Px404p1vPW3vbb8nISNHQsAACgQk03TXFDXw4bVgryis1ThtQjpZjyK2ubq+uUP0f/76F1FtQiad/52zc6bdfXOfD5GyiQqeLwNTT1B4Vc6mptyN0a5sGqXYNhSILOYLgLpGUTGcvUuZ9FV2DtzkHbEaM0i69ff+/fBLq70GbRd+vP0Jh91stbJU/ENsqktIy/1piCmopmXHJuAAP/6cATX8wAAAiUYYFBFFAxCoxu5JGVliCQTf6CIYAERhG/0IZhYQJC8ltxptMjLQPoyatUPWC5BFIuSde+rQYdhmdss6orEsqPLnE4HLORuyJxb9GUBAEDkPhiouFwfHh90QAgEDgIOnPy6ABUlLw049LFzjn+qwDnQLer2CejczknF2qD1VLgc8Soq/qes4ppMHL7T57XuUBLfxAGAxLjvE7784GBAUDESSHz7iKBCAAACkVKShiCSD2GrigYSoHxACBfggXBAMfxAsCBEPcHwQcT7Kg/Uc/2g5/LzYnD5SUyhztc8EAGUDFqaCjnIoVmgAAFIuVcEQWDG/OcMhZwoGEqB8QBAvwAAwQIdPBBYEOASmD4IOJ0WVB/Of3CcHP4npE4fEEpqOdrnggAygYt0FLkxBTUUAAD/+nIEp9oAAAIWJGNoIRt4REEMBgRgCghcU3YAvMABD4xztBGJjoABykiiUSkoDBA0PHDuQqCXSoAoD+UyQGYduXyhHtyCp6CtATFH2OYIH1nHn3BNwTteSvXfKpOyKf0q4AD6eAzSdb4QIIVQMCQAAxAGe5hRJ4RFQkeArgAFQTNVDxCCRKHVPQLLPloivgrQCoo9rHMKP3PDx0JuCfReuiVS2lP1pVfcbT3U60NsB+wEVIUSa6MW/Sdy2xI/KJEh16tzcpuyJQSrADg8zA8gMJEgKMc1OUcBmoAleazrC6yyqb1v1rte0VY31XUJv9EDajtjbkbSTTpUG5zRuwmqkMuk26rfYslkdRAYJBXB9xwYSQBRncoYwDNQBJe5G1i1iJVL3i+8Vta+xjUPqVbMJ78lsTEFNRQAAP/6cARSWgAA4hMIXilmKLA/IjuwMMMmCQCBeSSMSUETiS5EZIxgCQDsIC4j0eaB6Xl4LOpDJVwIhUVhJpMOjRMg2FUjqxVoaKAdjEMF7osAh7t+NeWRYthJUmvMuVbzHQ6tlDM9blVTNrwC2AdwVNYHVqFv04ceqfMw7G6hQiNe9YcDIGSbFYkCZVYZeqoqMQqg236E3uUlVjF9BqyvQ5lJiwWZ+vbYhAAigJWvekGliq64Q65EL7fF8Nw40qku8m7tXaymcurUdmYZbFnXJhTbFx7uwfGQwhFEqUYeQ55L2FUY0kmoi1lbI+kyskE40L0kADQfLkZ5zouQtxa2XinBrvSd8GZwWAxdTgVDoLIkUGFiiQ25McbYXQ0e5GKj0OcGELciVUx5K8l9HGtT/10lpIJxqYgpqKD/+nIEFoMADgIIGFwITChgQ+IbzQwiZgf8Y2oAvQFBE4juGJMNSAHgmlxCJDb4QWX67AxHTmhlKszEogtJ1eLmUxA1wRKPCqR4wqhaSPI2Kp1bHLSwi5FDdm5Z5xmofSi5DlLkdH0rgABAAJIRbbmEDDigAxY0EkUFDaUvAqeKgBawehkddJEWra6OfaqJAKevvEZxRXKKNTFcVFga9Dsi290oizeq5jSW+lR2o8S/EqZDmJLlmjRW9QhAS0MCTJlFGwfsIUMWVe8ydU8dHvF2i5uw8EUT4iWoyKx0Am1YFMETSk7f9SKf+v+8AQq1/lDSIGgeSRDkh+SqEpVfAvSYCG370wYRJDCEuJzxpxV1666WVJK2iVFRiBTBFCjbusJgoTJU7xFEJHoqXxFz3m9iYgpqKZlxybgAAP/6cASXlgAAAhcK3skBGZxBAqvdDCJ3CGh3eySEa/ERDq7ckYmiwADUWVWrz7CMgp1einPISMEpIqo88eeQ1YBEzyMOhVJA6LKmidbApa43Y8ZDo8BuNblCp9qw2xu9nlbKDdj6q3/f1MAEAUMxRyWfIE4oWEZbJwUWOiE+YYagam+GOTInkls2dNJZsc/VQduvlQFLNET0RuNiVbWPqZ51XRTx4nvLAwsnACFQMq397KYbWgP9+skJLCWmlbqDffIodJVYbkmAIQJYWWSDRDps0lContcXxS5GuR49gusC2Bmlow62mkyiYPsc1MBwU23JPSSawXSo34/ThjVOhvKQ8o2nCr6eqS4zE1GUCqWzRETsYl9UDi4T73T98jWrGEmOSJlMAMk0TXdJVczfbZY5aYgpqKAAAAD/+nIEdXcAAAIYFVrRgjOAQoJrYz0iOAhkW42hiG5xEJrxdDCPHoAAACBbkks/YVgJAGJwNQiZTOon0Nl514pvTica+W7MKQhesasRhZ5xb5vQqD1CVxRwqdoVmnqWHHEuSTq6vjP//5UEpy23HsaKgK9PMISzaakTk58S7yH3Ph6IgJmGBjzMobEgjRA9Ty06RNJKvRWt5tdpo8PiFcPJYihydnX9fXS+2r6cwUmo465K5MYqMsqzG1UyQ8L3Q6spvmnD7DoWiOrfDcJOSfI5Q0BzA+OFCrXk5X9iH8u+9A2jTqXd4BcggZPFxIcpXUCkk61HHHJW7SH1T7qISH4bcy8s+ETEQ5yaymUt3hFkGU+G5eX6DV/IzPsvBMk/U1D6SVgkcoJhLW5r+p17RDbxa1BFMQU1FAAAAP/6cATPZgAAwgwp4FBhG/xDpquHPMJECFzVaGeEVwEIGu2MF4go0BFsJNNNynmAhQtG3I52tl2OJkhKaN5LOZD3yCuL3lRHl4VSpR+xzrlgT6nHCTaKSSUjWNgI2tneTj4HqrlQ9IkAAhqa//57W8JyRUszR7zOLRNrCtwS3dTDUa+CJd61vvg20etLkcxmKhUOXX17ovtoR6hjwnMQN323OdLG+jKcv2X9FIITdtuaWM4TUFaQM6ilhI5zjR3pxO5v1L/a7XeC4+zFP66mbwDlAMSosl2sewalZOz5Zf96PeMfAiKbDNFf/69n9W0Ey3bc8E/hHVtfYFT7P8otgMONIAZjgliaGXIV9OhtMicbm69HdSI7gjpo8hVeif5b8BVAZLKTe+d7P+y0ZVLak+tMQU1FMy45NwD/+nIEBb0ADIIiFNkbD0nAQ8KbVz2GJggQSWhntSYBBQqszPYUoAAUpbcOafxqAKhGEwmHMyGsjpnI5xi+Q9PfP2L03cz5nUpNV+u4sVJOGyL0UMS91MSnbvygcU5wx3utewZ/qU1L/Rs6ACAhy7/11zOD9RidCVYiWyKSWTifoHv4dkm3S6y6u9znupXGzbyq3Unh+8g1LlfBg00BWDru8W5xp4nnngJjLL1in+8Apy/+TVDSJ4G4J4aQr0Jo8WEAXp3sL/pvk1bVUuY69PkppmKoOw7knK3TSW/asDKbaQ8NTQvS2xf0bbPopu6+gANS7bEyILIXUIg5PgzsT3iJgGkjtKAbGj0DVO7QIrFvibzR4PpPIDr0CLO7s89ULJFp9drWk275zYOvei/93v+lMQU1FMy45NwAAP/6cAQvPAAAAhwc1gMPMmBA5lsjYYUoCHD9cSCwobD8By2cF5QqrX5IW8FNGz5JxrAvuyU6NTQyy+hQDQQmEpx7b0VKoPdHs7O4yNrqpdUu4TTgukblSOKK1/bMgF3OLoNihJpaJKm/oqABcu21x8GwR8mi6foKUdtGLBPGMv0agRFINPVBsKIqOoj2LjEf5H177vV678dbq6N+2wogtSdRzdjCtHX/Z/x1UAAIKTNd4bjRUlFkfty3c83kdRuo668oThwHZXifJe/bcWNS+16Wl6bKtiNVjto/xe8roy0f/K+MsIkgp2r1pSjp9NEgQQU3LScJqXiJpGhSpRq52KoN6vqOGoNQ8mQBYeYkIK6j/2MbIwdnNm1RqE7fnbHM6eE0petyCKDIFSit6NCYgpqKZlxybgAAAAD/+nIEi+MAAAIRM1q55iokQwYb+gwia4ihZ4FBhFH5F6vv6DCLFoCAhtuSav1IXQW5jLijsqQRoMCaOqJNirPFas8KOIu9bU6d8zFaLGeskaxE2c5Homr/714jkWpZ4jhyyqK1fV9Py+kozBJuKTQXwESG8XMEQG8HRnU4pYnvWgZ/0bd8/WjpuvCI06MZEb4V66n/lgupwwYBwEMPmuecM3l9+s4ontUFxTToS1RKjjl+eqCwdX2OuDv6+7h6iV5f11mFYTzZSqXM8jHjoXJad1Tj7Pf+1O7NT30/UvXlz3/VNTKUEIOwUymHZjPdMrPpivRbbjk1OpBdc84bEpJzLal97UKZs3BjjDcY4keawgzPBJkWb49le6cnV8KTMPq//zM10372p219dV6/19LxrRuYomnNTEFNRf/6cASLBQAAAfszWjnpEbRApntTMGV2iQ0RavTBABEhrC6qjFAGgAACk5bnF1c4CvkbT5zVXH3FhET+htTHRBcKwHKxDadfbTr0/os9tehP/+3CxIaK/KutX7vi74042XdEt/0dYITbluzCmiF768XbGnjhQHvZ8D9DY9jehyik5ox9bZtNxq4xboaqPRGSFpycntn5eI6YT+7eS6PpdqUiGq6ZX+oAgRbcts9KlQIR2H6+131SZ0q25yFqrllK6lCbBgTMHJQE1C6Wq4pJBfTTod3KJ3XW3/o7oJFQQQ69/3ZJ9zddRs/J+OuRo10IAhCCUknK+cxwrT9dp0/NpbQg3V6uco6hDE0bufbRt07/RH68r6+//VNRg+glXft3Si9+tbd++6rVLojtQUJkmOiTqdYq39mhMQT/+nIEnpYAAAImTlyOMKAARKnLgMYUAAhVWYZ4E4ARCyswzwJwAgCScK1hCq+bMhRCgba8DyelaqqIy3JORsvJWn35zuKL/+4oRn//8OAAKC6E///A4fPWomL///+RTsouBCTn/+gH4PggU46wVcS3yyJBmeXMXDhouxDSh9TuRhS7lQ9tn2T39WY4u4ov/7ihGf//xAOCguhP//w+Lv1Of///yXZTgQk5//oD8PhhAAICAgEAgHObgpr//////+nsjolP05KJzTnI/+/GweSJicH46KQd///kxuQUbggNRKCymf//+ooHCZ4OFFAqEkkYKQAQEBAIBAJ1M6goyXpT//////ZHRP9OSic05yP/vxsMnExOD8dFIO///3G5ZSYYGolCpTP///UUDhM8HCjgqEkkYKUxBTUUAP/6cAQeUAAAAh4K3mcMQABCgmup4YgACJDZcyMEU0ETGy/0UIo0oABArTKqtGge0CK8hxzh0BuDRBa1rsjl12UMTNhQek4BmpZEoTHC2t8xBQVF4tQVagsmuyVnRQWQGW7MDG8w2ZZ2ziIAAIpAAYcB0BAkB7MqDmx0D89q5Surg2YTYNn1br4GItNJCsdWCpMVZk6HFQgL1suG732qzq1CrCg/VpDr6BtA7roQEDwtmb+tOAiaksU+ltE73XrPByiyKcwZrPIyFCmYVyFBgFRkaz/oKW6L6q30h5GvVyT+qTK+8ZpWCrAq5pJ4bRimpC4QBFPUZI00peViOzzEeZxxqAI+Rr5c0p/l6TMK4BQOAVZrP+gpbp9VbTRA6EXrc0UZiR+YVBYS+8ZpWJWBVzSTzqMU1ITEFND/+nAEXWEAAMISQFmphxOAQigLEDBidgiVA3uhhFYhFi7siPGJoIANff8B0mEoUrS00zqhQ62g9hJSMQZAtIGzOrNZkono6+301ImarG9/V9X+rZ/UV/Lo7I/sGHVPrW5Z2K9WV9TParY3A5AwCQ8jqEVaWNgUO3T8/4P7iAQ0XBgrwd0aR9CtSjxt2/dFJeat/01eiuvq2emor/7sj/DDvr2vFVasC+pntVkAkHHXJ9rr7KYwdaWORNsnwX8WDZ7KWGOnl2mL68K2+gXVWtVqyV9S982q9+FFv+TlPqssBzp7DQ5RpU99XeKN9gaSAAGq/OIfNol5QsM1IlXhRNwpAnC6GEo9Y6EBFHCtgm3tP1bX219rV1ZqreUnmpt/hX/r/Xy/t6rd0lpmzf9+3onXrwaEqrTEFNRQ//pyBBSKAAACGEBbOMEsMENoDC0MIseIWNdsZJxPQQ4bLEjDCdiAgMsu0r44BDA9pjHqnA8l47K8nXIHz60jqHMwx++xe6KzVTRW1TmDuuatdz+IhTS1SnK75auMSWsfSWa6h37OHfsqaITLkijkbSmVqvdN4WSkJ8V1BbGUOo5eZeGE+SBBy5dcGE0iIkQVAE61mX/ORFUKTKnC0/7gv2o9b/syxF9FQdGCXWHLJdt8w2QkSKQ7Rljw18o5Kin82hj1T3alDkCUtYikwzbmzsqkeRLzrm6HvV/70TxNkbvcTvQpGqVcxaG/H8E3/ZYBXf9EyjKo8B2G6BZ4vE/EQusj/G/yOdtzWIA9wcaXdnt25XoU700H3S6t1ZpltgV2SbVvTxrN0nCtP8Y3aVfrpZagmmIKaigAAAD/+nAExxIAAAIUXd9oYRX+Q0OrjSSiZAh813MjBHLxCA6ryPMJ4KQAUWmWmmkog2tkg27bQiPe8p5dOJeTz5FZPvIfh29Pt54KhRLPiYXjeyXkaDyCeWvn/Jc6PL+f2VejnShvwbniL9oAAAbIk13/6TvGI1A+6o2aXPAnQSxMNTV0iuxaqN+9rvRxSRWRmHXkY2UacAOxwPiw4kSNMsEmxyvxsy1TP097PvWuARKSalZtY82eBZaWLlrve+9i6uXQpIHFsXeYuiz/b5mbXpj8sNlpcYNv1H/Q/klsFDND8asXJ3s1rde8y3Ja0Z/v2UADVUkexO0JSYSQfxNLIXhkClby5ucHnE/wMk0sfpRY7AN/OPKL/eZGmbHbdmkPlHBdRYMPI3BGnfs06/6/QW/R/0JiCmooAAAA//pyBDedAAwCGCvYmeYS4ENmyzcF4gwIcSNmZhROQRUOb6gRiC4MJyOTG4TKiSat58Ening5jikRSceVfl5PkP3Nzu6cZ11lRqK4jUR59eXt5HuvYG54EpSBzta6jK7un6+16W/8Qu+RAIC5LbbcJ6jS2kaXDReS71E4YMWw2DGnvc5anOVq6o5lPyNjdVLV8juy2oxZNbIpHvpqrvWiiLb2VSree6KpGT/6g0CZZttP1HlEMJeHaiVT/6lqisTcMXS4Uat+rb6iMh3qVsImi7r3S5Dey2ZP/qz0glXsx+7tT2f1TfkjRRjA+x/2da/TIMU6Zjbbt4MiDIuSHTDs2ftHUvAXVzM1R7l7ddQYdg0KNXUaXXijLNyyLACNwSFUtRnXTWtIxqmPa97aKdI1151AZUyLpiCmooD/+nAERjgAAAH/TVzIoRbMQGObKg3iHAigr1pHpK0BEJuwKCCPjoABgKqq1dVICJNHndZjNh31270RjtYixlkBUxA0zeI9+eTzB8kfPvIyYhzBHo9/t8ysllU25VXmZYLdc92oh9QAAYACW7bWGIJIYDESRayzMbNBVTBa/u6nswcdV5nUU3R8l79uFZyOZOrauQa658PEw1LqOrI33Pb+jz1elH1GQG/zNUmTEmCUMxDAftUIQk6D8ZSUkR/UWpnIB+aSPefEu4sIi43jUFEBjZmqgvydu7o59U3d3iKIw6LESh6eUQ6u3/f6vBh/TVkkmVntVnkKyV6iuxXvsTc3R9f5tBz8RnoS3eEHfh/JnErI32BswTg+HrnOEonmL7mRoYwqDO+kVieQdDSUqd3JiCmopmXHJuAA//pyBGuPAAACIk5eUEUeXD/CqwM9AnYIlKVm4yRE0Rsnb3RQix6UVdg0225bRYJZBFXGe99vXO6OnUThH2tN35w3r36dM9pLvGJv+iNdme4/n21ZZvumZuftbeAn7iCKByiE8ehL3dx3c8ABu3DzVMxKkDCnaN0a1MjSqhKKZ3yFaxh7mZt6lVeK8Fi+k5J19VTmuFR6os/SNJvcdGH05OyyinZcKuJMFZWAABLbktQZIMLEYgAux5QnKsKfvtB1I6HOyqV9PIymbZ9RHT6fbKg1QQ5LiDW4WaeW6yzFMOyF3XVS4YaU/ZvIt+LXPOREEEkotJtyUSHMjsNcl7yspN+9qUddCi7wkO14+T4OX6ebVVgHxKv8WjMlLLVunKpNmOCV6NzPurTM6px5nIONDOsNU3/9SYgpqKD/+nAEppcAAEIYHVYQL0BgPKgLdyAimojdQXlBiK+xHKhuHFMUTgAfwhFEQcF8og22YPSlGIb5pF8Ot8yOrgAvoMXQ+0DsHXks4+sZf3D4eP6xygvKy0Xi9bij11ecSdU+a7LJyz+uuqqAYJLkt0XbBsOiROOzIZEkpfN+SoRPa5vr8Hvnw8unnz5BffT/Jy9+b+jujsjhJpivRWRWpT1e6PIst7NGADVotJuTQ4o7K4+uMcV3wLSrOJzhJHMv9KFHodtRXfr0/n4V06f7JY2u6Cv+z1KkfzPSyV0nkY3FkdBXivOcg0y+SOR8DuBAJKSdvCw6aA5kCe7WH4tqXlq5Nb9ted9W3H9+vN/XjH06N+gYhEF9SOhBWiF1fbzXv/+sjJ3ZEFdhW590yIHKMucAQRi8kmIKaigA//pyBJNEAACCGTXYGekS0ELFKvM9ImgIoHV/oYRK8RYJrJz0jNoAOXf/e+LqRSFoYTuBg87EpCSvaa8SH1SsPhr9MfyqXV6vroZ9dgfrqbXofpqUuqJ6eraHwrzUrwgoQ2076MDc25H0gBGXbXnijDNYgY0C5PanFZpHu0CYEy3iIc8DN7iPb4X9wRdW1TJ1bCvqjV8v9OFirdVJ02wrLs9T1H1+znoNWzV30QkEkpNNx2S9zoMezEcjJpLLvUuo14l3q+Cp8vCLEU7bZORYqPIEXH1AnUxMLH0212W7ukCEkBNLUrQ6/DTABSeKpUSKUFBJTktzjJQ9wHc1ElhC3lVYrDpyHuPjUaVQh8ElOw4xsxORWLQtXCEk9djkZ5p9jw9JsSgVsn/4EFkFBG0Pj59S9T0M/QmIKaD/+nAEnkcAAAIjNtedPKAARKUrqqGUAYhtgXhYY4AJCybuxxJwAAAnNsM5RYnLcbgnysEZgHDhbZ1BaRlw7iKEOQExgK4iJuLZn21NzaFH99+ja97qZdq1fbmniJ1U1YqaUkQyyyxjX/XxsAi8kpuJy0sOICBkWXFh2ycklVJazWJr6dke7ZsYJv036NnK8XWcJC44Jz4CDUOqdPPOzms1DhwZ/JLDxd5Q6hFs5RIMBCCCCCcE+6cfVoBAx2t3d+NCH7H//U//zXMf/+eyECZ3/+NBsAwsQHBI///JEK6n////e5ijcWHoXM/////kPmI0gACSTFBU6QKybZEhj1LA+G+oOC3WphNP9jFJ/+rmGG//k2Qwmn/+NB4HhYwwaf//lzJ+p////3uYpMgFgJ//ykpD4YcmIKaA//pyBIKRAACCJCDeLzBAAERkG8XmCAAIkCGHpJTAIQmObhWAjegAfp8KwNmA7JSSYsvQ0Mo2Taq5DP2uYU+9S6O/1wT/vVtRRi5r60NIg2MnYq5C6clOlYlFkdvSxrkotpfGEciDPiz5KOAF6/CsDZgTkpJMXXmWDqOQkaXMY/apjd0cujmm66P6L245i5r60NYDYydirkLpyU6VgqLIYiS1kWNpRb4wjkQZ8WfRHAN2Zf/bVtJ2YDgvKmHmbZIiMSWAIZDGfS0gtz2WnkQNNoMEnFJni40PGW3CzkjrGttW52r6yIvInUhPom45Sxx4kLbDo2DdsExWbhUo/lnN1c+Vrqx8dgupXOdvNYf+5LBzyaHGJxBrWSE4BAtA0gg2xSV1yRXWz4sPOqAsCt0uS9LGJFK+VTEFNRT/+nAEJU0AAAIeNd7R4yhYQQbLUGHiDAg0T5GijEexEprxNICWzgAI+UbTJLU5aCRTAlszLoYB962N6UR6IdmaZ1r8z2z9DMNa/XZfbuUzlIqPzqy/iKn+x+QvXiwXOiLXZfmy2o9frsiJYMXAaCFJ5ib2l3daeSsFgdC1GVmnRjmk7ldFb69F3fQdehmB79dl9vUzyLfnsv4Wn1J1O21kRKpm1VPeexF9qqgyq4tY7LGm7Bzi1bjOxxA4FkeiUfhkhL9XHFDwOt2Sz7REoPTwkcR5KQ1nVYi/GHqSWnTvrEXK2d7oaflgdmzwlYGykzK25G0lGCMeJ4bZ/dikBtXaJqdUdRS7jH1nf56IUnDTcuah+lWzP+rVMX6lalZUBn/GPyX03vrEX2fhp+oeoOnkEExBTUUzLjk3//pyBN53AADCCDZZEyYToEAoC1NgRXQJJWOToxRN8RIbLQ2SifABGqMSIHnRiPPunK90ufm7SS3VPeIoYIf+YS961dQoMUqQF47ON2Bj+7dgd7fv+++T2b/XrfQXotpoq9/ur/+j6gnG3JAZIM9ORWxQUUU5dooxrtkRDo8bi9qs0ZGqXedNJ3u2jRMGXtTreXVN/yt7f6/H0+n+SkY6rqNyUf9X0fYGm3VrHLbZbitbdMqM+XiIoHpUTQaK/ZelN38aCenyvrP1FNooMzq36L/WtPzCuvXo7rojB23Vm703VFurnNf82rG98o3fWGrKwDJJJAvIiNG4vLexidmvnH/zmag4VKreKuwLqqjhuhHtk87W7YWRaFyi9vTUtV70h3ofbf1XUVQyziSA3b8ij1v9Xo+tMQU1FAD/+nAEut8AAIIBNdsZ4xOgPuKbYzxidwj013NEjE9hFRrupGSVFig5bbULCYblY7P5ggT4yfmmxrHZIhL6dGPzoyVLXRU7qv27mbypqp9HPRVIq6OVqLo3TteNG1VP7v9W3/xb9/UECWm4h4eI6sipHptzhVTm81BhunfG0En5roZxC1wNKeiSdo1i1SbQWEw5ZO0UqknjKYu/hp2jacvuI9irkxYCAOY25JLlQLgZeH+s33iPzgAFIfrHxD4mTJGRIRk98qIp/P8G3u2qa3jaJV7Kr+3LT7oNepTLmU/rc9pNSH1ENKHUMv1IkEByhMtmhcFyRTOv6X1PgRrVmIJAnTNIgixm0+3+oo/qzdfs31fZ95qvRG0XitS7dpJNH2tEKVA6KElFXiAgFi9BFXSBZJqYgpqKAAAA//pyBP/zAAACHjXeSQMTvD7jm+oYYnWIzFNk54huQSAa8KhQieYEBOZqqunlDjX7nEDWwJScletn5XqGoWNxu9U9fVtJfbRaCfRdHBe/7lYqmoDCaklolIhsUKHL+XWyyQJts102P31IoAIj5NtNJzKyvGqqdajaCs570Mndb3hVlqpPKjpYTeIRY+ucnU6qluaqjD6xVerYz05OPWsCybcXu72Lfh1MkAEBJO207lKp0JVwnDWwt2WgWfSQegrEiWmSIuxMBDLJYb0qPQfYk/kscq5i3rM1DNzKQsW1OK6HPJKYja6PbP2OZv+LP+hWyf25JJJUYpYwYNFlqwyAvhgEM7NnAQmYdLyAnZ38Zf75iyNYVVW2VJjF29bC0pRQWkvFGKjGhwtU4XIrctbEHE2XpxM/9mhMQU3/+nAEl9AAAEIaSFsZiRKUQKVraiXiRQi8k3VDLEpw/iNuGJCJ7gU1JJGzHXrIJrebNjRiNQZxnehOwP4Lnt3slKHfIz0qQpi7bStrRHZQS3SVUSxmrS6mlr00R/XTdv+kQpiFDvx3/uvAACApV225eaEJavE++UqSreESK9mgkm7R21I0jopKOjfbsY30fTtl7UfBLNF9wQErHOst6Gghq3HUdyzf7+6n1AKBISWkk5zzVcSjpr0J0XibhGSYR3Bvq1iI6HfVhv+10lnZmODa40ILDCGUM1IJJD5WP9PRpschwGxGASZG1iLIuxSNqKYlpmuLGGNOpunbyvyYX5hok3pk8kdF+czai9Az+z/y6atdkaTJr3erU5bUv+czU7/R9KAmMRcYW2Scgx1r+7qTEFNRTMuOTcAA//pwBLvBAABCIQ/i6GsanD8my0Mx4lMIgD9kZL0IkRMQrNzyjeqGNhuxtzW27wbNHnPmoiMGGRwLYI/Sj8G6NQrnIsLXDjpV4OIDYjcx4u+rgFWsS3Ui6oo/9rWkhGWSKsa9kRbTOe9hNIKJklDRycB+8YNiT1WDqyYwPBuVt1qKyCCUqZ1u35R/a+yf+2fl1aZ91p+ruhC2PUePKkEwrxS9X4jizh+NxUAFNyTWi4UMo0RsmWlIDt8dwkq4Uc8WQZc0+CUCIXbJUpZTKEM/KSVxWZNgBQVA7BSOLME/oltS0zQpSOXQhTfv6LNaggS03JMpBRIZYgTQZcTbKnfO4hPPE3dIWBnRRN6TLstCd0Qns81CklnAQz61JnY808OVUpsW5VGW44e9KkT7Iiu3/6/sTEFNRQAAAP/6cgTADAAAAhQ/2rHhHdxCiAwaFCLliJSlhUGkqnEbiGzYh4w2AKAi1+fDHNJikXbs9tV4orJbX3qTHzSEiFq+vd585HwWXfEZeQC55dZaeL9/bzsL0/+SUriQ0ZWDJVzWnVbnN9VelA3e23JJb1LWdnu5jiJQL61NzHrViSNs3snf3Ev0ab5zFkhBHsn/k7bzCef/s73gIRFs6sap1SwcU1fJWqDT++pdKp3O/kkll3jRhLZFe4kVDEa1oZtglK4ixEMNcidkY2qJ0V9+1CszvXlxB5HXhVtiguxutVw2WaWc+KaRhsyMjnKoqUPfWAQBFbcLu08/D5ss1ljRPuIGNEY7qYMNPEhTtGAJ86KuKJDiyFLLwG4q6KxWZq+Bx5acEgfB61qXd66ZaigGwospRYp5aKl0xBTQ//pwBLZLAAACHRjeUGkSLD4Ca7oY41eIvJ9s5LxMMRwm7qhliU8FRZGm03JoUGILacxLbSGVGTwVLdGQ1RJKURn1Fg62KHMxeu0bTZdtqW4RHD6kbrsPtGzxRhB7WSK6qO5EWSx5tK7MYgIAySaacp+aE7iU5OXWBM8vpKv16aKWJJgijscPknLHVehxJjx0q84l6TKLNl2h1f6qDAoo+wAMQ6hE4v9MFCUiinFGChrBAmba7YzJlGOqEq2MForZjjOQRLYqqHTxDXjPe6PrJ3BUTYZohqP3F3ZOLe/Ihsheui+o4kJa30JvDRY6oAACiU1E6JW4WwFdx8Q22GD2awpxtLuVZcuf3lfZu5tC3TyU1bGocucyHZDvYk6s9dU3/5/s7Ubda6NvnHY07A8aFmM6PhC0xBTUUP/6cgT59wAAwfMd15sGG6A/pPsjMeVgiT0ph7RigDkojqwOnoACAKLm4jsbf9BSiXM9sv3Qy5mVh8Jkm5XWtjGYBAPHOQUNd5ArrnX/XefFbLw1nh1XcXktf3Ms18I3bb1fs7EgkpSUUQhSEFVRJfUNLx3wuHwOd0wrNCwVr2q7oy0q3q6e3ZObtnztVqj4e1+hxEcsatMmw4l3vU5HseasM1WviDRakbkttv5pbz3ic+TaZgXeHibjAd8qihDtEtcj72tnXVhnXJ1yjmYrnuxW1ff66lWj+pbeTNq36ftrqcqCyAJ0ZNHFbtQUt84BJbkimepEOOdrVDGy7VRiR36gCUl7Pvc1xoQnvcvpf3XPz/dtH7zxqMcVr1uRDc6VMJu6wGp5U63a5DxZDCjzCzpVr3Lsrbq6aExB//pwBL6ZAAACGU1erhigAELJq7DElAAIuCOVvGEAMRiN83eGMAYAAEkklNOWLdOKOCxpYcvmopJGvXSwuT/IjH/9WJb+/jDIdif9LYuJAOUpyf//iKOjOcj///+89w+BxL//1HFjyofL28ujmNvUYTFYlUHi5MqZSihYt+KC5P9kY/26qxF/z+MMh2J/uS2LiQDqU7f9v8RR0Zzkf///a8+HwOL//9TFjyofLgEtFyySNIkrsrZzHytec4TKxk8FAVdYoOhQOgYRQdLhIUtSIRZ7WPp3Bo2LIrO3a5IseF1B0iiqS9yHTbPra60Yt2dZO1gFRt2y2xtJPhGpRSbVSIKpUpzz6X/D4zHsxyN7ruFgpwZFntY+ncGjYsQrO3VD4lLHhKoOkTEtJaLkOUbZ9bXWjFuzrJ2tMP/6cgSi5AAAAh1MZehiKjxEYdwfKGILCKDZh6MMRuEGmy2ZBggwCbjb11kjRSeCNq6xTpSPo4GytrCZyPq/TLQtUydCfRT5S2FzPNd76UZ5v6f7U+nuqbSo9v+iSPSXGBTuh9TqTDWiv0AACQoLNHtbRSkETNYcU8zBUNdHDtK0oJzYGFzyFFVHVQgjy8AoB5542meIPPdl1CtWPYgqRR6oZUuImeXeQzQ1am1dyQTcktP/tc5aLAeThpaAr7xUptC7czO1FDGorumnTM/+UrkuZNpW9eqGcqkKWuqU/H/U3VyeI48k8Ang7JJi4CKugrT26gAmIioFcAHFoInpwtcd91FKRx+JVg3Rm0Z14JO7k0b5n/ylMS5vq3/o5bFL6pT8f9TdXJqkVrxp4O6awEjBWntXUmIKaigA//pwBNg5AACCEhJZCyww0ELmq2Zhgg0IwNd/Jhig8ROgLlzzCDQDxn0B3IdGnqwEE5OfYrJS9BFgMYggeYTMRT9SYOrHw/pFQLBVrBFQqqpLyolAfqeAg1krUjfOyWxP9BWkqN9X//rAIlqrDtw+wWH8emOfka7mlJ7/KqOjPqC+BtR7LxJX4bt2m6Gdqqqtb89HsZm9S2XsDQzOyWKpr17SrUlRrur9vp6lgUh13VVhKNzs4pBOHJjYPWYKNO+wU8roAhvHkyBYgjajqaN9tREnlTLt5+teuiVwmPENysdew5ntUeGyCQ4a9S+IH+11VQ25rXJQNULckgsmXVeCOi+Ff6SO+wLZVEvCpOmzJ59MzImz5QfexcZ2XmFdPsdKXwf9V9+9IRLy3jhe4DNd1WvwzbXImUxBTf/6cgRhsgAAAhk2XenmKGhD6wxaDCJbiKCNYkegToEOmy7oYIlEBBAAqFLsu1hMDKNScHGGZ53m2AJSRFWL901Ogknt0gv1pmHbocuuyajv91bpTrrbiIJJVnjhGuAhlrsgoVZEh3Z7tIv1ft22uShBRH12OK0MPnj7LWD5+jhU5dGVFStC6Eu5EKRbc5VMAZLXZ3/So36l9tn2Am+/0er/VlBN06++nIPpra7oAH/wbQCapxSNBDYaMlwjjF2kFEJKyFK/Wo9EoKBNl7h2YrIvODROcjdvlXgqkaY4kHS2tepR1NJKRG6/2pqYdd+/UJvpCoBdx67X8BBMjNMS99cyYSfJn4y7NdrNEdaJz9lvpDO+siNR8mr6WSHo/+qsglooTDIXJyVbvyBE2KnVOfsIl9Qms1JiCmoo//pwBMvEAAACFSZeSMIcXEKmy/0MItWImNlxRISuoQ0HLMzHiYQCBOWaqssBOewjpa/S51B+nNFc69UrS+5kKmaN9fBd5v1ox2DqF71EJO5pAmRGH02BtZVZNgN0HW7SSr0mu+mOz9lgAJKDUSTbacoFvgocrD0zemWxUX9rEn2/8B/nBM/v3vcK4iubGvva3XkJ9wjsYXARpFqE7/1lh7BxYVR0wIrP2dL6QAAwjduluKuIUWmj2vtwl9qiguJlNAPiaI7DTspWQHtK9e2iU7StYrnZWqZ/41jtyfolF0FvwkbdQ9Cu1FUeOR/rNaMUADLbcHEqk8LmQmKC7eGwQVUQjoXaGIwNiu+OHiKwmYUlzn0OIzZSNwP3TQux+8UHMADDyp90jSJiGzrHoW0CM+1PQxMQU1FAAP/6cgR8+QAAAgBMXDGBE/xDSxwNDCJJiNTJgUEEerESry4McIq+GADMtIVnVuhfV3cqWpzDlDUZxXokXB1XdHkut7ivC6uvsdIsGy0Uvejb0VfzLWzSeqP9f3kkqrMDNkfj0yxj6gCSAIkkrI3LPUw6JZcrp73sD+n357L17IeyHQqGeY1EcLZP7tKyP60bNsJ/k8iV6r9PqqUR2qxSutG/qnTxI1XZbMLQYippxyOZ0j2HpcTcoq2m754y7CxFd+0ZXZH1xrQ0J4sslz8QS6b8UELHlTkobeRGX1fqKrIIB9aOphBEYYIvIpSMeoe8QElJJKHHFwmcakiKRkWTywQ+mj8q/jU3DkiP3nRrmK9JFIIe5POVf6Vme5DkQgIvy6zy91n619en0o3W5Ud2XZmcIfuUtMQU1FAA//pwBCo+AAiCCBTZ0ekToEMjqzM8YnUINEdiZ5muQREZbhyEiKYAAIAA7v/5W5IOFBlR494a8WvX45NIDjXk5Ue34oRno87dRnJIE7Lytj4BkQIflCkjQUlljord17PkfTreSs/ijiinJJasGSG2LpHZ40NmPPonlVAQhsOUQf4FHvBmnZrYG7307sDcpCYu2I6IoODyGk5sshqXgWKbNXe/ZTtFgyrnVcgAFJ9xO5mEXvn+wu76IaQzLI1EbXljXB+BhQfJNFU8rnpEg1AZTReOrVAYPEoNUYpZqKKYwDgNCDv2M9Fr5JQTEjyEJJJKNJQE2KIX3bjG1oPqEtwdGfW+SEWn6lS+Up7uydHrrchd3u3T6afQGjZQaKAAaGxNGWJ00N5iGyH7LByTCuHNiUxBTUUzLjk3AP/6cgR05QAAAgghWZnpE6RDIduqJMIriJE1cuMEVXEYJrB0EwiWACTkkjzpBCOndx7SKM49npcbzdD+mNXWf8kB795dHR/Z/9pTNRTuCcfdI1zqz1uQJHVpDg3OPJpfS7dk79Bf+ioBQJkgUm3ZZhJxsFQjunysBGHzoJoJuCBAcYAQ0kBnCpj3exZxjH+U+KCsLAVUZfNi9bcq9YvPpwA1P+xJhA4cJnU0UDIKLbcPTotmre6QxgIYEwH/Kw2M/4kfmZM+L0KLNbqpwahP7+FIYavP5jEdzOxWvZ0VoPYicird6M2tlp99h3int5FCATZKUbdllvYY4R0trEKMebMydyBGZlr8ybss2rZ4ONs8zKoV6/0KVVRBkmr0c3Rd//VKk10F3rTt6+5piC1HlfrBpD3/UmIKaigA//pwBDAHAAwCBQ9aGYIzlEJJrAoYIn+IxTloZLRLERWnLczDFKYEJyW1y8IMPgOunTQj8+UBm2dWOMDZfcwOmbuAtlqpUs6OU5LECRo1CVBpXCCC134CLvFXuoTap7IlURWdVV6whBc1bakst+V45gCH33L/xG5zRPjCrKF6Mjw3PPhyXyyLSrdFYfVrluv+tv121uh2pp/56XvqUTsLAJ30UQpj1jhf2EhKW0EC4yC9B5RXVFw4sO48R3KF55Wx131j4fuqpmLOCbd6fmr7LRpE+X8tpar7UmRrG5UQjrMdzsVxrJ39DdqboumjYSkkJNJyF3ivgLIDnmiuLQBnXqwz1VosWPelZN3ybcfOjatRrNRc77PUUIpWqrp0dOiaN3+uzaPR0Xp29TO1HsFvGP/7KUUpiCmooP/6cgTTTAAAgfIdWRnpE6RC6ZuaGCWdiRxje0GIyfEbGu3cZIhqICTkgbHMoz0mW1muO5I7jJdikrhT/UOnXVvUEVW6yn9F1dp/xKgz6DQ+9cnoJLDvtKtXZ60tU0o9vbqqU8VAACokJOOAt7AuDhkI6jfPLdBubzj+7BQ3N9083v9T+59lyuTKNZ+/srHHsoszUZ0MclFaJrRsj/fr6zUooULqqg92QUBUi27Jc4a3BgyGtrGHisFUZs5GW0lO3XGcdDanNgV7tsOrgBpQ6cM1FCAWYAlJllRYDv0JahGkqBRYBB95pLrhW4BDHBgPaiIJy7bzHE2DqTNUul0ukkOvugtt9R0iE/1V1H1bavrtm/Ca5m16JTpMUhlNj3xx06DjzxRBSlBRfeo6TPoU7FcsMOBg+kYpMQU0//pwBLQ0AAZCERfZAewZ4EPiG2IkI3AIBMt8owhrgP6IMDQwjchdx5R1dKNPUzER8DQnmXj58r8qeqN/CkpjCA2Bb1YDNLDEA8AxUDhF44Ng4TCJpqG+UFjB/Tb/Of40giln/4s5qgHI1/qJ8EpwJjadmi/WcucbzZC0plCcIDDgHxzQILOFTbqiYOJCJoXQ31Cxg/pk7kl5Ccjb/BMgQpIej59QWcTFgGp37oFzrIYBECCG5EJEpQQpiEfEigEFEOtrc8wpQiXivmR+daPwrHEFL6mlpk8RTvnlv4jgIYiaIIV1f263n/X9NvRAAAQAASpvBBBAgRHKaayEokvUdQVDLTmfEQKDVBu6tTSDQcU2BBc0Fgi/dKfS53qZ+vky//jwsNHuNDgw99xFMQU1FMy45NwAAAAAAP/6cgQvhAAGwh4a30mBGhBBw2vAPMMuCABldgeYYoEbh+8E8wxIAgAAAEW2S4CaZ6t3X7piyPYpEQ3YBNMsUxLnW1LXh+OnxIOPAUAJ5h1wCAqHP5t6GKwGuhuj4vrazxppIoZMX8NvO781Jgi2xyJolheN0L2ei0/iIqzkbQJIzEu1hCRtcIK4PvzqwyDSQ0GDa3BxFCA6ddTzcYkVmD+N7tlbcXV6Cd+7/phxI4LYKTWXBpm6UJtCabgRmNrYf96YElIkYwsFx9yhgxouBSQmTNXw9atDmmqmFWqeFbC3p1UOfNLzeTbp2I6w/zQ3hLFjqSaOw+dI2JoRn5FQpYitZYVcEBKIbVSBAasPgEaHDYGDCWAu4VJPMbkqnZEsTHT3eP2TBdyQ02l9YHZv0uftZQmIKaigAAAA//pwBL1QAADyFgDgYAMICEGii6AwwxgIXCVzIYUoQQaQbgAmDBgAFultCQAQMhFg8OjxIYAw9xFAiVtFg8sSIAwulwKjEgBMiS3o9J5GliEirRVy0ElSS8yldneEnYrMRtarowo8Y7anHIAZiQ0puBLei9iCj9dGNEOCv4/cWxBwMgyHwKaMMNBB6gZOS1zGIUBluZ3DmqRat1bhaae8mpPQztQpG34ZQQ1gBJAACDaBaAu1EaOgifWzqUBUTiYdY9LlpBwaHAmmKLBhdBpyFVMBc0xiFOpC09NIy1S2erqZ77kJOtY5SVXSCw6AIZ8B1VsDyyGPjbwciYrG6zMzclbdyNuG6U2JVzM5NWEYlYNMHAmw0lZ8YQDUhg2wtDI0a2eiIWNd7G9/T3I9liPWmIKaimZccm4AAP/6cATofwAKgdkX2wGBHJBDgSuGGEYQCOTJZMeMT4EQGWxA9gzYislD1Z1nHci6sGiRd+a06lH+iz2nNjmtVgJB4OiwjQHTYKyRU6eVIG1JcOXHIHYdHdaiLWzeor1WfdeAQBGqwzQkNMCEehOCurpg6dFnqeLpa9LHFmFiywaDxms7gqrIzY5U5sLsuJnkmhY6kGRVvYwNKfFCx7zUs1TbFu7PSIVqRUiNZqKJhVi1tHtOn6wmYMZdsES8CiXuzk8czVj0cmMuLNqX95xvZt9H2oZ6MbQtsS0Jzw9yhLQjKUGCY5//MEUdH80+t5wLU6mbSsRpDst1HvhclMsg2DBfuocLZhLm9m/iWTL0P8jL9UjcZ5Jn6NctZeHO9NSgW3GwMJRqiWnZR6unQtT7v6UxBTUUzLjk3AD/+nIEbH4AAAIaFFiQLxhgQkR68GGCPgh8OXOkGEUhGQxtXLSUbAAWqBcjwcFGcsFHNqeUEp7s5lXqZB0gq7jwc88ntH2oGwqOYiJBg+fJCVRuae8uhVRhPQdVtixJD++9Xra/e5Pwl8VzgjZWaln2f2za5OJDIcqwpThDfXuQ3H89JCwt/sd+k+wJgZ1DmvNTeVaovPCipkck1WTeEzRtXotqL6+yjZ66f364AmAAmm645MyyEahk8/cG3s2g+4N5noEIonGkUMQMaecpzVFkjGpYl7BV54mpYg1UqdbwiAtOZe3QsUad9Wn7ZpReoxAEApEpS2wJ5lxk+/mU/Z85beF7PiK7Mhvamp1iKUPPVJVDgHMhtkKkXmVCZzdiElXFFvzt1tS1Pt1lbNaYZcUbrtdDBaxCYgpqKP/6cATbVwAMAhcOWZApGGhDwysSPYIcCJg9YEC9IYENHG8kII7mAiVkS4AJhEnmxInU4eCQLpoO0epOhWGzzgM7QNMpeXc1wBHCrD9GBSKCKzAknC0aRJPJNwaXLa6jSkoW6gbsd+a/1AT3+lFkWjxPXvr8LazzAbhOhpularjJUGshWlXLW1hRoKEnpcVODbpKYU2gmLjTQlh4tUl5bnTdJZXuWL26Faf5r6NQAX+NSj/XQUTNHWH6ua5hkOy/R2XOkLxMX7bRcmw822EDIckS+uNJb37CbWJjKEdy2rdLrWfW+ZKZMgtqLlop36XdNjlIihftNVyxcQitRYRyWyDuJO04dIxbN5m7CrXw9aL81++5pi310GNoGd0PaD4wMLxOMWla1mRoxGYbffQejZLXtc5EkmIKaij/+nIEP/8AAMIZCttRIzkoQmL7MzCiYwjUY15nsGxBDIytDIMIbIDABJTccle/hjDQho4mQUYYV8o90IDS9BEqwsGCqUvqQsm0IHjzjyR6RjG0sFLK1OH7ljjyM5XyFnZtwlvIVb2HWsQAEkU61HcDlg0KiuuQ9gPOHg9Ap7D5D1a4fK+nGj0vk9jD6zOeWdIeyO8w4/O7LCrUpTijrd7XCrSYLuFVOo7hx1qCAk3JJ3kIL9bB6tqqW86Z8KIkgT3HzHSt2peLhXtpRsvliOoo3p0jmLImluQXc5pFighcKJrILdMFRLUEXPR4xWas7l+r9gRiacrGIADYeGSxx1YkNPeME0vMtUyPR7H+op3suHln44g4lInjTwyprcsxFw9BhrENJodtoH4sSHOWpbb3NLbDuiRTEFNRQP/6cAQB9gAMQg8TV5noG6BDAfsHPYMoCDw7YGw8xQEHh2zM8wigABSckh3axoWUKWytPFlkyG2dIm2EDGGR2XXRhaHqUlwgxJciPdWhi2XlwIZDVhZ6QdverLwzdJI5yJPctPTYxf/cIAAKRy3TPCHI9GihnFoR0W6NdS5w2j0Y1KUBzDiginEjizw/PaEix1xogDxNy+0upTBa0YxP/Ofk8q4chLGDGpH/1ABuO21pMXPCp23Y0YDe/Vh1rcQcq2Q1scfuPp/3B5oTeDd6jxRKY+hbBQ8OFQGge7mhAKdNdpet3TlWMez+r///oIcl3/xMJODbR70BIhZAFOhFaJSN5XK1h1Asu56sOGRUifM3DUpSUKo60WsSxVa3kocuFrnnutGyvrS1Qs3Sj99/SmIKaimZccm4AAD/+nIEq9QADMIEFVebTDDQQcMK82WCWAi8YWZsJENRGYmsTYSMoAA05IGeNiFwgQqLY1B4mi0ZLICEWjMOna8bzcxg58GLf19zeZrzQGcaegeA0rP4ww+pKHjCuah9xqiVuZRX9P6QAW5aGeZgvwFTSdLStUtVln5wEpXQalORlWeQ52irXmGebBUCMlHMGj6l0tasMXCe1tYjJD+TrRb3FWLX6kJ/V/vQKbctgAxjT5VASguqYxOC/m2GjmpPtoQs6MioTp6uH/TgEBWFlFjb6i749zlP6Nws1ooIuoZRBZCg/GtOpS4FqXGWNo/OEBu7/tkNc0DSIFK0oMxUD4FV3U0HszPhSghEMDGksQKohi/LlXk64TDTxbYG3nrk3HBgsli7kxZsUY5z7EOHXmIoqvum/tTEFNRQAP/6cAT6lgAMwgITWBsLQOBB4msDYeUMCHzJYGwwRQEIGO0NhIhqIEln/VXB7LiYbQOh7RPSMEMowiUEyKKNo/Ck2Z0y8D+DpR6ZVbhZzypgyPJw856iYZLU33/0tYw0vfW/+3zNu0AOXbZEMBkfAt2cRcmN5lyPDfNhxUK9toQaMbDMdobHoD1Kj1yhzxZbXxK5Fug9SwyeA4Qfah760w4R531Uf6Nv9ACd1/6To3mwp1gwh8tr4ErPL3hmUjXBwQpkBgbOBFa+TVtXYeitpfZWVNVZ1JbzvsyGLT0VGg2v/jhdosVZuSp/Hf+aILcktVRIk2WsYFSN+JAy7kcESUMGyGarJNx2tr9zf8M62K6vWpSKqa6La5kdm3argwqWeLardf+zqCrn+gLbTy0pTEFNRTMuOTcAAAD/+nIEwwAACAIZPliaLCjYQOJrdzzCKYjVFWz0woAxDw+tTp4gBiTXZdwToL2KRUCETHmJGa6ar2kJQnQe2GmhEXZXz53bco023557ptapdn39loZNUerMvbn6N6VGo5Re7ylF1J//0tWEJFJNJwaZS7TPXB2yUnvE4aVOtHar6yA1Vxc7MBViGtvS1hUQadz2hR44TmHHStZ/fOLPJu9F1BELB6tb7lfXkQkJpluAqe3MthWdRuDsSYpgd1e7UFEkDWtr+jxrtPzNUtXq9B7qVJn291myt6tU3//rndkJV7E3vUXeKnZBgXWkVFenXX9IBaSThIXs5fqMSkxjLNExAngSR8rztm71Rrd+VVf/Y2JSHxwlEry3rMD+4FuDa3pF20IHkL/dehQHFywVeHq13y4urUmIKaigAP/6cAStRQAAAiNI4O4E4ABAiQviwRQACMBnebwxgAEVhy73iiAAAAAAAAAAgEAoFAoFAoqEtXQnGX///f/7OQvT/jQw8wmPdf+LCIOxwugkd2/+KDXn1ch/X/8yZeWJgh3/8oHEh8CBAp0kGGGGGRkEgg7bs1Gc3T////7SXp/xQhyC4lU6/8UFQDEB6BzuzN/xhXnq5Pkr/+SS8cDjEy1/+CTgObBBhznr1IAQANSNkEkmWtoP1nLci2uEky4k55PD9JOcnKJQ4XQ9JYWYZW6yoaFBxFd61YhWwS4oUw0x1GMGPZahmnGnkQyVuP2CrTpBEALdiQBJJRoGsdiodK2pgmmLQZ5GLUCikkVnTGm88pDyVWQExEIPaRtZUdFlnamHJUYjdQQIqrjOaoEzXFHnUF6iwuVTEED/+nIEXWwAAAIVNdxI4RRwPKbLzRQijgkc13OjDE7BJRIxdDCOnoBBqpAAboBVxArIxxOKeqyRYkPQk+XWVchKP2vjAU3ihET/EzIVD/DQ3xVRsraAU7NmpTVCjwVPehZlqWhAeh+WbNEIIlyOVpFJOaCxh6TLGuLS7jjE8i+WSkJI8hKfv9gLeKmX8Uydfw0/FZF51aDr/9i7CJUFvyQ+soRa7vpIBgACUbjZjjvg7k0fDqIcta3D+DKKfWpIfLdaybm9VLmVPzopj5enUt6lEs05WTRv8T+BXVklS08pTFYqb1VNI4lDSQroPXElwOVKy22RoJOpnuHfcpgzC0V4MpzICck0MLQJITGK3GYQ2f5MmxlRKwNHnaw1C+Q930ncXcyeLarIwmeoYsKjDdYKuAzcFlIGuamIIP/6cAQmmwAAAgZM20nmEUhD6ZtGMSUbCKjXZMesrMEPoCyY9AmQgIABGUWntFQFSrJ7sLJ+R5BzMHS24dtIit0+RvK+1G9OYnui/9y5X/6+Cb+mia9ontL9W6r3MK4zT9r7qR1MiHCAQ6Irz2hh0LR++aEOkodS3Ma0XbUIvZVFXV8nRtcrZGJ18xur6/3oOy2/evUKbdPdzJt8Vb/7d17mFotGaft9Q7kQ4AQBv/jS5H+5ksNfV/InNFAoimBtftXkz62iu1Xw81sWXQgYr8fvQno/Z/QN767neiOfXW3jAxmNzOQPWs+3Df1r7X/MUQEF7/3dGEecYzHkbWUIMLBQLRaH+K2SG74UGOr0BbcukZTa89Gq9fR/X9HRvT8rbWwXb0f+yWFGd9DF7EId9utNvChhMQU1FAD/+nIE+jcAAMIQQN5pIykYRAgbMzCiZAh0X3uDBHAxFRrsTPQJkOBKQLC3Jttu0+0GB4GaLGxjPDSRjx7tRX8a1Gk6CB//VvN1Lqpi206kQ3fpolPQH11lXs6eo2lX1GESerlOKv/kKgDJJJJXrwOcTjXsgOQVwVck2jhMGfRnV6D9p+iPbI+iGK2U4qzXU60PY2lszOc8/Nbu7ILfrp29dQrVdU95D4r3P+jqRBgBiX1dXiuB89W99O95BZDcB9p9SaeipW5RZTYQ2nObaHnu8uIxzw2fEYgUesD8BLAj1izGe/XOA9WLEMhmX5Xv30AhNxxzRrEjlD7e+XZXjeyp3HQmdLAuL4MM98HqzhH0Qb0ffb0Zt3RldU0r1UvK693Si2Zx0rYCZkWdPf6D+t4c+5i5X9KYgpqKAP/6cARQKQAMAfwgVosGE8BDxrsjPWJWCFCLZGeMTsEbGu6kgwjOBylm5eXuthZ7NotdquIAC5Jkzj4xBIUnoJZOB3LHA6QMrsbv7wGvtaB1yB/hhOqBnKfVnShoQkL62pa5ZzsgJANyy2bOhvRyhR1qtqcJzkn0wbrUyKaX3uGJMw8lAfdH9H2o3UqzVt7fS8qTl/VWMqWVRDgVc7f/3i7lrnW/nX7Pg9pQDkktg4sP14LIW7W7OIhWsR2aONtWBEM37MYM+JGV0FG9E7/Rs0uyiHOFpNBxS4zW55mCrqul67PqU+2hn/Iu35GiARJVeqtJgIcg71hpKd4MUzWKI6JVsNnoif93zwTdyJaMjvzsqLNajsTb9XRnSwWoDhBa3QmIXy1lZ95xI4NP/PKpGtv0NpTEFNRQAAD/+nIEWjAAAAHWFNwxYRO8Qoa7Uz0iKIjo0WhkMENRLCqvqBMIJppCzLUvgj4pFlcPDzp2xU2odmtBa7U2MHZA8t+Rz0u6thS4RjVJcsmsE0yDPscuz6nyB4KiE99KMl+khNxyTE0hFc7tdqS4M8q+zo5n2bYEzc7Uzei6Kj5ZxtjK5KN5WNVmqzqsrqZtboteiCrpfHRVRmVI60tzw9bdqvDTgS245MrAJoFxb3a8Ab3PhQzWxYd26DpwfKo5Oir2b1JapWUqKVNSjZ5tWS29OqgzFUaRPQoJSKmt+vQoss5Fls+wzU74oPjonG6jckbllxmEzc2tShHZQuIezcEyMsOdTlhG2Qdej+D7l9DYeqNOfVlUF0OZN1q5WZfR/P/+n7/TSy5luCpz2or0FWsQYpRU+MlExBTUUP/6cAR4mwAAAhEm2jmHE6RBZrvqFCK3iJzzasWgSzEYmS3ckIpWCAQU3JJNWCXUFUs7poBnl0rodzopfnBR1aoV95Pj9C/Rfe1JUZVcTnySUSaZIeHzYZEtTzuIqs7Gc88zl27dbMW0wqPVG443LaLxo16yxJnCxLNQrdGUUxA6hclnm9PKF+EuRUWtHteFZa11Zru78Qmper/3FmsWUIMOkoiFW6hffu6wCAytb4x4tW17g1DawnVVDyOaGrX0GKXu9KF6RVKX97rO7F3vuRtTfVmr+tFIvka1/8GFlCBCJDafO0qYd6br6E1Pq7FxHCTSSUfNcBOQd+Wy1y7r5jbXw5bvMc1vWSkgYiZpusI/R5Wj6qoInd8n1160BpMQstoqBsJC0Ba+3S8UcN7UI403Tgk2tMQU1FD/+nIEJmAAAAIUEloR7DjcPwILMz0mJojtAXdGGKNxGyZtWQMUpwEluOTwHIQwmcfYvqUQY3IKv+PT6oVLNnCAYtQVPHNit12ep2ZCtA4cFwG4q8FizWHztd6usX/SvSKofj5ELXo6fqIKTkkciQA5CaqA5hRRHE+OzEJOdAm+bwLZ+FPvQARm8lINcp6S/GWVsaJfIuXaCzps8m95BL2VO+5VifT1/oAUFJJItyWQ0BU0w4g2PA7ytqTxm2iuGOsx2o6GTRW8z+ny+vvp3atLr9+jjHJNN1fptIpXnE1NKLPUkRrrWDyQOnJpDetL0BDAmbFBOAQilDwpMIiXi7LXNDsKhINPQoI9de87e/p1sP82eqLvdNh7K+ViqdV0Vro9Ut//VEW9H0r+nasrniY7jQrtk0kiYgpqKP/6cASpHgAAAh4y3mlhFVxEZmuqMSINiGBfhaSIo/EUh3A0kaQ2BSgCKSSablBwCI2K2j2cpktO8LnZJ3xT2x7SKtLw9SfufeUa/No19nm9T/+KgJYh/W5paXc0P6XQwHSJQGnSCA+s+BgDCRSLTblWAGWO8QvyG3X76oxoyP1BYrY1GnNsv0fSr9i72L3Vj8xWL+vfRkDDiEJnl+SPXpb+lxkUHBeRTS9bpZ7/9YRkBUrjllu3odiilFs6XgneISOMUQJaoXtQXeme9XFEcHSdZJJXNApTepPgAHnkdtFgdZPNUJU7mUIfYhwenkaqaPSACwXI25ZZb6CJNoPle4I3gHmyPDBt39rXEHlS7Z0hBqEib1hJLN4tI3i6FvYtynkV/mt5BIl3rir7x1Fq9lQSFEiXS/oTEED/+nAEfBEABIIHI1qx7BBsQwgrij0lD4ikyVhsvEfBDZluKMSIfhAArPhUBbaTaCi/fOD7IXKPYLODaIXQr2yNNzazq/bavuvhrQbCLjJAQChr3w37eXDgfCwic025YYFkErz7VmPSAoMxIZbjC4GrQpoRM5lOU63yZNDF3OG8UM1qE0eya99Okabu7N20z9/paqfVmVkZHm9/7nR62MKQUPqjVWzwu5bvIgApW0J9iYsWGhpEnU0CDvlcgvtFODwWT0DfmjMt0kWjTbCvRa3BrjrqbBcZmlZl7l9TdjV3bI6SQqfr/9A5kHLb/7f26FpBTTUgMwhsWuXMO9Nr8vsohCsV9iZ/nfbN/1KQG2blnF9Sy19pyvBmZztl/9lDk0fg8D4gGnEB3GhNeCyxYRFkpt1JiCmooAAA//pyBBVVAAACEyZZVTygBEQE2yenlACI2MF4GMEAARMWL1cYUAAAgEACk5aB2oHZ4coZYvtg+oZZtEa7Udw8QQFR9CDQdXxrVbbRhdf7WPohJvozx8zet23BJgSv+t63n1TB+nIJQZ0gBILTloEVNVwIbdIwpsQ1UelFbHzAVNzvQ+HJxorrro3Yq9uRA4G9qIaXRlUWYqfNv/KA2VSlbPDYSQtrLqxayFCrw9BWBJcJ47GBRNS47c8of/QgcCyXcQxG93D6W+6LdERf5zIUuRyOs7+Zgx3YVKogIg35HKvHICrPldovsNzf+Nl5c9pSHFrGgAJJALW3MbBf9OCOpLlPryP3kOHvO4EYY33E3pb7oNvRF/nMhS5HR8/zRImAkCQaB/I63jkBX8q60L7Dfd+Nl5c9psXWmIL/+nAEhg0ACAIcDV43PEAAQ0HsneGMAYhE2XsnmEBg/QkupYMUWAENKq1SMkiV0Z9Fpuv1ibddb7XgiZYIw4i8g/O/UaUPGCy+VJEjb0El7pxoGYLqU2Jf5AstCyg3WnFlQMOani5wq5bgU03K25GkSVgwKH2TVIWXVOjXk/BHY5brHizAkvLRhpQ8ICy+VDxI29BJci6caBmC6lNiWn5AstCyg3XtFlSw5qeuVFc+FCXg/QCMPFjRF3ibp5rwSICBBeF3s8heS0zPnXeVjNOmX/eyUKuv1uhnLdgOGXHNh7Z11mFvfIrR7BfKgTDv1AADhurNvgIBwzJhMIPLWWoJaPanhTYUG8Uqa0jEyCzr+eLkSt+9Sjtu0ceOz7rNjP+YnhzCIqj2Bd8qBMO91SYgpqKZlxybgAAA//pyBJSGAAACGEBcEeY5QEPmzH0IYsGInQF7p5hBoRUmMPSCic4Or/VwLptfdOjuFXCJYzcYfqOrj1kQC75T+j0q1WZKpfXNMWj33f/o7qqr6o9OqlPtTqhz/KP5UrWoS57prks6tHRqBTTUkTdjbSkMpnQkkxFJzanXFtoPy9uj5Qqk9s1dOuWtN902+qO6g1Xyo7k6qEfOiixY9qf0Lrtz38lWdWAU4FARJVQAgAoaiccmvRYlSkcBCRpuZr5/QN0Vm6BQw5ShW0Vd6EXk6sr16c/1f/o/f69/Blp/7asawdTSq66hSkDK/9c6nQ1z1jQGkTWk042m4IYIwqaU89jZ5KJ0IrQvsKO0+iVp78nWtxaTC7711b9tB/b17+on/9vNYPTrXnIrprZ3DAb79ddyayS3VhNMQU3/+nAEfv4AAAITNdsbDClwQ0gLyjzFCwi02YFDBKaxERstnPSUuEAnbbRnpESYh+01o1oQGm6Ia3/gW7OP0d6n/dSLBzwf2behSdPdHpp7dqin1bd7zXqi2t40E4+OR2XfkSM77q/f/QBAdzsd0u4OMxGD4WXmfFFn9IPxdBWAovVjNgNsesx9gTu2jdP3Efu30a17//b1Hf0/rfGfkC9Typc/7260+sJjhVtQiCyolJtuAE/EOLR47GaeGtt0dVIUmTr+jiftyI9dOjJ6t/spUqtl0kS/gKyQ+KsUwwKQeuIUJaKgIKxU77khmuTalFVqgEAJTbbAXRv7ODKlI8IpNi6pM/plv7YpmAw+acG+Bd9qviQfTZsYO1bn77LZNuzJHOxS7FytuTQV96nTEt+U+t/izeH0xBTQ//pyBBLRAAwB/zXbGek4YEQmvDoM4m+IKNdsZ6BMwRka7Uz1ldAAGXbYwhNmAvMCSdoSoUAIWMVqYgzDp0QUUwPfH/96MOdOhFX9U52t79W5yLr6X0TXJTqlXr/5V8V0dm7iX93WFy9TLlkckFbmEcwvlG6B2Y6Y95d6Am/67qNuvKDD/RN7FRhUplVS20Te+2r0W7oLDoodB1IEfZ/oYw+VDALdlzExL+gALa/+g9MROWGxAsf1npz5XptAC3jFrsCjILVuAe7or7n6dD3ppaxDao70QrJ3Z61TNen4/1k6kftva2Y7P7PWi8AK77eAOlYOih8MEZEblUaSRU3kV/aRYpenTJTGb4E+ka+5eTxX9fs7b+uz1GsX+k6WSkWly45Ys5xZFPVaG+YZ+3inzlzUxBTUUzLjk3D/+nAELcsAAAIWGtuZJROoQua7yhQi8IioY27npEXRFQ5vqCMJTgE5ZLiNVsR4Pjkwfp4IdI90h/RnaEgxiyti+6tKIpV9BpZJe2B4uIHCrmFghTapAUJSra3XvJLp+q9RyRbs/DrlXaAQBkQUkt2O7CcxIniAxGqBmqdHtzj9Fb9fJzNhFS4tIjcQZGXo1eqt6/O6egjvV1xX9rD8cOIt4swiLtA7mOdpBK1QhiClJJYLHdsyPfWhRGgT50ztj2YuxjGae6+7xK++NSANbZRN0PWp3UGkFwDEI8Ryyqyytz10/HLPi2mz7LzoCUM0mGpShAQpKTjcrWCVNJh2fKcN26NQOnfr15gFcraj/LOEpW/ePQZMl0MDoZSK1qENY2swLadA+NFDwNNY6HElRO+gANsniEhKpiCA//pyBM0rAAACEEzg0EEfPEQDm9oYJ3mIvLeDQQxG8RWV8HSBDm7RAmUS5JLgJSA7D3yVwaayNBtV0539qef/HT6pzcAzEvcU5Of+xBb5p/4neeia5/xGXHWnkKFZpDxUsPsAjm8SoSkJAlEk3G3avl0MvpfSnnvyeb48jSIywh6mxwe/7DJDKVxEONqcJoSWXPLSH2OSXUxqBz1220py1e+3e9HSUDG6oS2OF1YH2aVlkulmhhVfeqZA10ZNWco7NsbNPpuN/BhJl44YiQWZWQfRXWpgTq4OFBYRCiaKF+14ybZ9hlGWFW9hIiiEk1u84lbYQCkaRblkueaBVxVr8ynAhgSroyYcMu6RHEf00UR76D2t36i0e6CtO1dhVtV6R9SZBS34xX63IFCEQkz9B5Yq3Umpu/qTEED/+nAE52wAAIIXHNmZaRLEPqNbJz0lWAjk13VAmEFxFQvuqJEdZiAXHILMEgZaoVYUawYDYID1nWK+rFmtO5AoPNjKYIlEU9Bkx/8TXxQ0cAAqWcZCwUAoFKPHBRWn58YwzYn3NbqvZ9IBACnd/9epkxUPQiMNLaVMyxrNKgx8k9Nh5kXZlhnQP76wofsK8Y0iSEde9l5ansqvdiG9dT46lP1/Ip/d/rCQAQFMtuWmHpHVhNrFltuN1QU/BOvN5agj1YTwgjudoZyOr5/o+23O91b3NIdOhxMheKHIDSnu1DxEuIx5q5LxYTw01Af/YiSCSbcsH6fsD5cjq1WO2J0q0EOqhQ7oVC23co9i5yQkGFvPtvQ4MMNb4WAl1KTes0UJpWlHlHxoGIh1Nwh1EtDdj3WaWJiCmooA//pyBFRnAAACEEFbkScS7EQDm1cwxXSIfKVw4wSuURYOrWjzFSoJa8igmGHF18M28QCFtCOyI+ziBjONd5ptBmHKGShi8QuuVPdtlNRctU2/9LJ/rR73IQ6EoYUwhZYVfFXPKRYbb5OBgBTcusFVwMJNRnIiagrsc6dQY8KdDBStctDh3fZxj5h7duGIREEoBLoqKHavvRYr4oAijYza2rJAQDgsHT1tRYIJkCBAxu3cG5oOmRvirgWB1+4/bA0NjIO82T1NEw4AxaM10DEeuhn7+rb4hETWogE5W/0ldR8vMoNj6kTcyoXsLQkFyY06ABAACTlu1NPi/yCmP+tQ2CdPbuEf3rzz1V7Q4ts1KPUt+2oZo5qvNFC5b2w3cNP51D2Bppci/8ukqKnlZa0XM6Y9PUt1SYgpqKD/+nAEjKsACMIKItwYwROEQ+f7lySiYcgA/27klHWRD44tTPSIugS5duJScGoCzQPETANf5ZmlPBsxxgfLnRkq2wXpwR0aE97giMVPiaVFRKa6lGlHaohewFRYoAlg4z4pUTNqc/yqQEEIpuQQY0YwiPaQxc1au4F2ZWr4loMjXSnqxT0L1bt/Vnbj7tfV2/7ud2urI323Z2d3BTFC4HRdadXoW624On//8b2QFJTbjKiKXNsagptGda/sI79l387eK5DiWlre4S6NoZGn19W++2VP/+Wujdej9CSwEVosPHZrG4uVMlmzF7n1AFu24etS+3KskaBZZCZLjG+ZN/JrDP+utZjk+7W5uC4UFgkfLNhqstaPe1JMQi00tpwY9MMUqDBAaH13fNF0Iac2ZGxMQU1FMy45NwAA//pyBCgkAAACHEBbPSRADD7n+2OmFACJBEFw+JGAERoGr2sMMAIRgAJScEk9AqxC1FqZpAqPZqhckhVHhXOdjDVDtqx6pwbZ20La2iPgyVecFm2bp/9+67fXdOzsVqi/oOsjGoHsrRHCMAtW7hvk4HqGKrC/itUyZdOqOKoU78Y/P+1tl522B3aYqlb/yablPmS9vZNr9vnXvYmJpETY79SG0GZa6oNC4AAYJSTjtdsAqaBsGBEURsmzqjHgOJCeeRgjoIHWCGFBoaAi49pxFyy6WlpIV0UPyovabx6Q8+pYdeH/Dfljvr3//+XeljkCcAABWWZJSS27T7AZxnQaPJJzjreJTZGV3tBA8HQ8UhOBoRQwgZmQ2WSBR5EvTA2tuBq3tpRXNh/wk1yd3nTaXm3f/6X2UCdMQU3/+nAEkvgAAAIgWuKeGEACRAtcU8MIAEiE14+8YQAxEYauZ7AgAAAwNBsNhsP2PEBHul3O9sn6/0/////yX//yAYu4QQUoCrm/xaNuxzlsVzsUP/1efMRpBTrYCqlP/s5PJnfmU5FFPwlCAEI0Gw2GwbWPMCPqS7n9vP1//////7kV/p/kAxdwggpQFXN/i0bdjnWxXOyh//efMRpB3VmAqpT/7O3kzvzKpFFaCUIAFNSSRtEkBcQRRzy1fZ+7Xt3BnsxHqZj5yvyZ736mtb+Vqvc97/1Mt59PXO+VxCXHQm8yU0s+prrpNP7qywtOpxMldAAABygIMuHEFpVeybcOyq5V+9Uzv/v9X4RBE3DnWlNbxneGlhQDmUpdWDaS89XD8ycNIW1Cwhjr/FUUVvHfRtCrCvWy1MQQ//pyBBNaAAACFjZl6MEUTELmy+0wYi8IgQF1rBhBoRQgLMWWFGAJN2z6y2NJJ+ouvvzoMcDL/3YgdSJhMQ+Dum3NI0YBnl9UQW1y5N1XT8yX/o/fhyue/ah1vpsKDGscWNeFY2sI4d+sAEITS1tkkFHgHjg/g1jmRzPqnFeeGGVu6szTb3+1Pb1RBbXLk3X/qZLr+Z+/DlOvebtRt9NiBjWHSxrwrG1hHDuvWAGiMIpGkSQ0nCswHGJPsRMlFbbHd2eSqO9kDpsi7c7ZXX/RWqVsqXTmfovVfzTe0N/Toql+DdulSyvO/Wd4x7edLR9XmjDxP4z2QMvrLEGi+IvAUVKpthZHhMMPYx2hNMvUmo3rWrGfoUVqVvv5n/1X8zm9lD39OiqX40ZtlSxbzv1hrqejtLR6YgpqKAD/+nAEEvEAAAIYNePowRHcQgarAWmCZgfZAX0ljKGxF6BwNMKINgnXFpbZrY46BDXd5e3cijIspksiHMeortd13YIVGU+Rla+vYxJmd0Kr08pZv/m8GpDXj1x4Ynj5FDVBNGd/9b/iFZQTxywc4CsmH4HsyzBuFDEp+ONEvfxYnGm4HnRNRdfcfADvLIk9UCF16uufXrb3tfS9Wm29ubwYx39Kv0r0lbv+T+kGY6uuqsfwgwI0OTVJkUfNUlb1Q2Ol+rcv8Y2qJZmz6f/yF6U7O0u0LDn0q+e/NcbK25BKBjy3qbZzuiormrAEgFGok24k2fjZyGLd7TsyhQRwxJoMZ4d3399Xuwd9eojk6aH1pOtMzEO4ir5m3PrTUR26N+vh2+OackbzmwLESysXr5ZKYgpqKZlxybgA//pwBDxpAAwCEzZYkwkTMD3oGzJhgg0IrHNmZ4htASMQbijBGXQG+/BG0b3KcGNPtLJ6n+TyXNkNneL/p+zHVe5RMx01SlWKyI+ThGVrdtHzktnq6PUyOhy6aW/ifZa3EP06NT9vbqMJzKwUmtNgTehhuOND9lsq3zDfWgpwcaoA+E13VRCugAPlbBCUpzF7VRPp+4qjLS/b+N7a9+jcL/1cj/0O+kFRy21SBUQTqdKdWWcavG5JnTPDit2tCOHBFEN+QkOlzM+P+CUkIJZSUMfF7It0FRatxHFwFkr70dC7yVQiPbbDN0qdv1gwAwF9bZawElT6OcwpXesoz1NyxTrfZvppic8U3MPx7OH1/fzreB1KeT0KF2oekSBuwwCYUFRwlLHnXos9zDqqiYdR2OdlTv0JiCmooP/6cgTUsQAMAhQ2WRHsKMhCpswKDKJ1iKivYGeYrMEKq+/oMIp2BqazAbWgrWRSWqGWGpyJrD0LfK90CRBYzDWomo1k/Cm5tXsi6H1ZftVc1VQeWqPTR9/i9uiKJiqu7a2Vpfr7ea+yodG727I23ZaJQVO41oQ+i+imqj0Cb4DyyUPVDHsBtRDdnZUymlPu2T9Eo/+968VFrGSeK/ShQidmzfXlq6Gp4mOjywBDkkhTtiAC7RxcFp0erUzq4s38+ga+56nD/SBB8h6CrUolXaYC8z6v3/uX3apbm1ONXceJoeHfkMy6EiH1LbZan/iL8kEjNybbbcr5j0HctZULp/bW6SxOQOF1Fo+fI+K/nMjvl+6D/O737iP6/7Sov/6nZUZ6uUzqzN95klYlsM0+zY4MxGlMQU1FAAAA//pwBAOeAAkCHhzXmekTMEIGu2MkJXcH3HNoxIxO0RkRsPQynZ4AGOSQqzeKUWxWBLlbggGTbZTtUW8OtfDjSb7D9jsYpI6O9U4MfMO/vBj21ySTY+KqhAhplJZNTPq6PTec5Jn7ck/1oSkt23K02BjWC/FSxu7Wzp1kjzqMWRuXX0iXW2D8rd822WhS2elEvr92QSZHZBFKliEep8VCbNt5VveSSz/MD6rvrRDaxAkD+Bcy2K7Ulg/9Kt+HW/a9ITLBM+9Xzo2nUfqiymPWr3NsURBu0QilLBkVuJkmcw9J0fkkexCsg/5LQi0G5I3Lrbbo0D4ZTuKmHGjVzoqwvSXRN0dd+W6risHSu98cd5a4qTEpjS1rcVmxGAD4uppmlK47uuIJYQTeHNF12l/lXTaYgpqKZlxybv/6cgREHQAAAesx2jklE6RBwjsTPKNiCTSbYmYIdJEapu8oNIlHEEgk3JJjVgM2DsmiSItZxHvTfsZlpMqAmgr9lerWsF6PxHXqb1to+tUuv7EBOBWx8w+4tVQivs6SpL/3/aARJ/+9ooxkPU+qFokDa4XhmDHhZsatpgxbSQ+AesbSHLZKrrcNW7i5X0AYEnSyouDFaqnkEUUzpahImY/xRnNVd4ATbkjhqUgI2JAlYHx0ZphOKSOkOT21zfvLOpnO3fZfGb9hHZdvsNBTMRUSYDbeAhhV9U1p/EY0RCcPJkVXPDjli6TtF1FdH/QkAHakW3G7q8C4Ob6loUaq2SqvHD6Pk/2zy6vqfr/Qfbbda2u6015JztXidObftpob/9e62O5QpAUyLlNoFrldIdfj3oOXuqmIKaig//pwBI3uAACB/xhXmekTMELEe0MkolCI3KGDoxRxcRQObCj0lSoABzf9gZY44ogpy6VI5XpNlyewyHlqx/XNUn46WkFjY/6y9JAN6anX8MrairUd1SQsy8M0a0C0UXr6kOblb/8mCW5ban2ABdATYKYtbCWonOlaOCS77+6vKjuV/w4SsJvNqG2pK4dAyDOWUIB4TTEyXIC6ZY0QR3IYHVqedIJp1s7/Ui2G5Uo5bbd2zz0lY3GUwoN3QbhJPpzOMxEf+TlGfzhm56xCkMz6qXphnwmlPNJMDqpwi1V0sZf8jcoaKhUHSb3qkmNXd67CABBTkkV0FUiG2Gx3EgVxxmyg5FVL9CT/4k/FEx9MReVLixH5v6hjdVILrh57Fxc6vhbFhwqZfYsCtdIfVYWRIIvTZJ60xBTUUP/6cgQGMgAIgiZPWpEoEh5Do5rjPSJmCC1JbUMkQbD4m20YwxSeAn/E07ArQPT4XpVnDWJwT8Eyo4otXf7p2L78YEzPodtev8euMYyoRHR5iOGM40+7bTHYqIyVndpn9tdCrZmqDWj4TW/IAJX7hcOayB+jF7ITFFnwxwpUkfW8d/D1J5azZHAZQFxD2NR2asL1fCBWgNCt9eltNOMCAgTHlLmBBSn5j/uF2HwBbeYICSacAmLHey3hi2t5c8/RPptOM4Kj5PpR6ETlfTRhq/Kbep6YpZJAio+lP6+nft778jtWzf9EVjmdnM5h2rsJrUg1WFxDhD2oWl8ZJllg4Z4H0FmZDT2BdhiX0p6P30Mu39TF5J6O5bLVP/XtuQZnnHWcUeL+vexgLTdqjqQ8mIKaimZccm4AAAAA//pwBN0UAAACIEzb1SCgDD7J22ekiAHIxY1xWGKAGRUxrd8SUAMCAUQUm24FNsToGHJFrqsC63MxJrRtjEO/+kjqaLleZ8r3b196kOca8wUZUZbt/pazotTNiFv5Pb+nrpsy1bFZww9MGQFElFpwLbom5IbmTUhqyt+a2CLZllkUfZpfjq5GLo+RqMb/b69KJVXJp9OT/k6//e1+n37auUxiu4Q3X1+hkAAAQAlIICAQDAQECzCxkBILA1FCRh4NIKxCFY5Gd3M9B6OtV9KPSn9f/+v///P///k3zn////yE//////ZxAUQOCgHF8x4AAhJAASAYCAioKSDDSA69UyymgTnmJEdSHFLvR8ev6/uZldFq7L////v/5///8m+c7////xQn/////7OICgwBAQAxfMemIKaigP/6cgRtrAAAAhon5W4CAARD5hv6wagACHyRhTwxAAEUjXAnjDAAAAAAAEAQEAoG2eowGg8xL36BcR/yfPGZP/+QMhw4Dhr/+JTFKjNBqgWcLI2VkA2UUcaYwJuPGvrB1agW//Ii4f39WoAAAIEIBgUU2igVCaDm5F6M3aAfkI+/JAbCX/FtiMf/+K4yFhDf/wK4NIhgGBBA2f+3MZCSf79wcNPGO4FX/3+wXT/1YAoIAtfwg4tzI09kMs4jrI67nmvIv1ZCvR13u/oru9UeCe0UeCxo6FD7mrmngFZVSnpcswDTEr3vekAIQ93+7AvzVWuAAAAAWqHBws/VLIZjRsPwICOrD/Huexo+WZ1YrcZfHLamm1kSz6g6VDoBAQqYXpDaDtzCM1JDB17aeaOOMmP9Gk67pdYmIKaA//pwBDkRAA8B3BTeAasY0ERDDMwAYgmIXD92B5hjQQeMrsD2DGho4QVBShlJw1P8Jd0MDqVKlUYoG2q5+YIuWAxUXFiLUDEFQg0SCBljYmFxg1NzC6YGx1uTdT/1Iaj87pASdijffTXSJHzGYEzthWYQ9WqXNRc/z7gkJi6uRGhKVCAQq1hUkxtCr99Nccs0FiB860VTFWMacAdz1XuK1pPOcjamYxI+HiqC0fdRbeRALB4RAmldKMVAAREBUmIAATDp8PmoqHHpSTetZZ+GiQXSyimJZN4ohGqcYuadd2GER45OwVV9327wmR0rIkN2CytiO37IVIbLyzrOI9alhZFwEmTCjxs25QPOvB+8k0G3WDQG0dum1nVpVc7YralCjFkUuyYrqU36PrTEFNRTMuOTcAAAAAAAAP/6cgSWbwAA4iEr3ZGBG7BBg/uQPSMaCLiPgaMEb2EHjG4Ex42AAWVvq5G0jLu7SKd/dgGcguC5TJ7lI1+cMyWUvXlzMGbGR9I8terZPOjIMFrw0Ye0RV3vVjUitirm+o9R4T8m5CK4uYbfC+R1D4OrtxS3V52qJRZEjbHKJol/hxCFFeM2bhZkeX0oYMDFGEs6GhZk0iWvOoe0BS957jYcm1XC/gZlHhP/r3QAgpFMtNsJSjvj2+tT9tzCwpRcUIirgqBtSTIpoDtcyh3Fd6jAgKIA6VUgCkBg57hapHu5fF3UrFquQ6Jm0815ax9ddsOntBpZOzOEtxeLes1s8Ejwii+WIVSG3hhl84i9NIM0ymaYoONeOPsIlZACsUnVUVpuM9d4TCdN1x2hD/AVrLFWPprTEFNRQAAA//pwBOUlAAgCHBjbsYE0MEQha/0EaQsIVHFqQLxBwREWr/Q0iYwAQCtVt01xTJwbIbJRaSRyhnIsyTmFxhO5YjEKATsu445+MPPFnLMKK8kmeJCA0tMisOmwqNcoKLi4yKdDE+vzH6PW6BBEiBJKONSoUIKaD6C+S5QRV1jYxzAMMFqkvVPFpUjQu2qMWTGGnrARGTHKaEng0hzi2hrFfsrdO8kZtRrTZeSMFi1YE/+sFBHuQwsncRsQiCuJpXasGbXDuzJCvBIzZnq2NqZlENbjyJg37ryDnNCRsRLQsQtxiDyj0l0upZ6Y7zFfZMf78ACiMY0bdduNMXUBHsGsLugkpRZjZWOaqYSnr2sXbpa/t8M2s8q7FlbBDbnRaki4alrxAp8yMhumgKUjnr3B1zd8r6LkxBTUUP/6cgSZUwAEAhYP2xgvMGBBJrv9GCKPCFDZbOeEU4EYFq7kgIreACcltTliUF4bEWdjgyQolXTASfKAHduI9RiMqG0Kc4gVa+ywUqFEsjJ4MIW1uT9ERGqUHmP6VjolfIvTzlq8nQ7TuwAAYsTb+/36jux+FfvP+enDIqL4xV0EnGj5icwWLfvNp/aCjtfouVJkKUmWnjDo02KIbfb13sCyUpajmBd7dDvXACHLbbN8tBuES1LNZktfc1IDaY1eq9UpmOBQAfLyANEQyXeXydTjpGgJwUhnSFlZxdX3kBmSlwtRcwrt9H9h36P9SACLIzK9RmjQ0UO0E/K0yFHWyps1cJM9irXU1mYj7+3QSeVWbkiazborDxVVS3h1ZJxoi9wsmeNegXR/elVCpslaymB90wmIKaigAAAA//pwBHsMAADCFhxc6SUbyELD6yJh4yoIqGNxpbyogQiOLM2EFdCgAAAAoKSRy5qh4DiVMz0iTrofR0MIwr3LdiSsUGRU76yGB9sLEWLaKhkUE4smols2qQWvLMpNryNqvTZpTM7P4V+oCf83jEIOipFRmMWyHFwgN8GQxVcD1YcIlXGkDmFMSmVAgq0TrGIGfgLVC+H9UcwzZSo7GqSRMbSyVzyjx2/TT2emmAAAAABOXb/7kgTwqTF86bLVvrd3CKW4k930UjkxTVtNULBroaqcSLCoqMERypYYY5+s4prE7x9DFZGL33FnLa9Hs0G/qAKUktr/JGnwYRMeGDsY+7Px+pUgaNlnmUBhasu5WlEWurYtq7MAWO42YR3KU+xL7Q3dWgVYbDak9XXfy2iRT2q7UJiCmooAAP/6cgSnWAAMQfQjWhnmK6BB4xtDBeMOCOi1amC8YcEcjGzo9AnQADkto9IZzDII8zWnDox+er7ZwF2HG9TFaPLctsstActdC7M5Qz8fr1XmHMJUxKSMyO1HQNhNKZJiaZb+nT6QApbaDTGcQ0gyUjWJFK7CZjTQ4YQ2umeIG7B1kA9oDO5ep+UoC7Tm1wAwata30BR4naLnjgWsMTRwJKWj/q9+1f1EJzb/liGquA7Yqby1QspFbhL8MJ7V2u9ounKcJcAlvxvilAtI/3//3vO/Pn3inHypDSwYB1GhsjJp0umWbuoRqrvV+nT31VwAABmzbRrsJHAyDpO2fpg0JUyh73rAi1iUFp5cnMyW5xRoR7NQMnWGQFYGgM2Hje8t1kzNyJyRMNTJLVWQUg19i3U2ou2fvTEFNRQA//pwBGJhAAwCASxZmw8RYEMDCxM8xXYIrGNgbDCugR2JLaj2DKoAqWbDLkqvu+TPLJLbnMXrlN3MoXUt0jw9KSyz0MDZ6C6o/drzzG/X1eyaJYRMfvvc6x32mRedHWyfuFmDv//0AAqWUT4hKkEMM9XrbG7HC8SJpwjwTQDKiKBvJ6WL1naE+LL59gFskWxatrrqpI23rNlhdi73AqaWxUr6GR93bV3aCwAKctFvckkKT5NpPzVC1V++1WTbc1nJPk+MQF6wzH+vNvQqmrztHNyuB0h25b1pnhxhQFYUlCNjfSaU8Xu2j2tvS/p//rQACAEJuS31hRTeGtR9guhnfXnlWgDWUpmJUCbXGswDWfCkhOoq5E+oehYdLMW3fXa8BCLE1Fz7Vzc1pOKKqWypIqIpXfT/1JiCmv/6cgTL4gAMggAcWRnsKVBBBkszPMV0CJRzZmeYrhEeEu5odAi+AKluAvTIzwXKFAWGDAkCVdgKlgjBcBFcqEjjliFhOL2ojWoXRnEQG7Y51OtijIcjQEhoFcy4FlCq+S5axYv6LAUnd+Na4tdASzFPHViIxhPZUzmDldwOT+qkJenjmFrLQ+6DrA1Dk7Iz3+rRjVtJrPoiWf7YyJ0I0ZOtak9N/Z/rACTcg3ddi4CnoQvppnQ5KUXC3EVJciTysL0ljNGuDGepcmGvS8My8WURD3l7YRJHRaeYLkjzj+xdFZjvNLYfaij9ft9YQgBJpO5UIAqGoZWCvByGsDx5xDpHKo971LdUqvtE7+VLqmftIxjiAkdUg4VcKNeXYzKtbovMWMb3argdI9fQtZM0uq5rkwCmIKaigAAA//pwBHZaAAACFhlYGDhIYEQjnC0MJaGIeEl7QwRK8RITLQzEiSoApzYADfRNFZaoGNxWMKzvP1skr237Jw6TFBzDdWd3bRk9P6KWTukyT/dtIjgmq4yAZEiNHkDPW3L3LDxEZofePdWugwJFmJuyy3sK1dBVleRyPD0rP6QoLs3urNASy3ILbcYOEBoYkWNG0iVzy+g0KXkrUVYtrWDcXImW3OXVU9/V8ywSf9mCwNEluOXIS1SiJfSCTFoYKHVlVQ73iSLttntM4i4qBxGswImlgRgMi4lZYmfoO5dg4dP70G8SsBMAMY0MCJ7tmaqxEAU3JRWpSMj0MFnnY8D3kQp2Rgkgxh7tkpJO9Td8TVsXqck6uduezXa2VTs7lGiLKqLuGlOwiprZ3Wx6p8b+VW4xqQzKJiCmgP/6cARNCAAAAhcmWT08oARDpJutpAgBiGV/cjjBABkWMnCrBiACgAAEm4A4WYC/nePFErTKhSMrCic05iGmcEFx5TGsoTEVdwZaviyNeovt2dZEbfTIiqOZGEGnMiiW7JTiV4aGkK3J1kAiEEkEtNOCYQuRMkVay5r0gIscUUfM2dT7t6kepiuI/QysIZnvphB4OjXXMY9Cja7L1YzuKRZAXbf0H5g7e9JlDjaQgSYQrQ9QxOJUTFU78PaU5cDFEMJGla7Tyd+2y+vnV//rxBCT//5yKchG///FoR8hCf//+LO5CEkILP/////Fv1g4eAAABV00oCAgGBAJi69aEfTKb4N+0pe1yEgiv25j+/Of96/yf//uRTkI3//4tGfYhP///AznIEEIEEAZ/////4G9CQAkW6YgpoD/+nIEQgQAAQIaK15PGEAARQVrteSIAAfo13SkmENA/Ilu5BMIMAAQJYQAO9DYkxuye/u8r1pq5Nnwbt0b+jUeVFasqB3OkisjNZbvsUKcchooagZ6nVsTSOecSrfkQCHVP378Uyi6OZqBMoHTsSJLqZlZK5XeGFuqa+vP3ov8zUeVFasqB3OiEVkZrLd7MUKccVaKGoGepzliFLjI4ecSrfkQCHVPx2/FMotiOZqALjbQD+AgrCt3WlJjYwmzKz87QY9+yovP+rvte5qtV7M/TqyI7oX+1W8X1SQtkG9EyEjyhZoMiLTvi9JX6GaAABH6ABZmPBTZVbb6umH7ItgZjsUW2MLh9QRPPE8CmulJ4kt5jyQobZ63S/VbyDfxgjULNBkRaWXxebK7NDNCYgpqKZlxybgAAAAAAP/6cAQxdgAEwfY13CkhE6BCxBtgMSI+CKRRZieZDoEHCqzE9higgB5u6cEhpEkydZYrnjMaFmY+hooYNoAKOvf20crc30KM9Ey0/f0dLfqVk24gqGk95GonlNIzrGlvXI5Zq/5G+icLCiTolIfddJwPZNvMavPhf1jO3tDTXO+/0VK6J9kKMPFJ0i2klIioB1hqiUKtT3kam/S57KxoibfWJiLpZq/5FJ4+oCdOkma+6RiMWZl2wF7cz4OF1olkPIkNmowugGMZI90Ezy48XHaRx4lixrfvU7EttR3tkkZRVWpRVN5Q98r60t8bUN8esiLLySMaTQinLzrK4Da9kgL5uHKdEZDGqF7xAdr38i1CXPJARlJ4KBrAW7S9TsFbYsd+SRlNXoTeUPfNev9dSYgpqKZlxybgAAD/+nIEhHgAAAIZNd5QwROoQ2arUzDCdgi08XlEhLDRDCAv5DCKFsAgpMltkuXhaCWX04Tt5rGHoRMeamkjRV0fqmzk1XI9cpUXBr6d1dehh3q1evR+GJ0dSgMfu5GaQpT0V0qQrF3+gjWEXG3JHwpLg7AdHNwnPY/K8kUqOKfai72htx3FtwJP9QJ3Vrj66oW/XuqvzTX6o+au3+jtgn9vy/1MMWsT/8nRwy/W1AAIQk3LZN5dNhBfEMZe5u9k1wmqeamtQ4ns1bFf0f3s+v/9NDiiU22LzsuEgHe9yOMWowzy0qDxZrzrySNy2fY/t9YlzcAK1HfVZwHQoR8HknA54aW6bIILmpY7FUOUjNjfmHb0YuzZzHVkRNfI3/50ckMLo9cL6aI+JP/BkvuijfkNDbUax/WmIKaigP/6cARWpwAAAgQ2WxkjE7BAiwvpDCJziNTngUQEVzEXpi8kYYl+Bcltt9jIFADi6QLBNhlNddQZCDZSHhVhFQa7EhtHshKC/0M9K2PqnW21fMVFoXVbX/iq5Bsi/AX7ZLFxn7daPrwBCpqurSVgwJTCZ3aJ0MEVUXKD3zNSZvFdWr+jG91b0TrY6A2YyuzpolytuK9Nta5USwY33+rP/4J/372yZ43rkhHopxtpyrqhSUca03xcwGuLWPjeeRvxWusiYxcoyjhcIbUGjQVt2qIsgeR/mg2aklgtbPrs3jA86DrMhSBmnoldPaDVKlwAhS11N5USYBqWn8r7eGi8Uk+xwpZPULjIW0JYTZYoafsIZGRN8v66oy5nTkstVcikdKbmpm/f//SZd+KGP1oUX1mHJ9ykxBTUUAD/+nIE3MwAAAIdNdzpgxQ4QoKsXQgiD4h8aXTgpGDRFJrtXPGV4AAAAKQI5bJL0oSCMqWkkOvdAucs0fxPXgBFzl6DPo0IzR6Ro6r7KjNbUrd74hlT7KlS6L6936iV+L6k/1Oss7NfId9VARZUjcllkmGCWZiaRhyFCDYR7tWcHg3TkDmUuOd0YSU5BmjWkDnA0JSVgAN52pmIc89OulQB2XO+qqYmyeWHOKjWyHDbkstUYYCDXw+eENKpYpd+J2rJ54P2bKXs1CIgVBQaWJk7jg8POU+koTIe8Xc84cmB+igb+ykqREgECuu26sO/W6uAAbk2202GJkC0Ig/p2a8afDahrLSBWyb6Jcli+DH4cIM3DYH1ekcpi53pVGVU+/a7FHO0vqq/oPywpvnnMp97HKHu/8smIKaigP/6cAS5SAAIAhwY2JsPMUBBIxtKPCNyCCBldSAlAbERiSxMHBgwCLckg5p0n9BTnHlhhAXSGqFTH6XppXy1qU9AkNsevYkGW4KPZBL7rrbeYNmVg2xyZFLpB3GtVTQ9TK3/V2t+Fmq0/pwAAgNObbbVmptAxjmMzSRPOJNaYvTNc4wWSVZuQyFgx6Rx7HjGQBMjQGzPJQ673B8dJqtU8q3lt1t+jVNVaf//QEr1VWuYACf+rNvl4OyxzUQO+6H8xHeHBykNlW4+3xgRwDTyL50Q4YQpDNUwpboq40ucZIpvO8GO9CxF493m7qzIAUdts77JCbisa5IObgt6vTYRxikhrroRljzvlD5F45z8SOLoBxLDZ9yGvc217KjBV7FGG+lxpbjhLf7xfu3/Ga/fp+hMQU1FMy45NwD/+nIEfdUACAIgK9gbDBJgRGScDQTDB4hsTXTlhFCxAZXtyPMUpgAXLaO7T/ayFWM6fDa+40BgEa3AerLwhqNrtUapgo0vlnfklWfRbPMp6mRuh36MnujznZ639WQdzOTRSrX1bGnvsZ1IRASJVRbkjk03scb8OXF4LOOfcgJwEfpnD88Rde8hqWTfD3+e37Q4ovNrtToj91YyXuVqFQ9G21t3WMZQ6OddCtbnw/0MCKTTd8ilwvfFMONS5Q3Gc63bs9p1QuDiDo1ti1Gj5WbTPo2lwsSGPNIOLJXmGeXCx5hBRw6uxKHomgj36kSrvijlLBma/22QW1wAYYQpRzQEK1IlHR3Qee8YG50fraVzJkehR7bpLvLbT/lOQaq5hU03S6VttWpZhnrF2iyJFmjp6fUmIKaigAAAAP/6cASqAAAIQgoZV5sJE7A/glsTYeYmCCj/dOAwoREYkCydgxVaAAktA7tnsXBiqrvuY50fULeHDjVdwWuomUas6bfYSkpaIkf6bfyNejMErYKqSdVcLMsNT5RYxI/YpDnmua+YV8UBKl347m8cpFXJSCnujCQCrSMjekwOSKO+bWs8dnStS/DHggaYLmS+MEjFuoPoACacMrYB9L2OajVoYjJEfVaswjcu37CMO2EOrL7GYeqGNPV9/bTEnePOZEUwKzJy1SbXf5KafNZHs963ynteI+3Otlblcaw0pCPtCwVTvk7lyJGAAptyDLGrTCrYurbF5XAtSOUAQalFU6TFyzva47E7G59WWCMkzK87OjoOaJAgTDIoKPYWVrdHW7VWprJuOhf3Yrioq930piCmopmXHJuAAAD/+nIEZoAAAAIiLF7QqBWcQ4mbmjzCK4ikqWzlpEORFJVsDYSJ2sAAqDbckoRxkBHEXuO0HUY503jB5ztJo0hNr6mJYL/zzs5C7+GejNzeomYIyEPSRhoLPNIS82aCUqsKI+uhhBBMDexCIAAVBKTbg96XJAmRIeFyX4nsjrm3sWexCa5MyWXQfTV/595jrr/y2Z5avXe9i2U2XMh38ztV0PJXp62O81EsEae6lmACBJyXYcC54BxCVPnEidL0P0kG37CuCsujQbbKjIVUgw2e6GV6PpU5LsRWZ6vbHzCqN5lYTIjAKZW57RgfOOKsHkL/v9AAKbkGeppkxnSp55lxvs0Jv4Axzgecyb+HD3LvwpsRthiUuXy+iyqlW76T777sjzVV5X8eeFQgRsQm9A55EY1viR9Xp5pMQf/6cATrKQABQg8sW9EjE8xBhssTYSU8iGStbtTBADEZDqzenjACQACIIktODesSGHHDVnjSfVQWmJkUGWZsMavVLBlWLQmK6l96Onp3ZP/6skLPqc/CO5ius4SKpPBCnah7w6Oe3jYuESnIBfrxKTByEOy2GzkIQAgF00IgVTLOtnqvtLcynu0LRQ8aGSMpt7XqZUrp3pJ/9frdJt9mfxRaiQUKUUD3in0AGVqbtoHlwaLUdRQTQ5tCoOUQiXVqYLAXu1iu2iiOlwTlV2bZjucirZat6mgjoTAtzLeKWJXSKUsIuO7bmnjQ70TQ+AJJyWjGcIeBSSQrlkgYitXvEQn1cXp+ZufMUxYU9UREZI27ayco6pICAwBg4KqOlKhcwkTgUQ7juZZcnePSkilx37zVV60xBTUUAAD/+nIEPSgAAAIgZOYeAUAEROnLscYUAAhlWYj4goABCCswyxJQAgIBQMBgMBv/////6K6p/+SERJUp//gVBFAFBMHhUW///wXwa3dT2KCx///43UfjhQLwFY8YeG/////k40WgqEhASMP3BJJ0RZN6AJv/rUp6xNqjmfvqUTVVTD4o3+4cFP/kU7J/+KDBScZ//gQWAQUIov//+Ji/egp///4+dkFAchT//lzkMCAuAJAAABQMBgMBJdGg+qJcTirxL5DeZP/////6f/5VOMHCpdf/nOVRY4iJin//7WKgsGjRgkHP//t8WAQUAAahxixcBAAMMMMOTwMgvBRz6uxx5rFeQ3mRP3////+n/+HSiYUOFS6//GnKosLiImKf//szFQWHjRgkKf///FgEYBBqHGLOEExBTUUAAP/6cAQdmgAAAgUa5W8NAAxEI0wd5YgBCJyDcSewoYESmvE0YJReBcbc0sljRJVDwwy7ty4hsx1R2657hIekd1/++F/uAcOYxswHRwHLNKoK5UJ38+nW5VOP5XJfWZhm3pfVTdy2sSgJlna761ootMC6TqZD3bq0DNc8tq/W7MjNdEbpInWgOOXQAjZForCU7hNqeXWew1PX0t52t2jJPKPX5vFLw1rh2oXOrAADG2RaO8CwcH3ITsLf8xQJnP1ZIrq1c7OeYnRHZ1IfugSb96lcSeGZ4V0Zax+1eWK0L6M19jjwvkwLXdLFssHlN6WpaECUJZHGyiSnCDjCow7jHsLIwk19pmSdTNdEMZxVj93AZm/nZxLTmp+21TLSb5/8PFaF7DWa0OkXHhfJr7pYtljSm6rbUxBTUUD/+nIEW/YACIILQF1R6BBQQIa7EGTFagiY12hsMEGBF40smZMJoAAA4DttltSQIMeWLMZJ3jZAtWic9W+3VP04OiXDjGtmtZSsrPVqW3r4NLGR7c2rpqJ/+/9wbqudooFeurTraxH9Q6QeqbhqVxtLWcm3onbsZqDjzsFiwtXL5mVkcWrIY1aoaSpbN5X8BWfX6qWVO1Lb/rsl/c1RWlFFf/JfR6m/Z9oAKckgE8GuHAnrRLUJUFzzjgCI2c5Dhdm4cY1yB6KijytUX6/T6tqT8btbqRtv6wjtYGIFXxlbywoxiHfYnPaNXl/211EA9bzC0AYy9Yap4dkVLH53CNWRUwrCBrACv9KX2nTVP7g/Na4symq/gToA0h9uKJvFa3UdR3cnb0VI3bDFg8t/8/ZySdSUxBTUUAAAAP/6cASm6QAIAgI12RMpEzBC6Bu6PWILCFjZa0ewo4EVJu+ohJVGAmqALsNJQLhAyjs/DOUtsTcQYNLGlgpt1hZuv8A89VBQR2RIgrDQYaPlMlzBmaiAy6t9D/9Aumu6dNcT9ft/+gBQ7klrnNwhSOTlQodK3o0fDikEvwQ/q2gIkiZvVPK3+7EEXv1m3WrHZIN6Hr1bH12//19gbfz1cwJO9gQ4qQVxEwyEAArltpbwVjH4GF5zHflScFV8wGO/DkWrYItsUy6oWmIrbGc6CY+1Geune/IvRX037o9Wrj9lty00ffm27ujdrNfQAQXRSqSSZYIHNjK6vaxsyaDkQyQ2mX6X4onGr4k2rkQxRhw0PlfpvOn+ol9fupkrQ7TH6NdX78v79+c7ba41ucV4mdu0piCmooAAAAD/+nIEPSMAAAIkNeJooRQ8QgQLiiXiUwhkr2lGJEzBFRiu5MQJphEYCtG3JI5KpwsEUyqItaIPhoEGVNo8fdeTk5QnmI/BP78hesv65Zeki1TneXF29QTIkizaEahzjhBrPwMjKuiwyszSsAAcC1InLioXFCFARu6tY7Ip8uJZCtKo/ZXbq09ANVZlEereT4zLA1AWzP0+ZvPUo0bj2zYoctjlLVqqXw033MUq0AAMQBJLbXHzGryGYnbVpDPQJESR0CFXdLK/3SF8+CTdkW+X3bwwp6+83spfl0QE/qrChMW6n2NVZUypSn2oT+vkP0gIDVL2t81FG+3WlJ9/QdN0oNTKoSd32on9MhW5L6l9+8E6Krqu8nRWq/4YAOlWOj1Cqat5xZ7poV2bFQ1YAL/oru9BV1SYgpqKAP/6cATtJgAIAiEpXDjKEbhDJltHJeVFCCRrXCwwzNEPIe8ogInOAOkdLbeKRvTRepq0TFA3oNUVLs3OLc1Fvsq038L3eYjzh1g5Lcr+oyShAWazHh2ectg7KqkhJp0IykKj3fU6VDuT2PaAgERNNwHhKaEg6u355Q6Nn0CPcI0NHRlFeYYGyNUujht0zeVOgkzJIXyuj6dtvEnLt7LizxYr1bhS47mtz1P3W7vlgeia7WrwtlS7FVn/kErlgkvNDSmwTgn1+KZ12HnTkZ4K1c5nVW02YWEdf11rJEK2VaH7lijrqqkoRbvr/1Wv1/0oSFKUW0m45aS0TemL4GAkrCdarwT9V6RLuXG830G6v62TXUzK3RG9OnQ2Mj/6d6VXSmVBqgEt9RdiDlhwCHk/UdoMzKYgpqKAAAD/+nAElLMAAAH8NN1JJRMsQ8ar6g1iU4hRL31ChFixFhSrzZSdmBBBkGqr+vHLOFozlESQNOw9iCwf4/X3o3QBk2Ob283ZK1ZRSaFP1Rqa/SC4stElzKW6NlT2jgDQHiJ/QRs2vqB4P1NNtuXqoDNT10xUeo6os2zro+XujmAmvn9vnb0J2NV6+6v4Jvf6QBphc21zDtK1ejY2ResNrVhhoac/Bc5TkYrmdBB5TUjblGGZUGIzO4vC3eKrV1BqYwfsgg/QIzujBW30QGe+OFpTVERCuW3/Y8+pFf///7b9r8lN4IqIvy2zu+goQADjko1AzR3fbovuT1YnL7CYcoGbkIH4DMrtrwvs2sfflBAxjGQr1Ye8l7KWRaH3dK0Ux37oSwxnbL5x3Ftb6vp37B6++tMQU1FAAAAA//pyBHRRAAgCHhhbUYY7JD7juyMyBVAIwMtiZ7ROwRuabqjDleYAAJCC1JLdetbl5ypg+AWYAhVND306i0s3CherIPN0O9iPYVGhhgnfP5PFCKmEU4EKvcdOBDUirM6MnsTDgtX0r/btqQDl3/KZUFxkdkQj1d+4eLBMBwPOGpM6AlMoOg5RgMGdwr0X2XyDvSerdUyAnXVQ3FS9ins3bvL6bS1rft+vWCErt/WdXQVOFBHeytsiNEvEBvLMeClrO5W6CaChUS3kiz1h53oL9F72phPT6/QbYwOyp+pozSP9z8/UYrewheV/qbt1/1gcFyUm2m5tWK8UnE+f0enY2j5xUpDq8uy1FLZrALelX93yodjdfFGvY4q3Chxcxi6/lexceWp2Wh/PRzlFRZiTFfv7ez+6tMQU1FD/+nAESbQAAAIePeDoZRQ8QoZbmhmHRYg002VHlHHBDKLuaIWVXkiUArI425LfGZogfkyweiiIgbGhlWMAwJvm3hAliTez+jf9P2+N1B/f6sQzqPv/Xu4lTidQyg9FMTJ7OZkAilYAr5mgQApWS0UnfBlUSG7P3tC01cZfpcoLd44Xoo+JIbP4ifbzG7D5ZOna9v9G9f/tLRMtbNu7ZVqy1ImJSP2YIpWp6x3mgAAwBMl//ajwgrLsczKt+sIr2oEYku1IbBjauUr84Y6Yho6hNbVFfM+tyN2fo3k+grns//yPSo9TPnqdTqvo/+3UggUgJSiTroI1SB5rJtEi2npmdQ9qi3GM+IfD/oW+v1+3sT3+Y8ylBB7UlNYzb6vf//P9v/Ga7UNp6bMmxwCGrW59KkxBTUUzLjk3//pyBF4IAACCDEjdUMgUPEKFeyc9ImiIvNNedPOAAQ8abWaYcAYgANEkkU5Wy1yLjX8q4UptDzRUT/81tkOsMtBIv5hr5Pq3p4VU0/T6f/e7V/+b6e7enz12FHYridFuSpelRdyx3oAIgW03JpNwN2Hyq40vdM6+DjR8JmPGuWZgtlDTuzHmxIpwD6y6NooNPFm8Gm9WRqP4b6SzxZe2/7vL5bst+3frr+gERu3a6fjqw/h4kGVc6rVaDsAyuLWiz8LmqeI40MMZRuHbOKT1aUEVXuhPzzMzINlT9cu/t6P6fN/m5Wp5LO7vXldGn//WEA0y2sfJrkihdHeLUVw5F96LXPPcUE3Rpxa1RFqp5gj3xoGzznMIGervdU1uS9D/l+prevqZN1LKks7926VytCf/u1piCmooAAD/+nAElBAAAAISQOBWDEAEQIFbk8YMAIi9iXYYkoAJGCcwTwZQAAAAFpfo3tdttv+AOIG888mlvIy0MZwqyZ4RzQ6Fck0itVfN8UZ0oKelJm0fSt+261bV1p0Tr13pXjFk7AdOPegsihJNuSWW2gB44Q77kpouUnYKLj+okwsGgeF2hq8iw61jGPKm72VuHxhpTnj60urfUAwCU7GwMsoLypZKc0ce+oql6zk1wXdOEw2HDtqWBdIsgkKS4ydyPa3RCE+9scY//+IMh2N/t8VEQEUejf9v8RQ50Ygmn//29rug0VZDN/////bOLyRCOBAJ0/T4/0j+4H3riAUD7n6N/////FDLL/+HGGHYSvn7fFRoCKPRvO/ZUfJVU+v6f+jLdmu8aKsInDAAnPyTDEDi+5J+cLpiCmoo//pyBNz+AAACCA9lbwxgDESA6/rmDAAIyElzLBhFgQ2KtDwwlg4ApJOSWNpEA8Ea7IaqWZs3KpZHGoVVChM8LiJ9ulqeLkVlmIqxYqZGNCRWJLQ6swKNcz7B69SGVce7eLyt/qZi6QABpsbbSR0uAYXnuQUj2vWOgaDgqGmgaGoUD7IQetqHlmp3i5FZZiKsWKmRjQkV88swKXMbjbB69SGVIx50UmxLAN/+0AArOkADpKR3YtMxwTvRYHkE3JuY3lT41+oquRhw+kFXvRCJ+dEa38qCoeKimUms4yppNArktXYWiVhcJyXNixZS6fF88HQM1UzeWb7WRtzgk7hfLvCqAIUaPW5roEQZeH1ESiB8uLvfhE/OiO/lQVDxUUyk1nGVNoFclq7E2sLhP5sWLV0+L4VNpiCmooD/+nAErSYAAAIcIOToZiqcPkL8LyRlHwiE2WQsnE7BHRru9QSUNAlIpvbbI2knXErsIdV5w+JlgJC6A9L2JSEAY6EKONpWrVbSra4rPPYTbI4s8W5xhGAysjxbWZ14oEzukqjdWAWOlXFgIyUBaJjf2WNwuI2Yk79LmY1g/UkA+nMQES8IC2SCGXURWdDTO0rPdtOLHhZ+cYRgMQyPbzP7TvK/WhjpVyl/FbwX8ntGZ8kFqV99qQPiwm/cqwK9WKhM/HA43UVOjwSGrU3Ure66USkiqWVG/pVrlfbVte0VfskeWHp/LZYd6v//HgAIBRF2WRuUOuC0ImZxnd7i/Gz1W961H10HbwlpQDJZoh6P6dKG31LWm/kq1yu/lbXtFk+p/DrfRYHYaBpmyRO+xT+MJJpJpiCmooAA//pyBBA4AAgCBjZcagkoWEMmuxZlBWYImNdnTCTlARYmsXSQiqYAAAGoGElpuBZ0K2oOSms3cjoxVxcjkdRTxg7oECZYq6ORRf7er/bu3z6duyb39tPUX8/RJQDq80MxK/7H6RX1JAIFVVGc2U5ymDLTRpbF78/K10cKpcVODIcItYvGt+DFPS28q9QHV9CV0H+iaye7r6crP/ryX1H+/0BvZqY+r+n1VdYQAgJ1SjFgdqO2phFwRaesnJtCbqTEiUSGWXHAZvEYsm570lRp6P6v69aep39EOXtbrX6KPNAKhWv2f5FEHv0cYf9SbHhpMl+xuSSOU40rt7Cdb953a55qjU1RbL3934FyXsH19+R5IXpTPS+H1T+qW2BpzNQNY324J/X9MpP3QG/1tsWI7EVKfVWmIKaigAD/+nAEBQEADIIYNlnR4zuQQgPLSTzFaQgsU2RnjS5BC5BrmYSV0AAAgBCbckFyDRgsjCebJM8oyMFC+57YjXi2Be48HJqKy2ij3cqNfT5z9DfNJ1tZNXbnm6/t/yP5Jqf9q0bH/v1GaOgAAIqlykcQxWOJcz2y+NulTsYIq8KAc1OsG76xiNbkg6umBt97cd8fltYiQllAwebkGrF/d30JBuBvtWUfsW/0r5kkBpy09QEa8OEIq5RIOmEzYRAteETcZpIO3uHBb77Q77uJ3J5Hbvjj0NyQ9kXf1o0kOrex3Xrve8hMclYhLN+uvYQAaqTdB2msPvD6xVrP7qQWaeVCJRF7KnVigkg5wGfb4EWakYCnVZwm3Vu7o/v4q2e9bpmCOg9U1aurd/7ed+y7v+YTEFNRTMuOTcAA//pyBDxUAAgCBjZaUewpUELkarBl52gIhK9vpiBPYRKbMHQ0Ca4AAMAFJbbUTM4WXyYFpRzZsD1A7d9OSnKWocfTEwzxmtSeJv6v5WasjI1iNSn7WSFdV9UTShEH/sKa/7fG/+/TrLmiRsRehhgMIV8w1/IEfNdV4VqJmnSLDcng+75r3AdxSZv5MeyKi0aVCBM1Sz0kDPHrbnepY3Wu6phjq9//R9n//3gZmLQ625JRFsAqPetLhBkk/2oB1WSIJ/+XW1SK/+al+D5nUy5azh28/y/+j1sCVBmlidZzJNS9qIbx9apu42Qd/zTvtACYE0Tkcccsz+WquuC3GByXJqY/jxt8VIlr6oISOlBpfN9fl8MVDbDzlo/r/6HZ1eygEveF6/1bS0HYjBpyPby4a0VzaYgpqKAAAAD/+nAEdT8ACAIaGFgZ6FMgQ2Y7iTCibYg4a29EnKrhDRksKMepGAAC7LWY5EJUyZBnjhyu4SRDGkJktuQCm7WCQgpVYHSfqFQzWP/Uc5uVfVr25GG2b4sOhSy+cfI6MK7Eoc1ZT+tG7RrAQHBaWbswjtZOzD36ZaNSBYGYwk9/UN2OXqgu+sMpVV0hXbUV6D+n79Cf/FnjxIAmQ2gX+s7Vq3VJOtZb+PMr722PQOjcVm3+JJZQVO3VtALFb525yB1cLH9g8GER0CvGArPlFsjtrBsUUKVvi9qMsWiq6b20bzLVOpcSXlCLPTRsdXv9QAAYBFqS2rEY2MXQVH9uJ6MDlqdYiFnqEarLEGbryAIupzXSLCaqavRe4wLtqSqf0/9Xq0pun8vtvFterRoyVXrR/0JiCmooAAAA//pyBL4QAACCDi9g0GUT3EMEeslh5WgI1Sds4xxJ0RANbWTEnZa1y/m5JJLo8LamxbYxkwz6jAXyhvhbbKJ+gP0R/T4J9nFoXb6j2qu2Gak6Ir3Nf7KjNi8FKz7HnwpZubdQYdTR8FQAAgAEqIMZ1L1vxsUQ5U3EJRLDKjRUOTJhMxPcw89I51itjMI/4+EnXkLA9qlNbj34U/Qz9BO5He1erRpLWf5/bu+gAgRSkmrrmeDynMEwEwsZKH9Rw30D/KvSoODpqSfT0WlTe37fAuRiMqQYjY6fJfm/8rdf0buW+HEFAwXFETI8Y5BUnRltHQHIKK3hyjpILGl5iqEsg+p+moSpgZXubTg9c8Ql15Z+YGPQhkVJZQGd2QrduWKYkK27cptw5hOVKiyzhG5vvooE1wh9CYgpqKD/+nAERWAACAIJFNq5h0LEQoX8HQzih4h9A2NHrE7hDw2tJPWctgCFgpSXQqzPPdsjpfA6gvI4bNzB4v4oD8bmhuVaCjhQvldOykM52pv3VGooAa/WVpRo2WtfaS9y+BjFRlZd7SCEJBMB2OOySS+1H3E5Ltgz6IHhMPh/j5fqRBNVxM3MHtmCuuYf08yP0+gXSi/GwNqORf6PktlAWwmzqcpLz4CG65nRSGARQKl010jNAYj0aa2ekhoaM9m9JIHb1Cj9wuJ38mA5kGdhr0QS/hvKjJVF6o3v8F8n19G/+//d68bDm6XDfRZtrf6wBB0FUS4LpNMjaFKT6u8twSQ7WnT0pTU6wq/QeGVsOCktWYIr+OdjFH7sZiDVl8OWhxjTP3/1YBeksOq1wCnNOU2s4tMQU1FAAAAA//pyBFlzAAwB+yxb0ScS3D7DCwM85mgIUWtsZJRQsSEwrmiyiscAANCggilEou3cD1M1qKFjKAiP0PKC3yhDxQDVWCECXbN9fRb5Rda//g27h8to2bPnW8o+LPjn3a8t10V9luLhgxWfpNlyuYQzBW6l8UneyAEVHxllaIRvugVDBurjNnoGV/TCvtqO0PgQ3IacR5bRWGHyP3b/V3xmvSQ9GzQEASUolU28IA8wp8Q8Lx6Q+41JYKBzdwzwoN3KBkTr9Pj/fdE6IvqJ+T7edvT6f/fyp83UE/ZG/6v6t6t52phh/l9gCA9JFFIuq0u7FiT480zBfVSSOuhEfrm9g4pPVYR5tCe6qBt3T0H9X9Pv8v0+J9W9G9P/v//6ewL0K3VPVvVtUduUNtResfk0mIKaimZccm4AAAD/+nAEWIMAAAIcRlvQxRQsPiUrehlnJ4kIU3eiiKXhGgvxNBMVVgAB0CBRTM7HL50DH/BmCd7kkMNlDegZ4QHzjnD2vb7ff3fgjMtjPtIP9fRvI3VX6Fb+6KnYvLZm4d4uV+3b6HbJTMGgAAwBJRKUy4nBxZ3WX4oMoGT1KoVIehauNC98afFj+R9X9e0n6nd0L/Z8/t9L5WtWXcsEXWZLJeUM098wiKAAIBVhSRRpxXTCakbCw12GHsdDv0GkVAr1FhUOAmESAcygsTAgskAmwjq1DHA+5Gy8oc4D/wI1NQDe+woT90EQuBAQIqdflzSQkJeackTSTJpzCiVcrOw+SRly+jSHQK7wOymHCguESAcz4sGwILJCJsI/UMcH6KN5Ty8vSX4EFieX5AoT6jK1nwIGE/lzSYgg//pwBMMGAApiDw/fsCMYED2i2/kMI3IJMJV4BiBrARMMr0TzDGABAALVaIg13LagfCtc0On0GwkEJCDBaQHgsJ3lwyK4sKqOAU2Uz5RQLtE4fKF4qQkLAim8+xZTqYUcsD7v/lz/r9cAAgAANVfQl65Z4sZlNDgm+DFKCqZQ7/CMOCwneXIrxZijgFNlKD9Rlq6MVIWYR59g9XUwo5b85/qy5/1+uZDQGpWPCaVVvRrV07ZofKZ7cYDtmENB2ymmbD1mqpUX/vzUn5HpjoYkicAA2s2SW4QraSbh5ZIO3ivXxQC1mAIqtz8n3s885Ol+dFAYopQSEgHNNUWGjGK3w2LzCBcQSRmvDTMczaUSshzqzIslJoxaG4AEazbZ0VKtCTUySxIHWPZ/UVrMFFd+T1P+xyUxBTUUAP/6cgRX2AAA8d8X3gHmGLBCgvuwPSMYCUwZeSQYYkEUB+6AJiAQvRuD0lSPsUsog31afm0O0Otzh68j5Hi1AQLGGBiQYCb1zAmZfJCBdDTAcpjm3IQoqxmiryMtZ7N3Xv/TLMQIddgU1NMiqjKbiPJ8mDCjrK4XOHqc7jOQulgEdYQiE4CdcwJiE/aUXRMBylw5rxqGiIqwipiIA8tVPbZH1e/9IANAAIqytQLjQfFvwGIwgbkRDDZ5kbPFXPNGUAi+k0cSBg896L7TzR0YwWUQGLAA0VoCZ0stk410MLK2RVjx1JKWSk8LjeAqEsaqACcEUJ/0eWbMhnh9OGgnuoZjqg0WmC5cFXgAzmwAKGwbDZu1NalpDhpS4oMYSMCzYBKzwtKLxjmuVUdSlke6+Btuh9w5MQU1FAAA//pwBC7uAArhyiPcASEaoENEe4EwwxgI9K1vIKRiwR2M7UCTDWjoBCQuOrPP/C0uTXHQ68Y2+dSbHtVCGdE09z4UOsnbbAannrDo8j7ICQ9jBZ669gsY1Rb28je6LVpAP2CUMuNqyU8C2R8ox/Ycc2DNG9i0jH3pJtVc9zOJ1WTe9IKOeKNItA56QJWOLIeKsab+wWJcy1jq70yKnuQpVN1hAkFWBZFlBE+t52MVeQ/3b8EXw35tW5DUBlUp3zLh5qvKS7dIr2np4M8bY1Ky4OA2hBNLCT2BVcj2npHvbvPuWmy4BW3kEKklwoaFWjAiRmawb0od3SK3dKmv2OV3awN9kMNt6q1Emkc0FbxLOnSB0qsySAILETS5NonCDUlYpMyzRdj6V3iTTX1okni3ZplkxBTUUAAAAP/6cgSkGwAAwhgYWQmIMrBEIysQMSYWCKhfaSMUcmEMEixI85XIAeBaTB3TEAOIf1wqIBesCIdXDhcmv0FPjWJX3azZz3x8mRLUSrjFnQekLXtWeKkVJcqeS9T+zZZ1mGtSrdnTxb436OaD0jeIIHjN4BCzA+FiPM8omr54IvJ3NTaW0XmfrnWWaOrGgVSAgi0LhhRIKklFB7DqQ0OebvCRFDsRYv67F/7PrX/1ACCwIALekTlEwQaTzQuAy3g/u7gYJLzhTdRANLzJ7QdEZd7UIInVoYYMb89FLHPGb5sapKd1GKLrx1Aazcs1Nb0Pq+ei6ArVVPH+hFc9Z3WxrUKByzERr1qHssiIVaBwNXPc8YQcm1X9l1r7mF3uaMH2Bq9x59j0sbRPGbfMaRqGJmLtPsmejamIKaig//pwBFS/AAkCIjXYsYsS0EHC+vE8aXIINGFgRiyrARIL8DRhJT4AgitVqo3Q3xpfH3dYNGBlW8NmVDFBA90wXP+5IhnSJRzspwxWVqaZdPRCmfauuja/olbn9nUbOoct/6VWaPsK3vt+moDpXMxlLNERJwsvmILQMZGYkNsI8tE142kE9jGho/nvTlx6SgiCRIomQVEkcFz6W9xKpzSxFk4imM/XTuuDuj+9qxJh03F3IY9k1hHVGuD97R9r2mA/uu7FxHu6gIDzzGTuNR5U68eOBaWFwIkQlsB2C60cuh5r8RF3MLXTxn9X/qP/UykgXEm3G0nHfNpMDfPS7VGVjvf48ucfW19URuSE7CKBCStvEx4WnxxkwWe4uwLUAOJ7n3R16PIZ0pNJaFrirgpcXV63JiCmooAAAP/6cgQ76QAIgiAYWTklOsBChArhPYY8CGRrYSYgUIESDWzck4mkAEUEnJJFgCRQkqA6FnWg9AB7CDrjAZ+YN8KOe9Sc6dIQyt1danHVIutJLWTXVao+yOUwCPXMLRtPLw5liKjfu+Y0k+kX3ioMVAokZZM0uYcjNlB6GPtm2S3WL5z7EMXVnOEjn3dJ0jiOkNbdeM+fG+ZznNLFrrsfbujIrPUa2xej5dPYz/7gwAX7+LBDKroWD3c15EFsATTMBJdi7Gb+QyCUb4DjIrGtYx92jOWSDFsUKAtbcHioCaSPJHEBUNMvADgFW7v/96f/QQEsolxRgfNJBh5t2OCsiM9kbTFP7FRm2IH8AuiOOzWKNeVOMNGyvO1kYDfDNRUVMOfMX7tZ7DtbZWp9k8bMOPDr0fmUpiCmooAA//pwBFnlAADCHB1ZUYcS4EBjSuJgy2QI3SFzgxRLsRMNK4jHnGgAAJEQ25LcqbrnhyMY0XKxogqWnCUY9GLVxSH3qI4wlYzpUd9pfBpjmC5mshisUZdmpvWPZTQ/a6zIp6b12cojRs1e0CWgkkRgJs0fISJ6QuQWKFaUrLCaeoit2XCH/ZJDr6vJVX8kPVVDVZnpM7CZeDBkY2xU66M+BiZ9SKwERuZTb9hABAMZJma/eeReVOsfEH1OR2EyS6C3kbjQIqNKb1fViaVR5K6fyu9kjXRLm9u6rarp/sM+pBFrhOh1W2O9SUuT6b1HXcf1KQavEysDBm8JuDAoqZ0sTGUR0a+pKql2BGjuBAlZ0KEXSPhyqtjGtKpWgYeELZNDWyL6mPlpYe0XvTI9w0SAJw7W6nSqhMQU0P/6cgTBDwAIwhEX2VHpKTBBhiriYQdoCISDX0espcEWG2tI9RWoAAGAgOS3/szLC2O0JtMPJQCSFTFgg740EbgOfoEx3UF+L5HB116TyElG2vX3SzhSywc++nRoROamLvUg7yarUa/0gL/nv1NxqVjhWZN5O0cUZF2VKFUUJ/iS5trULCak4Jr1DHmE9XRzn17KdvvpRfZqujGt0OyiKlfTdP7KXdtk16NIQABJt7dVHma8cIABpKHDID+CqvBjC8yYpzNyLSP9SvwS2hvVlrcr3Ye61FrKyWQPZlDdlSn0VaNH8kxCjjC1m5G6/jQNvxrTZFnHs2haxmJpmqGbDL4IlH4dNRYgxa6hiONdx8amQAdvUKF/EybTHMzjXVydXT/+K9K+9b0uVxI31KZK4k3qu/qTEFNRQAAA//pwBKHCAADCCxhbSYUzTD0C6yM86GcJGQ2JoZyxMSOkbEz1CapgQNGVUvbrjrAeUZtaIWsWxaZI0NfkH2Kp+pON3ZcUObfK6ccqL5qWc/mHkhtIjTlrd+6v0ofW9xhbmw05SXnO3QUEmVdLmJCwVB3tETxifUGg5Sp1qif4QN2mq5cME9bjH0NSjS5CEzOOfBdPMfT8/v3V/EI/xJi7nOVexH0Nhwua2222XekMN0Y2nd/pNzHSHQQItoiMi1DSm3Cx7rmV5vtq6NVUCjdxhbKj+n7vv+l+3iPeV33F+GKwsA0n4mFpW0tYDGwOAFJpSQXBGo2qiHK7daoMGg7nCpZ+QklMHhM6EDdBu/CCi8E/RxkohNYRrvBP5Lau9ZKaIf9eZv+z+r+f7fFsUbMT27FSxbV/kExBTf/6cgQXjwAAAiBJW1HlEtRCaRtpJOJfiBDfd6OMWjEYIey09QngAADBARxy30vPcmLnje7JKIvsY6rHeMHbQHZuN8f76IDGKrAzXXD3uj/I+h3ThCbJ/+3p8tunhSehOoZbg5R3SNKXUh4AQZFehvwbfwfIT8IWC04Yy2mMecMboDsslVHPQJ9Xk97DM2HvOCDu9L3z+oV3qie7//b0+X5vUR6N1BOeW0fJ4c1kAAB0gNRtJya+EBfxPM0HWPYxDfQt5V80oExjrhJ6p3xkPq8Oh3MzBF7z9H+Yb1fy+wN8vkayFDaj+f3er69f0AAABQANS27+R6zKiGnWJar7lDmVZvSHQycenKhzxJfVhcWdZwD0DeCZlqXoad9F+/sP6Nsq/V/X0X3H9G5AbKn8/S71fAKYgpqKAAAA//pwBPsMAAAB+BhYVT1AAESE2wmnqAEIrXWEeBOAARuusQ8CoAIAAIAglHN5G9kb5AJI6HUklxdI6uiU8Ind3OG3Zw1RsVRTNqVLMiR1qm5+n6tGYxXJ6ak7M5U6PdZof/Tp05kAAIANhZqb1A/Vg7CRNN/yifkjPCmXznIfG3YgA6i4gRSpuMWoYeFMSNyHehOdZV3zvkXxQ4u2vvsfUmjFclY2PT6v/oDAwGAwGAwszmq6////9f85j2Sv/NFg3SI/X/oJQABoYFASN//xEIk3U9Bov3p/+RcycJA2Ik1T////IDjTiB5p5Msf79QQEAgEAgEEv9VKf/////Onsn/zRMH6KIv/+UEMAUHoUgwBo//8Lgek5xp6CIK///41OJJwiBaJR+pT////JB404kPNJx+WPTEFNP/6cgSUPwAAAiEZ5H8IoAw9IkwP4RgACOTXdeGcUIEdGy/8MomkQwJDRIZJGiQAbvq9i1OEooZCiB0AVum9WDXdUTPpkFkn1LYkWaKmVs63jRowjClWILxj5ddRv+ya7U2taNYiwlurOseSCSgTXLfNFJJXHDz2bWVzOBQ5RRwP2/KT/qZDvJPL2Tz/HiqtvtIEiQo8syzclBFu2x/6mUa9egW1XUa6SERACKCWwkAgG2CnEDlPEGOXgIHUeM+N37hAM1lHBlK08uj6t8jiDb0fdPfKyKxn/RWQ3xass9gGRUz9xDg0d9Cw3WG9X7HogEwIuMvzZBAVeAXXKekYfFXKSMBn5h+0IBqqpTqVioeXR9SfI4g29H3T31orI/9WQz+Lsw6WSedFP0KG5YGfmRKPiV+z6kpiCmoo//pwBB4+AAACIDZe6METiEQGy98EZR0IpTNthZRL4QKmMHRQi1aIbBT2Kxsolvip4ghkv18vtY5M4MC8KLfgtXQBfnFNkR0/tmSY7K1HP7+j6fos2ymURcr2aueSsSqtrZ8uM4KjGJx66jAAQAaWOaxlEs0lpfuB+cRq7xR/qP8KWpyAU0tatIR07dkYWRzOytQy7X9H0/RZtqKKXbNmrsSsSlra2fWM4KjGJ1rqAAAAwEogrsk/A9ELfUiWEHxRSKit8N2MA5GcomG9gtvP9f10Ksu4j2+ahit6Pr8T/XVWpfYHWld9esz7FE9aXtR/5jpIIkgCWOJxIpOS9C+SlkFcspvEB9UQBzeDeJp5n+nKMMRFntevuQl8uvxP/7X7sDrSv1IRVmPZiiVN6/o05Jo7CjkxBTUUAP/6cgR88QAIAgxAWeHmOqBEaBt9MKdXCEjXZ0ecsEESFeuFhomoAAAAgACb/irk8mEXRtrfk2YfG8HN2XKO/cPpUTNypDsOkPX6n7U9H+32TVm1dN0/5Cv/v7Y9SnyLB0t+1+tn77bQACAIwm42knnPdqH3/+hXCQ7cuYKBJt36jz1YLjJ+wpP5yu3X9Vujp772Vlc+/f/a2harVrb+7PKc3coRO7zf18Wq5VIYIAFSSSZiM+wxUfi+cEMmbf2s4nkS2ChnjQPpYoITGi/YhfOX0f3bt9P+o0vRP3p6DQDqzTaTXvXWonIa+jffu8iD9BMuxG6Rs0Wk++1gOCrSNWZx3WWiJoKFeQTbrFg2oZxwI9PoLbq/V02PnzNrreDYcInY9WKrxemnNaNan+Ju6Eel/0piCmooAAAA//pwBG1XAAACHxRc6Mg5aEPGuzk85VkImNmFoaTpMQob7/Rwj8ZAABDsSByOWtswwId/gHsi+QocbyUteUeyKBpZalXiQcyvL4q6SMpMuYfgE+ru95ZDMUauV/M0Nih00lVpA2isBUI0VAABBAyLYjMryOVDVvHwEHVA5TBJXQQBzceIc4WGJhEO+gZ6v7P7s2kyXKdrz9JudF//j3n3ofes7DX1tP6qf2LiQ99TQbFWskkcbco53gjb7GE5j0m2fyhicfZC7ZxXspdaRABXuhrcV+g82if/23MfVL8zXaVJS1jVgVmNp8RFUrpf36+Z+L1IkgBVlJxtpSSPWcW8aVbGTKqIBb9vRuokeULNuz9Cul2fS5bud5h0ToGlziY8crW8eBWq6hH9pCq/+2xA9ZjgjJNUmIKaiv/6cgT+igAIAiQXW2liM8hChrspGecbCDBTa0C8oSEUjy90VBbGAAAYiACTjksvclglll30AHZ1feJhJlinpnfxelV7in0OlcrMl7QvY1wTOXDaElY0NIodokBI/ZewrUwXHjqn6HRMf0awAAwmRVnTDnSidrP/2CruVMXErxGdRUL/E5GpzCOW6CE17hYapR3O8r7mfe+rb13qrrv+zp6FnOrqff1aaW6zf9mXTpEktqU06iykxd63riaRhIWHFZ1KGsuHQfwKPe40DHYNtq+HJZ9b94sOaoDVEQHaUUZyXyPr44WUC36SFh0bVyTCg0Ca2m40k4sMaIgdaw5Gvjh6uzAz8I/CI/dsD1XuYWesq+UHrOVgslp4rDdQd+aPjRjymNZ0L+LFoBsIHiKabOAUP+hCYgpqKAAA//pwBNGfAAwB4xfXkww6sEIDuw09JVYJAOtWLD1LAR4V7vRwi04Ab/xik2/U0hMWdZ7nJDGGJf2sgZ/vw4WV2g4GuhQZPcxQlN6A4ZMadFB6l+cItf1i/L9Yhp55lud+3/0AAAAQgAFuSSCyPXbkbiqvrbwIe4Hxe6DcGmKTRT+E38KDVkUJ9UHej+g+shmmrpnJYfdrQ5Sa1QL/pyd/t2ct/0Ug7MQKp9muiHkES2zackyKundQmxNf62MoZrylMECOidJwDgdIVNJhXeji4GvucncjKNRS5ndW6v1n7UZ0qurV/b/yL/5n//2gFgFRlNNNJyz2HhR5jsXqPG6D4xbKt4gL3xJc0xTgmbqnYhc6ksPj+OyWBOpoBU2+ALaiz1/6C9bo4iTtQ8UGiwSTDVTqX9KYgpqKAP/6cARd1wAIgg062JnqEtBDRTtqGOJtCIClYUe0skEFFKyolZT6BCct2kgsiroT5A+J4xMyguzBsnEoW24zCB7wqF7uDU7qiC//IXzeze6/AXaij9PWo/kY1afDsUQyHzPrP/dV/r9YAAcqNy3btLPwNLM9YwiqmHcVk646MeKnzlGz2UqFfn/G9O8zNnV+oV2kD9bkGD9QIqUw4J6eQpJUiOmYk0sZujOn1hgAS3LtJGVKgyLOcOIlaB9TMc25Dxhz0y1M2NYhxR3cQxpygBfUJL1+h/N6n0jU+JS6FWWg9zvWEaN7q9LJnFvvT7sgEBIKTclaYg2AATO9YNmj0d4H1O3QIbOfMC3/HhnwE8gCepvI/p6n9Segkv9AmQsLPFKFv931EYfvJK1TSRVlRjSmIKaimZccm4D/+nIEtPoAAAIdK1azDzrQQgO7bA2HE4gIdYWjDKJxDxpr6PWdWACADfmcDN+5cyKBdegpc7I5OdOz1hCXpa9l0/xr1BDmLj7oX4+qIa4C7qjCT8z3foQ8eZ+pDzl1HW57DeyrX9GW0btxIBBEIhWW2FOtAud5qg5mGYyDweWITa5QtWYEwMTUf8VBduvynPVBGhFRKXiwiwR1Ksxb4ZrfW6UNKMFGNwxVRZ9UhcDmhUeckue0gTg8njEGY0JCg6QYa2vxreAj6IG/blGBUJB4UaQf5Spu6oJGlvQWxMLezM7q2xmvE2N0YeP/2AABAAW1bbl2tOWRNxYfNVmBvQFAwhoaH2xArLPyYM8DW3gEP3Fz9fhE1UUsjtQ13Wn09G+3obfK0jX1p9WrKZPZ9/9aYgpqKZlxybgAAP/6cARMZwAAAf4i1zmYOiBCiuvdFOJriOFDcaQcUTEbKGvo84ngAIAGnLazELBNsCypq2aJH2JXzuCtBdkkzXW4LQ/aTaylBavQRvKr0Y9bT03HjlWV0vvrD2W3S4pbrxT53+jXoSCYLqLbqacpZGhYW5Vh2dqAvUkbFZb1b32hQMNyAXUO/OMQtlIRWo//r4I32/6/t6/N9fRv+jV0tX1yv7onGvXT+gAkhGIolJJOaSyQjWmBQ8RbxglkTo5Un4gfuJL8od4mHfHPhh/vuU3VRvHL5vV/J6obqidlXr6/Xbb0duC+r+OftHT9LCn6wAAwADKt3lcHV4xcFtp1CbQVXKFo8ppEGSEId4CG6FvEA+3BC/I/K5ecf0/+/p9fsr6o16n8lPbqvr9vR2/fX4+18++cV+tMQU3/+nIEMRQAAAIhHVlQzzokQmrLDTzlhgiIg3FUhQAxGBds6p5wBAAAgBJKkkZEcC3qDXliA0ase5jEEPYdDfjzdBR2ExPuFhw7nv5TLZPIb6sprSoSw0CefYs81b3kHqSQpbZsrOIXlcvIAAAAUAANu3/up4kUn0By11oG72vPqeN4tIahycSjeg38TB6e/n9Qoi6n9X83r9Pk/Xq22/n/9fl/+9/9PijJr8aisBSOSSyUlInbLhLlwVCgqO5MJ1nkYbT5hAf42LUMGF+cf7eYWZEmETHlP8liPRhx8JFlaF0ZNEOk5nZ6nV6Mmdhsv5+oQAioVHJL6VtvvWC1JYYmURcz3ki+OA/fhUm6uKyRA+xQJz+Pizy32LUVX1p7Mf7P0P8uzpDdn5Q368j9nqdL6MTNk1+fqTEFNP/6cAQxYQAAAiE24U4goABBxdw6xBQACMCDdLzxAAENhW7XniAAQAAEAMMMMMMMlcbg7yI6kv7SIqY0zP2PRvo5TvT8qo6a/5hZGIazp/xpgFGEGiWj//jDT0gXFf5qGAUYz/9YFKDFBgoAAgYYQIBAIAwIBB++DvJ6it1tIiphYs3jjkRv5T/8qjHTX/MLIxDf/xpgFGEGiVX6nvgX/zUMAoRZ/+sHQIMUGCgA+AghRgMSqjMUa6qpK+pb2kjUFmORjFczv2n/tusz17LWyCpFqj3aFBzxTCbVPi20wRa2Ikt+bFHjXsD7O+NIyLyvqZfWAfm6hrgxp2lztdp1LG9rbktIODTgqlNa6KCrwEf64ikW39oMDnigihNqnxbFzBFuWS39TwG9gfZ3rGkWEXlZ3UFb5VMQU0D/+nIEm9gACAIBNlyrCRBQRCbMjQwia4ihM3EnsEPBFhrtmYYUMAMuW2QGFB0UIVHEI5Lsm5wQXoAVYy2EbdFen1rWWn8iqjKqZen9avZrfK6Gct4OwOz219f1tYoXOkhZPsF+f/WEjI3JFJEkS4AgypdDMggdukZFueZvkVyQqlRQQuVX/kUoIiqlS9P61ezW+VyGct4WwOqPTrX1/W1ihdyFfYL8+mS65USDaq9mAXmFrXnK2gt99Hau6xk6T/3ucSGZ+gt3mkXVsfly+9C1vR6PXf0Paj66kenwI3/pL/f/LqX0d+JTryQ12S/dYgAjSrNJwCQpil5QtOQwt79kk498gdZEkoZ7PUDu9wm/VHGA1n7NzrWvR6O+/0W1H+pH/i34l1GUsrOhM7vlevat2S+9yaExBTUUAP/6cAR2gwAAAiReZe0MQAxBxrsxrJQAiCWFdBmDgAkUsK5HMFAAELklksr1sszjCBsuXgkZOKeAbzVKyLozPUw3tt+lvNei+j3fddCjVy+rb+DL/63qm6ijIdJfzV27S//v/5b9q0jnn3ll/MuEbpbRrNI6stnFm08npIOmqW3+ncPEGHFgZiOPFmW4dAnElVEVqPKuhV5Fv73e9/Padtv37+Nb/z31e479P/qK3SFbdgOU7USrSuelM7esYXr/jwwKyAXV2Qw+OmI9aOm62P7o/X//////////qff///+YxlCdjCH////7GGGDdIBSSUkaak7Kwyz/W7EG3Y9jM8s3r9EqFOqArSGibMh9x+IPs592fqb///t//7////U7v///+xGJILyEDn////5CEIBwJuTEFNRQAAD/+nIETMUAAAIoVmNuCEAAQyrMGsQIAAikNW7c8YABDZtvN5IgBAAAEAibhcMNqMBgMRgGjKqE63f7pS1xZSe+jqvulE+5mt0s//1RBCqv/+yBRJWEKT//9GOUGZEcgfR///wYiIccrmUgZwAAVAP4nAICwIBAIUKSIIxrLTWa/WZFSmfbTq/dPRHbo7fy//rQi1/7+yKXJJ//+jHKpqOwfR//1+DJEO5ZlIGf2JSA479ASQ4pLmwf80rJmy7cKVVkD8A1UhIqk4ggfLCOKuLiNThdy4rVdJtDb3cXG5LEvk/5YGTvbPYlO4sFIKhsFTvztYSAAAgBba0rAJYNSkQZlH/K9jW/MD7GLWQ56FJzDe1URfE1qt0WrdrbZbFLe1UXauUq7KAuvWe/g0GvltbuWwVWGvlcqmIKaP/6cATD5wAAAhc2WjnmEtA/JNvJHMIFiOyvfVSBADEXHy0OnnAAAIAI5LbxMcTnoE+5TGrhwRABPgiX9sV2cGjMf0p8vD9CD9hGS/opZVQqVm0Z/69jt3br/oL7Sd9Xp4iIIKd29X/2UAIAEz01wzHADw4qRKzm4BzWi2RLpdlmE6WqNFdG60cs7HR78G6iKJcYsfyNfWfYQbJ3pUNFxrUzfxW3vv4PaEJRhBMbbcLm+gtZSxM38tyAPVdX+i6X8uu+gPqacqs6vOxrMRttYV8yigDCSIspcWcFRMMayHjUHgg9IEWSY+pBRRXH43hlwJju2yLZ1g3A4FG5tc2atfixlQ4xlMBeyuNR10Kn2z2H9R9OY2qtSn2Zvbb7Gd+rGUV063T5ibud/5VZBdfEJtlDf/t29aYgpoD/+nIEbn0AAAIkEFzWPGAARGEro8YIAIhINYG8IQABFIVu65YgAAAAAAICTLLbLBUApD8ZCVNZGbR3GKyw41duVMgo1BqChXxqBRWUWbSegGkCTq4hlWrcsiLQLoe83/urA//neTP///m+gktNtuORAJoOlIh4FK3llHmkzeWo+AhEXPGU2FYCvSuNdBh72L1hpUAir3SqdqVuzh4kOctZcjpWLoHClugTn///jTfQyGUAJdppGSapBinV1JbR+27gglCTQeDMLrY5BUup6T4lqo70rCxo0JrVcPCG9w4Ez34x24mndscu5cg9zWHGsbuIAcU0IomQBD4UgZr7fetFvNroIWgCoG4IB0q1SJxzRHN/6bxORMhFi3My6mppICb9BC5CwqbkKFIUTMMaoyhaii1LPUFrUxBTQP/6cASmnQAAAhlIXejjETBCIyt2PGU0CKCVbUWU7wEKjm2c8wigIEgIAJjsKQEa0C5jqkGR+AIhsV52ryWRnqo/vQn2dzOVt/9syPXq3//M/Rf5VcpfLWpWqAsUs3UBr8lRDVNXrh3BpAwSvECXWmpXkfHkp0AcFYZoH66im6WdUGE3H+8YCpERK/8AwpbJfyz4rypMNflQCdxK9a+JZWGoijHnUxdwdqBo0oDAAQZd/9d1IAXCD06g3VLvHJXNwLRoW1GGxjPbsbnu5QI6Mr66N9p9C2GdhUdeRgatyyzyyyLdVsidHD6Dorc36CP/5YQAQ+3/+okFqLeTZbY6Q9ZyiBPKhHvB7OsE1SjVdPamnhwk0Ok0Jg6u+mnnwOjNofWSX2TsrI61EkFavV3fbIqCrjyYgpqKAAD/+nIE5G0AAAIlGVrVPQAERIXrR6ecAIhEHYlYkRBRD4lwKwYwAoEAACLbkkxqsMt4bjJNiGzRnCuWRXY8W48cfcVy47suqQwm7jbcIji1OiICxOB1GoMxlTp9NhlDoZO9KEqq0O2J5j//aAEkMuSRgco74OMnThjVGh7TLxvZvMarqKS5c9D4RlnPItjx76H0Ot5Y6mjmf39vdfvy8y5Ys8mdrVJRlep1rFdy/02VgCIEkGBCZ3S+74AREd5IBsQ7uKKEqXnWMIi65IYs6sfdTujzRQXCTqnPAoLlb30pcs+5LEvsOvFzEihiq99xgDiyAAEZFeozbdttvgBsLgGLcyhUjI1JlyvLTmXHIFh97YNhqHKMsRYtCHECQ8ShYFyZdrFGpO563yhhLwcDWAGutr////QmIKaigP/6cASQkgAAAiFd5x4A4ARDi7wjwRQACHStd1xhAAERjLA/hoAEAAoEFz2Gw////////Vns//9X2//744eTNG0aDg3167K7Z+YpEjPHBQLT2MDP/zzFcbnuhjIY2TJCKJQYEsXDg3jg3ByABgKBjsNhB2vM7K/Z6////f///6saxv/6u9qf/+ii5TQ4Q/67K7b5FYs5BAVOyAv/S5LnO8mRseLB0PBgHHoL0OBfQBJFSckZAD1ywIijiO7kbp8NYRknasrs6kUV2+rI6q3t22mN+xjPV9RMztJbpHg0eI/lgK7WDVfPayND2MSklLGsEmqIQYTJJdbt7CQYncagzoAh2Ac4KFq2so3mnUdqQ4jX9cfWMC79bkuQWes1ETycS+35HiI8It+4sBXaw7X2ayO9jMlU/GJiCmj/+nIEDucAAAIMJ15QYhvcPSMrqhhHcIkA+Wx0woARFpJt6pIwAoJITBMbJBKdAFcE9h1NKWfaqmBs73or7t7Yv/DKb1jRj5hR87RzwTCYaP70bXqCr+dd1ywVU98iRq/W7Ku8lbKgJAAVwlHZG7nCzEOXrDb87Uth/E5BFJkadC+n3w1zyiqUMlX00uDRrNFdqVBV/Ou65Yep+RI1frdlXfkpUBBKOW7Y61IIVf1Vx659uT2/HbSPPZ1d0ZKu15h9QAIQq1MQXqOO0xHfK/VOr6qXxH0Tq+1/bz9RffKbd0QuUfercvQHnQX0X0gAAsIpJbsnDFwobiuwkbUypwzMgnkokXh2GcORQEWoSzV2gOF0+yF7ReEdPgtd35VqyJRYXandTSeptI+iKB6X2fat1HpTEFNRQAAAAP/6cASvfgAAAhgh4FYYQARDI5uXxIwAiKl3ivghAAEUFjALEFAAAA46q6TV1222+4A7dPqzt9siHk58CFdjkNctEkHLu6XIuvcM/L/dRQcLGHWHJseQIkjgwFuwMOrQ/PLAT7LW8ukeBwABEk47JI7aAKIIgPEhIVGCFEfdnZ3mQMlMUYdS04BPczmfdbPHSfA/Li7yISsIAF7zciUODE+52Qf1p7Gu1rLGkoYAAgALRYLRaLRoqAFgO6Z1N0/Zk/////ciuc4U8MZfziCEdCMYCQhjfz77oxbIhHUn/O97Cz+h6Hsv/+Rsjf6KpIuqmLBAUSUUUaQWQAg6ALwwxMO3FvCmk7MZ0/s//2503JzlPMiszrnIRnRmQWIA1JNA+kg1wgHnPPpQD+xQfG/zkh4qgusTNZ0piCD/+nIEkv4AAMImGN1HYEAARCFbkewIAAgA124HpEOBABstgYSIWACMBYm+joDis1oqs472t28/5vKtbzNsrrpRnxm2RlLjPKpS7W4mIxZ9uIRVyr4uXegWW5ggoFcgz73VvcQR+7DqV+V3I8mmGAAaq9YTjGH+o68v7+v7llQHGtqLhtRhbFxKHUE0upWMEwjFntbiEVcq+Ll3oFhdzBBeK5Bn3ure4gj92HUr8ruQTgFcSyMLxwmX3MjfdYmzl/R2dL2VhqjNn1bdK9H9WYU1y6MjzdviVVjNTW/fcKheqWN4p32nGIloaf1NINlnVrsJlLUjcyX4J1w9ZVsxGhtB2f2LGSXi/dAFqpo/qzCmuXRkebt8SVWM1Nb99wt3w6+pjqayirp4qZ8VFBtYcySYgpqKZlxybgAAAP/6cAT9ZgAAAhs2XEsMEGBCCaxcJGIPiHkBkaGEp/EUDOxFoqFgABAMRu/JAD4BSfK3RORoMt7uf7LmI7gp2eyxUGzVFatkb//LUrbUd+rWqT3RHypT8V+LA1eTd9tbHqDnZWGiOMensQCkVZnL/2tIcC9WU8T1ypiUcl3SfTuYKXprxxv/8tStVqObYpVy/MiPlT/Ff/oY29Xa/+y61R30FNyKlhojWMensQEypC5VLrJHQQlTFJNxCvrFxEgN+pi7S+KqMEVo9Ac2yrUjrMl6GWn8xpf60Q0rsNF01anS3nZxUsouq6rlv/p/PPrLD+OMAOVXK16URCPP7DlZe9dhGMI+Fug0GCiMuOIQKFg6Nrpv7eAgCOyM92G27LLXERd3UrJxfoy9zPV7jyYfrTz8f93MJiCmooD/+nAEhjkACAIJNds7CThgQuMsXRglI4i412psMKGBDQzshYMhYgIAGW22oPkWBVILLkQlMckTqPXOK3FO59n0Yen0P/Rmhr87XtbMPW/3fqfPLm0a3U7XypLqqRz1v2tRO/Wr3/f1BJBx1tJSNJMmGSaqpQgjK69BB6tbo1jVtr+PTXTdV3qtekf+2FW8LKbWpRiYHveaEwecqyEzyhV5Gx5tqT7XHHRRigVJZbRANfZ8QVw7lcsjaBahTn40g0EHugmOIekTbEhr76WGgAGSvqnoyIu71T7p6Mve6JRP6jtepFCCXxeteS09fW+77aR/BGU35ihizvt3hyluMl5cWI2/xGNgbhmbgSiGoOwy15HxV4VNw24ZPEBW1YG2L0SdP0O6vkqg6zYdd2qPbXSRX+iVTEFNRQAA//pyBLVmAAACBCveaSESiEOpq/0cI9mIzLFtRJRvwRGa7miQipIQABFFpvb/fpDDJfvTg7vu0VhWP1WpaSyIzPO/tGXfJbpvu9kT9/hlJJkRV9Z0gtYqPx8z/veAqxKdrVY66JXeoMgCFkkqNpNoODATFzyt5e4yONfG7aq3RyrlKNqf2aNk7xKeC3MWXuL3OASZgLgN+cldcp7/eX/ngq5fYQUxqKLK6w0AAAaCt2/4gELhCKSYZRTigS6c3rwx2iDXQ7ZnCZx3tpRBg+nKjePZ9m9+vpSUQzmteRvUoP9NqHPP13ZEmpVDkbtvMfFpUQBIQC3JJSghiKXrHW2oiio3r7hWCF/qk7qPSdnS66zOXLZtB1bZFZ/l+sKDQrdmpe9tB0bfSR/c19Ux/PaDkZqBa9IqmIKaigD/+nAEte0ACIIRK9kZiRLQQaWLtxhiUogxA31BHFgxEYyu9GEWVCAnbbS2qLA7g1ZiH4ZYIqX1RA2Z9CbPSOLfjl5djS6zaHwTqivtvb37X9+m3RASHNZvPREmmmaaLWadXVY9/+bd9gFQSpZLbpK9qU4YJd0NMbBd1HPo6eM+/7Gp/7/1d/Q+97EHNDgmGmm9ZDb62L/XDaBAKwMER0ccNLEZA085VrtINicJRttxbMVkd48xqtwHug50ZqSzl0FZnRQ09ei0pHvR2XR+dOjqrla9O3Nc2Y3ll5qvVKAlystW9ZaRb+0xFm7qgJGE/YimbWz3MSVw9dvbX+FKdkDByrvknGfL5IaK3INB1aMVLMrebWHxZxF3TeQVUsc78v5RuMsB08piC8g9yWn2X7jSYgpqKZlxybgA//pyBMr2AACCFx9ZGSYskEOIa5ogolwINJNq5IirkQ6cbejCicIAyW7ZQYibAmhorh0oqx+NLseg960+NRH5WH4OKJq+L6uqgTfnIvGACh6jMTblKvJWPtu0c8h7EbpEsSX3mvu7turUABCYJv+9sC2WOUCmWRSWoSPwvuMcYlH3Q7Pv3xn+pfT5erGlK7b4VqZv/37MZ+YyMarWWvlCiQEeOrK6/1Pq9HO4UQEQFJuSTwiIEL6i7A5VYUMM6h2ncOx7WLGBI7oDAOcegAj421E929rWHVvrYw/4QcAGChKrJC8fst3nnMbV/4s/7AlBKcstho3EaoQXOfi3Nzl+0wQNucCGX0lRy8fmxWfmT3/7N/3RJjs/5XSmbHREsCCYwphenbrzpM8ISXxjKFDu5TNyYgpqKZlxybj/+nAEzQMAAAIaPl7QJhIsRCcr2hglN4fs+WdGIEfRCR8szPMI+hJ92CSUUlopzDIX7e2iZy+FdLA6aIjzhe9DP9l9f/bq3U1HKGLdlXIb0/6P7QlwgmHGXlH2LLNWKMGHat7hrVhnOfpEEiIJNRyV73ywEpxWJFWEvVq1oxsupeao16PlK/X19Wft7m2Eka5+z1slpEa0YuNEwkB4s6U4SZfFB4TVT+w7ZtbWk2AAEBARbkjDsI1FMiQ4mG3OI1sE1OW5mwct/Eqi9OVPitXz7c4Sunr9vb21MV1/dfdtP36K/Ewmap5FXqd9n7AAS3JK1mSChexxtOWMOYtREy/wPHfEF/OB89QGxjagXNkESenp/7e3UitYt/zP6N09H9XyBanje0igyK73/oTXV+lMQU1FMy45NwAA//pyBMilAAiCIENYmeg7sERDG0I8wj4ISPtq5hjl0Q6VrVz0CRgAtzf+ErW5NPxQSYWJWyaJbUj8xqQis0gFRhCKJXJyIjoMZVZUJnl+cfXR+j+W9vWiTv+y9U9ur+cz481V+VdRV9n/6AL6aSC6XWUJHeai3DvhfS0P+Iz/ALPXLsDwyjhtQH3jtfX+Kow2o1YHyIzqcXQHlNKSjo7kwxOLUNf6iftnC6lAnX1lyglqSSN3uWlwwMhFhrqL+CUGwLz44HNlYa+eiNlD81TFDG15MdflKaP1Le/UpupF1zm0R72+f2MbU3yunJbv+3d+tABkibcC9F0zMg04rupHI11lPAfdGO3UcK9DDPp2aMKt1/b/Vl2c26VGX2fI8qNDVfiMY/EVJXaSWMuoPHgKynPYJNYmIKaigAD/+nAEGZwACIHgKlmZLCh0RAJrRzzHNoi1bW70Y4ARIx9sjp5wAgCm3JBmFRYORzxxti2kf77aUM0q/oER98fiCMV6P5RgWOm5Bj3q3vqNEzc3jcTZKr5zyLlP6nf8zX/VmQAgApSSLiXUiFXE5QsqAXti66sBhejikmmpe9ELPL0HzSqmfrD9W3+F1REDRwsyeZBV9LWO2lWLLPt5JdJUZyJa6VGLsEVJdtOUTBgGeYtznSOjGRwM2jQv76zWe6b0Q5ttv2/2Ebqjc0v79W6J0+7f6E9RSIh/IWo7vn9zF6H+d1NfnO2lNW48cAG25IxMz6hjK8co63COls4XtuOK4AxBnHyIvVZUkjmlCJZ5hr7GVSe29TGyyTOlhGtq1qP5/t3O/9uvoemVKZmBn27XL/s9EsmIKaig//pyBFlqAAACGmNevgxABESMa7fECACIMJF0PDGAARSJLueMMAACAdoySyWwBgVwVIgoK03Iz38zWVja7aJqXvsdrs/Z+fRlW2u97BDc/b1u79G9badL0z1////nf/////8h1OQhAM8EAAAUi2JHBAEBKizXOD5ARk+L6uLVlYGwJ2PUsyMnSbYpWyo6UL/avr/b7GtfW7v7J//V5M53////n//////sdXIQgt5ABYNAdFV2d6od0zrqiIm4AAACF13SIXeuiF5UDO/7u7om/wIIA4PwfA4fBBdTqRGfWD5+JwffIIqP/sb//5RFb/rkAEAAAml+btCB23b3b5+71ERI4AACETcO5wMLg4CBMuH9YPg4cPqBAuH4Pjz4IfiN8H3y4fyCJQP9DpAnmOz/lAxF3/LpiCmooAD/+nAEfYEAAEIZK+BJIxLwQaMsBiQjRAjUg3injEnBFw1uwPMMuIAkAARaqwuG0bahdqW3k+usmrczFm21lsy41ikpN1G0N0W75lxO3ZDLc0YRIHpQoM7GLqToIoe2ntEKo1Px7+wy/sfAABCqqJGK0bYxbTU4cNYmoYps7Ns8v/7WCekKgkp8VS8s0S9wVSeGPMtfED4sq1go96RjzK9+trmIa9PT6kG/T6QQAHTnaPi8eJLtqzVeRfeY4VqFCoKNqJO3xelWV3rASfkTEjA0dARK3qeTl3PXmZ07KMGKo1v66msAZFqr4wwImvSrc22tMnE5JdHgILTJd4RzGe9SjpKKsSXg6hr9NDMyNdadOhQR1RKMJBIBEnt0Cx4PrS568vteKERinIdXuSNapsBsq7jBG/3bdKYA//pyBF3LAACB/RldgeYYsEADK7A9IxQJQH95IJhAwRYQLtiQiWDEJfGaQxtCNxdLblIIejM7DZGpIk4ShpadMhISASmqB4s8ST7Q7AD8KGR756qOcpFtyNkxG1vbw6qkm+tlnf9eLGySXBxYlRJoYmoyJZXpWXQxB0iRjhVmOnmox1SmqB4s8SLa03CT8KGXvQzW9ykWucrZMOG1v8Oqyb62WdVnWGA8qC1XCpFb9Ab1jtUCJYK019zx0i77ELXZjIVFb93B2qFEzxEVWqNJlkvqMpLnRANCIiU2NJo6Hnh6oCBp9ECueqP1WsisXUxZAEVryGTPjZKOxZ7AjawYjYKNC9ESis07WZJSL3y3B1sYQeqOa0igXeeTkHpC8MRZ7KBe58mZUyxAdI3OIhpM4xvWrbaKJiCmooD/+nAEUMoADPIJINuBJhsgQqV7cDzDXgi4ZW4jMGMBDBAtAMMVoOtRsDZ9cQCSbXGbkwa9slfpJNWXQ5hKRAi2liB7tvEZ8yhrR4dFknGhotLrMqfcJssxkcfc9RfWiiR9myinbdt0+E1mSq50mqC0hOfRN2itvnfsx+Nnhf+zk1/bdotzw9X4a58yYUi9j0bvS2LzKjRRs3U6WPY57jak40NdNOx1nT/bpX+ZkEK76GhB/VZdL8uLItYRoFPH5yLb+XS9mGGSlYmYPRylKCh+Jg6oy5KkhM400Iwo11wujovXbV0ww0hN3iQcrc1wvqrapdAYlE0FB7hMrDyz8hOU5F/xwKt99VFF30MdpcpDMGNj1Ut34iqkY5ZgQNYKMCQZk2pDTHDyL2uxKSjN//4aZq6kxBTUUAAA//pyBFbOAADCGBVd4MMRqEQh+2MFiAoImGV3QJiiYRUW7Mj0iSiAAAlFk9AMfBK1H2LKytw3dccEkru+6r46yQwOLp3B9zEFF0E8Fio16/sJPdJCj36cWHCV0ShpdWLPUg0qMfY1u7MsRCbclgw9FrpVOgyZRUJ/+wb/OppJIvli3CQy8oHY5J8ZQ59GJzKhyWZc6QWgcdO7XJqTxdQ7TtRvTFCO+edgfiWE38jgCESckjdpYiAxyYBRanHP3yOyElcsiIfQNWzYDBpeVcoWxwIE5k0VHvD1b1yPYCq9MZ35JnLHZxFQuduDUCjQ0vm2dlgE//ikjOPyaCaYRUJNfK2IMZ7FN/iBrOUiq+cErzgitrZEnm3dFoa9Z8uz297o5ZxIwg6TRAiUnoBXvSLI1fVcz7W6G/QmIID/+nAE17wAAAIjNltpJRPAQQSbUz0iLAhoh141hYABGozt6pJQAIAAAACEZJbak+IlCaEYC5LzO1nkK2sjTB8IxPoRFR+YNVerj+99WfsVQaVuerLdUS5/SvyG1xloeyHEkpX9kVSz2f/yoATdluKZPQUFFAHCP9jhj6K99NS9LlXYejpZLs6P0LZ/07L+1RNdyiNlVmCSzKIeWYW1t0ZUhNnUSc2qyHWId/NA5Un4eVOASNPW00smLUleM9Idx+BoEecJ5w84NAoc4xbKx7M1CY6eF/7vQrpZtS+ePq71DJUNXOxhWpbSN+r9lv2uTgAIoJct28/AFBt5YjBjeomx7LSsKFxQIO6xJ3moPZ0de9R1x0V7lTh24FqVLMdF8MpkjfeptCEpTe0JantimrsTt1Ja0kXTEFNA//pyBHuZAAACHWFcljBAAkFADErACACIWKtsvPKAERgJbiuMUAIAIIMMPAMMB/ALCWxBUcDflvx4sZEQOwsiqrgTyPvSCG2p27f/+uv//2ef///oxzoRT///+zs1B6Gb////9yEUYZA8iAArI1e27bbbrrtsWwsSCJUDMBBDXray9RmNhaBGIO49bQqXYITbitDcdPUOpkgFOM5sqZir0K2NY3ax8v3/3tdeAEcPJGURM1dZSJG1sQce09XcFehAmKjHKrMY5WZttOo//7fXm+nbGnCJPdvLKSnQK/2kDylC+1UsRirLFsS+L6tYoACgRQbkcj7zCNHCEtxDFfjnxz5RMCw9TdjChACiFiVnspHMW6YAdgUSKgMSj0nS45wLpjjQZSA0BrDmz2ZT3nEbGGP63UJiCmooAAD/+nAErUkAAAIMGV1RYjH4QuJbOj0oRgjY+31DjFpxGAzuGIKNlsAQDFdrhUqs6MtVgtkQHqZ8CbgxZ2heZQZGdgL+ldtFmTaiCdqRcmBrQKeiWuv4lI0KhR/4i/X53UeqCu6tR4NeoAIAwQ7Lba2lQ4CPbWhA6Y8Ks9MGH+jW8MBou61FpdybLg+pFqYGIUtmxZRpoOlahKihuFbBeGRzn1VhzX0eV3ez/dAEjpRjabqNKhgsVUlPj77PjvLil+rnWU99CFvUt8ZD1XZD1SH3t7T5zJSuv6tZdvavzKyoEBhjKXw5ucw21lfxQDLl++kPgHEmW+saAapJGVcHrofjiY8LH4rm7taOdojOE3gGyAFvJLnWhwVpsKU3tKOJLPJ9F8YFZnfJUHTww2i4wZGNp9aDDmLQhCYA//pwBKGyAAACHxlabTygAEIDKzekmAAInYV4+MEAARYNLk8YMAJAAAAQAlu7f+ao7BbWVdn+zQ3KXWO1t9qBEHO4kHQ1SuIuF3Yz926lisEXhmrI296JNzl+KLeekLnLyFWM36LVp//d1QCAMdk2pmIhBeJcPiAnSVUY5dbfxd89Zof47rlMuUE977+0vHBaCrXqGQNZsek8sLBdS6LF691dTKre+rbSqunKUgAAlv7f3fAUDyfYLYCPzp4+/Tr1p1COChqOz60et+d0e3QvdC69aX+2hv3/tsn/X9KNzurn///9r87z/////yOjCHE4YADTbbkkkAhQoA9wNxPQFvFKKv1xx1XjgjBVJoFICc7snmueK/xt9ILqsepJ5hnHtqTppWHUEA4465qWAFTiC1WIRS9WitMQQP/6cgSarAAAAf8RXe8cwABD5yxt4pQBiBhlcaeYpcEbnywNhIkwgAABshKtbSV4jgsJlHGEWrSvzkkZ7GDplt52KTLYoDnoi7UlWuSpzXIWlPovMvdHXeiWcN0k9+Mf0y11LduKV4FNSXNOba3AYA9SaOY9ysJ2RwGZCB0EfqgxtD83pGgj+empHPqDLUcV1QYirnWhz01fTTpslLEGZKXHV7K+eLRqN22ygAAFrAv6Rt6t3AdNgIxWpdPOdH/Fz+4g632I3iD8vQ6iQZSVOL28XOgqMxKv89aIudDgdr8SjeV1fUs7yNv86SACllw+jwfUpg8m5pX8yAKfIdIJx3RnNpADkp5pU80LdIPC2xKPKBwiz+Te6dVzlMQtF7s9HVvpq3/b3fq3Doq3RbbVWFt9SYgpqKZlxybg//pwBDGbAAACDBlYGW9IkD6Ja1MkIrqJMPuBtFKAMRufLZ6QcAIENS0VrqACBzoYZxoCuwYKjGcqOu+hS/SwiPQU8cdqiQLGc6iUv+lajJQWKOlK20aaiMTJ6/Xiz2hSU3+S///2/pADbktvcSDJ610WC2Rzzl/zkP9LOyuq47kklnkvBr5Degg98L8fvxL/M3+j82+32Ee6dfVP1sNnFXR/ezbT6WQAw6oUpJJfJIjWeVG0DTsMFzajRzuQQ9DjsY3lo/apOj/dXl8jtovUv+u936E0lZ7jWVVcFS2tEkjl0jxZkAz7lZyJypsmh7KwQhFyS3TDlAUslmwXenVeK+o+WOoYxuiOrFEa9rhutqELpP8w5Md807p1Pb/pf6W0Q131z1QtLmRrXLOVZ9I8z1+rF62NuUmIIP/6cgTjogAAAfwYXb4YYARE4eyKwQyQiKyFf/whAAELDLC/jCAEAGGaERkEgAACgTSbBAyg7JxjbfpuFSnTzJUYn317zJBbCD1HAgGVDKoABBPMaS5Dpa20a9LP//nEf/9Inky5QAadeusl2zZ3T9gVGi7LYqWd5OcOfHainGVpQPVOV5xxFow+wLjAwkmJoDrWXIdJPueByGHxVZRRNZUXYNfzn//pJyaSkCBGQGSs60SSigiCMoNUV78Ty9zXPLTsRXOM8ruZwruDZKmtqguOQd56kGj7RHvgZ9281QVFLu/WYQ46iN5VIpqMP81XrgFRDdYR5WiACWQ7Ip+q3fYafHoTqr0dWXlnc7PK/CvJM7T45B1GexQ/I74GfdvNUFRRLr79ZhDjqI3lUimoAv81XrTEFNRQAAAA//pwBDTvAAACEkxe4EEVcDsBa6gEwwIJISNvJJxEgSWbLWDECVDEAmEqXQtSbBhauRUV2z8N3JZZI8h89hPJ7/huxTU2aX+TM5Bz8lNeczvHlmeULTz60ahmsfChU7XpPLO1uanvemYwABNASTmB1zeymv38CKUfONUTt4iv945biZIbfUPaIZJinErmi6Z9Txfa75JZZB9YVdX2L1uanvDqZiAACyQBGt0VFw7JlUDpc4IbC6xQxkhoV6Bz69eQeZ9W9ZRKUTbb7XoqKh7P5W7cNb6+0+zIdtMjMrIckLK1PJesJEcRHUE/ZabQAAIAM+5kUUGDedGEqbeyeDvmAm5pHggY+r+1D0fVvWUSaCNtt3lvRUVD2fyt2rDXYlYgvcI3KhgaoHCs7EQqdtxKKGqgZjX/WmIKaP/6cgSqyAAIghJAW0gsKGBDSAs1MQVoCDjXZyYkS0EJGyzI8RVgAAiMY07vlAGy0hRrMP5htuIosWbhE6rSFdBfX5cWVL/9DVblZJrf1bVfy/5f/oYzrW4i6pFR1y7hOd7VlfnpjU/jQAl/P4WCebNUGhMX6Le9xxvgcP9Qzcv6BjmHF9TetQpUdP/NVuqabfqOoVWtlFWn+Ct/1I5n+IuqRja7hOd7Vlf/+NAMH6/36iOB9YUqONh98t8DV/ld90Cj57y+Py6D3QbsZC//6PlV5mTqSnUpW3/bR+ghn6xdh609sqetbk09Cu/7esD7/zvSHjrcpJk/GMaMLyAQ+D5QAfgwQJDpEH8npIBf0+/VSvduyt1aoquj/tR2xg70jMTyyqdQhstRfu/TJ8POboTEFNRTMuOTcAAA//pwBOUjAAACGUDaOYcqsEMIC7wUIuOIuNd5oZROoREmrIj0iTAAoAjckmzcxMxs04p0IOTHxsL8VPsFgRNWLZyM9F7Fj3/Tz52ran09HkOtUq2qrfWgF39dFL+w2z1wqfxX6le/xQK0MEAEoQXpGKSU2UpRSbOz4zxggczgKOVqEfQ/ZZg3+ynMdFE9Az5MuQmo3MlSlW6cEXdOEft/B/kiruL/kLVG9vGKxuAJOKsam2nvpYNBqhwHhC4p4QbMJBl6X1CtoTsRSD/36tWp5Z1e6oRNHKZUr+1PvFa9ZRzkhm00yhZJaFEW/H7ROi7iltQFf/5rIsdKVhuJ7SAjbFaKG8/Sz9caPZvSMlc2leCAOxJSbafsps339tLkUE8i1fZE1bQdr9n/v3uv/0ov0wfVSjWf9SYggP/6cgQ9OwAAAiEVWujLOihDprwdHCLHiGyxZ0Swo4EXGyxo85UwgAAIDAQZSTsKk8ZJpSbibit8swXtYaoWTKEyr6NlBH2Heypzgw62rNj9qJ99Q8+nDtCkv7VbJWxAV1PXFS1YKsYzNPXCUhHZGnG2nEah7KynPMY0mmwPy1zAsHJhQ82xgr6E+xTel/JVh3PxLK5sqRQabXW9RTbgXXd/8+1U6S12z9iwld00swAQAAyRySDuCoiG9dH5kIK0pjuIp0k3zIOV4wNATuXQU9UtRt1Xs6tf9mahLTmWMM8ttalSznZPYxWrsQpf79vMfVXAAAAAbUcklaExL80JnBmvRIua4SmtUT3khMDOIA8+dC7PGAnrgl2HzK/npd1KzpZqN+m/n8rO9MUtatlOr/v478pzDbMqmIKa//pwBMZZAAAB/xVa0WkRUENmyyc9Ij4IFSF1IoRcsRMc7ygRiGaADNAFbrtmbzwWXouLrHafuIoZ5MH6ljJoKFNh+g+EmryeweGs1XU0RiEDkGtQRaW/pTfGe00sgpCxoz29aPqeAYANy22m53IUiCvxgSiv1/hFKP5K/2haFq/WFxvbsbd8f8gHX3tx/Klf5eprmOjpVFut13oHoi7vCiFbMK1UEaP9BqAVJxemtnkBSRrQZ6y+EjaCYqNEtwvVR+g73j2t7eNq3e3WDwSKrznOGRGHO/XlWk2pW/7yuM6UKuZ97t7es0PUiF0wSaaTl0JeuKwVauDvgXQOJ8dX1fUR5pw1vI3Vujt18nkWS17q2pxqOpVip/iZR8tZoxTOrODX7nLcCZSWS5TnuMySYgpqKZlxybgAAP/6cgRMhgAIwhoyWLnpEmA+4qsjBScMCJxlXuelaMEcoGxM9YkwACAG3bbrcx/LBSStolKgxkIaIvv5e/bJDtJ0UkKH4msK4jSRSDX927V/RHZsbp967ecQ54LQpmKhO/jfih7RGfxteoANS3YxYCQmHoAmMqBHurlO/Ba+PgRJrmC4UDOr6kc6AdZqhzdT5Co6bY5CZdzyS35w7XThP7bZVqzujr76dJAAErba42J4DcJquT2CwUwE4xXEN+7PGvOCyJjJVDdVlY3R6ZG+LrWOrVdTtl8HanL+J3rceH4dptIqUAn+ovUdt+337gkbLtoNLrD5abFWOhqG+KdRLznilfoB8M5nc4VRp2mHJ6XR1k29/27tu3T1r/V6JLSyou+87Jk8EyVspW01ILadb1jupG/IJiCmooAA//pwBMLLAAQiEidYmesqYEJGu3cJAiaIMTNk56Cq0RGgbuhhFT4gt27aurFEwKOpMj9sBtI3nUl/AfK9Yr+8Q3o1rLNhYroroMX6E/X+3QcWsggMzopJtFwhCHkbA2ka7qsd0i36v2JgsCm5bao0dxyCjHDbMukuJbQV4OZpbYNur69aJ5H7e3jedUvY73bgxQkqnLoOFWFngCEWXho5xQohHB375vf719iYACim5NTaKoSxHrg7h5rk6pRG9VbwB64VH5HSgSHcqdVR6m6N1J7fTv6dP/R6bd3Xsi7Wu/o/21+ptdo0xtxieirQCWm26wbEG0prcly8fF9SeSrQTiNn67GVejJyehXt3g7tkfo1q/F0RWrmc9reOnuLk4kzErtVIUECrtmszkHpWdGEPQmIKaimZccm4P/6cgRajgAAAig0XFElEuxDCAs3PQJYiFk3YmekS0ESnG6oMRo+wAQQIsJJxA64Hxc5wlEIRJ7Y7z+eNxMwphATqq1FMvo/UudDent6l3X1+Ou4lKcxVNHKzca69bcYSF0tbQVnGza2tVLLgAAU3JJAmy/cHJJOjHmLTU1dO3hC/u9zOIdWnDjaP+Oy+j+vv1M+o32+1Wv3LyarbRrbMOaTqF9b2tdco6/6Tjl9KgAFvv6burgjZjLonool4w8BDPqJn8e3+LG5WoXsbhWZMEbXUm3v///DE6plV03ZsGOXne6LbvzFbS+r//9e/gvkP64AEFALTjVQ+GPBIFH1meXt6N5XtUG2j/s/3FeZu7c/gvVdU9a6isqIFvttjZ6SBg8DYcUL1oV6DtezyHYl48AjzzbUKSmIKaig//pwBML5AAACFT7ZOC8oZEMoK5oEwgyIbPt1RJRJ8Qqsb3QRiC4CQIJUkifSIP+inJCoMi1QoNNTPL4cbEgNfCnEp1GpiR9b2f9PX19ft7f9fZJ5DKbTqj0uewgPVYo6mv73EN7e+hdSAEshJu3b2xejzyKtHd2zNnENU4b0deozwRv318nt6+rdG6jf1q+p36I6H+X6rmFqqsILTt6CaB2latKZmFSAFL2KoQACYlNJt37y73HVRywSP98PtlCxdcIRupX1B9XVHHm8jcE3I/t0+vT6ebqY6oRBHVvZ1uGCSrrd/nvTHP73ji1jZjakABCgCk245rUMpgyGqcFpR8Dbr6Qb1K2CF/q9PP6ffo3r6+3p6LuCeiII5WXo2oJOvr//zE0P8/uq+7RbqSwGHymhMQU1FAAAAP/6cgSaWgAEwg4c1ZMPOmBAJ9sjPKJYiHyBaUSwo2EaDKwMxIj4ARvMZ6aGBr9QtIDpjDcVggA6vmVm5ju0d4J6ufo2Iwi4+8EydRLVa7E2icFG850j/j94j9NTKtm6zZXt3V7qHXegAspyTOLvDZVhZqZowhNJ7h1KgUfwFB+JSD1VtAD3qAQfQSRVneZE+/Q3p/09H6Gbr6/bghL3OlXLO2hij+yV04ABKSySrwSGWFtQmKpwDJ12+uw9kHpUJBq6HNqLdAFJdcx7c9j6D7RUoXDYoH3LiokDtS2EmhFzszpQ09FsQdCN//+gAJOSC/IQ9AzGA4gWBoRAU5+0ZPu/ERusTDw9P1RsafPTTVQKTXhSGg+cfWiG7XRYaU1SCA+EbhqkyguQaw411C9CGz/WilMQU1FAAAAA//pwBM5gAA/yEQxUmGZIEkCCSoIZIxpAAAGkAAAAIAAANIAAAAQFt22iiTkwkUjEoBhonHUU2UnoZsrTZlN/2JJZjv5ObKSf1hLlfUpr+sJd7mlmt/nGlEu/3v7XOOY7llrc1jv/+d2A3+SRgBDJIJiGFJwaVYRJ5rDWH3UmBjBnEzFXff/Jf3mO73lNKtsJd5RZRVvNKkmkuWt7va5z2O5Za3NY5z+c6mIKaimZccm4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==
diff --git a/tests/tests/media/res/raw/video_1280x720_mp4_hevc_1150kbps_30fps_aac_stereo_128kbps_48000hz.mp4 b/tests/tests/media/res/raw/video_1280x720_mp4_hevc_1150kbps_30fps_aac_stereo_128kbps_48000hz.mp4
new file mode 100644
index 0000000..9bf2124
--- /dev/null
+++ b/tests/tests/media/res/raw/video_1280x720_mp4_hevc_1150kbps_30fps_aac_stereo_128kbps_48000hz.mp4
Binary files differ
diff --git a/tests/tests/media/res/raw/video_480x360_mp4_h264_1350kbps_30fps_aac_stereo_128kbps_44100hz_dash.mp4 b/tests/tests/media/res/raw/video_480x360_mp4_h264_1350kbps_30fps_aac_stereo_128kbps_44100hz_dash.mp4
new file mode 100644
index 0000000..19c4e06
--- /dev/null
+++ b/tests/tests/media/res/raw/video_480x360_mp4_h264_1350kbps_30fps_aac_stereo_128kbps_44100hz_dash.mp4
Binary files differ
diff --git a/tests/tests/media/res/raw/video_480x360_mp4_h264_1350kbps_30fps_aac_stereo_128kbps_44100hz_fragmented.mp4 b/tests/tests/media/res/raw/video_480x360_mp4_h264_1350kbps_30fps_aac_stereo_128kbps_44100hz_fragmented.mp4
new file mode 100644
index 0000000..c321586
--- /dev/null
+++ b/tests/tests/media/res/raw/video_480x360_mp4_h264_1350kbps_30fps_aac_stereo_128kbps_44100hz_fragmented.mp4
Binary files differ
diff --git a/tests/tests/media/res/raw/video_480x360_mp4_hevc_325kbps_30fps_aac_stereo_128kbps_48000hz.mp4 b/tests/tests/media/res/raw/video_480x360_mp4_hevc_325kbps_30fps_aac_stereo_128kbps_48000hz.mp4
new file mode 100644
index 0000000..e3e3ef0
--- /dev/null
+++ b/tests/tests/media/res/raw/video_480x360_mp4_hevc_325kbps_30fps_aac_stereo_128kbps_48000hz.mp4
Binary files differ
diff --git a/tests/tests/media/res/raw/video_480x360_mp4_hevc_650kbps_30fps_aac_stereo_128kbps_48000hz.mp4 b/tests/tests/media/res/raw/video_480x360_mp4_hevc_650kbps_30fps_aac_stereo_128kbps_48000hz.mp4
new file mode 100644
index 0000000..4eb6161
--- /dev/null
+++ b/tests/tests/media/res/raw/video_480x360_mp4_hevc_650kbps_30fps_aac_stereo_128kbps_48000hz.mp4
Binary files differ
diff --git a/tests/tests/media/res/raw/video_h264_main_b_frames.mp4 b/tests/tests/media/res/raw/video_h264_main_b_frames.mp4
new file mode 100644
index 0000000..448ad3c
--- /dev/null
+++ b/tests/tests/media/res/raw/video_h264_main_b_frames.mp4
Binary files differ
diff --git a/tests/tests/media/res/raw/video_h264_main_b_frames_frag.mp4 b/tests/tests/media/res/raw/video_h264_main_b_frames_frag.mp4
new file mode 100644
index 0000000..b54a4d6
--- /dev/null
+++ b/tests/tests/media/res/raw/video_h264_main_b_frames_frag.mp4
Binary files differ
diff --git a/tests/tests/media/src/android/media/cts/AdaptivePlaybackTest.java b/tests/tests/media/src/android/media/cts/AdaptivePlaybackTest.java
index 13852ae..0a6d5ae 100644
--- a/tests/tests/media/src/android/media/cts/AdaptivePlaybackTest.java
+++ b/tests/tests/media/src/android/media/cts/AdaptivePlaybackTest.java
@@ -55,6 +55,15 @@
                 R.raw.video_1280x720_mp4_h264_1000kbps_25fps_aac_stereo_128kbps_44100hz);
     }
 
+    public Iterable<Codec> HEVC(CodecFactory factory) {
+        return factory.createCodecList(
+                mContext,
+                "video/hevc",
+                "OMX.google.hevc.decoder",
+                R.raw.video_480x360_mp4_hevc_325kbps_30fps_aac_stereo_128kbps_48000hz,
+                R.raw.video_1280x720_mp4_hevc_1150kbps_30fps_aac_stereo_128kbps_48000hz);
+    }
+
     public Iterable<Codec> H263(CodecFactory factory) {
         return factory.createCodecList(
                 mContext,
@@ -94,21 +103,22 @@
     CodecFactory HW  = new HWCodecFactory();
 
     public Iterable<Codec> H264()  { return H264(ALL);  }
+    public Iterable<Codec> HEVC()  { return HEVC(ALL);  }
     public Iterable<Codec> VP8()   { return VP8(ALL);   }
     public Iterable<Codec> VP9()   { return VP9(ALL);   }
     public Iterable<Codec> Mpeg4() { return Mpeg4(ALL); }
     public Iterable<Codec> H263()  { return H263(ALL);  }
 
     public Iterable<Codec> AllCodecs() {
-        return chain(H264(ALL), VP8(ALL), VP9(ALL), Mpeg4(ALL), H263(ALL));
+        return chain(H264(ALL), HEVC(ALL), VP8(ALL), VP9(ALL), Mpeg4(ALL), H263(ALL));
     }
 
     public Iterable<Codec> SWCodecs() {
-        return chain(H264(SW), VP8(SW), VP9(SW), Mpeg4(SW), H263(SW));
+        return chain(H264(SW), HEVC(SW), VP8(SW), VP9(SW), Mpeg4(SW), H263(SW));
     }
 
     public Iterable<Codec> HWCodecs() {
-        return chain(H264(HW), VP8(HW), VP9(HW), Mpeg4(HW), H263(HW));
+        return chain(H264(HW), HEVC(HW), VP8(HW), VP9(HW), Mpeg4(HW), H263(HW));
     }
 
     /* tests for adaptive codecs */
@@ -162,18 +172,21 @@
     public void sanityHW()  { sanity = true; try { runHW();  } finally { sanity = false; } }
 
     public void runH264()  { ex(H264(),  allTests); }
+    public void runHEVC()  { ex(HEVC(),  allTests); }
     public void runVP8()   { ex(VP8(),   allTests); }
     public void runVP9()   { ex(VP9(),   allTests); }
     public void runMpeg4() { ex(Mpeg4(), allTests); }
     public void runH263()  { ex(H263(),  allTests); }
 
     public void onlyH264HW()  { ex(H264(HW),  allTests); }
+    public void onlyHEVCHW()  { ex(HEVC(HW),  allTests); }
     public void onlyVP8HW()   { ex(VP8(HW),   allTests); }
     public void onlyVP9HW()   { ex(VP9(HW),   allTests); }
     public void onlyMpeg4HW() { ex(Mpeg4(HW), allTests); }
     public void onlyH263HW()  { ex(H263(HW),  allTests); }
 
     public void onlyH264SW()  { ex(H264(SW),  allTests); }
+    public void onlyHEVCSW()  { ex(HEVC(SW),  allTests); }
     public void onlyVP8SW()   { ex(VP8(SW),   allTests); }
     public void onlyVP9SW()   { ex(VP9(SW),   allTests); }
     public void onlyMpeg4SW() { ex(Mpeg4(SW), allTests); }
@@ -184,66 +197,78 @@
 
     /* inidividual tests */
     public void testH264_adaptiveEarlyEos()  { ex(H264(),  adaptiveEarlyEos); }
+    public void testHEVC_adaptiveEarlyEos()  { ex(HEVC(),  adaptiveEarlyEos); }
     public void testVP8_adaptiveEarlyEos()   { ex(VP8(),   adaptiveEarlyEos); }
     public void testVP9_adaptiveEarlyEos()   { ex(VP9(),   adaptiveEarlyEos); }
     public void testMpeg4_adaptiveEarlyEos() { ex(Mpeg4(), adaptiveEarlyEos); }
     public void testH263_adaptiveEarlyEos()  { ex(H263(),  adaptiveEarlyEos); }
 
     public void testH264_adaptiveEosFlushSeek()  { ex(H264(),  adaptiveEosFlushSeek); }
+    public void testHEVC_adaptiveEosFlushSeek()  { ex(HEVC(),  adaptiveEosFlushSeek); }
     public void testVP8_adaptiveEosFlushSeek()   { ex(VP8(),   adaptiveEosFlushSeek); }
     public void testVP9_adaptiveEosFlushSeek()   { ex(VP9(),   adaptiveEosFlushSeek); }
     public void testMpeg4_adaptiveEosFlushSeek() { ex(Mpeg4(), adaptiveEosFlushSeek); }
     public void testH263_adaptiveEosFlushSeek()  { ex(H263(),  adaptiveEosFlushSeek); }
 
     public void testH264_adaptiveSkipAhead()  { ex(H264(),  adaptiveSkipAhead); }
+    public void testHEVC_adaptiveSkipAhead()  { ex(HEVC(),  adaptiveSkipAhead); }
     public void testVP8_adaptiveSkipAhead()   { ex(VP8(),   adaptiveSkipAhead); }
     public void testVP9_adaptiveSkipAhead()   { ex(VP9(),   adaptiveSkipAhead); }
     public void testMpeg4_adaptiveSkipAhead() { ex(Mpeg4(), adaptiveSkipAhead); }
     public void testH263_adaptiveSkipAhead()  { ex(H263(),  adaptiveSkipAhead); }
 
     public void testH264_adaptiveSkipBack()  { ex(H264(),  adaptiveSkipBack); }
+    public void testHEVC_adaptiveSkipBack()  { ex(HEVC(),  adaptiveSkipBack); }
     public void testVP8_adaptiveSkipBack()   { ex(VP8(),   adaptiveSkipBack); }
     public void testVP9_adaptiveSkipBack()   { ex(VP9(),   adaptiveSkipBack); }
     public void testMpeg4_adaptiveSkipBack() { ex(Mpeg4(), adaptiveSkipBack); }
     public void testH263_adaptiveSkipBack()  { ex(H263(),  adaptiveSkipBack); }
 
     public void testH264_adaptiveReconfigDrc()  { ex(H264(),  adaptiveReconfigDrc); }
+    public void testHEVC_adaptiveReconfigDrc()  { ex(HEVC(),  adaptiveReconfigDrc); }
     public void testVP8_adaptiveReconfigDrc()   { ex(VP8(),   adaptiveReconfigDrc); }
     public void testVP9_adaptiveReconfigDrc()   { ex(VP9(),   adaptiveReconfigDrc); }
     public void testMpeg4_adaptiveReconfigDrc() { ex(Mpeg4(), adaptiveReconfigDrc); }
     public void testH263_adaptiveReconfigDrc()  { ex(H263(),  adaptiveReconfigDrc); }
 
     public void testH264_adaptiveSmallReconfigDrc()  { ex(H264(),  adaptiveSmallReconfigDrc); }
+    public void testHEVC_adaptiveSmallReconfigDrc()  { ex(HEVC(),  adaptiveSmallReconfigDrc); }
     public void testVP8_adaptiveSmallReconfigDrc()   { ex(VP8(),   adaptiveSmallReconfigDrc); }
     public void testVP9_adaptiveSmallReconfigDrc()   { ex(VP9(),   adaptiveSmallReconfigDrc); }
     public void testMpeg4_adaptiveSmallReconfigDrc() { ex(Mpeg4(), adaptiveSmallReconfigDrc); }
     public void testH263_adaptiveSmallReconfigDrc()  { ex(H263(),  adaptiveSmallReconfigDrc); }
 
     public void testH264_adaptiveDrc() { ex(H264(), adaptiveDrc); }
+    public void testHEVC_adaptiveDrc() { ex(HEVC(), adaptiveDrc); }
     public void testVP8_adaptiveDrc()  { ex(VP8(),  adaptiveDrc); }
     public void testVP9_adaptiveDrc()  { ex(VP9(),  adaptiveDrc); }
 
     public void testH264_adaptiveDrcEarlyEos() { ex(H264(), new AdaptiveDrcEarlyEosTest()); }
+    public void testHEVC_adaptiveDrcEarlyEos() { ex(HEVC(), new AdaptiveDrcEarlyEosTest()); }
     public void testVP8_adaptiveDrcEarlyEos()  { ex(VP8(),  new AdaptiveDrcEarlyEosTest()); }
     public void testVP9_adaptiveDrcEarlyEos()  { ex(VP9(),  new AdaptiveDrcEarlyEosTest()); }
 
     public void testH264_adaptiveSmallDrc()  { ex(H264(),  adaptiveSmallDrc); }
+    public void testHEVC_adaptiveSmallDrc()  { ex(HEVC(),  adaptiveSmallDrc); }
     public void testVP8_adaptiveSmallDrc()   { ex(VP8(),   adaptiveSmallDrc); }
     public void testVP9_adaptiveSmallDrc()   { ex(VP9(),   adaptiveSmallDrc); }
 
     public void testH264_earlyEos()  { ex(H264(),  earlyEos); }
+    public void testHEVC_earlyEos()  { ex(HEVC(),  earlyEos); }
     public void testVP8_earlyEos()   { ex(VP8(),   earlyEos); }
     public void testVP9_earlyEos()   { ex(VP9(),   earlyEos); }
     public void testMpeg4_earlyEos() { ex(Mpeg4(), earlyEos); }
     public void testH263_earlyEos()  { ex(H263(),  earlyEos); }
 
     public void testH264_eosFlushSeek()  { ex(H264(),  eosFlushSeek); }
+    public void testHEVC_eosFlushSeek()  { ex(HEVC(),  eosFlushSeek); }
     public void testVP8_eosFlushSeek()   { ex(VP8(),   eosFlushSeek); }
     public void testVP9_eosFlushSeek()   { ex(VP9(),   eosFlushSeek); }
     public void testMpeg4_eosFlushSeek() { ex(Mpeg4(), eosFlushSeek); }
     public void testH263_eosFlushSeek()  { ex(H263(),  eosFlushSeek); }
 
     public void testH264_flushConfigureDrc()  { ex(H264(),  flushConfigureDrc); }
+    public void testHEVC_flushConfigureDrc()  { ex(HEVC(),  flushConfigureDrc); }
     public void testVP8_flushConfigureDrc()   { ex(VP8(),   flushConfigureDrc); }
     public void testVP9_flushConfigureDrc()   { ex(VP9(),   flushConfigureDrc); }
     public void testMpeg4_flushConfigureDrc() { ex(Mpeg4(), flushConfigureDrc); }
@@ -1275,13 +1300,11 @@
 
             /* test if the explicitly named codec is present on the system */
             if (explicitCodecName != null) {
-                try {
-                    MediaCodec codec = MediaCodec.createByCodecName(explicitCodecName);
-                    if (codec != null) {
-                        codec.release();
-                        add(new Codec(explicitCodecName, null, mediaList));
-                    }
-                } catch (Exception e) {}
+                MediaCodec codec = MediaCodec.createByCodecName(explicitCodecName);
+                if (codec != null) {
+                    codec.release();
+                    add(new Codec(explicitCodecName, null, mediaList));
+                }
             }
         } catch (Throwable t) {
             Log.wtf("Constructor failed", t);
diff --git a/tests/tests/media/src/android/media/cts/AudioTrackTest.java b/tests/tests/media/src/android/media/cts/AudioTrackTest.java
index 090cde8..5d92966 100644
--- a/tests/tests/media/src/android/media/cts/AudioTrackTest.java
+++ b/tests/tests/media/src/android/media/cts/AudioTrackTest.java
@@ -23,7 +23,10 @@
 import android.test.AndroidTestCase;
 import android.util.Log;
 
+import java.nio.ByteOrder;
 import java.nio.ByteBuffer;
+import java.nio.ShortBuffer;
+import java.nio.FloatBuffer;
 
 public class AudioTrackTest extends AndroidTestCase {
     private String TAG = "AudioTrackTest";
@@ -447,10 +450,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;
@@ -466,61 +468,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
@@ -645,6 +621,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
     // ----------------------------------
@@ -1289,7 +1286,7 @@
 
         // -------- initialization --------------
         int bufferSize = AudioTrack.getMinBufferSize(TEST_SR, TEST_CONF, TEST_FORMAT);
-        byte data[] = createSoundDataInByteArray(bufferSize, TEST_SR);
+        byte data[] = createSoundDataInByteArray(bufferSize, TEST_SR, 1024);
         AudioTrack track = new AudioTrack(TEST_STREAM_TYPE, TEST_SR, TEST_CONF, TEST_FORMAT,
                 bufferSize, TEST_MODE);
         // -------- test --------------
@@ -1307,10 +1304,10 @@
         track.release();
     }
 
-    public static byte[] createSoundDataInByteArray(int bufferSize, final int sampleRate) {
-        final int frequency = 1024;
+    public static byte[] createSoundDataInByteArray(int bufferSamples, final int sampleRate,
+            final double frequency) {
         final double rad = 2 * Math.PI * frequency / sampleRate;
-        byte[] vai = new byte[bufferSize];
+        byte[] vai = new byte[bufferSamples];
         for (int j = 0; j < vai.length; j++) {
             int unsigned =  (int)(Math.sin(j * rad) * Byte.MAX_VALUE) + Byte.MAX_VALUE & 0xFF;
             vai[j] = (byte) unsigned;
@@ -1318,51 +1315,220 @@
         return vai;
     }
 
-    public static short[] createSoundDataInShortArray(int bufferSize, final int sampleRate) {
-        final double frequency = 1024;
+    public static short[] createSoundDataInShortArray(int bufferSamples, final int sampleRate,
+            final double frequency) {
         final double rad = 2 * Math.PI * frequency / sampleRate;
-        short[] vai = new short[bufferSize];
+        short[] vai = new short[bufferSamples];
         for (int j = 0; j < vai.length; j++) {
-            vai[j] = (short) (Math.sin(j * rad) * Short.MAX_VALUE);
+            vai[j] = (short)(Math.sin(j * rad) * Short.MAX_VALUE);
         }
         return vai;
     }
 
+    public static float[] createSoundDataInFloatArray(int bufferSamples, final int sampleRate,
+            final double frequency) {
+        final double rad = 2 * Math.PI * frequency / sampleRate;
+        float[] vaf = new float[bufferSamples];
+        for (int j = 0; j < vaf.length; j++) {
+            vaf[j] = (float)(Math.sin(j * rad));
+        }
+        return vaf;
+    }
+
     public void testPlayStreamData() throws Exception {
         // constants for test
         final String TEST_NAME = "testPlayStreamData";
-        final int TEST_SR = 22050;
-        final int TEST_CONF = AudioFormat.CHANNEL_CONFIGURATION_MONO;
-        final int TEST_FORMAT = AudioFormat.ENCODING_PCM_16BIT;
+        final int TEST_FORMAT_ARRAY[] = {  // should hear 28 increasing frequency tones, 3 times
+                AudioFormat.ENCODING_PCM_8BIT,
+                AudioFormat.ENCODING_PCM_16BIT,
+                AudioFormat.ENCODING_PCM_FLOAT,
+        };
+        final int TEST_SR_ARRAY[] = {
+                4000,
+                22050,
+                44100,
+                48000,
+        };
+        final int TEST_CONF_ARRAY[] = {
+                AudioFormat.CHANNEL_OUT_MONO,    // 1.0
+                AudioFormat.CHANNEL_OUT_STEREO,  // 2.0
+                AudioFormat.CHANNEL_OUT_STEREO | AudioFormat.CHANNEL_OUT_FRONT_CENTER, // 3.0
+                AudioFormat.CHANNEL_OUT_QUAD,    // 4.0
+                AudioFormat.CHANNEL_OUT_QUAD | AudioFormat.CHANNEL_OUT_FRONT_CENTER,   // 5.0
+                AudioFormat.CHANNEL_OUT_5POINT1, // 5.1
+                AudioFormat.CHANNEL_OUT_5POINT1 | AudioFormat.CHANNEL_OUT_BACK_CENTER, // 6.1
+                // unsupported masks
+                // AudioFormat.CHANNEL_OUT_7POINT1_SURROUND, // 7.1
+                // AudioFormat.CHANNEL_OUT_7POINT1_SURROUND | AudioFormat.CHANNEL_OUT_BACK_CENTER,
+        };
         final int TEST_MODE = AudioTrack.MODE_STREAM;
         final int TEST_STREAM_TYPE = AudioManager.STREAM_MUSIC;
 
-        // -------- initialization --------------
-        int minBufferSize = AudioTrack.getMinBufferSize(TEST_SR, TEST_CONF, TEST_FORMAT);
-        int bufferSize = 3 * minBufferSize;
-        short data[] = createSoundDataInShortArray(bufferSize, TEST_SR);
-        AudioTrack track = new AudioTrack(TEST_STREAM_TYPE, TEST_SR, TEST_CONF, TEST_FORMAT,
-                minBufferSize, TEST_MODE);
-        // -------- test --------------
-        assertTrue(TEST_NAME, track.getState() == AudioTrack.STATE_INITIALIZED);
-        boolean hasPlayed = false;
-        int written = 0;
-        while (written < data.length) {
-            if (data.length - written <= minBufferSize) {
-                written += track.write(data, written, data.length - written);
-            } else {
-                written += track.write(data, written, minBufferSize);
-                if (!hasPlayed) {
-                    track.play();
-                    hasPlayed = true;
+        for (int TEST_FORMAT : TEST_FORMAT_ARRAY) {
+            double frequency = 400; // frequency changes for each test
+            for (int TEST_SR : TEST_SR_ARRAY) {
+                for (int TEST_CONF : TEST_CONF_ARRAY) {
+                    // -------- initialization --------------
+                    final int minBufferSize = AudioTrack.getMinBufferSize(TEST_SR,
+                            TEST_CONF, TEST_FORMAT); // in bytes
+                    final int bufferSamples = 12 * minBufferSize
+                            / AudioFormat.getBytesPerSample(TEST_FORMAT);
+                    final int channelCount = Integer.bitCount(TEST_CONF);
+                    final double testFrequency = frequency / channelCount;
+                    AudioTrack track = new AudioTrack(TEST_STREAM_TYPE, TEST_SR,
+                            TEST_CONF, TEST_FORMAT, minBufferSize, TEST_MODE);
+                    assertTrue(TEST_NAME, track.getState() == AudioTrack.STATE_INITIALIZED);
+                    boolean hasPlayed = false;
+                    int written = 0;
+
+                    // -------- test --------------
+                    switch (TEST_FORMAT) {
+                    case AudioFormat.ENCODING_PCM_8BIT: {
+                        byte data[] = createSoundDataInByteArray(
+                                bufferSamples, TEST_SR,
+                                testFrequency);
+                        while (written < data.length) {
+                            int ret = track.write(data, written,
+                                    Math.min(data.length - written, minBufferSize));
+                            assertTrue(TEST_NAME, ret >= 0);
+                            written += ret;
+                            if (!hasPlayed) {
+                                track.play();
+                                hasPlayed = true;
+                            }
+                        }
+                        } break;
+                    case AudioFormat.ENCODING_PCM_16BIT: {
+                        short data[] = createSoundDataInShortArray(
+                                bufferSamples, TEST_SR,
+                                testFrequency);
+                        while (written < data.length) {
+                            int ret = track.write(data, written,
+                                    Math.min(data.length - written, minBufferSize));
+                            assertTrue(TEST_NAME, ret >= 0);
+                            written += ret;
+                            if (!hasPlayed) {
+                                track.play();
+                                hasPlayed = true;
+                            }
+                        }
+                        } break;
+                    case AudioFormat.ENCODING_PCM_FLOAT: {
+                        float data[] = createSoundDataInFloatArray(
+                                bufferSamples, TEST_SR,
+                                testFrequency);
+                        while (written < data.length) {
+                            int ret = track.write(data, written,
+                                    Math.min(data.length - written, minBufferSize),
+                                    AudioTrack.WRITE_BLOCKING);
+                            assertTrue(TEST_NAME, ret >= 0);
+                            written += ret;
+                            if (!hasPlayed) {
+                                track.play();
+                                hasPlayed = true;
+                            }
+                        }
+                        } break;
+                    }
+
+                    Thread.sleep(WAIT_MSEC);
+                    track.stop();
+                    Thread.sleep(WAIT_MSEC);
+                    // -------- tear down --------------
+                    track.release();
+                    frequency += 100; // increment test tone frequency
                 }
             }
         }
-        Thread.sleep(WAIT_MSEC);
-        track.stop();
-        Thread.sleep(WAIT_MSEC);
-        // -------- tear down --------------
-        track.release();
+    }
+
+    public void testPlayStreamByteBuffer() throws Exception {
+        // constants for test
+        final String TEST_NAME = "testPlayStreamByteBuffer";
+        final int TEST_FORMAT_ARRAY[] = {  // should hear 2 tones played 3 times
+                AudioFormat.ENCODING_PCM_8BIT,
+                AudioFormat.ENCODING_PCM_16BIT,
+                AudioFormat.ENCODING_PCM_FLOAT,
+        };
+        final int TEST_SR_ARRAY[] = {
+                48000,
+        };
+        final int TEST_CONF_ARRAY[] = {
+                AudioFormat.CHANNEL_OUT_STEREO,
+        };
+        final int TEST_MODE = AudioTrack.MODE_STREAM;
+        final int TEST_STREAM_TYPE = AudioManager.STREAM_MUSIC;
+
+        for (int TEST_FORMAT : TEST_FORMAT_ARRAY) {
+            double frequency = 800; // frequency changes for each test
+            for (int TEST_SR : TEST_SR_ARRAY) {
+                for (int TEST_CONF : TEST_CONF_ARRAY) {
+                    for (int useDirect = 0; useDirect < 2; ++useDirect) {
+                        // -------- initialization --------------
+                        int minBufferSize = AudioTrack.getMinBufferSize(TEST_SR,
+                                TEST_CONF, TEST_FORMAT); // in bytes
+                        int bufferSize = 12 * minBufferSize;
+                        int bufferSamples = bufferSize
+                                / AudioFormat.getBytesPerSample(TEST_FORMAT);
+                        AudioTrack track = new AudioTrack(TEST_STREAM_TYPE, TEST_SR,
+                                TEST_CONF, TEST_FORMAT, minBufferSize, TEST_MODE);
+                        assertTrue(TEST_NAME, track.getState() == AudioTrack.STATE_INITIALIZED);
+                        boolean hasPlayed = false;
+                        int written = 0;
+                        ByteBuffer bb = (useDirect == 1)
+                                ? ByteBuffer.allocateDirect(bufferSize)
+                                        : ByteBuffer.allocate(bufferSize);
+                        bb.order(java.nio.ByteOrder.nativeOrder());
+
+                        // -------- test --------------
+                        switch (TEST_FORMAT) {
+                        case AudioFormat.ENCODING_PCM_8BIT: {
+                            byte data[] = createSoundDataInByteArray(
+                                    bufferSamples, TEST_SR,
+                                    frequency);
+                            bb.put(data);
+                            bb.flip();
+                            } break;
+                        case AudioFormat.ENCODING_PCM_16BIT: {
+                            short data[] = createSoundDataInShortArray(
+                                    bufferSamples, TEST_SR,
+                                    frequency);
+                            ShortBuffer sb = bb.asShortBuffer();
+                            sb.put(data);
+                            bb.limit(sb.limit() * 2);
+                            } break;
+                        case AudioFormat.ENCODING_PCM_FLOAT: {
+                            float data[] = createSoundDataInFloatArray(
+                                    bufferSamples, TEST_SR,
+                                    frequency);
+                            FloatBuffer fb = bb.asFloatBuffer();
+                            fb.put(data);
+                            bb.limit(fb.limit() * 4);
+                            } break;
+                        }
+
+                        while (written < bufferSize) {
+                            int ret = track.write(bb,
+                                    Math.min(bufferSize - written, minBufferSize),
+                                    AudioTrack.WRITE_BLOCKING);
+                            assertTrue(TEST_NAME, ret >= 0);
+                            written += ret;
+                            if (!hasPlayed) {
+                                track.play();
+                                hasPlayed = true;
+                            }
+                        }
+
+                        Thread.sleep(WAIT_MSEC);
+                        track.stop();
+                        Thread.sleep(WAIT_MSEC);
+                        // -------- tear down --------------
+                        track.release();
+                        frequency += 200; // increment test tone frequency
+                    }
+                }
+            }
+        }
     }
 
     public void testGetTimestamp() throws Exception {
diff --git a/tests/tests/media/src/android/media/cts/AudioTrack_ListenerTest.java b/tests/tests/media/src/android/media/cts/AudioTrack_ListenerTest.java
index 26841da..c283a67 100644
--- a/tests/tests/media/src/android/media/cts/AudioTrack_ListenerTest.java
+++ b/tests/tests/media/src/android/media/cts/AudioTrack_ListenerTest.java
@@ -62,7 +62,7 @@
     private void doTest() throws Exception {
         mOnMarkerReachedCalled = false;
         mOnPeriodicNotificationCalled = false;
-        byte[] vai = AudioTrackTest.createSoundDataInByteArray(2 * mMinBuffSize, TEST_SR);
+        byte[] vai = AudioTrackTest.createSoundDataInByteArray(2 * mMinBuffSize, TEST_SR, 1024);
         int markerInFrames = vai.length / 4;
         assertEquals(AudioTrack.SUCCESS, mAudioTrack.setNotificationMarkerPosition(markerInFrames));
         int periodInFrames = vai.length / 2;
diff --git a/tests/tests/media/src/android/media/cts/CamcorderProfileTest.java b/tests/tests/media/src/android/media/cts/CamcorderProfileTest.java
index 1df5011..8130a9a 100644
--- a/tests/tests/media/src/android/media/cts/CamcorderProfileTest.java
+++ b/tests/tests/media/src/android/media/cts/CamcorderProfileTest.java
@@ -30,6 +30,7 @@
 public class CamcorderProfileTest extends AndroidTestCase {
 
     private static final String TAG = "CamcorderProfileTest";
+    private static final int MIN_HIGH_SPEED_FPS = 100;
 
     // Uses get without id if cameraId == -1 and get with id otherwise.
     private CamcorderProfile getWithOptionalId(int quality, int cameraId) {
@@ -65,13 +66,20 @@
                    profile.quality == CamcorderProfile.QUALITY_480P ||
                    profile.quality == CamcorderProfile.QUALITY_720P ||
                    profile.quality == CamcorderProfile.QUALITY_1080P ||
+                   profile.quality == CamcorderProfile.QUALITY_2160P ||
                    profile.quality == CamcorderProfile.QUALITY_TIME_LAPSE_LOW ||
                    profile.quality == CamcorderProfile.QUALITY_TIME_LAPSE_HIGH ||
                    profile.quality == CamcorderProfile.QUALITY_TIME_LAPSE_QCIF ||
                    profile.quality == CamcorderProfile.QUALITY_TIME_LAPSE_CIF ||
                    profile.quality == CamcorderProfile.QUALITY_TIME_LAPSE_480P ||
                    profile.quality == CamcorderProfile.QUALITY_TIME_LAPSE_720P ||
-                   profile.quality == CamcorderProfile.QUALITY_TIME_LAPSE_1080P);
+                   profile.quality == CamcorderProfile.QUALITY_TIME_LAPSE_1080P ||
+                   profile.quality == CamcorderProfile.QUALITY_TIME_LAPSE_2160P ||
+                   profile.quality == CamcorderProfile.QUALITY_HIGH_SPEED_LOW ||
+                   profile.quality == CamcorderProfile.QUALITY_HIGH_SPEED_HIGH ||
+                   profile.quality == CamcorderProfile.QUALITY_HIGH_SPEED_480P ||
+                   profile.quality == CamcorderProfile.QUALITY_HIGH_SPEED_720P ||
+                   profile.quality == CamcorderProfile.QUALITY_HIGH_SPEED_1080P);
         assertTrue(profile.videoBitRate > 0);
         assertTrue(profile.videoFrameRate > 0);
         assertTrue(profile.videoFrameWidth > 0);
@@ -137,6 +145,30 @@
                 assertTrue(1088 == profile.videoFrameHeight ||
                            1080 == profile.videoFrameHeight);
                 break;
+            case CamcorderProfile.QUALITY_2160P:
+            case CamcorderProfile.QUALITY_TIME_LAPSE_2160P:
+                assertEquals(3840, profile.videoFrameWidth);
+                assertEquals(2160, profile.videoFrameHeight);
+                break;
+            case CamcorderProfile.QUALITY_HIGH_SPEED_480P:
+                assertTrue(720 == profile.videoFrameWidth ||  // SMPTE 293M/ITU-R Rec. 601
+                640 == profile.videoFrameWidth ||  // ATSC/NTSC (square sampling)
+                704 == profile.videoFrameWidth);   // ATSC/NTSC (non-square sampling)
+                assertEquals(480, profile.videoFrameHeight);
+                assertTrue(profile.videoFrameRate >= MIN_HIGH_SPEED_FPS);
+                break;
+            case CamcorderProfile.QUALITY_HIGH_SPEED_720P:
+                assertEquals(1280, profile.videoFrameWidth);
+                assertEquals(720, profile.videoFrameHeight);
+                assertTrue(profile.videoFrameRate >= MIN_HIGH_SPEED_FPS);
+                break;
+            case CamcorderProfile.QUALITY_HIGH_SPEED_1080P:
+                // 1080p could be either 1920x1088 or 1920x1080.
+                assertEquals(1920, profile.videoFrameWidth);
+                assertTrue(1088 == profile.videoFrameHeight ||
+                           1080 == profile.videoFrameHeight);
+                assertTrue(profile.videoFrameRate >= MIN_HIGH_SPEED_FPS);
+                break;
         }
     }
 
@@ -150,6 +182,16 @@
             int[] specificQualities,
             List<Size> videoSizes) {
 
+        // For high speed levels, low and high quality are optional,skip the test if
+        // they are missing.
+        if (low == null && high == null) {
+            // No profile should be available if low and high are unavailable.
+            for (int quality : specificQualities) {
+                assertFalse(CamcorderProfile.hasProfile(cameraId, quality));
+            }
+            return;
+        }
+
         CamcorderProfile minProfile = null;
         CamcorderProfile maxProfile = null;
 
@@ -205,24 +247,55 @@
         checkProfile(lowTimeLapseProfile, null);
         checkProfile(highTimeLapseProfile, null);
 
+        // High speed low and high profile are optional,
+        // but they should be both present or missing.
+        CamcorderProfile lowHighSpeedProfile = null;
+        CamcorderProfile highHighSpeedProfile = null;
+        if (CamcorderProfile.hasProfile(cameraId, CamcorderProfile.QUALITY_HIGH_SPEED_LOW)) {
+            lowHighSpeedProfile =
+                    getWithOptionalId(CamcorderProfile.QUALITY_HIGH_SPEED_LOW, cameraId);
+        }
+        if (CamcorderProfile.hasProfile(cameraId, CamcorderProfile.QUALITY_HIGH_SPEED_HIGH)) {
+            highHighSpeedProfile =
+                    getWithOptionalId(CamcorderProfile.QUALITY_HIGH_SPEED_HIGH, cameraId);
+        }
+        if (lowHighSpeedProfile != null) {
+            assertNotNull("high speed high quality profile should be supported if low" +
+                    " is supported ",
+                    highHighSpeedProfile);
+            checkProfile(lowHighSpeedProfile, null);
+            checkProfile(highHighSpeedProfile, null);
+        } else {
+            assertNull("high speed high quality profile shouldn't be supported if " +
+                    "low is unsupported ", highHighSpeedProfile);
+        }
+
         int[] specificProfileQualities = {CamcorderProfile.QUALITY_QCIF,
                                           CamcorderProfile.QUALITY_QVGA,
                                           CamcorderProfile.QUALITY_CIF,
                                           CamcorderProfile.QUALITY_480P,
                                           CamcorderProfile.QUALITY_720P,
-                                          CamcorderProfile.QUALITY_1080P};
+                                          CamcorderProfile.QUALITY_1080P,
+                                          CamcorderProfile.QUALITY_2160P};
 
         int[] specificTimeLapseProfileQualities = {CamcorderProfile.QUALITY_TIME_LAPSE_QCIF,
                                                    CamcorderProfile.QUALITY_TIME_LAPSE_QVGA,
                                                    CamcorderProfile.QUALITY_TIME_LAPSE_CIF,
                                                    CamcorderProfile.QUALITY_TIME_LAPSE_480P,
                                                    CamcorderProfile.QUALITY_TIME_LAPSE_720P,
-                                                   CamcorderProfile.QUALITY_TIME_LAPSE_1080P};
+                                                   CamcorderProfile.QUALITY_TIME_LAPSE_1080P,
+                                                   CamcorderProfile.QUALITY_TIME_LAPSE_2160P};
+
+        int[] specificHighSpeedProfileQualities = {CamcorderProfile.QUALITY_HIGH_SPEED_480P,
+                                                   CamcorderProfile.QUALITY_HIGH_SPEED_720P,
+                                                   CamcorderProfile.QUALITY_HIGH_SPEED_1080P};
 
         checkSpecificProfiles(cameraId, lowProfile, highProfile,
                 specificProfileQualities, videoSizes);
         checkSpecificProfiles(cameraId, lowTimeLapseProfile, highTimeLapseProfile,
                 specificTimeLapseProfileQualities, null);
+        checkSpecificProfiles(cameraId, lowHighSpeedProfile, highHighSpeedProfile,
+                specificHighSpeedProfileQualities, null);
     }
 
     public void testGet() {
diff --git a/tests/tests/media/src/android/media/cts/ClearKeySystemTest.java b/tests/tests/media/src/android/media/cts/ClearKeySystemTest.java
new file mode 100644
index 0000000..c05a605
--- /dev/null
+++ b/tests/tests/media/src/android/media/cts/ClearKeySystemTest.java
@@ -0,0 +1,454 @@
+/*
+ * 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.media.cts;
+
+import android.content.Context;
+import android.media.MediaCodec;
+import android.media.MediaCodecInfo;
+import android.media.MediaCodecInfo.CodecCapabilities;
+import android.media.MediaCodecInfo.CodecProfileLevel;
+import android.media.MediaCodecList;
+import android.media.MediaDrm;
+import android.media.MediaDrmException;
+import android.media.CamcorderProfile;
+import android.net.Uri;
+import android.os.Environment;
+import android.os.Looper;
+import android.test.ActivityInstrumentationTestCase2;
+import android.util.Base64;
+import android.util.Log;
+import android.view.SurfaceHolder;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.CharBuffer;
+import java.nio.charset.Charset;
+import java.util.ArrayList;
+import java.util.concurrent.TimeUnit;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+import java.util.Vector;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+/**
+ * Tests of MediaPlayer streaming capabilities.
+ */
+public class ClearKeySystemTest extends MediaPlayerTestBase {
+    private static final String TAG = ClearKeySystemTest.class.getSimpleName();
+
+    // Add additional keys here if the content has more keys.
+    private static final byte[] CLEAR_KEY =
+        { 0x1a, (byte)0x8a, 0x20, (byte)0x95, (byte)0xe4, (byte)0xde, (byte)0xb2, (byte)0xd2,
+          (byte)0x9e, (byte)0xc8, 0x16, (byte)0xac, 0x7b, (byte)0xae, 0x20, (byte)0x82 };
+
+    private static final int SLEEP_TIME_MS = 1000;
+    private static final int VIDEO_WIDTH = 1280;
+    private static final int VIDEO_HEIGHT = 720;
+    private static final long PLAY_TIME_MS = TimeUnit.MILLISECONDS.convert(1, TimeUnit.MINUTES);
+    private static final String MIME_VIDEO_AVC = "video/avc";
+
+    private static final Uri AUDIO_URL = Uri.parse(
+            "http://yt-dash-mse-test.commondatastorage.googleapis.com/media/car_cenc-20120827-8c.mp4");
+    private static final Uri VIDEO_URL = Uri.parse(
+            "http://yt-dash-mse-test.commondatastorage.googleapis.com/media/car_cenc-20120827-88.mp4");
+
+    private static final UUID CLEARKEY_SCHEME_UUID =
+            new UUID(0x1077efecc0b24d02L, 0xace33c1e52e2fb4bL);
+
+    private byte[] mDrmInitData;
+    private byte[] mSessionId;
+    private Context mContext;
+    private final List<byte[]> mClearKeys = new ArrayList<byte[]>() {
+        {
+            add(CLEAR_KEY);
+            // add additional keys here
+        }
+    };
+    private Looper mLooper;
+    private MediaCodecCencPlayer mMediaCodecPlayer;
+    private MediaDrm mDrm;
+    private Object mLock = new Object();
+    private SurfaceHolder mSurfaceHolder;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        if (false == deviceHasMediaDrm()) {
+            tearDown();
+        }
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        super.tearDown();
+    }
+
+    private boolean deviceHasMediaDrm() {
+        // ClearKey is introduced after KitKat.
+        if (android.os.Build.VERSION.SDK_INT <= android.os.Build.VERSION_CODES.KITKAT) {
+            Log.i(TAG, "This test is designed to work after Android KitKat.");
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Extracts key ids from the pssh blob returned by getKeyRequest() and
+     * places it in keyIds.
+     * keyRequestBlob format (section 5.1.3.1):
+     * https://dvcs.w3.org/hg/html-media/raw-file/default/encrypted-media/encrypted-media.html#clear-key
+     *
+     * @return size of keyIds vector that contains the key ids, 0 for error
+     */
+    private int getKeyIds(byte[] keyRequestBlob, Vector<String> keyIds) {
+        if (0 == keyRequestBlob.length || keyIds == null)
+            return 0;
+
+        String jsonLicenseRequest = new String(keyRequestBlob);
+        keyIds.clear();
+
+        try {
+            JSONObject license = new JSONObject(jsonLicenseRequest);
+            final JSONArray ids = license.getJSONArray("kids");
+            for (int i = 0; i < ids.length(); ++i) {
+                keyIds.add(ids.getString(i));
+            }
+        } catch (JSONException e) {
+            Log.e(TAG, "Invalid JSON license = " + jsonLicenseRequest);
+            return 0;
+        }
+        return keyIds.size();
+    }
+
+    /**
+     * Creates the JSON Web Key string.
+     *
+     * @return JSON Web Key string.
+     */
+    private String createJsonWebKeySet(Vector<String> keyIds, Vector<String> keys) {
+        String jwkSet = "{\"keys\":[";
+        for (int i = 0; i < keyIds.size(); ++i) {
+            String id = new String(keyIds.get(i).getBytes(Charset.forName("UTF-8")));
+            String key = new String(keys.get(i).getBytes(Charset.forName("UTF-8")));
+
+            jwkSet += "{\"kty\":\"oct\",\"kid\":\"" + id +
+                    "\",\"k\":\"" + key + "\"}";
+        }
+        jwkSet += "]}";
+        return jwkSet;
+    }
+
+    /**
+     * Retrieves clear key ids from getKeyRequest(), create JSON Web Key
+     * set and send it to the CDM via provideKeyResponse().
+     */
+    private void getKeys(MediaDrm drm, byte[] sessionId, byte[] drmInitData) {
+        MediaDrm.KeyRequest drmRequest = null;;
+        try {
+            drmRequest = drm.getKeyRequest(sessionId, drmInitData, "cenc",
+                    MediaDrm.KEY_TYPE_STREAMING, null);
+        } catch (Exception e) {
+            e.printStackTrace();
+            Log.i(TAG, "Failed to get key request: " + e.toString());
+        }
+        if (drmRequest == null) {
+            Log.e(TAG, "Failed getKeyRequest");
+            return;
+        }
+
+        Vector<String> keyIds = new Vector<String>();
+        if (0 == getKeyIds(drmRequest.getData(), keyIds)) {
+            Log.e(TAG, "No key ids found in initData");
+            return;
+        }
+
+        if (mClearKeys.size() != keyIds.size()) {
+            Log.e(TAG, "Mismatch number of key ids and keys: ids=" +
+                    keyIds.size() + ", keys=" + mClearKeys.size());
+            return;
+        }
+
+        // Base64 encodes clearkeys. Keys are known to the application.
+        Vector<String> keys = new Vector<String>();
+        for (int i = 0; i < mClearKeys.size(); ++i) {
+            String clearKey = Base64.encodeToString(mClearKeys.get(i),
+                    Base64.NO_PADDING | Base64.NO_WRAP);
+            keys.add(clearKey);
+        }
+
+        String jwkSet = createJsonWebKeySet(keyIds, keys);
+        byte[] jsonResponse = jwkSet.getBytes(Charset.forName("UTF-8"));
+
+        try {
+            try {
+                drm.provideKeyResponse(sessionId, jsonResponse);
+            } catch (IllegalStateException e) {
+                Log.e(TAG, "Failed to provide key response: " + e.toString());
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+            Log.e(TAG, "Failed to provide key response: " + e.toString());
+        }
+    }
+
+    private MediaDrm startDrm() {
+        new Thread() {
+            @Override
+            public void run() {
+                // Set up a looper to handle events
+                Looper.prepare();
+
+                // Save the looper so that we can terminate this thread
+                // after we are done with it.
+                mLooper = Looper.myLooper();
+
+                try {
+                    mDrm = new MediaDrm(CLEARKEY_SCHEME_UUID);
+                } catch (MediaDrmException e) {
+                    Log.e(TAG, "Failed to create MediaDrm: " + e.getMessage());
+                    return;
+                }
+
+                synchronized(mLock) {
+                    mDrm.setOnEventListener(new MediaDrm.OnEventListener() {
+                            @Override
+                            public void onEvent(MediaDrm md, byte[] sessionId, int event,
+                                    int extra, byte[] data) {
+                                if (event == MediaDrm.EVENT_KEY_REQUIRED) {
+                                    Log.i(TAG, "MediaDrm event: Key required");
+                                    getKeys(mDrm, mSessionId, mDrmInitData);
+                                } else if (event == MediaDrm.EVENT_KEY_EXPIRED) {
+                                    Log.i(TAG, "MediaDrm event: Key expired");
+                                    getKeys(mDrm, mSessionId, mDrmInitData);
+                                } else {
+                                    Log.e(TAG, "Events not supported" + event);
+                                }
+                            }
+                        });
+                    mLock.notify();
+                }
+                Looper.loop();  // Blocks forever until Looper.quit() is called.
+            }
+        }.start();
+
+        // wait for mDrm to be created
+        synchronized(mLock) {
+            try {
+                mLock.wait(1000);
+            } catch (Exception e) {
+            }
+        }
+        return mDrm;
+    }
+
+    private void stopDrm(MediaDrm drm) {
+        if (drm != mDrm) {
+            Log.e(TAG, "invalid drm specified in stopDrm");
+        }
+        mLooper.quit();
+    }
+
+    private byte[] openSession(MediaDrm drm) {
+        byte[] mSessionId = null;
+        boolean mRetryOpen;
+        do {
+            try {
+                mRetryOpen = false;
+                mSessionId = drm.openSession();
+            } catch (Exception e) {
+                mRetryOpen = true;
+            }
+        } while (mRetryOpen);
+        return mSessionId;
+    }
+
+    private void closeSession(MediaDrm drm, byte[] sessionId) {
+        drm.closeSession(sessionId);
+    }
+
+    public boolean isResolutionSupported(int videoWidth, int videoHeight) {
+        if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.JELLY_BEAN) {
+            if  (videoHeight <= 144) {
+                return CamcorderProfile.hasProfile(CamcorderProfile.QUALITY_QCIF);
+            } else if (videoHeight <= 240) {
+                return CamcorderProfile.hasProfile(CamcorderProfile.QUALITY_QVGA);
+            } else if (videoHeight <= 288) {
+                return CamcorderProfile.hasProfile(CamcorderProfile.QUALITY_CIF);
+            } else if (videoHeight <= 480) {
+                return CamcorderProfile.hasProfile(CamcorderProfile.QUALITY_480P);
+            } else if (videoHeight <= 720) {
+                return CamcorderProfile.hasProfile(CamcorderProfile.QUALITY_720P);
+            } else if (videoHeight <= 1080) {
+                return CamcorderProfile.hasProfile(CamcorderProfile.QUALITY_1080P);
+            } else {
+                return false;
+            }
+        }
+
+        CodecCapabilities cap;
+        int highestProfileLevel = 0;
+        MediaCodecInfo codecInfo;
+
+        for (int i = 0; i < MediaCodecList.getCodecCount(); i++) {
+            codecInfo = MediaCodecList.getCodecInfoAt(i);
+            if (codecInfo.isEncoder()) {
+                continue;
+            }
+
+            String[] types = codecInfo.getSupportedTypes();
+            for (int j = 0; j < types.length; ++j) {
+                if (!types[j].equalsIgnoreCase(MIME_VIDEO_AVC)) {
+                    continue;
+                }
+
+                Log.d(TAG, "codec: " + codecInfo.getName() + "types: " + types[j]);
+                cap = codecInfo.getCapabilitiesForType(types[j]);
+                for (CodecProfileLevel profileLevel : cap.profileLevels) {
+                    Log.i(TAG, "codec " + codecInfo.getName() + ", level " + profileLevel.level);
+                    if (profileLevel.level > highestProfileLevel) {
+                        highestProfileLevel = profileLevel.level;
+                    }
+                }
+                Log.i(TAG, "codec " + codecInfo.getName() + ", highest level " + highestProfileLevel);
+            }
+        }
+
+        // AVCLevel and its resolution is taken from http://en.wikipedia.org/wiki/H.264/MPEG-4_AVC
+        switch(highestProfileLevel) {
+        case CodecProfileLevel.AVCLevel1:
+        case CodecProfileLevel.AVCLevel1b:
+            return (videoWidth <= 176 && videoHeight <= 144);
+        case CodecProfileLevel.AVCLevel11:
+        case CodecProfileLevel.AVCLevel12:
+        case CodecProfileLevel.AVCLevel13:
+        case CodecProfileLevel.AVCLevel2:
+            return (videoWidth <= 352 && videoHeight <= 288);
+        case CodecProfileLevel.AVCLevel21:
+            return (videoWidth <= 352 && videoHeight <= 576);
+        case CodecProfileLevel.AVCLevel22:
+        case CodecProfileLevel.AVCLevel3:
+            return (videoWidth <= 720 && videoHeight <= 576);
+        case CodecProfileLevel.AVCLevel31:
+            return (videoWidth <= 1280 && videoHeight <= 720);
+        case CodecProfileLevel.AVCLevel32:
+            return (videoWidth <= 1280 && videoHeight <= 1024);
+        case CodecProfileLevel.AVCLevel4:
+        case CodecProfileLevel.AVCLevel41:
+            // 1280 x 720
+            // 1920 x 1080
+            // 2048 x 1024
+            if (videoWidth <= 1920) {
+                return (videoHeight <= 1080);
+            } else if (videoWidth <= 2048) {
+                return (videoHeight <= 1024);
+            } else {
+                return false;
+            }
+        case CodecProfileLevel.AVCLevel42:
+            return (videoWidth <= 2048 && videoHeight <= 1080);
+        case CodecProfileLevel.AVCLevel5:
+            // 1920 x 1080
+            // 2048 x 1024
+            // 2048 x 1080
+            // 2560 x 1920
+            // 3672 x 1536
+            if (videoWidth <= 1920) {
+                return (videoHeight <= 1080);
+            } else if (videoWidth <= 2048) {
+                return (videoHeight <= 1080);
+            } else if (videoWidth <= 2560) {
+                return (videoHeight <= 1920);
+            } else if (videoWidth <= 3672) {
+                return (videoHeight <= 1536);
+            } else {
+                return false;
+            }
+        case CodecProfileLevel.AVCLevel51:
+        default:  // any future extension will cap at level 5.1
+            // 1920 x 1080
+            // 2560 x 1920
+            // 3840 x 2160
+            // 4096 x 2048
+            // 4096 x 2160
+            // 4096 x 2304
+            if (videoWidth <= 1920) {
+                return (videoHeight <= 1080);
+            } else if (videoWidth <= 2560) {
+                return (videoHeight <= 1920);
+            } else if (videoWidth <= 3840) {
+                return (videoHeight <= 2160);
+            } else if (videoWidth <= 4096) {
+                return (videoHeight <= 2304);
+            } else {
+                return false;
+            }
+        }
+    }
+
+    /**
+     * Tests clear key system playback.
+     */
+    public void testClearKeyPlayback() throws Exception {
+        MediaDrm drm = startDrm();
+        if (null == drm) {
+            throw new Error("Failed to create drm.");
+        }
+
+        if (!drm.isCryptoSchemeSupported(CLEARKEY_SCHEME_UUID)) {
+            stopDrm(drm);
+            throw new Error("Crypto scheme is not supported.");
+        }
+
+        if (!isResolutionSupported(VIDEO_WIDTH, VIDEO_HEIGHT)) {
+            Log.i(TAG, "Device does not support " +
+                    VIDEO_WIDTH + "x" + VIDEO_HEIGHT + "resolution.");
+            return;
+        }
+
+        mSessionId = openSession(drm);
+        mMediaCodecPlayer = new MediaCodecCencPlayer(
+                getActivity().getSurfaceHolder(), mSessionId);
+
+        mMediaCodecPlayer.setAudioDataSource(AUDIO_URL, null, false);
+        mMediaCodecPlayer.setVideoDataSource(VIDEO_URL, null, true);
+        mMediaCodecPlayer.start();
+        mMediaCodecPlayer.prepare();
+        mDrmInitData = mMediaCodecPlayer.getPsshInfo().get(CLEARKEY_SCHEME_UUID);
+
+        getKeys(mDrm, mSessionId, mDrmInitData);
+        // starts video playback
+        mMediaCodecPlayer.startThread();
+
+        long timeOut = System.currentTimeMillis() + PLAY_TIME_MS * 4;
+        while (timeOut > System.currentTimeMillis() && !mMediaCodecPlayer.isEnded()) {
+            Thread.sleep(SLEEP_TIME_MS);
+            if (mMediaCodecPlayer.getCurrentPosition() >= mMediaCodecPlayer.getDuration() ) {
+                Log.d(TAG, "current pos = " + mMediaCodecPlayer.getCurrentPosition() +
+                        ">= duration = " + mMediaCodecPlayer.getDuration());
+                break;
+            }
+        }
+
+        Log.d(TAG, "playVideo player.reset()");
+        mMediaCodecPlayer.reset();
+        closeSession(drm, mSessionId);
+        stopDrm(drm);
+    }
+}
diff --git a/tests/tests/media/src/android/media/cts/CodecState.java b/tests/tests/media/src/android/media/cts/CodecState.java
new file mode 100644
index 0000000..cd6b68f
--- /dev/null
+++ b/tests/tests/media/src/android/media/cts/CodecState.java
@@ -0,0 +1,347 @@
+/*
+ * 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.media.cts;
+
+import android.media.AudioTrack;
+import android.media.MediaCodec;
+import android.media.MediaExtractor;
+import android.media.MediaFormat;
+import android.util.Log;
+
+import java.nio.ByteBuffer;
+import java.util.LinkedList;
+
+/**
+ * Class for directly managing both audio and video playback by
+ * using {@link MediaCodec} and {@link AudioTrack}.
+ */
+public class CodecState {
+    private static final String TAG = CodecState.class.getSimpleName();
+
+    private boolean mSawInputEOS, mSawOutputEOS;
+    private boolean mLimitQueueDepth;
+    private ByteBuffer[] mCodecInputBuffers;
+    private ByteBuffer[] mCodecOutputBuffers;
+    private int mTrackIndex;
+    private LinkedList<Integer> mAvailableInputBufferIndices;
+    private LinkedList<Integer> mAvailableOutputBufferIndices;
+    private LinkedList<MediaCodec.BufferInfo> mAvailableOutputBufferInfos;
+    private long mPresentationTimeUs;
+    private MediaCodec mCodec;
+    private MediaCodecCencPlayer mMediaCodecPlayer;
+    private MediaExtractor mExtractor;
+    private MediaFormat mFormat;
+    private MediaFormat mOutputFormat;
+    private NonBlockingAudioTrack mAudioTrack;
+
+    /**
+     * Manages audio and video playback using MediaCodec and AudioTrack.
+     */
+    public CodecState(
+            MediaCodecCencPlayer mediaCodecPlayer,
+            MediaExtractor extractor,
+            int trackIndex,
+            MediaFormat format,
+            MediaCodec codec,
+            boolean limitQueueDepth) {
+
+        mMediaCodecPlayer = mediaCodecPlayer;
+        mExtractor = extractor;
+        mTrackIndex = trackIndex;
+        mFormat = format;
+        mSawInputEOS = mSawOutputEOS = false;
+        mLimitQueueDepth = limitQueueDepth;
+
+        mCodec = codec;
+
+        mAvailableInputBufferIndices = new LinkedList<Integer>();
+        mAvailableOutputBufferIndices = new LinkedList<Integer>();
+        mAvailableOutputBufferInfos = new LinkedList<MediaCodec.BufferInfo>();
+
+        mPresentationTimeUs = 0;
+    }
+
+    public void release() {
+        mCodec.stop();
+        mCodecInputBuffers = null;
+        mCodecOutputBuffers = null;
+        mOutputFormat = null;
+
+        mAvailableInputBufferIndices.clear();
+        mAvailableOutputBufferIndices.clear();
+        mAvailableOutputBufferInfos.clear();
+
+        mAvailableInputBufferIndices = null;
+        mAvailableOutputBufferIndices = null;
+        mAvailableOutputBufferInfos = null;
+
+        mCodec.release();
+        mCodec = null;
+
+        if (mAudioTrack != null) {
+            mAudioTrack.release();
+            mAudioTrack = null;
+        }
+    }
+
+    public void start() {
+        mCodec.start();
+        mCodecInputBuffers = mCodec.getInputBuffers();
+        mCodecOutputBuffers = mCodec.getOutputBuffers();
+
+        if (mAudioTrack != null) {
+            mAudioTrack.play();
+        }
+    }
+
+    public void pause() {
+        if (mAudioTrack != null) {
+            mAudioTrack.pause();
+        }
+    }
+
+    public long getCurrentPositionUs() {
+        return mPresentationTimeUs;
+    }
+
+    public void flush() {
+        mAvailableInputBufferIndices.clear();
+        mAvailableOutputBufferIndices.clear();
+        mAvailableOutputBufferInfos.clear();
+
+        mSawInputEOS = false;
+        mSawOutputEOS = false;
+
+        if (mAudioTrack != null
+                && mAudioTrack.getPlayState() == AudioTrack.PLAYSTATE_STOPPED) {
+            mAudioTrack.play();
+        }
+
+        mCodec.flush();
+    }
+
+    public boolean isEnded() {
+        return mSawInputEOS && mSawOutputEOS;
+    }
+
+    /**
+     * doSomeWork() is the worker function that does all buffer handling and decoding works.
+     * It first reads data from {@link MediaExtractor} and pushes it into {@link MediaCodec};
+     * it then dequeues buffer from {@link MediaCodec}, consumes it and pushes back to its own
+     * buffer queue for next round reading data from {@link MediaExtractor}.
+     */
+    public void doSomeWork() {
+        int indexInput = mCodec.dequeueInputBuffer(0 /* timeoutUs */);
+
+        if (indexInput != MediaCodec.INFO_TRY_AGAIN_LATER) {
+            mAvailableInputBufferIndices.add(indexInput);
+        }
+
+        while (feedInputBuffer()) {
+        }
+
+        MediaCodec.BufferInfo info = new MediaCodec.BufferInfo();
+        int indexOutput = mCodec.dequeueOutputBuffer(info, 0 /* timeoutUs */);
+
+        if (indexOutput == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
+            mOutputFormat = mCodec.getOutputFormat();
+            onOutputFormatChanged();
+        } else if (indexOutput == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
+            mCodecOutputBuffers = mCodec.getOutputBuffers();
+        } else if (indexOutput != MediaCodec.INFO_TRY_AGAIN_LATER) {
+            mAvailableOutputBufferIndices.add(indexOutput);
+            mAvailableOutputBufferInfos.add(info);
+        }
+
+        while (drainOutputBuffer()) {
+        }
+    }
+
+    /** Returns true if more input data could be fed. */
+    private boolean feedInputBuffer() throws MediaCodec.CryptoException, IllegalStateException {
+        if (mSawInputEOS || mAvailableInputBufferIndices.isEmpty()) {
+            return false;
+        }
+
+        // stalls read if audio queue is larger than 2MB full so we will not occupy too much heap
+        if (mLimitQueueDepth && mAudioTrack != null &&
+                mAudioTrack.getNumBytesQueued() > 2 * 1024 * 1024) {
+            return false;
+        }
+
+        int index = mAvailableInputBufferIndices.peekFirst().intValue();
+
+        ByteBuffer codecData = mCodecInputBuffers[index];
+
+        int trackIndex = mExtractor.getSampleTrackIndex();
+
+        if (trackIndex == mTrackIndex) {
+            int sampleSize =
+                mExtractor.readSampleData(codecData, 0 /* offset */);
+
+            long sampleTime = mExtractor.getSampleTime();
+
+            int sampleFlags = mExtractor.getSampleFlags();
+
+            if (sampleSize <= 0) {
+                Log.d(TAG, "sampleSize: " + sampleSize + " trackIndex:" + trackIndex +
+                        " sampleTime:" + sampleTime + " sampleFlags:" + sampleFlags);
+                mSawInputEOS = true;
+                return false;
+            }
+
+            if ((sampleFlags & MediaExtractor.SAMPLE_FLAG_ENCRYPTED) != 0) {
+                MediaCodec.CryptoInfo info = new MediaCodec.CryptoInfo();
+                mExtractor.getSampleCryptoInfo(info);
+
+                mCodec.queueSecureInputBuffer(
+                        index, 0 /* offset */, info, sampleTime, 0 /* flags */);
+            } else {
+                mCodec.queueInputBuffer(
+                        index, 0 /* offset */, sampleSize, sampleTime, 0 /* flags */);
+            }
+
+            mAvailableInputBufferIndices.removeFirst();
+            mExtractor.advance();
+
+            return true;
+        } else if (trackIndex < 0) {
+            Log.d(TAG, "saw input EOS on track " + mTrackIndex);
+
+            mSawInputEOS = true;
+
+            mCodec.queueInputBuffer(
+                    index, 0 /* offset */, 0 /* sampleSize */,
+                    0 /* sampleTime */, MediaCodec.BUFFER_FLAG_END_OF_STREAM);
+
+            mAvailableInputBufferIndices.removeFirst();
+        }
+
+        return false;
+    }
+
+    private void onOutputFormatChanged() {
+        String mime = mOutputFormat.getString(MediaFormat.KEY_MIME);
+        // b/9250789
+        Log.d(TAG, "CodecState::onOutputFormatChanged " + mime);
+
+        if (mime.startsWith("audio/")) {
+            int sampleRate =
+                mOutputFormat.getInteger(MediaFormat.KEY_SAMPLE_RATE);
+
+            int channelCount =
+                mOutputFormat.getInteger(MediaFormat.KEY_CHANNEL_COUNT);
+
+            Log.d(TAG, "CodecState::onOutputFormatChanged Audio" +
+                    " sampleRate:" + sampleRate + " channels:" + channelCount);
+            // We do sanity check here after we receive data from MediaExtractor and before
+            // we pass them down to AudioTrack. If MediaExtractor works properly, this
+            // sanity-check is not necessary, however, in our tests, we found that there
+            // are a few cases where ch=0 and samplerate=0 were returned by MediaExtractor.
+            if (channelCount < 1 || channelCount > 8 ||
+                    sampleRate < 8000 || sampleRate > 128000) {
+                return;
+            }
+            mAudioTrack = new NonBlockingAudioTrack(sampleRate, channelCount);
+            mAudioTrack.play();
+        }
+
+        if (mime.startsWith("video/")) {
+            int width = mOutputFormat.getInteger(MediaFormat.KEY_WIDTH);
+            int height = mOutputFormat.getInteger(MediaFormat.KEY_HEIGHT);
+            Log.d(TAG, "CodecState::onOutputFormatChanged Video" +
+                    " width:" + width + " height:" + height);
+        }
+    }
+
+    /** Returns true if more output data could be drained. */
+    private boolean drainOutputBuffer() {
+        if (mSawOutputEOS || mAvailableOutputBufferIndices.isEmpty()) {
+            return false;
+        }
+
+        int index = mAvailableOutputBufferIndices.peekFirst().intValue();
+        MediaCodec.BufferInfo info = mAvailableOutputBufferInfos.peekFirst();
+
+        if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
+            Log.d(TAG, "saw output EOS on track " + mTrackIndex);
+
+            mSawOutputEOS = true;
+
+            return false;
+        }
+
+        long realTimeUs =
+            mMediaCodecPlayer.getRealTimeUsForMediaTime(info.presentationTimeUs);
+
+        long nowUs = mMediaCodecPlayer.getNowUs();
+
+        long lateUs = nowUs - realTimeUs;
+
+        if (mAudioTrack != null) {
+            ByteBuffer buffer = mCodecOutputBuffers[index];
+            buffer.clear();
+            buffer.position(0 /* offset */);
+
+            byte[] audioCopy = new byte[info.size];
+            buffer.get(audioCopy, 0, info.size);
+
+            mAudioTrack.write(audioCopy, info.size);
+
+            mCodec.releaseOutputBuffer(index, false /* render */);
+
+            mPresentationTimeUs = info.presentationTimeUs;
+
+            mAvailableOutputBufferIndices.removeFirst();
+            mAvailableOutputBufferInfos.removeFirst();
+            return true;
+        } else {
+            // video
+            boolean render;
+
+            if (lateUs < -45000) {
+                // too early;
+                return false;
+            } else if (lateUs > 30000) {
+                Log.d(TAG, "video late by " + lateUs + " us.");
+                render = false;
+            } else {
+                render = true;
+                mPresentationTimeUs = info.presentationTimeUs;
+            }
+
+            mCodec.releaseOutputBuffer(index, render);
+
+            mAvailableOutputBufferIndices.removeFirst();
+            mAvailableOutputBufferInfos.removeFirst();
+            return true;
+        }
+    }
+
+    public long getAudioTimeUs() {
+        if (mAudioTrack == null) {
+            return 0;
+        }
+
+        return mAudioTrack.getAudioTimeUs();
+    }
+
+    public void process() {
+        if (mAudioTrack != null) {
+            mAudioTrack.process();
+        }
+    }
+}
diff --git a/tests/tests/media/src/android/media/cts/DecodeEditEncodeTest.java b/tests/tests/media/src/android/media/cts/DecodeEditEncodeTest.java
index 0d83647..bb430e3 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;
@@ -315,7 +317,7 @@
                     encoderOutputBuffers = encoder.getOutputBuffers();
                     if (VERBOSE) Log.d(TAG, "encoder output buffers changed");
                 } else if (encoderStatus == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
-                    // not expected for an encoder
+                    // expected on API 18+
                     MediaFormat newFormat = encoder.getOutputFormat();
                     if (VERBOSE) Log.d(TAG, "encoder output format changed: " + newFormat);
                 } else if (encoderStatus < 0) {
@@ -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..440f354 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 {
@@ -41,6 +42,12 @@
     private static final int RESET_MODE_NONE = 0;
     private static final int RESET_MODE_RECONFIGURE = 1;
     private static final int RESET_MODE_FLUSH = 2;
+    private static final int RESET_MODE_EOS_FLUSH = 3;
+
+    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,43 +76,386 @@
         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
     // phase and delay errors, channel swap, etc.
     public void testDecodeMp3Lame() throws Exception {
         decode(R.raw.sinesweepmp3lame, 804.f);
+        testTimeStampOrdering(R.raw.sinesweepmp3lame);
     }
     public void testDecodeMp3Smpb() throws Exception {
         decode(R.raw.sinesweepmp3smpb, 413.f);
+        testTimeStampOrdering(R.raw.sinesweepmp3smpb);
     }
     public void testDecodeM4a() throws Exception {
         decode(R.raw.sinesweepm4a, 124.f);
+        testTimeStampOrdering(R.raw.sinesweepm4a);
     }
     public void testDecodeOgg() throws Exception {
         decode(R.raw.sinesweepogg, 168.f);
+        testTimeStampOrdering(R.raw.sinesweepogg);
     }
     public void testDecodeWav() throws Exception {
         decode(R.raw.sinesweepwav, 0.0f);
+        testTimeStampOrdering(R.raw.sinesweepwav);
     }
     public void testDecodeFlac() throws Exception {
         decode(R.raw.sinesweepflac, 0.0f);
+        testTimeStampOrdering(R.raw.sinesweepflac);
     }
 
     public void testDecodeMonoMp3() throws Exception {
         monoTest(R.raw.monotestmp3);
+        testTimeStampOrdering(R.raw.monotestmp3);
     }
 
     public void testDecodeMonoM4a() throws Exception {
         monoTest(R.raw.monotestm4a);
+        testTimeStampOrdering(R.raw.monotestm4a);
     }
 
     public void testDecodeMonoOgg() throws Exception {
         monoTest(R.raw.monotestogg);
+        testTimeStampOrdering(R.raw.monotestogg);
     }
 
+    public void testDecodeAacTs() throws Exception {
+        testTimeStampOrdering(R.raw.sinesweeptsaac);
+    }
+
+    private void testTimeStampOrdering(int res) throws Exception {
+        List<Long> timestamps = new ArrayList<Long>();
+        decodeToMemory(res, RESET_MODE_NONE, CONFIG_MODE_NONE, -1, timestamps);
+        Long lastTime = Long.MIN_VALUE;
+        for (int i = 0; i < timestamps.size(); i++) {
+            Long thisTime = timestamps.get(i);
+            assertTrue("timetravel occurred: " + lastTime + " > " + thisTime, thisTime >= lastTime);
+            lastTime = thisTime;
+        }
+    }
+
+    public void testTrackSelection() throws Exception {
+        testTrackSelection(R.raw.video_480x360_mp4_h264_1350kbps_30fps_aac_stereo_128kbps_44100hz);
+        testTrackSelection(
+                R.raw.video_480x360_mp4_h264_1350kbps_30fps_aac_stereo_128kbps_44100hz_fragmented);
+        testTrackSelection(
+                R.raw.video_480x360_mp4_h264_1350kbps_30fps_aac_stereo_128kbps_44100hz_dash);
+    }
+
+    public void testBFrames() throws Exception {
+        testBFrames(R.raw.video_h264_main_b_frames);
+        testBFrames(R.raw.video_h264_main_b_frames_frag);
+    }
+
+    public void testBFrames(int res) throws Exception {
+        AssetFileDescriptor fd = mResources.openRawResourceFd(res);
+        MediaExtractor ex = new MediaExtractor();
+        ex.setDataSource(fd.getFileDescriptor(), fd.getStartOffset(), fd.getLength());
+        MediaFormat format = ex.getTrackFormat(0);
+        String mime = format.getString(MediaFormat.KEY_MIME);
+        assertTrue("not a video track. Wrong test file?", mime.startsWith("video/"));
+        MediaCodec dec = MediaCodec.createDecoderByType(mime);
+        Surface s = getActivity().getSurfaceHolder().getSurface();
+        dec.configure(format, s, null, 0);
+        dec.start();
+        ByteBuffer[] buf = dec.getInputBuffers();
+        ex.selectTrack(0);
+        MediaCodec.BufferInfo info = new MediaCodec.BufferInfo();
+        long lastPresentationTimeUsFromExtractor = -1;
+        long lastPresentationTimeUsFromDecoder = -1;
+        boolean inputoutoforder = false;
+        while(true) {
+            int flags = ex.getSampleFlags();
+            long time = ex.getSampleTime();
+            if (time >= 0 && time < lastPresentationTimeUsFromExtractor) {
+                inputoutoforder = true;
+            }
+            lastPresentationTimeUsFromExtractor = time;
+            int bufidx = dec.dequeueInputBuffer(5000);
+            if (bufidx >= 0) {
+                int n = ex.readSampleData(buf[bufidx], 0);
+                if (n < 0) {
+                    flags = MediaCodec.BUFFER_FLAG_END_OF_STREAM;
+                    time = 0;
+                    n = 0;
+                }
+                dec.queueInputBuffer(bufidx, 0, n, time, flags);
+                ex.advance();
+            }
+            int status = dec.dequeueOutputBuffer(info, 5000);
+            if (status >= 0) {
+                if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
+                    break;
+                }
+                assertTrue("out of order timestamp from decoder",
+                        info.presentationTimeUs > lastPresentationTimeUsFromDecoder);
+                dec.releaseOutputBuffer(status, true);
+                lastPresentationTimeUsFromDecoder = info.presentationTimeUs;
+            }
+        }
+        assertTrue("extractor timestamps were ordered, wrong test file?", inputoutoforder);
+        dec.release();
+        ex.release();
+      }
+
+    private void testTrackSelection(int resid) throws Exception {
+        AssetFileDescriptor fd1 = null;
+        try {
+            fd1 = mResources.openRawResourceFd(resid);
+            MediaExtractor ex1 = new MediaExtractor();
+            ex1.setDataSource(fd1.getFileDescriptor(), fd1.getStartOffset(), fd1.getLength());
+
+            ByteBuffer buf1 = ByteBuffer.allocate(1024*1024);
+            ArrayList<Integer> vid = new ArrayList<Integer>();
+            ArrayList<Integer> aud = new ArrayList<Integer>();
+
+            // scan the file once and build lists of audio and video samples
+            ex1.selectTrack(0);
+            ex1.selectTrack(1);
+            while(true) {
+                int n1 = ex1.readSampleData(buf1, 0);
+                if (n1 < 0) {
+                    break;
+                }
+                int idx = ex1.getSampleTrackIndex();
+                if (idx == 0) {
+                    vid.add(n1);
+                } else if (idx == 1) {
+                    aud.add(n1);
+                } else {
+                    fail("unexpected track index: " + idx);
+                }
+                ex1.advance();
+            }
+
+            // read the video track once, then rewind and do it again, and
+            // verify we get the right samples
+            ex1.release();
+            ex1 = new MediaExtractor();
+            ex1.setDataSource(fd1.getFileDescriptor(), fd1.getStartOffset(), fd1.getLength());
+            ex1.selectTrack(0);
+            for (int i = 0; i < 2; i++) {
+                ex1.seekTo(0, MediaExtractor.SEEK_TO_NEXT_SYNC);
+                int idx = 0;
+                while(true) {
+                    int n1 = ex1.readSampleData(buf1, 0);
+                    if (n1 < 0) {
+                        assertEquals(vid.size(), idx);
+                        break;
+                    }
+                    assertEquals(vid.get(idx++).intValue(), n1);
+                    ex1.advance();
+                }
+            }
+
+            // read the audio track once, then rewind and do it again, and
+            // verify we get the right samples
+            ex1.release();
+            ex1 = new MediaExtractor();
+            ex1.setDataSource(fd1.getFileDescriptor(), fd1.getStartOffset(), fd1.getLength());
+            ex1.selectTrack(1);
+            for (int i = 0; i < 2; i++) {
+                ex1.seekTo(0, MediaExtractor.SEEK_TO_NEXT_SYNC);
+                int idx = 0;
+                while(true) {
+                    int n1 = ex1.readSampleData(buf1, 0);
+                    if (n1 < 0) {
+                        assertEquals(aud.size(), idx);
+                        break;
+                    }
+                    assertEquals(aud.get(idx++).intValue(), n1);
+                    ex1.advance();
+                }
+            }
+
+            // read the video track first, then rewind and get the audio track instead, and
+            // verify we get the right samples
+            ex1.release();
+            ex1 = new MediaExtractor();
+            ex1.setDataSource(fd1.getFileDescriptor(), fd1.getStartOffset(), fd1.getLength());
+            for (int i = 0; i < 2; i++) {
+                ex1.selectTrack(i);
+                ex1.seekTo(0, MediaExtractor.SEEK_TO_NEXT_SYNC);
+                int idx = 0;
+                while(true) {
+                    int n1 = ex1.readSampleData(buf1, 0);
+                    if (i == 0) {
+                        if (n1 < 0) {
+                            assertEquals(vid.size(), idx);
+                            break;
+                        }
+                        assertEquals(vid.get(idx++).intValue(), n1);
+                    } else if (i == 1) {
+                        if (n1 < 0) {
+                            assertEquals(aud.size(), idx);
+                            break;
+                        }
+                        assertEquals(aud.get(idx++).intValue(), n1);
+                    } else {
+                        fail("unexpected track index: " + idx);
+                    }
+                    ex1.advance();
+                }
+                ex1.unselectTrack(i);
+            }
+
+            // read the video track first, then rewind, enable the audio track in addition
+            // to the video track, and verify we get the right samples
+            ex1.release();
+            ex1 = new MediaExtractor();
+            ex1.setDataSource(fd1.getFileDescriptor(), fd1.getStartOffset(), fd1.getLength());
+            for (int i = 0; i < 2; i++) {
+                ex1.selectTrack(i);
+                ex1.seekTo(0, MediaExtractor.SEEK_TO_NEXT_SYNC);
+                int vididx = 0;
+                int audidx = 0;
+                while(true) {
+                    int n1 = ex1.readSampleData(buf1, 0);
+                    if (n1 < 0) {
+                        // we should have read all audio and all video samples at this point
+                        assertEquals(vid.size(), vididx);
+                        if (i == 1) {
+                            assertEquals(aud.size(), audidx);
+                        }
+                        break;
+                    }
+                    int trackidx = ex1.getSampleTrackIndex();
+                    if (trackidx == 0) {
+                        assertEquals(vid.get(vididx++).intValue(), n1);
+                    } else if (trackidx == 1) {
+                        assertEquals(aud.get(audidx++).intValue(), n1);
+                    } else {
+                        fail("unexpected track index: " + trackidx);
+                    }
+                    ex1.advance();
+                }
+            }
+
+            // read both tracks from the start, then rewind and verify we get the right
+            // samples both times
+            ex1.release();
+            ex1 = new MediaExtractor();
+            ex1.setDataSource(fd1.getFileDescriptor(), fd1.getStartOffset(), fd1.getLength());
+            for (int i = 0; i < 2; i++) {
+                ex1.selectTrack(0);
+                ex1.selectTrack(1);
+                ex1.seekTo(0, MediaExtractor.SEEK_TO_NEXT_SYNC);
+                int vididx = 0;
+                int audidx = 0;
+                while(true) {
+                    int n1 = ex1.readSampleData(buf1, 0);
+                    if (n1 < 0) {
+                        // we should have read all audio and all video samples at this point
+                        assertEquals(vid.size(), vididx);
+                        assertEquals(aud.size(), audidx);
+                        break;
+                    }
+                    int trackidx = ex1.getSampleTrackIndex();
+                    if (trackidx == 0) {
+                        assertEquals(vid.get(vididx++).intValue(), n1);
+                    } else if (trackidx == 1) {
+                        assertEquals(aud.get(audidx++).intValue(), n1);
+                    } else {
+                        fail("unexpected track index: " + trackidx);
+                    }
+                    ex1.advance();
+                }
+            }
+
+        } finally {
+            if (fd1 != null) {
+                fd1.close();
+            }
+        }
+    }
+
+    public void testDecodeFragmented() throws Exception {
+        testDecodeFragmented(R.raw.video_480x360_mp4_h264_1350kbps_30fps_aac_stereo_128kbps_44100hz,
+                R.raw.video_480x360_mp4_h264_1350kbps_30fps_aac_stereo_128kbps_44100hz_fragmented);
+        testDecodeFragmented(R.raw.video_480x360_mp4_h264_1350kbps_30fps_aac_stereo_128kbps_44100hz,
+                R.raw.video_480x360_mp4_h264_1350kbps_30fps_aac_stereo_128kbps_44100hz_dash);
+    }
+
+    private void testDecodeFragmented(int reference, int teststream) throws Exception {
+        AssetFileDescriptor fd1 = null;
+        AssetFileDescriptor fd2 = null;
+        try {
+            fd1 = mResources.openRawResourceFd(reference);
+            MediaExtractor ex1 = new MediaExtractor();
+            ex1.setDataSource(fd1.getFileDescriptor(), fd1.getStartOffset(), fd1.getLength());
+
+            fd2 = mResources.openRawResourceFd(teststream);
+            MediaExtractor ex2 = new MediaExtractor();
+            ex2.setDataSource(fd2.getFileDescriptor(), fd2.getStartOffset(), fd2.getLength());
+
+            assertEquals("different track count", ex1.getTrackCount(), ex2.getTrackCount());
+
+            ByteBuffer buf1 = ByteBuffer.allocate(1024*1024);
+            ByteBuffer buf2 = ByteBuffer.allocate(1024*1024);
+
+            for (int i = 0; i < ex1.getTrackCount(); i++) {
+                // note: this assumes the tracks are reported in the order in which they appear
+                // in the file.
+                ex1.seekTo(0, MediaExtractor.SEEK_TO_NEXT_SYNC);
+                ex1.selectTrack(i);
+                ex2.seekTo(0, MediaExtractor.SEEK_TO_NEXT_SYNC);
+                ex2.selectTrack(i);
+
+                while(true) {
+                    int n1 = ex1.readSampleData(buf1, 0);
+                    int n2 = ex2.readSampleData(buf2, 0);
+                    assertEquals("different buffer size on track " + i, n1, n2);
+
+                    if (n1 < 0) {
+                        break;
+                    }
+                    // see bug 13008204
+                    buf1.limit(n1);
+                    buf2.limit(n2);
+                    buf1.rewind();
+                    buf2.rewind();
+
+                    assertEquals("limit does not match return value on track " + i,
+                            n1, buf1.limit());
+                    assertEquals("limit does not match return value on track " + i,
+                            n2, buf2.limit());
+
+                    assertEquals("buffer data did not match on track " + i, buf1, buf2);
+
+                    ex1.advance();
+                    ex2.advance();
+                }
+                ex1.unselectTrack(i);
+                ex2.unselectTrack(i);
+            }
+        } finally {
+            if (fd1 != null) {
+                fd1.close();
+            }
+            if (fd2 != null) {
+                fd2.close();
+            }
+        }
+    }
+
+
     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 +468,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 +482,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 +499,54 @@
         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, RESET_MODE_EOS_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 +567,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 +602,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 +626,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 +660,33 @@
 
                 if (info.size > 0) {
                     noOutputCounter = 0;
+                    if (timestamps != null) {
+                        timestamps.add(info.presentationTimeUs);
+                    }
                 }
-                if (info.size > 0 && resetMode != RESET_MODE_NONE) {
+                if (info.size > 0 &&
+                        resetMode != RESET_MODE_NONE && resetMode != RESET_MODE_EOS_FLUSH) {
                     // 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,15 +697,29 @@
                     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 */);
 
                 if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
                     Log.d(TAG, "saw output EOS.");
-                    sawOutputEOS = true;
+                    if (resetMode == RESET_MODE_EOS_FLUSH) {
+                        resetMode = RESET_MODE_NONE;
+                        codec.flush();
+                        extractor.seekTo(0, MediaExtractor.SEEK_TO_NEXT_SYNC);
+                        sawInputEOS = false;
+                        samplecounter = 0;
+                        decoded = new short[0];
+                        decodedIdx = 0;
+                        if (timestamps != null) {
+                            timestamps.clear();
+                        }
+                    } else {
+                        sawOutputEOS = true;
+                    }
                 }
             } else if (res == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
                 codecOutputBuffers = codec.getOutputBuffers();
@@ -301,12 +733,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(
@@ -320,6 +846,19 @@
         assertEquals("different number of frames when using Surface", frames1, frames2);
     }
 
+    public void testCodecBasicHEVC() throws Exception {
+        Surface s = getActivity().getSurfaceHolder().getSurface();
+        int frames1 = countFrames(
+                R.raw.video_1280x720_mp4_hevc_1150kbps_30fps_aac_stereo_128kbps_48000hz,
+                RESET_MODE_NONE, -1 /* eosframe */, s);
+        assertEquals("wrong number of frames decoded", 300, frames1);
+
+        int frames2 = countFrames(
+                R.raw.video_1280x720_mp4_hevc_1150kbps_30fps_aac_stereo_128kbps_48000hz,
+                RESET_MODE_NONE, -1 /* eosframe */, null);
+        assertEquals("different number of frames when using Surface", frames1, frames2);
+    }
+
     public void testCodecBasicH263() throws Exception {
         Surface s = getActivity().getSurfaceHolder().getSurface();
         int frames1 = countFrames(
@@ -388,6 +927,14 @@
         assertEquals("wrong number of frames decoded", 120, frames1);
     }
 
+    public void testCodecEarlyEOSHEVC() throws Exception {
+        Surface s = getActivity().getSurfaceHolder().getSurface();
+        int frames1 = countFrames(
+                R.raw.video_1280x720_mp4_hevc_1150kbps_30fps_aac_stereo_128kbps_48000hz,
+                RESET_MODE_NONE, 120 /* eosframe */, s);
+        assertEquals("wrong number of frames decoded", 120, frames1);
+    }
+
     public void testCodecEarlyEOSMpeg4() throws Exception {
         Surface s = getActivity().getSurfaceHolder().getSurface();
         int frames1 = countFrames(
@@ -423,6 +970,17 @@
                 R.raw.video_480x360_mp4_h264_1000kbps_25fps_aac_stereo_128kbps_44100hz, s);
     }
 
+    public void testCodecResetsHEVCWithoutSurface() throws Exception {
+        testCodecResets(
+                R.raw.video_1280x720_mp4_hevc_1150kbps_30fps_aac_stereo_128kbps_48000hz, null);
+    }
+
+    public void testCodecResetsHEVCWithSurface() throws Exception {
+        Surface s = getActivity().getSurfaceHolder().getSurface();
+        testCodecResets(
+                R.raw.video_1280x720_mp4_hevc_1150kbps_30fps_aac_stereo_128kbps_48000hz, s);
+    }
+
     public void testCodecResetsH263WithoutSurface() throws Exception {
         testCodecResets(
                 R.raw.video_176x144_3gp_h263_300kbps_12fps_aac_stereo_128kbps_22050hz, null);
@@ -472,21 +1030,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 +1055,148 @@
         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("hevc")) {
+                    return MediaCodec.createByCodecName("OMX.google.hevc.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 +1204,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 +1233,291 @@
                     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 testEOSBehaviorHEVC() throws Exception {
+        testEOSBehavior(R.raw.video_480x360_mp4_hevc_650kbps_30fps_aac_stereo_128kbps_48000hz, 17);
+        testEOSBehavior(R.raw.video_480x360_mp4_hevc_650kbps_30fps_aac_stereo_128kbps_48000hz, 23);
+        testEOSBehavior(R.raw.video_480x360_mp4_hevc_650kbps_30fps_aac_stereo_128kbps_48000hz, 49);
     }
 
     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 +1550,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 +1608,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 f2fb322..a480d97 100644
--- a/tests/tests/media/src/android/media/cts/EncodeDecodeTest.java
+++ b/tests/tests/media/src/android/media/cts/EncodeDecodeTest.java
@@ -605,7 +605,7 @@
                     encoderOutputBuffers = encoder.getOutputBuffers();
                     if (VERBOSE) Log.d(TAG, "encoder output buffers changed");
                 } else if (encoderStatus == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
-                    // not expected for an encoder
+                    // expected on API 18+
                     MediaFormat newFormat = encoder.getOutputFormat();
                     if (VERBOSE) Log.d(TAG, "encoder output format changed: " + newFormat);
                 } else if (encoderStatus < 0) {
@@ -894,7 +894,7 @@
                         encoderOutputBuffers = encoder.getOutputBuffers();
                         if (VERBOSE) Log.d(TAG, "encoder output buffers changed");
                     } else if (encoderStatus == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
-                        // not expected for an encoder
+                        // expected on API 18+
                         MediaFormat newFormat = encoder.getOutputFormat();
                         if (VERBOSE) Log.d(TAG, "encoder output format changed: " + newFormat);
                     } else if (encoderStatus < 0) {
@@ -1073,16 +1073,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 7b33b75..89b06dc 100755
--- a/tests/tests/media/src/android/media/cts/EncodeVirtualDisplayTest.java
+++ b/tests/tests/media/src/android/media/cts/EncodeVirtualDisplayTest.java
@@ -239,7 +239,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 40b4949..43b769a 100644
--- a/tests/tests/media/src/android/media/cts/ExtractDecodeEditEncodeMuxTest.java
+++ b/tests/tests/media/src/android/media/cts/ExtractDecodeEditEncodeMuxTest.java
@@ -517,7 +517,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();
@@ -537,7 +538,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.
@@ -551,7 +553,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();
@@ -564,7 +567,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/ImageReaderDecoderTest.java b/tests/tests/media/src/android/media/cts/ImageReaderDecoderTest.java
new file mode 100644
index 0000000..f115b63
--- /dev/null
+++ b/tests/tests/media/src/android/media/cts/ImageReaderDecoderTest.java
@@ -0,0 +1,572 @@
+/*
+ * 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.media.cts;
+
+import com.android.cts.media.R;
+
+import android.content.Context;
+import android.content.res.AssetFileDescriptor;
+import android.content.res.Resources;
+import android.graphics.ImageFormat;
+import android.media.Image;
+import android.media.Image.Plane;
+import android.media.ImageReader;
+import android.media.MediaCodec;
+import android.media.MediaCodecInfo;
+import android.media.MediaCodecList;
+import android.media.MediaExtractor;
+import android.media.MediaFormat;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.test.AndroidTestCase;
+import android.util.Log;
+import android.view.Surface;
+
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Basic test for ImageReader APIs.
+ * <p>
+ * It uses MediaCodec to decode a short video stream, send the video frames to
+ * the surface provided by ImageReader. Then compare if output buffers of the
+ * ImageReader matches the output buffers of the MediaCodec. The video format
+ * used here is AVC although the compression format doesn't matter for this
+ * test. For decoder test, hw and sw decoders are tested,
+ * </p>
+ */
+public class ImageReaderDecoderTest extends AndroidTestCase {
+    private static final String TAG = "ImageReaderDecoderTest";
+    private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
+    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+    private static final long DEFAULT_TIMEOUT_US = 10000;
+    private static final long WAIT_FOR_IMAGE_TIMEOUT_MS = 1000;
+    private static final String DEBUG_FILE_NAME_BASE = "/sdcard/";
+    private static final int NUM_FRAME_DECODED = 100;
+    private static final int MAX_NUM_IMAGES = 3;
+
+    private Resources mResources;
+    private MediaCodec.BufferInfo mBufferInfo = new MediaCodec.BufferInfo();
+    private ByteBuffer[] mInputBuffers;
+    private ByteBuffer[] mOutputBuffers;
+    private ImageReader mReader;
+    private Surface mReaderSurface;
+    private HandlerThread mHandlerThread;
+    private Handler mHandler;
+    private ImageListener mImageListener;
+
+    @Override
+    public void setContext(Context context) {
+        super.setContext(context);
+        mResources = mContext.getResources();
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mHandlerThread = new HandlerThread(TAG);
+        mHandlerThread.start();
+        mHandler = new Handler(mHandlerThread.getLooper());
+        mImageListener = new ImageListener();
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        mHandlerThread.quitSafely();
+        mHandler = null;
+    }
+
+    /**
+     * Test ImageReader with 480x360 hw AVC decoding for flexible yuv format, which is mandatory
+     * to be supported by hw decoder.
+     */
+    public void testHwAVCDecode360pForFlexibleYuv() throws Exception {
+        try {
+            int format = ImageFormat.YUV_420_888;
+            videoDecodeToSurface(
+                    R.raw.video_480x360_mp4_h264_1000kbps_25fps_aac_stereo_128kbps_44100hz,
+                    /* width */480, /* height */ 360, format, /* useHw */ true);
+        } finally {
+            closeImageReader();
+        }
+    }
+
+    /**
+     * Test ImageReader with 480x360 sw AVC decoding for flexible yuv format, which is mandatory
+     * to be supported by sw decoder.
+     */
+    public void testSwAVCDecode360pForFlexibleYuv() throws Exception {
+        try {
+            int format = ImageFormat.YUV_420_888;
+            videoDecodeToSurface(
+                    R.raw.video_480x360_mp4_h264_1000kbps_25fps_aac_stereo_128kbps_44100hz,
+                    /* width */ 480, /* height */ 360, format, /* useHw */ false);
+        } finally {
+            closeImageReader();
+        }
+    }
+
+    private static class ImageListener implements ImageReader.OnImageAvailableListener {
+        private final LinkedBlockingQueue<Image> mQueue =
+                new LinkedBlockingQueue<Image>();
+
+        @Override
+        public void onImageAvailable(ImageReader reader) {
+            try {
+                mQueue.put(reader.acquireNextImage());
+            } catch (InterruptedException e) {
+                throw new UnsupportedOperationException(
+                        "Can't handle InterruptedException in onImageAvailable");
+            }
+        }
+
+        /**
+         * Get an image from the image reader.
+         *
+         * @param timeout Timeout value for the wait.
+         * @return The image from the image reader.
+         */
+        public Image getImage(long timeout) throws InterruptedException {
+            Image image = mQueue.poll(timeout, TimeUnit.MILLISECONDS);
+            assertNotNull("Wait for an image timed out in " + timeout + "ms", image);
+            return image;
+        }
+    }
+
+    private void videoDecodeToSurface(int video, int width,
+            int height, int imageFormat, boolean useHw) throws Exception {
+        MediaCodec decoder = null;
+        MediaExtractor extractor;
+        int outputBufferIndex;
+        ByteBuffer[] decoderInputBuffers;
+        ByteBuffer[] decoderOutputBuffers;
+
+        AssetFileDescriptor vidFD = mResources.openRawResourceFd(video);
+
+        extractor = new MediaExtractor();
+        extractor.setDataSource(vidFD.getFileDescriptor(), vidFD.getStartOffset(),
+                vidFD.getLength());
+
+        MediaFormat mediaFmt = extractor.getTrackFormat(0);
+        String mime = mediaFmt.getString(MediaFormat.KEY_MIME);
+        try {
+            // Create decoder
+            decoder = createDecoder(mime, useHw);
+            assertNotNull("couldn't find decoder", decoder);
+            if (VERBOSE) Log.v(TAG, "using decoder: " + decoder.getName());
+
+            decodeFramesToImageReader(width, height, imageFormat, decoder,
+                    extractor, mediaFmt, mime);
+
+            decoder.stop();
+        } finally {
+            decoder.release();
+        }
+
+    }
+
+    /**
+     * Decode video frames to image reader.
+     */
+    private void decodeFramesToImageReader(int width, int height, int imageFormat,
+            MediaCodec decoder, MediaExtractor extractor, MediaFormat mediaFmt, String mime)
+            throws Exception, InterruptedException {
+        ByteBuffer[] decoderInputBuffers;
+        ByteBuffer[] decoderOutputBuffers;
+        // Get decoder output ImageFormat, will be used to create ImageReader
+        int codecImageFormat = getImageFormatFromCodecType(mime);
+        assertEquals("Codec image format should match image reader format",
+                imageFormat, codecImageFormat);
+        createImageReader(width, height, imageFormat, MAX_NUM_IMAGES, mImageListener);
+
+        // Configure decoder.
+        if (VERBOSE) Log.v(TAG, "stream format: " + mediaFmt);
+        decoder.configure(mediaFmt, mReaderSurface, /*crypto*/null, /*flags*/0);
+        decoder.start();
+        decoderInputBuffers = decoder.getInputBuffers();
+        decoderOutputBuffers = decoder.getOutputBuffers();
+        extractor.selectTrack(0);
+
+        // Start decoding and get Image, only test the first NUM_FRAME_DECODED frames.
+        MediaCodec.BufferInfo info = new MediaCodec.BufferInfo();
+        boolean sawInputEOS = false;
+        boolean sawOutputEOS = false;
+        int outputFrameCount = 0;
+        while (!sawOutputEOS && outputFrameCount < NUM_FRAME_DECODED) {
+            if (VERBOSE) Log.v(TAG, "loop:" + outputFrameCount);
+            // Feed input frame.
+            if (!sawInputEOS) {
+                int inputBufIndex = decoder.dequeueInputBuffer(DEFAULT_TIMEOUT_US);
+                if (inputBufIndex >= 0) {
+                    ByteBuffer dstBuf = decoderInputBuffers[inputBufIndex];
+                    int sampleSize =
+                        extractor.readSampleData(dstBuf, 0 /* offset */);
+
+                    if (VERBOSE) Log.v(TAG, "queue a input buffer, idx/size: "
+                        + inputBufIndex + "/" + sampleSize);
+
+                    long presentationTimeUs = 0;
+
+                    if (sampleSize < 0) {
+                        if (VERBOSE) Log.v(TAG, "saw input EOS.");
+                        sawInputEOS = true;
+                        sampleSize = 0;
+                    } else {
+                        presentationTimeUs = extractor.getSampleTime();
+                    }
+
+                    decoder.queueInputBuffer(
+                            inputBufIndex,
+                            0 /* offset */,
+                            sampleSize,
+                            presentationTimeUs,
+                            sawInputEOS ? MediaCodec.BUFFER_FLAG_END_OF_STREAM : 0);
+
+                    if (!sawInputEOS) {
+                        extractor.advance();
+                    }
+                }
+            }
+
+            // Get output frame
+            int res = decoder.dequeueOutputBuffer(info, DEFAULT_TIMEOUT_US);
+            if (VERBOSE) Log.v(TAG, "got a buffer: " + info.size + "/" + res);
+            if (res == MediaCodec.INFO_TRY_AGAIN_LATER) {
+                // no output available yet
+                if (VERBOSE) Log.v(TAG, "no output frame available");
+            } else if (res == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
+                // decoder output buffers changed, need update.
+                if (VERBOSE) Log.v(TAG, "decoder output buffers changed");
+                decoderOutputBuffers = decoder.getOutputBuffers();
+            } else if (res == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
+                // this happens before the first frame is returned.
+                MediaFormat outFormat = decoder.getOutputFormat();
+                if (VERBOSE) Log.v(TAG, "decoder output format changed: " + outFormat);
+            } else if (res < 0) {
+                // Should be decoding error.
+                fail("unexpected result from deocder.dequeueOutputBuffer: " + res);
+            } else {
+                // res >= 0: normal decoding case, copy the output buffer.
+                // Will use it as reference to valid the ImageReader output
+                // Some decoders output a 0-sized buffer at the end. Ignore those.
+                outputFrameCount++;
+                boolean doRender = (info.size != 0);
+
+                decoder.releaseOutputBuffer(res, doRender);
+                if (doRender) {
+                    // Read image and verify
+                    Image image = mImageListener.getImage(WAIT_FOR_IMAGE_TIMEOUT_MS);
+                    Plane[] imagePlanes = image.getPlanes();
+
+                    //Verify
+                    String fileName = DEBUG_FILE_NAME_BASE + width + "x" + height + "_"
+                            + outputFrameCount + ".yuv";
+                    validateImage(image, width, height, imageFormat, fileName);
+
+                    if (VERBOSE) {
+                        Log.v(TAG, "Image " + outputFrameCount + " Info:");
+                        Log.v(TAG, "first plane pixelstride " + imagePlanes[0].getPixelStride());
+                        Log.v(TAG, "first plane rowstride " + imagePlanes[0].getRowStride());
+                        Log.v(TAG, "Image timestamp:" + image.getTimestamp());
+                    }
+                    image.close();
+                }
+            }
+        }
+    }
+
+    private int getImageFormatFromCodecType(String mimeType) {
+        // TODO: Need pick a codec first, then get the codec info, will revisit for future.
+        MediaCodecInfo codecInfo = getCodecInfoByType(mimeType);
+        if (VERBOSE) Log.v(TAG, "found decoder: " + codecInfo.getName());
+
+        int colorFormat = selectDecoderOutputColorFormat(codecInfo, mimeType);
+        if (VERBOSE) Log.v(TAG, "found decoder output color format: " + colorFormat);
+        switch (colorFormat) {
+            case MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Planar:
+                // TODO: This is fishy, OMX YUV420P is not identical as YV12, U and V planes are
+                // swapped actually. It should give YV12 if producer is setup first, that is, after
+                // Configuring the Surface (provided by ImageReader object) into codec, but this
+                // is Chicken-egg issue, do the translation on behalf of driver here:)
+                return ImageFormat.YV12;
+            case MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420SemiPlanar:
+                // same as above.
+                return ImageFormat.NV21;
+            default:
+                return colorFormat;
+        }
+    }
+
+    private static MediaCodecInfo getCodecInfoByType(String mimeType) {
+        int numCodecs = MediaCodecList.getCodecCount();
+        for (int i = 0; i < numCodecs; i++) {
+            MediaCodecInfo codecInfo = MediaCodecList.getCodecInfoAt(i);
+
+            if (codecInfo.isEncoder()) {
+                continue;
+            }
+
+            String[] types = codecInfo.getSupportedTypes();
+            for (int j = 0; j < types.length; j++) {
+                if (types[j].equalsIgnoreCase(mimeType)) {
+                    return codecInfo;
+                }
+            }
+        }
+        return null;
+    }
+
+    private static int selectDecoderOutputColorFormat(MediaCodecInfo codecInfo, String mimeType) {
+        MediaCodecInfo.CodecCapabilities capabilities = codecInfo.getCapabilitiesForType(mimeType);
+        for (int i = 0; i < capabilities.colorFormats.length; i++) {
+            int colorFormat = capabilities.colorFormats[i];
+            if (isRecognizedFormat(colorFormat)) {
+                return colorFormat;
+            }
+        }
+        fail("couldn't find a good color format for " + codecInfo.getName() + " / " + mimeType);
+        return 0;   // not reached
+    }
+
+    // Need make this function simple, may be merge into above functions.
+    private static boolean isRecognizedFormat(int colorFormat) {
+        if (VERBOSE) Log.v(TAG, "colorformat: " + colorFormat);
+        switch (colorFormat) {
+            // these are the formats we know how to handle for this test
+            case MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Planar:
+            case MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420PackedPlanar:
+            case MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420SemiPlanar:
+            case MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420PackedSemiPlanar:
+            case MediaCodecInfo.CodecCapabilities.COLOR_TI_FormatYUV420PackedSemiPlanar:
+            case ImageFormat.YUV_420_888:
+                return true;
+            default:
+                return false;
+        }
+    }
+
+    private MediaCodec createDecoder(String mime, boolean useHw) throws Exception {
+        if (!useHw) {
+            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.vpx.decoder");
+            }
+        }
+        return MediaCodec.createDecoderByType(mime);
+    }
+
+    /**
+     * Validate image based on format and size.
+     *
+     * @param image The image to be validated.
+     * @param width The image width.
+     * @param height The image height.
+     * @param format The image format.
+     * @param filePath The debug dump file path, null if don't want to dump to file.
+     */
+    public static void validateImage(Image image, int width, int height, int format,
+            String filePath) {
+        assertNotNull("Input image is invalid", image);
+        assertEquals("Format doesn't match", format, image.getFormat());
+        assertEquals("Width doesn't match", width, image.getWidth());
+        assertEquals("Height doesn't match", height, image.getHeight());
+
+        if(VERBOSE) Log.v(TAG, "validating Image");
+        byte[] data = getDataFromImage(image);
+        assertTrue("Invalid image data", data != null && data.length > 0);
+
+        validateYuvData(data, width, height, format, image.getTimestamp(), filePath);
+    }
+
+    private static void validateYuvData(byte[] yuvData, int width, int height, int format,
+            long ts, String fileName) {
+
+        assertTrue("YUV format must be one of the YUV420_888, NV21, or YV12",
+                format == ImageFormat.YUV_420_888 ||
+                format == ImageFormat.NV21 ||
+                format == ImageFormat.YV12);
+
+        if (VERBOSE) Log.v(TAG, "Validating YUV data");
+        int expectedSize = width * height * ImageFormat.getBitsPerPixel(format) / 8;
+        assertEquals("Yuv data doesn't match", expectedSize, yuvData.length);
+
+        if (DEBUG && fileName != null) {
+            dumpFile(fileName, yuvData);
+        }
+    }
+
+    private static void checkYuvFormat(int format) {
+        if ((format != ImageFormat.YUV_420_888) &&
+                (format != ImageFormat.NV21) &&
+                (format != ImageFormat.YV12)) {
+            fail("Wrong formats: " + format);
+        }
+    }
+    /**
+     * <p>Check android image format validity for an image, only support below formats:</p>
+     *
+     * <p>Valid formats are YUV_420_888/NV21/YV12 for video decoder</p>
+     */
+    private static void checkAndroidImageFormat(Image image) {
+        int format = image.getFormat();
+        Plane[] planes = image.getPlanes();
+        switch (format) {
+            case ImageFormat.YUV_420_888:
+            case ImageFormat.NV21:
+            case ImageFormat.YV12:
+                assertEquals("YUV420 format Images should have 3 planes", 3, planes.length);
+                break;
+            default:
+                fail("Unsupported Image Format: " + format);
+        }
+    }
+
+    /**
+     * Get a byte array image data from an Image object.
+     * <p>
+     * Read data from all planes of an Image into a contiguous unpadded,
+     * unpacked 1-D linear byte array, such that it can be write into disk, or
+     * accessed by software conveniently. It supports YUV_420_888/NV21/YV12
+     * input Image format.
+     * </p>
+     * <p>
+     * For YUV_420_888/NV21/YV12/Y8/Y16, it returns a byte array that contains
+     * the Y plane data first, followed by U(Cb), V(Cr) planes if there is any
+     * (xstride = width, ystride = height for chroma and luma components).
+     * </p>
+     */
+    private static byte[] getDataFromImage(Image image) {
+        assertNotNull("Invalid image:", image);
+        int format = image.getFormat();
+        int width = image.getWidth();
+        int height = image.getHeight();
+        int rowStride, pixelStride;
+        byte[] data = null;
+
+        // Read image data
+        Plane[] planes = image.getPlanes();
+        assertTrue("Fail to get image planes", planes != null && planes.length > 0);
+
+        // Check image validity
+        checkAndroidImageFormat(image);
+
+        ByteBuffer buffer = null;
+
+        int offset = 0;
+        data = new byte[width * height * ImageFormat.getBitsPerPixel(format) / 8];
+        byte[] rowData = new byte[planes[0].getRowStride()];
+        if(VERBOSE) Log.v(TAG, "get data from " + planes.length + " planes");
+        for (int i = 0; i < planes.length; i++) {
+            buffer = planes[i].getBuffer();
+            assertNotNull("Fail to get bytebuffer from plane", buffer);
+            rowStride = planes[i].getRowStride();
+            pixelStride = planes[i].getPixelStride();
+            assertTrue("pixel stride " + pixelStride + " is invalid", pixelStride > 0);
+            if (VERBOSE) {
+                Log.v(TAG, "pixelStride " + pixelStride);
+                Log.v(TAG, "rowStride " + rowStride);
+                Log.v(TAG, "width " + width);
+                Log.v(TAG, "height " + height);
+            }
+            // For multi-planar yuv images, assuming yuv420 with 2x2 chroma subsampling.
+            int w = (i == 0) ? width : width / 2;
+            int h = (i == 0) ? height : height / 2;
+            assertTrue("rowStride " + rowStride + " should be >= width " + w , rowStride >= w);
+            for (int row = 0; row < h; row++) {
+                int bytesPerPixel = ImageFormat.getBitsPerPixel(format) / 8;
+                if (pixelStride == bytesPerPixel) {
+                    // Special case: optimized read of the entire row
+                    int length = w * bytesPerPixel;
+                    buffer.get(data, offset, length);
+                    // Advance buffer the remainder of the row stride
+                    buffer.position(buffer.position() + rowStride - length);
+                    offset += length;
+                } else {
+                    // Generic case: should work for any pixelStride but slower.
+                    // Use intermediate buffer to avoid read byte-by-byte from
+                    // DirectByteBuffer, which is very bad for performance
+                    buffer.get(rowData, 0, rowStride);
+                    for (int col = 0; col < w; col++) {
+                        data[offset++] = rowData[col * pixelStride];
+                    }
+                }
+            }
+            if (VERBOSE) Log.v(TAG, "Finished reading data from plane " + i);
+        }
+        return data;
+    }
+
+    private static void dumpFile(String fileName, byte[] data) {
+        assertNotNull("fileName must not be null", fileName);
+        assertNotNull("data must not be null", data);
+
+        FileOutputStream outStream;
+        try {
+            Log.v(TAG, "output will be saved as " + fileName);
+            outStream = new FileOutputStream(fileName);
+        } catch (IOException ioe) {
+            throw new RuntimeException("Unable to create debug output file " + fileName, ioe);
+        }
+
+        try {
+            outStream.write(data);
+            outStream.close();
+        } catch (IOException ioe) {
+            throw new RuntimeException("failed writing data to file " + fileName, ioe);
+        }
+    }
+
+    private void createImageReader(int width, int height, int format, int maxNumImages,
+            ImageReader.OnImageAvailableListener listener) throws Exception {
+        closeImageReader();
+
+        mReader = ImageReader.newInstance(width, height, format, maxNumImages);
+        mReaderSurface = mReader.getSurface();
+        mReader.setOnImageAvailableListener(listener, mHandler);
+        if (VERBOSE) {
+            Log.v(TAG, String.format("Created ImageReader size (%dx%d), format %d", width, height,
+                    format));
+        }
+    }
+
+    /**
+     * Close the pending images then close current active {@link ImageReader} object.
+     */
+    private void closeImageReader() {
+        if (mReader != null) {
+            try {
+                // Close all possible pending images first.
+                Image image = mReader.acquireLatestImage();
+                if (image != null) {
+                    image.close();
+                }
+            } finally {
+                mReader.close();
+                mReader = null;
+            }
+        }
+    }
+}
diff --git a/tests/tests/media/src/android/media/cts/IvfReader.java b/tests/tests/media/src/android/media/cts/IvfReader.java
index 508ae25..2f679ae 100644
--- a/tests/tests/media/src/android/media/cts/IvfReader.java
+++ b/tests/tests/media/src/android/media/cts/IvfReader.java
@@ -22,18 +22,19 @@
 /**
  * A simple reader for an IVF file.
  *
- * IVF format is a simple container format for VP8 encoded frames.
+ * IVF format is a simple container format for VP8 encoded frames defined at
+ * http://wiki.multimedia.cx/index.php?title=IVF.
  * This reader is capable of getting frame count, width and height
  * from the header, and access individual frames randomly by
  * frame number.
  */
 
 public class IvfReader {
-    private static final byte HEADER_END = 32;
-    private static final byte FOURCC_HEAD = 8;
-    private static final byte WIDTH_HEAD = 12;
-    private static final byte HEIGHT_HEAD = 14;
-    private static final byte FRAMECOUNT_HEAD = 24;
+    private static final byte HEADER_SIZE = 32;
+    private static final byte FOURCC_OFFSET = 8;
+    private static final byte WIDTH_OFFSET = 12;
+    private static final byte HEIGHT_OFFSET = 14;
+    private static final byte FRAMECOUNT_OFFSET = 24;
     private static final byte FRAME_HEADER_SIZE = 12;
 
     private RandomAccessFile mIvfFile;
@@ -101,7 +102,7 @@
      * than 0 and less than frameCount.
      */
     public byte[] readFrame(int frameIndex) throws IOException {
-        if (frameIndex > mFrameCount | frameIndex < 0){
+        if (frameIndex > mFrameCount || frameIndex < 0){
             return null;
         }
         int frameSize = mFrameSizes[frameIndex];
@@ -124,7 +125,7 @@
     private boolean verifyHeader() throws IOException{
         mIvfFile.seek(0);
 
-        if (mIvfFile.length() < HEADER_END){
+        if (mIvfFile.length() < HEADER_SIZE){
             return false;
         }
 
@@ -135,7 +136,7 @@
                 (mIvfFile.readByte() == (byte)'F'));
 
         // Fourcc
-        mIvfFile.seek(FOURCC_HEAD);
+        mIvfFile.seek(FOURCC_OFFSET);
         boolean fourccMatch = ((mIvfFile.readByte() == (byte)'V') &&
                 (mIvfFile.readByte() == (byte)'P') &&
                 (mIvfFile.readByte() == (byte)'8') &&
@@ -146,15 +147,15 @@
 
     private void readHeaderData() throws IOException{
         // width
-        mIvfFile.seek(WIDTH_HEAD);
+        mIvfFile.seek(WIDTH_OFFSET);
         mWidth = (int) changeEndianness(mIvfFile.readShort());
 
         // height
-        mIvfFile.seek(HEIGHT_HEAD);
+        mIvfFile.seek(HEIGHT_OFFSET);
         mHeight = (int) changeEndianness(mIvfFile.readShort());
 
         // frame count
-        mIvfFile.seek(FRAMECOUNT_HEAD);
+        mIvfFile.seek(FRAMECOUNT_OFFSET);
         mFrameCount = changeEndianness(mIvfFile.readInt());
 
         // allocate frame metadata
@@ -163,7 +164,7 @@
     }
 
     private void readFrameMetadata() throws IOException{
-        int frameHead = HEADER_END;
+        int frameHead = HEADER_SIZE;
         for(int i = 0; i < mFrameCount; i++){
             mIvfFile.seek(frameHead);
             int frameSize = changeEndianness(mIvfFile.readInt());
diff --git a/tests/tests/media/src/android/media/cts/IvfWriter.java b/tests/tests/media/src/android/media/cts/IvfWriter.java
index ccc0ac5..075f73c 100644
--- a/tests/tests/media/src/android/media/cts/IvfWriter.java
+++ b/tests/tests/media/src/android/media/cts/IvfWriter.java
@@ -22,7 +22,8 @@
 /**
  * Writes an IVF file.
  *
- * IVF format is a simple container format for VP8 encoded frames.
+ * IVF format is a simple container format for VP8 encoded frames defined at
+ * http://wiki.multimedia.cx/index.php?title=IVF.
  */
 
 public class IvfWriter {
@@ -56,13 +57,13 @@
         mScale = scale;
         mRate = rate;
         mFrameCount = 0;
+        mOutputFile.setLength(0);
         mOutputFile.seek(HEADER_END);  // Skip the header for now, as framecount is unknown
     }
 
     /**
      * Initializes the IVF file writer with a microsecond timebase.
      *
-     *
      * Microsecond timebase is default for OMX thus stagefright.
      *
      * @param filename   name of the IVF file
@@ -87,7 +88,7 @@
      * Writes a single encoded VP8 frame with its frame header.
      *
      * @param frame     actual contents of the encoded frame data
-     * @param width     timestamp of the frame (in accordance to specified timebase)
+     * @param timeStamp timestamp of the frame (in accordance to specified timebase)
      */
     public void writeFrame(byte[] frame, long timeStamp) throws IOException {
         mOutputFile.write(makeIvfFrameHeader(frame.length, timeStamp));
diff --git a/tests/tests/media/src/android/media/cts/MediaCodecCapabilitiesTest.java b/tests/tests/media/src/android/media/cts/MediaCodecCapabilitiesTest.java
index 142318a..a3f1815 100644
--- a/tests/tests/media/src/android/media/cts/MediaCodecCapabilitiesTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaCodecCapabilitiesTest.java
@@ -32,6 +32,7 @@
 
     private static final String TAG = "MediaCodecCapabilitiesTest";
     private static final String AVC_MIME = "video/avc";
+    private static final String HEVC_MIME = "video/hevc";
     private static final int PLAY_TIME_MS = 30000;
 
     public void testAvcBaseline1() throws Exception {
@@ -123,6 +124,78 @@
                 + "&key=test_key1", 1920, 1080, PLAY_TIME_MS);
     }
 
+    public void testHevcMain1() throws Exception {
+        if (!supports(HEVC_MIME, CodecProfileLevel.HEVCProfileMain,
+                CodecProfileLevel.HEVCMainTierLevel1)) {
+            throw new RuntimeException("HECLevel1 support is required by CDD");
+        }
+        // We don't have a test stream, but at least we're testing
+        // that supports() returns true for something.
+    }
+    public void testHevcMain2() throws Exception {
+        if (!supports(HEVC_MIME, CodecProfileLevel.HEVCProfileMain,
+                CodecProfileLevel.HEVCMainTierLevel2)) {
+            Log.i(TAG, "HevcMain2 not supported");
+            return;
+        }
+    }
+
+    public void testHevcMain21() throws Exception {
+        if (!supports(HEVC_MIME, CodecProfileLevel.HEVCProfileMain,
+                CodecProfileLevel.HEVCMainTierLevel21)) {
+            Log.i(TAG, "HevcMain21 not supported");
+            return;
+        }
+    }
+
+    public void testHevcMain3() throws Exception {
+        if (!supports(HEVC_MIME, CodecProfileLevel.HEVCProfileMain,
+                CodecProfileLevel.HEVCMainTierLevel3)) {
+            Log.i(TAG, "HevcMain3 not supported");
+            return;
+        }
+    }
+
+    public void testHevcMain31() throws Exception {
+        if (!supports(HEVC_MIME, CodecProfileLevel.HEVCProfileMain,
+                CodecProfileLevel.HEVCMainTierLevel31)) {
+            Log.i(TAG, "HevcMain31 not supported");
+            return;
+        }
+    }
+
+    public void testHevcMain4() throws Exception {
+        if (!supports(HEVC_MIME, CodecProfileLevel.HEVCProfileMain,
+                CodecProfileLevel.HEVCMainTierLevel4)) {
+            Log.i(TAG, "HevcMain4 not supported");
+            return;
+        }
+    }
+
+    public void testHevcMain41() throws Exception {
+        if (!supports(HEVC_MIME, CodecProfileLevel.HEVCProfileMain,
+                CodecProfileLevel.HEVCMainTierLevel41)) {
+            Log.i(TAG, "HevcMain41 not supported");
+            return;
+        }
+    }
+
+    public void testHevcMain5() throws Exception {
+        if (!supports(HEVC_MIME, CodecProfileLevel.HEVCProfileMain,
+                CodecProfileLevel.HEVCMainTierLevel5)) {
+            Log.i(TAG, "HevcMain5 not supported");
+            return;
+        }
+    }
+
+    public void testHevcMain51() throws Exception {
+        if (!supports(HEVC_MIME, CodecProfileLevel.HEVCProfileMain,
+                CodecProfileLevel.HEVCMainTierLevel51)) {
+            Log.i(TAG, "HevcMain51 not supported");
+            return;
+        }
+    }
+
     private boolean supports(String mimeType, int profile) {
         return supports(mimeType, profile, 0, false);
     }
diff --git a/tests/tests/media/src/android/media/cts/MediaCodecCencPlayer.java b/tests/tests/media/src/android/media/cts/MediaCodecCencPlayer.java
new file mode 100644
index 0000000..90696ff
--- /dev/null
+++ b/tests/tests/media/src/android/media/cts/MediaCodecCencPlayer.java
@@ -0,0 +1,532 @@
+/*
+ * 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.media.cts;
+
+import android.media.MediaCodec;
+import android.media.MediaCodecInfo;
+import android.media.MediaCodecList;
+import android.media.MediaCrypto;
+import android.media.MediaCryptoException;
+import android.media.MediaExtractor;
+import android.media.MediaFormat;
+import android.net.Uri;
+import android.util.Log;
+import android.view.SurfaceHolder;
+
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.UUID;
+
+/**
+ * JB(API 16) introduces {@link MediaCodec} API.  It allows apps have more control over
+ * media playback, pushes individual frames to decoder and supports decryption via
+ * {@link MediaCrypto} API.
+ *
+ * {@link MediaDrm} can be used to obtain keys for decrypting protected media streams,
+ * in conjunction with MediaCrypto.
+ */
+public class MediaCodecCencPlayer {
+    private static final String TAG = MediaCodecCencPlayer.class.getSimpleName();
+
+    private static final int STATE_IDLE = 1;
+    private static final int STATE_PREPARING = 2;
+    private static final int STATE_PLAYING = 3;
+    private static final int STATE_PAUSED = 4;
+
+    private static final UUID CLEARKEY_SCHEME_UUID =
+            new UUID(0x1077efecc0b24d02L, 0xace33c1e52e2fb4bL);
+
+    private boolean mEncryptedAudio;
+    private boolean mEncryptedVideo;
+    private boolean mThreadStarted = false;
+    private byte[] mSessionId;
+    private CodecState mAudioTrackState;
+    private int mMediaFormatHeight;
+    private int mMediaFormatWidth;
+    private int mState;
+    private long mDeltaTimeUs;
+    private long mDurationUs;
+    private Map<Integer, CodecState> mAudioCodecStates;
+    private Map<Integer, CodecState> mVideoCodecStates;
+    private Map<String, String> mAudioHeaders;
+    private Map<String, String> mVideoHeaders;
+    private Map<UUID, byte[]> mPsshInitData;
+    private MediaCrypto mCrypto;
+    private MediaExtractor mAudioExtractor;
+    private MediaExtractor mVideoExtractor;
+    private SurfaceHolder mSurfaceHolder;
+    private Thread mThread;
+    private Uri mAudioUri;
+    private Uri mVideoUri;
+
+    private static final byte[] PSSH = hexStringToByteArray(
+            "0000003470737368" +  // BMFF box header (4 bytes size + 'pssh')
+            "01000000" +          // Full box header (version = 1 flags = 0)
+            "1077efecc0b24d02" +  // SystemID
+            "ace33c1e52e2fb4b" +
+            "00000001" +          // Number of key ids
+            "60061e017e477e87" +  // Key id
+            "7e57d00d1ed00d1e" +
+            "00000000"            // Size of Data, must be zero
+            );
+
+    /**
+     * Convert a hex string into byte array.
+     */
+    private static byte[] hexStringToByteArray(String s) {
+        int len = s.length();
+        byte[] data = new byte[len / 2];
+        for (int i = 0; i < len; i += 2) {
+            data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4)
+                    + Character.digit(s.charAt(i + 1), 16));
+        }
+        return data;
+    }
+
+    /*
+     * Media player class to stream CENC content using MediaCodec class.
+     */
+    public MediaCodecCencPlayer(SurfaceHolder holder, byte[] sessionId) {
+        mSessionId = sessionId;
+        mSurfaceHolder = holder;
+        mState = STATE_IDLE;
+        mThread = new Thread(new Runnable() {
+            @Override
+            public void run() {
+                while (mThreadStarted == true) {
+                    doSomeWork();
+                    if (mAudioTrackState != null) {
+                        mAudioTrackState.process();
+                    }
+                    try {
+                        Thread.sleep(5);
+                    } catch (InterruptedException ex) {
+                        Log.d(TAG, "Thread interrupted");
+                    }
+                }
+            }
+        });
+    }
+
+    public void setAudioDataSource(Uri uri, Map<String, String> headers, boolean encrypted) {
+        mAudioUri = uri;
+        mAudioHeaders = headers;
+        mEncryptedAudio = encrypted;
+    }
+
+    public void setVideoDataSource(Uri uri, Map<String, String> headers, boolean encrypted) {
+        mVideoUri = uri;
+        mVideoHeaders = headers;
+        mEncryptedVideo = encrypted;
+    }
+
+    public final int getMediaFormatHeight() {
+        return mMediaFormatHeight;
+    }
+
+    public final int getMediaFormatWidth() {
+        return mMediaFormatWidth;
+    }
+
+    public final Map<UUID, byte[]> getPsshInfo() {
+        // TODO (edwinwong@)
+        // Remove the if statement when we get content that has the clear key system id.
+        if (mPsshInitData == null ||
+                (mPsshInitData != null && !mPsshInitData.containsKey(CLEARKEY_SCHEME_UUID))) {
+            mPsshInitData = new HashMap<UUID, byte[]>();
+            mPsshInitData.put(CLEARKEY_SCHEME_UUID, PSSH);
+        }
+        return mPsshInitData;
+    }
+
+    private void prepareAudio() throws IOException {
+        boolean hasAudio = false;
+        for (int i = mAudioExtractor.getTrackCount(); i-- > 0;) {
+            MediaFormat format = mAudioExtractor.getTrackFormat(i);
+            String mime = format.getString(MediaFormat.KEY_MIME);
+
+            Log.d(TAG, "audio track #" + i + " " + format + " " + mime +
+                  " Is ADTS:" + getMediaFormatInteger(format, MediaFormat.KEY_IS_ADTS) +
+                  " Sample rate:" + getMediaFormatInteger(format, MediaFormat.KEY_SAMPLE_RATE) +
+                  " Channel count:" +
+                  getMediaFormatInteger(format, MediaFormat.KEY_CHANNEL_COUNT));
+
+            if (!hasAudio) {
+                mAudioExtractor.selectTrack(i);
+                addTrack(i, format, mEncryptedAudio);
+                hasAudio = true;
+
+                if (format.containsKey(MediaFormat.KEY_DURATION)) {
+                    long durationUs = format.getLong(MediaFormat.KEY_DURATION);
+
+                    if (durationUs > mDurationUs) {
+                        mDurationUs = durationUs;
+                    }
+                    Log.d(TAG, "audio track format #" + i +
+                            " Duration:" + mDurationUs + " microseconds");
+                }
+
+                if (hasAudio) {
+                    break;
+                }
+            }
+        }
+    }
+
+    private void prepareVideo() throws IOException {
+        boolean hasVideo = false;
+
+        for (int i = mVideoExtractor.getTrackCount(); i-- > 0;) {
+            MediaFormat format = mVideoExtractor.getTrackFormat(i);
+            String mime = format.getString(MediaFormat.KEY_MIME);
+
+            mMediaFormatHeight = getMediaFormatInteger(format, MediaFormat.KEY_HEIGHT);
+            mMediaFormatWidth = getMediaFormatInteger(format, MediaFormat.KEY_WIDTH);
+            Log.d(TAG, "video track #" + i + " " + format + " " + mime +
+                  " Width:" + mMediaFormatWidth + ", Height:" + mMediaFormatHeight);
+
+            if (!hasVideo) {
+                mVideoExtractor.selectTrack(i);
+                addTrack(i, format, mEncryptedVideo);
+
+                hasVideo = true;
+
+                if (format.containsKey(MediaFormat.KEY_DURATION)) {
+                    long durationUs = format.getLong(MediaFormat.KEY_DURATION);
+
+                    if (durationUs > mDurationUs) {
+                        mDurationUs = durationUs;
+                    }
+                    Log.d(TAG, "track format #" + i + " Duration:" +
+                            mDurationUs + " microseconds");
+                }
+
+                if (hasVideo) {
+                    break;
+                }
+            }
+        }
+        return;
+    }
+
+    public void prepare() throws IOException, MediaCryptoException {
+        if (null == mAudioExtractor) {
+            mAudioExtractor = new MediaExtractor();
+            if (null == mAudioExtractor) {
+                Log.e(TAG, "Cannot create Audio extractor.");
+                return;
+            }
+        }
+
+        if (null == mVideoExtractor){
+            mVideoExtractor = new MediaExtractor();
+            if (null == mVideoExtractor) {
+                Log.e(TAG, "Cannot create Video extractor.");
+                return;
+            }
+        }
+
+        mAudioExtractor.setDataSource(mAudioUri.toString(), mAudioHeaders);
+        mVideoExtractor.setDataSource(mVideoUri.toString(), mVideoHeaders);
+        mPsshInitData = mVideoExtractor.getPsshInfo();
+
+        if (null == mCrypto && (mEncryptedVideo || mEncryptedAudio)) {
+            try {
+                mCrypto = new MediaCrypto(CLEARKEY_SCHEME_UUID, mSessionId);
+            } catch (MediaCryptoException e) {
+                reset();
+                Log.e(TAG, "Failed to create MediaCrypto instance.");
+                throw e;
+            }
+        } else {
+            reset();
+            mCrypto.release();
+            mCrypto = null;
+        }
+
+        if (null == mVideoCodecStates) {
+            mVideoCodecStates = new HashMap<Integer, CodecState>();
+        } else {
+            mVideoCodecStates.clear();
+        }
+
+        if (null == mAudioCodecStates) {
+            mAudioCodecStates = new HashMap<Integer, CodecState>();
+        } else {
+            mAudioCodecStates.clear();
+        }
+
+        prepareVideo();
+        prepareAudio();
+
+        mState = STATE_PAUSED;
+    }
+
+    private void addTrack(int trackIndex, MediaFormat format,
+            boolean encrypted) throws IOException {
+        String mime = format.getString(MediaFormat.KEY_MIME);
+        boolean isVideo = mime.startsWith("video/");
+        boolean isAudio = mime.startsWith("audio/");
+
+        MediaCodec codec;
+
+        if (encrypted && mCrypto.requiresSecureDecoderComponent(mime)) {
+            codec = MediaCodec.createByCodecName(
+                    getSecureDecoderNameForMime(mime));
+        } else {
+            codec = MediaCodec.createDecoderByType(mime);
+        }
+
+        codec.configure(
+                format,
+                isVideo ? mSurfaceHolder.getSurface() : null,
+                mCrypto,
+                0);
+
+        CodecState state;
+        if (isVideo) {
+            state = new CodecState(this, mVideoExtractor, trackIndex, format, codec, true);
+            mVideoCodecStates.put(Integer.valueOf(trackIndex), state);
+        } else {
+            state = new CodecState(this, mAudioExtractor, trackIndex, format, codec, true);
+            mAudioCodecStates.put(Integer.valueOf(trackIndex), state);
+        }
+
+        if (isAudio) {
+            mAudioTrackState = state;
+        }
+    }
+
+    protected int getMediaFormatInteger(MediaFormat format, String key) {
+        return format.containsKey(key) ? format.getInteger(key) : 0;
+    }
+
+    protected String getSecureDecoderNameForMime(String mime) {
+        int n = MediaCodecList.getCodecCount();
+        for (int i = 0; i < n; ++i) {
+            MediaCodecInfo info = MediaCodecList.getCodecInfoAt(i);
+
+            if (info.isEncoder()) {
+                continue;
+            }
+
+            String[] supportedTypes = info.getSupportedTypes();
+
+            for (int j = 0; j < supportedTypes.length; ++j) {
+                if (supportedTypes[j].equalsIgnoreCase(mime)) {
+                    return info.getName() + ".secure";
+                }
+            }
+        }
+        return null;
+    }
+
+    public void start() {
+        Log.d(TAG, "start");
+
+        if (mState == STATE_PLAYING || mState == STATE_PREPARING) {
+            return;
+        } else if (mState == STATE_IDLE) {
+            mState = STATE_PREPARING;
+            return;
+        } else if (mState != STATE_PAUSED) {
+            throw new IllegalStateException();
+        }
+
+        for (CodecState state : mVideoCodecStates.values()) {
+            state.start();
+        }
+
+        for (CodecState state : mAudioCodecStates.values()) {
+            state.start();
+        }
+
+        mDeltaTimeUs = -1;
+        mState = STATE_PLAYING;
+    }
+
+    public void startWork() throws IOException, MediaCryptoException, Exception {
+        try {
+            // Just change state from STATE_IDLE to STATE_PREPARING.
+            start();
+            // Extract media information from uri asset, and change state to STATE_PAUSED.
+            prepare();
+            // Start CodecState, and change from STATE_PAUSED to STATE_PLAYING.
+            start();
+        } catch (IOException e) {
+            throw e;
+        } catch (MediaCryptoException e) {
+            throw e;
+        }
+
+        mThreadStarted = true;
+        mThread.start();
+    }
+
+    public void startThread() {
+        start();
+        mThreadStarted = true;
+        mThread.start();
+    }
+
+    public void pause() {
+        Log.d(TAG, "pause");
+
+        if (mState == STATE_PAUSED) {
+            return;
+        } else if (mState != STATE_PLAYING) {
+            throw new IllegalStateException();
+        }
+
+        for (CodecState state : mVideoCodecStates.values()) {
+            state.pause();
+        }
+
+        for (CodecState state : mAudioCodecStates.values()) {
+            state.pause();
+        }
+
+        mState = STATE_PAUSED;
+    }
+
+    public void reset() {
+        if (mState == STATE_PLAYING) {
+            mThreadStarted = false;
+
+            try {
+                mThread.join();
+            } catch (InterruptedException ex) {
+                Log.d(TAG, "mThread.join " + ex);
+            }
+
+            pause();
+        }
+
+        if (mVideoCodecStates != null) {
+            for (CodecState state : mVideoCodecStates.values()) {
+                state.release();
+            }
+            mVideoCodecStates = null;
+        }
+
+        if (mAudioCodecStates != null) {
+            for (CodecState state : mAudioCodecStates.values()) {
+                state.release();
+            }
+            mAudioCodecStates = null;
+        }
+
+        if (mAudioExtractor != null) {
+            mAudioExtractor.release();
+            mAudioExtractor = null;
+        }
+
+        if (mVideoExtractor != null) {
+            mVideoExtractor.release();
+            mVideoExtractor = null;
+        }
+
+        if (mCrypto != null) {
+            mCrypto.release();
+            mCrypto = null;
+        }
+
+        mDurationUs = -1;
+        mState = STATE_IDLE;
+    }
+
+    public boolean isEnded() {
+        for (CodecState state : mVideoCodecStates.values()) {
+          if (!state.isEnded()) {
+            return false;
+          }
+        }
+
+        for (CodecState state : mAudioCodecStates.values()) {
+            if (!state.isEnded()) {
+              return false;
+            }
+        }
+
+        return true;
+    }
+
+    private void doSomeWork() {
+        try {
+            for (CodecState state : mVideoCodecStates.values()) {
+                state.doSomeWork();
+            }
+        } catch (MediaCodec.CryptoException e) {
+            throw new Error("Video CryptoException w/ errorCode "
+                    + e.getErrorCode() + ", '" + e.getMessage() + "'");
+        } catch (IllegalStateException e) {
+            throw new Error("Video CodecState.feedInputBuffer IllegalStateException " + e);
+        }
+
+        try {
+            for (CodecState state : mAudioCodecStates.values()) {
+                state.doSomeWork();
+            }
+        } catch (MediaCodec.CryptoException e) {
+            throw new Error("Audio CryptoException w/ errorCode "
+                    + e.getErrorCode() + ", '" + e.getMessage() + "'");
+        } catch (IllegalStateException e) {
+            throw new Error("Aduio CodecState.feedInputBuffer IllegalStateException " + e);
+        }
+
+    }
+
+    public long getNowUs() {
+        if (mAudioTrackState == null) {
+            return System.currentTimeMillis() * 1000;
+        }
+
+        return mAudioTrackState.getAudioTimeUs();
+    }
+
+    public long getRealTimeUsForMediaTime(long mediaTimeUs) {
+        if (mDeltaTimeUs == -1) {
+            long nowUs = getNowUs();
+            mDeltaTimeUs = nowUs - mediaTimeUs;
+        }
+
+        return mDeltaTimeUs + mediaTimeUs;
+    }
+
+    public int getDuration() {
+        return (int)((mDurationUs + 500) / 1000);
+    }
+
+    public int getCurrentPosition() {
+        if (mVideoCodecStates == null) {
+                return 0;
+        }
+
+        long positionUs = 0;
+
+        for (CodecState state : mVideoCodecStates.values()) {
+            long trackPositionUs = state.getCurrentPositionUs();
+
+            if (trackPositionUs > positionUs) {
+                positionUs = trackPositionUs;
+            }
+        }
+        return (int)((positionUs + 500) / 1000);
+    }
+
+}
diff --git a/tests/tests/media/src/android/media/cts/MediaCodecListTest.java b/tests/tests/media/src/android/media/cts/MediaCodecListTest.java
index 2ed9002..d8a20e9 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();
@@ -131,6 +132,12 @@
         assertTrue(checkProfileSupported("video/avc", true, profile));
     }
 
+    // HEVC main profile must be supported
+    public void testIsHEVCMainProfileSupported() {
+        int profile = CodecProfileLevel.HEVCProfileMain;
+        assertTrue(checkProfileSupported("video/hevc", false, profile));
+    }
+
     // MPEG4 simple profile must be supported
     public void testIsM4VSimpleProfileSupported() {
         int profile = CodecProfileLevel.MPEG4ProfileSimple;
@@ -150,6 +157,7 @@
 
         int codecCount = MediaCodecList.getCodecCount();
         for (int i = 0; i < codecCount; ++i) {
+
             MediaCodecInfo info = MediaCodecList.getCodecInfoAt(i);
             String[] types = info.getSupportedTypes();
 
@@ -237,6 +245,7 @@
         // Mandatory video codecs
         list.add(new CodecType("video/avc", false));            // avc decoder
         list.add(new CodecType("video/avc", true));             // avc encoder
+        list.add(new CodecType("video/hevc", false));           // hevc decoder
         list.add(new CodecType("video/3gpp", false));           // h263 decoder
         list.add(new CodecType("video/3gpp", true));            // h263 encoder
         list.add(new CodecType("video/mp4v-es", false));        // m4v decoder
diff --git a/tests/tests/media/src/android/media/cts/MediaCodecTest.java b/tests/tests/media/src/android/media/cts/MediaCodecTest.java
index ce85b78..6726728 100644
--- a/tests/tests/media/src/android/media/cts/MediaCodecTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaCodecTest.java
@@ -34,7 +34,7 @@
 import java.io.IOException;
 import java.nio.ByteBuffer;
 import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.Locale;
+
 
 /**
  * General MediaCodec tests.
@@ -72,6 +72,201 @@
 
     /**
      * Tests:
+     * <br> Exceptions for MediaCodec factory methods
+     * <br> Exceptions for MediaCodec methods when called in the incorrect state.
+     *
+     * A selective test to ensure proper exceptions are thrown from MediaCodec
+     * methods when called in incorrect operational states.
+     */
+    public void testException() throws Exception {
+        MediaFormat[] formatList = new MediaFormat[2];
+
+        // use audio format
+        formatList[0] = new MediaFormat();
+        formatList[0].setString(MediaFormat.KEY_MIME, "audio/amr-wb");
+        formatList[0].setInteger(MediaFormat.KEY_SAMPLE_RATE, 16000);
+        formatList[0].setInteger(MediaFormat.KEY_CHANNEL_COUNT, 1);
+        formatList[0].setInteger(MediaFormat.KEY_BIT_RATE, 19850);
+
+        // use video format
+        formatList[1] = createMediaFormat();
+
+        for (MediaFormat format : formatList) {
+            verifyIllegalStateException(format, false);
+            verifyIllegalStateException(format, true);
+        }
+    }
+
+    // wrap MediaCodec encoder and decoder creation
+    private static MediaCodec createCodecByType(String type, boolean isEncoder)
+            throws IOException {
+        if (isEncoder) {
+            return MediaCodec.createEncoderByType(type);
+        }
+        return MediaCodec.createDecoderByType(type);
+    }
+
+    private static void logMediaCodecException(MediaCodec.CodecException ex) {
+        if (ex.isRecoverable()) {
+            Log.w(TAG, "CodecException Recoverable: " + ex.getErrorCode());
+        } else if (ex.isTransient()) {
+            Log.w(TAG, "CodecException Transient: " + ex.getErrorCode());
+        } else {
+            Log.w(TAG, "CodecException Fatal: " + ex.getErrorCode());
+        }
+    }
+
+    private static void verifyIllegalStateException(MediaFormat format, boolean isEncoder)
+            throws IOException {
+        MediaCodec codec;
+
+        // create codec (enter Initialized State)
+
+        // create improperly
+        final String methodName = isEncoder ? "createEncoderByType" : "createDecoderByType";
+        try {
+            codec = createCodecByType(null, isEncoder);
+            fail(methodName + " should return NullPointerException on null");
+        } catch (NullPointerException e) { // expected
+        }
+        try {
+            codec = createCodecByType("foobarplan9", isEncoder); // invalid type
+            fail(methodName + " should return IllegalArgumentException on invalid type");
+        } catch (IllegalArgumentException e) { // expected
+        }
+        try {
+            codec = MediaCodec.createByCodecName("foobarplan9"); // invalid name
+            fail(methodName + " should return IllegalArgumentException on invalid name");
+        } catch (IllegalArgumentException e) { // expected
+        }
+        // correct
+        codec = createCodecByType(format.getString(MediaFormat.KEY_MIME), isEncoder);
+
+        // test a few commands
+        try {
+            codec.start();
+            fail("start should return IllegalStateException when in Initialized state");
+        } catch (MediaCodec.CodecException e) {
+            logMediaCodecException(e);
+            fail("start should not return MediaCodec.CodecException on wrong state");
+        } catch (IllegalStateException e) { // expected
+        }
+        try {
+            codec.flush();
+            fail("flush should return IllegalStateException when in Initialized state");
+        } catch (MediaCodec.CodecException e) {
+            logMediaCodecException(e);
+            fail("flush should not return MediaCodec.CodecException on wrong state");
+        } catch (IllegalStateException e) { // expected
+        }
+        MediaCodecInfo codecInfo = codec.getCodecInfo(); // obtaining the codec info now is fine.
+        try {
+            int bufIndex = codec.dequeueInputBuffer(0);
+            fail("dequeueInputBuffer should return IllegalStateException"
+                    + " when in the Initialized state");
+        } catch (MediaCodec.CodecException e) {
+            logMediaCodecException(e);
+            fail("dequeueInputBuffer should not return MediaCodec.CodecException"
+                    + " on wrong state");
+        } catch (IllegalStateException e) { // expected
+        }
+        try {
+            MediaCodec.BufferInfo info = new MediaCodec.BufferInfo();
+            int bufIndex = codec.dequeueOutputBuffer(info, 0);
+            fail("dequeueOutputBuffer should return IllegalStateException"
+                    + " when in the Initialized state");
+        } catch (MediaCodec.CodecException e) {
+            logMediaCodecException(e);
+            fail("dequeueOutputBuffer should not return MediaCodec.CodecException"
+                    + " on wrong state");
+        } catch (IllegalStateException e) { // expected
+        }
+
+        // configure (enter Configured State)
+
+        // configure improperly
+        try {
+            codec.configure(format, null /* surface */, null /* crypto */,
+                    isEncoder ? 0 : MediaCodec.CONFIGURE_FLAG_ENCODE /* flags */);
+            fail("configure needs MediaCodec.CONFIGURE_FLAG_ENCODE for encoders only");
+        } catch (MediaCodec.CodecException e) { // expected
+            logMediaCodecException(e);
+        } catch (IllegalStateException e) {
+            fail("configure should not return IllegalStateException when improperly configured");
+        }
+        // correct
+        codec.configure(format, null /* surface */, null /* crypto */,
+                isEncoder ? MediaCodec.CONFIGURE_FLAG_ENCODE : 0 /* flags */);
+
+        // test a few commands
+        try {
+            codec.flush();
+            fail("flush should return IllegalStateException when in Configured state");
+        } catch (MediaCodec.CodecException e) {
+            logMediaCodecException(e);
+            fail("flush should not return MediaCodec.CodecException on wrong state");
+        } catch (IllegalStateException e) { // expected
+        }
+        try {
+            Surface surface = codec.createInputSurface();
+            if (!isEncoder) {
+                fail("createInputSurface should not work on a decoder");
+            }
+        } catch (IllegalStateException e) { // expected for decoder and audio encoder
+            if (isEncoder && format.getString(MediaFormat.KEY_MIME).startsWith("video/")) {
+                throw e;
+            }
+        }
+
+        // start codec (enter Executing state)
+        codec.start();
+
+        // test a few commands
+        try {
+            codec.configure(format, null /* surface */, null /* crypto */, 0 /* flags */);
+            fail("configure should return IllegalStateException when in Executing state");
+        } catch (MediaCodec.CodecException e) {
+            logMediaCodecException(e);
+            // TODO: consider configuring after a flush.
+            fail("configure should not return MediaCodec.CodecException on wrong state");
+        } catch (IllegalStateException e) { // expected
+        }
+
+        // two flushes should be fine.
+        codec.flush();
+        codec.flush();
+
+        // stop codec (enter Initialized state)
+        // two stops should be fine.
+        codec.stop();
+        codec.stop();
+
+        // release codec (enter Uninitialized state)
+        // two releases should be fine.
+        codec.release();
+        codec.release();
+
+        try {
+            codecInfo = codec.getCodecInfo();
+            fail("getCodecInfo should should return IllegalStateException" +
+                    " when in Uninitialized state");
+        } catch (MediaCodec.CodecException e) {
+            logMediaCodecException(e);
+            fail("getCodecInfo should not return MediaCodec.CodecException on wrong state");
+        } catch (IllegalStateException e) { // expected
+        }
+        try {
+            codec.stop();
+            fail("stop should return IllegalStateException when in Uninitialized state");
+        } catch (MediaCodec.CodecException e) {
+            logMediaCodecException(e);
+            fail("stop should not return MediaCodec.CodecException on wrong state");
+        } catch (IllegalStateException e) { // expected
+        }
+    }
+
+    /**
+     * Tests:
      * <br> calling createInputSurface() before configure() throws exception
      * <br> calling createInputSurface() after start() throws exception
      * <br> calling createInputSurface() with a non-Surface color format throws exception
@@ -83,15 +278,15 @@
 
         // Replace color format with something that isn't COLOR_FormatSurface.
         MediaCodecInfo codecInfo = selectCodec(MIME_TYPE);
-        if (codecInfo == null) {
-            // Pass if no codec was available.
-            return;
-        }
         int colorFormat = findNonSurfaceColorFormat(codecInfo, MIME_TYPE);
         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");
@@ -124,7 +319,6 @@
         assertNull(surface);
     }
 
-
     /**
      * Tests:
      * <br> signaling end-of-stream before any data is sent works
@@ -137,10 +331,10 @@
         InputSurface inputSurface = null;
 
         try {
-            encoder = createEncoderForMimeType(MIME_TYPE);
-            if (encoder == null) {
-                // Pass if no codec was available.
-                return;
+            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());
@@ -181,6 +375,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() {
@@ -189,10 +438,10 @@
         Surface surface = null;
 
         try {
-            encoder = createEncoderForMimeType(MIME_TYPE);
-            if (encoder == null) {
-                // Pass if no codec was available.
-                return;
+            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();
@@ -227,10 +476,10 @@
         Surface surface = null;
 
         try {
-            encoder = createEncoderForMimeType(MIME_TYPE);
-            if (encoder == null) {
-                // Pass if no codec was available.
-                return;
+            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();
@@ -305,11 +554,8 @@
             mediaExtractor = getMediaExtractorForMimeType(inputResourceId, "video/");
             MediaFormat mediaFormat =
                     mediaExtractor.getTrackFormat(mediaExtractor.getSampleTrackIndex());
-            mediaCodec = createDecoderForMimeType(mediaFormat.getString(MediaFormat.KEY_MIME));
-            if (mediaCodec == null) {
-              // Pass if no decoder was available.
-              return true;
-            }
+            mediaCodec =
+                    MediaCodec.createDecoderByType(mediaFormat.getString(MediaFormat.KEY_MIME));
             mediaCodec.configure(mediaFormat, outputSurface.getSurface(), null, 0);
             mediaCodec.start();
             boolean eos = false;
@@ -384,15 +630,19 @@
         MediaCodec audioDecoderA = null;
         MediaCodec audioDecoderB = null;
         try {
-            audioDecoderA = createDecoderForMimeType(MIME_TYPE_AUDIO);
-            if (audioDecoderA == null) {
-              // Pass if no decoder was available.
-              return;
+            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 = createDecoderForMimeType(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 {
@@ -430,18 +680,18 @@
         MediaCodec audioEncoder = null;
         MediaCodec audioDecoder = null;
         try {
-            audioEncoder = createEncoderForMimeType(MIME_TYPE_AUDIO);
-            if (audioEncoder == null) {
-              // Pass if no encoder was available.
-              return;
+            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 = createDecoderForMimeType(MIME_TYPE_AUDIO);
-            if (audioDecoder == null) {
-              // Pass if no decoder was available.
-              return;
+            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();
@@ -471,10 +721,6 @@
         // audio only checks this and stop
         mVideoEncodingOngoing = true;
         final CodecInfo info = getAvcSupportedFormatInfo();
-        if (info == null) {
-            // Pass if no codec was available.
-            return;
-        }
         long start = System.currentTimeMillis();
         Thread videoEncodingThread = new Thread(new Runnable() {
             @Override
@@ -509,9 +755,6 @@
 
     private static CodecInfo getAvcSupportedFormatInfo() {
         MediaCodecInfo mediaCodecInfo = selectCodec(MIME_TYPE);
-        if (mediaCodecInfo == null) { // not supported
-            return null;
-        }
         CodecCapabilities cap = mediaCodecInfo.getCapabilitiesForType(MIME_TYPE);
         if (cap == null) { // not supported
             return null;
@@ -595,11 +838,7 @@
         InputSurface inputSurface = null;
         mVideoEncoderHadError = false;
         try {
-            encoder = createEncoderForMimeType(MIME_TYPE);
-            if (encoder == null) {
-              // Pass if no encoder was available.
-              return;
-            }
+            encoder = MediaCodec.createEncoderByType(MIME_TYPE);
             encoder.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
             inputSurface = new InputSurface(encoder.createInputSurface());
             inputSurface.makeCurrent();
@@ -641,11 +880,7 @@
         MediaCodec encoder = null;
         mAudioEncoderHadError = false;
         try {
-            encoder = createEncoderForMimeType(MIME_TYPE_AUDIO);
-            if (encoder == null) {
-              // Pass if no codec was available.
-              return;
-            }
+            encoder = MediaCodec.createEncoderByType(MIME_TYPE_AUDIO);
             encoder.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
             MediaCodec.BufferInfo info = new MediaCodec.BufferInfo();
             encoder.start();
@@ -748,39 +983,6 @@
         return 0;   // not reached
     }
 
-    /** Returns a decoder for {@code mimeType}, or {@code null} if none is available. */
-    private static MediaCodec createDecoderForMimeType(String mimeType) {
-        return createCodecForMimeType(mimeType, false);
-    }
-
-    /** Returns a encoder for {@code mimeType}, or {@code null} if none is available. */
-    private static MediaCodec createEncoderForMimeType(String mimeType) {
-        return createCodecForMimeType(mimeType, true);
-    }
-
-    /**
-     * Returns a codec for {@code mimeType}, or {@code null} if there is no suitable codec on this
-     * device. The codec is an encoder if {@code encoder} is {@code true}, and a decoder otherwise.
-     */
-    private static MediaCodec createCodecForMimeType(String mimeType, boolean encoder) {
-        mimeType = mimeType.toLowerCase(Locale.US);
-        for (int index = 0; index < MediaCodecList.getCodecCount(); index++) {
-            MediaCodecInfo codecInfo = MediaCodecList.getCodecInfoAt(index);
-            if (encoder != codecInfo.isEncoder()) {
-                continue;
-            }
-
-            for (String codecType : codecInfo.getSupportedTypes()) {
-                if (codecType.equals(mimeType)) {
-                    return encoder
-                            ? MediaCodec.createEncoderByType(codecType.toLowerCase(Locale.US))
-                            : MediaCodec.createDecoderByType(codecType.toLowerCase(Locale.US));
-                }
-            }
-        }
-        return null;
-    }
-
     private MediaExtractor getMediaExtractorForMimeType(int resourceId, String mimeTypePrefix)
             throws IOException {
         MediaExtractor mediaExtractor = new MediaExtractor();
diff --git a/tests/tests/media/src/android/media/cts/MediaDrmMockTest.java b/tests/tests/media/src/android/media/cts/MediaDrmMockTest.java
index a09d368..1a3184d 100644
--- a/tests/tests/media/src/android/media/cts/MediaDrmMockTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaDrmMockTest.java
@@ -22,6 +22,7 @@
 import android.media.MediaDrm.CryptoSession;
 import android.media.MediaDrmException;
 import android.media.NotProvisionedException;
+import android.media.ResourceBusyException;
 import android.test.AndroidTestCase;
 import android.util.Log;
 import java.util.HashMap;
@@ -785,6 +786,8 @@
             sessionId = md.openSession();
         } catch (NotProvisionedException e) {
             // ignore, not thrown by mock
+        } catch (ResourceBusyException e) {
+            // ignore, not thrown by mock
         }
         return sessionId;
     }
diff --git a/tests/tests/media/src/android/media/cts/MediaPlayerTest.java b/tests/tests/media/src/android/media/cts/MediaPlayerTest.java
index c670b8c..0817c65 100644
--- a/tests/tests/media/src/android/media/cts/MediaPlayerTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaPlayerTest.java
@@ -37,7 +37,10 @@
 import android.os.SystemClock;
 import android.util.Log;
 
+import java.io.BufferedReader;
 import java.io.File;
+import java.io.InputStream;
+import java.io.InputStreamReader;
 import java.util.StringTokenizer;
 import java.util.UUID;
 import java.util.Vector;
@@ -117,11 +120,74 @@
         }
     }
 
-    public void testPlayAudio() throws Exception {
+    public void testPlayAudioFromDataURI() throws Exception {
         final int mp3Duration = 34909;
         final int tolerance = 70;
         final int seekDuration = 100;
+
+        // This is "R.raw.testmp3_2", base64-encoded.
+        final int resid = R.raw.testmp3_3;
+
+        InputStream is = mContext.getResources().openRawResource(resid);
+        BufferedReader reader = new BufferedReader(new InputStreamReader(is));
+
+        StringBuilder builder = new StringBuilder();
+        builder.append("data:;base64,");
+        builder.append(reader.readLine());
+        Uri uri = Uri.parse(builder.toString());
+
+        MediaPlayer mp = MediaPlayer.create(mContext, uri);
+
+        try {
+            mp.setAudioStreamType(AudioManager.STREAM_MUSIC);
+            mp.setWakeMode(mContext, PowerManager.PARTIAL_WAKE_LOCK);
+
+            assertFalse(mp.isPlaying());
+            mp.start();
+            assertTrue(mp.isPlaying());
+
+            assertFalse(mp.isLooping());
+            mp.setLooping(true);
+            assertTrue(mp.isLooping());
+
+            assertEquals(mp3Duration, mp.getDuration(), tolerance);
+            int pos = mp.getCurrentPosition();
+            assertTrue(pos >= 0);
+            assertTrue(pos < mp3Duration - seekDuration);
+
+            mp.seekTo(pos + seekDuration);
+            assertEquals(pos + seekDuration, mp.getCurrentPosition(), tolerance);
+
+            // test pause and restart
+            mp.pause();
+            Thread.sleep(SLEEP_TIME);
+            assertFalse(mp.isPlaying());
+            mp.start();
+            assertTrue(mp.isPlaying());
+
+            // test stop and restart
+            mp.stop();
+            mp.reset();
+            mp.setDataSource(mContext, uri);
+            mp.prepare();
+            assertFalse(mp.isPlaying());
+            mp.start();
+            assertTrue(mp.isPlaying());
+
+            // waiting to complete
+            while(mp.isPlaying()) {
+                Thread.sleep(SLEEP_TIME);
+            }
+        } finally {
+            mp.release();
+        }
+    }
+
+    public void testPlayAudio() throws Exception {
         final int resid = R.raw.testmp3_2;
+        final int mp3Duration = 34909;
+        final int tolerance = 70;
+        final int seekDuration = 100;
 
         MediaPlayer mp = MediaPlayer.create(mContext, resid);
         try {
@@ -657,6 +723,14 @@
                 R.raw.video_480x360_mp4_h264_1350kbps_30fps_aac_stereo_128kbps_44100hz, 480, 360);
     }
 
+    public void testLocalVideo_MP4_H264_480x360_1350kbps_30fps_AAC_Stereo_128kbps_44110Hz_frag()
+            throws Exception {
+        playVideoTest(
+                R.raw.video_480x360_mp4_h264_1350kbps_30fps_aac_stereo_128kbps_44100hz_fragmented,
+                480, 360);
+    }
+
+
     public void testLocalVideo_MP4_H264_480x360_1350kbps_30fps_AAC_Stereo_192kbps_44110Hz()
             throws Exception {
         playVideoTest(
@@ -918,7 +992,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) {
@@ -989,7 +1063,7 @@
             if (trackInfos[i].getTrackType() == MediaPlayer.TrackInfo.MEDIA_TRACK_TYPE_TIMEDTEXT) {
                 String trackLanguage = trackInfos[i].getLanguage();
                 assertTrue(trackLanguage != null);
-                trackLanguage.trim();
+                trackLanguage = trackLanguage.trim();
                 Log.d(LOG_TAG, "track info lang: " + trackLanguage);
                 assertTrue("Should not see empty track language with our test data.",
                            trackLanguage.length() > 0);
@@ -1000,6 +1074,41 @@
         assertEquals(4, count);
     }
 
+    /*
+     *  This test assumes the resources being tested are between 8 and 14 seconds long
+     *  The ones being used here are 10 seconds long.
+     */
+    public void testResumeAtEnd() throws Throwable {
+        testResumeAtEnd(R.raw.loudsoftmp3);
+        testResumeAtEnd(R.raw.loudsoftwav);
+        testResumeAtEnd(R.raw.loudsoftogg);
+        testResumeAtEnd(R.raw.loudsoftitunes);
+        testResumeAtEnd(R.raw.loudsoftfaac);
+        testResumeAtEnd(R.raw.loudsoftaac);
+    }
+
+    private void testResumeAtEnd(int res) throws Throwable {
+
+        loadResource(res);
+        mMediaPlayer.prepare();
+        mOnCompletionCalled.reset();
+        mMediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
+            @Override
+            public void onCompletion(MediaPlayer mp) {
+                mOnCompletionCalled.signal();
+                mMediaPlayer.start();
+            }
+        });
+        // skip the first part of the file so we reach EOF sooner
+        mMediaPlayer.seekTo(5000);
+        mMediaPlayer.start();
+        // sleep long enough that we restart playback at least once, but no more
+        Thread.sleep(10000);
+        assertTrue("MediaPlayer should still be playing", mMediaPlayer.isPlaying());
+        mMediaPlayer.reset();
+        assertEquals("wrong number of repetitions", 1, mOnCompletionCalled.getNumSignal());
+    }
+
     public void testCallback() throws Throwable {
         final int mp4Duration = 8484;
 
diff --git a/tests/tests/media/src/android/media/cts/MediaRandomTest.java b/tests/tests/media/src/android/media/cts/MediaRandomTest.java
index f99c927..7b49a37 100644
--- a/tests/tests/media/src/android/media/cts/MediaRandomTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaRandomTest.java
@@ -170,8 +170,13 @@
             afd.close();
         }
     }
-
-    public void testPlayerRandomAction() throws Exception {
+    public void testPlayerRandomActionH264() throws Exception {
+        testPlayerRandomAction(R.raw.video_480x360_mp4_h264_500kbps_30fps_aac_stereo_128kbps_44100hz);
+    }
+    public void testPlayerRandomActionHEVC() throws Exception {
+        testPlayerRandomAction(R.raw.video_480x360_mp4_hevc_650kbps_30fps_aac_stereo_128kbps_48000hz);
+    }
+    private void testPlayerRandomAction(int resid) throws Exception {
         Watchdog watchdog = new Watchdog(5000);
         try {
             mPlayer.setOnErrorListener(new MediaPlayer.OnErrorListener() {
@@ -185,7 +190,7 @@
                     return true;
                 }
             });
-            loadSource(R.raw.video_480x360_mp4_h264_500kbps_30fps_aac_stereo_128kbps_44100hz);
+            loadSource(resid);
             mPlayer.setDisplay(mSurfaceHolder);
             mPlayer.prepare();
             mPlayer.start();
diff --git a/tests/tests/media/src/android/media/cts/MediaScannerTest.java b/tests/tests/media/src/android/media/cts/MediaScannerTest.java
index 2dafdc5..1d492bb 100644
--- a/tests/tests/media/src/android/media/cts/MediaScannerTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaScannerTest.java
@@ -76,6 +76,7 @@
 
     @Override
     protected void tearDown() throws Exception {
+        cleanup();
         super.tearDown();
     }
 
@@ -84,10 +85,12 @@
             mMediaFile.delete();
         }
         if (mFileDir != null) {
-            new File(mFileDir + "/testmp3.mp3").delete();
-            new File(mFileDir + "/testmp3_2.mp3").delete();
-            new File(mFileDir + "/ctsmediascanplaylist1.pls").delete();
-            new File(mFileDir + "/ctsmediascanplaylist2.m3u").delete();
+            String files[] = new File(mFileDir).list();
+            if (files != null) {
+                for (String f: files) {
+                    new File(mFileDir + "/" + f).delete();
+                }
+            }
             new File(mFileDir).delete();
         }
 
@@ -367,6 +370,129 @@
         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, // this is actually utf-8
+                    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} ),
+            // The following 3 use cp1251 encoding, expanded to 16 bits and stored as utf16 
+            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.cp1251_7,
+                    new String[] {"Вопли Видоплясова", "Хвилі Амура", null, "Або або", null} ),
+            new MediaScanEntry(R.raw.cp1251_8,
+                    new String[] {"Вопли Видоплясова", "Хвилі Амура", null, "Таємнi сфери", 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.iso88591_2, // actually UTF16, but only uses iso8859-1 chars
+                    new String[] {"Björk", "Telegram", "Björk", "Possibly Maybe (Lucy Mix)", 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 name = mContext.getResources().getResourceEntryName(entry.res);
+            String path =  mFileDir + "/" + name + ".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 name = mContext.getResources().getResourceEntryName(entry.res);
+            String path =  mFileDir + "/" + name + ".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/NativeDecoderTest.java b/tests/tests/media/src/android/media/cts/NativeDecoderTest.java
new file mode 100644
index 0000000..fc27dfa
--- /dev/null
+++ b/tests/tests/media/src/android/media/cts/NativeDecoderTest.java
@@ -0,0 +1,491 @@
+/*
+ * 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.media.cts;
+
+import com.android.cts.media.R;
+
+import android.content.res.AssetFileDescriptor;
+import android.content.res.Resources;
+import android.media.MediaCodec;
+import android.media.MediaCodec.BufferInfo;
+import android.media.MediaCodecInfo;
+import android.media.MediaExtractor;
+import android.media.MediaFormat;
+import android.media.MediaMetadataRetriever;
+import android.media.MediaPlayer;
+import android.net.Uri;
+import android.os.Environment;
+import android.os.ParcelFileDescriptor;
+import android.util.Log;
+import android.view.Surface;
+import android.webkit.cts.CtsTestServer;
+
+import java.io.BufferedInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.RandomAccessFile;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.UUID;
+
+public class NativeDecoderTest extends MediaPlayerTestBase {
+    private static final String TAG = "DecoderTest";
+
+    private static final int RESET_MODE_NONE = 0;
+    private static final int RESET_MODE_RECONFIGURE = 1;
+    private static final int RESET_MODE_FLUSH = 2;
+    private static final int RESET_MODE_EOS_FLUSH = 3;
+
+    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 static Resources mResources;
+    short[] mMasterBuffer;
+
+    /** Load jni on initialization */
+    static {
+        Log.i("@@@", "before loadlibrary");
+        System.loadLibrary("ctsmediacodec_jni");
+        Log.i("@@@", "after loadlibrary");
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mResources = mContext.getResources();
+
+    }
+
+    // check that native extractor behavior matches java extractor
+
+    public void testExtractor() throws Exception {
+        testExtractor(R.raw.sinesweepogg);
+        testExtractor(R.raw.sinesweepmp3lame);
+        testExtractor(R.raw.sinesweepmp3smpb);
+        testExtractor(R.raw.sinesweepm4a);
+        testExtractor(R.raw.sinesweepflac);
+        testExtractor(R.raw.sinesweepwav);
+
+        testExtractor(R.raw.video_1280x720_mp4_h264_1000kbps_25fps_aac_stereo_128kbps_44100hz);
+        testExtractor(R.raw.video_1280x720_webm_vp8_333kbps_25fps_vorbis_stereo_128kbps_44100hz);
+        testExtractor(R.raw.video_1280x720_webm_vp9_309kbps_25fps_vorbis_stereo_128kbps_44100hz);
+        testExtractor(R.raw.video_176x144_3gp_h263_300kbps_12fps_aac_mono_24kbps_11025hz);
+        testExtractor(R.raw.video_480x360_mp4_mpeg4_860kbps_25fps_aac_stereo_128kbps_44100hz);
+
+        CtsTestServer foo = new CtsTestServer(mContext);
+        testExtractor(foo.getAssetUrl("noiseandchirps.ogg"));
+        testExtractor(foo.getAssetUrl("ringer.mp3"));
+        testExtractor(foo.getRedirectingAssetUrl("ringer.mp3"));
+    }
+
+    private void testExtractor(String path) throws Exception {
+        int[] jsizes = getSampleSizes(path);
+        int[] nsizes = getSampleSizesNativePath(path);
+
+        //Log.i("@@@", Arrays.toString(jsizes));
+        assertTrue("different samplesizes", Arrays.equals(jsizes, nsizes));
+    }
+
+    private void testExtractor(int res) throws Exception {
+        AssetFileDescriptor fd = mResources.openRawResourceFd(res);
+
+        int[] jsizes = getSampleSizes(
+                fd.getFileDescriptor(), fd.getStartOffset(), fd.getLength());
+        int[] nsizes = getSampleSizesNative(
+                fd.getParcelFileDescriptor().getFd(), fd.getStartOffset(), fd.getLength());
+
+        fd.close();
+        //Log.i("@@@", Arrays.toString(jsizes));
+        assertTrue("different samplesizes", Arrays.equals(jsizes, nsizes));
+    }
+
+    private static int[] getSampleSizes(String path) throws IOException {
+        MediaExtractor ex = new MediaExtractor();
+        ex.setDataSource(path);
+        return getSampleSizes(ex);
+    }
+
+    private static int[] getSampleSizes(FileDescriptor fd, long offset, long size)
+            throws IOException {
+        MediaExtractor ex = new MediaExtractor();
+        ex.setDataSource(fd, offset, size);
+        return getSampleSizes(ex);
+    }
+
+    private static int[] getSampleSizes(MediaExtractor ex) {
+        ArrayList<Integer> foo = new ArrayList<Integer>();
+        ByteBuffer buf = ByteBuffer.allocate(1024*1024);
+        int numtracks = ex.getTrackCount();
+        assertTrue("no tracks", numtracks > 0);
+        foo.add(numtracks);
+        for (int i = 0; i < numtracks; i++) {
+            MediaFormat format = ex.getTrackFormat(i);
+            String mime = format.getString(MediaFormat.KEY_MIME);
+            if (mime.startsWith("audio/")) {
+                foo.add(0);
+                foo.add(format.getInteger(MediaFormat.KEY_SAMPLE_RATE));
+                foo.add(format.getInteger(MediaFormat.KEY_CHANNEL_COUNT));
+                foo.add((int)format.getLong(MediaFormat.KEY_DURATION));
+            } else if (mime.startsWith("video/")) {
+                foo.add(1);
+                foo.add(format.getInteger(MediaFormat.KEY_WIDTH));
+                foo.add(format.getInteger(MediaFormat.KEY_HEIGHT));
+                foo.add((int)format.getLong(MediaFormat.KEY_DURATION));
+            } else {
+                fail("unexpected mime type: " + mime);
+            }
+            ex.selectTrack(i);
+        }
+        while(true) {
+            int n = ex.readSampleData(buf, 0);
+            if (n < 0) {
+                break;
+            }
+            foo.add(n);
+            foo.add(ex.getSampleTrackIndex());
+            foo.add(ex.getSampleFlags());
+            foo.add((int)ex.getSampleTime()); // just the low bits should be OK
+            ex.advance();
+        }
+
+        int [] ret = new int[foo.size()];
+        for (int i = 0; i < ret.length; i++) {
+            ret[i] = foo.get(i);
+        }
+        return ret;
+    }
+
+    private static native int[] getSampleSizesNative(int fd, long offset, long size);
+    private static native int[] getSampleSizesNativePath(String path);
+
+
+    public void testDecoder() throws Exception {
+        testDecoder(R.raw.sinesweepogg);
+        testDecoder(R.raw.sinesweepmp3lame);
+        testDecoder(R.raw.sinesweepmp3smpb);
+        testDecoder(R.raw.sinesweepm4a);
+        testDecoder(R.raw.sinesweepflac);
+        testDecoder(R.raw.sinesweepwav);
+
+        testDecoder(R.raw.video_1280x720_mp4_h264_1000kbps_25fps_aac_stereo_128kbps_44100hz);
+        testDecoder(R.raw.video_1280x720_webm_vp8_333kbps_25fps_vorbis_stereo_128kbps_44100hz);
+        testDecoder(R.raw.video_1280x720_webm_vp9_309kbps_25fps_vorbis_stereo_128kbps_44100hz);
+        testDecoder(R.raw.video_176x144_3gp_h263_300kbps_12fps_aac_mono_24kbps_11025hz);
+        testDecoder(R.raw.video_480x360_mp4_mpeg4_860kbps_25fps_aac_stereo_128kbps_44100hz);
+
+    }
+
+    private void testDecoder(int res) throws Exception {
+        AssetFileDescriptor fd = mResources.openRawResourceFd(res);
+
+        int[] jdata = getDecodedData(
+                fd.getFileDescriptor(), fd.getStartOffset(), fd.getLength());
+        int[] ndata = getDecodedDataNative(
+                fd.getParcelFileDescriptor().getFd(), fd.getStartOffset(), fd.getLength());
+
+        fd.close();
+        Log.i("@@@", Arrays.toString(jdata));
+        Log.i("@@@", Arrays.toString(ndata));
+        assertEquals("number of samples differs", jdata.length, ndata.length);
+        assertTrue("different decoded data", Arrays.equals(jdata, ndata));
+    }
+
+    private static int[] getDecodedData(FileDescriptor fd, long offset, long size)
+            throws IOException {
+        MediaExtractor ex = new MediaExtractor();
+        ex.setDataSource(fd, offset, size);
+        return getDecodedData(ex);
+    }
+    private static int[] getDecodedData(MediaExtractor ex) throws IOException {
+        int numtracks = ex.getTrackCount();
+        assertTrue("no tracks", numtracks > 0);
+        ArrayList<Integer>[] trackdata = new ArrayList[numtracks];
+        MediaCodec[] codec = new MediaCodec[numtracks];
+        MediaFormat[] format = new MediaFormat[numtracks];
+        ByteBuffer[][] inbuffers = new ByteBuffer[numtracks][];
+        ByteBuffer[][] outbuffers = new ByteBuffer[numtracks][];
+        for (int i = 0; i < numtracks; i++) {
+            format[i] = ex.getTrackFormat(i);
+            String mime = format[i].getString(MediaFormat.KEY_MIME);
+            if (mime.startsWith("audio/") || mime.startsWith("video/")) {
+                codec[i] = MediaCodec.createDecoderByType(mime);
+                codec[i].configure(format[i], null, null, 0);
+                codec[i].start();
+                inbuffers[i] = codec[i].getInputBuffers();
+                outbuffers[i] = codec[i].getOutputBuffers();
+                trackdata[i] = new ArrayList<Integer>();
+            } else {
+                fail("unexpected mime type: " + mime);
+            }
+            ex.selectTrack(i);
+        }
+
+        boolean[] sawInputEOS = new boolean[numtracks];
+        boolean[] sawOutputEOS = new boolean[numtracks];
+        int eosCount = 0;
+        BufferInfo info = new BufferInfo();
+        while(eosCount < numtracks) {
+            int t = ex.getSampleTrackIndex();
+            if (t >= 0) {
+                assertFalse("saw input EOS twice", sawInputEOS[t]);
+                int bufidx = codec[t].dequeueInputBuffer(5000);
+                if (bufidx >= 0) {
+                    Log.i("@@@@", "track " + t + " buffer " + bufidx);
+                    ByteBuffer buf = inbuffers[t][bufidx];
+                    int sampleSize = ex.readSampleData(buf, 0);
+                    Log.i("@@@@", "read " + sampleSize);
+                    if (sampleSize < 0) {
+                        sampleSize = 0;
+                        sawInputEOS[t] = true;
+                        Log.i("@@@@", "EOS");
+                        //break;
+                    }
+                    long presentationTimeUs = ex.getSampleTime();
+
+                    codec[t].queueInputBuffer(bufidx, 0, sampleSize, presentationTimeUs,
+                            sawInputEOS[t] ? MediaCodec.BUFFER_FLAG_END_OF_STREAM : 0);
+                    ex.advance();
+                }
+            } else {
+                Log.i("@@@@", "no more input samples");
+                for (int tt = 0; tt < codec.length; tt++) {
+                    if (!sawInputEOS[tt]) {
+                        // we ran out of samples without ever signaling EOS to the codec,
+                        // so do that now
+                        int bufidx = codec[tt].dequeueInputBuffer(5000);
+                        if (bufidx >= 0) {
+                            codec[tt].queueInputBuffer(bufidx, 0, 0, 0,
+                                    MediaCodec.BUFFER_FLAG_END_OF_STREAM);
+                            sawInputEOS[tt] = true;
+                        }
+                    }
+                }
+            }
+
+            // see if any of the codecs have data available
+            for (int tt = 0; tt < codec.length; tt++) {
+                if (!sawOutputEOS[tt]) {
+                    int status = codec[tt].dequeueOutputBuffer(info, 1);
+                    if (status >= 0) {
+                        if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
+                            Log.i("@@@@", "EOS on track " + tt);
+                            sawOutputEOS[tt] = true;
+                            eosCount++;
+                        }
+                        Log.i("@@@@", "got decoded buffer for track " + tt + ", size " + info.size);
+                        if (info.size > 0) {
+                            addSampleData(trackdata[tt], outbuffers[tt][status], info.size, format[tt]);
+                        }
+                        codec[tt].releaseOutputBuffer(status, false);
+                    } else if (status == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
+                        Log.i("@@@@", "output buffers changed for track " + tt);
+                        outbuffers[tt] = codec[tt].getOutputBuffers();
+                    } else if (status == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
+                        format[tt] = codec[tt].getOutputFormat();
+                        Log.i("@@@@", "format changed for track " + t + ": " + format[tt].toString());
+                    } else if (status == MediaCodec.INFO_TRY_AGAIN_LATER) {
+                        Log.i("@@@@", "no buffer right now for track " + tt);
+                    } else {
+                        Log.i("@@@@", "unexpected info code for track " + tt + ": " + status);
+                    }
+                } else {
+                    Log.i("@@@@", "already at EOS on track " + tt);
+                }
+            }
+        }
+
+        int totalsize = 0;
+        for (int i = 0; i < numtracks; i++) {
+            totalsize += trackdata[i].size();
+        }
+        int[] trackbytes = new int[totalsize];
+        int idx = 0;
+        for (int i = 0; i < numtracks; i++) {
+            ArrayList<Integer> src = trackdata[i];
+            int tracksize = src.size();
+            for (int j = 0; j < tracksize; j++) {
+                trackbytes[idx++] = src.get(j);
+            }
+        }
+
+        return trackbytes;
+    }
+
+    static void addSampleData(ArrayList<Integer> dst,
+            ByteBuffer buf, int size, MediaFormat format) throws IOException{
+
+        Log.i("@@@", "addsample " + dst.size() + "/" + size);
+        int width = format.getInteger(MediaFormat.KEY_WIDTH, size);
+        int stride = format.getInteger(MediaFormat.KEY_STRIDE, width);
+        int height = format.getInteger(MediaFormat.KEY_HEIGHT, 1);
+        byte[] bb = new byte[width * height];
+        for (int i = 0; i < height; i++) {
+            buf.position(i * stride);
+            buf.get(bb, i * width, width);
+        }
+        // bb is filled with data
+        long sum = adler32(bb);
+        dst.add( (int) (sum & 0xffffffff));
+    }
+
+    // simple checksum computed over every decoded buffer
+    static long adler32(byte[] input) {
+        int a = 1;
+        int b = 0;
+        for (int i = 0; i < input.length; i++) {
+            int unsignedval = input[i];
+            if (unsignedval < 0) {
+                unsignedval = 256 + unsignedval;
+            }
+            a += unsignedval;
+            b += a;
+        }
+        a = a % 65521;
+        b = b % 65521;
+        long ret = b * 65536 + a;
+        Log.i("@@@", "adler " + input.length + "/" + ret);
+        return ret;
+    }
+
+    private static native int[] getDecodedDataNative(int fd, long offset, long size)
+            throws IOException;
+
+    public void testVideoPlayback() throws Exception {
+        testVideoPlayback(R.raw.video_1280x720_mp4_h264_1000kbps_25fps_aac_stereo_128kbps_44100hz);
+        testVideoPlayback(R.raw.video_1280x720_webm_vp8_333kbps_25fps_vorbis_stereo_128kbps_44100hz);
+        testVideoPlayback(R.raw.video_1280x720_webm_vp9_309kbps_25fps_vorbis_stereo_128kbps_44100hz);
+        testVideoPlayback(R.raw.video_176x144_3gp_h263_300kbps_12fps_aac_mono_24kbps_11025hz);
+        testVideoPlayback(R.raw.video_480x360_mp4_mpeg4_860kbps_25fps_aac_stereo_128kbps_44100hz);
+    }
+
+    private void testVideoPlayback(int res) throws Exception {
+        AssetFileDescriptor fd = mResources.openRawResourceFd(res);
+
+        boolean ret = testPlaybackNative(mActivity.getSurfaceHolder().getSurface(),
+                fd.getParcelFileDescriptor().getFd(), fd.getStartOffset(), fd.getLength());
+        assertTrue("native playback error", ret);
+    }
+
+    private static native boolean testPlaybackNative(Surface surface,
+            int fd, long startOffset, long length);
+
+    public void testMuxer() throws Exception {
+        testMuxer(R.raw.video_1280x720_mp4_h264_1000kbps_25fps_aac_stereo_128kbps_44100hz, false);
+    }
+
+    private void testMuxer(int res, boolean webm) throws Exception {
+        AssetFileDescriptor infd = mResources.openRawResourceFd(res);
+
+        File base = mContext.getExternalFilesDir(null);
+        String tmpFile = base.getPath() + "/tmp.dat";
+        Log.i("@@@", "using tmp file " + tmpFile);
+        new File(tmpFile).delete();
+        ParcelFileDescriptor out = ParcelFileDescriptor.open(new File(tmpFile),
+                ParcelFileDescriptor.MODE_READ_WRITE | ParcelFileDescriptor.MODE_CREATE);
+
+        assertTrue("muxer failed", testMuxerNative(
+                infd.getParcelFileDescriptor().getFd(), infd.getStartOffset(), infd.getLength(),
+                out.getFd(), webm));
+
+        // compare the original with the remuxed
+        MediaExtractor org = new MediaExtractor();
+        org.setDataSource(infd.getFileDescriptor(),
+                infd.getStartOffset(), infd.getLength());
+
+        MediaExtractor remux = new MediaExtractor();
+        remux.setDataSource(out.getFileDescriptor());
+
+        assertEquals("mismatched numer of tracks", org.getTrackCount(), remux.getTrackCount());
+        for (int i = 0; i < 2; i++) {
+            MediaFormat format1 = org.getTrackFormat(i);
+            MediaFormat format2 = remux.getTrackFormat(i);
+            Log.i("@@@", "org: " + format1);
+            Log.i("@@@", "remux: " + format2);
+            assertTrue("different formats", compareFormats(format1, format2));
+        }
+
+        org.release();
+        remux.release();
+
+        MediaPlayer player1 = MediaPlayer.create(mContext, res);
+        MediaPlayer player2 = MediaPlayer.create(mContext, Uri.parse("file://" + tmpFile));
+        assertEquals("duration is different", player1.getDuration(), player2.getDuration());
+        player1.release();
+        player2.release();
+        new File(tmpFile).delete();
+    }
+
+    boolean compareFormats(MediaFormat f1, MediaFormat f2) {
+        // there's no good way to compare two MediaFormats, so compare their string
+        // representation
+        return f1.toString().equals(f2.toString());
+    }
+
+    private static native boolean testMuxerNative(int in, long inoffset, long insize,
+            int out, boolean webm);
+
+    public void testFormat() throws Exception {
+        assertTrue("media format fail, see log for details", testFormatNative());
+    }
+
+    private static native boolean testFormatNative();
+
+    public void testPssh() throws Exception {
+        testPssh(R.raw.psshtest);
+    }
+
+    private void testPssh(int res) throws Exception {
+        AssetFileDescriptor fd = mResources.openRawResourceFd(res);
+
+        MediaExtractor ex = new MediaExtractor();
+        ex.setDataSource(fd.getParcelFileDescriptor().getFileDescriptor(),
+                fd.getStartOffset(), fd.getLength());
+        testPssh(ex);
+        ex.release();
+
+        boolean ret = testPsshNative(
+                fd.getParcelFileDescriptor().getFd(), fd.getStartOffset(), fd.getLength());
+        assertTrue("native pssh error", ret);
+    }
+
+    private static void testPssh(MediaExtractor ex) {
+        Map<UUID, byte[]> map = ex.getPsshInfo();
+        Set<UUID> keys = map.keySet();
+        for (UUID uuid: keys) {
+            Log.i("@@@", "uuid: " + uuid + ", data size " +
+                    map.get(uuid).length);
+        }
+    }
+
+    private static native boolean testPsshNative(int fd, long offset, long size);
+
+    public void testCryptoInfo() throws Exception {
+        assertTrue("native cryptoinfo failed, see log for details", testCryptoInfoNative());
+    }
+
+    private static native boolean testCryptoInfoNative();
+}
+
diff --git a/tests/tests/media/src/android/media/cts/NonBlockingAudioTrack.java b/tests/tests/media/src/android/media/cts/NonBlockingAudioTrack.java
new file mode 100644
index 0000000..3ba1ce8
--- /dev/null
+++ b/tests/tests/media/src/android/media/cts/NonBlockingAudioTrack.java
@@ -0,0 +1,202 @@
+/*
+ * 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.media.cts;
+
+import android.media.AudioFormat;
+import android.media.AudioManager;
+import android.media.AudioTrack;
+import android.util.Log;
+
+import java.util.LinkedList;
+
+/**
+ * Class for playing audio by using audio track.
+ * {@link #write(byte[], int, int)} and {@link #write(short[], int, int)} methods will
+ * block until all data has been written to system. In order to avoid blocking, this class
+ * caculates available buffer size first then writes to audio sink.
+ */
+public class NonBlockingAudioTrack {
+    private static final String TAG = NonBlockingAudioTrack.class.getSimpleName();
+
+    class QueueElem {
+        byte[] data;
+        int offset;
+        int size;
+    }
+
+    private AudioTrack mAudioTrack;
+    private boolean mWriteMorePending = false;
+    private int mSampleRate;
+    private int mFrameSize;
+    private int mBufferSizeInFrames;
+    private int mNumFramesSubmitted = 0;
+    private int mNumBytesQueued = 0;
+    private LinkedList<QueueElem> mQueue = new LinkedList<QueueElem>();
+
+    public NonBlockingAudioTrack(int sampleRate, int channelCount) {
+        int channelConfig;
+        switch (channelCount) {
+            case 1:
+                channelConfig = AudioFormat.CHANNEL_OUT_MONO;
+                break;
+            case 2:
+                channelConfig = AudioFormat.CHANNEL_OUT_STEREO;
+                break;
+            case 6:
+                channelConfig = AudioFormat.CHANNEL_OUT_5POINT1;
+                break;
+            default:
+                throw new IllegalArgumentException();
+        }
+
+        int minBufferSize =
+            AudioTrack.getMinBufferSize(
+                    sampleRate,
+                    channelConfig,
+                    AudioFormat.ENCODING_PCM_16BIT);
+
+        int bufferSize = 2 * minBufferSize;
+
+        mAudioTrack = new AudioTrack(
+                AudioManager.STREAM_MUSIC,
+                sampleRate,
+                channelConfig,
+                AudioFormat.ENCODING_PCM_16BIT,
+                bufferSize,
+                AudioTrack.MODE_STREAM);
+
+        mSampleRate = sampleRate;
+        mFrameSize = 2 * channelCount;
+        mBufferSizeInFrames = bufferSize / mFrameSize;
+    }
+
+    public long getAudioTimeUs() {
+        int numFramesPlayed = mAudioTrack.getPlaybackHeadPosition();
+
+        return (numFramesPlayed * 1000000L) / mSampleRate;
+    }
+
+    public int getNumBytesQueued() {
+        return mNumBytesQueued;
+    }
+
+    public void play() {
+        mAudioTrack.play();
+    }
+
+    public void stop() {
+        cancelWriteMore();
+
+        mAudioTrack.stop();
+
+        mNumFramesSubmitted = 0;
+        mQueue.clear();
+        mNumBytesQueued = 0;
+    }
+
+    public void pause() {
+        cancelWriteMore();
+
+        mAudioTrack.pause();
+    }
+
+    public void release() {
+        cancelWriteMore();
+
+        mAudioTrack.release();
+        mAudioTrack = null;
+    }
+
+    public void process() {
+        mWriteMorePending = false;
+        writeMore();
+    }
+
+    public int getPlayState() {
+        return mAudioTrack.getPlayState();
+    }
+
+    private void writeMore() {
+        if (mQueue.isEmpty()) {
+            return;
+        }
+
+        int numFramesPlayed = mAudioTrack.getPlaybackHeadPosition();
+        int numFramesPending = mNumFramesSubmitted - numFramesPlayed;
+        int numFramesAvailableToWrite = mBufferSizeInFrames - numFramesPending;
+        int numBytesAvailableToWrite = numFramesAvailableToWrite * mFrameSize;
+
+        while (numBytesAvailableToWrite > 0) {
+            QueueElem elem = mQueue.peekFirst();
+
+            int numBytes = elem.size;
+            if (numBytes > numBytesAvailableToWrite) {
+                numBytes = numBytesAvailableToWrite;
+            }
+
+            int written = mAudioTrack.write(elem.data, elem.offset, numBytes);
+            assert(written == numBytes);
+
+            mNumFramesSubmitted += written / mFrameSize;
+
+            elem.size -= numBytes;
+            numBytesAvailableToWrite -= numBytes;
+            mNumBytesQueued -= numBytes;
+
+            if (elem.size == 0) {
+                mQueue.removeFirst();
+
+                if (mQueue.isEmpty()) {
+                    break;
+                }
+            } else {
+                elem.offset += numBytes;
+            }
+        }
+
+        if (!mQueue.isEmpty()) {
+            scheduleWriteMore();
+        }
+    }
+
+    private void scheduleWriteMore() {
+        if (mWriteMorePending) {
+            return;
+        }
+
+        int numFramesPlayed = mAudioTrack.getPlaybackHeadPosition();
+        int numFramesPending = mNumFramesSubmitted - numFramesPlayed;
+        int pendingDurationMs = 1000 * numFramesPending / mSampleRate;
+
+        mWriteMorePending = true;
+    }
+
+    private void cancelWriteMore() {
+        mWriteMorePending = false;
+    }
+
+    public void write(byte[] data, int size) {
+        QueueElem elem = new QueueElem();
+        elem.data = data;
+        elem.offset = 0;
+        elem.size = size;
+
+        // accumulate size written to queue
+        mNumBytesQueued += size;
+        mQueue.add(elem);
+    }
+}
+
diff --git a/tests/tests/media/src/android/media/cts/StreamingMediaPlayerTest.java b/tests/tests/media/src/android/media/cts/StreamingMediaPlayerTest.java
index 8bac442..2b93064 100644
--- a/tests/tests/media/src/android/media/cts/StreamingMediaPlayerTest.java
+++ b/tests/tests/media/src/android/media/cts/StreamingMediaPlayerTest.java
@@ -16,9 +16,13 @@
 package android.media.cts;
 
 import android.media.MediaPlayer;
+import android.os.Looper;
+import android.os.SystemClock;
 import android.util.Log;
 import android.webkit.cts.CtsTestServer;
 
+import java.io.IOException;
+
 
 /**
  * Tests of MediaPlayer streaming capabilities.
@@ -259,6 +263,93 @@
         localHlsTest("hls.m3u8", false, true);
     }
 
+    private static class WorkerWithPlayer implements Runnable {
+        private final Object mLock = new Object();
+        private Looper mLooper;
+        private MediaPlayer mMediaPlayer;
+
+        /**
+         * Creates a worker thread with the given name. The thread
+         * then runs a {@link android.os.Looper}.
+         * @param name A name for the new thread
+         */
+        WorkerWithPlayer(String name) {
+            Thread t = new Thread(null, this, name);
+            t.setPriority(Thread.MIN_PRIORITY);
+            t.start();
+            synchronized (mLock) {
+                while (mLooper == null) {
+                    try {
+                        mLock.wait();
+                    } catch (InterruptedException ex) {
+                    }
+                }
+            }
+        }
+
+        public MediaPlayer getPlayer() {
+            return mMediaPlayer;
+        }
+
+        @Override
+        public void run() {
+            synchronized (mLock) {
+                Looper.prepare();
+                mLooper = Looper.myLooper();
+                mMediaPlayer = new MediaPlayer();
+                mLock.notifyAll();
+            }
+            Looper.loop();
+        }
+
+        public void quit() {
+            mLooper.quit();
+            mMediaPlayer.release();
+        }
+    }
+
+    public void testBlockingReadRelease() throws Throwable {
+
+        mServer = new CtsTestServer(mContext);
+
+        WorkerWithPlayer worker = new WorkerWithPlayer("player");
+        final MediaPlayer mp = worker.getPlayer();
+
+        try {
+            String path = mServer.getDelayedAssetUrl("noiseandchirps.ogg", 15000);
+            mp.setDataSource(path);
+            mp.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
+                @Override
+                public void onPrepared(MediaPlayer mp) {
+                    fail("prepare should not succeed");
+                }
+            });
+            mp.prepareAsync();
+            Thread.sleep(1000);
+            long start = SystemClock.elapsedRealtime();
+            mp.release();
+            long end = SystemClock.elapsedRealtime();
+            long releaseDuration = (end - start);
+            assertTrue("release took too long: " + releaseDuration, releaseDuration < 1000);
+        } catch (IllegalArgumentException e) {
+            fail(e.getMessage());
+        } catch (SecurityException e) {
+            fail(e.getMessage());
+        } catch (IllegalStateException e) {
+            fail(e.getMessage());
+        } catch (IOException e) {
+            fail(e.getMessage());
+        } catch (InterruptedException e) {
+            fail(e.getMessage());
+        } finally {
+            mServer.shutdown();
+        }
+
+        // give the worker a bit of time to start processing the message before shutting it down
+        Thread.sleep(5000);
+        worker.quit();
+    }
+
     private void localHlsTest(final String name, boolean appendQueryString, boolean redirect)
             throws Throwable {
         mServer = new CtsTestServer(mContext);
diff --git a/tests/tests/media/src/android/media/cts/Vp8CodecTestBase.java b/tests/tests/media/src/android/media/cts/Vp8CodecTestBase.java
new file mode 100644
index 0000000..54e2ca9
--- /dev/null
+++ b/tests/tests/media/src/android/media/cts/Vp8CodecTestBase.java
@@ -0,0 +1,1940 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.cts;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.media.MediaCodec;
+import android.media.MediaCodec.CodecException;
+import android.media.MediaCodecInfo.CodecCapabilities;
+import android.media.MediaCodecList;
+import android.media.MediaCodecInfo;
+import android.media.MediaFormat;
+import android.os.Bundle;
+import android.os.Environment;
+import android.os.Looper;
+import android.os.Handler;
+import android.test.AndroidTestCase;
+import android.util.Log;
+import com.android.cts.media.R;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.InputStream;
+import java.nio.ByteBuffer;
+import java.util.Locale;
+import java.util.ArrayList;
+import java.util.concurrent.Callable;
+import java.util.concurrent.CountDownLatch;
+
+/**
+ * Verification test for vp8 encoder and decoder.
+ *
+ * A raw yv12 stream is encoded at various settings and written to an IVF
+ * file. Encoded stream bitrate and key frame interval are checked against target values.
+ * The stream is later decoded by vp8 decoder to verify frames are decodable and to
+ * calculate PSNR values for various bitrates.
+ */
+public class Vp8CodecTestBase extends AndroidTestCase {
+
+    protected static final String TAG = "VP8CodecTestBase";
+    private static final String VP8_MIME = "video/x-vnd.on2.vp8";
+    private static final String VPX_SW_DECODER_NAME = "OMX.google.vp8.decoder";
+    private static final String VPX_SW_ENCODER_NAME = "OMX.google.vp8.encoder";
+    private static final String OMX_SW_CODEC_PREFIX = "OMX.google";
+    protected static final String SDCARD_DIR =
+            Environment.getExternalStorageDirectory().getAbsolutePath();
+
+    // Default timeout for MediaCodec buffer dequeue - 200 ms.
+    protected static final long DEFAULT_DEQUEUE_TIMEOUT_US = 200000;
+    // Default timeout for MediaEncoderAsync - 30 sec.
+    protected static final long DEFAULT_ENCODE_TIMEOUT_MS = 30000;
+    // Default sync frame interval in frames (zero means allow the encoder to auto-select
+    // key frame interval).
+    private static final int SYNC_FRAME_INTERVAL = 0;
+    // Video bitrate type - should be set to OMX_Video_ControlRateConstant from OMX_Video.h
+    protected static final int VIDEO_ControlRateVariable = 1;
+    protected static final int VIDEO_ControlRateConstant = 2;
+    // NV12 color format supported by QCOM codec, but not declared in MediaCodec -
+    // see /hardware/qcom/media/mm-core/inc/OMX_QCOMExtns.h
+    private static final int COLOR_QCOM_FORMATYUV420PackedSemiPlanar32m = 0x7FA30C04;
+    // Allowable color formats supported by codec - in order of preference.
+    private static final int[] mSupportedColorList = {
+            CodecCapabilities.COLOR_FormatYUV420Planar,
+            CodecCapabilities.COLOR_FormatYUV420SemiPlanar,
+            CodecCapabilities.COLOR_QCOM_FormatYUV420SemiPlanar,
+            COLOR_QCOM_FORMATYUV420PackedSemiPlanar32m
+    };
+    // Scaled image cache list - contains scale factors, for which up-scaled frames
+    // were calculated and were written to yuv file.
+    ArrayList<Integer> mScaledImages = new ArrayList<Integer>();
+
+    private Resources mResources;
+
+    @Override
+    public void setContext(Context context) {
+        super.setContext(context);
+        mResources = mContext.getResources();
+    }
+
+    /**
+     *  VP8 codec properties generated by getVp8CodecProperties() function.
+     */
+    private class CodecProperties {
+        CodecProperties(String codecName, int colorFormat) {
+            this.codecName = codecName;
+            this.colorFormat = colorFormat;
+        }
+        public boolean  isGoogleSwCodec() {
+            return codecName.startsWith(OMX_SW_CODEC_PREFIX);
+        }
+
+        public final String codecName; // OpenMax component name for VP8 codec.
+        public final int colorFormat;  // Color format supported by codec.
+    }
+
+    /**
+     * Function to find VP8 codec.
+     *
+     * Iterates through the list of available codecs and tries to find
+     * VP8 codec, which can support either YUV420 planar or NV12 color formats.
+     * If forceSwGoogleCodec parameter set to true the function always returns
+     * Google sw VP8 codec.
+     * If forceSwGoogleCodec parameter set to false the functions looks for platform
+     * specific VP8 codec first. If no platform specific codec exist, falls back to
+     * Google sw VP8 codec.
+     *
+     * @param isEncoder     Flag if encoder is requested.
+     * @param forceSwGoogleCodec  Forces to use Google sw codec.
+     */
+    private CodecProperties getVp8CodecProperties(boolean isEncoder,
+            boolean forceSwGoogleCodec) throws Exception {
+        CodecProperties codecProperties = null;
+
+        if (!forceSwGoogleCodec) {
+            // Loop through the list of omx components in case platform specific codec
+            // is requested.
+            for (int i = 0; i < MediaCodecList.getCodecCount(); i++) {
+                MediaCodecInfo codecInfo = MediaCodecList.getCodecInfoAt(i);
+                if (isEncoder != codecInfo.isEncoder()) {
+                    continue;
+                }
+                Log.v(TAG, codecInfo.getName());
+                // Check if this is sw Google codec - we should ignore it.
+                boolean isGoogleSwCodec = codecInfo.getName().startsWith(OMX_SW_CODEC_PREFIX);
+                if (isGoogleSwCodec) {
+                    continue;
+                }
+
+                for (String type : codecInfo.getSupportedTypes()) {
+                    if (!type.equalsIgnoreCase(VP8_MIME)) {
+                        continue;
+                    }
+                    CodecCapabilities capabilities = codecInfo.getCapabilitiesForType(VP8_MIME);
+
+                    // Get candidate codec properties.
+                    Log.v(TAG, "Found candidate codec " + codecInfo.getName());
+                    for (int colorFormat : capabilities.colorFormats) {
+                        Log.v(TAG, "   Color: 0x" + Integer.toHexString(colorFormat));
+                    }
+
+                    // Check supported color formats.
+                    for (int supportedColorFormat : mSupportedColorList) {
+                        for (int codecColorFormat : capabilities.colorFormats) {
+                            if (codecColorFormat == supportedColorFormat) {
+                                codecProperties = new CodecProperties(codecInfo.getName(),
+                                        codecColorFormat);
+                                Log.v(TAG, "Found target codec " + codecProperties.codecName +
+                                        ". Color: 0x" + Integer.toHexString(codecColorFormat));
+                                return codecProperties;
+                            }
+                        }
+                    }
+                    // HW codec we found does not support one of necessary color formats.
+                    throw new RuntimeException("No hw codec with YUV420 or NV12 color formats");
+                }
+            }
+        }
+        // If no hw vp8 codec exist or sw codec is requested use default Google sw codec.
+        if (codecProperties == null) {
+            Log.v(TAG, "Use SW VP8 codec");
+            if (isEncoder) {
+                codecProperties = new CodecProperties(VPX_SW_ENCODER_NAME,
+                        CodecCapabilities.COLOR_FormatYUV420Planar);
+            } else {
+                codecProperties = new CodecProperties(VPX_SW_DECODER_NAME,
+                        CodecCapabilities.COLOR_FormatYUV420Planar);
+            }
+        }
+
+        return codecProperties;
+    }
+
+    /**
+     * Parameters for encoded video stream.
+     */
+    protected class EncoderOutputStreamParameters {
+        // Name of raw YUV420 input file. When the value of this parameter
+        // is set to null input file descriptor from inputResourceId parameter
+        // is used instead.
+        public String inputYuvFilename;
+        // Name of scaled YUV420 input file.
+        public String scaledYuvFilename;
+        // File descriptor for the raw input file (YUV420). Used only if
+        // inputYuvFilename parameter is null.
+        int inputResourceId;
+        // Name of the IVF file to write encoded bitsream
+        public String outputIvfFilename;
+        // Force to use Google SW VP8 encoder.
+        boolean forceSwEncoder;
+        // Number of frames to encode.
+        int frameCount;
+        // Frame rate of input file in frames per second.
+        int frameRate;
+        // Encoded frame width.
+        public int frameWidth;
+        // Encoded frame height.
+        public int frameHeight;
+        // Encoding bitrate array in bits/second for every frame. If array length
+        // is shorter than the total number of frames, the last value is re-used for
+        // all remaining frames. For constant bitrate encoding single element
+        // array can be used with first element set to target bitrate value.
+        public int[] bitrateSet;
+        // Encoding bitrate type - VBR or CBR
+        public int bitrateType;
+        // Number of temporal layers
+        public int temporalLayers;
+        // Desired key frame interval - codec is asked to generate key frames
+        // at a period defined by this parameter.
+        public int syncFrameInterval;
+        // Optional parameter - forced key frame interval. Used to
+        // explicitly request the codec to generate key frames using
+        // MediaCodec.PARAMETER_KEY_REQUEST_SYNC_FRAME parameter.
+        public int syncForceFrameInterval;
+        // Buffer timeout
+        long timeoutDequeue;
+        // Flag if encoder should run in Looper thread.
+        boolean runInLooperThread;
+    }
+
+    /**
+     * Generates an array of default parameters for encoder output stream based on
+     * upscaling value.
+     */
+    protected ArrayList<EncoderOutputStreamParameters> getDefaultEncodingParameterList(
+            String inputYuvName,
+            String outputIvfBaseName,
+            int encodeSeconds,
+            int[] resolutionScales,
+            int frameWidth,
+            int frameHeight,
+            int frameRate,
+            int bitrateMode,
+            int[] bitrates,
+            boolean syncEncoding) {
+        assertTrue(resolutionScales.length == bitrates.length);
+        int numCodecs = resolutionScales.length;
+        ArrayList<EncoderOutputStreamParameters> outputParameters =
+                new ArrayList<EncoderOutputStreamParameters>(numCodecs);
+        for (int i = 0; i < numCodecs; i++) {
+            EncoderOutputStreamParameters params = new EncoderOutputStreamParameters();
+            if (inputYuvName != null) {
+                params.inputYuvFilename = SDCARD_DIR + File.separator + inputYuvName;
+            } else {
+                params.inputYuvFilename = null;
+            }
+            params.scaledYuvFilename = SDCARD_DIR + File.separator +
+                    outputIvfBaseName + resolutionScales[i]+ ".yuv";
+            params.inputResourceId = R.raw.football_qvga;
+            params.outputIvfFilename = SDCARD_DIR + File.separator +
+                    outputIvfBaseName + resolutionScales[i] + ".ivf";
+            params.forceSwEncoder = false;
+            params.frameCount = encodeSeconds * frameRate;
+            params.frameRate = frameRate;
+            params.frameWidth = Math.min(frameWidth * resolutionScales[i], 1280);
+            params.frameHeight = Math.min(frameHeight * resolutionScales[i], 720);
+            params.bitrateSet = new int[1];
+            params.bitrateSet[0] = bitrates[i];
+            params.bitrateType = bitrateMode;
+            params.temporalLayers = 0;
+            params.syncFrameInterval = SYNC_FRAME_INTERVAL;
+            params.syncForceFrameInterval = 0;
+            if (syncEncoding) {
+                params.timeoutDequeue = DEFAULT_DEQUEUE_TIMEOUT_US;
+                params.runInLooperThread = false;
+            } else {
+                params.timeoutDequeue = 0;
+                params.runInLooperThread = true;
+            }
+            outputParameters.add(params);
+        }
+        return outputParameters;
+    }
+
+    protected EncoderOutputStreamParameters getDefaultEncodingParameters(
+            String inputYuvName,
+            String outputIvfBaseName,
+            int encodeSeconds,
+            int frameWidth,
+            int frameHeight,
+            int frameRate,
+            int bitrateMode,
+            int bitrate,
+            boolean syncEncoding) {
+        int[] scaleValues = { 1 };
+        int[] bitrates = { bitrate };
+        return getDefaultEncodingParameterList(
+                inputYuvName,
+                outputIvfBaseName,
+                encodeSeconds,
+                scaleValues,
+                frameWidth,
+                frameHeight,
+                frameRate,
+                bitrateMode,
+                bitrates,
+                syncEncoding).get(0);
+    }
+
+    /**
+     * Converts (interleaves) YUV420 planar to NV12 (if hw) or NV21 (if sw).
+     * Assumes packed, macroblock-aligned frame with no cropping
+     * (visible/coded row length == stride).  Swap U/V if |sw|.
+     */
+    private static byte[] YUV420ToNV(int width, int height, byte[] yuv, boolean sw) {
+        byte[] nv = new byte[yuv.length];
+        // Y plane we just copy.
+        System.arraycopy(yuv, 0, nv, 0, width * height);
+
+        // U & V plane we interleave.
+        int u_offset = width * height;
+        int v_offset = u_offset + u_offset / 4;
+        int nv_offset = width * height;
+        if (sw) {
+            for (int i = 0; i < width * height / 4; i++) {
+                nv[nv_offset++] = yuv[v_offset++];
+                nv[nv_offset++] = yuv[u_offset++];
+            }
+        }
+        else {
+            for (int i = 0; i < width * height / 4; i++) {
+                nv[nv_offset++] = yuv[u_offset++];
+                nv[nv_offset++] = yuv[v_offset++];
+            }
+        }
+        return nv;
+    }
+
+    /**
+     * Converts (de-interleaves) NV12 to YUV420 planar.
+     * Stride may be greater than width, slice height may be greater than height.
+     */
+    private static byte[] NV12ToYUV420(int width, int height,
+            int stride, int sliceHeight, byte[] nv12) {
+        byte[] yuv = new byte[width * height * 3 / 2];
+
+        // Y plane we just copy.
+        for (int i = 0; i < height; i++) {
+            System.arraycopy(nv12, i * stride, yuv, i * width, width);
+        }
+
+        // U & V plane - de-interleave.
+        int u_offset = width * height;
+        int v_offset = u_offset + u_offset / 4;
+        int nv_offset;
+        for (int i = 0; i < height / 2; i++) {
+            nv_offset = stride * (sliceHeight + i);
+            for (int j = 0; j < width / 2; j++) {
+                yuv[u_offset++] = nv12[nv_offset++];
+                yuv[v_offset++] = nv12[nv_offset++];
+            }
+        }
+        return yuv;
+    }
+
+    private static void imageUpscale1To2(byte[] src, int srcByteOffset, int srcStride,
+            byte[] dst, int dstByteOffset, int dstWidth, int dstHeight) {
+        for (int i = 0; i < dstHeight/2 - 1; i++) {
+            int dstOffset0 = 2 * i * dstWidth + dstByteOffset;
+            int dstOffset1 = dstOffset0 + dstWidth;
+            int srcOffset0 = i * srcStride + srcByteOffset;
+            int srcOffset1 = srcOffset0 + srcStride;
+            int pixel00 = (int)src[srcOffset0++] & 0xff;
+            int pixel10 = (int)src[srcOffset1++] & 0xff;
+            for (int j = 0; j < dstWidth/2 - 1; j++) {
+                int pixel01 = (int)src[srcOffset0++] & 0xff;
+                int pixel11 = (int)src[srcOffset1++] & 0xff;
+                dst[dstOffset0++] = (byte)pixel00;
+                dst[dstOffset0++] = (byte)((pixel00 + pixel01 + 1) / 2);
+                dst[dstOffset1++] = (byte)((pixel00 + pixel10 + 1) / 2);
+                dst[dstOffset1++] = (byte)((pixel00 + pixel01 + pixel10 + pixel11 + 2) / 4);
+                pixel00 = pixel01;
+                pixel10 = pixel11;
+            }
+            // last column
+            dst[dstOffset0++] = (byte)pixel00;
+            dst[dstOffset0++] = (byte)pixel00;
+            dst[dstOffset1++] = (byte)((pixel00 + pixel10 + 1) / 2);
+            dst[dstOffset1++] = (byte)((pixel00 + pixel10 + 1) / 2);
+        }
+
+        // last row
+        int dstOffset0 = (dstHeight - 2) * dstWidth + dstByteOffset;
+        int dstOffset1 = dstOffset0 + dstWidth;
+        int srcOffset0 = (dstHeight/2 - 1) * srcStride + srcByteOffset;
+        int pixel00 = (int)src[srcOffset0++] & 0xff;
+        for (int j = 0; j < dstWidth/2 - 1; j++) {
+            int pixel01 = (int)src[srcOffset0++] & 0xff;
+            dst[dstOffset0++] = (byte)pixel00;
+            dst[dstOffset0++] = (byte)((pixel00 + pixel01 + 1) / 2);
+            dst[dstOffset1++] = (byte)pixel00;
+            dst[dstOffset1++] = (byte)((pixel00 + pixel01 + 1) / 2);
+            pixel00 = pixel01;
+        }
+        // the very last pixel - bottom right
+        dst[dstOffset0++] = (byte)pixel00;
+        dst[dstOffset0++] = (byte)pixel00;
+        dst[dstOffset1++] = (byte)pixel00;
+        dst[dstOffset1++] = (byte)pixel00;
+    }
+
+    /**
+    * Up-scale image.
+    * Scale factor is defined by source and destination width ratio.
+    * Only 1:2 and 1:4 up-scaling is supported for now.
+    * For 640x480 -> 1280x720 conversion only top 640x360 part of the original
+    * image is scaled.
+    */
+    private static byte[] imageScale(byte[] src, int srcWidth, int srcHeight,
+            int dstWidth, int dstHeight) throws Exception {
+        int srcYSize = srcWidth * srcHeight;
+        int dstYSize = dstWidth * dstHeight;
+        byte[] dst = null;
+        if (dstWidth == 2 * srcWidth && dstHeight <= 2 * srcHeight) {
+            // 1:2 upscale
+            dst = new byte[dstWidth * dstHeight * 3 / 2];
+            imageUpscale1To2(src, 0, srcWidth,
+                    dst, 0, dstWidth, dstHeight);                                 // Y
+            imageUpscale1To2(src, srcYSize, srcWidth / 2,
+                    dst, dstYSize, dstWidth / 2, dstHeight / 2);                  // U
+            imageUpscale1To2(src, srcYSize * 5 / 4, srcWidth / 2,
+                    dst, dstYSize * 5 / 4, dstWidth / 2, dstHeight / 2);          // V
+        } else if (dstWidth == 4 * srcWidth && dstHeight <= 4 * srcHeight) {
+            // 1:4 upscale - in two steps
+            int midWidth = 2 * srcWidth;
+            int midHeight = 2 * srcHeight;
+            byte[] midBuffer = imageScale(src, srcWidth, srcHeight, midWidth, midHeight);
+            dst = imageScale(midBuffer, midWidth, midHeight, dstWidth, dstHeight);
+
+        } else {
+            throw new RuntimeException("Can not find proper scaling function");
+        }
+
+        return dst;
+    }
+
+    private void cacheScaledImage(
+            String srcYuvFilename, int srcResourceId, int srcFrameWidth, int srcFrameHeight,
+            String dstYuvFilename, int dstFrameWidth, int dstFrameHeight) throws Exception {
+        InputStream srcStream = OpenFileOrResourceId(srcYuvFilename, srcResourceId);
+        FileOutputStream dstFile = new FileOutputStream(dstYuvFilename, false);
+        int srcFrameSize = srcFrameWidth * srcFrameHeight * 3 / 2;
+        byte[] srcFrame = new byte[srcFrameSize];
+        byte[] dstFrame = null;
+        Log.d(TAG, "Scale to " + dstFrameWidth + " x " + dstFrameHeight + ". -> " + dstYuvFilename);
+        while (true) {
+            int bytesRead = srcStream.read(srcFrame);
+            if (bytesRead != srcFrame.length) {
+                break;
+            }
+            if (dstFrameWidth == srcFrameWidth && dstFrameHeight == srcFrameHeight) {
+                dstFrame = srcFrame;
+            } else {
+                dstFrame = imageScale(srcFrame, srcFrameWidth, srcFrameHeight,
+                        dstFrameWidth, dstFrameHeight);
+            }
+            dstFile.write(dstFrame);
+        }
+        srcStream.close();
+        dstFile.close();
+    }
+
+
+    /**
+     * A basic check if an encoded stream is decodable.
+     *
+     * The most basic confirmation we can get about a frame
+     * being properly encoded is trying to decode it.
+     * (Especially in realtime mode encode output is non-
+     * deterministic, therefore a more thorough check like
+     * md5 sum comparison wouldn't work.)
+     *
+     * Indeed, MediaCodec will raise an IllegalStateException
+     * whenever vp8 decoder fails to decode a frame, and
+     * this test uses that fact to verify the bitstream.
+     *
+     * @param inputIvfFilename  The name of the IVF file containing encoded bitsream.
+     * @param outputYuvFilename The name of the output YUV file (optional).
+     * @param frameRate         Frame rate of input file in frames per second
+     * @param forceSwDecoder    Force to use Googlw sw VP8 decoder.
+     */
+    protected ArrayList<MediaCodec.BufferInfo> decode(
+            String inputIvfFilename,
+            String outputYuvFilename,
+            int frameRate,
+            boolean forceSwDecoder) throws Exception {
+        ArrayList<MediaCodec.BufferInfo> bufferInfos = new ArrayList<MediaCodec.BufferInfo>();
+        CodecProperties properties = getVp8CodecProperties(false, forceSwDecoder);
+        // Open input/output.
+        IvfReader ivf = new IvfReader(inputIvfFilename);
+        int frameWidth = ivf.getWidth();
+        int frameHeight = ivf.getHeight();
+        int frameCount = ivf.getFrameCount();
+        int frameStride = frameWidth;
+        int frameSliceHeight = frameHeight;
+        int frameColorFormat = properties.colorFormat;
+        assertTrue(frameWidth > 0);
+        assertTrue(frameHeight > 0);
+        assertTrue(frameCount > 0);
+
+        FileOutputStream yuv = null;
+        if (outputYuvFilename != null) {
+            yuv = new FileOutputStream(outputYuvFilename, false);
+        }
+
+        // Create decoder.
+        MediaFormat format = MediaFormat.createVideoFormat(VP8_MIME,
+                                                           ivf.getWidth(),
+                                                           ivf.getHeight());
+        format.setInteger(MediaFormat.KEY_COLOR_FORMAT, properties.colorFormat);
+        Log.d(TAG, "Creating decoder " + properties.codecName +
+                ". Color format: 0x" + Integer.toHexString(frameColorFormat) +
+                ". " + frameWidth + " x " + frameHeight);
+        Log.d(TAG, "  Format: " + format);
+        Log.d(TAG, "  In: " + inputIvfFilename + ". Out:" + outputYuvFilename);
+        MediaCodec decoder = MediaCodec.createByCodecName(properties.codecName);
+        decoder.configure(format,
+                          null,  // surface
+                          null,  // crypto
+                          0);    // flags
+        decoder.start();
+
+        ByteBuffer[] inputBuffers = decoder.getInputBuffers();
+        ByteBuffer[] outputBuffers = decoder.getOutputBuffers();
+        MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
+
+        // decode loop
+        int inputFrameIndex = 0;
+        int outputFrameIndex = 0;
+        long inPresentationTimeUs = 0;
+        long outPresentationTimeUs = 0;
+        boolean sawOutputEOS = false;
+        boolean sawInputEOS = false;
+
+        while (!sawOutputEOS) {
+            if (!sawInputEOS) {
+                int inputBufIndex = decoder.dequeueInputBuffer(DEFAULT_DEQUEUE_TIMEOUT_US);
+                if (inputBufIndex >= 0) {
+                    byte[] frame = ivf.readFrame(inputFrameIndex);
+
+                    if (inputFrameIndex == frameCount - 1) {
+                        Log.d(TAG, "  Input EOS for frame # " + inputFrameIndex);
+                        sawInputEOS = true;
+                    }
+
+                    inputBuffers[inputBufIndex].clear();
+                    inputBuffers[inputBufIndex].put(frame);
+                    inputBuffers[inputBufIndex].rewind();
+                    inPresentationTimeUs = (inputFrameIndex * 1000000) / frameRate;
+
+                    decoder.queueInputBuffer(
+                            inputBufIndex,
+                            0,  // offset
+                            frame.length,
+                            inPresentationTimeUs,
+                            sawInputEOS ? MediaCodec.BUFFER_FLAG_END_OF_STREAM : 0);
+
+                    inputFrameIndex++;
+                }
+            }
+
+            int result = decoder.dequeueOutputBuffer(bufferInfo, DEFAULT_DEQUEUE_TIMEOUT_US);
+            while (result == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED ||
+                    result == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
+                if (result == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
+                    outputBuffers = decoder.getOutputBuffers();
+                } else  if (result == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
+                    // Process format change
+                    format = decoder.getOutputFormat();
+                    frameWidth = format.getInteger(MediaFormat.KEY_WIDTH);
+                    frameHeight = format.getInteger(MediaFormat.KEY_HEIGHT);
+                    frameColorFormat = format.getInteger(MediaFormat.KEY_COLOR_FORMAT);
+                    Log.d(TAG, "Decoder output format change. Color: 0x" +
+                            Integer.toHexString(frameColorFormat));
+                    Log.d(TAG, "Format: " + format.toString());
+
+                    // Parse frame and slice height from undocumented values
+                    if (format.containsKey("stride")) {
+                        frameStride = format.getInteger("stride");
+                    } else {
+                        frameStride = frameWidth;
+                    }
+                    if (format.containsKey("slice-height")) {
+                        frameSliceHeight = format.getInteger("slice-height");
+                    } else {
+                        frameSliceHeight = frameHeight;
+                    }
+                    Log.d(TAG, "Frame stride and slice height: " + frameStride +
+                            " x " + frameSliceHeight);
+                }
+                result = decoder.dequeueOutputBuffer(bufferInfo, DEFAULT_DEQUEUE_TIMEOUT_US);
+            }
+            if (result >= 0) {
+                int outputBufIndex = result;
+                outPresentationTimeUs = bufferInfo.presentationTimeUs;
+                Log.v(TAG, "Writing buffer # " + outputFrameIndex +
+                        ". Size: " + bufferInfo.size +
+                        ". InTime: " + (inPresentationTimeUs + 500)/1000 +
+                        ". OutTime: " + (outPresentationTimeUs + 500)/1000);
+                if ((bufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
+                    sawOutputEOS = true;
+                    Log.d(TAG, "   Output EOS for frame # " + outputFrameIndex);
+                }
+
+                if (bufferInfo.size > 0) {
+                    // Save decoder output to yuv file.
+                    if (yuv != null) {
+                        byte[] frame = new byte[bufferInfo.size];
+                        outputBuffers[outputBufIndex].position(bufferInfo.offset);
+                        outputBuffers[outputBufIndex].get(frame, 0, bufferInfo.size);
+                        // Convert NV12 to YUV420 if necessary
+                        if (frameColorFormat != CodecCapabilities.COLOR_FormatYUV420Planar) {
+                            frame = NV12ToYUV420(frameWidth, frameHeight,
+                                    frameStride, frameSliceHeight, frame);
+                        }
+                        yuv.write(frame);
+                    }
+                    outputFrameIndex++;
+
+                    // Update statistics - store presentation time delay in offset
+                    long presentationTimeUsDelta = inPresentationTimeUs - outPresentationTimeUs;
+                    MediaCodec.BufferInfo bufferInfoCopy = new MediaCodec.BufferInfo();
+                    bufferInfoCopy.set((int)presentationTimeUsDelta, bufferInfo.size,
+                            outPresentationTimeUs, bufferInfo.flags);
+                    bufferInfos.add(bufferInfoCopy);
+                }
+                decoder.releaseOutputBuffer(outputBufIndex, false);
+            }
+        }
+        decoder.stop();
+        decoder.release();
+        ivf.close();
+        if (yuv != null) {
+            yuv.close();
+        }
+
+        return bufferInfos;
+    }
+
+
+    /**
+     * Helper function to return InputStream from either filename (if set)
+     * or resource id (if filename is not set).
+     */
+    private InputStream OpenFileOrResourceId(String filename, int resourceId) throws Exception {
+        if (filename != null) {
+            return new FileInputStream(filename);
+        }
+        return mResources.openRawResource(resourceId);
+    }
+
+    /**
+     * Results of frame encoding.
+     */
+    protected class MediaEncoderOutput {
+        public long inPresentationTimeUs;
+        public long outPresentationTimeUs;
+        public boolean outputGenerated;
+        public int flags;
+        public byte[] buffer;
+    }
+
+    protected class MediaEncoderAsyncHelper {
+        private final EncoderOutputStreamParameters mStreamParams;
+        private final CodecProperties mProperties;
+        private final ArrayList<MediaCodec.BufferInfo> mBufferInfos;
+        private final IvfWriter mIvf;
+        private final byte[] mSrcFrame;
+
+        private InputStream mYuvStream;
+        private int mInputFrameIndex;
+
+        MediaEncoderAsyncHelper(
+                EncoderOutputStreamParameters streamParams,
+                CodecProperties properties,
+                ArrayList<MediaCodec.BufferInfo> bufferInfos,
+                IvfWriter ivf)
+                throws Exception {
+            mStreamParams = streamParams;
+            mProperties = properties;
+            mBufferInfos = bufferInfos;
+            mIvf = ivf;
+
+            int srcFrameSize = streamParams.frameWidth * streamParams.frameHeight * 3 / 2;
+            mSrcFrame = new byte[srcFrameSize];
+
+            mYuvStream = OpenFileOrResourceId(
+                    streamParams.inputYuvFilename, streamParams.inputResourceId);
+        }
+
+        public byte[] getInputFrame() {
+            // Check EOS
+            if (mStreamParams.frameCount == 0
+                    || (mStreamParams.frameCount > 0
+                            && mInputFrameIndex >= mStreamParams.frameCount)) {
+                Log.d(TAG, "---Sending EOS empty frame for frame # " + mInputFrameIndex);
+                return null;
+            }
+
+            try {
+                int bytesRead = mYuvStream.read(mSrcFrame);
+
+                if (bytesRead == -1) {
+                    // rewind to beginning of file
+                    mYuvStream.close();
+                    mYuvStream = OpenFileOrResourceId(
+                            mStreamParams.inputYuvFilename, mStreamParams.inputResourceId);
+                    bytesRead = mYuvStream.read(mSrcFrame);
+                }
+            } catch (Exception e) {
+                Log.e(TAG, "Failed to read YUV file.");
+                return null;
+            }
+            mInputFrameIndex++;
+
+            // Convert YUV420 to NV12 if necessary
+            if (mProperties.colorFormat != CodecCapabilities.COLOR_FormatYUV420Planar) {
+                return YUV420ToNV(mStreamParams.frameWidth, mStreamParams.frameHeight,
+                        mSrcFrame, mProperties.isGoogleSwCodec());
+            } else {
+                return mSrcFrame;
+            }
+        }
+
+        public boolean saveOutputFrame(MediaEncoderOutput out) {
+            if (out.outputGenerated) {
+                if (out.buffer.length > 0) {
+                    // Save frame
+                    try {
+                        mIvf.writeFrame(out.buffer, out.outPresentationTimeUs);
+                    } catch (Exception e) {
+                        Log.d(TAG, "Failed to write frame");
+                        return true;
+                    }
+
+                    // Update statistics - store presentation time delay in offset
+                    long presentationTimeUsDelta = out.inPresentationTimeUs -
+                            out.outPresentationTimeUs;
+                    MediaCodec.BufferInfo bufferInfoCopy = new MediaCodec.BufferInfo();
+                    bufferInfoCopy.set((int)presentationTimeUsDelta, out.buffer.length,
+                            out.outPresentationTimeUs, out.flags);
+                    mBufferInfos.add(bufferInfoCopy);
+                }
+                // Detect output EOS
+                if ((out.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
+                    Log.d(TAG, "----Output EOS ");
+                    return true;
+                }
+            }
+            return false;
+        }
+    }
+
+    /**
+     * Video encoder wrapper class.
+     * Allows to run the encoder either in a callee's thread or in a looper thread
+     * using buffer dequeue ready notification callbacks.
+     *
+     * Function feedInput() is used to send raw video frame to the encoder input. When encoder
+     * is configured to run in async mode the function will run in a looper thread.
+     * Encoded frame can be retrieved by calling getOutput() function.
+     */
+    protected class MediaEncoderAsync extends Thread {
+        private int mId;
+        private MediaCodec mCodec;
+        private MediaFormat mFormat;
+        private ByteBuffer[] mInputBuffers;
+        private ByteBuffer[] mOutputBuffers;
+        private int mInputFrameIndex;
+        private int mOutputFrameIndex;
+        private int mInputBufIndex;
+        private int mFrameRate;
+        private long mTimeout;
+        private MediaCodec.BufferInfo mBufferInfo;
+        private long mInPresentationTimeUs;
+        private long mOutPresentationTimeUs;
+        private boolean mAsync;
+        // Flag indicating if input frame was consumed by the encoder in feedInput() call.
+        private boolean mConsumedInput;
+        // Result of frame encoding returned by getOutput() call.
+        private MediaEncoderOutput mOutput;
+        // Object used to signal that looper thread has started and Handler instance associated
+        // with looper thread has been allocated.
+        private final Object mThreadEvent = new Object();
+        // Object used to signal that MediaCodec buffer dequeue notification callback
+        // was received.
+        private final Object mCallbackEvent = new Object();
+        private Handler mHandler;
+        private boolean mCallbackReceived;
+        private MediaEncoderAsyncHelper mHelper;
+        private final Object mCompletionEvent = new Object();
+        private boolean mCompleted;
+
+        private MediaCodec.Callback mCallback = new MediaCodec.Callback() {
+            @Override
+            public void onInputBufferAvailable(MediaCodec codec, int index) {
+                if (mHelper == null) {
+                    Log.e(TAG, "async helper not available");
+                    return;
+                }
+
+                byte[] encFrame = mHelper.getInputFrame();
+                boolean inputEOS = (encFrame == null);
+
+                int encFrameLength = 0;
+                int flags = 0;
+                if (inputEOS) {
+                    flags = MediaCodec.BUFFER_FLAG_END_OF_STREAM;
+                } else {
+                    encFrameLength = encFrame.length;
+
+                    mInputBuffers[index].clear();
+                    mInputBuffers[index].put(encFrame);
+                    mInputBuffers[index].rewind();
+
+                    mInPresentationTimeUs = (mInputFrameIndex * 1000000) / mFrameRate;
+
+                    Log.v(TAG, "Enc" + mId + ". Frame in # " + mInputFrameIndex +
+                            ". InTime: " + (mInPresentationTimeUs + 500)/1000);
+
+                    mInputFrameIndex++;
+                }
+
+                mCodec.queueInputBuffer(
+                        index,
+                        0,  // offset
+                        encFrameLength,  // size
+                        mInPresentationTimeUs,
+                        flags);
+            }
+
+            @Override
+            public void onOutputBufferAvailable(MediaCodec codec,
+                    int index, MediaCodec.BufferInfo info) {
+                if (mHelper == null) {
+                    Log.e(TAG, "async helper not available");
+                    return;
+                }
+
+                MediaEncoderOutput out = new MediaEncoderOutput();
+
+                out.buffer = new byte[info.size];
+                mOutputBuffers[index].position(info.offset);
+                mOutputBuffers[index].get(out.buffer, 0, info.size);
+                mOutPresentationTimeUs = info.presentationTimeUs;
+
+                String logStr = "Enc" + mId + ". Frame # " + mOutputFrameIndex;
+                if ((info.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0) {
+                    logStr += " CONFIG. ";
+                }
+                if ((info.flags & MediaCodec.BUFFER_FLAG_SYNC_FRAME) != 0) {
+                    logStr += " KEY. ";
+                }
+                if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
+                    logStr += " EOS. ";
+                }
+                logStr += " Size: " + info.size;
+                logStr += ". InTime: " + (mInPresentationTimeUs + 500)/1000 +
+                        ". OutTime: " + (mOutPresentationTimeUs + 500)/1000;
+                Log.v(TAG, logStr);
+
+                if (mOutputFrameIndex == 0 &&
+                        ((info.flags & MediaCodec.BUFFER_FLAG_SYNC_FRAME) == 0) ) {
+                    throw new RuntimeException("First frame is not a sync frame.");
+                }
+
+                if (info.size > 0) {
+                    mOutputFrameIndex++;
+                    out.inPresentationTimeUs = mInPresentationTimeUs;
+                    out.outPresentationTimeUs = mOutPresentationTimeUs;
+                }
+                mCodec.releaseOutputBuffer(index, false);
+
+                out.flags = info.flags;
+                out.outputGenerated = true;
+
+                if (mHelper.saveOutputFrame(out)) {
+                    // output EOS
+                    signalCompletion();
+                }
+            }
+
+            @Override
+            public void onError(MediaCodec codec, CodecException e) {
+                Log.e(TAG, "onError: " + e
+                        + ", transient " + e.isTransient()
+                        + ", recoverable " + e.isRecoverable()
+                        + ", error " + e.getErrorCode());
+            }
+
+            @Override
+            public void onOutputFormatChanged(MediaCodec codec, MediaFormat format) {
+                Log.i(TAG, "onOutputFormatChanged: " + format.toString());
+            }
+        };
+
+        private synchronized void requestStart() throws Exception {
+            mHandler = null;
+            start();
+            // Wait for Hander allocation
+            synchronized (mThreadEvent) {
+                while (mHandler == null) {
+                    mThreadEvent.wait();
+                }
+            }
+        }
+
+        public void setAsyncHelper(MediaEncoderAsyncHelper helper) {
+            mHelper = helper;
+        }
+
+        @Override
+        public void run() {
+            Looper.prepare();
+            synchronized (mThreadEvent) {
+                mHandler = new Handler();
+                mThreadEvent.notify();
+            }
+            Looper.loop();
+        }
+
+        private void runCallable(final Callable<?> callable) throws Exception {
+            if (mAsync) {
+                final Exception[] exception = new Exception[1];
+                final CountDownLatch countDownLatch = new CountDownLatch(1);
+                mHandler.post( new Runnable() {
+                    @Override
+                    public void run() {
+                        try {
+                            callable.call();
+                        } catch (Exception e) {
+                            exception[0] = e;
+                        } finally {
+                            countDownLatch.countDown();
+                        }
+                    }
+                } );
+
+                // Wait for task completion
+                countDownLatch.await();
+                if (exception[0] != null) {
+                    throw exception[0];
+                }
+            } else {
+                callable.call();
+            }
+        }
+
+        private synchronized void requestStop() throws Exception {
+            mHandler.post( new Runnable() {
+                @Override
+                public void run() {
+                    // This will run on the Looper thread
+                    Log.v(TAG, "MediaEncoder looper quitting");
+                    Looper.myLooper().quitSafely();
+                }
+            } );
+            // Wait for completion
+            join();
+            mHandler = null;
+        }
+
+        private void createCodecInternal(final String name,
+                final MediaFormat format, final long timeout) throws Exception {
+            mBufferInfo = new MediaCodec.BufferInfo();
+            mFormat = format;
+            mFrameRate = format.getInteger(MediaFormat.KEY_FRAME_RATE);
+            mTimeout = timeout;
+            mInputFrameIndex = 0;
+            mOutputFrameIndex = 0;
+            mInPresentationTimeUs = 0;
+            mOutPresentationTimeUs = 0;
+
+            mCodec = MediaCodec.createByCodecName(name);
+            if (mAsync) {
+                mCodec.setCallback(mCallback);
+            }
+            mCodec.configure(mFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
+            mCodec.start();
+            mInputBuffers = mCodec.getInputBuffers();
+            mOutputBuffers = mCodec.getOutputBuffers();
+        }
+
+        public void createCodec(int id, final String name, final MediaFormat format,
+                final long timeout, boolean async)  throws Exception {
+            mId = id;
+            mAsync = async;
+            if (mAsync) {
+                requestStart(); // start looper thread
+            }
+            runCallable( new Callable<Void>() {
+                @Override
+                public Void call() throws Exception {
+                    createCodecInternal(name, format, timeout);
+                    return null;
+                }
+            } );
+        }
+
+        private void feedInputInternal(final byte[] encFrame, final boolean inputEOS) {
+            mConsumedInput = false;
+            // Feed input
+            mInputBufIndex = mCodec.dequeueInputBuffer(mTimeout);
+
+            if (mInputBufIndex >= 0) {
+                mInputBuffers[mInputBufIndex].clear();
+                mInputBuffers[mInputBufIndex].put(encFrame);
+                mInputBuffers[mInputBufIndex].rewind();
+                int encFrameLength = encFrame.length;
+                int flags = 0;
+                if (inputEOS) {
+                    encFrameLength = 0;
+                    flags = MediaCodec.BUFFER_FLAG_END_OF_STREAM;
+                }
+                if (!inputEOS) {
+                    Log.v(TAG, "Enc" + mId + ". Frame in # " + mInputFrameIndex +
+                            ". InTime: " + (mInPresentationTimeUs + 500)/1000);
+                    mInPresentationTimeUs = (mInputFrameIndex * 1000000) / mFrameRate;
+                    mInputFrameIndex++;
+                }
+
+                mCodec.queueInputBuffer(
+                        mInputBufIndex,
+                        0,  // offset
+                        encFrameLength,  // size
+                        mInPresentationTimeUs,
+                        flags);
+
+                mConsumedInput = true;
+            } else {
+                Log.v(TAG, "In " + mId + " - TRY_AGAIN_LATER");
+            }
+            mCallbackReceived = false;
+        }
+
+        public boolean feedInput(final byte[] encFrame, final boolean inputEOS) throws Exception {
+            runCallable( new Callable<Void>() {
+                @Override
+                public Void call() throws Exception {
+                    feedInputInternal(encFrame, inputEOS);
+                    return null;
+                }
+            } );
+            return mConsumedInput;
+        }
+
+        private void getOutputInternal() {
+            mOutput = new MediaEncoderOutput();
+            mOutput.inPresentationTimeUs = mInPresentationTimeUs;
+            mOutput.outPresentationTimeUs = mOutPresentationTimeUs;
+            mOutput.outputGenerated = false;
+
+            // Get output from the encoder
+            int result = mCodec.dequeueOutputBuffer(mBufferInfo, mTimeout);
+            while (result == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED ||
+                    result == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
+                if (result == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
+                    mOutputBuffers = mCodec.getOutputBuffers();
+                } else if (result == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
+                    mFormat = mCodec.getOutputFormat();
+                    Log.d(TAG, "Format changed: " + mFormat.toString());
+                }
+                result = mCodec.dequeueOutputBuffer(mBufferInfo, mTimeout);
+            }
+            if (result == MediaCodec.INFO_TRY_AGAIN_LATER) {
+                Log.v(TAG, "Out " + mId + " - TRY_AGAIN_LATER");
+            }
+
+            if (result >= 0) {
+                int outputBufIndex = result;
+                mOutput.buffer = new byte[mBufferInfo.size];
+                mOutputBuffers[outputBufIndex].position(mBufferInfo.offset);
+                mOutputBuffers[outputBufIndex].get(mOutput.buffer, 0, mBufferInfo.size);
+                mOutPresentationTimeUs = mBufferInfo.presentationTimeUs;
+
+                String logStr = "Enc" + mId + ". Frame # " + mOutputFrameIndex;
+                if ((mBufferInfo.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0) {
+                    logStr += " CONFIG. ";
+                }
+                if ((mBufferInfo.flags & MediaCodec.BUFFER_FLAG_SYNC_FRAME) != 0) {
+                    logStr += " KEY. ";
+                }
+                if ((mBufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
+                    logStr += " EOS. ";
+                }
+                logStr += " Size: " + mBufferInfo.size;
+                logStr += ". InTime: " + (mInPresentationTimeUs + 500)/1000 +
+                        ". OutTime: " + (mOutPresentationTimeUs + 500)/1000;
+                Log.v(TAG, logStr);
+                if (mOutputFrameIndex == 0 &&
+                        ((mBufferInfo.flags & MediaCodec.BUFFER_FLAG_SYNC_FRAME) == 0) ) {
+                    throw new RuntimeException("First frame is not a sync frame.");
+                }
+
+                if (mBufferInfo.size > 0) {
+                    mOutputFrameIndex++;
+                    mOutput.outPresentationTimeUs = mOutPresentationTimeUs;
+                }
+                mCodec.releaseOutputBuffer(outputBufIndex, false);
+
+                mOutput.flags = mBufferInfo.flags;
+                mOutput.outputGenerated = true;
+            }
+            mCallbackReceived = false;
+        }
+
+        public MediaEncoderOutput getOutput() throws Exception {
+            runCallable( new Callable<Void>() {
+                @Override
+                public Void call() throws Exception {
+                    getOutputInternal();
+                    return null;
+                }
+            } );
+            return mOutput;
+        }
+
+        public void forceSyncFrame() throws Exception {
+            final Bundle syncFrame = new Bundle();
+            syncFrame.putInt(MediaCodec.PARAMETER_KEY_REQUEST_SYNC_FRAME, 0);
+            runCallable( new Callable<Void>() {
+                @Override
+                public Void call() throws Exception {
+                    mCodec.setParameters(syncFrame);
+                    return null;
+                }
+            } );
+        }
+
+        public void updateBitrate(int bitrate) throws Exception {
+            final Bundle bitrateUpdate = new Bundle();
+            bitrateUpdate.putInt(MediaCodec.PARAMETER_KEY_VIDEO_BITRATE, bitrate);
+            runCallable( new Callable<Void>() {
+                @Override
+                public Void call() throws Exception {
+                    mCodec.setParameters(bitrateUpdate);
+                    return null;
+                }
+            } );
+        }
+
+
+        public void waitForBufferEvent() throws Exception {
+            Log.v(TAG, "----Enc" + mId + " waiting for bufferEvent");
+            if (mAsync) {
+                synchronized (mCallbackEvent) {
+                    if (!mCallbackReceived) {
+                        mCallbackEvent.wait(1000); // wait 1 sec for a callback
+                        // throw an exception if callback was not received
+                        if (!mCallbackReceived) {
+                            throw new RuntimeException("MediaCodec callback was not received");
+                        }
+                    }
+                }
+            } else {
+                Thread.sleep(5);
+            }
+            Log.v(TAG, "----Waiting for bufferEvent done");
+        }
+
+
+        public void waitForCompletion(long timeoutMs) throws Exception {
+            synchronized (mCompletionEvent) {
+                long timeoutExpiredMs = System.currentTimeMillis() + timeoutMs;
+
+                while (!mCompleted) {
+                    mCompletionEvent.wait(timeoutExpiredMs - System.currentTimeMillis());
+                    if (System.currentTimeMillis() >= timeoutExpiredMs) {
+                        throw new RuntimeException("encoding has timed out!");
+                    }
+                }
+            }
+        }
+
+        public void signalCompletion() {
+            synchronized (mCompletionEvent) {
+                mCompleted = true;
+                mCompletionEvent.notify();
+            }
+        }
+
+        public void deleteCodec() throws Exception {
+            runCallable( new Callable<Void>() {
+                @Override
+                public Void call() throws Exception {
+                    mCodec.stop();
+                    mCodec.release();
+                    return null;
+                }
+            } );
+            if (mAsync) {
+                requestStop(); // Stop looper thread
+            }
+        }
+    }
+
+    /**
+     * Vp8 encoding loop supporting encoding single streams with an option
+     * to run in a looper thread and use buffer ready notification callbacks.
+     *
+     * Output stream is described by encodingParams parameters.
+     *
+     * MediaCodec will raise an IllegalStateException
+     * whenever vp8 encoder fails to encode a frame.
+     *
+     * Color format of input file should be YUV420, and frameWidth,
+     * frameHeight should be supplied correctly as raw input file doesn't
+     * include any header data.
+     *
+     * @param streamParams  Structure with encoder parameters
+     * @return              Returns array of encoded frames information for each frame.
+     */
+    protected ArrayList<MediaCodec.BufferInfo> encode(
+            EncoderOutputStreamParameters streamParams) throws Exception {
+
+        ArrayList<MediaCodec.BufferInfo> bufferInfos = new ArrayList<MediaCodec.BufferInfo>();
+        CodecProperties properties = getVp8CodecProperties(true, streamParams.forceSwEncoder);
+        Log.d(TAG, "Source reslution: " + streamParams.frameWidth + " x " +
+                streamParams.frameHeight);
+        int bitrate = streamParams.bitrateSet[0];
+
+        // Open input/output
+        InputStream yuvStream = OpenFileOrResourceId(
+                streamParams.inputYuvFilename, streamParams.inputResourceId);
+        IvfWriter ivf = new IvfWriter(
+                streamParams.outputIvfFilename, streamParams.frameWidth, streamParams.frameHeight);
+
+        // Create a media format signifying desired output.
+        MediaFormat format = MediaFormat.createVideoFormat(
+                VP8_MIME, streamParams.frameWidth, streamParams.frameHeight);
+        format.setInteger(MediaFormat.KEY_BIT_RATE, bitrate);
+        if (streamParams.bitrateType == VIDEO_ControlRateConstant) {
+            format.setInteger("bitrate-mode", VIDEO_ControlRateConstant); // set CBR
+        }
+        if (streamParams.temporalLayers > 0) {
+            format.setInteger("ts-layers", streamParams.temporalLayers); // 1 temporal layer
+        }
+        format.setInteger(MediaFormat.KEY_COLOR_FORMAT, properties.colorFormat);
+        format.setInteger(MediaFormat.KEY_FRAME_RATE, streamParams.frameRate);
+        int syncFrameInterval = (streamParams.syncFrameInterval + streamParams.frameRate/2) /
+                streamParams.frameRate;
+        format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, syncFrameInterval);
+
+        // Create encoder
+        Log.d(TAG, "Creating encoder " + properties.codecName +
+                ". Color format: 0x" + Integer.toHexString(properties.colorFormat)+ " : " +
+                streamParams.frameWidth + " x " + streamParams.frameHeight +
+                ". Bitrate: " + bitrate + " Bitrate type: " + streamParams.bitrateType +
+                ". Fps:" + streamParams.frameRate + ". TS Layers: " + streamParams.temporalLayers +
+                ". Key frame:" + syncFrameInterval * streamParams.frameRate +
+                ". Force keyFrame: " + streamParams.syncForceFrameInterval);
+        Log.d(TAG, "  Format: " + format);
+        Log.d(TAG, "  Output ivf:" + streamParams.outputIvfFilename);
+        MediaEncoderAsync codec = new MediaEncoderAsync();
+        codec.createCodec(0, properties.codecName, format,
+                streamParams.timeoutDequeue, streamParams.runInLooperThread);
+
+        // encode loop
+        boolean sawInputEOS = false;  // no more data
+        boolean consumedInputEOS = false; // EOS flag is consumed dy encoder
+        boolean sawOutputEOS = false;
+        boolean inputConsumed = true;
+        int inputFrameIndex = 0;
+        int lastBitrate = bitrate;
+        int srcFrameSize = streamParams.frameWidth * streamParams.frameHeight * 3 / 2;
+        byte[] srcFrame = new byte[srcFrameSize];
+
+        while (!sawOutputEOS) {
+
+            // Read and feed input frame
+            if (!consumedInputEOS) {
+
+                // Read new input buffers - if previous input was consumed and no EOS
+                if (inputConsumed && !sawInputEOS) {
+                    int bytesRead = yuvStream.read(srcFrame);
+
+                    // Check EOS
+                    if (streamParams.frameCount > 0 && inputFrameIndex >= streamParams.frameCount) {
+                        sawInputEOS = true;
+                        Log.d(TAG, "---Sending EOS empty frame for frame # " + inputFrameIndex);
+                    }
+
+                    if (!sawInputEOS && bytesRead == -1) {
+                        if (streamParams.frameCount == 0) {
+                            sawInputEOS = true;
+                            Log.d(TAG, "---Sending EOS empty frame for frame # " + inputFrameIndex);
+                        } else {
+                            yuvStream.close();
+                            yuvStream = OpenFileOrResourceId(
+                                    streamParams.inputYuvFilename, streamParams.inputResourceId);
+                            bytesRead = yuvStream.read(srcFrame);
+                        }
+                    }
+
+                    // Force sync frame if syncForceFrameinterval is set.
+                    if (!sawInputEOS && inputFrameIndex > 0 &&
+                            streamParams.syncForceFrameInterval > 0 &&
+                            (inputFrameIndex % streamParams.syncForceFrameInterval) == 0) {
+                        Log.d(TAG, "---Requesting sync frame # " + inputFrameIndex);
+                        codec.forceSyncFrame();
+                    }
+
+                    // Dynamic bitrate change.
+                    if (!sawInputEOS && streamParams.bitrateSet.length > inputFrameIndex) {
+                        int newBitrate = streamParams.bitrateSet[inputFrameIndex];
+                        if (newBitrate != lastBitrate) {
+                            Log.d(TAG, "--- Requesting new bitrate " + newBitrate +
+                                    " for frame " + inputFrameIndex);
+                            codec.updateBitrate(newBitrate);
+                            lastBitrate = newBitrate;
+                        }
+                    }
+
+                    // Convert YUV420 to NV12 if necessary
+                    if (properties.colorFormat != CodecCapabilities.COLOR_FormatYUV420Planar) {
+                        srcFrame = YUV420ToNV(streamParams.frameWidth, streamParams.frameHeight,
+                                srcFrame, properties.isGoogleSwCodec());
+                    }
+                }
+
+                inputConsumed = codec.feedInput(srcFrame, sawInputEOS);
+                if (inputConsumed) {
+                    inputFrameIndex++;
+                    consumedInputEOS = sawInputEOS;
+                }
+            }
+
+            // Get output from the encoder
+            MediaEncoderOutput out = codec.getOutput();
+            if (out.outputGenerated) {
+                // Detect output EOS
+                if ((out.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
+                    Log.d(TAG, "----Output EOS ");
+                    sawOutputEOS = true;
+                }
+
+                if (out.buffer.length > 0) {
+                    // Save frame
+                    ivf.writeFrame(out.buffer, out.outPresentationTimeUs);
+
+                    // Update statistics - store presentation time delay in offset
+                    long presentationTimeUsDelta = out.inPresentationTimeUs -
+                            out.outPresentationTimeUs;
+                    MediaCodec.BufferInfo bufferInfoCopy = new MediaCodec.BufferInfo();
+                    bufferInfoCopy.set((int)presentationTimeUsDelta, out.buffer.length,
+                            out.outPresentationTimeUs, out.flags);
+                    bufferInfos.add(bufferInfoCopy);
+                }
+            }
+
+            // If codec is not ready to accept input/poutput - wait for buffer ready callback
+            if ((!inputConsumed || consumedInputEOS) && !out.outputGenerated) {
+                codec.waitForBufferEvent();
+            }
+        }
+
+        codec.deleteCodec();
+        ivf.close();
+        yuvStream.close();
+
+        return bufferInfos;
+    }
+
+    /**
+     * Vp8 encoding run in a looper thread and use buffer ready callbacks.
+     *
+     * Output stream is described by encodingParams parameters.
+     *
+     * MediaCodec will raise an IllegalStateException
+     * whenever vp8 encoder fails to encode a frame.
+     *
+     * Color format of input file should be YUV420, and frameWidth,
+     * frameHeight should be supplied correctly as raw input file doesn't
+     * include any header data.
+     *
+     * @param streamParams  Structure with encoder parameters
+     * @return              Returns array of encoded frames information for each frame.
+     */
+    protected ArrayList<MediaCodec.BufferInfo> encodeAsync(
+            EncoderOutputStreamParameters streamParams) throws Exception {
+        if (!streamParams.runInLooperThread) {
+            throw new RuntimeException("encodeAsync should run with a looper thread!");
+        }
+
+        ArrayList<MediaCodec.BufferInfo> bufferInfos = new ArrayList<MediaCodec.BufferInfo>();
+        CodecProperties properties = getVp8CodecProperties(true, streamParams.forceSwEncoder);
+        Log.d(TAG, "Source reslution: " + streamParams.frameWidth + " x " +
+                streamParams.frameHeight);
+        int bitrate = streamParams.bitrateSet[0];
+
+        // Open input/output
+        IvfWriter ivf = new IvfWriter(
+                streamParams.outputIvfFilename, streamParams.frameWidth, streamParams.frameHeight);
+
+        // Create a media format signifying desired output.
+        MediaFormat format = MediaFormat.createVideoFormat(
+                VP8_MIME, streamParams.frameWidth, streamParams.frameHeight);
+        format.setInteger(MediaFormat.KEY_BIT_RATE, bitrate);
+        if (streamParams.bitrateType == VIDEO_ControlRateConstant) {
+            format.setInteger("bitrate-mode", VIDEO_ControlRateConstant); // set CBR
+        }
+        if (streamParams.temporalLayers > 0) {
+            format.setInteger("ts-layers", streamParams.temporalLayers); // 1 temporal layer
+        }
+        format.setInteger(MediaFormat.KEY_COLOR_FORMAT, properties.colorFormat);
+        format.setInteger(MediaFormat.KEY_FRAME_RATE, streamParams.frameRate);
+        int syncFrameInterval = (streamParams.syncFrameInterval + streamParams.frameRate/2) /
+                streamParams.frameRate;
+        format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, syncFrameInterval);
+
+        // Create encoder
+        Log.d(TAG, "Creating encoder " + properties.codecName +
+                ". Color format: 0x" + Integer.toHexString(properties.colorFormat)+ " : " +
+                streamParams.frameWidth + " x " + streamParams.frameHeight +
+                ". Bitrate: " + bitrate + " Bitrate type: " + streamParams.bitrateType +
+                ". Fps:" + streamParams.frameRate + ". TS Layers: " + streamParams.temporalLayers +
+                ". Key frame:" + syncFrameInterval * streamParams.frameRate +
+                ". Force keyFrame: " + streamParams.syncForceFrameInterval);
+        Log.d(TAG, "  Format: " + format);
+        Log.d(TAG, "  Output ivf:" + streamParams.outputIvfFilename);
+
+        MediaEncoderAsync codec = new MediaEncoderAsync();
+        MediaEncoderAsyncHelper helper = new MediaEncoderAsyncHelper(
+                streamParams, properties, bufferInfos, ivf);
+
+        codec.setAsyncHelper(helper);
+        codec.createCodec(0, properties.codecName, format,
+                streamParams.timeoutDequeue, streamParams.runInLooperThread);
+        codec.waitForCompletion(DEFAULT_ENCODE_TIMEOUT_MS);
+
+        codec.deleteCodec();
+        ivf.close();
+
+        return bufferInfos;
+    }
+
+    /**
+     * Vp8 encoding loop supporting encoding multiple streams at a time.
+     * Each output stream is described by encodingParams parameters allowing
+     * simultaneous encoding of various resolutions, bitrates with an option to
+     * control key frame and dynamic bitrate for each output stream indepandently.
+     *
+     * MediaCodec will raise an IllegalStateException
+     * whenever vp8 encoder fails to encode a frame.
+     *
+     * Color format of input file should be YUV420, and frameWidth,
+     * frameHeight should be supplied correctly as raw input file doesn't
+     * include any header data.
+     *
+     * @param srcFrameWidth     Frame width of input yuv file
+     * @param srcFrameHeight    Frame height of input yuv file
+     * @param encodingParams    Encoder parameters
+     * @return                  Returns 2D array of encoded frames information for each stream and
+     *                          for each frame.
+     */
+    protected ArrayList<ArrayList<MediaCodec.BufferInfo>> encodeSimulcast(
+            int srcFrameWidth,
+            int srcFrameHeight,
+            ArrayList<EncoderOutputStreamParameters> encodingParams)  throws Exception {
+        int numEncoders = encodingParams.size();
+
+        // Create arrays of input/output, formats, bitrates etc
+        ArrayList<ArrayList<MediaCodec.BufferInfo>> bufferInfos =
+                new ArrayList<ArrayList<MediaCodec.BufferInfo>>(numEncoders);
+        InputStream yuvStream[] = new InputStream[numEncoders];
+        IvfWriter[] ivf = new IvfWriter[numEncoders];
+        FileOutputStream[] yuvScaled = new FileOutputStream[numEncoders];
+        MediaFormat[] format = new MediaFormat[numEncoders];
+        MediaEncoderAsync[] codec = new MediaEncoderAsync[numEncoders];
+        int[] inputFrameIndex = new int[numEncoders];
+        boolean[] sawInputEOS = new boolean[numEncoders];
+        boolean[] consumedInputEOS = new boolean[numEncoders];
+        boolean[] inputConsumed = new boolean[numEncoders];
+        boolean[] bufferConsumed = new boolean[numEncoders];
+        boolean[] sawOutputEOS = new boolean[numEncoders];
+        byte[][] srcFrame = new byte[numEncoders][];
+        boolean sawOutputEOSTotal = false;
+        boolean bufferConsumedTotal = false;
+        CodecProperties[] codecProperties = new CodecProperties[numEncoders];
+
+        for (int i = 0; i < numEncoders; i++) {
+            EncoderOutputStreamParameters params = encodingParams.get(i);
+            CodecProperties properties = getVp8CodecProperties(true, params.forceSwEncoder);
+
+            // Check if scaled image was created
+            int scale = params.frameWidth / srcFrameWidth;
+            if (!mScaledImages.contains(scale)) {
+                // resize image
+                cacheScaledImage(params.inputYuvFilename, params.inputResourceId,
+                        srcFrameWidth, srcFrameHeight,
+                        params.scaledYuvFilename, params.frameWidth, params.frameHeight);
+                mScaledImages.add(scale);
+            }
+
+            // Create buffer info storage
+            bufferInfos.add(new ArrayList<MediaCodec.BufferInfo>());
+
+            // Create YUV reader
+            yuvStream[i] = new FileInputStream(params.scaledYuvFilename);
+
+            // Create IVF writer
+            ivf[i] = new IvfWriter(params.outputIvfFilename, params.frameWidth, params.frameHeight);
+
+            // Frame buffer
+            int frameSize = params.frameWidth * params.frameHeight * 3 / 2;
+            srcFrame[i] = new byte[frameSize];
+
+            // Create a media format signifying desired output.
+            int bitrate = params.bitrateSet[0];
+            format[i] = MediaFormat.createVideoFormat(VP8_MIME,
+                    params.frameWidth, params.frameHeight);
+            format[i].setInteger(MediaFormat.KEY_BIT_RATE, bitrate);
+            if (params.bitrateType == VIDEO_ControlRateConstant) {
+                format[i].setInteger("bitrate-mode", VIDEO_ControlRateConstant); // set CBR
+            }
+            if (params.temporalLayers > 0) {
+                format[i].setInteger("ts-layers", params.temporalLayers); // 1 temporal layer
+            }
+            format[i].setInteger(MediaFormat.KEY_COLOR_FORMAT, properties.colorFormat);
+            format[i].setInteger(MediaFormat.KEY_FRAME_RATE, params.frameRate);
+            int syncFrameInterval = (params.syncFrameInterval + params.frameRate/2) /
+                    params.frameRate; // in sec
+            format[i].setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, syncFrameInterval);
+            // Create encoder
+            Log.d(TAG, "Creating encoder #" + i +" : " + properties.codecName +
+                    ". Color format: 0x" + Integer.toHexString(properties.colorFormat)+ " : " +
+                    params.frameWidth + " x " + params.frameHeight +
+                    ". Bitrate: " + bitrate + " Bitrate type: " + params.bitrateType +
+                    ". Fps:" + params.frameRate + ". TS Layers: " + params.temporalLayers +
+                    ". Key frame:" + syncFrameInterval * params.frameRate +
+                    ". Force keyFrame: " + params.syncForceFrameInterval);
+            Log.d(TAG, "  Format: " + format[i]);
+            Log.d(TAG, "  Output ivf:" + params.outputIvfFilename);
+
+            // Create encoder
+            codec[i] = new MediaEncoderAsync();
+            codec[i].createCodec(i, properties.codecName, format[i],
+                    params.timeoutDequeue, params.runInLooperThread);
+            codecProperties[i] = new CodecProperties(properties.codecName, properties.colorFormat);
+
+            inputConsumed[i] = true;
+        }
+
+        while (!sawOutputEOSTotal) {
+            // Feed input buffer to all encoders
+            for (int i = 0; i < numEncoders; i++) {
+                bufferConsumed[i] = false;
+                if (consumedInputEOS[i]) {
+                    continue;
+                }
+
+                EncoderOutputStreamParameters params = encodingParams.get(i);
+                // Read new input buffers - if previous input was consumed and no EOS
+                if (inputConsumed[i] && !sawInputEOS[i]) {
+                    int bytesRead = yuvStream[i].read(srcFrame[i]);
+
+                    // Check EOS
+                    if (params.frameCount > 0 && inputFrameIndex[i] >= params.frameCount) {
+                        sawInputEOS[i] = true;
+                        Log.d(TAG, "---Enc" + i +
+                                ". Sending EOS empty frame for frame # " + inputFrameIndex[i]);
+                    }
+
+                    if (!sawInputEOS[i] && bytesRead == -1) {
+                        if (params.frameCount == 0) {
+                            sawInputEOS[i] = true;
+                            Log.d(TAG, "---Enc" + i +
+                                    ". Sending EOS empty frame for frame # " + inputFrameIndex[i]);
+                        } else {
+                            yuvStream[i].close();
+                            yuvStream[i] = new FileInputStream(params.scaledYuvFilename);
+                            bytesRead = yuvStream[i].read(srcFrame[i]);
+                        }
+                    }
+
+                    // Convert YUV420 to NV12 if necessary
+                    if (codecProperties[i].colorFormat !=
+                            CodecCapabilities.COLOR_FormatYUV420Planar) {
+                        srcFrame[i] = YUV420ToNV(params.frameWidth, params.frameHeight, srcFrame[i],
+                                codecProperties[i].isGoogleSwCodec());
+                    }
+                }
+
+                inputConsumed[i] = codec[i].feedInput(srcFrame[i], sawInputEOS[i]);
+                if (inputConsumed[i]) {
+                    inputFrameIndex[i]++;
+                    consumedInputEOS[i] = sawInputEOS[i];
+                    bufferConsumed[i] = true;
+                }
+
+            }
+
+            // Get output from all encoders
+            for (int i = 0; i < numEncoders; i++) {
+                if (sawOutputEOS[i]) {
+                    continue;
+                }
+
+                MediaEncoderOutput out = codec[i].getOutput();
+                if (out.outputGenerated) {
+                    bufferConsumed[i] = true;
+                    // Detect output EOS
+                    if ((out.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
+                        Log.d(TAG, "----Enc" + i + ". Output EOS ");
+                        sawOutputEOS[i] = true;
+                    }
+
+                    if (out.buffer.length > 0) {
+                        // Save frame
+                        ivf[i].writeFrame(out.buffer, out.outPresentationTimeUs);
+
+                        // Update statistics - store presentation time delay in offset
+                        long presentationTimeUsDelta = out.inPresentationTimeUs -
+                                out.outPresentationTimeUs;
+                        MediaCodec.BufferInfo bufferInfoCopy = new MediaCodec.BufferInfo();
+                        bufferInfoCopy.set((int)presentationTimeUsDelta, out.buffer.length,
+                                out.outPresentationTimeUs, out.flags);
+                        bufferInfos.get(i).add(bufferInfoCopy);
+                    }
+                }
+            }
+
+            // If codec is not ready to accept input/output - wait for buffer ready callback
+            bufferConsumedTotal = false;
+            for (boolean bufferConsumedCurrent : bufferConsumed) {
+                bufferConsumedTotal |= bufferConsumedCurrent;
+            }
+            if (!bufferConsumedTotal) {
+                // Pick the encoder to wait for
+                for (int i = 0; i < numEncoders; i++) {
+                    if (!bufferConsumed[i] && !sawOutputEOS[i]) {
+                        codec[i].waitForBufferEvent();
+                        break;
+                    }
+                }
+            }
+
+            // Check if EOS happened for all encoders
+            sawOutputEOSTotal = true;
+            for (boolean sawOutputEOSStream : sawOutputEOS) {
+                sawOutputEOSTotal &= sawOutputEOSStream;
+            }
+        }
+
+        for (int i = 0; i < numEncoders; i++) {
+            codec[i].deleteCodec();
+            ivf[i].close();
+            yuvStream[i].close();
+            if (yuvScaled[i] != null) {
+                yuvScaled[i].close();
+            }
+        }
+
+        return bufferInfos;
+    }
+
+    /**
+     * Some encoding statistics.
+     */
+    protected class Vp8EncodingStatistics {
+        Vp8EncodingStatistics() {
+            mBitrates = new ArrayList<Integer>();
+            mFrames = new ArrayList<Integer>();
+            mKeyFrames = new ArrayList<Integer>();
+            mMinimumKeyFrameInterval = Integer.MAX_VALUE;
+        }
+
+        public ArrayList<Integer> mBitrates;// Bitrate values for each second of the encoded stream.
+        public ArrayList<Integer> mFrames; // Number of frames in each second of the encoded stream.
+        public int mAverageBitrate;         // Average stream bitrate.
+        public ArrayList<Integer> mKeyFrames;// Stores the position of key frames in a stream.
+        public int mAverageKeyFrameInterval; // Average key frame interval.
+        public int mMaximumKeyFrameInterval; // Maximum key frame interval.
+        public int mMinimumKeyFrameInterval; // Minimum key frame interval.
+    }
+
+    /**
+     * Calculates average bitrate and key frame interval for the encoded streams.
+     * Output mBitrates field will contain bitrate values for every second
+     * of the encoded stream.
+     * Average stream bitrate will be stored in mAverageBitrate field.
+     * mKeyFrames array will contain the position of key frames in the encoded stream and
+     * mKeyFrameInterval - average key frame interval.
+     */
+    protected Vp8EncodingStatistics computeEncodingStatistics(int encoderId,
+            ArrayList<MediaCodec.BufferInfo> bufferInfos ) {
+        Vp8EncodingStatistics statistics = new Vp8EncodingStatistics();
+
+        int totalSize = 0;
+        int frames = 0;
+        int framesPerSecond = 0;
+        int totalFrameSizePerSecond = 0;
+        int maxFrameSize = 0;
+        int currentSecond;
+        int nextSecond = 0;
+        String keyFrameList = "  IFrame List: ";
+        String bitrateList = "  Bitrate list: ";
+        String framesList = "  FPS list: ";
+
+
+        for (int j = 0; j < bufferInfos.size(); j++) {
+            MediaCodec.BufferInfo info = bufferInfos.get(j);
+            currentSecond = (int)(info.presentationTimeUs / 1000000);
+            boolean lastFrame = (j == bufferInfos.size() - 1);
+            if (!lastFrame) {
+                nextSecond = (int)(bufferInfos.get(j+1).presentationTimeUs / 1000000);
+            }
+
+            totalSize += info.size;
+            totalFrameSizePerSecond += info.size;
+            maxFrameSize = Math.max(maxFrameSize, info.size);
+            framesPerSecond++;
+            frames++;
+
+            // Update the bitrate statistics if the next frame will
+            // be for the next second
+            if (lastFrame || nextSecond > currentSecond) {
+                int currentBitrate = totalFrameSizePerSecond * 8;
+                bitrateList += (currentBitrate + " ");
+                framesList += (framesPerSecond + " ");
+                statistics.mBitrates.add(currentBitrate);
+                statistics.mFrames.add(framesPerSecond);
+                totalFrameSizePerSecond = 0;
+                framesPerSecond = 0;
+            }
+
+            // Update key frame statistics.
+            if ((info.flags & MediaCodec.BUFFER_FLAG_SYNC_FRAME) != 0) {
+                statistics.mKeyFrames.add(j);
+                keyFrameList += (j + "  ");
+            }
+        }
+        int duration = (int)(bufferInfos.get(bufferInfos.size() - 1).presentationTimeUs / 1000);
+        duration = (duration + 500) / 1000;
+        statistics.mAverageBitrate = (int)(((long)totalSize * 8) / duration);
+        Log.d(TAG, "Statistics for encoder # " + encoderId);
+        // Calculate average key frame interval in frames.
+        int keyFrames = statistics.mKeyFrames.size();
+        if (keyFrames > 1) {
+            statistics.mAverageKeyFrameInterval =
+                    statistics.mKeyFrames.get(keyFrames - 1) - statistics.mKeyFrames.get(0);
+            statistics.mAverageKeyFrameInterval =
+                    Math.round((float)statistics.mAverageKeyFrameInterval / (keyFrames - 1));
+            for (int j = 1; j < keyFrames; j++) {
+                int keyFrameInterval =
+                        statistics.mKeyFrames.get(j) - statistics.mKeyFrames.get(j - 1);
+                statistics.mMaximumKeyFrameInterval =
+                        Math.max(statistics.mMaximumKeyFrameInterval, keyFrameInterval);
+                statistics.mMinimumKeyFrameInterval =
+                        Math.min(statistics.mMinimumKeyFrameInterval, keyFrameInterval);
+            }
+            Log.d(TAG, "  Key frame intervals: Max: " + statistics.mMaximumKeyFrameInterval +
+                    ". Min: " + statistics.mMinimumKeyFrameInterval +
+                    ". Avg: " + statistics.mAverageKeyFrameInterval);
+        }
+        Log.d(TAG, "  Frames: " + frames + ". Duration: " + duration +
+                ". Total size: " + totalSize + ". Key frames: " + keyFrames);
+        Log.d(TAG, keyFrameList);
+        Log.d(TAG, bitrateList);
+        Log.d(TAG, framesList);
+        Log.d(TAG, "  Bitrate average: " + statistics.mAverageBitrate);
+        Log.d(TAG, "  Maximum frame size: " + maxFrameSize);
+
+        return statistics;
+    }
+
+    protected Vp8EncodingStatistics computeEncodingStatistics(
+            ArrayList<MediaCodec.BufferInfo> bufferInfos ) {
+        return computeEncodingStatistics(0, bufferInfos);
+    }
+
+    protected ArrayList<Vp8EncodingStatistics> computeSimulcastEncodingStatistics(
+            ArrayList<ArrayList<MediaCodec.BufferInfo>> bufferInfos) {
+        int numCodecs = bufferInfos.size();
+        ArrayList<Vp8EncodingStatistics> statistics = new ArrayList<Vp8EncodingStatistics>();
+
+        for (int i = 0; i < numCodecs; i++) {
+            Vp8EncodingStatistics currentStatistics =
+                    computeEncodingStatistics(i, bufferInfos.get(i));
+            statistics.add(currentStatistics);
+        }
+        return statistics;
+    }
+
+    /**
+     * Calculates maximum latency for encoder/decoder based on buffer info array
+     * generated either by encoder or decoder.
+     */
+    protected int maxPresentationTimeDifference(ArrayList<MediaCodec.BufferInfo> bufferInfos) {
+        int maxValue = 0;
+        for (MediaCodec.BufferInfo bufferInfo : bufferInfos) {
+            maxValue = Math.max(maxValue,  bufferInfo.offset);
+        }
+        maxValue = (maxValue + 500) / 1000; // mcs -> ms
+        return maxValue;
+    }
+
+    /**
+     * Decoding PSNR statistics.
+     */
+    protected class Vp8DecodingStatistics {
+        Vp8DecodingStatistics() {
+            mMinimumPSNR = Integer.MAX_VALUE;
+        }
+        public double mAveragePSNR;
+        public double mMinimumPSNR;
+    }
+
+    /**
+     * Calculates PSNR value between two video frames.
+     */
+    private double computePSNR(byte[] data0, byte[] data1) {
+        long squareError = 0;
+        assertTrue(data0.length == data1.length);
+        int length = data0.length;
+        for (int i = 0 ; i < length; i++) {
+            int diff = ((int)data0[i] & 0xff) - ((int)data1[i] & 0xff);
+            squareError += diff * diff;
+        }
+        double meanSquareError = (double)squareError / length;
+        double psnr = 10 * Math.log10((double)255 * 255 / meanSquareError);
+        return psnr;
+    }
+
+    /**
+     * Calculates average and minimum PSNR values between
+     * set of reference and decoded video frames.
+     * Runs PSNR calculation for the full duration of the decoded data.
+     */
+    protected Vp8DecodingStatistics computeDecodingStatistics(
+            String referenceYuvFilename,
+            int referenceYuvRawId,
+            String decodedYuvFilename,
+            int width,
+            int height) throws Exception {
+        Vp8DecodingStatistics statistics = new Vp8DecodingStatistics();
+        InputStream referenceStream =
+                OpenFileOrResourceId(referenceYuvFilename, referenceYuvRawId);
+        InputStream decodedStream = new FileInputStream(decodedYuvFilename);
+
+        int ySize = width * height;
+        int uvSize = width * height / 4;
+        byte[] yRef = new byte[ySize];
+        byte[] yDec = new byte[ySize];
+        byte[] uvRef = new byte[uvSize];
+        byte[] uvDec = new byte[uvSize];
+
+        int frames = 0;
+        double averageYPSNR = 0;
+        double averageUPSNR = 0;
+        double averageVPSNR = 0;
+        double minimumYPSNR = Integer.MAX_VALUE;
+        double minimumUPSNR = Integer.MAX_VALUE;
+        double minimumVPSNR = Integer.MAX_VALUE;
+        int minimumPSNRFrameIndex = 0;
+
+        while (true) {
+            // Calculate Y PSNR.
+            int bytesReadRef = referenceStream.read(yRef);
+            int bytesReadDec = decodedStream.read(yDec);
+            if (bytesReadDec == -1) {
+                break;
+            }
+            if (bytesReadRef == -1) {
+                // Reference file wrapping up
+                referenceStream.close();
+                referenceStream =
+                        OpenFileOrResourceId(referenceYuvFilename, referenceYuvRawId);
+                bytesReadRef = referenceStream.read(yRef);
+            }
+            double curYPSNR = computePSNR(yRef, yDec);
+            averageYPSNR += curYPSNR;
+            minimumYPSNR = Math.min(minimumYPSNR, curYPSNR);
+            double curMinimumPSNR = curYPSNR;
+
+            // Calculate U PSNR.
+            bytesReadRef = referenceStream.read(uvRef);
+            bytesReadDec = decodedStream.read(uvDec);
+            double curUPSNR = computePSNR(uvRef, uvDec);
+            averageUPSNR += curUPSNR;
+            minimumUPSNR = Math.min(minimumUPSNR, curUPSNR);
+            curMinimumPSNR = Math.min(curMinimumPSNR, curUPSNR);
+
+            // Calculate V PSNR.
+            bytesReadRef = referenceStream.read(uvRef);
+            bytesReadDec = decodedStream.read(uvDec);
+            double curVPSNR = computePSNR(uvRef, uvDec);
+            averageVPSNR += curVPSNR;
+            minimumVPSNR = Math.min(minimumVPSNR, curVPSNR);
+            curMinimumPSNR = Math.min(curMinimumPSNR, curVPSNR);
+
+            // Frame index for minimum PSNR value - help to detect possible distortions
+            if (curMinimumPSNR < statistics.mMinimumPSNR) {
+                statistics.mMinimumPSNR = curMinimumPSNR;
+                minimumPSNRFrameIndex = frames;
+            }
+
+            String logStr = String.format(Locale.US, "PSNR #%d: Y: %.2f. U: %.2f. V: %.2f",
+                    frames, curYPSNR, curUPSNR, curVPSNR);
+            Log.v(TAG, logStr);
+
+            frames++;
+        }
+
+        averageYPSNR /= frames;
+        averageUPSNR /= frames;
+        averageVPSNR /= frames;
+        statistics.mAveragePSNR = (4 * averageYPSNR + averageUPSNR + averageVPSNR) / 6;
+
+        Log.d(TAG, "PSNR statistics for " + frames + " frames.");
+        String logStr = String.format(Locale.US,
+                "Average PSNR: Y: %.1f. U: %.1f. V: %.1f. Average: %.1f",
+                averageYPSNR, averageUPSNR, averageVPSNR, statistics.mAveragePSNR);
+        Log.d(TAG, logStr);
+        logStr = String.format(Locale.US,
+                "Minimum PSNR: Y: %.1f. U: %.1f. V: %.1f. Overall: %.1f at frame %d",
+                minimumYPSNR, minimumUPSNR, minimumVPSNR,
+                statistics.mMinimumPSNR, minimumPSNRFrameIndex);
+        Log.d(TAG, logStr);
+
+        referenceStream.close();
+        decodedStream.close();
+        return statistics;
+    }
+}
+
diff --git a/tests/tests/media/src/android/media/cts/Vp8EncoderTest.java b/tests/tests/media/src/android/media/cts/Vp8EncoderTest.java
index 2be0fc4..c4fe4bb 100644
--- a/tests/tests/media/src/android/media/cts/Vp8EncoderTest.java
+++ b/tests/tests/media/src/android/media/cts/Vp8EncoderTest.java
@@ -16,633 +16,335 @@
 
 package android.media.cts;
 
-import android.content.Context;
-import android.content.res.Resources;
 import android.media.MediaCodec;
-import android.media.MediaCodecInfo;
-import android.media.MediaCodecInfo.CodecCapabilities;
-import android.media.MediaCodecList;
-import android.media.MediaFormat;
-import android.os.Bundle;
-import android.test.AndroidTestCase;
 import android.util.Log;
-
 import com.android.cts.media.R;
 
-import java.io.InputStream;
-import java.nio.ByteBuffer;
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Arrays;
 
 /**
- * Basic verification test for vp8 encoder.
+ * Verification test for vp8 encoder and decoder.
  *
- * A raw yv12 stream is encoded and written to an IVF
- * file, which is later decoded by vp8 decoder to verify
- * frames are at least decodable.
+ * A raw yv12 stream is encoded at various settings and written to an IVF
+ * file. Encoded stream bitrate and key frame interval are checked against target values.
+ * The stream is later decoded by vp8 decoder to verify frames are decodable and to
+ * calculate PSNR values for various bitrates.
  */
-public class Vp8EncoderTest extends AndroidTestCase {
+public class Vp8EncoderTest extends Vp8CodecTestBase {
 
-    private static final String TAG = "VP8EncoderTest";
-    private static final String VP8_MIME = "video/x-vnd.on2.vp8";
-    private static final String VPX_DECODER_NAME = "OMX.google.vp8.decoder";
-    private static final String VPX_ENCODER_NAME = "OMX.google.vp8.encoder";
-    private static final String BASIC_IVF = "video_176x144_vp8_basic.ivf";
-    private static final long DEFAULT_TIMEOUT_US = 5000;
+    private static final String ENCODED_IVF_BASE = "football";
+    private static final String INPUT_YUV = null;
+    private static final String OUTPUT_YUV = SDCARD_DIR + File.separator +
+            ENCODED_IVF_BASE + "_out.yuv";
 
-    private Resources mResources;
-    private MediaCodec.BufferInfo mBufferInfo = new MediaCodec.BufferInfo();
-    private ByteBuffer[] mInputBuffers;
-    private ByteBuffer[] mOutputBuffers;
-
-    @Override
-    public void setContext(Context context) {
-        super.setContext(context);
-        mResources = mContext.getResources();
-    }
-
-     // TODO: Make a public method selectCodec() in common libraries (e.g. cts/libs/), to avoid
-     // redundant function definitions in this and other media related test files.
-     private static boolean hasCodec(String mimeType) {
-         int numCodecs = MediaCodecList.getCodecCount();
-
-         for (int i = 0; i < numCodecs; i++) {
-             MediaCodecInfo codecInfo = MediaCodecList.getCodecInfoAt(i);
-
-             if (!codecInfo.isEncoder()) {
-                 continue;
-             }
-
-             String[] types = codecInfo.getSupportedTypes();
-             for (int j = 0; j < types.length; j++) {
-                 if (types[j].equalsIgnoreCase(mimeType)) {
-                     return true;
-                 }
-             }
-         }
-         return false;
-     }
+    // YUV stream properties.
+    private static final int WIDTH = 320;
+    private static final int HEIGHT = 240;
+    private static final int FPS = 30;
+    // Default encoding bitrate.
+    private static final int BITRATE = 400000;
+    // Default encoding bitrate mode
+    private static final int BITRATE_MODE = VIDEO_ControlRateVariable;
+    // List of bitrates used in quality and basic bitrate tests.
+    private static final int[] TEST_BITRATES_SET = { 300000, 500000, 700000, 900000 };
+    // Maximum allowed bitrate variation from the target value.
+    private static final double MAX_BITRATE_VARIATION = 0.2;
+    // Average PSNR values for reference SW VP8 codec for the above bitrates.
+    private static final double[] REFERENCE_AVERAGE_PSNR = { 33.1, 35.2, 36.6, 37.8 };
+    // Minimum PSNR values for reference SW VP8 codec for the above bitrates.
+    private static final double[] REFERENCE_MINIMUM_PSNR = { 25.9, 27.5, 28.4, 30.3 };
+    // Maximum allowed average PSNR difference of HW encoder comparing to reference SW encoder.
+    private static final double MAX_AVERAGE_PSNR_DIFFERENCE = 2;
+    // Maximum allowed minimum PSNR difference of HW encoder comparing to reference SW encoder.
+    private static final double MAX_MINIMUM_PSNR_DIFFERENCE = 4;
+    // Maximum allowed average PSNR difference of the encoder running in a looper thread with 0 ms
+    // buffer dequeue timeout comparing to the encoder running in a callee's thread with 100 ms
+    // buffer dequeue timeout.
+    private static final double MAX_ASYNC_AVERAGE_PSNR_DIFFERENCE = 0.5;
+    // Maximum allowed minimum PSNR difference of the encoder running in a looper thread
+    // comparing to the encoder running in a callee's thread.
+    private static final double MAX_ASYNC_MINIMUM_PSNR_DIFFERENCE = 2;
+    // Maximum allowed average key frame interval variation from the target value.
+    private static final int MAX_AVERAGE_KEYFRAME_INTERVAL_VARIATION = 1;
+    // Maximum allowed key frame interval variation from the target value.
+    private static final int MAX_KEYFRAME_INTERVAL_VARIATION = 3;
 
     /**
      * A basic test for VP8 encoder.
      *
-     * Encodes a raw stream with default configuration options,
+     * Encodes 9 seconds of raw stream with default configuration options,
      * and then decodes it to verify the bitstream.
+     * Also checks the average bitrate is within MAX_BITRATE_VARIATION of the target value.
      */
     public void testBasic() throws Exception {
-        if (!hasCodec(VP8_MIME)) {
-            Log.w(TAG, "Codec " + VP8_MIME + " not supported. Return from testBasic.");
-            return;
-        }
+        int encodeSeconds = 9;
 
-        encode(BASIC_IVF,
-               R.raw.video_176x144_yv12,
-               176,  // width
-               144,  // height
-               30);  // framerate
-        decode(BASIC_IVF);
+        for (int targetBitrate : TEST_BITRATES_SET) {
+            EncoderOutputStreamParameters params = getDefaultEncodingParameters(
+                    INPUT_YUV,
+                    ENCODED_IVF_BASE,
+                    encodeSeconds,
+                    WIDTH,
+                    HEIGHT,
+                    FPS,
+                    BITRATE_MODE,
+                    targetBitrate,
+                    true);
+            ArrayList<MediaCodec.BufferInfo> bufInfo = encode(params);
+            Vp8EncodingStatistics statistics = computeEncodingStatistics(bufInfo);
+
+            assertEquals("Stream bitrate " + statistics.mAverageBitrate +
+                    " is different from the target " + targetBitrate,
+                    targetBitrate, statistics.mAverageBitrate,
+                    MAX_BITRATE_VARIATION * targetBitrate);
+
+            decode(params.outputIvfFilename, null, FPS, params.forceSwEncoder);
+        }
+    }
+
+    /**
+     * Asynchronous encoding test for VP8 encoder.
+     *
+     * Encodes 9 seconds of raw stream using synchronous and asynchronous calls.
+     * Checks the PSNR difference between the encoded and decoded output and reference yuv input
+     * does not change much for two different ways of the encoder call.
+     */
+    public void testAsyncEncoding() throws Exception {
+        int encodeSeconds = 9;
+
+        // First test the encoder running in a looper thread with buffer callbacks enabled.
+        boolean syncEncoding = false;
+        EncoderOutputStreamParameters params = getDefaultEncodingParameters(
+                INPUT_YUV,
+                ENCODED_IVF_BASE,
+                encodeSeconds,
+                WIDTH,
+                HEIGHT,
+                FPS,
+                BITRATE_MODE,
+                BITRATE,
+                syncEncoding);
+        ArrayList<MediaCodec.BufferInfo> bufInfos = encodeAsync(params);
+        computeEncodingStatistics(bufInfos);
+        decode(params.outputIvfFilename, OUTPUT_YUV, FPS, params.forceSwEncoder);
+        Vp8DecodingStatistics statisticsAsync = computeDecodingStatistics(
+                params.inputYuvFilename, R.raw.football_qvga, OUTPUT_YUV,
+                params.frameWidth, params.frameHeight);
+
+
+        // Test the encoder running in a callee's thread.
+        syncEncoding = true;
+        params = getDefaultEncodingParameters(
+                INPUT_YUV,
+                ENCODED_IVF_BASE,
+                encodeSeconds,
+                WIDTH,
+                HEIGHT,
+                FPS,
+                BITRATE_MODE,
+                BITRATE,
+                syncEncoding);
+        bufInfos = encode(params);
+        computeEncodingStatistics(bufInfos);
+        decode(params.outputIvfFilename, OUTPUT_YUV, FPS, params.forceSwEncoder);
+        Vp8DecodingStatistics statisticsSync = computeDecodingStatistics(
+                params.inputYuvFilename, R.raw.football_qvga, OUTPUT_YUV,
+                params.frameWidth, params.frameHeight);
+
+        // Check PSNR difference.
+        Log.d(TAG, "PSNR Average: Async: " + statisticsAsync.mAveragePSNR +
+                ". Sync: " + statisticsSync.mAveragePSNR);
+        Log.d(TAG, "PSNR Minimum: Async: " + statisticsAsync.mMinimumPSNR +
+                ". Sync: " + statisticsSync.mMinimumPSNR);
+        if ((Math.abs(statisticsAsync.mAveragePSNR - statisticsSync.mAveragePSNR) >
+            MAX_ASYNC_AVERAGE_PSNR_DIFFERENCE) ||
+            (Math.abs(statisticsAsync.mMinimumPSNR - statisticsSync.mMinimumPSNR) >
+            MAX_ASYNC_MINIMUM_PSNR_DIFFERENCE)) {
+            throw new RuntimeException("Difference between PSNRs for async and sync encoders");
+        }
     }
 
     /**
      * Check if MediaCodec.PARAMETER_KEY_REQUEST_SYNC_FRAME is honored.
      *
-     * At frame 15, request a sync frame. If one does not occur by EOF the
-     * encoder fails. The test does not verify the output stream.
+     * Encodes 9 seconds of raw stream and requests a sync frame every second (30 frames).
+     * The test does not verify the output stream.
      */
     public void testSyncFrame() throws Exception {
-        if (!hasCodec(VP8_MIME)) {
-            Log.w(TAG, "Codec " + VP8_MIME + " not supported. Return from testSyncFrame.");
-            return;
+        int encodeSeconds = 9;
+
+        EncoderOutputStreamParameters params = getDefaultEncodingParameters(
+                INPUT_YUV,
+                ENCODED_IVF_BASE,
+                encodeSeconds,
+                WIDTH,
+                HEIGHT,
+                FPS,
+                BITRATE_MODE,
+                BITRATE,
+                true);
+        params.syncFrameInterval = encodeSeconds * FPS;
+        params.syncForceFrameInterval = FPS;
+        ArrayList<MediaCodec.BufferInfo> bufInfo = encode(params);
+        Vp8EncodingStatistics statistics = computeEncodingStatistics(bufInfo);
+
+        // First check if we got expected number of key frames.
+        int actualKeyFrames = statistics.mKeyFrames.size();
+        if (actualKeyFrames != encodeSeconds) {
+            throw new RuntimeException("Number of key frames " + actualKeyFrames +
+                    " is different from the expected " + encodeSeconds);
         }
 
-        encodeSyncFrame(R.raw.video_176x144_yv12,
-                        176, // width
-                        144, // height
-                        30); // framerate
+        // Check key frame intervals:
+        // Average value should be within +/- 1 frame of the target value,
+        // maximum value should not be greater than target value + 3,
+        // and minimum value should not be less that target value - 3.
+        if (Math.abs(statistics.mAverageKeyFrameInterval - FPS) >
+            MAX_AVERAGE_KEYFRAME_INTERVAL_VARIATION ||
+            (statistics.mMaximumKeyFrameInterval - FPS > MAX_KEYFRAME_INTERVAL_VARIATION) ||
+            (FPS - statistics.mMinimumKeyFrameInterval > MAX_KEYFRAME_INTERVAL_VARIATION)) {
+            throw new RuntimeException(
+                    "Key frame intervals are different from the expected " + FPS);
+        }
     }
 
     /**
      * Check if MediaCodec.PARAMETER_KEY_VIDEO_BITRATE is honored.
      *
-     * Run the sample multiple times. Request periodic changes to the
-     * bitrate and ensure the encoder responds.
+     * Run the the encoder for 12 seconds. Request changes to the
+     * bitrate after 6 seconds and ensure the encoder responds.
      */
-    public void testVariableBitrate() throws Exception {
-        if (!hasCodec(VP8_MIME)) {
-            Log.w(TAG, "Codec " + VP8_MIME + " not supported. Return from testVariableBitrate.");
-            return;
+     public void testDynamicBitrateChange() throws Exception {
+        int encodeSeconds = 12;    // Encoding sequence duration in seconds.
+        int[] bitrateTargetValues = { 400000, 800000 };  // List of bitrates to test.
+
+        EncoderOutputStreamParameters params = getDefaultEncodingParameters(
+                INPUT_YUV,
+                ENCODED_IVF_BASE,
+                encodeSeconds,
+                WIDTH,
+                HEIGHT,
+                FPS,
+                BITRATE_MODE,
+                bitrateTargetValues[0],
+                true);
+
+        // Number of seconds for each bitrate
+        int stepSeconds = encodeSeconds / bitrateTargetValues.length;
+        // Fill the bitrates values.
+        params.bitrateSet = new int[encodeSeconds * FPS];
+        for (int i = 0; i < bitrateTargetValues.length ; i++) {
+            Arrays.fill(params.bitrateSet,
+                    i * encodeSeconds * FPS / bitrateTargetValues.length,
+                    (i + 1) * encodeSeconds * FPS / bitrateTargetValues.length,
+                    bitrateTargetValues[i]);
         }
 
-        encodeVariableBitrate(R.raw.video_176x144_yv12,
-                              176, // width
-                              144, // height
-                              30); // framerate
-    }
+        ArrayList<MediaCodec.BufferInfo> bufInfo = encode(params);
+        Vp8EncodingStatistics statistics = computeEncodingStatistics(bufInfo);
 
-    /**
-     * A basic check if an encoded stream is decodable.
-     *
-     * The most basic confirmation we can get about a frame
-     * being properly encoded is trying to decode it.
-     * (Especially in realtime mode encode output is non-
-     * deterministic, therefore a more thorough check like
-     * md5 sum comparison wouldn't work.)
-     *
-     * Indeed, MediaCodec will raise an IllegalStateException
-     * whenever vp8 decoder fails to decode a frame, and
-     * this test uses that fact to verify the bitstream.
-     *
-     * @param filename  The name of the IVF file containing encoded bitsream.
-     */
-    private void decode(String filename) throws Exception {
-        IvfReader ivf = null;
-        try {
-            ivf = new IvfReader(filename);
-            int frameWidth = ivf.getWidth();
-            int frameHeight = ivf.getHeight();
-            int frameCount = ivf.getFrameCount();
-
-            assertTrue(frameWidth > 0);
-            assertTrue(frameHeight > 0);
-            assertTrue(frameCount > 0);
-
-            MediaFormat format = MediaFormat.createVideoFormat(VP8_MIME,
-                                                               ivf.getWidth(),
-                                                               ivf.getHeight());
-
-            Log.d(TAG, "Creating decoder");
-            MediaCodec decoder = MediaCodec.createByCodecName(VPX_DECODER_NAME);
-            decoder.configure(format,
-                              null,  // surface
-                              null,  // crypto
-                              0);  // flags
-            decoder.start();
-
-            mInputBuffers = decoder.getInputBuffers();
-            mOutputBuffers = decoder.getOutputBuffers();
-
-            // decode loop
-            int frameIndex = 0;
-            boolean sawOutputEOS = false;
-            boolean sawInputEOS = false;
-
-            while (!sawOutputEOS) {
-                if (!sawInputEOS) {
-                    int inputBufIndex = decoder.dequeueInputBuffer(DEFAULT_TIMEOUT_US);
-                    if (inputBufIndex >= 0) {
-                        byte[] frame = ivf.readFrame(frameIndex);
-
-                        if (frameIndex == frameCount - 1) {
-                            sawInputEOS = true;
-                        }
-
-                        mInputBuffers[inputBufIndex].clear();
-                        mInputBuffers[inputBufIndex].put(frame);
-                        mInputBuffers[inputBufIndex].rewind();
-
-                        Log.d(TAG, "Decoding frame at index " + frameIndex);
-                        try {
-                            decoder.queueInputBuffer(
-                                    inputBufIndex,
-                                    0,  // offset
-                                    frame.length,
-                                    frameIndex,
-                                    sawInputEOS ? MediaCodec.BUFFER_FLAG_END_OF_STREAM : 0);
-                        } catch (IllegalStateException ise) {
-                            //That is all what is passed from MediaCodec in case of
-                            //decode failure.
-                            fail("Failed to decode frame at index " + frameIndex);
-                        }
-                        frameIndex++;
-                    }
-                }
-
-                int result = decoder.dequeueOutputBuffer(mBufferInfo, DEFAULT_TIMEOUT_US);
-                if (result >= 0) {
-                    int outputBufIndex = result;
-                    if ((mBufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
-                        sawOutputEOS = true;
-                    }
-                    decoder.releaseOutputBuffer(outputBufIndex, false);
-                } else if (result == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
-                    mOutputBuffers = decoder.getOutputBuffers();
-                }
+        // Calculate actual average bitrates  for every [stepSeconds] second.
+        int[] bitrateActualValues = new int[bitrateTargetValues.length];
+        for (int i = 0; i < bitrateTargetValues.length ; i++) {
+            bitrateActualValues[i] = 0;
+            for (int j = i * stepSeconds; j < (i + 1) * stepSeconds; j++) {
+                bitrateActualValues[i] += statistics.mBitrates.get(j);
             }
-            decoder.stop();
-            decoder.release();
-        } finally {
-            if (ivf != null) {
-                ivf.close();
+            bitrateActualValues[i] /= stepSeconds;
+            Log.d(TAG, "Actual bitrate for interval #" + i + " : " + bitrateActualValues[i] +
+                    ". Target: " + bitrateTargetValues[i]);
+
+            // Compare actual bitrate values to make sure at least same increasing/decreasing
+            // order as the target bitrate values.
+            for (int j = 0; j < i; j++) {
+                long differenceTarget = bitrateTargetValues[i] - bitrateTargetValues[j];
+                long differenceActual = bitrateActualValues[i] - bitrateActualValues[j];
+                if (differenceTarget * differenceActual < 0) {
+                    throw new RuntimeException("Target bitrates: " +
+                            bitrateTargetValues[j] + " , " + bitrateTargetValues[i] +
+                            ". Actual bitrates: "
+                            + bitrateActualValues[j] + " , " + bitrateActualValues[i]);
+                }
             }
         }
     }
 
     /**
-     * A basic vp8 encode loop.
+     * Check the encoder quality for various bitrates by calculating PSNR
      *
-     * MediaCodec will raise an IllegalStateException
-     * whenever vp8 encoder fails to encode a frame.
-     *
-     * In addition to that written IVF file can be tested
-     * to be decodable in order to verify the bitstream produced.
-     *
-     * Color format of input file should be YUV420, and frameWidth,
-     * frameHeight should be supplied correctly as raw input file doesn't
-     * include any header data.
-     *
-     * @param outputFilename  The name of the IVF file to write encoded bitsream
-     * @param rawInputFd      File descriptor for the raw input file (YUV420)
-     * @param frameWidth      Frame width of input file
-     * @param frameHeight     Frame height of input file
-     * @param frameRate       Frame rate of input file in frames per second
+     * Run the the encoder for 9 seconds for each bitrate and calculate PSNR
+     * for each encoded stream.
+     * Video streams with higher bitrates should have higher PSNRs.
+     * Also compares average and minimum PSNR of HW codec with PSNR values of reference SW codec.
      */
-    private void encode(String outputFilename, int rawInputFd,
-                       int frameWidth, int frameHeight, int frameRate) throws Exception {
-        // Create a media format signifying desired output
-        MediaFormat format = MediaFormat.createVideoFormat(VP8_MIME, frameWidth, frameHeight);
-        format.setInteger(MediaFormat.KEY_BIT_RATE, 100000);
-        format.setInteger(MediaFormat.KEY_COLOR_FORMAT,
-                          CodecCapabilities.COLOR_FormatYUV420Planar);
-        format.setInteger(MediaFormat.KEY_FRAME_RATE, 30);
+    public void testEncoderQuality() throws Exception {
+        int encodeSeconds = 9;      // Encoding sequence duration in seconds for each bitrate.
+        double[] psnrPlatformCodecAverage = new double[TEST_BITRATES_SET.length];
+        double[] psnrPlatformCodecMin = new double[TEST_BITRATES_SET.length];
 
-        Log.d(TAG, "Creating encoder");
-        MediaCodec encoder;
-        encoder = MediaCodec.createByCodecName(VPX_ENCODER_NAME);
-        encoder.configure(format,
-                          null,  // surface
-                          null,  // crypto
-                          MediaCodec.CONFIGURE_FLAG_ENCODE);
-        encoder.start();
+        // Run platform specific encoder for different bitrates
+        // and compare PSNR of hw codec with PSNR of reference sw codec.
+        for (int i = 0; i < TEST_BITRATES_SET.length; i++) {
+            EncoderOutputStreamParameters params = getDefaultEncodingParameters(
+                    INPUT_YUV,
+                    ENCODED_IVF_BASE,
+                    encodeSeconds,
+                    WIDTH,
+                    HEIGHT,
+                    FPS,
+                    BITRATE_MODE,
+                    TEST_BITRATES_SET[i],
+                    true);
+            encode(params);
 
-        mInputBuffers = encoder.getInputBuffers();
-        mOutputBuffers = encoder.getOutputBuffers();
+            decode(params.outputIvfFilename, OUTPUT_YUV, FPS, params.forceSwEncoder);
+            Vp8DecodingStatistics statistics = computeDecodingStatistics(
+                    params.inputYuvFilename, R.raw.football_qvga, OUTPUT_YUV,
+                    params.frameWidth, params.frameHeight);
+            psnrPlatformCodecAverage[i] = statistics.mAveragePSNR;
+            psnrPlatformCodecMin[i] = statistics.mMinimumPSNR;
+        }
 
-        InputStream rawStream = null;
-        IvfWriter ivf = null;
-
-        try {
-            rawStream = mResources.openRawResource(rawInputFd);
-            ivf = new IvfWriter(outputFilename, frameWidth, frameHeight);
-            // encode loop
-            long presentationTimeUs = 0;
-            int inputFrameIndex = 0;
-            int outputFrameIndex = 0;
-            boolean sawInputEOS = false;
-            boolean sawOutputEOS = false;
-
-            while (!sawOutputEOS) {
-                if (!sawInputEOS) {
-                    int inputBufIndex = encoder.dequeueInputBuffer(DEFAULT_TIMEOUT_US);
-                    if (inputBufIndex >= 0) {
-                        // YUV420 has 3 planes. Y is full size. U and V are each half size (1/4 the
-                        // pixels).
-                        int frameSize = frameWidth * frameHeight * 3 / 2;
-
-                        byte[] frame = new byte[frameSize];
-                        int bytesRead = rawStream.read(frame);
-
-                        if (bytesRead == -1) {
-                            sawInputEOS = true;
-                            bytesRead = 0;
-                        }
-
-                        mInputBuffers[inputBufIndex].clear();
-                        mInputBuffers[inputBufIndex].put(frame);
-                        mInputBuffers[inputBufIndex].rewind();
-
-                        presentationTimeUs = (inputFrameIndex * 1000000) / frameRate;
-                        Log.d(TAG, "Encoding frame at index " + inputFrameIndex);
-                        encoder.queueInputBuffer(
-                                inputBufIndex,
-                                0,  // offset
-                                bytesRead,  // size
-                                presentationTimeUs,
-                                sawInputEOS ? MediaCodec.BUFFER_FLAG_END_OF_STREAM : 0);
-
-                        inputFrameIndex++;
-                    }
+        // First do a sanity check - higher bitrates should results in higher PSNR.
+        for (int i = 1; i < TEST_BITRATES_SET.length ; i++) {
+            for (int j = 0; j < i; j++) {
+                double differenceBitrate = TEST_BITRATES_SET[i] - TEST_BITRATES_SET[j];
+                double differencePSNR = psnrPlatformCodecAverage[i] - psnrPlatformCodecAverage[j];
+                if (differenceBitrate * differencePSNR < 0) {
+                    throw new RuntimeException("Target bitrates: " +
+                            TEST_BITRATES_SET[j] + ", " + TEST_BITRATES_SET[i] +
+                            ". Actual PSNRs: "
+                            + psnrPlatformCodecAverage[j] + ", " + psnrPlatformCodecAverage[i]);
                 }
-
-                int result = encoder.dequeueOutputBuffer(mBufferInfo, DEFAULT_TIMEOUT_US);
-                if (result >= 0) {
-                    int outputBufIndex = result;
-                    byte[] buffer = new byte[mBufferInfo.size];
-                    mOutputBuffers[outputBufIndex].rewind();
-                    mOutputBuffers[outputBufIndex].get(buffer, 0, mBufferInfo.size);
-
-                    if ((outputFrameIndex == 0)
-                        && ((mBufferInfo.flags & MediaCodec.BUFFER_FLAG_SYNC_FRAME) == 0)) {
-                      throw new RuntimeException("First frame is not a sync frame.");
-
-                    }
-
-                    if ((mBufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
-                        sawOutputEOS = true;
-                    } else {
-                        ivf.writeFrame(buffer, mBufferInfo.presentationTimeUs);
-                    }
-                    encoder.releaseOutputBuffer(outputBufIndex,
-                                                false);  // render
-
-                    outputFrameIndex++;
-                } else if (result == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
-                    mOutputBuffers = encoder.getOutputBuffers();
-                }
-            }
-
-            encoder.stop();
-            encoder.release();
-        } finally {
-            if (ivf != null) {
-                ivf.close();
-            }
-
-            if (rawStream != null) {
-                rawStream.close();
             }
         }
-    }
 
-
-    /**
-     * Request Sync Frames
-     *
-     * MediaCodec will raise an IllegalStateException
-     * whenever vp8 encoder fails to encode a frame.
-     *
-     * This presumes a file with 28 frames. Under normal circumstances there
-     * would only be one sync frame: the first one. This test will request an
-     * additional sync frame at 15 and ensure that it occurs by EOF.
-     *
-     * Color format of input file should be YUV420, and frameWidth,
-     * frameHeight should be supplied correctly as raw input file doesn't
-     * include any header data.
-     *
-     * @param rawInputFd      File descriptor for the raw input file (YUV420)
-     * @param frameWidth      Frame width of input file
-     * @param frameHeight     Frame height of input file
-     * @param frameRate       Frame rate of input file in frames per second
-     */
-    private void encodeSyncFrame(int rawInputFd, int frameWidth,
-                                 int frameHeight, int frameRate) throws Exception {
-        // Create a media format signifying desired output
-        MediaFormat format = MediaFormat.createVideoFormat(VP8_MIME, frameWidth, frameHeight);
-        format.setInteger(MediaFormat.KEY_BIT_RATE, 100000);
-        format.setInteger(MediaFormat.KEY_COLOR_FORMAT,
-                          CodecCapabilities.COLOR_FormatYUV420Planar);
-        format.setInteger(MediaFormat.KEY_FRAME_RATE, 30);
-
-        Log.d(TAG, "Creating encoder");
-        MediaCodec encoder;
-        encoder = MediaCodec.createByCodecName(VPX_ENCODER_NAME);
-        encoder.configure(format,
-                          null,  // surface
-                          null,  // crypto
-                          MediaCodec.CONFIGURE_FLAG_ENCODE);
-        encoder.start();
-
-        mInputBuffers = encoder.getInputBuffers();
-        mOutputBuffers = encoder.getOutputBuffers();
-
-        InputStream rawStream = null;
-
-        try {
-            rawStream = mResources.openRawResource(rawInputFd);
-            // encode loop
-            long presentationTimeUs = 0;
-            int inputFrameIndex = 0;
-            boolean sawInputEOS = false;
-            boolean sawOutputEOS = false;
-            boolean syncFrameRequested = false;
-            boolean matchedSyncFrame = false;
-
-            while (!sawOutputEOS) {
-                if (!sawInputEOS) {
-                    int inputBufIndex = encoder.dequeueInputBuffer(DEFAULT_TIMEOUT_US);
-                    if (inputBufIndex >= 0) {
-                        int frameSize = frameWidth * frameHeight * 3 / 2;
-
-                        byte[] frame = new byte[frameSize];
-                        int bytesRead = rawStream.read(frame);
-
-                        if (bytesRead == -1) {
-                            sawInputEOS = true;
-                            bytesRead = 0;
-                        }
-
-                        mInputBuffers[inputBufIndex].clear();
-                        mInputBuffers[inputBufIndex].put(frame);
-                        mInputBuffers[inputBufIndex].rewind();
-
-                        if (inputFrameIndex == 15) {
-                            Log.d(TAG, "Requesting sync frame at index " + inputFrameIndex);
-                            Bundle syncFrame = new Bundle();
-                            syncFrame.putInt(MediaCodec.PARAMETER_KEY_REQUEST_SYNC_FRAME, 0);
-                            encoder.setParameters(syncFrame);
-                            syncFrameRequested = true;
-                        }
-
-                        presentationTimeUs = (inputFrameIndex * 1000000) / frameRate;
-                        encoder.queueInputBuffer(
-                                inputBufIndex,
-                                0,  // offset
-                                bytesRead,  // size
-                                presentationTimeUs,
-                                sawInputEOS ? MediaCodec.BUFFER_FLAG_END_OF_STREAM : 0);
-
-                        inputFrameIndex++;
-                    }
-                }
-
-                int result = encoder.dequeueOutputBuffer(mBufferInfo, DEFAULT_TIMEOUT_US);
-                if (result >= 0) {
-                    if (syncFrameRequested && ((mBufferInfo.flags & MediaCodec.BUFFER_FLAG_SYNC_FRAME) != 0)) {
-                        Log.d(TAG, "Found sync frame");
-                        matchedSyncFrame = true;
-                    }
-
-                    if ((mBufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
-                        sawOutputEOS = true;
-                    }
-
-                    encoder.releaseOutputBuffer(result,
-                                                false);  // render
-
-                } else if (result == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
-                    mOutputBuffers = encoder.getOutputBuffers();
-                }
+        // Then compare average and minimum PSNR of platform codec with reference sw codec -
+        // average PSNR for platform codec should be no more than 2 dB less than reference PSNR
+        // and minumum PSNR - no more than 4 dB less than reference minimum PSNR.
+        // These PSNR difference numbers are arbitrary for now, will need further estimation
+        // when more devices with hw VP8 codec will appear.
+        for (int i = 0; i < TEST_BITRATES_SET.length ; i++) {
+            Log.d(TAG, "Bitrate " + TEST_BITRATES_SET[i]);
+            Log.d(TAG, "Reference: Average: " + REFERENCE_AVERAGE_PSNR[i] + ". Minimum: " +
+                    REFERENCE_MINIMUM_PSNR[i]);
+            Log.d(TAG, "Platform:  Average: " + psnrPlatformCodecAverage[i] + ". Minimum: " +
+                    psnrPlatformCodecMin[i]);
+            if (psnrPlatformCodecAverage[i] < REFERENCE_AVERAGE_PSNR[i] -
+                    MAX_AVERAGE_PSNR_DIFFERENCE) {
+                throw new RuntimeException("Low average PSNR " + psnrPlatformCodecAverage[i] +
+                        " comparing to reference PSNR " + REFERENCE_AVERAGE_PSNR[i] +
+                        " for bitrate " + TEST_BITRATES_SET[i]);
             }
-
-            if (!matchedSyncFrame) {
-                throw new RuntimeException("Requested sync frame did not occur");
-            }
-
-            encoder.stop();
-            encoder.release();
-        } finally {
-            if (rawStream != null) {
-                rawStream.close();
-            }
-        }
-    }
-
-
-    /**
-     * Adjust bitrate
-     *
-     * MediaCodec will raise an IllegalStateException
-     * whenever vp8 encoder fails to encode a frame.
-     *
-     * Encode the file three times: once at the initial bitrate, once at an
-     * increased bitrate, and once at a decreased bitrate. Record the frame
-     * sizes that are returned and verify a strict ordering.
-     *
-     * Color format of input file should be YUV420, and frameWidth,
-     * frameHeight should be supplied correctly as raw input file doesn't
-     * include any header data.
-     *
-     * @param rawInputFd      File descriptor for the raw input file (YUV420)
-     * @param frameWidth      Frame width of input file
-     * @param frameHeight     Frame height of input file
-     * @param frameRate       Frame rate of input file in frames per second
-     */
-    private void encodeVariableBitrate(int rawInputFd, int frameWidth,
-                                       int frameHeight, int frameRate) throws Exception {
-        // Create a media format signifying desired output
-        MediaFormat format = MediaFormat.createVideoFormat(VP8_MIME, frameWidth, frameHeight);
-        format.setInteger(MediaFormat.KEY_BIT_RATE, 75000);
-        format.setInteger(MediaFormat.KEY_COLOR_FORMAT,
-                          CodecCapabilities.COLOR_FormatYUV420Planar);
-        format.setInteger(MediaFormat.KEY_FRAME_RATE, 30);
-
-        Log.d(TAG, "Creating encoder");
-        MediaCodec encoder;
-        encoder = MediaCodec.createByCodecName(VPX_ENCODER_NAME);
-        encoder.configure(format,
-                          null,  // surface
-                          null,  // crypto
-                          MediaCodec.CONFIGURE_FLAG_ENCODE);
-        encoder.start();
-
-        mInputBuffers = encoder.getInputBuffers();
-        mOutputBuffers = encoder.getOutputBuffers();
-
-        InputStream rawStream = null;
-
-        int iteration = 0;
-        int[] bits = new int[100];
-
-        try {
-            rawStream = mResources.openRawResource(rawInputFd);
-            /* Doc says this is not the default:
-             * http://developer.android.com/reference/java/io/InputStream.html#markSupported()
-             * but it returns true so using .reset() instead of close/open
-             */
-            if (rawStream.markSupported()) Log.d(TAG, "Stream marking supported");
-            rawStream.mark(1000000);
-
-            // encode loop
-            long presentationTimeUs = 0;
-            int inputFrameIndex = 0;
-            int outputFrameIndex = 0;
-            boolean sawInputEOS = false;
-            boolean sawOutputEOS = false;
-
-            while (!sawOutputEOS) {
-                if (!sawInputEOS) {
-                    int inputBufIndex = encoder.dequeueInputBuffer(DEFAULT_TIMEOUT_US);
-                    if (inputBufIndex >= 0) {
-                        int frameSize = frameWidth * frameHeight * 3 / 2;
-
-                        byte[] frame = new byte[frameSize];
-                        int bytesRead = rawStream.read(frame);
-
-                        if (bytesRead == -1) {
-                            if (iteration < 2) {
-                                rawStream.reset();
-                                Bundle bitrate = new Bundle();
-                                if (iteration == 0) {
-                                    bitrate.putInt(MediaCodec.PARAMETER_KEY_VIDEO_BITRATE, 150000);
-                                    Log.d(TAG, "Setting bitrate to 150000");
-                                } else {
-                                    bitrate.putInt(MediaCodec.PARAMETER_KEY_VIDEO_BITRATE, 25000);
-                                    Log.d(TAG, "Setting bitrate to 25000");
-                                }
-                                encoder.setParameters(bitrate);
-
-                                iteration++;
-                                continue;
-                            } else {
-                                sawInputEOS = true;
-                                bytesRead = 0;
-                            }
-                        }
-
-                        mInputBuffers[inputBufIndex].clear();
-                        mInputBuffers[inputBufIndex].put(frame);
-                        mInputBuffers[inputBufIndex].rewind();
-
-                        presentationTimeUs = (inputFrameIndex * 1000000) / frameRate;
-                        encoder.queueInputBuffer(
-                                inputBufIndex,
-                                0,  // offset
-                                bytesRead,  // size
-                                presentationTimeUs,
-                                sawInputEOS ? MediaCodec.BUFFER_FLAG_END_OF_STREAM : 0);
-
-                        inputFrameIndex++;
-                    }
-                }
-
-                int result = encoder.dequeueOutputBuffer(mBufferInfo, DEFAULT_TIMEOUT_US);
-                if (result >= 0) {
-
-                    bits[outputFrameIndex] = mBufferInfo.size;
-
-                    if ((mBufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
-                        sawOutputEOS = true;
-                    }
-
-                    encoder.releaseOutputBuffer(result,
-                                                false);  // render
-
-                    outputFrameIndex++;
-
-                } else if (result == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
-                    mOutputBuffers = encoder.getOutputBuffers();
-                }
-            }
-
-            // 29 frames per run
-            int i;
-            int sum = 0;
-            int frames = 29;
-            for(i = 0; i < frames; i++)
-              sum += bits[i];
-            int midBitrateAvg = sum / frames;
-
-            sum = 0;
-            for(; i < frames * 2; i++)
-              sum += bits[i];
-            int highBitrateAvg = sum / frames;
-
-            sum = 0;
-            for(; i < frames * 3; i++)
-              sum += bits[i];
-            int lowBitrateAvg = sum / frames;
-
-            // For the given bitrates we expect mid ~= 350, high ~= 575 and low ~= 150
-            // bytes per frame
-            if ((midBitrateAvg + 100) > highBitrateAvg)
-                throw new RuntimeException("Bitrate did not increase when requesting higher bitrate");
-            if ((lowBitrateAvg + 100) > midBitrateAvg)
-                throw new RuntimeException("Bitrate did not decrease when requesting lower bitrate");
-
-
-            encoder.stop();
-            encoder.release();
-        } finally {
-            if (rawStream != null) {
-                rawStream.close();
+            if (psnrPlatformCodecMin[i] < REFERENCE_MINIMUM_PSNR[i] -
+                    MAX_MINIMUM_PSNR_DIFFERENCE) {
+                throw new RuntimeException("Low minimum PSNR " + psnrPlatformCodecMin[i] +
+                        " comparing to sw PSNR " + REFERENCE_MINIMUM_PSNR[i] +
+                        " for bitrate " + TEST_BITRATES_SET[i]);
             }
         }
     }
 }
+
diff --git a/tests/tests/mediastress/Android.mk b/tests/tests/mediastress/Android.mk
index 9f43597..5c4930b 100644
--- a/tests/tests/mediastress/Android.mk
+++ b/tests/tests/mediastress/Android.mk
@@ -20,8 +20,6 @@
 
 LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
 
-LOCAL_JAVA_LIBRARIES := android.test.runner
-
 LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner ctsdeviceutil
 
 LOCAL_JNI_SHARED_LIBRARIES := libctsmediastress_jni
diff --git a/tests/tests/mediastress/AndroidManifest.xml b/tests/tests/mediastress/AndroidManifest.xml
index 931a774..9d48c8c 100644
--- a/tests/tests/mediastress/AndroidManifest.xml
+++ b/tests/tests/mediastress/AndroidManifest.xml
@@ -39,8 +39,11 @@
         <activity android:name="android.mediastress.cts.NativeMediaActivity"
                   android:label="NativeMedia" />
     </application>
-    <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
             android:targetPackage="com.android.cts.mediastress"
-            android:label="Media stress tests InstrumentationRunner" />
+            android:label="Media stress tests InstrumentationRunner" >
+        <meta-data android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
 
 </manifest>
diff --git a/tests/tests/mediastress/src/android/mediastress/cts/HEVCR1080pAacLongPlayerTest.java b/tests/tests/mediastress/src/android/mediastress/cts/HEVCR1080pAacLongPlayerTest.java
new file mode 100644
index 0000000..e8a92e0
--- /dev/null
+++ b/tests/tests/mediastress/src/android/mediastress/cts/HEVCR1080pAacLongPlayerTest.java
@@ -0,0 +1,42 @@
+/*
+ * 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.mediastress.cts;
+
+import android.media.CamcorderProfile;
+import android.media.MediaRecorder.AudioEncoder;
+import android.media.MediaRecorder.VideoEncoder;
+
+public class HEVCR1080pAacLongPlayerTest extends MediaPlayerStressTest {
+    private static final String VIDEO_PATH_MIDDLE = "bbb_full/1920x1080/mp4_libx265_libfaac/";
+    private final String[] mMedias = {
+        "bbb_full.ffmpeg.1920x1080.mp4.libx265_6500kbps_30fps.libfaac_stereo_128kbps_48000Hz.mp4"
+    };
+
+    public HEVCR1080pAacLongPlayerTest() {
+        super(CamcorderProfile.QUALITY_1080P, VideoEncoder.H264, AudioEncoder.AAC);
+    }
+
+    public void testPlay00() throws Exception {
+        doTestVideoPlaybackLong(0);
+    }
+
+    @Override
+    protected String getFullVideoClipName(int mediaNumber) {
+        return VIDEO_TOP_DIR + VIDEO_PATH_MIDDLE + mMedias[mediaNumber];
+    }
+
+}
diff --git a/tests/tests/mediastress/src/android/mediastress/cts/HEVCR1080pAacRepeatedPlayerTest.java b/tests/tests/mediastress/src/android/mediastress/cts/HEVCR1080pAacRepeatedPlayerTest.java
new file mode 100644
index 0000000..7ce3c3a
--- /dev/null
+++ b/tests/tests/mediastress/src/android/mediastress/cts/HEVCR1080pAacRepeatedPlayerTest.java
@@ -0,0 +1,41 @@
+/*
+ * 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.mediastress.cts;
+
+import android.media.CamcorderProfile;
+import android.media.MediaRecorder.AudioEncoder;
+import android.media.MediaRecorder.VideoEncoder;
+
+public class HEVCR1080pAacRepeatedPlayerTest extends MediaPlayerStressTest {
+    private static final String VIDEO_PATH_MIDDLE = "bbb_short/1920x1080/mp4_libx265_libfaac/";
+    private final String[] mMedias = {
+        "bbb_short.fmpeg.1920x1080.mp4.libx265_6500kbps_30fps.libfaac_stereo_128kbps_48000hz.mp4",
+    };
+
+    public HEVCR1080pAacRepeatedPlayerTest() {
+        super(CamcorderProfile.QUALITY_1080P, VideoEncoder.H264, AudioEncoder.AAC);
+    }
+
+    public void testPlay00() throws Exception {
+        doTestVideoPlaybackRepeated(0);
+    }
+
+    @Override
+    protected String getFullVideoClipName(int mediaNumber) {
+        return VIDEO_TOP_DIR + VIDEO_PATH_MIDDLE + mMedias[mediaNumber];
+    }
+}
diff --git a/tests/tests/mediastress/src/android/mediastress/cts/HEVCR1080pAacShortPlayerTest.java b/tests/tests/mediastress/src/android/mediastress/cts/HEVCR1080pAacShortPlayerTest.java
new file mode 100644
index 0000000..1d12b8c
--- /dev/null
+++ b/tests/tests/mediastress/src/android/mediastress/cts/HEVCR1080pAacShortPlayerTest.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.mediastress.cts;
+
+import android.media.CamcorderProfile;
+import android.media.MediaRecorder.AudioEncoder;
+import android.media.MediaRecorder.VideoEncoder;
+
+public class HEVCR1080pAacShortPlayerTest extends MediaPlayerStressTest {
+    private static final String VIDEO_PATH_MIDDLE = "bbb_short/1920x1080/mp4_libx265_libfaac/";
+    private final String[] mMedias = {
+        "bbb_short.fmpeg.1920x1080.mp4.libx265_6500kbps_30fps.libfaac_stereo_128kbps_48000hz.mp4",
+        "bbb_short.fmpeg.1920x1080.mp4.libx265_1140kbps_30fps.libfaac_stereo_128kbps_48000hz.mp4",
+        "bbb_short.fmpeg.1920x1080.mp4.libx265_3250kbps_30fps.libfaac_stereo_128kbps_48000hz.mp4"
+    };
+
+    public HEVCR1080pAacShortPlayerTest() {
+        super(CamcorderProfile.QUALITY_1080P, VideoEncoder.H264, AudioEncoder.AAC);
+    }
+
+    public void testPlay00() throws Exception {
+        doTestVideoPlaybackShort(0);
+    }
+
+    public void testPlay01() throws Exception {
+        doTestVideoPlaybackShort(1);
+    }
+
+    public void testPlay02() throws Exception {
+        doTestVideoPlaybackShort(2);
+    }
+
+    @Override
+    protected String getFullVideoClipName(int mediaNumber) {
+        return VIDEO_TOP_DIR + VIDEO_PATH_MIDDLE + mMedias[mediaNumber];
+    }
+
+}
diff --git a/tests/tests/mediastress/src/android/mediastress/cts/HEVCR480pAacLongPlayerTest.java b/tests/tests/mediastress/src/android/mediastress/cts/HEVCR480pAacLongPlayerTest.java
new file mode 100644
index 0000000..e54c51f
--- /dev/null
+++ b/tests/tests/mediastress/src/android/mediastress/cts/HEVCR480pAacLongPlayerTest.java
@@ -0,0 +1,42 @@
+/*
+ * 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.mediastress.cts;
+
+import android.media.CamcorderProfile;
+import android.media.MediaRecorder.AudioEncoder;
+import android.media.MediaRecorder.VideoEncoder;
+
+public class HEVCR480pAacLongPlayerTest extends MediaPlayerStressTest {
+    private static final String VIDEO_PATH_MIDDLE = "bbb_full/720x480/mp4_libx265_libfaac/";
+    private final String[] mMedias = {
+        "bbb_full.ffmpeg.720x480.mp4.libx265_325kbps_24fps.libfaac_stereo_128kbps_48000Hz.mp4"
+    };
+
+    public HEVCR480pAacLongPlayerTest() {
+        super(CamcorderProfile.QUALITY_480P, VideoEncoder.H264, AudioEncoder.AAC);
+    }
+
+    public void testPlay00() throws Exception {
+        doTestVideoPlaybackLong(0);
+    }
+
+    @Override
+    protected String getFullVideoClipName(int mediaNumber) {
+        return VIDEO_TOP_DIR + VIDEO_PATH_MIDDLE + mMedias[mediaNumber];
+    }
+
+}
diff --git a/tests/tests/mediastress/src/android/mediastress/cts/HEVCR480pAacShortPlayerTest.java b/tests/tests/mediastress/src/android/mediastress/cts/HEVCR480pAacShortPlayerTest.java
new file mode 100644
index 0000000..2b64abd
--- /dev/null
+++ b/tests/tests/mediastress/src/android/mediastress/cts/HEVCR480pAacShortPlayerTest.java
@@ -0,0 +1,67 @@
+/*
+ * 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.mediastress.cts;
+
+import android.media.CamcorderProfile;
+import android.media.MediaRecorder.AudioEncoder;
+import android.media.MediaRecorder.VideoEncoder;
+
+public class HEVCR480pAacShortPlayerTest extends MediaPlayerStressTest {
+    private static final String VIDEO_PATH_MIDDLE = "bbb_short/720x480/mp4_libx265_libfaac/";
+    private final String[] mMedias = {
+        "bbb_short.fmpeg.720x480.mp4.libx265_650kbps_24fps.libfaac_stereo_128kbps_48000hz.mp4",
+        "bbb_short.fmpeg.720x480.mp4.libx265_650kbps_30fps.libfaac_stereo_128kbps_48000hz.mp4",
+        "bbb_short.fmpeg.720x480.mp4.libx265_880kbps_24fps.libfaac_stereo_128kbps_48000hz.mp4",
+        "bbb_short.fmpeg.720x480.mp4.libx265_880kbps_30fps.libfaac_stereo_128kbps_48000hz.mp4",
+        "bbb_short.fmpeg.720x480.mp4.libx265_325kbps_24fps.libfaac_stereo_128kbps_48000hz.mp4",
+        "bbb_short.fmpeg.720x480.mp4.libx265_325kbps_30fps.libfaac_stereo_128kbps_48000hz.mp4"
+    };
+
+    public HEVCR480pAacShortPlayerTest() {
+        super(CamcorderProfile.QUALITY_480P, VideoEncoder.H264, AudioEncoder.AAC);
+    }
+
+    public void testPlay00() throws Exception {
+        doTestVideoPlaybackShort(0);
+    }
+
+    public void testPlay01() throws Exception {
+        doTestVideoPlaybackShort(1);
+    }
+
+    public void testPlay02() throws Exception {
+        doTestVideoPlaybackShort(2);
+    }
+
+    public void testPlay03() throws Exception {
+        doTestVideoPlaybackShort(3);
+    }
+
+    public void testPlay04() throws Exception {
+        doTestVideoPlaybackShort(4);
+    }
+
+    public void testPlay05() throws Exception {
+        doTestVideoPlaybackShort(5);
+    }
+
+    @Override
+    protected String getFullVideoClipName(int mediaNumber) {
+        return VIDEO_TOP_DIR + VIDEO_PATH_MIDDLE + mMedias[mediaNumber];
+    }
+
+}
diff --git a/tests/tests/mediastress/src/android/mediastress/cts/HEVCR480x360AacShortPlayerTest.java b/tests/tests/mediastress/src/android/mediastress/cts/HEVCR480x360AacShortPlayerTest.java
new file mode 100644
index 0000000..78139ce
--- /dev/null
+++ b/tests/tests/mediastress/src/android/mediastress/cts/HEVCR480x360AacShortPlayerTest.java
@@ -0,0 +1,59 @@
+/*
+ * 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.mediastress.cts;
+
+public class HEVCR480x360AacShortPlayerTest extends MediaPlayerStressTest {
+    private static final String VIDEO_PATH_MIDDLE = "bbb_short/480x360/mp4_libx265_libfaac/";
+    private final String[] mMedias = {
+        "bbb_short.fmpeg.480x360.mp4.libx265_650kbps_24fps.libfaac_stereo_128kbps_48000hz.mp4",
+        "bbb_short.fmpeg.480x360.mp4.libx265_650kbps_30fps.libfaac_stereo_128kbps_48000hz.mp4",
+        "bbb_short.fmpeg.480x360.mp4.libx265_880kbps_24fps.libfaac_stereo_128kbps_48000hz.mp4",
+        "bbb_short.fmpeg.480x360.mp4.libx265_880kbps_30fps.libfaac_stereo_128kbps_48000hz.mp4",
+        "bbb_short.fmpeg.480x360.mp4.libx265_325kbps_24fps.libfaac_stereo_128kbps_48000hz.mp4",
+        "bbb_short.fmpeg.480x360.mp4.libx265_325kbps_30fps.libfaac_stereo_128kbps_48000hz.mp4"
+    };
+
+    public void testPlay00() throws Exception {
+        doTestVideoPlaybackShort(0);
+    }
+
+    public void testPlay01() throws Exception {
+        doTestVideoPlaybackShort(1);
+    }
+
+    public void testPlay02() throws Exception {
+        doTestVideoPlaybackShort(2);
+    }
+
+    public void testPlay03() throws Exception {
+        doTestVideoPlaybackShort(3);
+    }
+
+    public void testPlay04() throws Exception {
+        doTestVideoPlaybackShort(4);
+    }
+
+    public void testPlay05() throws Exception {
+        doTestVideoPlaybackShort(5);
+    }
+
+    @Override
+    protected String getFullVideoClipName(int mediaNumber) {
+        return VIDEO_TOP_DIR + VIDEO_PATH_MIDDLE + mMedias[mediaNumber];
+    }
+
+}
diff --git a/tests/tests/mediastress/src/android/mediastress/cts/HEVCR720pAacLongPlayerTest.java b/tests/tests/mediastress/src/android/mediastress/cts/HEVCR720pAacLongPlayerTest.java
new file mode 100644
index 0000000..540f78a
--- /dev/null
+++ b/tests/tests/mediastress/src/android/mediastress/cts/HEVCR720pAacLongPlayerTest.java
@@ -0,0 +1,47 @@
+/*
+ * 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.mediastress.cts;
+
+import android.media.CamcorderProfile;
+import android.media.MediaRecorder.AudioEncoder;
+import android.media.MediaRecorder.VideoEncoder;
+
+public class HEVCR720pAacLongPlayerTest extends MediaPlayerStressTest {
+    private static final String VIDEO_PATH_MIDDLE = "bbb_full/1280x720/mp4_libx265_libfaac/";
+    private final String[] mMedias = {
+        "bbb_full.ffmpeg.1280x720.mp4.libx265_880kbps_24fps.libfaac_stereo_128kbps_48000Hz.mp4",
+        "bbb_full.ffmpeg.1280x720.mp4.libx265_1140kbps_30fps.libfaac_stereo_128kbps_48000Hz.mp4"
+    };
+
+    public HEVCR720pAacLongPlayerTest() {
+        super(CamcorderProfile.QUALITY_720P, VideoEncoder.H264, AudioEncoder.AAC);
+    }
+
+    public void testPlay00() throws Exception {
+        doTestVideoPlaybackLong(0);
+    }
+
+    public void testPlay01() throws Exception {
+        doTestVideoPlaybackLong(1);
+    }
+
+    @Override
+    protected String getFullVideoClipName(int mediaNumber) {
+        return VIDEO_TOP_DIR + VIDEO_PATH_MIDDLE + mMedias[mediaNumber];
+    }
+
+}
diff --git a/tests/tests/mediastress/src/android/mediastress/cts/HEVCR720pAacShortPlayerTest.java b/tests/tests/mediastress/src/android/mediastress/cts/HEVCR720pAacShortPlayerTest.java
new file mode 100644
index 0000000..dd93dfc
--- /dev/null
+++ b/tests/tests/mediastress/src/android/mediastress/cts/HEVCR720pAacShortPlayerTest.java
@@ -0,0 +1,82 @@
+/*
+ * 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.mediastress.cts;
+
+import android.media.CamcorderProfile;
+import android.media.MediaRecorder.AudioEncoder;
+import android.media.MediaRecorder.VideoEncoder;
+
+public class HEVCR720pAacShortPlayerTest extends MediaPlayerStressTest {
+    private static final String VIDEO_PATH_MIDDLE = "bbb_short/1280x720/mp4_libx265_libfaac/";
+    private final String[] mMedias = {
+        "bbb_short.fmpeg.1280x720.mp4.libx265_325kbps_24fps.libfaac_stereo_128kbps_48000hz.mp4",
+        "bbb_short.fmpeg.1280x720.mp4.libx265_325kbps_30fps.libfaac_stereo_128kbps_48000hz.mp4",
+        "bbb_short.fmpeg.1280x720.mp4.libx265_650kbps_24fps.libfaac_stereo_128kbps_48000hz.mp4",
+        "bbb_short.fmpeg.1280x720.mp4.libx265_650kbps_30fps.libfaac_stereo_128kbps_48000hz.mp4",
+        "bbb_short.fmpeg.1280x720.mp4.libx265_880kbps_24fps.libfaac_stereo_128kbps_48000hz.mp4",
+        "bbb_short.fmpeg.1280x720.mp4.libx265_880kbps_30fps.libfaac_stereo_128kbps_48000hz.mp4",
+        "bbb_short.fmpeg.1280x720.mp4.libx265_1140kbps_30fps.libfaac_stereo_128kbps_48000hz.mp4",
+        "bbb_short.fmpeg.1280x720.mp4.libx265_3250kbps_30fps.libfaac_stereo_128kbps_48000hz.mp4",
+        "bbb_short.fmpeg.1280x720.mp4.libx265_6500kbps_30fps.libfaac_stereo_128kbps_48000hz.mp4"
+    };
+
+    public HEVCR720pAacShortPlayerTest() {
+        super(CamcorderProfile.QUALITY_720P, VideoEncoder.H264, AudioEncoder.AAC);
+    }
+
+    public void testPlay00() throws Exception {
+        doTestVideoPlaybackShort(0);
+    }
+
+    public void testPlay01() throws Exception {
+        doTestVideoPlaybackShort(1);
+    }
+
+    public void testPlay02() throws Exception {
+        doTestVideoPlaybackShort(2);
+    }
+
+    public void testPlay03() throws Exception {
+        doTestVideoPlaybackShort(3);
+    }
+
+    public void testPlay04() throws Exception {
+        doTestVideoPlaybackShort(4);
+    }
+
+    public void testPlay05() throws Exception {
+        doTestVideoPlaybackShort(5);
+    }
+
+    public void testPlay06() throws Exception {
+        doTestVideoPlaybackShort(6);
+    }
+
+    public void testPlay07() throws Exception {
+        doTestVideoPlaybackShort(7);
+    }
+
+    public void testPlay08() throws Exception {
+        doTestVideoPlaybackShort(8);
+    }
+
+    @Override
+    protected String getFullVideoClipName(int mediaNumber) {
+        return VIDEO_TOP_DIR + VIDEO_PATH_MIDDLE + mMedias[mediaNumber];
+    }
+
+}
diff --git a/tests/tests/nativeopengl/Android.mk b/tests/tests/nativeopengl/Android.mk
index dd19548..672eb5c 100644
--- a/tests/tests/nativeopengl/Android.mk
+++ b/tests/tests/nativeopengl/Android.mk
@@ -24,9 +24,6 @@
 # When built, explicitly put it in the data partition.
 LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
 
-# All tests should include android.test.runner.
-LOCAL_JAVA_LIBRARIES := android.test.runner
-
 LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner ctswrappedgtest
 
 LOCAL_JNI_SHARED_LIBRARIES := libnativeopengltests
diff --git a/tests/tests/ndef/Android.mk b/tests/tests/ndef/Android.mk
index 70853d9..ba78f29 100644
--- a/tests/tests/ndef/Android.mk
+++ b/tests/tests/ndef/Android.mk
@@ -24,9 +24,6 @@
 # When built, explicitly put it in the data partition.
 LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
 
-# All tests should include android.test.runner.
-LOCAL_JAVA_LIBRARIES := android.test.runner
-
 LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
diff --git a/tests/tests/ndef/AndroidManifest.xml b/tests/tests/ndef/AndroidManifest.xml
index a7ebb6e..e0244e1 100644
--- a/tests/tests/ndef/AndroidManifest.xml
+++ b/tests/tests/ndef/AndroidManifest.xml
@@ -23,9 +23,12 @@
     </application>
 
     <!-- This is a self-instrumenting test package. -->
-    <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
                      android:targetPackage="com.android.cts.ndef"
-                     android:label="CTS tests of NDEF data classes"/>
+                     android:label="CTS tests of NDEF data classes">
+        <meta-data android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
 
 </manifest>
 
diff --git a/tests/tests/net/Android.mk b/tests/tests/net/Android.mk
index 82abd62..da19a4d 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 := voip-common conscrypt
 
 LOCAL_JNI_SHARED_LIBRARIES := libnativedns_jni
 
@@ -33,7 +33,7 @@
 LOCAL_STATIC_JAVA_LIBRARIES := ctstestserver ctsdeviceutil ctstestrunner \
                                core-tests-support
 
-# uncomment when dalvik.annotation.Test* are removed or part of SDK
+# uncomment when b/13249961 is fixed
 #LOCAL_SDK_VERSION := current
 
 include $(BUILD_CTS_PACKAGE)
diff --git a/tests/tests/net/AndroidManifest.xml b/tests/tests/net/AndroidManifest.xml
index ade6728..652262d 100644
--- a/tests/tests/net/AndroidManifest.xml
+++ b/tests/tests/net/AndroidManifest.xml
@@ -32,9 +32,12 @@
         <uses-library android:name="android.test.runner" />
     </application>
 
-    <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
                      android:targetPackage="com.android.cts.net"
-                     android:label="CTS tests of android.net"/>
+                     android:label="CTS tests of android.net">
+        <meta-data android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
 
 </manifest>
 
diff --git a/tests/tests/net/src/android/net/cts/MultinetworkTest.java b/tests/tests/net/src/android/net/cts/MultinetworkTest.java
new file mode 100644
index 0000000..256c030
--- /dev/null
+++ b/tests/tests/net/src/android/net/cts/MultinetworkTest.java
@@ -0,0 +1,99 @@
+/*
+ * 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.net.cts;
+
+import android.system.ErrnoException;
+import android.system.Os;
+import android.system.OsConstants;
+import android.system.StructStat;
+import android.test.AndroidTestCase;
+
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.IOException;
+
+/**
+ * Tests for multinetwork functionality.
+ */
+public class MultinetworkTest extends AndroidTestCase {
+
+    // Global sysctls. Must be present and set to 1.
+    private static final String[] GLOBAL_SYSCTLS = {
+        "/proc/sys/net/ipv4/fwmark_reflect",
+        "/proc/sys/net/ipv6/fwmark_reflect",
+        "/proc/sys/net/ipv4/tcp_fwmark_accept",
+    };
+
+    // Per-interface IPv6 autoconf sysctls.
+    private static final String IPV6_SYSCTL_DIR = "/proc/sys/net/ipv6/conf";
+    private static final String AUTOCONF_SYSCTL = "accept_ra_rt_table";
+
+    // Expected mode, UID, and GID of sysctl files.
+    private static final int SYSCTL_MODE = 0100644;
+    private static final int SYSCTL_UID = 0;
+    private static final int SYSCTL_GID = 0;
+
+    private void checkSysctlPermissions(String fileName) throws ErrnoException {
+        StructStat stat = Os.stat(fileName);
+        assertEquals("mode of " + fileName + ":", SYSCTL_MODE, stat.st_mode);
+        assertEquals("UID of " + fileName + ":", SYSCTL_UID, stat.st_uid);
+        assertEquals("GID of " + fileName + ":", SYSCTL_GID, stat.st_gid);
+    }
+
+    private void assertLess(String what, int a, int b) {
+        assertTrue(what + " expected < " + b + " but was: " + a, a < b);
+    }
+
+    private String readFile(String fileName) throws ErrnoException, IOException {
+        byte[] buf = new byte[1024];
+        FileDescriptor fd = Os.open(fileName, 0, OsConstants.O_RDONLY);
+        int bytesRead = Os.read(fd, buf, 0, buf.length);
+        assertLess("length of " + fileName + ":", bytesRead, buf.length);
+        return new String(buf);
+    }
+
+    /**
+     * Checks that the sysctls for multinetwork kernel features are present and
+     * enabled. The necessary kernel commits are:
+     *
+     * Mainline Linux:
+     *   e110861 net: add a sysctl to reflect the fwmark on replies
+     *   1b3c61d net: Use fwmark reflection in PMTU discovery.
+     *   84f39b0 net: support marking accepting TCP sockets
+     *
+     * Common Android tree (e.g., 3.10):
+     *   a03f539 net: ipv6: autoconf routes into per-device tables
+     */
+     public void testProcFiles() throws ErrnoException, IOException, NumberFormatException {
+         for (String sysctl : GLOBAL_SYSCTLS) {
+             checkSysctlPermissions(sysctl);
+             int value = Integer.parseInt(readFile(sysctl).trim());
+             assertEquals("value of " + sysctl + ":", 1, value);
+         }
+
+         File[] interfaceDirs = new File(IPV6_SYSCTL_DIR).listFiles();
+         for (File interfaceDir : interfaceDirs) {
+             if (interfaceDir.getName().equals("all") || interfaceDir.getName().equals("lo")) {
+                 continue;
+             }
+             String sysctl = new File(interfaceDir, AUTOCONF_SYSCTL).getAbsolutePath();
+             checkSysctlPermissions(sysctl);
+             int value = Integer.parseInt(readFile(sysctl).trim());
+             assertLess("value of " + sysctl + ":", value, 0);
+         }
+     }
+}
diff --git a/tests/tests/net/src/android/net/cts/SSLCertificateSocketFactoryTest.java b/tests/tests/net/src/android/net/cts/SSLCertificateSocketFactoryTest.java
index cb8aeaf..6175923 100644
--- a/tests/tests/net/src/android/net/cts/SSLCertificateSocketFactoryTest.java
+++ b/tests/tests/net/src/android/net/cts/SSLCertificateSocketFactoryTest.java
@@ -26,8 +26,6 @@
 import android.net.SSLCertificateSocketFactory;
 import android.test.AndroidTestCase;
 
-import dalvik.annotation.BrokenTest;
-
 import libcore.javax.net.ssl.SSLDefaultConfigurationAsserts;
 
 public class SSLCertificateSocketFactoryTest extends AndroidTestCase {
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/opengl/Android.mk b/tests/tests/opengl/Android.mk
index 98f11e9..a14ee7a 100644
--- a/tests/tests/opengl/Android.mk
+++ b/tests/tests/opengl/Android.mk
@@ -24,9 +24,6 @@
 # When built, explicitly put it in the data partition.
 LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
 
-# All tests should include android.test.runner.
-LOCAL_JAVA_LIBRARIES := android.test.runner
-
 LOCAL_JNI_SHARED_LIBRARIES := libopengltest_jni
 
 LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner
diff --git a/tests/tests/opengl/AndroidManifest.xml b/tests/tests/opengl/AndroidManifest.xml
index 266216f..914b2d2 100644
--- a/tests/tests/opengl/AndroidManifest.xml
+++ b/tests/tests/opengl/AndroidManifest.xml
@@ -22,8 +22,11 @@
     <uses-sdk android:minSdkVersion="14" />
     <uses-feature android:glEsVersion="0x00020000"/>
     <instrumentation
-        android:name="android.test.InstrumentationCtsTestRunner"
-        android:targetPackage="com.android.cts.opengl" />
+        android:name="android.support.test.runner.AndroidJUnitRunner"
+        android:targetPackage="com.android.cts.opengl" >
+        <meta-data android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
     <application
         android:icon="@drawable/ic_launcher"
         android:label="@string/app_name" >
@@ -35,7 +38,7 @@
           <activity
             android:label="@string/app_name"
             android:name="android.opengl.cts.OpenGLES20ActivityTwo">
-         </activity> 
+         </activity>
          <uses-library  android:name="android.test.runner" />
          <activity
             android:name="android.opengl.cts.OpenGLES20NativeActivityOne"
diff --git a/tests/tests/opengl/src/android/opengl/cts/FramebufferTest.java b/tests/tests/opengl/src/android/opengl/cts/FramebufferTest.java
new file mode 100644
index 0000000..4ca3a99
--- /dev/null
+++ b/tests/tests/opengl/src/android/opengl/cts/FramebufferTest.java
@@ -0,0 +1,653 @@
+/*
+ * 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.opengl.cts;
+
+import android.graphics.Bitmap;
+import android.graphics.SurfaceTexture;
+import android.opengl.EGL14;
+import android.opengl.EGLConfig;
+import android.opengl.EGLContext;
+import android.opengl.EGLDisplay;
+import android.opengl.EGLExt;
+import android.opengl.EGLSurface;
+import android.opengl.GLES20;
+import android.opengl.GLES30;
+import android.test.AndroidTestCase;
+import android.util.Log;
+import android.view.Surface;
+
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.util.Arrays;
+
+/**
+ * Test some GLES framebuffer stuff.
+ */
+public class FramebufferTest extends AndroidTestCase {
+    private static final String TAG = "FramebufferTest";
+
+
+    /**
+     * Tests very basic glBlitFramebuffer() features by copying from one offscreen framebuffer
+     * to another.
+     * <p>
+     * Requires GLES3.
+     */
+    public void testBlitFramebuffer() throws Throwable {
+        final int WIDTH = 640;
+        final int HEIGHT = 480;
+        final int BYTES_PER_PIXEL = 4;
+        final int TEST_RED = 255;
+        final int TEST_GREEN = 0;
+        final int TEST_BLUE = 127;
+        final int TEST_ALPHA = 255;
+        final byte expectedBytes[] = new byte[] {
+                (byte) TEST_RED, (byte) TEST_GREEN, (byte) TEST_BLUE, (byte) TEST_ALPHA
+        };
+        EglCore eglCore = null;
+        OffscreenSurface surface1 = null;
+        OffscreenSurface surface2 = null;
+
+        try {
+            eglCore = new EglCore(null, EglCore.FLAG_TRY_GLES3);
+            if (eglCore.getGlVersion() < 3) {
+                Log.d(TAG, "GLES3 not available, skipping test");
+                return;
+            }
+
+            // Create two surfaces, and clear surface1
+            surface1 = new OffscreenSurface(eglCore, WIDTH, HEIGHT);
+            surface2 = new OffscreenSurface(eglCore, WIDTH, HEIGHT);
+            surface1.makeCurrent();
+            GLES30.glClearColor(TEST_RED / 255.0f, TEST_GREEN / 255.0f, TEST_BLUE / 255.0f,
+                    TEST_ALPHA / 255.0f);
+            GLES30.glClear(GLES30.GL_COLOR_BUFFER_BIT);
+            checkGlError("glClear");
+
+            // Set surface2 as "draw", surface1 as "read", and blit.
+            surface2.makeCurrentReadFrom(surface1);
+            GLES30.glBlitFramebuffer(0, 0, WIDTH, HEIGHT, 0, 0, WIDTH, HEIGHT,
+                    GLES30.GL_COLOR_BUFFER_BIT, GLES30.GL_NEAREST);
+            checkGlError("glBlitFramebuffer");
+
+            ByteBuffer pixelBuf = ByteBuffer.allocateDirect(WIDTH * HEIGHT * BYTES_PER_PIXEL);
+            pixelBuf.order(ByteOrder.LITTLE_ENDIAN);
+            byte testBytes[] = new byte[4];
+
+            // Confirm that surface1 has the color by testing a pixel from the center.
+            surface1.makeCurrent();
+            pixelBuf.clear();
+            GLES30.glReadPixels(0, 0, WIDTH, HEIGHT, GLES30.GL_RGBA, GLES30.GL_UNSIGNED_BYTE,
+                    pixelBuf);
+            checkGlError("glReadPixels");
+            pixelBuf.position((WIDTH * (HEIGHT / 2) + (WIDTH / 2)) * BYTES_PER_PIXEL);
+            pixelBuf.get(testBytes, 0, 4);
+            Log.v(TAG, "testBytes1 = " + Arrays.toString(testBytes));
+            assertTrue(Arrays.equals(testBytes, expectedBytes));
+
+            // Confirm that surface2 has the color.
+            surface2.makeCurrent();
+            pixelBuf.clear();
+            GLES30.glReadPixels(0, 0, WIDTH, HEIGHT, GLES30.GL_RGBA, GLES30.GL_UNSIGNED_BYTE,
+                    pixelBuf);
+            checkGlError("glReadPixels");
+            pixelBuf.position((WIDTH * (HEIGHT / 2) + (WIDTH / 2)) * BYTES_PER_PIXEL);
+            pixelBuf.get(testBytes, 0, 4);
+            Log.v(TAG, "testBytes2 = " + Arrays.toString(testBytes));
+            assertTrue(Arrays.equals(testBytes, expectedBytes));
+        } finally {
+            if (surface1 != null) {
+                surface1.release();
+            }
+            if (surface2 != null) {
+                surface2.release();
+            }
+            if (eglCore != null) {
+                eglCore.release();
+            }
+        }
+    }
+
+    /**
+     * Checks to see if a GLES error has been raised.
+     */
+    private static void checkGlError(String op) {
+        int error = GLES20.glGetError();
+        if (error != GLES20.GL_NO_ERROR) {
+            String msg = op + ": glError 0x" + Integer.toHexString(error);
+            Log.e(TAG, msg);
+            throw new RuntimeException(msg);
+        }
+    }
+
+
+    /**
+     * Core EGL state (display, context, config).
+     */
+    private static final class EglCore {
+        /**
+         * Constructor flag: surface must be recordable.  This discourages EGL from using a
+         * pixel format that cannot be converted efficiently to something usable by the video
+         * encoder.
+         */
+        public static final int FLAG_RECORDABLE = 0x01;
+
+        /**
+         * Constructor flag: ask for GLES3, fall back to GLES2 if not available.  Without this
+         * flag, GLES2 is used.
+         */
+        public static final int FLAG_TRY_GLES3 = 0x02;
+
+        // Android-specific extension.
+        private static final int EGL_RECORDABLE_ANDROID = 0x3142;
+
+        private EGLDisplay mEGLDisplay = EGL14.EGL_NO_DISPLAY;
+        private EGLContext mEGLContext = EGL14.EGL_NO_CONTEXT;
+        private EGLConfig mEGLConfig = null;
+        private int mGlVersion = -1;
+
+
+        /**
+         * Prepares EGL display and context.
+         * <p>
+         * Equivalent to EglCore(null, 0).
+         */
+        public EglCore() {
+            this(null, 0);
+        }
+
+        /**
+         * Prepares EGL display and context.
+         * <p>
+         * @param sharedContext The context to share, or null if sharing is not desired.
+         * @param flags Configuration bit flags, e.g. FLAG_RECORDABLE.
+         */
+        public EglCore(EGLContext sharedContext, int flags) {
+            if (mEGLDisplay != EGL14.EGL_NO_DISPLAY) {
+                throw new RuntimeException("EGL already set up");
+            }
+
+            if (sharedContext == null) {
+                sharedContext = EGL14.EGL_NO_CONTEXT;
+            }
+
+            mEGLDisplay = EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY);
+            if (mEGLDisplay == EGL14.EGL_NO_DISPLAY) {
+                throw new RuntimeException("unable to get EGL14 display");
+            }
+            int[] version = new int[2];
+            if (!EGL14.eglInitialize(mEGLDisplay, version, 0, version, 1)) {
+                mEGLDisplay = null;
+                throw new RuntimeException("unable to initialize EGL14");
+            }
+
+            // Try to get a GLES3 context, if requested.
+            if ((flags & FLAG_TRY_GLES3) != 0) {
+                //Log.d(TAG, "Trying GLES 3");
+                EGLConfig config = getConfig(flags, 3);
+                if (config != null) {
+                    int[] attrib3_list = {
+                            EGL14.EGL_CONTEXT_CLIENT_VERSION, 3,
+                            EGL14.EGL_NONE
+                    };
+                    EGLContext context = EGL14.eglCreateContext(mEGLDisplay, config, sharedContext,
+                            attrib3_list, 0);
+
+                    if (EGL14.eglGetError() == EGL14.EGL_SUCCESS) {
+                        //Log.d(TAG, "Got GLES 3 config");
+                        mEGLConfig = config;
+                        mEGLContext = context;
+                        mGlVersion = 3;
+                    }
+                }
+            }
+            if (mEGLContext == EGL14.EGL_NO_CONTEXT) {  // GLES 2 only, or GLES 3 attempt failed
+                //Log.d(TAG, "Trying GLES 2");
+                EGLConfig config = getConfig(flags, 2);
+                if (config == null) {
+                    throw new RuntimeException("Unable to find a suitable EGLConfig");
+                }
+                int[] attrib2_list = {
+                        EGL14.EGL_CONTEXT_CLIENT_VERSION, 2,
+                        EGL14.EGL_NONE
+                };
+                EGLContext context = EGL14.eglCreateContext(mEGLDisplay, config, sharedContext,
+                        attrib2_list, 0);
+                checkEglError("eglCreateContext");
+                mEGLConfig = config;
+                mEGLContext = context;
+                mGlVersion = 2;
+            }
+
+            // Confirm with query.
+            int[] values = new int[1];
+            EGL14.eglQueryContext(mEGLDisplay, mEGLContext, EGL14.EGL_CONTEXT_CLIENT_VERSION,
+                    values, 0);
+            Log.d(TAG, "EGLContext created, client version " + values[0]);
+        }
+
+        /**
+         * Finds a suitable EGLConfig.
+         *
+         * @param flags Bit flags from constructor.
+         * @param version Must be 2 or 3.
+         */
+        private EGLConfig getConfig(int flags, int version) {
+            int renderableType = EGL14.EGL_OPENGL_ES2_BIT;
+            if (version >= 3) {
+                renderableType |= EGLExt.EGL_OPENGL_ES3_BIT_KHR;
+            }
+
+            // The actual surface is generally RGBA or RGBX, so situationally omitting alpha
+            // doesn't really help.  It can also lead to a huge performance hit on glReadPixels()
+            // when reading into a GL_RGBA buffer.
+            int[] attribList = {
+                    EGL14.EGL_RED_SIZE, 8,
+                    EGL14.EGL_GREEN_SIZE, 8,
+                    EGL14.EGL_BLUE_SIZE, 8,
+                    EGL14.EGL_ALPHA_SIZE, 8,
+                    //EGL14.EGL_DEPTH_SIZE, 16,
+                    //EGL14.EGL_STENCIL_SIZE, 8,
+                    EGL14.EGL_RENDERABLE_TYPE, renderableType,
+                    EGL14.EGL_NONE, 0,      // placeholder for recordable [@-3]
+                    EGL14.EGL_NONE
+            };
+            if ((flags & FLAG_RECORDABLE) != 0) {
+                attribList[attribList.length - 3] = EGL_RECORDABLE_ANDROID;
+                attribList[attribList.length - 2] = 1;
+            }
+            EGLConfig[] configs = new EGLConfig[1];
+            int[] numConfigs = new int[1];
+            if (!EGL14.eglChooseConfig(mEGLDisplay, attribList, 0, configs, 0, configs.length,
+                    numConfigs, 0)) {
+                Log.w(TAG, "unable to find RGB8888 / " + version + " EGLConfig");
+                return null;
+            }
+            return configs[0];
+        }
+
+        /**
+         * Discard all resources held by this class, notably the EGL context.
+         */
+        public void release() {
+            if (mEGLDisplay != EGL14.EGL_NO_DISPLAY) {
+                // Android is unusual in that it uses a reference-counted EGLDisplay.  So for
+                // every eglInitialize() we need an eglTerminate().
+                EGL14.eglDestroyContext(mEGLDisplay, mEGLContext);
+                EGL14.eglReleaseThread();
+                EGL14.eglTerminate(mEGLDisplay);
+            }
+
+            mEGLDisplay = EGL14.EGL_NO_DISPLAY;
+            mEGLContext = EGL14.EGL_NO_CONTEXT;
+            mEGLConfig = null;
+        }
+
+        @Override
+        protected void finalize() throws Throwable {
+            try {
+                if (mEGLDisplay != EGL14.EGL_NO_DISPLAY) {
+                    // We're limited here -- finalizers don't run on the thread that holds
+                    // the EGL state, so if a surface or context is still current on another
+                    // thread we can't fully release it here.  Exceptions thrown from here
+                    // are quietly discarded.  Complain in the log file.
+                    Log.w(TAG, "WARNING: EglCore was not explicitly released -- state may be leaked");
+                    release();
+                }
+            } finally {
+                super.finalize();
+            }
+        }
+
+        /**
+         * Destroys the specified surface.  Note the EGLSurface won't actually be destroyed if it's
+         * still current in a context.
+         */
+        public void releaseSurface(EGLSurface eglSurface) {
+            EGL14.eglDestroySurface(mEGLDisplay, eglSurface);
+        }
+
+        /**
+         * Creates an EGL surface associated with a Surface.
+         * <p>
+         * If this is destined for MediaCodec, the EGLConfig should have the "recordable" attribute.
+         */
+        public EGLSurface createWindowSurface(Object surface) {
+            if (!(surface instanceof Surface) && !(surface instanceof SurfaceTexture)) {
+                throw new RuntimeException("invalid surface: " + surface);
+            }
+
+            // Create a window surface, and attach it to the Surface we received.
+            int[] surfaceAttribs = {
+                    EGL14.EGL_NONE
+            };
+            EGLSurface eglSurface = EGL14.eglCreateWindowSurface(mEGLDisplay, mEGLConfig, surface,
+                    surfaceAttribs, 0);
+            checkEglError("eglCreateWindowSurface");
+            if (eglSurface == null) {
+                throw new RuntimeException("surface was null");
+            }
+            return eglSurface;
+        }
+
+        /**
+         * Creates an EGL surface associated with an offscreen buffer.
+         */
+        public EGLSurface createOffscreenSurface(int width, int height) {
+            int[] surfaceAttribs = {
+                    EGL14.EGL_WIDTH, width,
+                    EGL14.EGL_HEIGHT, height,
+                    EGL14.EGL_NONE
+            };
+            EGLSurface eglSurface = EGL14.eglCreatePbufferSurface(mEGLDisplay, mEGLConfig,
+                    surfaceAttribs, 0);
+            checkEglError("eglCreatePbufferSurface");
+            if (eglSurface == null) {
+                throw new RuntimeException("surface was null");
+            }
+            return eglSurface;
+        }
+
+        /**
+         * Makes our EGL context current, using the supplied surface for both "draw" and "read".
+         */
+        public void makeCurrent(EGLSurface eglSurface) {
+            if (mEGLDisplay == EGL14.EGL_NO_DISPLAY) {
+                // called makeCurrent() before create?
+                Log.d(TAG, "NOTE: makeCurrent w/o display");
+            }
+            if (!EGL14.eglMakeCurrent(mEGLDisplay, eglSurface, eglSurface, mEGLContext)) {
+                throw new RuntimeException("eglMakeCurrent failed");
+            }
+        }
+
+        /**
+         * Makes our EGL context current, using the supplied "draw" and "read" surfaces.
+         */
+        public void makeCurrent(EGLSurface drawSurface, EGLSurface readSurface) {
+            if (mEGLDisplay == EGL14.EGL_NO_DISPLAY) {
+                // called makeCurrent() before create?
+                Log.d(TAG, "NOTE: makeCurrent w/o display");
+            }
+            if (!EGL14.eglMakeCurrent(mEGLDisplay, drawSurface, readSurface, mEGLContext)) {
+                throw new RuntimeException("eglMakeCurrent(draw,read) failed");
+            }
+        }
+
+        /**
+         * Makes no context current.
+         */
+        public void makeNothingCurrent() {
+            if (!EGL14.eglMakeCurrent(mEGLDisplay, EGL14.EGL_NO_SURFACE, EGL14.EGL_NO_SURFACE,
+                    EGL14.EGL_NO_CONTEXT)) {
+                throw new RuntimeException("eglMakeCurrent failed");
+            }
+        }
+
+        /**
+         * Calls eglSwapBuffers.  Use this to "publish" the current frame.
+         *
+         * @return false on failure
+         */
+        public boolean swapBuffers(EGLSurface eglSurface) {
+            return EGL14.eglSwapBuffers(mEGLDisplay, eglSurface);
+        }
+
+        /**
+         * Sends the presentation time stamp to EGL.  Time is expressed in nanoseconds.
+         */
+        public void setPresentationTime(EGLSurface eglSurface, long nsecs) {
+            EGLExt.eglPresentationTimeANDROID(mEGLDisplay, eglSurface, nsecs);
+        }
+
+        /**
+         * Returns true if our context and the specified surface are current.
+         */
+        public boolean isCurrent(EGLSurface eglSurface) {
+            return mEGLContext.equals(EGL14.eglGetCurrentContext()) &&
+                    eglSurface.equals(EGL14.eglGetCurrentSurface(EGL14.EGL_DRAW));
+        }
+
+        /**
+         * Performs a simple surface query.
+         */
+        public int querySurface(EGLSurface eglSurface, int what) {
+            int[] value = new int[1];
+            EGL14.eglQuerySurface(mEGLDisplay, eglSurface, what, value, 0);
+            return value[0];
+        }
+
+        /**
+         * Returns the GLES version this context is configured for (2 or 3).
+         */
+        public int getGlVersion() {
+            return mGlVersion;
+        }
+
+        /**
+         * Writes the current display, context, and surface to the log.
+         */
+        public static void logCurrent(String msg) {
+            EGLDisplay display;
+            EGLContext context;
+            EGLSurface surface;
+
+            display = EGL14.eglGetCurrentDisplay();
+            context = EGL14.eglGetCurrentContext();
+            surface = EGL14.eglGetCurrentSurface(EGL14.EGL_DRAW);
+            Log.i(TAG, "Current EGL (" + msg + "): display=" + display + ", context=" + context +
+                    ", surface=" + surface);
+        }
+
+        /**
+         * Checks for EGL errors.
+         */
+        private void checkEglError(String msg) {
+            int error;
+            if ((error = EGL14.eglGetError()) != EGL14.EGL_SUCCESS) {
+                throw new RuntimeException(msg + ": EGL error: 0x" + Integer.toHexString(error));
+            }
+        }
+    }
+
+
+    /**
+     * Common base class for EGL surfaces.
+     * <p>
+     * There can be multiple surfaces associated with a single context.
+     */
+    private static class EglSurfaceBase {
+        // EglCore object we're associated with.  It may be associated with multiple surfaces.
+        protected EglCore mEglCore;
+
+        private EGLSurface mEGLSurface = EGL14.EGL_NO_SURFACE;
+        private int mWidth = -1;
+        private int mHeight = -1;
+
+        protected EglSurfaceBase(EglCore eglCore) {
+            mEglCore = eglCore;
+        }
+
+        /**
+         * Creates a window surface.
+         * <p>
+         * @param surface May be a Surface or SurfaceTexture.
+         */
+        public void createWindowSurface(Object surface) {
+            if (mEGLSurface != EGL14.EGL_NO_SURFACE) {
+                throw new IllegalStateException("surface already created");
+            }
+            mEGLSurface = mEglCore.createWindowSurface(surface);
+            mWidth = mEglCore.querySurface(mEGLSurface, EGL14.EGL_WIDTH);
+            mHeight = mEglCore.querySurface(mEGLSurface, EGL14.EGL_HEIGHT);
+        }
+
+        /**
+         * Creates an off-screen surface.
+         */
+        public void createOffscreenSurface(int width, int height) {
+            if (mEGLSurface != EGL14.EGL_NO_SURFACE) {
+                throw new IllegalStateException("surface already created");
+            }
+            mEGLSurface = mEglCore.createOffscreenSurface(width, height);
+            mWidth = width;
+            mHeight = height;
+        }
+
+        /**
+         * Returns the surface's width, in pixels.
+         */
+        public int getWidth() {
+            return mWidth;
+        }
+
+        /**
+         * Returns the surface's height, in pixels.
+         */
+        public int getHeight() {
+            return mHeight;
+        }
+
+        /**
+         * Release the EGL surface.
+         */
+        public void releaseEglSurface() {
+            mEglCore.releaseSurface(mEGLSurface);
+            mEGLSurface = EGL14.EGL_NO_SURFACE;
+            mWidth = mHeight = -1;
+        }
+
+        /**
+         * Makes our EGL context and surface current.
+         */
+        public void makeCurrent() {
+            mEglCore.makeCurrent(mEGLSurface);
+        }
+
+        /**
+         * Makes our EGL context and surface current for drawing, using the supplied surface
+         * for reading.
+         */
+        public void makeCurrentReadFrom(EglSurfaceBase readSurface) {
+            mEglCore.makeCurrent(mEGLSurface, readSurface.mEGLSurface);
+        }
+
+        /**
+         * Calls eglSwapBuffers.  Use this to "publish" the current frame.
+         *
+         * @return false on failure
+         */
+        public boolean swapBuffers() {
+            boolean result = mEglCore.swapBuffers(mEGLSurface);
+            if (!result) {
+                Log.d(TAG, "WARNING: swapBuffers() failed");
+            }
+            return result;
+        }
+
+        /**
+         * Sends the presentation time stamp to EGL.
+         *
+         * @param nsecs Timestamp, in nanoseconds.
+         */
+        public void setPresentationTime(long nsecs) {
+            mEglCore.setPresentationTime(mEGLSurface, nsecs);
+        }
+
+        /**
+         * Saves the EGL surface to a file.
+         * <p>
+         * Expects that this object's EGL surface is current.
+         */
+        public void saveFrame(File file) throws IOException {
+            if (!mEglCore.isCurrent(mEGLSurface)) {
+                throw new RuntimeException("Expected EGL context/surface is not current");
+            }
+
+            // glReadPixels gives us a ByteBuffer filled with what is essentially big-endian RGBA
+            // data (i.e. a byte of red, followed by a byte of green...).  We need an int[] filled
+            // with little-endian ARGB data to feed to Bitmap.
+            //
+            // If we implement this as a series of buf.get() calls, we can spend 2.5 seconds just
+            // copying data around for a 720p frame.  It's better to do a bulk get() and then
+            // rearrange the data in memory.  (For comparison, the PNG compress takes about 500ms
+            // for a trivial frame.)
+            //
+            // So... we set the ByteBuffer to little-endian, which should turn the bulk IntBuffer
+            // get() into a straight memcpy on most Android devices.  Our ints will hold ABGR data.
+            // Swapping B and R gives us ARGB.
+            //
+            // Making this even more interesting is the upside-down nature of GL, which means
+            // our output will look upside-down relative to what appears on screen if the
+            // typical GL conventions are used.
+
+            String filename = file.toString();
+
+            ByteBuffer buf = ByteBuffer.allocateDirect(mWidth * mHeight * 4);
+            buf.order(ByteOrder.LITTLE_ENDIAN);
+            GLES20.glReadPixels(0, 0, mWidth, mHeight,
+                    GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, buf);
+            checkGlError("glReadPixels");
+            buf.rewind();
+
+            int pixelCount = mWidth * mHeight;
+            int[] colors = new int[pixelCount];
+            buf.asIntBuffer().get(colors);
+            for (int i = 0; i < pixelCount; i++) {
+                int c = colors[i];
+                colors[i] = (c & 0xff00ff00) | ((c & 0x00ff0000) >> 16) | ((c & 0x000000ff) << 16);
+            }
+
+            BufferedOutputStream bos = null;
+            try {
+                bos = new BufferedOutputStream(new FileOutputStream(filename));
+                Bitmap bmp = Bitmap.createBitmap(colors, mWidth, mHeight, Bitmap.Config.ARGB_8888);
+                bmp.compress(Bitmap.CompressFormat.PNG, 90, bos);
+                bmp.recycle();
+            } finally {
+                if (bos != null) bos.close();
+            }
+            Log.d(TAG, "Saved " + mWidth + "x" + mHeight + " frame as '" + filename + "'");
+        }
+    }
+
+    /**
+     * Off-screen EGL surface (pbuffer).
+     * <p>
+     * It's good practice to explicitly release() the surface, preferably from a "finally" block.
+     */
+    private static class OffscreenSurface extends EglSurfaceBase {
+        /**
+         * Creates an off-screen surface with the specified width and height.
+         */
+        public OffscreenSurface(EglCore eglCore, int width, int height) {
+            super(eglCore);
+            createOffscreenSurface(width, height);
+        }
+
+        /**
+         * Releases any resources associated with the surface.
+         */
+        public void release() {
+            releaseEglSurface();
+        }
+    }
+}
diff --git a/tests/tests/openglperf/Android.mk b/tests/tests/openglperf/Android.mk
index 55c39f2..1d57263 100644
--- a/tests/tests/openglperf/Android.mk
+++ b/tests/tests/openglperf/Android.mk
@@ -21,8 +21,6 @@
 # and when built explicitly put it in the data partition
 LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
 
-LOCAL_JAVA_LIBRARIES := android.test.runner
-
 LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner ctsdeviceutil
 
 LOCAL_JNI_SHARED_LIBRARIES := libctsopenglperf_jni
diff --git a/tests/tests/openglperf/AndroidManifest.xml b/tests/tests/openglperf/AndroidManifest.xml
index 1934f35..a213e51 100644
--- a/tests/tests/openglperf/AndroidManifest.xml
+++ b/tests/tests/openglperf/AndroidManifest.xml
@@ -27,10 +27,16 @@
     <!-- Two activities are used -->
     <instrumentation
         android:targetPackage="com.replica.replicaisland"
-        android:name="android.test.InstrumentationCtsTestRunner" />
+        android:name="android.support.test.runner.AndroidJUnitRunner" >
+        <meta-data android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
     <instrumentation
         android:targetPackage="com.android.cts.openglperf"
-        android:name="android.test.InstrumentationCtsTestRunner" />
+        android:name="android.support.test.runner.AndroidJUnitRunner">
+        <meta-data android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
 
     <application>
         <uses-library android:name="android.test.runner" />
diff --git a/tests/tests/os/Android.mk b/tests/tests/os/Android.mk
index f43043b..0007a54 100644
--- a/tests/tests/os/Android.mk
+++ b/tests/tests/os/Android.mk
@@ -23,8 +23,6 @@
 
 LOCAL_PROGUARD_ENABLED := disabled
 
-LOCAL_JAVA_LIBRARIES := android.test.runner
-
 LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner guava
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src) \
@@ -34,7 +32,8 @@
 
 LOCAL_INSTRUMENTATION_FOR := CtsTestStubs
 
-# uncomment when dalvik.annotation.Test* are removed or part of SDK
+# uncomment when b/13282254 is fixed
 #LOCAL_SDK_VERSION := current
+LOCAL_JAVA_LIBRARIES += android.test.runner
 
 include $(BUILD_CTS_PACKAGE)
diff --git a/tests/tests/os/AndroidManifest.xml b/tests/tests/os/AndroidManifest.xml
index 2418132..155e772 100644
--- a/tests/tests/os/AndroidManifest.xml
+++ b/tests/tests/os/AndroidManifest.xml
@@ -34,8 +34,11 @@
         <uses-library android:name="android.test.runner" />
     </application>
 
-    <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
                      android:targetPackage="com.android.cts.stub"
-                     android:label="CTS tests of android.os"/>
+                     android:label="CTS tests of android.os">
+        <meta-data android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
 
 </manifest>
diff --git a/tests/tests/os/src/android/os/cts/BuildTest.java b/tests/tests/os/src/android/os/cts/BuildTest.java
index 7176a6d..08db484 100644
--- a/tests/tests/os/src/android/os/cts/BuildTest.java
+++ b/tests/tests/os/src/android/os/cts/BuildTest.java
@@ -83,7 +83,7 @@
     /**
      * @param property name passed to getprop
      */
-    private String getProperty(String property)
+    static String getProperty(String property)
             throws IOException {
         Process process = new ProcessBuilder("getprop", property).start();
         Scanner scanner = null;
@@ -198,7 +198,7 @@
         assertNotEmpty(Build.USER);
     }
 
-    private static final String RO_DEBUGGABLE = "ro.debuggable";
+    static final String RO_DEBUGGABLE = "ro.debuggable";
     private static final String RO_SECURE = "ro.secure";
 
     /**
diff --git a/tests/tests/os/src/android/os/cts/BuildVersionTest.java b/tests/tests/os/src/android/os/cts/BuildVersionTest.java
index bee12b1..e47bf1f 100644
--- a/tests/tests/os/src/android/os/cts/BuildVersionTest.java
+++ b/tests/tests/os/src/android/os/cts/BuildVersionTest.java
@@ -29,16 +29,17 @@
 
     private static final String LOG_TAG = "BuildVersionTest";
     private static final Set<String> EXPECTED_RELEASES =
-        new HashSet<String>(Arrays.asList("4.4W", "4.4", "4.4.1", "4.4.2", "4.4.3"));
+            new HashSet<String>(Arrays.asList("4.4W", "4.4", "4.4.1", "4.4.2", "4.4.3"));
     private static final int EXPECTED_SDK = 20;
+    private static final Set<String> EXPECTED_BUILD_VARIANTS =
+            new HashSet<String>(Arrays.asList("user", "userdebug", "eng"));
+    private static final Set<String> EXPECTED_TAGS =
+            new HashSet<String>(Arrays.asList("test-keys", "dev-keys", "release-keys"));
 
     @SuppressWarnings("deprecation")
     public void testReleaseVersion() {
         // Applications may rely on the exact release version
-        assertTrue("Your Build.VERSION.RELEASE of " + Build.VERSION.RELEASE
-                + " was not one of the following: " + EXPECTED_RELEASES,
-                        EXPECTED_RELEASES.contains(Build.VERSION.RELEASE));
-
+        assertAnyOf("BUILD.VERSION.RELEASE", Build.VERSION.RELEASE, EXPECTED_RELEASES);
         assertEquals("" + EXPECTED_SDK, Build.VERSION.SDK);
         assertEquals(EXPECTED_SDK, Build.VERSION.SDK_INT);
     }
@@ -72,14 +73,26 @@
         assertEquals(Build.VERSION.RELEASE, devicePlatform[1]);
 
         assertEquals(Build.ID, fingerprintSegs[3]);
-        // no requirements for BUILD_NUMBER and BUILD_VARIANT
+
         assertTrue(fingerprintSegs[4].contains(":"));
-        // no strict requirement for TAGS
-        //assertEquals(Build.TAGS, fingerprintSegs[5]);
+        String[] buildNumberVariant = fingerprintSegs[4].split(":");
+        String buildVariant = buildNumberVariant[1];
+        assertAnyOf("Variant", buildVariant, EXPECTED_BUILD_VARIANTS);
+
+        // Build.TAGS
+        assertAnyOf("TAGS", fingerprintSegs[5], EXPECTED_TAGS);
     }
 
     private void assertNotEmpty(String value) {
         assertNotNull(value);
         assertFalse(value.isEmpty());
     }
+
+    /** Assert that {@code actualValue} is equals to one of {@code permittedValues}. */
+    private void assertAnyOf(String label, String actualValue, Set<String> permittedValues) {
+        if (!permittedValues.contains(actualValue)) {
+             fail("For: " + label + ", the value: " + actualValue +
+                     ", should be one of: " + permittedValues);
+        }
+    }
 }
diff --git a/tests/tests/os/src/android/os/cts/CpuFeaturesTest.java b/tests/tests/os/src/android/os/cts/CpuFeaturesTest.java
new file mode 100644
index 0000000..c5fb137
--- /dev/null
+++ b/tests/tests/os/src/android/os/cts/CpuFeaturesTest.java
@@ -0,0 +1,104 @@
+/*
+ * 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.os.cts;
+
+import java.io.BufferedReader;
+import java.io.FileReader;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import android.os.cts.CpuFeatures;
+
+import junit.framework.TestCase;
+
+public class CpuFeaturesTest extends TestCase {
+
+    private static void assertHwCap(String name, int hwcaps, int flag) {
+        assertEquals("Machine does not advertise " + name + " support", flag,
+                hwcaps & flag);
+    }
+
+    public void testArm64RequiredHwCaps() {
+        if (!CpuFeatures.isArm64CpuIn32BitMode()) {
+            return;
+        }
+
+        int hwcaps = CpuFeatures.getHwCaps();
+
+        assertFalse("Machine does not support getauxval(AT_HWCAP)",
+                hwcaps == 0);
+
+        assertHwCap("VFP", hwcaps, CpuFeatures.HWCAP_VFP);
+        assertHwCap("NEON", hwcaps, CpuFeatures.HWCAP_NEON);
+        assertHwCap("VFPv3", hwcaps, CpuFeatures.HWCAP_VFPv3);
+        assertHwCap("VFPv4", hwcaps, CpuFeatures.HWCAP_VFPv4);
+        assertHwCap("IDIVA", hwcaps, CpuFeatures.HWCAP_IDIVA);
+        assertHwCap("IDIVT", hwcaps, CpuFeatures.HWCAP_IDIVT);
+    }
+
+    private static List<String> getFeaturesFromCpuinfo() throws IOException {
+        BufferedReader br = new BufferedReader(new FileReader("/proc/cpuinfo"));
+        Pattern p = Pattern.compile("Features\\s*:\\s*(.*)");
+
+        try {
+            String line;
+            while ((line = br.readLine()) != null) {
+                Matcher m = p.matcher(line);
+                if (m.matches()) {
+                    String[] features = m.group(1).split("\\s");
+                    return Arrays.asList(features);
+                }
+            }
+       } finally {
+           br.close();
+       }
+
+       return null;
+    }
+
+    private static void assertNotInCpuinfo(List<String> features,
+            String feature) {
+        assertFalse("/proc/cpuinfo advertises required feature " + feature,
+                features.contains(feature));
+    }
+
+    public void testArm64Cpuinfo() throws IOException {
+        if (!CpuFeatures.isArm64Cpu()) {
+            return;
+        }
+
+        List<String> features = getFeaturesFromCpuinfo();
+        assertNotNull("Failed to parse /proc/cpuinfo", features);
+
+        assertNotInCpuinfo(features, "wp");
+        assertNotInCpuinfo(features, "half");
+        assertNotInCpuinfo(features, "thumb");
+        assertNotInCpuinfo(features, "fastmult");
+        assertNotInCpuinfo(features, "vfp");
+        assertNotInCpuinfo(features, "edsp");
+        assertNotInCpuinfo(features, "neon");
+        assertNotInCpuinfo(features, "vfpv3");
+        assertNotInCpuinfo(features, "tls");
+        assertNotInCpuinfo(features, "vfpv4");
+        assertNotInCpuinfo(features, "idiva");
+        assertNotInCpuinfo(features, "idivt");
+    }
+}
diff --git a/tests/tests/os/src/android/os/cts/MessageTest.java b/tests/tests/os/src/android/os/cts/MessageTest.java
index dc56a23..cc45c4b 100644
--- a/tests/tests/os/src/android/os/cts/MessageTest.java
+++ b/tests/tests/os/src/android/os/cts/MessageTest.java
@@ -231,6 +231,70 @@
         assertFalse(message.isAsynchronous());
     }
 
+    public void testRecycleThrowsIfMessageAlreadyRecycled() {
+        Message message = Message.obtain();
+        message.recycle();
+
+        try {
+            message.recycle();
+            fail("should throw IllegalStateException");
+        } catch (IllegalStateException ex) {
+            // expected
+        }
+    }
+
+    public void testSendMessageThrowsIfMessageAlreadyRecycled() {
+        Message message = Message.obtain();
+        message.recycle();
+
+        try {
+            mHandler.sendMessage(message);
+            fail("should throw IllegalStateException");
+        } catch (IllegalStateException ex) {
+            // expected
+        }
+    }
+
+    public void testRecycleThrowsIfMessageIsBeingDelivered() {
+        final Exception[] caught = new Exception[1];
+        Handler handler = new Handler(mHandler.getLooper()) {
+            @Override
+            public void handleMessage(Message msg) {
+                try {
+                    msg.recycle();
+                } catch (IllegalStateException ex) {
+                    caught[0] = ex; // expected
+                }
+            }
+        };
+        handler.sendEmptyMessage(WHAT);
+        sleep(SLEEP_TIME);
+
+        if (caught[0] == null) {
+            fail("should throw IllegalStateException");
+        }
+    }
+
+    public void testSendMessageThrowsIfMessageIsBeingDelivered() {
+        final Exception[] caught = new Exception[1];
+        Handler handler = new Handler(mHandler.getLooper()) {
+            @Override
+            public void handleMessage(Message msg) {
+                try {
+                    mHandler.sendMessage(msg);
+                } catch (IllegalStateException ex) {
+                    caught[0] = ex; // expected
+                }
+            }
+        };
+        handler.sendEmptyMessage(WHAT);
+        sleep(SLEEP_TIME);
+
+        if (caught[0] == null) {
+            fail("should throw IllegalStateException");
+        }
+    }
+
     private void sleep(long time) {
         try {
             Thread.sleep(time);
diff --git a/tests/tests/os/src/android/os/cts/SeccompTest.java b/tests/tests/os/src/android/os/cts/SeccompTest.java
new file mode 100644
index 0000000..e2ddcc2
--- /dev/null
+++ b/tests/tests/os/src/android/os/cts/SeccompTest.java
@@ -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.
+ */
+
+package android.os.cts;
+
+import junit.framework.TestCase;
+
+public class SeccompTest extends TestCase {
+
+    public void testSeccomp() {
+        if (CpuFeatures.isArm64Cpu()) {
+            return; // seccomp not supported on arm64
+        }
+        if (OSFeatures.needsSeccompSupport()) {
+            assertTrue("Please enable seccomp support "
+                       + "in your kernel (CONFIG_SECCOMP_FILTER=y)",
+                       OSFeatures.hasSeccompSupport());
+        }
+    }
+}
diff --git a/tests/tests/os/src/android/os/cts/SecurityFeaturesTest.java b/tests/tests/os/src/android/os/cts/SecurityFeaturesTest.java
index 6a02974..5a9a4da 100644
--- a/tests/tests/os/src/android/os/cts/SecurityFeaturesTest.java
+++ b/tests/tests/os/src/android/os/cts/SecurityFeaturesTest.java
@@ -16,6 +16,9 @@
 
 package android.os.cts;
 
+import android.system.Os;
+import static android.system.OsConstants.PR_GET_DUMPABLE;
+
 import junit.framework.TestCase;
 
 public class SecurityFeaturesTest extends TestCase {
@@ -49,4 +52,33 @@
             i++;
         }
     }
+
+    /**
+     * Verifies that prctl(PR_GET_DUMPABLE) == ro.debuggable
+     *
+     * When PR_SET_DUMPABLE is 0, an application will not generate a
+     * coredump, and PTRACE_ATTACH is disallowed. It's a security best
+     * practice to ensure that PR_SET_DUMPABLE is 0, to prevent an app
+     * from leaking the contents of its memory to persistent storage,
+     * other processes, or logs.
+     *
+     * By default, PR_SET_DUMPABLE is 0 for zygote spawned apps, except
+     * in the following circumstances:
+     *
+     * 1) ro.debuggable=1 (global debuggable enabled, i.e., userdebug or
+     * eng builds).
+     *
+     * 2) android:debuggable="true" in the manifest for an individual
+     * application.
+     *
+     * 3) An app which explicitly calls prctl(PR_SET_DUMPABLE, 1).
+     *
+     * For this test, neither #2 nor #3 are true, so we expect ro.debuggable
+     * to exactly equal prctl(PR_GET_DUMPABLE).
+     */
+    public void testPrctlDumpable() throws Exception {
+        int ro_debuggable = Integer.parseInt(BuildTest.getProperty(BuildTest.RO_DEBUGGABLE));
+        int prctl_dumpable = Os.prctl(PR_GET_DUMPABLE, 0, 0, 0, 0);
+        assertEquals(ro_debuggable, prctl_dumpable);
+    }
 }
diff --git a/tests/tests/permission/Android.mk b/tests/tests/permission/Android.mk
index 07f20d8..6d60499 100644
--- a/tests/tests/permission/Android.mk
+++ b/tests/tests/permission/Android.mk
@@ -19,9 +19,9 @@
 
 LOCAL_MODULE_TAGS := tests
 
-LOCAL_JAVA_LIBRARIES := android.test.runner telephony-common
+LOCAL_JAVA_LIBRARIES := telephony-common
 
-LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner
+LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner guava android-ex-camera2
 
 LOCAL_JNI_SHARED_LIBRARIES := libctspermission_jni
 
@@ -29,8 +29,9 @@
 
 LOCAL_PACKAGE_NAME := CtsPermissionTestCases
 
-# uncomment when dalvik test annotations are removed or part of SDK
+# uncomment when b/13249777 is fixed
 #LOCAL_SDK_VERSION := current
+LOCAL_JAVA_LIBRARIES += android.test.runner
 
 include $(BUILD_CTS_PACKAGE)
 
diff --git a/tests/tests/permission/AndroidManifest.xml b/tests/tests/permission/AndroidManifest.xml
index 945a303..fa03335 100644
--- a/tests/tests/permission/AndroidManifest.xml
+++ b/tests/tests/permission/AndroidManifest.xml
@@ -40,9 +40,12 @@
         package. That runner cannot be added to this package either, since it
         relies on hidden APIs.
     -->
-    <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
                      android:targetPackage="com.android.cts.permission"
-                     android:label="CTS tests of com.android.cts.permission"/>
+                     android:label="CTS tests of com.android.cts.permission">
+        <meta-data android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
 
 </manifest>
 
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/AppOpsTest.java b/tests/tests/permission/src/android/permission/cts/AppOpsTest.java
new file mode 100644
index 0000000..c29d5f5
--- /dev/null
+++ b/tests/tests/permission/src/android/permission/cts/AppOpsTest.java
@@ -0,0 +1,74 @@
+/*
+ * 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.permission.cts;
+
+import android.app.AppOpsManager;
+import android.content.Context;
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.util.AttributeSet;
+import junit.framework.AssertionFailedError;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+public class AppOpsTest extends AndroidTestCase {
+    static final Class<?>[] sSetModeSignature = new Class[] {
+            Context.class, AttributeSet.class};
+
+    private AppOpsManager mAppOps;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mAppOps = (AppOpsManager)getContext().getSystemService(Context.APP_OPS_SERVICE);
+        assertNotNull(mAppOps);
+    }
+
+    /**
+     * Test that the app can not change the app op mode for itself.
+     */
+    @SmallTest
+    public void testSetMode() {
+        boolean gotToTest = false;
+        try {
+            Method setMode = mAppOps.getClass().getMethod("setMode", int.class, int.class,
+                    String.class, int.class);
+            int writeSmsOp = mAppOps.getClass().getField("OP_WRITE_SMS").getInt(mAppOps);
+            gotToTest = true;
+            setMode.invoke(mAppOps, writeSmsOp, android.os.Process.myUid(),
+                    getContext().getPackageName(), AppOpsManager.MODE_ALLOWED);
+            fail("Was able to set mode for self");
+        } catch (NoSuchFieldException e) {
+            throw new AssertionError("Unable to find OP_WRITE_SMS", e);
+        } catch (NoSuchMethodException e) {
+            throw new AssertionError("Unable to find setMode method", e);
+        } catch (InvocationTargetException e) {
+            if (!gotToTest) {
+                throw new AssertionError("Whoops", e);
+            }
+            // If we got to the test, we want it to have thrown a security exception.
+            // We need to look inside of the wrapper exception to see.
+            Throwable t = e.getCause();
+            if (!(t instanceof SecurityException)) {
+                throw new AssertionError("Did not throw SecurityException", e);
+            }
+        } catch (IllegalAccessException e) {
+            throw new AssertionError("Whoops", e);
+        }
+    }
+}
diff --git a/tests/tests/permission/src/android/permission/cts/Camera2PermissionTest.java b/tests/tests/permission/src/android/permission/cts/Camera2PermissionTest.java
new file mode 100644
index 0000000..f9e19e0
--- /dev/null
+++ b/tests/tests/permission/src/android/permission/cts/Camera2PermissionTest.java
@@ -0,0 +1,135 @@
+/*
+ * 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.permission.cts;
+
+import static com.android.ex.camera2.blocking.BlockingStateListener.*;
+
+import android.content.Context;
+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;
+
+import com.android.ex.camera2.blocking.BlockingCameraManager;
+import com.android.ex.camera2.blocking.BlockingStateListener;
+
+/**
+ * Tests for Camera2 API related Permissions. Currently, this means
+ * android.permission.CAMERA.
+ */
+public class Camera2PermissionTest extends AndroidTestCase {
+    private static final String TAG = "CameraDeviceTest";
+    private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
+    private static final int CAMERA_CLOSE_TIMEOUT_MS = 2000;
+
+    private CameraManager mCameraManager;
+    private CameraDevice mCamera;
+    private BlockingStateListener mCameraListener;
+    private String[] mCameraIds;
+    protected Handler mHandler;
+    protected HandlerThread mHandlerThread;
+
+    @Override
+    public void setContext(Context context) {
+        super.setContext(context);
+        mCameraManager = (CameraManager) context.getSystemService(Context.CAMERA_SERVICE);
+        assertNotNull("Can't connect to camera manager!", mCameraManager);
+    }
+
+    /**
+     * Set up the camera2 test case required environments, including CameraManager,
+     * HandlerThread, Camera IDs, and CameraStateListener etc.
+     */
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mCameraIds = mCameraManager.getCameraIdList();
+        assertNotNull("Camera ids shouldn't be null", mCameraIds);
+        mHandlerThread = new HandlerThread(TAG);
+        mHandlerThread.start();
+        mHandler = new Handler(mHandlerThread.getLooper());
+        mCameraListener = new BlockingStateListener();
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        mHandlerThread.quitSafely();
+        mHandler = null;
+
+        super.tearDown();
+    }
+
+    /**
+     * Attempt to open camera. Requires Permission:
+     * {@link android.Manifest.permission#CAMERA}.
+     */
+    public void testCameraOpen() throws Exception {
+        for (String id : mCameraIds) {
+            try {
+                openCamera(id);
+                fail("Was able to open camera " + id + " with no permission");
+            }
+            catch (SecurityException e) {
+                // expected
+            } finally {
+                closeCamera();
+            }
+        }
+    }
+
+    /**
+     * Add and remove availability listeners should work without permission.
+     */
+    public void testAvailabilityListener() throws Exception {
+        DummyCameraListener availabilityListener = new DummyCameraListener();
+        // Remove a not-registered listener is a no-op.
+        mCameraManager.removeAvailabilityListener(availabilityListener);
+        mCameraManager.addAvailabilityListener(availabilityListener, mHandler);
+        mCameraManager.removeAvailabilityListener(availabilityListener);
+        mCameraManager.addAvailabilityListener(availabilityListener, mHandler);
+        mCameraManager.addAvailabilityListener(availabilityListener, mHandler);
+        mCameraManager.removeAvailabilityListener(availabilityListener);
+        // Remove a previously-added listener second time is a no-op.
+        mCameraManager.removeAvailabilityListener(availabilityListener);
+    }
+
+    private class DummyCameraListener extends CameraManager.AvailabilityListener {
+        @Override
+        public void onCameraAvailable(String cameraId) {
+        }
+
+        @Override
+        public void onCameraUnavailable(String cameraId) {
+        }
+    }
+
+    private void openCamera(String cameraId) throws Exception {
+        mCamera = (new BlockingCameraManager(mCameraManager)).openCamera(
+                cameraId, mCameraListener, mHandler);
+    }
+
+    private void closeCamera() {
+        if (mCamera != null) {
+            mCamera.close();
+            mCameraListener.waitForState(STATE_CLOSED, CAMERA_CLOSE_TIMEOUT_MS);
+            mCamera = null;
+        }
+    }
+}
+
diff --git a/tests/tests/permission/src/android/permission/cts/DevicePowerPermissionTest.java b/tests/tests/permission/src/android/permission/cts/DevicePowerPermissionTest.java
index c5f8ea5..006fb6d 100644
--- a/tests/tests/permission/src/android/permission/cts/DevicePowerPermissionTest.java
+++ b/tests/tests/permission/src/android/permission/cts/DevicePowerPermissionTest.java
@@ -20,7 +20,6 @@
 import android.os.PowerManager;
 import android.test.AndroidTestCase;
 import android.test.suitebuilder.annotation.LargeTest;
-import dalvik.annotation.KnownFailure;
 
 /**
  * Verify that various PowerManagement functionality requires Permission.
diff --git a/tests/tests/permission/src/android/permission/cts/FileSystemPermissionTest.java b/tests/tests/permission/src/android/permission/cts/FileSystemPermissionTest.java
index 85af555..61998e7 100755
--- a/tests/tests/permission/src/android/permission/cts/FileSystemPermissionTest.java
+++ b/tests/tests/permission/src/android/permission/cts/FileSystemPermissionTest.java
@@ -19,6 +19,7 @@
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
 import android.os.Environment;
+import android.system.OsConstants;
 import android.test.AndroidTestCase;
 import android.test.suitebuilder.annotation.MediumTest;
 import android.test.suitebuilder.annotation.LargeTest;
@@ -237,6 +238,17 @@
         assertFileOwnedByGroup(f, "net_bw_stats");
     }
 
+    @MediumTest
+    public void testTcpDefaultRwndSane() throws Exception {
+        File f = new File("/proc/sys/net/ipv4/tcp_default_init_rwnd");
+        assertTrue(f.canRead());
+        assertFalse(f.canWrite());
+        assertFalse(f.canExecute());
+
+        assertFileOwnedBy(f, "root");
+        assertFileOwnedByGroup(f, "root");
+    }
+
     /**
      * Assert that a file is owned by a specific owner. This is a noop if the
      * file does not exist.
@@ -838,6 +850,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
@@ -860,6 +896,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..af44a1c 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 android.system.OsConstants;
+
+import java.util.HashSet;
+import java.util.Set;
+
 /** 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/permission2/Android.mk b/tests/tests/permission2/Android.mk
index 86a8bc7..29d5e03 100755
--- a/tests/tests/permission2/Android.mk
+++ b/tests/tests/permission2/Android.mk
@@ -19,7 +19,7 @@
 
 LOCAL_MODULE_TAGS := tests
 
-LOCAL_JAVA_LIBRARIES := android.test.runner telephony-common mms-common
+LOCAL_JAVA_LIBRARIES := telephony-common
 
 LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner
 
diff --git a/tests/tests/permission2/AndroidManifest.xml b/tests/tests/permission2/AndroidManifest.xml
index 1c1a0d8..c0b78c4 100755
--- a/tests/tests/permission2/AndroidManifest.xml
+++ b/tests/tests/permission2/AndroidManifest.xml
@@ -56,9 +56,12 @@
             android:name="android.permission.FLASHLIGHT"
             android:maxSdkVersion="9000" />
 
-    <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
                      android:targetPackage="com.android.cts.permission2"
-                     android:label="More CTS tests for permissions"/>
+                     android:label="More CTS tests for permissions">
+        <meta-data android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
 
 </manifest>
 
diff --git a/tests/tests/preference/Android.mk b/tests/tests/preference/Android.mk
index cc2b210..5860406 100644
--- a/tests/tests/preference/Android.mk
+++ b/tests/tests/preference/Android.mk
@@ -20,8 +20,6 @@
 
 LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
 
-LOCAL_JAVA_LIBRARIES := android.test.runner
-
 LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
diff --git a/tests/tests/preference/AndroidManifest.xml b/tests/tests/preference/AndroidManifest.xml
index 3477192..e4c6b52 100644
--- a/tests/tests/preference/AndroidManifest.xml
+++ b/tests/tests/preference/AndroidManifest.xml
@@ -23,9 +23,12 @@
         <uses-library android:name="android.test.runner" />
     </application>
 
-    <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
                      android:targetPackage="com.android.cts.stub"
-                     android:label="CTS tests of android.os"/>
+                     android:label="CTS tests of android.os">
+        <meta-data android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
 
 </manifest>
 
diff --git a/tests/tests/preference2/Android.mk b/tests/tests/preference2/Android.mk
index 47b081d..59fedc8 100644
--- a/tests/tests/preference2/Android.mk
+++ b/tests/tests/preference2/Android.mk
@@ -22,8 +22,6 @@
 
 LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
 
-LOCAL_JAVA_LIBRARIES := android.test.runner
-
 LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
diff --git a/tests/tests/preference2/AndroidManifest.xml b/tests/tests/preference2/AndroidManifest.xml
index 23b085d..2dbd53a 100644
--- a/tests/tests/preference2/AndroidManifest.xml
+++ b/tests/tests/preference2/AndroidManifest.xml
@@ -35,9 +35,12 @@
     </application>
 
     <!-- This is a self-instrumenting test package. -->
-    <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
                      android:targetPackage="com.android.cts.preference2"
-                     android:label="CTS Test Cases for android.preference"/>
+                     android:label="CTS Test Cases for android.preference">
+        <meta-data android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
 
 </manifest>
 
diff --git a/tests/tests/print/Android.mk b/tests/tests/print/Android.mk
new file mode 100644
index 0000000..516f6a0
--- /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_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
+
+LOCAL_SDK_VERSION := current
+
+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..acb4369
--- /dev/null
+++ b/tests/tests/print/src/android/print/cts/BasePrintTest.java
@@ -0,0 +1,427 @@
+/*
+ * 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.graphics.pdf.PdfDocument;
+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.print.pdf.PrintedPdfDocument;
+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.io.FileOutputStream;
+import java.io.IOException;
+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 mCancelOperationCounter;
+    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.
+        mCancelOperationCounter = new CallCounter();
+        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 onCancelOperationCalled() {
+        mCancelOperationCounter.call();
+    }
+
+    protected void onLayoutCalled() {
+        mLayoutCallCounter.call();
+    }
+
+    protected int getWriteCallCount() {
+        return mWriteCallCounter.getCallCount();
+    }
+
+    protected void onWriteCalled() {
+        mWriteCallCounter.call();
+    }
+
+    protected void onFinishCalled() {
+        mFinishCallCounter.call();
+    }
+
+    protected void onPrintJobQueuedCalled() {
+        mPrintJobQueuedCallCounter.call();
+    }
+
+    protected void onPrinterDiscoverySessionDestroyCalled() {
+        mDestroySessionCallCounter.call();
+    }
+
+    protected void waitForCancelOperationCallbackCalled() {
+        waitForCallbackCallCount(mCancelOperationCounter, 1,
+                "Did not get expected call to onCancel for the current operation.");
+    }
+
+    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 waitForWriteAdapterCallback() {
+        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 openPrintOptions() throws UiObjectNotFoundException {
+        UiObject expandHandle = new UiObject(new UiSelector().resourceId(
+                "com.android.printspooler:id/expand_collapse_handle"));
+        expandHandle.click();
+    }
+
+    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 void writeBlankPages(PrintAttributes constraints, ParcelFileDescriptor output,
+            int fromIndex, int toIndex) throws IOException {
+        PrintedPdfDocument document = new PrintedPdfDocument(getActivity(), constraints);
+        final int pageCount = toIndex - fromIndex + 1;
+        for (int i = 0; i < pageCount; i++) {
+            PdfDocument.Page page = document.startPage(i);
+            document.finishPage(page);
+        }
+        FileOutputStream fos = new FileOutputStream(output.getFileDescriptor());
+        document.writeTo(fos);
+        document.close();
+    }
+
+    protected final class CallCounter {
+        private final Object mLock = new Object();
+
+        private int mCallCount;
+
+        public void call() {
+            synchronized (mLock) {
+                mCallCount++;
+                mLock.notifyAll();
+            }
+        }
+
+        public int getCallCount() {
+            synchronized (mLock) {
+                return 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..8351d56
--- /dev/null
+++ b/tests/tests/print/src/android/print/cts/PageRangeAdjustmentTest.java
@@ -0,0 +1,656 @@
+/*
+ * 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.PrintJobInfo;
+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.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();
+                assertTrue(pages.length == 1 && PageRange.ALL_PAGES.equals(pages[0]));
+                printJob.complete();
+                onPrintJobQueuedCalled();
+                return null;
+            }
+        }, null);
+
+        final PrintAttributes[] printAttributes = new PrintAttributes[1];
+
+        // 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 {
+                printAttributes[0] = (PrintAttributes) invocation.getArguments()[1];
+                LayoutResultCallback callback = (LayoutResultCallback) invocation.getArguments()[3];
+                PrintDocumentInfo info = new PrintDocumentInfo.Builder(PRINT_JOB_NAME)
+                        .setContentType(PrintDocumentInfo.CONTENT_TYPE_DOCUMENT)
+                        .setPageCount(100)
+                        .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];
+                writeBlankPages(printAttributes[0], fd, 0, 99);
+                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.
+        waitForWriteAdapterCallback();
+
+        // 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 ask for some pages for preview and in this
+                // case we write all, i.e. more than needed.
+                assertTrue(pages.length == 1 && pages[0].getStart() == 1
+                        && pages[0].getEnd() == 1);
+                printJob.complete();
+                onPrintJobQueuedCalled();
+                return null;
+            }
+        }, null);
+
+        final PrintAttributes[] printAttributes = new PrintAttributes[1];
+
+        // 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 {
+                printAttributes[0] = (PrintAttributes) invocation.getArguments()[1];
+                LayoutResultCallback callback = (LayoutResultCallback) invocation.getArguments()[3];
+                PrintDocumentInfo info = new PrintDocumentInfo.Builder(PRINT_JOB_NAME)
+                        .setContentType(PrintDocumentInfo.CONTENT_TYPE_DOCUMENT)
+                        .setPageCount(100)
+                        .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];
+                assertTrue(pages[pages.length - 1].getEnd() < 100);
+                ParcelFileDescriptor fd = (ParcelFileDescriptor) args[1];
+                WriteResultCallback callback = (WriteResultCallback) args[3];
+                writeBlankPages(printAttributes[0], fd, 0, 99);
+                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.
+        waitForWriteAdapterCallback();
+
+        // Open the print options.
+        openPrintOptions();
+
+        // 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];
+                PrintJobInfo printJobInfo = printJob.getInfo();
+                PageRange[] pages = printJobInfo.getPages();
+                // We asked only for page 60 (index 59) but got 60 and 61 (indices
+                // 59, 60), hence the written document has two pages (60 and 61)
+                // and the first one, i.e. 3 should be printed.
+                assertTrue(pages.length == 1 && pages[0].getStart() == 0
+                        && pages[0].getEnd() == 0);
+                assertSame(printJob.getDocument().getInfo().getPageCount(), 2);
+                printJob.complete();
+                onPrintJobQueuedCalled();
+                return null;
+            }
+        }, null);
+
+        final PrintAttributes[] printAttributes = new PrintAttributes[1];
+
+        // 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 {
+                printAttributes[0] = (PrintAttributes) invocation.getArguments()[1];
+                LayoutResultCallback callback = (LayoutResultCallback) invocation.getArguments()[3];
+                PrintDocumentInfo info = new PrintDocumentInfo.Builder(PRINT_JOB_NAME)
+                        .setContentType(PrintDocumentInfo.CONTENT_TYPE_DOCUMENT)
+                        .setPageCount(100)
+                        .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 pages for
+                // preview or the page we selected in the UI.
+                assertSame(pages.length, 1);
+
+                // The first write request for some pages to preview.
+                if (getWriteCallCount() == 0) {
+                    // Write all requested pages.
+                    writeBlankPages(printAttributes[0], fd, pages[0].getStart(), pages[0].getEnd());
+                    callback.onWriteFinished(pages);
+                } else {
+                    // Otherwise write a page more that the one we selected.
+                    writeBlankPages(printAttributes[0], fd, 59, 60);
+                    callback.onWriteFinished(new PageRange[] {new PageRange(59, 60)});
+                }
+
+                fd.close();
+
+                // 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.
+        waitForWriteAdapterCallback();
+
+        // Open the print options.
+        openPrintOptions();
+
+        // Select the first printer.
+        selectPrinter(FIRST_PRINTER);
+
+        // Wait for layout as the printer has different capabilities.
+        waitForLayoutAdapterCallbackCount(2);
+
+        // Select a page not written for preview.
+        selectPages("60");
+
+        // 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);
+
+        final PrintAttributes[] printAttributes = new PrintAttributes[1];
+
+        // 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 {
+                printAttributes[0] = (PrintAttributes) invocation.getArguments()[1];
+                LayoutResultCallback callback = (LayoutResultCallback) invocation.getArguments()[3];
+                PrintDocumentInfo info = new PrintDocumentInfo.Builder(PRINT_JOB_NAME)
+                        .setContentType(PrintDocumentInfo.CONTENT_TYPE_DOCUMENT)
+                        .setPageCount(100)
+                        .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);
+
+                // We should be asked for some pages...
+                assertSame(pages[0].getStart(), 0);
+                assertTrue(pages[0].getEnd() == 49);
+
+                writeBlankPages(printAttributes[0], fd, pages[0].getStart(), pages[0].getEnd());
+                fd.close();
+                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.
+        waitForWriteAdapterCallback();
+
+        // Cancel printing.
+        getUiDevice().pressBack(); // wakes up the device.
+        getUiDevice().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));
+    }
+
+    public void testWantedPagesAlreadyWrittenForPreview() 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];
+                    PrintJobInfo printJobInfo = printJob.getInfo();
+                    PageRange[] pages = printJobInfo.getPages();
+                    // We asked only for page 3 (index 2) but got this page when
+                    // we were getting the pages for preview (indices 0 - 49), hence
+                    // the written document has fifty pages (0 - 49) and the third one,
+                    // i.e. index 2 should be printed.
+                    assertTrue(pages.length == 1 && pages[0].getStart() == 2
+                            && pages[0].getEnd() == 2);
+                    assertSame(printJob.getDocument().getInfo().getPageCount(), 50);
+                    printJob.complete();
+                    onPrintJobQueuedCalled();
+                    return null;
+                }
+            }, null);
+
+        final PrintAttributes[] printAttributes = new PrintAttributes[1];
+
+        // 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 {
+                printAttributes[0] = (PrintAttributes) invocation.getArguments()[1];
+                LayoutResultCallback callback = (LayoutResultCallback) invocation.getArguments()[3];
+                PrintDocumentInfo info = new PrintDocumentInfo.Builder(PRINT_JOB_NAME)
+                        .setContentType(PrintDocumentInfo.CONTENT_TYPE_DOCUMENT)
+                        .setPageCount(100)
+                        .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 pages for
+                // preview or the page we selected in the UI.
+                assertSame(pages.length, 1);
+
+                // Write all requested pages.
+                writeBlankPages(printAttributes[0], fd, pages[0].getStart(), pages[0].getEnd());
+                callback.onWriteFinished(pages);
+                fd.close();
+
+                // 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.
+        waitForWriteAdapterCallback();
+
+        // Open the print options.
+        openPrintOptions();
+
+        // Select the first printer.
+        selectPrinter(FIRST_PRINTER);
+
+        // Wait for layout as the printer has different capabilities.
+        waitForLayoutAdapterCallbackCount(2);
+
+        // Select a page not written for preview.
+        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));
+    }
+
+    private void selectPages(String pages) throws UiObjectNotFoundException {
+        UiObject pagesSpinner = getUiDevice().findObject(new UiSelector().resourceId(
+                "com.android.printspooler:id/range_options_spinner"));
+        pagesSpinner.click();
+
+        UiObject rangeOption = getUiDevice().findObject(new UiSelector().text("Range"));
+        rangeOption.click();
+
+        UiObject pagesEditText = getUiDevice().findObject(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<>();
+
+                    // 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..64bc5d6
--- /dev/null
+++ b/tests/tests/print/src/android/print/cts/PrintDocumentAdapterContractTest.java
@@ -0,0 +1,1539 @@
+/*
+ * 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 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());
+
+        final PrintAttributes[] printAttributes = new PrintAttributes[1];
+
+        // Create a mock print adapter.
+        final PrintDocumentAdapter adapter = createMockPrintDocumentAdapter(
+            new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                printAttributes[0] = (PrintAttributes) invocation.getArguments()[1];
+                LayoutResultCallback callback = (LayoutResultCallback) invocation.getArguments()[3];
+                PrintDocumentInfo info = new PrintDocumentInfo.Builder(PRINT_JOB_NAME)
+                        .setContentType(PrintDocumentInfo.CONTENT_TYPE_DOCUMENT)
+                        .setPageCount(2)
+                        .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];
+                writeBlankPages(printAttributes[0], fd, pages[0].getStart(), pages[0].getEnd());
+                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.
+        waitForWriteAdapterCallback();
+
+        // 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 the first fifty pages for preview.
+        PageRange[] firstPages = new PageRange[] {new PageRange(0, 1)};
+        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 but we got
+        // them when asking for the ones for a preview, and the adapter does
+        // not report a content change. Hence, there is nothing to write.
+
+        // 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());
+
+        final PrintAttributes[] printAttributes = new PrintAttributes[1];
+
+        // Create a mock print adapter.
+        final PrintDocumentAdapter adapter = createMockPrintDocumentAdapter(
+            new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                printAttributes[0] = (PrintAttributes) invocation.getArguments()[1];
+                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];
+                writeBlankPages(printAttributes[0], fd, pages[0].getStart(), pages[0].getEnd());
+                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.
+        waitForWriteAdapterCallback();
+
+        // Cancel the printing.
+        getUiDevice().pressBack(); // wakes up the device.
+        getUiDevice().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 the first fifty pages 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());
+
+        final PrintAttributes[] printAttributes = new PrintAttributes[1];
+
+        // Create a mock print adapter.
+        final PrintDocumentAdapter adapter = createMockPrintDocumentAdapter(
+            new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                printAttributes[0] = (PrintAttributes) invocation.getArguments()[1];
+                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];
+                writeBlankPages(printAttributes[0], fd, pages[0].getStart(), pages[0].getEnd());
+                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.
+        waitForWriteAdapterCallback();
+
+        // Open the print options.
+        openPrintOptions();
+
+        // 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 the first fifty pages 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 but we got
+        // them when asking for the ones for a preview, and the adapter does
+        // not report a content change. Hence, there is nothing to write.
+
+        // 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());
+
+        final PrintAttributes[] printAttributes = new PrintAttributes[1];
+
+        // Create a mock print adapter.
+        final PrintDocumentAdapter adapter = createMockPrintDocumentAdapter(
+            new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                printAttributes[0] = (PrintAttributes) invocation.getArguments()[1];
+                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];
+                writeBlankPages(printAttributes[0], fd, pages[0].getStart(), pages[0].getEnd());
+                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.
+        waitForWriteAdapterCallback();
+
+        // Open the print options.
+        openPrintOptions();
+
+        // 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 the first fifty pages 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 color which results 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 but we got
+        // them when asking for the ones for a preview, and the adapter does
+        // not report a content change. Hence, there is nothing to write.
+
+        // 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());
+
+        final PrintAttributes[] printAttributes = new PrintAttributes[1];
+
+        // Create a mock print adapter.
+        final PrintDocumentAdapter adapter = createMockPrintDocumentAdapter(
+            new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                printAttributes[0] = (PrintAttributes) invocation.getArguments()[1];
+                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];
+                writeBlankPages(printAttributes[0], fd, pages[0].getStart(), pages[0].getEnd());
+                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.
+        waitForWriteAdapterCallback();
+
+        // Open the print options.
+        openPrintOptions();
+
+        // 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 the first fifty pages 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 as the adapter
+        // reports that the content changes after every layout pass.
+        PageRange[] thirdPages = new PageRange[] {new PageRange(0, 0)};
+        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());
+
+        final PrintAttributes[] printAttributes = new PrintAttributes[1];
+
+        // Create a mock print adapter.
+        final PrintDocumentAdapter adapter = createMockPrintDocumentAdapter(
+            new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                printAttributes[0] = (PrintAttributes) invocation.getArguments()[1];
+                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);
+                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];
+                writeBlankPages(printAttributes[0], fd, pages[0].getStart(), pages[0].getEnd());
+                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.
+        waitForWriteAdapterCallback();
+
+        // Open the print options.
+        openPrintOptions();
+
+        // 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 the first fifty pages 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[] {new PageRange(0, 0)};
+        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());
+
+        final PrintAttributes[] printAttributes = new PrintAttributes[1];
+
+        // Create a mock print adapter.
+        final PrintDocumentAdapter adapter = createMockPrintDocumentAdapter(
+            new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                printAttributes[0] = (PrintAttributes) invocation.getArguments()[1];
+                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];
+                PageRange[] pages = (PageRange[]) args[0];
+                writeBlankPages(printAttributes[0], fd, pages[0].getStart(), pages[0].getEnd());
+                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.
+        waitForWriteAdapterCallback();
+
+        // Open the print options.
+        openPrintOptions();
+
+        // 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 the first fifty pages for preview.
+        PageRange[] firstPages = new PageRange[] {new PageRange(0, 2)};
+        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() {
+                        onCancelOperationCalled();
+                        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.
+        getUiDevice().pressBack(); // wakes up the device.
+        getUiDevice().pressBack();
+
+        // Wait for the cancellation request.
+        waitForCancelOperationCallbackCalled();
+
+        // 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 */
+                        }
+                        onCancelOperationCalled();
+                        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.
+        waitForWriteAdapterCallback();
+
+        // Cancel printing.
+        getUiDevice().pressBack(); // wakes up the device.
+        getUiDevice().pressBack();
+
+        // Wait for the cancellation request.
+        waitForCancelOperationCallbackCalled();
+
+        // 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.
+        getUiDevice().pressBack(); // wakes up the device.
+        getUiDevice().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.
+        waitForWriteAdapterCallback();
+
+        // Cancel printing.
+        getUiDevice().pressBack(); // wakes up the device.
+        getUiDevice().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());
+
+        final PrintAttributes[] printAttributes = new PrintAttributes[1];
+
+        // Create a mock print adapter.
+        final PrintDocumentAdapter adapter = createMockPrintDocumentAdapter(
+            new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                printAttributes[0] = (PrintAttributes) invocation.getArguments()[1];
+                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];
+                PageRange[] pages = (PageRange[]) args[0];
+                writeBlankPages(printAttributes[0], fd, Integer.MAX_VALUE, Integer.MAX_VALUE);
+                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.
+        waitForWriteAdapterCallback();
+
+        // Cancel printing.
+        getUiDevice().pressBack(); // wakes up the device.
+        getUiDevice().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.
+        getUiDevice().pressBack(); // wakes up the device.
+        getUiDevice().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.
+        waitForWriteAdapterCallback();
+
+        // Cancel printing.
+        getUiDevice().pressBack(); // wakes up the device.
+        getUiDevice().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..e17dda5
--- /dev/null
+++ b/tests/tests/print/src/android/print/cts/PrinterDiscoverySessionLifecycleTest.java
@@ -0,0 +1,426 @@
+/*
+ * 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 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.
+        waitForWriteAdapterCallback();
+
+        // 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.
+        waitForWriteAdapterCallback();
+
+        // 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.
+        getUiDevice().pressBack(); // wakes up the device.
+        getUiDevice().pressBack();
+
+        // Wait for all print jobs to be handled after which the is session destroyed.
+        waitForPrinterDiscoverySessionDestroyCallbackCalled();
+
+        // Verify the expected calls.
+        InOrder inOrder = inOrder(firstSessionCallbacks);
+
+        // We start discovery with no printer history.
+        List<PrinterId> priorityList = new ArrayList<PrinterId>();
+        inOrder.verify(firstSessionCallbacks).onStartPrinterDiscovery(
+                priorityList);
+
+        // We selected the first printer and now it should be tracked.
+        inOrder.verify(firstSessionCallbacks).onStartPrinterStateTracking(
+                firstPrinterId);
+
+        // We confirmed print so the first should not be tracked.
+        inOrder.verify(firstSessionCallbacks).onStopPrinterStateTracking(
+                firstPrinterId);
+
+        // We print again which brings the print activity up but the old
+        // print activity is not destroyed yet (just fine) which is the
+        // printer discovery session is not destroyed and the second print
+        // will join the ongoing session. Hence, instead of start printer
+        // discovery we are getting a printer validation request.
+        priorityList.add(firstPrinterId);
+        inOrder.verify(firstSessionCallbacks).onValidatePrinters(priorityList);
+
+        // The system selects the highest ranked historical printer.
+        inOrder.verify(firstSessionCallbacks).onStartPrinterStateTracking(
+                firstPrinterId);
+
+        // We canceled print so the first should not be tracked.
+        inOrder.verify(firstSessionCallbacks).onStopPrinterStateTracking(
+                firstPrinterId);
+
+
+        // Discovery is always stopped before the session is always destroyed.
+        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() {
+        final PrintAttributes[] printAttributes = new PrintAttributes[1];
+
+        return createMockPrintDocumentAdapter(
+            new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                printAttributes[0] = (PrintAttributes) invocation.getArguments()[1];
+                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];
+                writeBlankPages(printAttributes[0], fd, pages[0].getStart(), pages[0].getEnd());
+                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/AndroidManifest.xml b/tests/tests/provider/AndroidManifest.xml
index 94dc408..7bf44b7 100644
--- a/tests/tests/provider/AndroidManifest.xml
+++ b/tests/tests/provider/AndroidManifest.xml
@@ -40,9 +40,12 @@
 
     </application>
 
-    <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
                      android:targetPackage="com.android.cts.stub"
-                     android:label="CTS tests of android.provider"/>
+                     android:label="CTS tests of android.provider">
+        <meta-data android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
 
     <instrumentation android:name="android.provider.cts.CalendarTest$CalendarEmmaTestRunner"
                      android:targetPackage="com.android.cts.stub"
diff --git a/tests/tests/provider/src/android/provider/cts/BrowserTest.java b/tests/tests/provider/src/android/provider/cts/BrowserTest.java
index 9654e43..ffeb2a1 100644
--- a/tests/tests/provider/src/android/provider/cts/BrowserTest.java
+++ b/tests/tests/provider/src/android/provider/cts/BrowserTest.java
@@ -28,9 +28,6 @@
 import android.provider.Browser;
 import android.provider.Browser.BookmarkColumns;
 import android.provider.Browser.SearchColumns;
-import android.provider.BrowserContract;
-import android.provider.BrowserContract.Bookmarks;
-import android.provider.BrowserContract.History;
 import android.test.ActivityInstrumentationTestCase2;
 
 import java.util.ArrayList;
@@ -73,39 +70,30 @@
         ContentResolver.setMasterSyncAutomatically(false);
 
         // backup the current contents in database
-        Cursor cursor = mProvider.query(Bookmarks.CONTENT_URI, null, null, null, null, null);
-        if (cursor.moveToFirst()) {
+        Cursor cursor = mProvider.query(Browser.BOOKMARKS_URI, null, null, null, null, null);
+        while (cursor.moveToNext()) {
             String[] colNames = cursor.getColumnNames();
-            while (!cursor.isAfterLast()) {
-                ContentValues value = new ContentValues();
+            ContentValues value = new ContentValues();
 
-                for (int i = 0; i < colNames.length; i++) {
-                    if (Bookmarks.PARENT_SOURCE_ID.equals(colNames[i])
-                            || Bookmarks.INSERT_AFTER_SOURCE_ID.equals(colNames[i])
-                            || Bookmarks.TYPE.equals(colNames[i])) {
-                        // These aren't actual columns, so skip them in the backup
-                        continue;
-                    }
-                    switch (cursor.getType(i)) {
-                    case Cursor.FIELD_TYPE_BLOB:
-                        value.put(colNames[i], cursor.getBlob(i));
-                        break;
-                    case Cursor.FIELD_TYPE_FLOAT:
-                        value.put(colNames[i], cursor.getFloat(i));
-                        break;
-                    case Cursor.FIELD_TYPE_INTEGER:
-                        value.put(colNames[i], cursor.getLong(i));
-                        break;
-                    case Cursor.FIELD_TYPE_STRING:
-                        value.put(colNames[i], cursor.getString(i));
-                        break;
-                    }
+            for (int i = 0; i < colNames.length; i++) {
+                switch (cursor.getType(i)) {
+                case Cursor.FIELD_TYPE_BLOB:
+                    value.put(colNames[i], cursor.getBlob(i));
+                    break;
+                case Cursor.FIELD_TYPE_FLOAT:
+                    value.put(colNames[i], cursor.getFloat(i));
+                    break;
+                case Cursor.FIELD_TYPE_INTEGER:
+                    value.put(colNames[i], cursor.getLong(i));
+                    break;
+                case Cursor.FIELD_TYPE_STRING:
+                    value.put(colNames[i], cursor.getString(i));
+                    break;
                 }
-                mBookmarksBackup.add(value);
-
-                cursor.moveToNext();
-            };
+            }
+            mBookmarksBackup.add(value);
         }
+
         cursor.close();
 
         cursor = mProvider.query(Browser.SEARCHES_URI, null, null, null, null, null);
@@ -123,12 +111,8 @@
         }
         cursor.close();
 
-        Uri uri = Bookmarks.CONTENT_URI.buildUpon()
-                .appendQueryParameter(BrowserContract.CALLER_IS_SYNCADAPTER, "true")
-                .build();
-        mProvider.delete(uri, null, null);
+        mProvider.delete(Browser.BOOKMARKS_URI, null, null);
         mProvider.delete(Browser.SEARCHES_URI, null, null);
-        mProvider.delete(History.CONTENT_URI, null, null);
 
         mActivity = getActivity();
     }
@@ -136,17 +120,13 @@
     @Override
     protected void tearDown() throws Exception {
         try {
-
             // clear all new contents added in test cases.
-            Uri uri = Bookmarks.CONTENT_URI.buildUpon()
-                .appendQueryParameter(BrowserContract.CALLER_IS_SYNCADAPTER, "true")
-                .build();
-            mProvider.delete(uri, null, null);
+            mProvider.delete(Browser.BOOKMARKS_URI, null, null);
             mProvider.delete(Browser.SEARCHES_URI, null, null);
 
             // recover the old backup contents
             for (ContentValues value : mBookmarksBackup) {
-                mProvider.insert(uri, value);
+                mProvider.insert(Browser.BOOKMARKS_URI, value);
             }
 
             for (ContentValues value : mSearchesBackup) {
diff --git a/tests/tests/provider/src/android/provider/cts/CalendarTest.java b/tests/tests/provider/src/android/provider/cts/CalendarTest.java
index bd8e06d..a8f547b 100644
--- a/tests/tests/provider/src/android/provider/cts/CalendarTest.java
+++ b/tests/tests/provider/src/android/provider/cts/CalendarTest.java
@@ -41,7 +41,6 @@
 import android.provider.CalendarContract.Instances;
 import android.provider.CalendarContract.Reminders;
 import android.provider.CalendarContract.SyncState;
-import android.test.InstrumentationCtsTestRunner;
 import android.test.InstrumentationTestCase;
 import android.test.suitebuilder.annotation.MediumTest;
 import android.text.TextUtils;
@@ -3706,7 +3705,8 @@
     /**
      * Special version of the test runner that does some remote Emma coverage housekeeping.
      */
-    public static class CalendarEmmaTestRunner extends InstrumentationCtsTestRunner {
+    // TODO: find if this is still used and if so convert to AndroidJUnitRunner framework
+    public static class CalendarEmmaTestRunner extends android.test.InstrumentationTestRunner {
         private static final Uri EMMA_CONTENT_URI =
             Uri.parse("content://" + CalendarContract.AUTHORITY + "/emma");
         private ContentResolver mContentResolver;
diff --git a/tests/tests/provider/src/android/provider/cts/ContactsContract_ContactCountsTest.java b/tests/tests/provider/src/android/provider/cts/ContactsContract_ContactCountsTest.java
new file mode 100644
index 0000000..61867b6
--- /dev/null
+++ b/tests/tests/provider/src/android/provider/cts/ContactsContract_ContactCountsTest.java
@@ -0,0 +1,181 @@
+/*
+ * 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.provider.cts;
+
+
+import java.util.Arrays;
+
+import android.content.ContentProviderClient;
+import android.content.ContentResolver;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Bundle;
+import android.provider.ContactsContract;
+import android.provider.ContactsContract.CommonDataKinds.StructuredName;
+import android.provider.ContactsContract.Contacts;
+import android.provider.ContactsContract.RawContacts;
+import android.provider.cts.ContactsContract_TestDataBuilder.TestContact;
+import android.provider.cts.ContactsContract_TestDataBuilder.TestRawContact;
+import android.test.InstrumentationTestCase;
+
+/**
+ * CTS tests for {@link android.provider.ContactsContract.ContactCounts} apis.
+ */
+public class ContactsContract_ContactCountsTest extends InstrumentationTestCase {
+    private ContentResolver mResolver;
+    private ContactsContract_TestDataBuilder mBuilder;
+
+    final String[] TEST_PROJECTION = new String[] {Contacts.DISPLAY_NAME};
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mResolver = getInstrumentation().getTargetContext().getContentResolver();
+        ContentProviderClient provider =
+                mResolver.acquireContentProviderClient(ContactsContract.AUTHORITY);
+        mBuilder = new ContactsContract_TestDataBuilder(provider);
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        super.tearDown();
+        mBuilder.cleanup();
+    }
+
+    public void testContactCounts_noExtraNoExtrasReturned() throws Exception {
+        final String filterString = getFilterString(setupTestData());
+        final Cursor cursor = mResolver.query(Contacts.CONTENT_URI, TEST_PROJECTION,
+                filterString, null, null);
+        try {
+            final Bundle extras = cursor.getExtras();
+            assertFalse(extras.containsKey(Contacts.EXTRA_ADDRESS_BOOK_INDEX_COUNTS));
+            assertFalse(extras.containsKey(Contacts.EXTRA_ADDRESS_BOOK_INDEX_TITLES));
+        } finally {
+            cursor.close();
+        }
+    }
+
+    public void testContactCounts_correctCountsReturned() throws Exception {
+        final String filterString = getFilterString(setupTestData());
+        final Uri uri = Contacts.CONTENT_URI.buildUpon()
+                .appendQueryParameter(Contacts.ADDRESS_BOOK_INDEX_EXTRAS, "true").build();
+        final Cursor cursor = mResolver.query(uri, TEST_PROJECTION,
+                filterString, null, null);
+        try {
+            final Bundle extras = cursor.getExtras();
+            assertTrue(extras.containsKey(Contacts.EXTRA_ADDRESS_BOOK_INDEX_COUNTS));
+            assertTrue(extras.containsKey(Contacts.EXTRA_ADDRESS_BOOK_INDEX_TITLES));
+
+            final String[] expectedSections = new String[] {"A", "B", "C"};
+            final String sections[] =
+                    extras.getStringArray(Contacts.EXTRA_ADDRESS_BOOK_INDEX_TITLES);
+            assertTrue(Arrays.equals(expectedSections, sections));
+
+            final int[] expectedCounts = new int[] {2, 3, 1};
+            final int counts[] = extras.getIntArray(Contacts.EXTRA_ADDRESS_BOOK_INDEX_COUNTS);
+            assertTrue(Arrays.equals(expectedCounts, counts));
+        } finally {
+            cursor.close();
+        }
+    }
+
+    private String getFilterString(long... contactIds) {
+        StringBuilder sb = new StringBuilder();
+        sb.append(Contacts._ID + " in ");
+        sb.append("(");
+        for (int i = 0; i < contactIds.length; i++) {
+            if (i != 0) sb.append(",");
+            sb.append(contactIds[i]);
+        }
+        sb.append(")");
+        return sb.toString();
+    }
+
+    /**
+     * Setup the contacts database with temporary contacts used for testing. These contacts will
+     * be removed during teardown.
+     *
+     * @return An array of long values corresponding to the ids of the created contacts
+     *
+     * @throws Exception
+     */
+    private long[] setupTestData() throws Exception {
+        TestRawContact rawContact = mBuilder.newRawContact()
+                .with(RawContacts.ACCOUNT_TYPE, "test_account")
+                .with(RawContacts.ACCOUNT_NAME, "test_name")
+                .insert();
+        rawContact.newDataRow(StructuredName.CONTENT_ITEM_TYPE)
+                .with(StructuredName.DISPLAY_NAME, "Apple Pie")
+                .insert();
+        rawContact.load();
+        TestContact contact = rawContact.getContact().load();
+
+        TestRawContact rawContact2 = mBuilder.newRawContact()
+                .with(RawContacts.ACCOUNT_TYPE, "test_account")
+                .with(RawContacts.ACCOUNT_NAME, "test_name")
+                .insert();
+        rawContact2.newDataRow(StructuredName.CONTENT_ITEM_TYPE)
+                .with(StructuredName.DISPLAY_NAME, "Banana Split")
+                .insert();
+        rawContact2.load();
+        TestContact contact2 = rawContact2.getContact().load();
+
+        TestRawContact rawContact3 = mBuilder.newRawContact()
+                .with(RawContacts.ACCOUNT_TYPE, "test_account")
+                .with(RawContacts.ACCOUNT_NAME, "test_name")
+                .insert();
+        rawContact3.newDataRow(StructuredName.CONTENT_ITEM_TYPE)
+                .with(StructuredName.DISPLAY_NAME, "Blackberry Shortcake")
+                .insert();
+        rawContact3.load();
+        TestContact contact3 = rawContact3.getContact().load();
+
+        TestRawContact rawContact4 = mBuilder.newRawContact()
+                .with(RawContacts.ACCOUNT_TYPE, "test_account")
+                .with(RawContacts.ACCOUNT_NAME, "test_name")
+                .insert();
+        rawContact4.newDataRow(StructuredName.CONTENT_ITEM_TYPE)
+                .with(StructuredName.DISPLAY_NAME, "Cherry icecream")
+                .insert();
+        rawContact4.load();
+        TestContact contact4 = rawContact4.getContact().load();
+
+        TestRawContact rawContact5 = mBuilder.newRawContact()
+                .with(RawContacts.ACCOUNT_TYPE, "test_account")
+                .with(RawContacts.ACCOUNT_NAME, "test_name")
+                .insert();
+        rawContact5.newDataRow(StructuredName.CONTENT_ITEM_TYPE)
+                .with(StructuredName.DISPLAY_NAME, "Apricot Jam ")
+                .insert();
+        rawContact5.load();
+        TestContact contact5 = rawContact5.getContact().load();
+
+        TestRawContact rawContact6 = mBuilder.newRawContact()
+                .with(RawContacts.ACCOUNT_TYPE, "test_account")
+                .with(RawContacts.ACCOUNT_NAME, "test_name")
+                .insert();
+        rawContact6.newDataRow(StructuredName.CONTENT_ITEM_TYPE)
+                .with(StructuredName.DISPLAY_NAME, "Blackcurrant Pie ")
+                .insert();
+        rawContact6.load();
+        TestContact contact6 = rawContact6.getContact().load();
+
+        return new long[] {contact.getId(), contact2.getId(), contact3.getId(), contact4.getId(),
+                contact5.getId(), contact6.getId()};
+    }
+}
+
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..91436a3 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,60 @@
     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;
+    }
+
+    private TestRawContact[] mRawContacts = new TestRawContact[3];
+
     @Override
     protected void setUp() throws Exception {
         super.setUp();
@@ -135,7 +191,7 @@
             // Contactables Uri should return only email and phone data items.
             DatabaseAsserts.assertStoredValuesInUriMatchExactly(mResolver, contentUri, null,
                     Data.RAW_CONTACT_ID + "=?", new String[] {String.valueOf(rawContact.getId())},
-                    null, cv, cv2);
+                    null, false, cv, cv2);
         } finally {
             // Clean up
             rawContact.delete();
@@ -182,6 +238,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);
@@ -213,6 +300,70 @@
         RawContactUtil.delete(mResolver, ids.mRawContactId, true);
     }
 
+    /**
+     * Tests that specifying the {@link android.provider.ContactsContract#REMOVE_DUPLICATE_ENTRIES}
+     * boolean parameter correctly results in deduped phone numbers.
+     */
+    public void testPhoneQuery_removeDuplicateEntries() throws Exception{
+        long[] ids = setupContactablesTestData();
+
+        // Insert duplicate data entry for raw contact 3. (existing phone number 518-354-1111)
+        mRawContacts[2].newDataRow(Phone.CONTENT_ITEM_TYPE)
+                .with(Phone.DATA, "518-354-1111")
+                .with(Phone.TYPE, Phone.TYPE_HOME)
+                .insert();
+
+        ContentValues dupe = new ContentValues();
+        dupe.put(Contacts.DISPLAY_NAME, "John Doe");
+        dupe.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE);
+        dupe.put(Phone.DATA, "518-354-1111");
+        dupe.put(Phone.TYPE, Phone.TYPE_HOME);
+
+        // Query for all phone numbers in the contacts database (without deduping).
+        // The phone number above should be listed twice, in its duplicated forms.
+        assertCursorStoredValuesWithRawContactsFilter(Phone.CONTENT_URI, ids, sContentValues[1],
+                sContentValues[5], dupe);
+
+        // Now query for all phone numbers in the contacts database but request deduping.
+        // The phone number should now be listed only once.
+        Uri uri = Phone.CONTENT_URI.buildUpon().
+                appendQueryParameter(ContactsContract.REMOVE_DUPLICATE_ENTRIES, "true").build();
+        assertCursorStoredValuesWithRawContactsFilter(uri, ids, sContentValues[1],
+                sContentValues[5]);
+    }
+
+    /**
+     * Tests that specifying the {@link android.provider.ContactsContract#REMOVE_DUPLICATE_ENTRIES}
+     * boolean parameter correctly results in deduped email addresses.
+     */
+    public void testEmailQuery_removeDuplicateEntries() throws Exception{
+        long[] ids = setupContactablesTestData();
+
+        // Insert duplicate data entry for raw contact 3. (existing email doeassociates@deer.com)
+        mRawContacts[2].newDataRow(Email.CONTENT_ITEM_TYPE)
+                .with(Email.DATA, "doeassociates@deer.com")
+                .with(Email.TYPE, Email.TYPE_WORK)
+                .insert();
+
+        ContentValues dupe = new ContentValues();
+        dupe.put(Contacts.DISPLAY_NAME, "John Doe");
+        dupe.put(Data.MIMETYPE, Email.CONTENT_ITEM_TYPE);
+        dupe.put(Email.DATA, "doeassociates@deer.com");
+        dupe.put(Email.TYPE, Email.TYPE_WORK);
+
+        // Query for all email addresses in the contacts database (without deduping).
+        // The email address above should be listed twice, in its duplicated forms.
+        assertCursorStoredValuesWithRawContactsFilter(Email.CONTENT_URI, ids, sContentValues[0],
+                sContentValues[2], sContentValues[3], sContentValues[4], dupe);
+
+        // Now query for all email addresses in the contacts database but request deduping.
+        // The email address should now be listed only once.
+        Uri uri = Email.CONTENT_URI.buildUpon().
+                appendQueryParameter(ContactsContract.REMOVE_DUPLICATE_ENTRIES, "true").build();
+        assertCursorStoredValuesWithRawContactsFilter(uri, ids, sContentValues[0],
+                sContentValues[2], sContentValues[3], sContentValues[4]);
+    }
+
     public void testDataUpdate_updatesContactLastUpdatedTimestamp() {
         DatabaseAsserts.ContactIdPair ids = DatabaseAsserts.assertAndCreateContact(mResolver);
         long dataId = createData(ids.mRawContactId);
@@ -253,7 +404,7 @@
         }
         sb.append(")");
         DatabaseAsserts.assertStoredValuesInUriMatchExactly(mResolver, uri, null, sb.toString(),
-                null, null, expected);
+                null, null, false, expected);
     }
 
 
@@ -277,6 +428,7 @@
                 .with(Phone.DATA, "510-123-5769")
                 .with(Email.TYPE, Phone.TYPE_HOME)
                 .insert();
+        mRawContacts[0] = rawContact;
 
         TestRawContact rawContact2 = mBuilder.newRawContact()
                 .with(RawContacts.ACCOUNT_TYPE, "test_account")
@@ -289,6 +441,14 @@
                 .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();
+        mRawContacts[1] = rawContact2;
 
         TestRawContact rawContact3 = mBuilder.newRawContact()
                 .with(RawContacts.ACCOUNT_TYPE, "test_account")
@@ -308,55 +468,12 @@
         rawContact3.newDataRow(Organization.CONTENT_ITEM_TYPE)
                 .with(Organization.DATA, "Doe Industries")
                 .insert();
+        mRawContacts[2] = rawContact3;
         return new long[] {rawContact.getId(), rawContact2.getId(), rawContact3.getId()};
     }
 
     // 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_FrequentsStrequentsTest.java b/tests/tests/provider/src/android/provider/cts/ContactsContract_FrequentsStrequentsTest.java
new file mode 100644
index 0000000..fcb48cf
--- /dev/null
+++ b/tests/tests/provider/src/android/provider/cts/ContactsContract_FrequentsStrequentsTest.java
@@ -0,0 +1,352 @@
+/*
+ * 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 android.content.ContentProviderClient;
+import android.content.ContentResolver;
+import android.content.ContentUris;
+import android.content.ContentValues;
+import android.net.Uri;
+import android.provider.ContactsContract;
+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.StructuredName;
+import android.provider.ContactsContract.Contacts;
+import android.provider.ContactsContract.DataUsageFeedback;
+import android.provider.ContactsContract.RawContacts;
+import android.provider.cts.ContactsContract_TestDataBuilder.TestContact;
+import android.provider.cts.ContactsContract_TestDataBuilder.TestRawContact;
+import android.provider.cts.contacts.DatabaseAsserts;
+import android.test.InstrumentationTestCase;
+
+import java.util.ArrayList;
+
+/**
+ * CTS tests for {@link android.provider.ContactsContract.Contacts#CONTENT_FREQUENT_URI},
+ * {@link android.provider.ContactsContract.Contacts#CONTENT_STREQUENT_URI} and
+ * {@link android.provider.ContactsContract.Contacts#CONTENT_STREQUENT_FILTER_URI} apis.
+ */
+public class ContactsContract_FrequentsStrequentsTest extends InstrumentationTestCase {
+    private ContentResolver mResolver;
+    private ContactsContract_TestDataBuilder mBuilder;
+
+    public static ContentValues[] sContentValues = new ContentValues[3];
+    static {
+        ContentValues cv1 = new ContentValues();
+        cv1.put(Contacts.DISPLAY_NAME, "Hot Tamale");
+        sContentValues[0] = cv1;
+
+        ContentValues cv2 = new ContentValues();
+        cv2.put(Contacts.DISPLAY_NAME, "Cold Tamago");
+        sContentValues[1] = cv2;
+
+        ContentValues cv3 = new ContentValues();
+        cv3.put(Contacts.DISPLAY_NAME, "John Doe");
+        sContentValues[2] = cv3;
+    }
+
+    private long[] mDataIds = new long[3];
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mResolver = getInstrumentation().getTargetContext().getContentResolver();
+        ContentProviderClient provider =
+                mResolver.acquireContentProviderClient(ContactsContract.AUTHORITY);
+        mBuilder = new ContactsContract_TestDataBuilder(provider);
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        super.tearDown();
+        mBuilder.cleanup();
+    }
+
+    /**
+     * Tests that {@link android.provider.ContactsContract.Contacts#CONTENT_STREQUENT_URI} returns
+     * no contacts if there are no starred or frequent contacts in the user's contacts.
+     */
+    public void testStrequents_noStarredOrFrequents() throws Exception {
+        long[] ids = setupTestData();
+        assertCursorStoredValuesWithContactsFilter(Contacts.CONTENT_STREQUENT_URI, ids, false);
+    }
+
+    /**
+     * Tests that {@link android.provider.ContactsContract.Contacts#CONTENT_STREQUENT_URI} returns
+     * starred contacts in the correct order if there are only starred contacts in the user's
+     * contacts.
+     */
+    public void testStrequents_starredOnlyInCorrectOrder() throws Exception {
+        long[] ids = setupTestData();
+
+        // Star/favorite the first and third contact.
+        starContact(ids[0]);
+        starContact(ids[1]);
+
+        // Only the starred contacts should be returned, ordered alphabetically by name
+        assertCursorStoredValuesWithContactsFilter(Contacts.CONTENT_STREQUENT_URI, ids,
+                false, sContentValues[1], sContentValues[0]);
+    }
+
+    /**
+     * Tests that {@link android.provider.ContactsContract.Contacts#CONTENT_STREQUENT_URI} returns
+     * frequent contacts in the correct order if there are only frequent contacts in the user's
+     * contacts.
+     */
+    public void testStrequents_frequentsOnlyInCorrectOrder() throws Exception {
+        long[] ids = setupTestData();
+
+        // Contact the first contact once.
+        markDataAsUsed(mDataIds[0], 1);
+
+        // Contact the second contact thrice.
+        markDataAsUsed(mDataIds[1], 3);
+
+        // Contact the third contact twice.
+        markDataAsUsed(mDataIds[2], 2);
+
+        // The strequents uri should now return contact 2, 3, 1 in order due to ranking by
+        // data usage.
+        assertCursorStoredValuesWithContactsFilter(Contacts.CONTENT_STREQUENT_URI, ids,
+                false, sContentValues[1], sContentValues[2], sContentValues[0]);
+    }
+
+    /**
+     * Tests that {@link android.provider.ContactsContract.Contacts#CONTENT_STREQUENT_URI} returns
+     * first starred, then frequent contacts in their respective correct orders if there are both
+     * starred and frequent contacts in the user's contacts.
+     */
+    public void testStrequents_starredAndFrequentsInCorrectOrder() throws Exception {
+        long[] ids = setupTestData();
+
+        // Contact the first contact once.
+        markDataAsUsed(mDataIds[0], 1);
+
+        // Contact the second contact thrice.
+        markDataAsUsed(mDataIds[1], 3);
+
+        // Contact the third contact twice, and mark it as used
+        markDataAsUsed(mDataIds[2], 2);
+        starContact(ids[2]);
+
+        // The strequents uri should now return contact 3, 2, 1 in order. Contact 3 is ranked first
+        // because it is starred, followed by contacts 2 and 1 due to their data usage ranking.
+        // Note that contact 3 is only returned once (as a starred contact) even though it is also
+        // a frequently contacted contact.
+        assertCursorStoredValuesWithContactsFilter(Contacts.CONTENT_STREQUENT_URI, ids,
+                false, sContentValues[2], sContentValues[1], sContentValues[0]);
+    }
+
+    /**
+     * Tests that {@link android.provider.ContactsContract.Contacts#CONTENT_STREQUENT_FILTER_URI}
+     * correctly filters the returned contacts with the given user input.
+     */
+    public void testStrequents_withFilter() throws Exception {
+        long[] ids = setupTestData();
+
+        //Star all 3 contacts
+        starContact(ids[0]);
+        starContact(ids[1]);
+        starContact(ids[2]);
+
+        // Construct a uri that filters for the query string "ta".
+        Uri uri = Contacts.CONTENT_STREQUENT_FILTER_URI.buildUpon().appendEncodedPath("ta").build();
+
+        // Only contact 1 and 2 should be returned (sorted in alphabetical order) due to the
+        // filtered query.
+        assertCursorStoredValuesWithContactsFilter(uri, ids, false, sContentValues[1], sContentValues[0]);
+    }
+
+    public void testStrequents_phoneOnly() throws Exception {
+        long[] ids = setupTestData();
+
+        // Star all 3 contacts
+        starContact(ids[0]);
+        starContact(ids[1]);
+        starContact(ids[2]);
+
+        // Construct a uri for phone only favorites.
+        Uri uri = Contacts.CONTENT_STREQUENT_URI.buildUpon().
+                appendQueryParameter(ContactsContract.STREQUENT_PHONE_ONLY, "true").build();
+
+        // Only the contacts with phone numbers are returned, in alphabetical order. Filtering
+        // is done with data ids instead of contact ids since each row contains a single data item.
+        assertCursorStoredValuesWithContactsFilter(uri, mDataIds, false,
+                sContentValues[0], sContentValues[2]);
+    }
+
+    public void testStrequents_phoneOnlyFrequentsOrder() throws Exception {
+        long[] ids = setupTestData();
+
+        // Contact the first contact once.
+        markDataAsUsed(mDataIds[0], 1);
+
+        // Contact the second contact twice.
+        markDataAsUsed(mDataIds[1], 2);
+
+        // Contact the third contact thrice.
+        markDataAsUsed(mDataIds[2], 3);
+
+        // Construct a uri for phone only favorites.
+        Uri uri = Contacts.CONTENT_STREQUENT_URI.buildUpon().
+                appendQueryParameter(ContactsContract.STREQUENT_PHONE_ONLY, "true").build();
+
+        // Only the contacts with phone numbers are returned, in frequency ranking order.
+        assertCursorStoredValuesWithContactsFilter(uri, mDataIds, false,
+                sContentValues[2], sContentValues[0]);
+    }
+
+    public void testFrequents_noFrequentsReturnsEmptyCursor() throws Exception {
+        long[] ids = setupTestData();
+        assertCursorStoredValuesWithContactsFilter(Contacts.CONTENT_FREQUENT_URI, ids, false);
+    }
+
+    public void testFrequents_CorrectOrder() throws Exception {
+        long[] ids = setupTestData();
+
+        // Contact the first contact once.
+        markDataAsUsed(mDataIds[0], 1);
+
+        // Contact the second contact thrice.
+        markDataAsUsed(mDataIds[1], 3);
+
+        // Contact the third contact twice.
+        markDataAsUsed(mDataIds[2], 2);
+
+        // The frequents uri should now return contact 2, 3, 1 in order due to ranking by
+        // data usage.
+        assertCursorStoredValuesWithContactsFilter(Contacts.CONTENT_FREQUENT_URI, ids,
+                true /* inOrder */, sContentValues[1], sContentValues[2], sContentValues[0]);
+    }
+
+    /**
+     * Given a uri, performs a query on the contacts provider for that uri and asserts that the
+     * cursor returned from the query matches the expected results.
+     *
+     * @param uri Uri to perform the query for
+     * @param contactsId Array of contact IDs that serves as an additional filter on the result
+     * set. This is needed to limit the output to temporary test contacts that were created for
+     * purposes of the test, so that the tests do not fail on devices with existing contacts on
+     * them
+     * @param inOrder Whether or not the returned rows in the cursor should correspond to the
+     * order of the provided ContentValues
+     * @param expected An array of ContentValues corresponding to the expected output of the query
+     */
+    private void assertCursorStoredValuesWithContactsFilter(Uri uri, long[] contactsId,
+            boolean inOrder, ContentValues... expected) {
+        // We need this helper function to add a filter for specific contacts because
+        // otherwise tests will fail if performed on a device with existing contacts data
+        StringBuilder sb = new StringBuilder();
+        sb.append(Contacts._ID + " in ");
+        sb.append("(");
+        for (int i = 0; i < contactsId.length; i++) {
+            if (i != 0) sb.append(",");
+            sb.append(contactsId[i]);
+        }
+        sb.append(")");
+        DatabaseAsserts.assertStoredValuesInUriMatchExactly(mResolver, uri, null, sb.toString(),
+                null, null, inOrder, expected);
+    }
+
+    /**
+     * Given a contact id, update the contact corresponding to that contactId so that it now shows
+     * up in the user's favorites/starred contacts.
+     *
+     * @param contactId Contact ID corresponding to the contact to star
+     */
+    private void starContact(long contactId) {
+        ContentValues values = new ContentValues();
+        values.put(Contacts.STARRED, 1);
+        mResolver.update(ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId), values,
+                null, null);
+    }
+
+    /**
+     * Given a data id, increment the data usage stats by a given number of usages to simulate
+     * the user making a call to the given data item.
+     *
+     * @param dataId Id of the data item to increment data usage stats for
+     * @param numTimes The number of times to increase the data usage stats by
+     */
+    private void markDataAsUsed(long dataId, int numTimes) {
+        Uri uri = ContactsContract.DataUsageFeedback.FEEDBACK_URI.buildUpon().
+                appendPath(String.valueOf(dataId)).
+                appendQueryParameter(DataUsageFeedback.USAGE_TYPE,
+                        DataUsageFeedback.USAGE_TYPE_CALL).build();
+        for (int i = 1; i <= numTimes; i++) {
+            mResolver.update(uri, new ContentValues(), null, null);
+        }
+    }
+
+    /**
+     * Setup the contacts database with temporary contacts used for testing. These contacts will
+     * be removed during teardown.
+     *
+     * @return An array of long values corresponding to the ids of the created contacts
+     *
+     * @throws Exception
+     */
+    private long[] setupTestData() throws Exception {
+        TestRawContact rawContact = mBuilder.newRawContact()
+                .with(RawContacts.ACCOUNT_TYPE, "test_account")
+                .with(RawContacts.ACCOUNT_NAME, "test_name")
+                .insert();
+        rawContact.newDataRow(StructuredName.CONTENT_ITEM_TYPE)
+                .with(StructuredName.DISPLAY_NAME, "Hot Tamale")
+                .insert();
+        mDataIds[0] = rawContact.newDataRow(Phone.CONTENT_ITEM_TYPE)
+                .with(Phone.DATA, "510-123-5769")
+                .with(Email.TYPE, Phone.TYPE_HOME)
+                .insert().load().getId();
+        rawContact.load();
+        TestContact contact = rawContact.getContact().load();
+
+        TestRawContact rawContact2 = mBuilder.newRawContact()
+                .with(RawContacts.ACCOUNT_TYPE, "test_account")
+                .with(RawContacts.ACCOUNT_NAME, "test_name")
+                .insert();
+        rawContact2.newDataRow(StructuredName.CONTENT_ITEM_TYPE)
+                .with(StructuredName.DISPLAY_NAME, "Cold Tamago")
+                .insert();
+        mDataIds[1] = rawContact2.newDataRow(Email.CONTENT_ITEM_TYPE)
+                .with(Email.DATA, "eggs@farmers.org")
+                .with(Email.TYPE, Email.TYPE_HOME)
+                .insert().load().getId();
+        rawContact2.load();
+        TestContact contact2 = rawContact2.getContact().load();
+
+        TestRawContact rawContact3 = mBuilder.newRawContact()
+                .with(RawContacts.ACCOUNT_TYPE, "test_account")
+                .with(RawContacts.ACCOUNT_NAME, "test_name")
+                .insert();
+        rawContact3.newDataRow(StructuredName.CONTENT_ITEM_TYPE)
+                .with(StructuredName.DISPLAY_NAME, "John Doe")
+                .insert();
+        mDataIds[2] = rawContact3.newDataRow(Phone.CONTENT_ITEM_TYPE)
+                .with(Phone.DATA, "518-354-1111")
+                .with(Phone.TYPE, Phone.TYPE_HOME)
+                .insert().load().getId();
+        rawContact3.load();
+        TestContact contact3 = rawContact3.getContact().load();
+
+        return new long[] {contact.getId(), contact2.getId(), contact3.getId()};
+    }
+}
+
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..2558c36
--- /dev/null
+++ b/tests/tests/provider/src/android/provider/cts/ContactsContract_PinnedPositionsTest.java
@@ -0,0 +1,407 @@
+/*
+ * 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.ContentProviderOperation;
+import android.content.ContentResolver;
+import android.content.ContentUris;
+import android.content.ContentValues;
+import android.content.OperationApplicationException;
+import android.net.Uri;
+import android.os.RemoteException;
+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;
+import android.util.Log;
+
+import java.util.ArrayList;
+
+/**
+ * CTS tests for {@link android.provider.ContactsContract.PinnedPositions} API
+ */
+public class ContactsContract_PinnedPositionsTest extends AndroidTestCase {
+    private static final String TAG = "ContactsContract_PinnedPositionsTest";
+
+    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 testPinnedPositionsUpdate() {
+        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 ArrayList<ContentProviderOperation> operations =
+                new ArrayList<ContentProviderOperation>();
+        operations.add(newPinningOperation(i1.mContactId, 1, true));
+        operations.add(newPinningOperation(i3.mContactId, 3, true));
+        operations.add(newPinningOperation(i4.mContactId, 2, false));
+        applyBatch(mResolver, operations);
+
+        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, 0));
+
+        // 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));
+
+        operations.clear();
+
+        // Now unpin the contact
+        operations.add(newPinningOperation(i3.mContactId, unpinned, false));
+        applyBatch(mResolver, operations);
+
+        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, 0));
+
+        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, 0));
+
+        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 ArrayList<ContentProviderOperation> operations =
+                new ArrayList<ContentProviderOperation>();
+
+        operations.add(newPinningOperation(i1.mContactId, 1, true));
+        operations.add(newPinningOperation(i2.mContactId, 2, true));
+        operations.add(newPinningOperation(i3.mContactId, 3, true));
+        operations.add(newPinningOperation(i5.mContactId, 5, true));
+        operations.add(newPinningOperation(i6.mContactId, 6, true));
+
+        applyBatch(mResolver, operations);
+
+        // 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.
+        operations.clear();
+        operations.add(newPinningOperation(i5.mContactId, PinnedPositions.DEMOTED, false));
+        applyBatch(mResolver, operations);
+
+        // 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 calling {@link PinnedPositions#UNDEMOTE_METHOD} with an illegal argument correctly
+     * throws an IllegalArgumentException.
+     */
+    public void testPinnedPositionsDemoteIllegalArguments() {
+        try {
+            mResolver.call(ContactsContract.AUTHORITY_URI, PinnedPositions.UNDEMOTE_METHOD,
+                    null, null);
+            fail();
+        } catch (IllegalArgumentException expected) {
+        }
+
+        try {
+            mResolver.call(ContactsContract.AUTHORITY_URI, PinnedPositions.UNDEMOTE_METHOD,
+                    "1.1", null);
+            fail();
+        } catch (IllegalArgumentException expected) {
+        }
+
+        try {
+            mResolver.call(ContactsContract.AUTHORITY_URI, PinnedPositions.UNDEMOTE_METHOD,
+                    "NotANumber", null);
+            fail();
+        } catch (IllegalArgumentException expected) {
+        }
+
+        // Valid contact ID that does not correspond to an actual contact is silently ignored
+        mResolver.call(ContactsContract.AUTHORITY_URI, PinnedPositions.UNDEMOTE_METHOD, "999",
+                null);
+    }
+
+    /**
+     * 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);
+
+        // Pin contact 1 and demote contact 2
+        final ArrayList<ContentProviderOperation> operations =
+                new ArrayList<ContentProviderOperation>();
+        operations.add(newPinningOperation(i1.mContactId, 1, true));
+        operations.add(newPinningOperation(i2.mContactId, PinnedPositions.DEMOTED, false));
+        applyBatch(mResolver, operations);
+
+        assertValuesForContact(i1.mContactId,
+                newContentValues(Contacts.PINNED, 1, Contacts.STARRED, 1));
+        assertValuesForContact(i2.mContactId,
+                newContentValues(Contacts.PINNED, PinnedPositions.DEMOTED, Contacts.STARRED, 0));
+
+        assertValuesForRawContact(i1.mRawContactId,
+                newContentValues(RawContacts.PINNED, 1, RawContacts.STARRED, 1));
+        assertValuesForRawContact(i2.mRawContactId,
+                newContentValues(RawContacts.PINNED, PinnedPositions.DEMOTED, RawContacts.STARRED, 0));
+
+        // Now undemote both contacts.
+        PinnedPositions.undemote(mResolver, i1.mContactId);
+        PinnedPositions.undemote(mResolver, i2.mContactId);
+
+        // Contact 1 remains pinned at 0, while contact 2 becomes unpinned.
+        assertValuesForContact(i1.mContactId,
+                newContentValues(Contacts.PINNED, 1, Contacts.STARRED, 1));
+        assertValuesForContact(i2.mContactId,
+                newContentValues(Contacts.PINNED, PinnedPositions.UNPINNED, Contacts.STARRED, 0));
+
+        assertValuesForRawContact(i1.mRawContactId,
+                newContentValues(RawContacts.PINNED, 1, 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);
+    }
+
+    private ContentProviderOperation newPinningOperation(long id, int pinned, boolean star) {
+        final Uri uri = Uri.withAppendedPath(Contacts.CONTENT_URI, String.valueOf(id));
+        final ContentValues values = new ContentValues();
+        values.put(Contacts.PINNED, pinned);
+        values.put(Contacts.STARRED, star ? 1 : 0);
+        return ContentProviderOperation.newUpdate(uri).withValues(values).build();
+    }
+
+    private static void applyBatch(ContentResolver resolver,
+            ArrayList<ContentProviderOperation> operations) {
+        try {
+            resolver.applyBatch(ContactsContract.AUTHORITY, operations);
+        } catch (OperationApplicationException e) {
+            Log.wtf(TAG, "ContentResolver batch operation failed.");
+        } catch (RemoteException e) {
+            Log.wtf(TAG, "Remote exception when performing batch operation.");
+        }
+    }
+}
+
diff --git a/tests/tests/provider/src/android/provider/cts/ContactsContract_SearchSnippetsTest.java b/tests/tests/provider/src/android/provider/cts/ContactsContract_SearchSnippetsTest.java
new file mode 100644
index 0000000..847e357
--- /dev/null
+++ b/tests/tests/provider/src/android/provider/cts/ContactsContract_SearchSnippetsTest.java
@@ -0,0 +1,272 @@
+/*
+ * 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.provider.cts;
+
+
+import android.content.ContentProviderClient;
+import android.content.ContentResolver;
+import android.content.ContentValues;
+import android.net.Uri;
+import android.provider.ContactsContract;
+import android.provider.ContactsContract.CommonDataKinds.Email;
+import android.provider.ContactsContract.CommonDataKinds.Note;
+import android.provider.ContactsContract.CommonDataKinds.Phone;
+import android.provider.ContactsContract.CommonDataKinds.StructuredName;
+import android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
+import android.provider.ContactsContract.Contacts;
+import android.provider.ContactsContract.RawContacts;
+import android.provider.ContactsContract.SearchSnippets;
+import android.provider.cts.ContactsContract_TestDataBuilder.TestContact;
+import android.provider.cts.ContactsContract_TestDataBuilder.TestRawContact;
+import android.provider.cts.contacts.DatabaseAsserts;
+import android.test.InstrumentationTestCase;
+
+/**
+ * CTS tests for {@link android.provider.ContactsContract.SearchSnippets} APIs.
+ */
+public class ContactsContract_SearchSnippetsTest extends InstrumentationTestCase {
+    private ContentResolver mResolver;
+    private ContactsContract_TestDataBuilder mBuilder;
+
+    public static String[] TEST_PROJECTION = new String[] {
+        Contacts._ID,
+        SearchSnippets.SNIPPET
+    };
+
+    public static ContentValues[] sContentValues = new ContentValues[3];
+    static {
+        ContentValues cv1 = new ContentValues();
+        cv1.put(Contacts.DISPLAY_NAME, "Hot Tamale");
+        sContentValues[0] = cv1;
+
+        ContentValues cv2 = new ContentValues();
+        cv2.put(Contacts.DISPLAY_NAME, "Cold Tamago");
+        sContentValues[1] = cv2;
+
+        ContentValues cv3 = new ContentValues();
+        cv3.put(Contacts.DISPLAY_NAME, "John Doe");
+        sContentValues[2] = cv3;
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mResolver = getInstrumentation().getTargetContext().getContentResolver();
+        ContentProviderClient provider =
+                mResolver.acquireContentProviderClient(ContactsContract.AUTHORITY);
+        mBuilder = new ContactsContract_TestDataBuilder(provider);
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        super.tearDown();
+        mBuilder.cleanup();
+    }
+
+    public void testSearchSnippets_NoMatch() throws Exception {
+        long[] ids = setupTestData();
+        final Uri uri = ContactsContract.Contacts.CONTENT_FILTER_URI.buildUpon()
+                .appendPath("nomatch").build();
+
+        assertCursorStoredValuesWithContactsFilter(uri, ids);
+    }
+
+    public void testSearchSnippets_MatchEmailAddressCorrectSnippet() throws Exception {
+        long[] ids = setupTestData();
+        final Uri uri = ContactsContract.Contacts.CONTENT_FILTER_URI.buildUpon()
+                .appendPath("farm").build();
+
+        final ContentValues expected = new ContentValues();
+        expected.put(Contacts._ID, ids[1]);
+        expected.put(SearchSnippets.SNIPPET, "eggs@[farmers].org");
+        assertCursorStoredValuesWithContactsFilter(uri, ids, expected);
+    }
+
+    public void testSearchSnippets_MatchPhoneNumberCorrectSnippet() throws Exception {
+        long[] ids = setupTestData();
+        final Uri uri = ContactsContract.Contacts.CONTENT_FILTER_URI.buildUpon()
+                .appendPath("510").build();
+
+        final ContentValues expected = new ContentValues();
+        expected.put(Contacts._ID, ids[0]);
+        expected.put(SearchSnippets.SNIPPET, "[510-123-5769]");
+        assertCursorStoredValuesWithContactsFilter(uri, ids, expected);
+    }
+
+    public void testSearchSnippets_MatchPostalAddressCorrectSnippet() throws Exception {
+        long[] ids = setupTestData();
+        final Uri uri = ContactsContract.Contacts.CONTENT_FILTER_URI.buildUpon()
+                .appendPath("street").build();
+
+        final ContentValues expected = new ContentValues();
+        expected.put(Contacts._ID, ids[2]);
+        expected.put(SearchSnippets.SNIPPET, "123 Main [Street] Unit 3113\u2026");
+        assertCursorStoredValuesWithContactsFilter(uri, ids, expected);
+    }
+
+    public void testSearchSnippets_LongMatchTruncation() throws Exception {
+        long[] ids = setupTestData();
+        final Uri uri = ContactsContract.Contacts.CONTENT_FILTER_URI.buildUpon()
+                .appendPath("over").build();
+
+        final ContentValues expected = new ContentValues();
+        expected.put(Contacts._ID, ids[2]);
+        expected.put(SearchSnippets.SNIPPET, "\u2026dog jumps [over] the quick\u2026");
+        assertCursorStoredValuesWithContactsFilter(uri, ids, expected);
+    }
+
+    public void testSearchSnippets_MultipleMatchesCorrectSnippet() throws Exception {
+        long[] ids = setupTestData();
+        final Uri uri = ContactsContract.Contacts.CONTENT_FILTER_URI.buildUpon()
+                .appendPath("123").build();
+
+        final ContentValues expected = new ContentValues();
+        expected.put(Contacts._ID, ids[1]);
+        expected.put(SearchSnippets.SNIPPET, "[123-456-7890]");
+
+        final ContentValues expected2 = new ContentValues();
+        expected2.put(Contacts._ID, ids[2]);
+        expected2.put(SearchSnippets.SNIPPET, "[123] Main Street Unit 3113\u2026");
+
+        assertCursorStoredValuesWithContactsFilter(uri, ids, expected, expected2);
+    }
+
+    /**
+     * Tests that if deferred snippeting is indicated, the provider will not perform formatting
+     * of the snippet for a single word query.
+     */
+    public void testSearchSnippets_DeferredSnippetingSingleWordQuerySnippetDeferred() throws
+            Exception {
+        long[] ids = setupTestData();
+        final Uri uri = ContactsContract.Contacts.CONTENT_FILTER_URI.buildUpon()
+                .appendQueryParameter(SearchSnippets.DEFERRED_SNIPPETING_KEY, "1")
+                .appendPath("510").build();
+
+        final ContentValues expected = new ContentValues();
+        expected.put(Contacts._ID, ids[0]);
+        expected.put(SearchSnippets.SNIPPET, "510-123-5769");
+        assertCursorStoredValuesWithContactsFilter(uri, ids, expected);
+    }
+
+    /**
+     * Tests that even if deferred snippeting is indicated, a multi-word query will have formatting
+     * of the snippet done by the provider using SQLite
+     */
+    public void testSearchSnippets_DeferredSnippetingMultiWordQuerySnippetNotDeferred() throws
+            Exception {
+        long[] ids = setupTestData();
+        final Uri uri = ContactsContract.Contacts.CONTENT_FILTER_URI.buildUpon()
+                .appendQueryParameter(SearchSnippets.DEFERRED_SNIPPETING_KEY, "1")
+                .appendPath("jumps over").build();
+
+        final ContentValues expected = new ContentValues();
+        expected.put(Contacts._ID, ids[2]);
+        expected.put(SearchSnippets.SNIPPET, "\u2026lazy dog [jumps] [over] the\u2026");
+        assertCursorStoredValuesWithContactsFilter(uri, ids, expected);
+    }
+
+    /**
+     * Given a uri, performs a query on the contacts provider for that uri and asserts that the
+     * cursor returned from the query matches the expected results.
+     *
+     * @param uri Uri to perform the query for
+     * @param contactsId Array of contact IDs that serves as an additional filter on the result
+     * set. This is needed to limit the output to temporary test contacts that were created for
+     * purposes of the test, so that the tests do not fail on devices with existing contacts on
+     * them
+     * @param expected An array of ContentValues corresponding to the expected output of the query
+     */
+    private void assertCursorStoredValuesWithContactsFilter(Uri uri, long[] contactsId,
+            ContentValues... expected) {
+        // We need this helper function to add a filter for specific contacts because
+        // otherwise tests will fail if performed on a device with existing contacts data
+        StringBuilder sb = new StringBuilder();
+        sb.append(Contacts._ID + " in ");
+        sb.append("(");
+        for (int i = 0; i < contactsId.length; i++) {
+            if (i != 0) sb.append(",");
+            sb.append(contactsId[i]);
+        }
+        sb.append(")");
+        DatabaseAsserts.assertStoredValuesInUriMatchExactly(mResolver, uri, TEST_PROJECTION,
+                sb.toString(), null, null, false, expected);
+    }
+
+    /**
+     * Setup the contacts database with temporary contacts used for testing. These contacts will be
+     * removed during teardown.
+     *
+     * @return An array of long values corresponding to the ids of the created contacts
+     * @throws Exception
+     */
+    private long[] setupTestData() throws Exception {
+        TestRawContact rawContact = mBuilder.newRawContact()
+                .with(RawContacts.ACCOUNT_TYPE, "test_account")
+                .with(RawContacts.ACCOUNT_NAME, "test_name")
+                .insert();
+        rawContact.newDataRow(StructuredName.CONTENT_ITEM_TYPE)
+                .with(StructuredName.DISPLAY_NAME, "Hot Tamale")
+                .insert();
+        rawContact.newDataRow(Phone.CONTENT_ITEM_TYPE)
+                .with(Phone.DATA, "510-123-5769")
+                .with(Email.TYPE, Phone.TYPE_HOME)
+                .insert().load().getId();
+        rawContact.load();
+        TestContact contact = rawContact.getContact().load();
+
+        TestRawContact rawContact2 = mBuilder.newRawContact()
+                .with(RawContacts.ACCOUNT_TYPE, "test_account")
+                .with(RawContacts.ACCOUNT_NAME, "test_name")
+                .insert();
+        rawContact2.newDataRow(StructuredName.CONTENT_ITEM_TYPE)
+                .with(StructuredName.DISPLAY_NAME, "Cold Tamago")
+                .insert();
+        rawContact2.newDataRow(Email.CONTENT_ITEM_TYPE)
+                .with(Email.DATA, "eggs@farmers.org")
+                .with(Email.TYPE, Email.TYPE_HOME)
+                .insert().load();
+        rawContact2.newDataRow(Phone.CONTENT_ITEM_TYPE)
+        .with(Phone.DATA, "123-456-7890")
+        .with(Phone.TYPE, Phone.TYPE_OTHER)
+        .insert().load();
+
+        rawContact2.load();
+        TestContact contact2 = rawContact2.getContact().load();
+
+        TestRawContact rawContact3 = mBuilder.newRawContact()
+                .with(RawContacts.ACCOUNT_TYPE, "test_account")
+                .with(RawContacts.ACCOUNT_NAME, "test_name")
+                .insert();
+        rawContact3.newDataRow(StructuredName.CONTENT_ITEM_TYPE)
+                .with(StructuredName.DISPLAY_NAME, "John Doe")
+                .insert();
+        rawContact3.newDataRow(StructuredPostal.CONTENT_ITEM_TYPE)
+                .with(StructuredPostal.DATA, "123 Main Street Unit 3113")
+                .with(StructuredPostal.TYPE, StructuredPostal.TYPE_HOME)
+                .insert().load().getId();
+        rawContact3.newDataRow(Note.CONTENT_ITEM_TYPE)
+                .with(Note.NOTE, "The lazy dog jumps over the quick brown fox.")
+                .insert().load();
+        rawContact3.load();
+        TestContact contact3 = rawContact3.getContact().load();
+
+        return new long[] {
+                contact.getId(), contact2.getId(), contact3.getId()
+        };
+    }
+}
+
diff --git a/tests/tests/provider/src/android/provider/cts/ContactsContract_TestDataBuilder.java b/tests/tests/provider/src/android/provider/cts/ContactsContract_TestDataBuilder.java
index 43c249e..471f895 100644
--- a/tests/tests/provider/src/android/provider/cts/ContactsContract_TestDataBuilder.java
+++ b/tests/tests/provider/src/android/provider/cts/ContactsContract_TestDataBuilder.java
@@ -334,6 +334,10 @@
             return getLong(Data.RAW_CONTACT_ID);
         }
 
+        public long getId() {
+            return getLong(Data._ID);
+        }
+
         public TestRawContact getRawContact() throws Exception {
             return mRawContact;
         }
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/MediaStore_FilesTest.java b/tests/tests/provider/src/android/provider/cts/MediaStore_FilesTest.java
index a362df3..356fe3c 100644
--- a/tests/tests/provider/src/android/provider/cts/MediaStore_FilesTest.java
+++ b/tests/tests/provider/src/android/provider/cts/MediaStore_FilesTest.java
@@ -26,14 +26,21 @@
 import android.net.Uri;
 import android.os.Environment;
 import android.os.ParcelFileDescriptor;
+import android.os.SystemClock;
 import android.provider.MediaStore;
 import android.provider.MediaStore.MediaColumns;
 import android.test.AndroidTestCase;
+import android.util.Log;
 
 import java.io.File;
 import java.io.FileNotFoundException;
 import java.io.FileOutputStream;
+import java.io.FilenameFilter;
 import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
 
 public class MediaStore_FilesTest extends AndroidTestCase {
 
@@ -43,6 +50,40 @@
     protected void setUp() throws Exception {
         super.setUp();
         mResolver = mContext.getContentResolver();
+        cleanup();
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        super.tearDown();
+        cleanup();
+    }
+
+    void cleanup() {
+        final String testName = getClass().getCanonicalName();
+        mResolver.delete(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
+                "_data LIKE ?1", new String[] {"%" + testName + "%"});
+        File ext = Environment.getExternalStorageDirectory();
+        File[] junk = ext.listFiles(new FilenameFilter() {
+
+            @Override
+            public boolean accept(File dir, String filename) {
+                return filename.contains(testName);
+            }
+        });
+        for (File f: junk) {
+            deleteAll(f);
+        }
+    }
+
+    void deleteAll(File f) {
+        if (f.isDirectory()) {
+            File [] sub = f.listFiles();
+            for (File s: sub) {
+                deleteAll(s);
+            }
+        }
+        f.delete();
     }
 
     public void testGetContentUri() {
@@ -153,6 +194,15 @@
         }
     }
 
+    String realPathFor(ParcelFileDescriptor pfd) {
+        File real = new File("/proc/self/fd/" + pfd.getFd());
+        try {
+            return real.getCanonicalPath();
+        } catch (IOException e) {
+            return null;
+        }
+    }
+
     public void testAccess() throws IOException {
         // clean up from previous run
         mResolver.delete(MediaStore.Images.Media.INTERNAL_CONTENT_URI,
@@ -255,19 +305,19 @@
 
                 // get the real path from the file descriptor (this relies on the media provider
                 // having opened the path via the real path instead of the emulated path).
-                File real = new File("/proc/self/fd/" + pfd.getFd());
                 values = new ContentValues();
-                values.put("_data", real.getCanonicalPath());
+                values.put("_data", realPathFor(pfd));
                 mResolver.update(uri, values, null, null);
                 pfd.close();
 
                 // we shouldn't be able to access this
                 try {
                     pfd = mResolver.openFileDescriptor(uri, "r");
-                    pfd.close();
-                    fail("shouldn't be here");
+                    fail("shouldn't have fd for " + realPathFor(pfd));
                 } catch (FileNotFoundException e) {
                     // expected
+                } finally {
+                    pfd.close();
                 }
             } catch (FileNotFoundException e) {
                 fail("couldn't open file");
@@ -279,6 +329,165 @@
         if (sdfile != null) {
             assertEquals(true, sdfile.delete());
         }
+
+        // test secondary storage if present
+        List<File> allpaths = getSecondaryPackageSpecificPaths(mContext);
+        List<String> trimmedPaths = new ArrayList<String>();
+
+        for (File extpath: allpaths) {
+            assertNotNull("Valid media must be inserted during CTS", extpath);
+            assertEquals("Valid media must be inserted for " + extpath
+                    + " during CTS", Environment.MEDIA_MOUNTED,
+                    Environment.getStorageState(extpath));
+
+            File child = extpath;
+            while (true) {
+                File parent = child.getParentFile();
+                if (parent == null) {
+                    fail("didn't expect to be here");
+                }
+                if (!Environment.MEDIA_MOUNTED.equals(Environment.getStorageState(parent))) {
+                    // we went past the root
+                    String abspath = child.getAbsolutePath();
+                    if (!trimmedPaths.contains(abspath)) {
+                        trimmedPaths.add(abspath);
+                    }
+                    break;
+                }
+                child = parent;
+            }
+        }
+
+        String fileDir = Environment.getExternalStorageDirectory() +
+                "/" + getClass().getCanonicalName() + "-" + SystemClock.elapsedRealtime();
+        String fileName = fileDir + "/TestSecondary.Mp3";
+        writeFile(R.raw.testmp3_2, fileName); // file without album art
+
+
+        // insert temp file
+        values = new ContentValues();
+        values.put(MediaStore.Audio.Media.DATA, fileName);
+        values.put(MediaStore.Audio.Media.ARTIST, "Artist-" + SystemClock.elapsedRealtime());
+        values.put(MediaStore.Audio.Media.ALBUM, "Album-" + SystemClock.elapsedRealtime());
+        values.put(MediaStore.Audio.Media.MIME_TYPE, "audio/mp3");
+        Uri fileUri = mResolver.insert(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, values);
+        // give media provider some time to realize there's no album art
+        //SystemClock.sleep(1000);
+        // get its album id
+        Cursor c = mResolver.query(fileUri, new String[] { MediaStore.Audio.Media.ALBUM_ID},
+                null, null, null);
+        assertTrue(c.moveToFirst());
+        int albumid = c.getInt(0);
+        Uri albumArtUriBase = Uri.parse("content://media/external/audio/albumart");
+        Uri albumArtUri = ContentUris.withAppendedId(albumArtUriBase, albumid);
+        try {
+            pfd = mResolver.openFileDescriptor(albumArtUri, "r");
+            fail("no album art, shouldn't be here. Got: " + realPathFor(pfd));
+        } catch (Exception e) {
+            // expected
+        }
+
+        // replace file with one that has album art
+        writeFile(R.raw.testmp3, fileName); // file with album art
+
+        for (String s: trimmedPaths) {
+            File dir = new File(s + "/foobardir-" + SystemClock.elapsedRealtime());
+            assertFalse("please remove " + dir.getAbsolutePath()
+                    + " before running", dir.exists());
+            File file = new File(dir, "foobar");
+            values = new ContentValues();
+            values.put(MediaStore.Audio.Media.ALBUM_ID, albumid);
+            values.put(MediaStore.Audio.Media.DATA, file.getAbsolutePath());
+            mResolver.insert(albumArtUriBase, values);
+            try {
+                pfd = mResolver.openFileDescriptor(albumArtUri, "r");
+                fail("shouldn't have fd for album " + albumid + ", got " + realPathFor(pfd));
+            } catch (Exception e) {
+                // expected
+            } finally {
+                pfd.close();
+            }
+            assertFalse(dir.getAbsolutePath() + " was created", dir.exists());
+        }
+        mResolver.delete(fileUri, null, null);
+        new File(fileName).delete();
+
+        // try creating files in root
+        for (String s: trimmedPaths) {
+            File dir = new File(s);
+            File file = new File(dir, "foobar.jpg");
+
+            values = new ContentValues();
+            values.put(MediaStore.Files.FileColumns.DATA, file.getAbsolutePath());
+            fileUri = mResolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
+            assertNotNull(fileUri);
+
+            // check that adding the file doesn't cause it to be created
+            assertFalse(file.exists());
+
+            // check if opening the file for write works
+            try {
+                mResolver.openOutputStream(fileUri).close();
+                fail("shouldn't have been able to create output stream");
+            } catch (SecurityException e) {
+                // expected
+            }
+            // check that deleting the file doesn't cause it to be created
+            mResolver.delete(fileUri, null, null);
+            assertFalse(file.exists());
+        }
+
+        // try creating files in new subdir
+        for (String s: trimmedPaths) {
+            File dir = new File(s + "/foobardir");
+            File file = new File(dir, "foobar.jpg");
+
+            values = new ContentValues();
+            values.put(MediaStore.Files.FileColumns.DATA, dir.getAbsolutePath());
+
+            Uri dirUri = mResolver.insert(MediaStore.Files.getContentUri("external"), values);
+            assertNotNull(dirUri);
+
+            values = new ContentValues();
+            values.put(MediaStore.Files.FileColumns.DATA, file.getAbsolutePath());
+            fileUri = mResolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
+            assertNotNull(fileUri);
+
+            // check that adding the file or its folder didn't cause either one to be created
+            assertFalse(dir.exists());
+            assertFalse(file.exists());
+
+            // check if opening the file for write works
+            try {
+                mResolver.openOutputStream(fileUri).close();
+                fail("shouldn't have been able to create output stream");
+            } catch (SecurityException e) {
+                // expected
+            }
+            // check that deleting the file or its folder doesn't cause either one to be created
+            mResolver.delete(fileUri, null, null);
+            assertFalse(dir.exists());
+            assertFalse(file.exists());
+            mResolver.delete(dirUri, null, null);
+            assertFalse(dir.exists());
+            assertFalse(file.exists());
+        }
+    }
+
+    public static List<File> getSecondaryPackageSpecificPaths(Context context) {
+        final List<File> paths = new ArrayList<File>();
+        Collections.addAll(paths, dropFirst(context.getExternalCacheDirs()));
+        Collections.addAll(paths, dropFirst(context.getExternalFilesDirs(null)));
+        Collections.addAll(
+                paths, dropFirst(context.getExternalFilesDirs(Environment.DIRECTORY_PICTURES)));
+        Collections.addAll(paths, dropFirst(context.getObbDirs()));
+        return paths;
+    }
+
+    private static File[] dropFirst(File[] before) {
+        final File[] after = new File[before.length - 1];
+        System.arraycopy(before, 1, after, 0, after.length);
+        return after;
     }
 
     private void writeFile(int resid, String path) throws IOException {
diff --git a/tests/tests/provider/src/android/provider/cts/MediaStore_Images_MediaTest.java b/tests/tests/provider/src/android/provider/cts/MediaStore_Images_MediaTest.java
index 3f28a34..e68286f 100644
--- a/tests/tests/provider/src/android/provider/cts/MediaStore_Images_MediaTest.java
+++ b/tests/tests/provider/src/android/provider/cts/MediaStore_Images_MediaTest.java
@@ -78,13 +78,6 @@
 
         mHelper = new FileCopyHelper(mContext);
         mRowsAdded = new ArrayList<Uri>();
-
-        String campath = Environment.getExternalStorageDirectory() + File.separator +
-                Environment.DIRECTORY_DCIM + File.separator + "Camera";
-        File camfile = new File(campath);
-        if (!camfile.exists()) {
-            assertTrue("failed to create " + campath, camfile.mkdir());
-        }
     }
 
     public void testInsertImageWithImagePath() throws Exception {
diff --git a/tests/tests/provider/src/android/provider/cts/MediaStore_Images_ThumbnailsTest.java b/tests/tests/provider/src/android/provider/cts/MediaStore_Images_ThumbnailsTest.java
index 3819cff..60bf011 100644
--- a/tests/tests/provider/src/android/provider/cts/MediaStore_Images_ThumbnailsTest.java
+++ b/tests/tests/provider/src/android/provider/cts/MediaStore_Images_ThumbnailsTest.java
@@ -69,13 +69,6 @@
 
         mHelper = new FileCopyHelper(mContext);
         mRowsAdded = new ArrayList<Uri>();
-
-        String campath = Environment.getExternalStorageDirectory() + File.separator +
-                Environment.DIRECTORY_DCIM + File.separator + "Camera";
-        File camfile = new File(campath);
-        if (!camfile.exists()) {
-            assertTrue("failed to create " + campath, camfile.mkdir());
-        }
     }
 
     public void testQueryInternalThumbnails() throws Exception {
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..ae3e41a 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));
+    }
+}
\ No newline at end of file
diff --git a/tests/tests/provider/src/android/provider/cts/contacts/DatabaseAsserts.java b/tests/tests/provider/src/android/provider/cts/contacts/DatabaseAsserts.java
index 19b5b13..7546066 100644
--- a/tests/tests/provider/src/android/provider/cts/contacts/DatabaseAsserts.java
+++ b/tests/tests/provider/src/android/provider/cts/contacts/DatabaseAsserts.java
@@ -142,7 +142,7 @@
      */
     public static void assertStoredValuesInUriMatchExactly(ContentResolver resolver, Uri uri,
             ContentValues... expectedValues) {
-        assertStoredValuesInUriMatchExactly(resolver, uri, null, null, null, null, expectedValues);
+        assertStoredValuesInUriMatchExactly(resolver, uri, null, null, null, null, false, expectedValues);
     }
 
     /**
@@ -156,15 +156,21 @@
      * @param selection - Selection string to use for the query.
      * @param selectionArgs - Selection arguments to use for the query.
      * @param sortOrder - Sort order to use for the query.
+     * @param inOrder Whether or not the returned rows in the cursor should correspond to the
+     * order of the provided ContentValues
      * @param expectedValues - Array of {@link ContentValues} which the cursor returned from the
      * query should contain.
      */
-    public static void assertStoredValuesInUriMatchExactly(ContentResolver resolver, Uri uri, String[] projection,
-            String selection, String[] selectionArgs, String sortOrder,
-            ContentValues... expectedValues) {
+    public static void assertStoredValuesInUriMatchExactly(ContentResolver resolver, Uri uri,
+            String[] projection, String selection, String[] selectionArgs, String sortOrder,
+            boolean inOrder, ContentValues... expectedValues) {
         final Cursor cursor = resolver.query(uri, projection, selection, selectionArgs, sortOrder);
         try {
-            assertCursorValuesMatchExactly(cursor, expectedValues);
+            if (inOrder) {
+                assertCursorValuesMatchExactlyInOrder(cursor, expectedValues);
+            } else {
+                assertCursorValuesMatchExactly(cursor, expectedValues);
+            }
         } finally {
             cursor.close();
         }
@@ -205,6 +211,29 @@
         }
     }
 
+    /**
+     * Ensures that the rows in the cursor match the rows in the expected values exactly. Requires
+     * that the rows in the cursor are ordered the same way as those in the expected values.
+     *
+     * @param cursor - Cursor containing the values to check for
+     * @param expectedValues - Array of ContentValues that the cursor should be expected to
+     * contain.
+     */
+    public static void assertCursorValuesMatchExactlyInOrder(Cursor cursor,
+            ContentValues... expectedValues) {
+        Assert.assertEquals("Cursor does not contain the number of expected rows",
+                expectedValues.length, cursor.getCount());
+        StringBuilder message = new StringBuilder();
+
+        cursor.moveToPosition(-1);
+        for (ContentValues v : expectedValues) {
+            cursor.moveToNext();
+            Assert.assertTrue("Expected values can not be found " + v + "," + message.toString(),
+                    equalsWithExpectedValues(cursor, v, message));
+        }
+    }
+
+
     private static boolean equalsWithExpectedValues(Cursor cursor, ContentValues expectedValues,
             StringBuilder msgBuffer) {
         for (String column : expectedValues.keySet()) {
diff --git a/tests/tests/renderscript/Android.mk b/tests/tests/renderscript/Android.mk
index e5ef654..77dc210 100644
--- a/tests/tests/renderscript/Android.mk
+++ b/tests/tests/renderscript/Android.mk
@@ -25,9 +25,6 @@
 # When built, explicitly put it in the data partition.
 LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
 
-# All tests should include android.test.runner.
-LOCAL_JAVA_LIBRARIES := android.test.runner
-
 LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner
 LOCAL_JNI_SHARED_LIBRARIES := libcoremathtestcpp_jni
 
diff --git a/tests/tests/renderscript/AndroidManifest.xml b/tests/tests/renderscript/AndroidManifest.xml
index 49fca1e..2a23090 100644
--- a/tests/tests/renderscript/AndroidManifest.xml
+++ b/tests/tests/renderscript/AndroidManifest.xml
@@ -27,9 +27,12 @@
     </application>
 
     <!-- This is a self-instrumenting test package. -->
-    <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
                      android:targetPackage="com.android.cts.stub"
-                     android:label="CTS tests of Renderscript component"/>
+                     android:label="CTS tests of Renderscript component">
+        <meta-data android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
 
 </manifest>
 
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/AtomicTest.java b/tests/tests/renderscript/src/android/renderscript/cts/AtomicTest.java
new file mode 100644
index 0000000..a0e3b7a
--- /dev/null
+++ b/tests/tests/renderscript/src/android/renderscript/cts/AtomicTest.java
@@ -0,0 +1,391 @@
+/*
+ * 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.renderscript.cts;
+import java.util.Random;
+import android.renderscript.*;
+
+public class AtomicTest extends RSBaseCompute {
+    int[] mSrcData;
+    int[] mReturnData = new int[1];
+    int[] mRefData = new int[1];
+    ScriptC_AtomicTest mScript;
+    Allocation mSrc;
+    Allocation mReturn;
+
+    private void initS(boolean fillData, int sz) {
+        mSrcData = new int[sz * sz];
+        mScript = new ScriptC_AtomicTest(mRS);
+        mSrc = Allocation.createTyped(mRS, Type.createXY(mRS, Element.I32(mRS), sz, sz));
+        mReturn = Allocation.createSized(mRS, Element.I32(mRS), 1);
+        if (fillData) {
+            RSUtils.genRandomInts(0, mSrcData, true, 32);
+            mSrc.copyFrom(mSrcData);
+        }
+    }
+
+    private void initU(boolean fillData, int sz) {
+        mSrcData = new int[sz * sz];
+        mScript = new ScriptC_AtomicTest(mRS);
+        mSrc = Allocation.createTyped(mRS, Type.createXY(mRS, Element.U32(mRS), sz, sz));
+        mReturn = Allocation.createSized(mRS, Element.U32(mRS), 1);
+        if (fillData) {
+            RSUtils.genRandomInts(0, mSrcData, false, 32);
+            mSrc.copyFrom(mSrcData);
+        }
+    }
+
+    public void testCas() {
+        initS(true, 1024);
+        mScript.set_gISum(10);
+        mScript.forEach_test_Cas(mSrc);
+        mScript.invoke_getValueS(mReturn);
+        mReturn.copyTo(mReturnData);
+        assertEquals("Incorrect value for Cas ", (10 + 1024*1024), mReturnData[0]);
+    }
+
+    public void testUCas() {
+        initU(true, 1024);
+        mScript.set_gUSum(10);
+        mScript.forEach_test_uCas(mSrc);
+        mScript.invoke_getValueU(mReturn);
+        mReturn.copyTo(mReturnData);
+        assertEquals("Incorrect value for UCas ", (10 + 1024*1024), mReturnData[0]);
+    }
+
+    public void testInc() {
+        initS(true, 1024);
+        mScript.set_gISum(10);
+        mScript.forEach_test_Inc(mSrc);
+        mScript.invoke_getValueS(mReturn);
+        mReturn.copyTo(mReturnData);
+        assertEquals("Incorrect value for Inc ", (10 + 1024*1024), mReturnData[0]);
+    }
+
+    public void testUInc() {
+        initU(true, 1024);
+        mScript.set_gUSum(10);
+        mScript.forEach_test_uInc(mSrc);
+        mScript.invoke_getValueU(mReturn);
+        mReturn.copyTo(mReturnData);
+        assertEquals("Incorrect value for UInc ", (10 + 1024*1024), mReturnData[0]);
+    }
+
+    public void testDec() {
+        initS(true, 1024);
+        mScript.set_gISum(10 + 1024*1024);
+        mScript.forEach_test_Dec(mSrc);
+        mScript.invoke_getValueS(mReturn);
+        mReturn.copyTo(mReturnData);
+        assertEquals("Incorrect value for Dec ", 10, mReturnData[0]);
+    }
+
+    public void testUDec() {
+        initU(true, 1024);
+        mScript.set_gUSum(10 + 1024*1024);
+        mScript.forEach_test_uDec(mSrc);
+        mScript.invoke_getValueU(mReturn);
+        mReturn.copyTo(mReturnData);
+        assertEquals("Incorrect value for UDec ", 10, mReturnData[0]);
+    }
+
+
+    public void testAdd() {
+        initS(true, 1024);
+        mScript.set_gISum(10);
+        mScript.forEach_test_Add(mSrc);
+        mScript.invoke_getValueS(mReturn);
+        mReturn.copyTo(mReturnData);
+
+        int sExpected = 10;
+        for (int i=0; i < mSrcData.length; i++) {
+            sExpected += mSrcData[i];
+        }
+        assertEquals("Incorrect value for Add ", sExpected, mReturnData[0]);
+    }
+
+    public void testUAdd() {
+        initU(true, 1024);
+        mScript.set_gUSum(10);
+        mScript.forEach_test_uAdd(mSrc);
+        mScript.invoke_getValueU(mReturn);
+        mReturn.copyTo(mReturnData);
+
+        int sExpected = 10;
+        for (int i=0; i < mSrcData.length; i++) {
+            sExpected += mSrcData[i];
+        }
+        assertEquals("Incorrect value for UAdd ", sExpected, mReturnData[0]);
+    }
+
+    public void testSub() {
+        initS(true, 1024);
+        mScript.set_gISum(10);
+        mScript.forEach_test_Sub(mSrc);
+        mScript.invoke_getValueS(mReturn);
+        mReturn.copyTo(mReturnData);
+
+        int sExpected = 10;
+        for (int i=0; i < mSrcData.length; i++) {
+            sExpected -= mSrcData[i];
+        }
+        assertEquals("Incorrect value for Sub ", sExpected, mReturnData[0]);
+    }
+
+    public void testUSub() {
+        initU(true, 1024);
+        mScript.set_gUSum(10);
+        mScript.forEach_test_uSub(mSrc);
+        mScript.invoke_getValueU(mReturn);
+        mReturn.copyTo(mReturnData);
+
+        int sExpected = 10;
+        for (int i=0; i < mSrcData.length; i++) {
+            sExpected -= mSrcData[i];
+        }
+        assertEquals("Incorrect value for USub ", sExpected, mReturnData[0]);
+    }
+
+    public void testXor() {
+        initS(true, 1024);
+        mScript.set_gISum(10);
+        mScript.forEach_test_Xor(mSrc);
+        mScript.invoke_getValueS(mReturn);
+        mReturn.copyTo(mReturnData);
+
+        int sExpected = 10;
+        for (int i=0; i < mSrcData.length; i++) {
+            sExpected ^= mSrcData[i];
+        }
+        assertEquals("Incorrect value for Xor ", sExpected, mReturnData[0]);
+    }
+
+    public void testUXor() {
+        initU(true, 1024);
+        mScript.set_gUSum(10);
+        mScript.forEach_test_uXor(mSrc);
+        mScript.invoke_getValueU(mReturn);
+        mReturn.copyTo(mReturnData);
+
+        int sExpected = 10;
+        for (int i=0; i < mSrcData.length; i++) {
+            sExpected ^= mSrcData[i];
+        }
+        assertEquals("Incorrect value for UXor ", sExpected, mReturnData[0]);
+    }
+
+    public void testMin() {
+        for (int i = 0; i < 16; i++) {
+            initS(true, 256);
+
+            mScript.set_gISum(0x7fffffff);
+            mScript.forEach_test_Min(mSrc);
+            mScript.invoke_getValueS(mReturn);
+            mReturn.copyTo(mReturnData);
+
+            mScript.set_gISum(0x7fffffff);
+            mScript.invoke_computeReference_Min(mSrc, mReturn);
+            mReturn.copyTo(mRefData);
+
+            assertEquals("Incorrect value for Min ", mRefData[0], mReturnData[0]);
+        }
+    }
+
+    public void testUMin() {
+        for (int i = 0; i < 16; i++) {
+            initU(true, 256);
+
+            mScript.set_gUSum(0xffffffffL);
+            mScript.forEach_test_uMin(mSrc);
+            mScript.invoke_getValueU(mReturn);
+            mReturn.copyTo(mReturnData);
+
+            mScript.set_gUSum(0xffffffffL);
+            mScript.invoke_computeReference_uMin(mSrc, mReturn);
+            mReturn.copyTo(mRefData);
+
+            assertEquals("Incorrect value for UMin ", mRefData[0], mReturnData[0]);
+        }
+    }
+
+    public void testMax() {
+        for (int i = 0; i < 16; i++) {
+            initS(true, 256);
+
+            mScript.set_gISum(0);
+            mScript.forEach_test_Max(mSrc);
+            mScript.invoke_getValueS(mReturn);
+            mReturn.copyTo(mReturnData);
+
+            mScript.set_gISum(0);
+            mScript.invoke_computeReference_Max(mSrc, mReturn);
+            mReturn.copyTo(mRefData);
+
+            assertEquals("Incorrect value for Min ", mRefData[0], mReturnData[0]);
+        }
+    }
+
+    public void testUMax() {
+        for (int i = 0; i < 16; i++) {
+            initU(true, 256);
+
+            mScript.set_gISum(0);
+            mScript.forEach_test_uMax(mSrc);
+            mScript.invoke_getValueU(mReturn);
+            mReturn.copyTo(mReturnData);
+
+            mScript.set_gISum(0);
+            mScript.invoke_computeReference_uMax(mSrc, mReturn);
+            mReturn.copyTo(mRefData);
+
+            assertEquals("Incorrect value for UMax ", mRefData[0], mReturnData[0]);
+        }
+    }
+
+    public void testAnd() {
+        Random r = new Random(78);
+
+        for (int i = 0; i < 64; i++) {
+            initS(false, 128);
+
+            for (int j = 0; j < mSrcData.length; j++) {
+                mSrcData[j] = ~0;
+            }
+            mSrcData[r.nextInt(mSrcData.length)] = ~0x40000000;
+            mSrcData[r.nextInt(mSrcData.length)] = ~0x10000000;
+            mSrcData[r.nextInt(mSrcData.length)] = ~0x02000000;
+            mSrcData[r.nextInt(mSrcData.length)] = ~0x00c00000;
+            mSrcData[r.nextInt(mSrcData.length)] = ~0x00010000;
+            mSrcData[r.nextInt(mSrcData.length)] = ~0x00080000;
+            mSrcData[r.nextInt(mSrcData.length)] = ~0x00001000;
+            mSrcData[r.nextInt(mSrcData.length)] = ~0x00000200;
+            mSrcData[r.nextInt(mSrcData.length)] = ~0x0000000f;
+            mSrc.copyFrom(mSrcData);
+
+            mScript.set_gISum(0xffffffff);
+            mScript.forEach_test_And(mSrc);
+            mScript.invoke_getValueS(mReturn);
+            mReturn.copyTo(mReturnData);
+
+            int sExpected = 0xffffffff;
+            for (int j = 0; j < mSrcData.length; j++) {
+                sExpected &= mSrcData[j];
+            }
+            assertEquals("Incorrect value for And ", sExpected, mReturnData[0]);
+        }
+    }
+
+    public void testUAnd() {
+        Random r = new Random(78);
+
+        for (int i = 0; i < 64; i++) {
+            initU(false, 128);
+
+            for (int j = 0; j < mSrcData.length; j++) {
+                mSrcData[j] = ~0;
+            }
+            mSrcData[r.nextInt(mSrcData.length)] = ~0x40000000;
+            mSrcData[r.nextInt(mSrcData.length)] = ~0x10000000;
+            mSrcData[r.nextInt(mSrcData.length)] = ~0x02000000;
+            mSrcData[r.nextInt(mSrcData.length)] = ~0x00c00000;
+            mSrcData[r.nextInt(mSrcData.length)] = ~0x00010000;
+            mSrcData[r.nextInt(mSrcData.length)] = ~0x00080000;
+            mSrcData[r.nextInt(mSrcData.length)] = ~0x00001000;
+            mSrcData[r.nextInt(mSrcData.length)] = ~0x00000200;
+            mSrcData[r.nextInt(mSrcData.length)] = ~0x0000000f;
+            mSrc.copyFrom(mSrcData);
+
+            mScript.set_gUSum(0xffffffffL);
+            mScript.forEach_test_uAnd(mSrc);
+            mScript.invoke_getValueU(mReturn);
+            mReturn.copyTo(mReturnData);
+
+            int sExpected = 0xffffffff;
+            for (int j = 0; j < mSrcData.length; j++) {
+                sExpected &= mSrcData[j];
+            }
+            assertEquals("Incorrect value for uAnd ", sExpected, mReturnData[0]);
+        }
+    }
+
+    public void testOr() {
+        Random r = new Random(78);
+
+        for (int i = 0; i < 64; i++) {
+            initS(false, 128);
+
+            for (int j = 0; j < mSrcData.length; j++) {
+                mSrcData[j] = 0;
+            }
+            mSrcData[r.nextInt(mSrcData.length)] = 0x40000000;
+            mSrcData[r.nextInt(mSrcData.length)] = 0x10000000;
+            mSrcData[r.nextInt(mSrcData.length)] = 0x02000000;
+            mSrcData[r.nextInt(mSrcData.length)] = 0x00c00000;
+            mSrcData[r.nextInt(mSrcData.length)] = 0x00010000;
+            mSrcData[r.nextInt(mSrcData.length)] = 0x00080000;
+            mSrcData[r.nextInt(mSrcData.length)] = 0x00001000;
+            mSrcData[r.nextInt(mSrcData.length)] = 0x00000200;
+            mSrcData[r.nextInt(mSrcData.length)] = 0x0000000f;
+            mSrc.copyFrom(mSrcData);
+
+            mScript.set_gISum(0);
+            mScript.forEach_test_Or(mSrc);
+            mScript.invoke_getValueS(mReturn);
+            mReturn.copyTo(mReturnData);
+
+            int sExpected = 0;
+            for (int j = 0; j < mSrcData.length; j++) {
+                sExpected |= mSrcData[j];
+            }
+            assertEquals("Incorrect value for Or ", sExpected, mReturnData[0]);
+        }
+    }
+
+    public void testUOr() {
+        Random r = new Random(78);
+
+        for (int i = 0; i < 64; i++) {
+            initU(false, 128);
+
+            for (int j = 0; j < mSrcData.length; j++) {
+                mSrcData[j] = 0;
+            }
+            mSrcData[r.nextInt(mSrcData.length)] = 0x40000000;
+            mSrcData[r.nextInt(mSrcData.length)] = 0x10000000;
+            mSrcData[r.nextInt(mSrcData.length)] = 0x02000000;
+            mSrcData[r.nextInt(mSrcData.length)] = 0x00c00000;
+            mSrcData[r.nextInt(mSrcData.length)] = 0x00010000;
+            mSrcData[r.nextInt(mSrcData.length)] = 0x00080000;
+            mSrcData[r.nextInt(mSrcData.length)] = 0x00001000;
+            mSrcData[r.nextInt(mSrcData.length)] = 0x00000200;
+            mSrcData[r.nextInt(mSrcData.length)] = 0x0000000f;
+            mSrc.copyFrom(mSrcData);
+
+            mScript.set_gUSum(0);
+            mScript.forEach_test_uOr(mSrc);
+            mScript.invoke_getValueU(mReturn);
+            mReturn.copyTo(mReturnData);
+
+            int sExpected = 0;
+            for (int j = 0; j < mSrcData.length; j++) {
+                sExpected |= mSrcData[j];
+            }
+            assertEquals("Incorrect value for UOr ", sExpected, mReturnData[0]);
+        }
+    }
+
+}
+
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/CoreMathVerifier.java b/tests/tests/renderscript/src/android/renderscript/cts/CoreMathVerifier.java
index 90f47da..74c34e3 100644
--- a/tests/tests/renderscript/src/android/renderscript/cts/CoreMathVerifier.java
+++ b/tests/tests/renderscript/src/android/renderscript/cts/CoreMathVerifier.java
@@ -229,7 +229,9 @@
                                           new Floaty(point2[i], ulpFactor, ulpRelaxedFactor));
             sum.add(Floaty.multiply(diff, diff));
         }
-        return Floaty.sqrt(sum);
+        Floaty d = Floaty.sqrt(sum);
+        d.setMinimumError(ulpFactor, ulpRelaxedFactor);
+        return d;
     }
 
     // Returns the length of the n-dimensional vector.
@@ -239,7 +241,9 @@
             Floaty f = new Floaty(array[i], ulpFactor, ulpRelaxedFactor);
             sum.add(Floaty.multiply(f, f));
         }
-        return Floaty.sqrt(sum);
+        Floaty l = Floaty.sqrt(sum);
+        l.setMinimumError(ulpFactor, ulpRelaxedFactor);
+        return l;
     }
 
     // Normalizes the n-dimensional vector, i.e. makes it length 1.
@@ -255,7 +259,8 @@
     }
 
     // Computes the cross product of two 3D vectors.
-    static private void cross(float[] v1, float[] v2, Floaty[] out) {
+    static private void cross(float[] v1, float[] v2, Floaty[] out, int ulpFactor,
+                              int ulpRelaxedFactor) {
         Floaty a12 = Floaty.multiply(new Floaty(v1[1]), new Floaty(v2[2]));
         Floaty a21 = Floaty.multiply(new Floaty(v1[2]), new Floaty(v2[1]));
         out[0] = Floaty.subtract(a12, a21);
@@ -268,6 +273,14 @@
         if (out.length == 4) {
             out[3] = new Floaty(0f);
         }
+        setMinimumError(out, ulpFactor, ulpRelaxedFactor);
+    }
+
+    // Set a minimum error for every entry of out.
+    static void setMinimumError(Floaty[] out, int ulpFactor, int ulpRelaxedFactor) {
+        for (int i = 0; i < out.length; i++) {
+            out[i].setMinimumError(ulpFactor, ulpRelaxedFactor);
+        }
     }
 
     static public void computeAbs(TestAbs.ArgumentsCharUchar args) {
@@ -364,7 +377,6 @@
                         Math.max(args.inValue, args.inMinValue)), 0, 0);
     }
 
-    /* TODO Not supporting long arguments currently
     static public void computeClamp(TestClamp.ArgumentsLongLongLongLong args) {
         args.out = minI64(args.inMaxValue, maxI64(args.inValue, args.inMinValue));
     }
@@ -372,7 +384,6 @@
     static public void computeClamp(TestClamp.ArgumentsUlongUlongUlongUlong args) {
         args.out = minU64(args.inMaxValue, maxU64(args.inValue, args.inMinValue));
     }
-    */
 
     static public void computeClz(TestClz.ArgumentsCharChar args) {
         int x = args.inValue;
@@ -419,22 +430,18 @@
     static public void computeConvert(TestConvert.ArgumentsCharUint args) {
         args.out = convertCharToUint(args.inV);
     }
-    /* TODO Not supporting long arguments currently
     static public void computeConvert(TestConvert.ArgumentsCharLong args) {
         args.out = convertCharToLong(args.inV);
     }
     static public void computeConvert(TestConvert.ArgumentsCharUlong args) {
         args.out = convertCharToUlong(args.inV);
     }
-    */
     static public void computeConvert(TestConvert.ArgumentsCharFloat args) {
         args.out = new Floaty(convertCharToFloat(args.inV), 0, 0);
     }
-    /* TODO Not supporting double arguments currently
     static public void computeConvert(TestConvert.ArgumentsCharDouble args) {
         args.out = new Floaty(convertCharToDouble(args.inV), 0, 0);
     }
-    */
 
     static public void computeConvert(TestConvert.ArgumentsUcharChar args) {
         args.out = convertUcharToChar(args.inV);
@@ -454,22 +461,18 @@
     static public void computeConvert(TestConvert.ArgumentsUcharUint args) {
         args.out = convertUcharToUint(args.inV);
     }
-    /* TODO Not supporting long arguments currently
     static public void computeConvert(TestConvert.ArgumentsUcharLong args) {
         args.out = convertUcharToLong(args.inV);
     }
     static public void computeConvert(TestConvert.ArgumentsUcharUlong args) {
         args.out = convertUcharToUlong(args.inV);
     }
-    */
     static public void computeConvert(TestConvert.ArgumentsUcharFloat args) {
         args.out = new Floaty(convertUcharToFloat(args.inV), 0, 0);
     }
-    /* TODO Not supporting double arguments currently
     static public void computeConvert(TestConvert.ArgumentsUcharDouble args) {
         args.out = new Floaty(convertUcharToDouble(args.inV), 0, 0);
     }
-    */
 
     static public void computeConvert(TestConvert.ArgumentsShortChar args) {
         args.out = convertShortToChar(args.inV);
@@ -489,22 +492,18 @@
     static public void computeConvert(TestConvert.ArgumentsShortUint args) {
         args.out = convertShortToUint(args.inV);
     }
-    /* TODO Not supporting long arguments currently
     static public void computeConvert(TestConvert.ArgumentsShortLong args) {
         args.out = convertShortToLong(args.inV);
     }
     static public void computeConvert(TestConvert.ArgumentsShortUlong args) {
         args.out = convertShortToUlong(args.inV);
     }
-    */
     static public void computeConvert(TestConvert.ArgumentsShortFloat args) {
         args.out = new Floaty(convertShortToFloat(args.inV), 0, 0);
     }
-    /* TODO Not supporting double arguments currently
     static public void computeConvert(TestConvert.ArgumentsShortDouble args) {
         args.out = new Floaty(convertShortToDouble(args.inV), 0, 0);
     }
-    */
 
     static public void computeConvert(TestConvert.ArgumentsUshortChar args) {
         args.out = convertUshortToChar(args.inV);
@@ -524,22 +523,18 @@
     static public void computeConvert(TestConvert.ArgumentsUshortUint args) {
         args.out = convertUshortToUint(args.inV);
     }
-    /* TODO Not supporting long arguments currently
     static public void computeConvert(TestConvert.ArgumentsUshortLong args) {
         args.out = convertUshortToLong(args.inV);
     }
     static public void computeConvert(TestConvert.ArgumentsUshortUlong args) {
         args.out = convertUshortToUlong(args.inV);
     }
-    */
     static public void computeConvert(TestConvert.ArgumentsUshortFloat args) {
         args.out = new Floaty(convertUshortToFloat(args.inV), 0, 0);
     }
-    /* TODO Not supporting double arguments currently
     static public void computeConvert(TestConvert.ArgumentsUshortDouble args) {
         args.out = new Floaty(convertUshortToDouble(args.inV), 0, 0);
     }
-    */
 
     static public void computeConvert(TestConvert.ArgumentsIntChar args) {
         args.out = convertIntToChar(args.inV);
@@ -559,22 +554,18 @@
     static public void computeConvert(TestConvert.ArgumentsIntUint args) {
         args.out = convertIntToUint(args.inV);
     }
-    /* TODO Not supporting long arguments currently
     static public void computeConvert(TestConvert.ArgumentsIntLong args) {
         args.out = convertIntToLong(args.inV);
     }
     static public void computeConvert(TestConvert.ArgumentsIntUlong args) {
         args.out = convertIntToUlong(args.inV);
     }
-    */
     static public void computeConvert(TestConvert.ArgumentsIntFloat args) {
         args.out = new Floaty(convertIntToFloat(args.inV), 1, 1);
     }
-    /* TODO Not supporting double arguments currently
     static public void computeConvert(TestConvert.ArgumentsIntDouble args) {
         args.out = new Floaty(convertIntToDouble(args.inV), 0, 0);
     }
-    */
 
     static public void computeConvert(TestConvert.ArgumentsUintChar args) {
         args.out = convertUintToChar(args.inV);
@@ -594,24 +585,19 @@
     static public void computeConvert(TestConvert.ArgumentsUintUint args) {
         args.out = convertUintToUint(args.inV);
     }
-    /* TODO Not supporting long arguments currently
     static public void computeConvert(TestConvert.ArgumentsUintLong args) {
         args.out = convertUintToLong(args.inV);
     }
     static public void computeConvert(TestConvert.ArgumentsUintUlong args) {
         args.out = convertUintToUlong(args.inV);
     }
-    */
     static public void computeConvert(TestConvert.ArgumentsUintFloat args) {
         args.out = new Floaty(convertUintToFloat(args.inV), 1, 1);
     }
-    /* TODO Not supporting double arguments currently
     static public void computeConvert(TestConvert.ArgumentsUintDouble args) {
         args.out = new Floaty(convertUintToDouble(args.inV), 0, 0);
     }
-    */
 
-    /* TODO Not supporting long arguments currently
     static public void computeConvert(TestConvert.ArgumentsLongChar args) {
         args.out = convertLongToChar(args.inV);
     }
@@ -673,7 +659,6 @@
     static public void computeConvert(TestConvert.ArgumentsUlongDouble args) {
         args.out = new Floaty(convertUlongToDouble(args.inV), 1, 1);
     }
-    */
 
     static public void computeConvert(TestConvert.ArgumentsFloatChar args) {
         args.out = convertFloatToChar(args.inV);
@@ -693,24 +678,19 @@
     static public void computeConvert(TestConvert.ArgumentsFloatUint args) {
         args.out = convertFloatToUint(args.inV);
     }
-    /* TODO Not supporting long arguments currently
     static public void computeConvert(TestConvert.ArgumentsFloatLong args) {
         args.out = convertFloatToLong(args.inV);
     }
     static public void computeConvert(TestConvert.ArgumentsFloatUlong args) {
         args.out = convertFloatToUlong(args.inV);
     }
-    */
     static public void computeConvert(TestConvert.ArgumentsFloatFloat args) {
         args.out = new Floaty(convertFloatToFloat(args.inV), 0, 0);
     }
-    /* TODO Not supporting double arguments currently
     static public void computeConvert(TestConvert.ArgumentsFloatDouble args) {
         args.out = new Floaty(convertFloatToDouble(args.inV), 0, 0);
     }
-    */
 
-    /* TODO Not supporting double arguments currently
     static public void computeConvert(TestConvert.ArgumentsDoubleChar args) {
         args.out = convertDoubleToChar(args.inV);
     }
@@ -741,7 +721,6 @@
     static public void computeConvert(TestConvert.ArgumentsDoubleDouble args) {
         args.out = new Floaty(convertDoubleToDouble(args.inV), 0, 0);
     }
-    */
 
     static public void computeCopysign(TestCopysign.ArgumentsFloatFloatFloat args) {
         args.out = new Floaty(Math.copySign(args.inX, args.inY), 0, 0);
@@ -756,11 +735,14 @@
     }
 
     static public void computeCospi(TestCospi.ArgumentsFloatFloat args) {
-        args.out = new Floaty(cos(args.in * (float) Math.PI), 4, 128);
+        Floaty ip = new Floaty((float) ((double)args.in * Math.PI), 1, 1);
+        args.out = Floaty.FloatyFromRange(
+            (float) Math.cos(ip.getDoubleMin()),
+            (float) Math.cos(ip.getDoubleMax()), 4, 128);
     }
 
     static public void computeCross(TestCross.ArgumentsFloatNFloatNFloatN args) {
-        cross(args.inLhs, args.inRhs, args.out);
+        cross(args.inLhs, args.inRhs, args.out, 1, 4);
     }
 
     static public void computeDegrees(TestDegrees.ArgumentsFloatFloat args) {
@@ -777,6 +759,7 @@
 
     static public void computeDot(TestDot.ArgumentsFloatFloatFloat args) {
         args.out = new Floaty(args.inLhs * args.inRhs);
+        args.out.setMinimumError(1, 4);
     }
 
     static public void computeDot(TestDot.ArgumentsFloatNFloatNFloat args) {
@@ -787,6 +770,7 @@
             sum.add(Floaty.multiply(a, b));
         }
         args.out = sum;
+        args.out.setMinimumError(1, 4);
     }
 
     static public void computeErf(TestErf.ArgumentsFloatFloat args) {
@@ -847,7 +831,7 @@
     }
 
     static public void computeFdim(TestFdim.ArgumentsFloatFloatFloat args) {
-        args.out = new Floaty(Math.max(0f, args.inA - args.inB), 0, 0);
+        args.out = new Floaty(Math.max(0f, args.inA - args.inB), 0, 1);
     }
 
     static public void computeFloor(TestFloor.ArgumentsFloatFloat args) {
@@ -910,8 +894,20 @@
         args.out = new Floaty(hypot(args.inX, args.inY), 4, 4);
     }
 
-    static public void computeIlogb(TestIlogb.ArgumentsFloatInt args) {
-        args.out = ilogb(args.in);
+    static public String verifyIlogb(TestIlogb.ArgumentsFloatInt args, boolean relaxed) {
+        // Special case when the input is 0.  We accept two different answers.
+        if (args.in == 0.0f) {
+            if (args.out != -Integer.MAX_VALUE && args.out != Integer.MIN_VALUE) {
+                return "Expected " + Integer.toString(-Integer.MAX_VALUE) + " or " +
+                    Integer.toString(Integer.MIN_VALUE);
+            }
+        } else {
+            int result = ilogb(args.in);
+            if (args.out != result) {
+                return "Expected " + Integer.toString(result);
+            }
+        }
+        return null;
     }
 
     static public void computeLdexp(TestLdexp.ArgumentsFloatIntFloat args) {
@@ -960,6 +956,7 @@
 
     static public void computeMad(TestMad.ArgumentsFloatFloatFloatFloat args) {
         args.out = Floaty.add(new Floaty(args.inA * args.inB), new Floaty(args.inC));
+        args.out.setMinimumError(1, 4);
     }
 
     static public void computeMax(TestMax.ArgumentsCharCharChar args) {
@@ -986,7 +983,6 @@
         args.out = maxU32(args.inV1, args.inV2);
     }
 
-    /* TODO enable once precision has been improved.
     static public void computeMax(TestMax.ArgumentsLongLongLong args) {
         args.out = maxI64(args.inV1, args.inV2);
     }
@@ -994,7 +990,6 @@
     static public void computeMax(TestMax.ArgumentsUlongUlongUlong args) {
         args.out = maxU64(args.inV1, args.inV2);
     }
-    */
 
     static public void computeMax(TestMax.ArgumentsFloatFloatFloat args) {
         args.out = new Floaty(Math.max(args.in, args.in1), 0, 0);
@@ -1024,7 +1019,6 @@
         args.out = minU32(args.inV1, args.inV2);
     }
 
-    /* TODO enable once precision has been improved.
     static public void computeMin(TestMin.ArgumentsLongLongLong args) {
         args.out = minI64(args.inV1, args.inV2);
     }
@@ -1032,7 +1026,6 @@
     static public void computeMin(TestMin.ArgumentsUlongUlongUlong args) {
         args.out = minU64(args.inV1, args.inV2);
     }
-    */
 
     static public void computeMin(TestMin.ArgumentsFloatFloatFloat args) {
         args.out = new Floaty(Math.min(args.in, args.in1), 0, 0);
@@ -1043,6 +1036,7 @@
         Floaty stop = new Floaty(args.inStop);
         Floaty diff = Floaty.subtract(stop, start);
         args.out = Floaty.add(start, Floaty.multiply(diff, new Floaty(args.inAmount)));
+        args.out.setMinimumError(1, 4);
     }
 
     static public void computeModf(TestModf.ArgumentsFloatFloatFloat args) {
@@ -1056,6 +1050,84 @@
         args.out = new Floaty(Float.NaN, 0, 0);
     }
 
+    static public void computeNativeAcos(TestNativeAcos.ArgumentsFloatFloat args) {
+        args.out = new Floaty(acos(args.inV), NATIVE_PRECISION, NATIVE_PRECISION);
+    }
+
+    static public void computeNativeAcosh(TestNativeAcosh.ArgumentsFloatFloat args) {
+        args.out = new Floaty(acosh(args.in), NATIVE_PRECISION, NATIVE_PRECISION);
+    }
+
+    static public void computeNativeAcospi(TestNativeAcospi.ArgumentsFloatFloat args) {
+        args.out = new Floaty(acos(args.inV) / (float) Math.PI, NATIVE_PRECISION, NATIVE_PRECISION);
+    }
+
+    static public void computeNativeAsin(TestNativeAsin.ArgumentsFloatFloat args) {
+        args.out = new Floaty(asin(args.inV), NATIVE_PRECISION, NATIVE_PRECISION);
+    }
+
+    static public void computeNativeAsinh(TestNativeAsinh.ArgumentsFloatFloat args) {
+        args.out = new Floaty(asinh(args.in), NATIVE_PRECISION, NATIVE_PRECISION);
+    }
+
+    static public void computeNativeAsinpi(TestNativeAsinpi.ArgumentsFloatFloat args) {
+        args.out = new Floaty(asin(args.inV) / (float) Math.PI, NATIVE_PRECISION, NATIVE_PRECISION);
+    }
+
+    static public void computeNativeAtan(TestNativeAtan.ArgumentsFloatFloat args) {
+        args.out = new Floaty(atan(args.inV), NATIVE_PRECISION, NATIVE_PRECISION);
+    }
+
+    static public void computeNativeAtanh(TestNativeAtanh.ArgumentsFloatFloat args) {
+        args.out = new Floaty(atanh(args.in), NATIVE_PRECISION, NATIVE_PRECISION);
+    }
+
+    static public void computeNativeAtanpi(TestNativeAtanpi.ArgumentsFloatFloat args) {
+        args.out = new Floaty(atan(args.inV) / (float) Math.PI, NATIVE_PRECISION, NATIVE_PRECISION);
+    }
+
+    static public void computeNativeAtan2(TestNativeAtan2.ArgumentsFloatFloatFloat args) {
+        args.out = new Floaty(atan2(args.inY, args.inX), NATIVE_PRECISION, NATIVE_PRECISION);
+    }
+
+    static public void computeNativeAtan2pi(TestNativeAtan2pi.ArgumentsFloatFloatFloat args) {
+        args.out = new Floaty(atan2(args.inY, args.inX) / (float)Math.PI, NATIVE_PRECISION,
+                              NATIVE_PRECISION);
+    }
+
+    static public void computeNativeCbrt(TestNativeCbrt.ArgumentsFloatFloat args) {
+        args.out = new Floaty(cbrt(args.in), NATIVE_PRECISION, NATIVE_PRECISION);
+    }
+
+    static public void computeNativeCos(TestNativeCos.ArgumentsFloatFloat args) {
+        args.out = new Floaty(cos(args.in), NATIVE_PRECISION, NATIVE_PRECISION);
+    }
+
+    static public void computeNativeCosh(TestNativeCosh.ArgumentsFloatFloat args) {
+        args.out = new Floaty(cosh(args.in), NATIVE_PRECISION, NATIVE_PRECISION);
+    }
+
+    static public void computeNativeCospi(TestNativeCospi.ArgumentsFloatFloat args) {
+        Floaty ip = new Floaty((float) ((double)args.in * Math.PI), 1, 1);
+        args.out = Floaty.FloatyFromRange(
+            (float) Math.cos(ip.getDoubleMin()),
+            (float) Math.cos(ip.getDoubleMax()), NATIVE_PRECISION, NATIVE_PRECISION);
+    }
+
+    static public void computeNativeDistance(TestNativeDistance.ArgumentsFloatFloatFloat args) {
+        args.out = distance(new float[]{args.inLhs}, new float[]{args.inRhs}, NATIVE_PRECISION,
+                            NATIVE_PRECISION);
+    }
+
+    static public void computeNativeDistance(TestNativeDistance.ArgumentsFloatNFloatNFloat args) {
+        args.out = distance(args.inLhs, args.inRhs, NATIVE_PRECISION, NATIVE_PRECISION);
+    }
+
+    static public void computeNativeDivide(TestNativeDivide.ArgumentsFloatFloatFloat args) {
+        args.out = Floaty.divide(new Floaty(args.inLhs), new Floaty(args.inRhs));
+        args.out.setMinimumError(NATIVE_PRECISION, NATIVE_PRECISION);
+    }
+
     static public void computeNativeExp(TestNativeExp.ArgumentsFloatFloat args) {
         // TODO we would like to use NATIVE_PRECISION, NATIVE_PRECISION
         args.out = new Floaty(exp(args.inV), 9500, 9500);
@@ -1071,6 +1143,14 @@
         args.out = new Floaty(exp2(args.inV), 13000, 13000);
     }
 
+    static public void computeNativeExpm1(TestNativeExpm1.ArgumentsFloatFloat args) {
+        args.out = new Floaty(expm1(args.in), NATIVE_PRECISION, NATIVE_PRECISION);
+    }
+
+    static public void computeNativeHypot(TestNativeHypot.ArgumentsFloatFloatFloat args) {
+        args.out = new Floaty(hypot(args.inX, args.inY), NATIVE_PRECISION, NATIVE_PRECISION);
+    }
+
     static public void computeNativeLog(TestNativeLog.ArgumentsFloatFloat args) {
         args.out = new Floaty(log(args.inV), NATIVE_PRECISION, NATIVE_PRECISION);
     }
@@ -1079,10 +1159,32 @@
         args.out = new Floaty(log10(args.inV), NATIVE_PRECISION, NATIVE_PRECISION);
     }
 
+    static public void computeNativeLog1p(TestNativeLog1p.ArgumentsFloatFloat args) {
+        args.out = new Floaty(log1p(args.in), NATIVE_PRECISION, NATIVE_PRECISION);
+    }
+
     static public void computeNativeLog2(TestNativeLog2.ArgumentsFloatFloat args) {
         args.out = new Floaty(log2(args.inV), NATIVE_PRECISION, NATIVE_PRECISION);
     }
 
+    static public void computeNativeLength(TestNativeLength.ArgumentsFloatFloat args) {
+        args.out = length(new float[] {args.inV}, NATIVE_PRECISION, NATIVE_PRECISION);
+    }
+
+    static public void computeNativeLength(TestNativeLength.ArgumentsFloatNFloat args) {
+        args.out = length(args.inV, NATIVE_PRECISION, NATIVE_PRECISION);
+    }
+
+    static public void computeNativeNormalize(TestNativeNormalize.ArgumentsFloatFloat args) {
+        Floaty[] out = new Floaty[1];
+        normalize(new float[] {args.inV}, out, NATIVE_PRECISION, NATIVE_PRECISION);
+        args.out = out[0];
+    }
+
+    static public void computeNativeNormalize(TestNativeNormalize.ArgumentsFloatNFloatN args) {
+        normalize(args.inV, args.out, NATIVE_PRECISION, NATIVE_PRECISION);
+    }
+
     /* TODO enable once fixed handling of v = 0
     static public void computeNativePowr(TestNativePowr.ArgumentsFloatFloatFloat args) {
         // TODO we would like to use NATIVE_PRECISION, NATIVE_PRECISION
@@ -1090,6 +1192,53 @@
     }
     */
 
+    static public void computeNativeRecip(TestNativeRecip.ArgumentsFloatFloat args) {
+        args.out = new Floaty(1.0f / args.inV, NATIVE_PRECISION, NATIVE_PRECISION);
+    }
+
+    static public void computeNativeRsqrt(TestNativeRsqrt.ArgumentsFloatFloat args) {
+        args.out = new Floaty(1f / sqrt(args.in), NATIVE_PRECISION, NATIVE_PRECISION);
+    }
+
+    static public void computeNativeSin(TestNativeSin.ArgumentsFloatFloat args) {
+        args.out = new Floaty(sin(args.in), NATIVE_PRECISION, NATIVE_PRECISION);
+    }
+
+    static public void computeNativeSincos(TestNativeSincos.ArgumentsFloatFloatFloat args) {
+        args.outCosptr = new Floaty(cos(args.inV), NATIVE_PRECISION, NATIVE_PRECISION);
+        args.out = new Floaty(sin(args.inV), NATIVE_PRECISION, NATIVE_PRECISION);
+    }
+
+    static public void computeNativeSinh(TestNativeSinh.ArgumentsFloatFloat args) {
+        args.out = new Floaty(sinh(args.in), NATIVE_PRECISION, NATIVE_PRECISION);
+    }
+
+    static public void computeNativeSinpi(TestNativeSinpi.ArgumentsFloatFloat args) {
+        Floaty ip = new Floaty((float) ((double)args.in * Math.PI), 1, 1);
+        args.out = Floaty.FloatyFromRange(
+            (float) Math.sin(ip.getDoubleMin()),
+            (float) Math.sin(ip.getDoubleMax()), NATIVE_PRECISION, NATIVE_PRECISION);
+    }
+
+    static public void computeNativeSqrt(TestNativeSqrt.ArgumentsFloatFloat args) {
+        args.out = new Floaty(sqrt(args.in), NATIVE_PRECISION, NATIVE_PRECISION);
+    }
+
+    static public void computeNativeTan(TestNativeTan.ArgumentsFloatFloat args) {
+        args.out = new Floaty(tan(args.in), NATIVE_PRECISION, NATIVE_PRECISION);
+    }
+
+    static public void computeNativeTanh(TestNativeTanh.ArgumentsFloatFloat args) {
+        args.out = new Floaty(tanh(args.in), NATIVE_PRECISION, NATIVE_PRECISION);
+    }
+
+    static public void computeNativeTanpi(TestNativeTanpi.ArgumentsFloatFloat args) {
+        Floaty ip = new Floaty((float) ((double)args.in * Math.PI), 1, 1);
+        args.out = Floaty.FloatyFromRange(
+            (float) Math.tan(ip.getDoubleMin()),
+            (float) Math.tan(ip.getDoubleMax()), NATIVE_PRECISION, NATIVE_PRECISION);
+    }
+
     static public void computeNextafter(TestNextafter.ArgumentsFloatFloatFloat args) {
         args.out = new Floaty(Math.nextAfter(args.inX, args.inY), 0, 0);
     }
@@ -1183,7 +1332,10 @@
     }
 
     static public void computeSinpi(TestSinpi.ArgumentsFloatFloat args) {
-        args.out = new Floaty(sin(args.in * (float) Math.PI), 4, 128);
+        Floaty ip = new Floaty((float) ((double)args.in * Math.PI), 1, 1);
+        args.out = Floaty.FloatyFromRange(
+            (float) Math.sin(ip.getDoubleMin()),
+            (float) Math.sin(ip.getDoubleMax()), 4, 128);
     }
 
     static public void computeSqrt(TestSqrt.ArgumentsFloatFloat args) {
@@ -1203,7 +1355,10 @@
     }
 
     static public void computeTanpi(TestTanpi.ArgumentsFloatFloat args) {
-        args.out = new Floaty(tan(args.in * (float) Math.PI), 6, 128);
+        Floaty ip = new Floaty((float) ((double)args.in * Math.PI), 1, 1);
+        args.out = Floaty.FloatyFromRange(
+            (float) Math.tan(ip.getDoubleMin()),
+            (float) Math.tan(ip.getDoubleMax()), 4, 128);
     }
 
     static public void computeTgamma(TestTgamma.ArgumentsFloatFloat args) {
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/Floaty.java b/tests/tests/renderscript/src/android/renderscript/cts/Floaty.java
index 608e172..85b00ce 100644
--- a/tests/tests/renderscript/src/android/renderscript/cts/Floaty.java
+++ b/tests/tests/renderscript/src/android/renderscript/cts/Floaty.java
@@ -24,8 +24,12 @@
  * compute the correct error of the result.
  */
 public class Floaty {
-    private double mValue;  // The value this instance represent.
-    private double mError;  // The real value should be between mValue - mError and mValue + mError.
+    // The value this instance represents.
+    private double mValue;
+    /* The real value should be between mValue - mError and mValue + mError.
+     * mError should be positive.
+     */
+    private double mError;
     /* The number of bits the value should have, either 32 or 64.  It would have been nice to
      * use generics, e.g. Floaty<float> and Floaty<double> but Java does not support generics
      * of float and double.  Also, Java does not have a f16 type.  This can simulate it, although
@@ -57,6 +61,30 @@
         setErrorFromValue(1, 1);
     }
 
+    /** Creates a Floaty for which any value between a and b are legal, expanded by the specified factor. */
+    public static Floaty FloatyFromRange(float a, float b, float ulpFactor, float ulpRelaxedFactor) {
+        // Order the edges of the range.
+        float min, max;
+        if (a < b) {
+            min = a;
+            max = b;
+        } else {
+            min = b;
+            max = a;
+        }
+        // Expand the edges by the specified factor.
+        float factor = relaxed ? ulpRelaxedFactor : ulpFactor;
+        min = min + (Math.nextAfter(min, -Float.MAX_VALUE) - min) * factor;
+        max = max + (Math.nextAfter(max, Float.MAX_VALUE) - max) * factor;
+
+        // Convert a [range] to a (mid +- error)
+        float delta = Math.abs(a - b);
+        float mid = (min + max) / 2.f;
+        Floaty fl = new Floaty(mid);
+        fl.mError = Math.max(mid - min, max - mid);
+        return fl;
+    }
+
     /** Sets the value and the error based on whether we're doing relaxed computations or not. */
     public Floaty(float v, int ulpFactor, int ulpRelaxedFactor) {
         mValue = v;
@@ -76,12 +104,17 @@
     public float getFloatError() { return (float) mError; }
     public double getDoubleError() { return mError; }
 
+    public double getDoubleMin() { return mValue - mError; }
+    public double getDoubleMax() { return mValue + mError; }
+
     /** Returns the number we would need to multiply the ulp to get the current error. */
     public int getUlf() {
-        return (int) Math.abs(mError / getUlp());
+        return (int) (mError / getUlp() + 0.5);
     }
 
-    /** Returns the unit of least precision for the number we handle. */
+    /** Returns the unit of least precision for the number we handle. This is
+     * always a positive number.
+     */
     private double getUlp() {
         if (mNumberOfBits == 64) {
             return Math.ulp(mValue);
@@ -112,7 +145,7 @@
         mError *= relaxed ? ulpRelaxedFactor : ulpFactor;
         mNumberOfBits = numberOfBits;
     }
-    
+
     /** If needed, increases the error so that the provided value is covered by the error range. */
     private void expandError(double valueWithError) {
         // We disregard NaN values that can be produced when testing close to a cliff.
@@ -125,6 +158,14 @@
         }
     }
 
+    /** Makes sure the allowed error is at least ulp{Relaxed}Factor. */
+    public void setMinimumError(int ulpFactor, int ulpRelaxedFactor) {
+        double minError = getUlp() * (relaxed ? ulpRelaxedFactor : ulpFactor);
+        if (mError < minError) {
+            mError = minError;
+        }
+    }
+
     /** Returns true if the number passed is within mError of our value. */
     public boolean couldBe(double a) {
         return couldBe(a, 0.0);
@@ -133,7 +174,7 @@
     /**
      * Returns true if the number passed is within mError of our value, or if it's whithin
      * minimumError of the value.
-     */ 
+     */
     public boolean couldBe(double a, double minimumError) {
         if (a != a && mValue != mValue) {
             return true;  // Both are NaN
@@ -146,11 +187,40 @@
         double error = Math.max(mError, minimumError);
         boolean inRange = mValue - error <= a && a <= mValue + error;
 
+        /* For relaxed precision, some implementations don't return denormalized values for very
+         * subnormal values.  Two examples:
+         * a) nextafter(0.0, 1.0):  When denormalized are allowed, 1.40129846e-45 (0x00000001) is
+         *    expected.  If only normalized values are returned, 1.1754944e-38 (0x00800000) is
+         *    expected.
+         * b) powr(1600.4, -11.9):  With denormalized, 7.4076481e-39 is returned.  For normalized,
+         *    0.0 can be returned.
+         */
+        if (!inRange && relaxed) {
+            boolean isSubnormal = false;
+            double normalized = 0.0;
+            // Check if we have a subnormal value.
+            if (mNumberOfBits == 32 && Math.abs(mValue) < Float.MIN_NORMAL) {
+                isSubnormal = true;
+                normalized = Math.copySign(Float.MIN_NORMAL, (float) mValue);
+            } else if (Math.abs(mValue) < Double.MIN_NORMAL) {
+                isSubnormal = true;
+                normalized = Math.copySign(Double.MIN_NORMAL, mValue);
+            }
+            if (isSubnormal) {
+                // First try replacing the expected value with the larger MIN_NORMAL
+                inRange = (normalized - error) <= a && a <= (normalized + error);
+                // Second try replacing the expected value with 0.
+                if (!inRange) {
+                    inRange = -error <= a && a <= error;
+                }
+            }
+        }
+
         /* This is useful for debugging:
         if (!inRange) {
-            int ulfNeeded = (int) Math.abs(Math.round((a - mValue) / Math.ulp(mValue)));
-            Log.e("Floaty.couldBe", "Comparing " + Float.toString(a) +
-                    " against " + Float.toString(mValue) + " +- " + Float.toString(error) +
+            int ulfNeeded = (int) Math.abs(Math.round((a - mValue) / getUlp()));
+            Log.e("Floaty.couldBe", "Comparing " + Double.toString(a) +
+                    " against " + Double.toString(mValue) + " +- " + Double.toString(error) +
                     " relaxed " + Boolean.toString(relaxed) +
                     " ulfNeeded " + Integer.toString(ulfNeeded) +
                     ", off by " + Integer.toString(ulfNeeded - getUlf()));
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/RSBaseCompute.java b/tests/tests/renderscript/src/android/renderscript/cts/RSBaseCompute.java
index f217bc3..0413f22 100644
--- a/tests/tests/renderscript/src/android/renderscript/cts/RSBaseCompute.java
+++ b/tests/tests/renderscript/src/android/renderscript/cts/RSBaseCompute.java
@@ -115,7 +115,6 @@
         Element element = getElement(rs, dataType, size);
         Allocation alloc = Allocation.createSized(rs, element, INPUTSIZE);
         int width = (size == 3) ? 4 : size;
-        /* TODO copy1DRangeFrom does not work for double
         if (dataType == Element.DataType.FLOAT_64) {
             double[] inArray = new double[INPUTSIZE * width];
             // TODO The ranges for float is too small.  We need to accept a wider range of values.
@@ -123,16 +122,13 @@
             double max = 4.0 * Math.PI;
             RSUtils.genRandomDoubles(seed, min, max, inArray, includeExtremes);
             alloc.copy1DRangeFrom(0, INPUTSIZE, inArray);
-        } else
-        */
-        if (dataType == Element.DataType.FLOAT_32) {
+        } else if (dataType == Element.DataType.FLOAT_32) {
             float[] inArray = new float[INPUTSIZE * width];
             // TODO The ranges for float is too small.  We need to accept a wider range of values.
             float min = -4.0f * (float) Math.PI;
             float max = 4.0f * (float) Math.PI;
             RSUtils.genRandomFloats(seed, min, max, inArray, includeExtremes);
             alloc.copy1DRangeFrom(0, INPUTSIZE, inArray);
-        /* TODO copy1DRangFrom does not work for long
         } else if (dataType == Element.DataType.SIGNED_64) {
             long[] inArray = new long[INPUTSIZE * width];
             RSUtils.genRandomLongs(seed, inArray, true, 63);
@@ -141,7 +137,6 @@
             long[] inArray = new long[INPUTSIZE * width];
             RSUtils.genRandomLongs(seed, inArray, false, 64);
             alloc.copy1DRangeFrom(0, INPUTSIZE, inArray);
-        */
         } else if (dataType == Element.DataType.SIGNED_32) {
             int[] inArray = new int[INPUTSIZE * width];
             RSUtils.genRandomInts(seed, inArray, true, 31);
@@ -178,13 +173,11 @@
         Element element = getElement(rs, dataType, size);
         Allocation alloc = Allocation.createSized(rs, element, INPUTSIZE);
         int width = (size == 3) ? 4 : size;
-        /* TODO copy1DRangeFrom does not work for double
         if (dataType == Element.DataType.FLOAT_64) {
             double[] inArray = new double[INPUTSIZE * width];
             RSUtils.genRandomDoubles(seed, minValue, maxValue, inArray, false);
             alloc.copy1DRangeFrom(0, INPUTSIZE, inArray);
-        } else */
-        if (dataType == Element.DataType.FLOAT_32) {
+        } else if (dataType == Element.DataType.FLOAT_32) {
             float[] inArray = new float[INPUTSIZE * width];
             RSUtils.genRandomFloats(seed, (float) minValue, (float) maxValue, inArray, false);
             alloc.copy1DRangeFrom(0, INPUTSIZE, inArray);
@@ -201,13 +194,12 @@
         Element element = getElement(rs, dataType, size);
         Allocation alloc = Allocation.createSized(rs, element, INPUTSIZE);
         int width = (size == 3) ? 4 : size;
-        /* TODO copy1DRangFrom does not work for long
         if (dataType == Element.DataType.SIGNED_64 ||
                 dataType == Element.DataType.UNSIGNED_64) {
             long[] inArray = new long[INPUTSIZE * width];
             RSUtils.genRandomLongs(seed, inArray, signed, numberOfBits);
             alloc.copy1DRangeFrom(0, INPUTSIZE, inArray);
-        } else */
+        } else
         if (dataType == Element.DataType.SIGNED_32 ||
                 dataType == Element.DataType.UNSIGNED_32) {
             int[] inArray = new int[INPUTSIZE * width];
@@ -239,7 +231,6 @@
         }
         int size = INPUTSIZE * stride;
         Element.DataType dataType = element.getDataType();
-        /* TODO copy1DRangeFrom does not work for double
         if (dataType == Element.DataType.FLOAT_64) {
             double[] minArray = new double[size];
             double[] maxArray = new double[size];
@@ -254,7 +245,7 @@
             }
             minAlloc.copyFrom(minArray);
             maxAlloc.copyFrom(maxArray);
-        } else */
+        } else
         if (dataType == Element.DataType.FLOAT_32) {
             float[] minArray = new float[size];
             float[] maxArray = new float[size];
@@ -269,7 +260,6 @@
             }
             minAlloc.copyFrom(minArray);
             maxAlloc.copyFrom(maxArray);
-        /* TODO copy1DRangFrom does not work for long
         } else if (dataType == Element.DataType.SIGNED_64) {
             long[] minArray = new long[size];
             long[] maxArray = new long[size];
@@ -298,7 +288,6 @@
             }
             minAlloc.copyFrom(minArray);
             maxAlloc.copyFrom(maxArray);
-        */
         } else if (dataType == Element.DataType.SIGNED_32) {
             int[] minArray = new int[size];
             int[] maxArray = new int[size];
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/TestAbs.java b/tests/tests/renderscript/src/android/renderscript/cts/TestAbs.java
index 7799a8a..3547d9c 100644
--- a/tests/tests/renderscript/src/android/renderscript/cts/TestAbs.java
+++ b/tests/tests/renderscript/src/android/renderscript/cts/TestAbs.java
@@ -70,7 +70,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeAbs(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 1 + j]) {
                     valid = false;
@@ -127,7 +127,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeAbs(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 2 + j]) {
                     valid = false;
@@ -184,7 +184,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeAbs(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 4 + j]) {
                     valid = false;
@@ -241,7 +241,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeAbs(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 4 + j]) {
                     valid = false;
@@ -303,7 +303,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeAbs(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 1 + j]) {
                     valid = false;
@@ -360,7 +360,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeAbs(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 2 + j]) {
                     valid = false;
@@ -417,7 +417,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeAbs(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 4 + j]) {
                     valid = false;
@@ -474,7 +474,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeAbs(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 4 + j]) {
                     valid = false;
@@ -536,7 +536,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeAbs(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 1 + j]) {
                     valid = false;
@@ -593,7 +593,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeAbs(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 2 + j]) {
                     valid = false;
@@ -650,7 +650,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeAbs(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 4 + j]) {
                     valid = false;
@@ -707,7 +707,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeAbs(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 4 + j]) {
                     valid = false;
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/TestAcos.java b/tests/tests/renderscript/src/android/renderscript/cts/TestAcos.java
index 95f7a51..d93ac1c 100644
--- a/tests/tests/renderscript/src/android/renderscript/cts/TestAcos.java
+++ b/tests/tests/renderscript/src/android/renderscript/cts/TestAcos.java
@@ -70,7 +70,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeAcos(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 1 + j])) {
                     valid = false;
@@ -129,7 +129,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeAcos(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 2 + j])) {
                     valid = false;
@@ -188,7 +188,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeAcos(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 4 + j])) {
                     valid = false;
@@ -247,7 +247,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeAcos(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 4 + j])) {
                     valid = false;
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/TestAcosh.java b/tests/tests/renderscript/src/android/renderscript/cts/TestAcosh.java
index 90993cf..de18d7e 100644
--- a/tests/tests/renderscript/src/android/renderscript/cts/TestAcosh.java
+++ b/tests/tests/renderscript/src/android/renderscript/cts/TestAcosh.java
@@ -70,7 +70,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeAcosh(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 1 + j])) {
                     valid = false;
@@ -129,7 +129,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeAcosh(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 2 + j])) {
                     valid = false;
@@ -188,7 +188,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeAcosh(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 4 + j])) {
                     valid = false;
@@ -247,7 +247,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeAcosh(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 4 + j])) {
                     valid = false;
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/TestAcospi.java b/tests/tests/renderscript/src/android/renderscript/cts/TestAcospi.java
index d11148d..2c342a2 100644
--- a/tests/tests/renderscript/src/android/renderscript/cts/TestAcospi.java
+++ b/tests/tests/renderscript/src/android/renderscript/cts/TestAcospi.java
@@ -70,7 +70,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeAcospi(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 1 + j])) {
                     valid = false;
@@ -129,7 +129,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeAcospi(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 2 + j])) {
                     valid = false;
@@ -188,7 +188,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeAcospi(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 4 + j])) {
                     valid = false;
@@ -247,7 +247,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeAcospi(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 4 + j])) {
                     valid = false;
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/TestAsin.java b/tests/tests/renderscript/src/android/renderscript/cts/TestAsin.java
index 71253f6..847bbea 100644
--- a/tests/tests/renderscript/src/android/renderscript/cts/TestAsin.java
+++ b/tests/tests/renderscript/src/android/renderscript/cts/TestAsin.java
@@ -70,7 +70,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeAsin(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 1 + j])) {
                     valid = false;
@@ -129,7 +129,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeAsin(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 2 + j])) {
                     valid = false;
@@ -188,7 +188,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeAsin(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 4 + j])) {
                     valid = false;
@@ -247,7 +247,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeAsin(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 4 + j])) {
                     valid = false;
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/TestAsinh.java b/tests/tests/renderscript/src/android/renderscript/cts/TestAsinh.java
index 1ec9c0a..8bc74fe 100644
--- a/tests/tests/renderscript/src/android/renderscript/cts/TestAsinh.java
+++ b/tests/tests/renderscript/src/android/renderscript/cts/TestAsinh.java
@@ -70,7 +70,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeAsinh(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 1 + j])) {
                     valid = false;
@@ -129,7 +129,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeAsinh(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 2 + j])) {
                     valid = false;
@@ -188,7 +188,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeAsinh(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 4 + j])) {
                     valid = false;
@@ -247,7 +247,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeAsinh(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 4 + j])) {
                     valid = false;
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/TestAsinpi.java b/tests/tests/renderscript/src/android/renderscript/cts/TestAsinpi.java
index f6bb172..8219acb 100644
--- a/tests/tests/renderscript/src/android/renderscript/cts/TestAsinpi.java
+++ b/tests/tests/renderscript/src/android/renderscript/cts/TestAsinpi.java
@@ -70,7 +70,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeAsinpi(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 1 + j])) {
                     valid = false;
@@ -129,7 +129,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeAsinpi(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 2 + j])) {
                     valid = false;
@@ -188,7 +188,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeAsinpi(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 4 + j])) {
                     valid = false;
@@ -247,7 +247,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeAsinpi(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 4 + j])) {
                     valid = false;
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/TestAtan.java b/tests/tests/renderscript/src/android/renderscript/cts/TestAtan.java
index cfdcba9..07bbe10 100644
--- a/tests/tests/renderscript/src/android/renderscript/cts/TestAtan.java
+++ b/tests/tests/renderscript/src/android/renderscript/cts/TestAtan.java
@@ -70,7 +70,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeAtan(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 1 + j])) {
                     valid = false;
@@ -129,7 +129,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeAtan(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 2 + j])) {
                     valid = false;
@@ -188,7 +188,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeAtan(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 4 + j])) {
                     valid = false;
@@ -247,7 +247,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeAtan(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 4 + j])) {
                     valid = false;
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/TestAtan2.java b/tests/tests/renderscript/src/android/renderscript/cts/TestAtan2.java
index 6981149..203db81 100644
--- a/tests/tests/renderscript/src/android/renderscript/cts/TestAtan2.java
+++ b/tests/tests/renderscript/src/android/renderscript/cts/TestAtan2.java
@@ -77,7 +77,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeAtan2(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 1 + j])) {
                     valid = false;
@@ -146,7 +146,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeAtan2(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 2 + j])) {
                     valid = false;
@@ -215,7 +215,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeAtan2(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 4 + j])) {
                     valid = false;
@@ -284,7 +284,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeAtan2(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 4 + j])) {
                     valid = false;
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/TestAtan2pi.java b/tests/tests/renderscript/src/android/renderscript/cts/TestAtan2pi.java
index 3f6960f..8d9f5fc 100644
--- a/tests/tests/renderscript/src/android/renderscript/cts/TestAtan2pi.java
+++ b/tests/tests/renderscript/src/android/renderscript/cts/TestAtan2pi.java
@@ -77,7 +77,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeAtan2pi(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 1 + j])) {
                     valid = false;
@@ -146,7 +146,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeAtan2pi(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 2 + j])) {
                     valid = false;
@@ -215,7 +215,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeAtan2pi(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 4 + j])) {
                     valid = false;
@@ -284,7 +284,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeAtan2pi(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 4 + j])) {
                     valid = false;
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/TestAtanh.java b/tests/tests/renderscript/src/android/renderscript/cts/TestAtanh.java
index d153efa..2c7797f 100644
--- a/tests/tests/renderscript/src/android/renderscript/cts/TestAtanh.java
+++ b/tests/tests/renderscript/src/android/renderscript/cts/TestAtanh.java
@@ -70,7 +70,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeAtanh(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 1 + j])) {
                     valid = false;
@@ -129,7 +129,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeAtanh(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 2 + j])) {
                     valid = false;
@@ -188,7 +188,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeAtanh(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 4 + j])) {
                     valid = false;
@@ -247,7 +247,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeAtanh(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 4 + j])) {
                     valid = false;
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/TestAtanpi.java b/tests/tests/renderscript/src/android/renderscript/cts/TestAtanpi.java
index c9cd821..956fed8 100644
--- a/tests/tests/renderscript/src/android/renderscript/cts/TestAtanpi.java
+++ b/tests/tests/renderscript/src/android/renderscript/cts/TestAtanpi.java
@@ -70,7 +70,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeAtanpi(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 1 + j])) {
                     valid = false;
@@ -129,7 +129,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeAtanpi(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 2 + j])) {
                     valid = false;
@@ -188,7 +188,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeAtanpi(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 4 + j])) {
                     valid = false;
@@ -247,7 +247,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeAtanpi(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 4 + j])) {
                     valid = false;
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/TestCbrt.java b/tests/tests/renderscript/src/android/renderscript/cts/TestCbrt.java
index 5c364d5..91e9a14 100644
--- a/tests/tests/renderscript/src/android/renderscript/cts/TestCbrt.java
+++ b/tests/tests/renderscript/src/android/renderscript/cts/TestCbrt.java
@@ -70,7 +70,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeCbrt(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 1 + j])) {
                     valid = false;
@@ -129,7 +129,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeCbrt(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 2 + j])) {
                     valid = false;
@@ -188,7 +188,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeCbrt(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 4 + j])) {
                     valid = false;
@@ -247,7 +247,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeCbrt(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 4 + j])) {
                     valid = false;
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/TestCeil.java b/tests/tests/renderscript/src/android/renderscript/cts/TestCeil.java
index d14de2b..7a8222a 100644
--- a/tests/tests/renderscript/src/android/renderscript/cts/TestCeil.java
+++ b/tests/tests/renderscript/src/android/renderscript/cts/TestCeil.java
@@ -70,7 +70,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeCeil(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 1 + j])) {
                     valid = false;
@@ -129,7 +129,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeCeil(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 2 + j])) {
                     valid = false;
@@ -188,7 +188,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeCeil(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 4 + j])) {
                     valid = false;
@@ -247,7 +247,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeCeil(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 4 + j])) {
                     valid = false;
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/TestClamp.java b/tests/tests/renderscript/src/android/renderscript/cts/TestClamp.java
index 8e11182..04cf924 100644
--- a/tests/tests/renderscript/src/android/renderscript/cts/TestClamp.java
+++ b/tests/tests/renderscript/src/android/renderscript/cts/TestClamp.java
@@ -85,7 +85,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeClamp(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 1 + j])) {
                     valid = false;
@@ -165,7 +165,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeClamp(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 2 + j])) {
                     valid = false;
@@ -245,7 +245,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeClamp(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 4 + j])) {
                     valid = false;
@@ -325,7 +325,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeClamp(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 4 + j])) {
                     valid = false;
@@ -405,7 +405,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeClamp(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 2 + j])) {
                     valid = false;
@@ -485,7 +485,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeClamp(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 4 + j])) {
                     valid = false;
@@ -565,7 +565,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeClamp(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 4 + j])) {
                     valid = false;
@@ -652,7 +652,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeClamp(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 1 + j]) {
                     valid = false;
@@ -728,7 +728,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeClamp(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 2 + j]) {
                     valid = false;
@@ -804,7 +804,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeClamp(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 4 + j]) {
                     valid = false;
@@ -880,7 +880,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeClamp(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 4 + j]) {
                     valid = false;
@@ -963,7 +963,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeClamp(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 1 + j]) {
                     valid = false;
@@ -1039,7 +1039,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeClamp(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 2 + j]) {
                     valid = false;
@@ -1115,7 +1115,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeClamp(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 4 + j]) {
                     valid = false;
@@ -1191,7 +1191,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeClamp(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 4 + j]) {
                     valid = false;
@@ -1274,7 +1274,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeClamp(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 1 + j]) {
                     valid = false;
@@ -1350,7 +1350,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeClamp(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 2 + j]) {
                     valid = false;
@@ -1426,7 +1426,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeClamp(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 4 + j]) {
                     valid = false;
@@ -1502,7 +1502,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeClamp(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 4 + j]) {
                     valid = false;
@@ -1585,7 +1585,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeClamp(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 1 + j]) {
                     valid = false;
@@ -1661,7 +1661,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeClamp(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 2 + j]) {
                     valid = false;
@@ -1737,7 +1737,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeClamp(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 4 + j]) {
                     valid = false;
@@ -1813,7 +1813,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeClamp(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 4 + j]) {
                     valid = false;
@@ -1896,7 +1896,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeClamp(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 1 + j]) {
                     valid = false;
@@ -1972,7 +1972,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeClamp(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 2 + j]) {
                     valid = false;
@@ -2048,7 +2048,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeClamp(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 4 + j]) {
                     valid = false;
@@ -2124,7 +2124,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeClamp(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 4 + j]) {
                     valid = false;
@@ -2207,7 +2207,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeClamp(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 1 + j]) {
                     valid = false;
@@ -2283,7 +2283,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeClamp(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 2 + j]) {
                     valid = false;
@@ -2359,7 +2359,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeClamp(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 4 + j]) {
                     valid = false;
@@ -2435,7 +2435,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeClamp(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 4 + j]) {
                     valid = false;
@@ -2467,6 +2467,628 @@
         }
     }
 
+    public class ArgumentsLongLongLongLong {
+        public long inValue;
+        public long inMinValue;
+        public long inMaxValue;
+        public long out;
+    }
+
+    private void checkClampLongLongLongLong() {
+        Allocation inValue = createRandomAllocation(mRS, Element.DataType.SIGNED_64, 1, 0x63fd360531c9c41dl, false);
+        Allocation inMinValue = createRandomAllocation(mRS, Element.DataType.SIGNED_64, 1, 0x9d04d1824ef4907l, false);
+        Allocation inMaxValue = createRandomAllocation(mRS, Element.DataType.SIGNED_64, 1, 0x9d04d0cb64c3b0dl, false);
+        enforceOrdering(inMinValue, inMaxValue);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_64, 1), INPUTSIZE);
+            script.set_gAllocInMinValue(inMinValue);
+            script.set_gAllocInMaxValue(inMaxValue);
+            script.forEach_testClampLongLongLongLong(inValue, out);
+            verifyResultsClampLongLongLongLong(inValue, inMinValue, inMaxValue, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testClampLongLongLongLong: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_64, 1), INPUTSIZE);
+            scriptRelaxed.set_gAllocInMinValue(inMinValue);
+            scriptRelaxed.set_gAllocInMaxValue(inMaxValue);
+            scriptRelaxed.forEach_testClampLongLongLongLong(inValue, out);
+            verifyResultsClampLongLongLongLong(inValue, inMinValue, inMaxValue, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testClampLongLongLongLong: " + e.toString());
+        }
+    }
+
+    private void verifyResultsClampLongLongLongLong(Allocation inValue, Allocation inMinValue, Allocation inMaxValue, Allocation out, boolean relaxed) {
+        long[] arrayInValue = new long[INPUTSIZE * 1];
+        inValue.copyTo(arrayInValue);
+        long[] arrayInMinValue = new long[INPUTSIZE * 1];
+        inMinValue.copyTo(arrayInMinValue);
+        long[] arrayInMaxValue = new long[INPUTSIZE * 1];
+        inMaxValue.copyTo(arrayInMaxValue);
+        long[] arrayOut = new long[INPUTSIZE * 1];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 1 ; j++) {
+                // Extract the inputs.
+                ArgumentsLongLongLongLong args = new ArgumentsLongLongLongLong();
+                args.inValue = arrayInValue[i];
+                args.inMinValue = arrayInMinValue[i];
+                args.inMaxValue = arrayInMaxValue[i];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeClamp(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (args.out != arrayOut[i * 1 + j]) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inValue: ");
+                    message.append(String.format("%d", args.inValue));
+                    message.append("\n");
+                    message.append("Input inMinValue: ");
+                    message.append(String.format("%d", args.inMinValue));
+                    message.append("\n");
+                    message.append("Input inMaxValue: ");
+                    message.append(String.format("%d", args.inMaxValue));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(String.format("%d", args.out));
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%d", arrayOut[i * 1 + j]));
+                    if (args.out != arrayOut[i * 1 + j]) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkClampLongLongLongLong" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkClampLong2Long2Long2Long2() {
+        Allocation inValue = createRandomAllocation(mRS, Element.DataType.SIGNED_64, 2, 0xccbae869c2b0f12dl, false);
+        Allocation inMinValue = createRandomAllocation(mRS, Element.DataType.SIGNED_64, 2, 0xe4c3844f4a3f8937l, false);
+        Allocation inMaxValue = createRandomAllocation(mRS, Element.DataType.SIGNED_64, 2, 0xe4c38443db9c7b3dl, false);
+        enforceOrdering(inMinValue, inMaxValue);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_64, 2), INPUTSIZE);
+            script.set_gAllocInMinValue(inMinValue);
+            script.set_gAllocInMaxValue(inMaxValue);
+            script.forEach_testClampLong2Long2Long2Long2(inValue, out);
+            verifyResultsClampLong2Long2Long2Long2(inValue, inMinValue, inMaxValue, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testClampLong2Long2Long2Long2: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_64, 2), INPUTSIZE);
+            scriptRelaxed.set_gAllocInMinValue(inMinValue);
+            scriptRelaxed.set_gAllocInMaxValue(inMaxValue);
+            scriptRelaxed.forEach_testClampLong2Long2Long2Long2(inValue, out);
+            verifyResultsClampLong2Long2Long2Long2(inValue, inMinValue, inMaxValue, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testClampLong2Long2Long2Long2: " + e.toString());
+        }
+    }
+
+    private void verifyResultsClampLong2Long2Long2Long2(Allocation inValue, Allocation inMinValue, Allocation inMaxValue, Allocation out, boolean relaxed) {
+        long[] arrayInValue = new long[INPUTSIZE * 2];
+        inValue.copyTo(arrayInValue);
+        long[] arrayInMinValue = new long[INPUTSIZE * 2];
+        inMinValue.copyTo(arrayInMinValue);
+        long[] arrayInMaxValue = new long[INPUTSIZE * 2];
+        inMaxValue.copyTo(arrayInMaxValue);
+        long[] arrayOut = new long[INPUTSIZE * 2];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 2 ; j++) {
+                // Extract the inputs.
+                ArgumentsLongLongLongLong args = new ArgumentsLongLongLongLong();
+                args.inValue = arrayInValue[i * 2 + j];
+                args.inMinValue = arrayInMinValue[i * 2 + j];
+                args.inMaxValue = arrayInMaxValue[i * 2 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeClamp(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (args.out != arrayOut[i * 2 + j]) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inValue: ");
+                    message.append(String.format("%d", args.inValue));
+                    message.append("\n");
+                    message.append("Input inMinValue: ");
+                    message.append(String.format("%d", args.inMinValue));
+                    message.append("\n");
+                    message.append("Input inMaxValue: ");
+                    message.append(String.format("%d", args.inMaxValue));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(String.format("%d", args.out));
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%d", arrayOut[i * 2 + j]));
+                    if (args.out != arrayOut[i * 2 + j]) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkClampLong2Long2Long2Long2" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkClampLong3Long3Long3Long3() {
+        Allocation inValue = createRandomAllocation(mRS, Element.DataType.SIGNED_64, 3, 0x2568063ab885ed75l, false);
+        Allocation inMinValue = createRandomAllocation(mRS, Element.DataType.SIGNED_64, 3, 0x7246acfc5d0b968fl, false);
+        Allocation inMaxValue = createRandomAllocation(mRS, Element.DataType.SIGNED_64, 3, 0x7246acf0ee688895l, false);
+        enforceOrdering(inMinValue, inMaxValue);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_64, 3), INPUTSIZE);
+            script.set_gAllocInMinValue(inMinValue);
+            script.set_gAllocInMaxValue(inMaxValue);
+            script.forEach_testClampLong3Long3Long3Long3(inValue, out);
+            verifyResultsClampLong3Long3Long3Long3(inValue, inMinValue, inMaxValue, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testClampLong3Long3Long3Long3: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_64, 3), INPUTSIZE);
+            scriptRelaxed.set_gAllocInMinValue(inMinValue);
+            scriptRelaxed.set_gAllocInMaxValue(inMaxValue);
+            scriptRelaxed.forEach_testClampLong3Long3Long3Long3(inValue, out);
+            verifyResultsClampLong3Long3Long3Long3(inValue, inMinValue, inMaxValue, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testClampLong3Long3Long3Long3: " + e.toString());
+        }
+    }
+
+    private void verifyResultsClampLong3Long3Long3Long3(Allocation inValue, Allocation inMinValue, Allocation inMaxValue, Allocation out, boolean relaxed) {
+        long[] arrayInValue = new long[INPUTSIZE * 4];
+        inValue.copyTo(arrayInValue);
+        long[] arrayInMinValue = new long[INPUTSIZE * 4];
+        inMinValue.copyTo(arrayInMinValue);
+        long[] arrayInMaxValue = new long[INPUTSIZE * 4];
+        inMaxValue.copyTo(arrayInMaxValue);
+        long[] arrayOut = new long[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 3 ; j++) {
+                // Extract the inputs.
+                ArgumentsLongLongLongLong args = new ArgumentsLongLongLongLong();
+                args.inValue = arrayInValue[i * 4 + j];
+                args.inMinValue = arrayInMinValue[i * 4 + j];
+                args.inMaxValue = arrayInMaxValue[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeClamp(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (args.out != arrayOut[i * 4 + j]) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inValue: ");
+                    message.append(String.format("%d", args.inValue));
+                    message.append("\n");
+                    message.append("Input inMinValue: ");
+                    message.append(String.format("%d", args.inMinValue));
+                    message.append("\n");
+                    message.append("Input inMaxValue: ");
+                    message.append(String.format("%d", args.inMaxValue));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(String.format("%d", args.out));
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%d", arrayOut[i * 4 + j]));
+                    if (args.out != arrayOut[i * 4 + j]) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkClampLong3Long3Long3Long3" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkClampLong4Long4Long4Long4() {
+        Allocation inValue = createRandomAllocation(mRS, Element.DataType.SIGNED_64, 4, 0x7e15240bae5ae9bdl, false);
+        Allocation inMinValue = createRandomAllocation(mRS, Element.DataType.SIGNED_64, 4, 0xffc9d5a96fd7a3e7l, false);
+        Allocation inMaxValue = createRandomAllocation(mRS, Element.DataType.SIGNED_64, 4, 0xffc9d59e013495edl, false);
+        enforceOrdering(inMinValue, inMaxValue);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_64, 4), INPUTSIZE);
+            script.set_gAllocInMinValue(inMinValue);
+            script.set_gAllocInMaxValue(inMaxValue);
+            script.forEach_testClampLong4Long4Long4Long4(inValue, out);
+            verifyResultsClampLong4Long4Long4Long4(inValue, inMinValue, inMaxValue, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testClampLong4Long4Long4Long4: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_64, 4), INPUTSIZE);
+            scriptRelaxed.set_gAllocInMinValue(inMinValue);
+            scriptRelaxed.set_gAllocInMaxValue(inMaxValue);
+            scriptRelaxed.forEach_testClampLong4Long4Long4Long4(inValue, out);
+            verifyResultsClampLong4Long4Long4Long4(inValue, inMinValue, inMaxValue, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testClampLong4Long4Long4Long4: " + e.toString());
+        }
+    }
+
+    private void verifyResultsClampLong4Long4Long4Long4(Allocation inValue, Allocation inMinValue, Allocation inMaxValue, Allocation out, boolean relaxed) {
+        long[] arrayInValue = new long[INPUTSIZE * 4];
+        inValue.copyTo(arrayInValue);
+        long[] arrayInMinValue = new long[INPUTSIZE * 4];
+        inMinValue.copyTo(arrayInMinValue);
+        long[] arrayInMaxValue = new long[INPUTSIZE * 4];
+        inMaxValue.copyTo(arrayInMaxValue);
+        long[] arrayOut = new long[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 4 ; j++) {
+                // Extract the inputs.
+                ArgumentsLongLongLongLong args = new ArgumentsLongLongLongLong();
+                args.inValue = arrayInValue[i * 4 + j];
+                args.inMinValue = arrayInMinValue[i * 4 + j];
+                args.inMaxValue = arrayInMaxValue[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeClamp(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (args.out != arrayOut[i * 4 + j]) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inValue: ");
+                    message.append(String.format("%d", args.inValue));
+                    message.append("\n");
+                    message.append("Input inMinValue: ");
+                    message.append(String.format("%d", args.inMinValue));
+                    message.append("\n");
+                    message.append("Input inMaxValue: ");
+                    message.append(String.format("%d", args.inMaxValue));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(String.format("%d", args.out));
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%d", arrayOut[i * 4 + j]));
+                    if (args.out != arrayOut[i * 4 + j]) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkClampLong4Long4Long4Long4" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    public class ArgumentsUlongUlongUlongUlong {
+        public long inValue;
+        public long inMinValue;
+        public long inMaxValue;
+        public long out;
+    }
+
+    private void checkClampUlongUlongUlongUlong() {
+        Allocation inValue = createRandomAllocation(mRS, Element.DataType.UNSIGNED_64, 1, 0x2b378139749bf4c5l, false);
+        Allocation inMinValue = createRandomAllocation(mRS, Element.DataType.UNSIGNED_64, 1, 0x75ac5050a8ca97fl, false);
+        Allocation inMaxValue = createRandomAllocation(mRS, Element.DataType.UNSIGNED_64, 1, 0x75ac4f99be99b85l, false);
+        enforceOrdering(inMinValue, inMaxValue);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_64, 1), INPUTSIZE);
+            script.set_gAllocInMinValue(inMinValue);
+            script.set_gAllocInMaxValue(inMaxValue);
+            script.forEach_testClampUlongUlongUlongUlong(inValue, out);
+            verifyResultsClampUlongUlongUlongUlong(inValue, inMinValue, inMaxValue, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testClampUlongUlongUlongUlong: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_64, 1), INPUTSIZE);
+            scriptRelaxed.set_gAllocInMinValue(inMinValue);
+            scriptRelaxed.set_gAllocInMaxValue(inMaxValue);
+            scriptRelaxed.forEach_testClampUlongUlongUlongUlong(inValue, out);
+            verifyResultsClampUlongUlongUlongUlong(inValue, inMinValue, inMaxValue, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testClampUlongUlongUlongUlong: " + e.toString());
+        }
+    }
+
+    private void verifyResultsClampUlongUlongUlongUlong(Allocation inValue, Allocation inMinValue, Allocation inMaxValue, Allocation out, boolean relaxed) {
+        long[] arrayInValue = new long[INPUTSIZE * 1];
+        inValue.copyTo(arrayInValue);
+        long[] arrayInMinValue = new long[INPUTSIZE * 1];
+        inMinValue.copyTo(arrayInMinValue);
+        long[] arrayInMaxValue = new long[INPUTSIZE * 1];
+        inMaxValue.copyTo(arrayInMaxValue);
+        long[] arrayOut = new long[INPUTSIZE * 1];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 1 ; j++) {
+                // Extract the inputs.
+                ArgumentsUlongUlongUlongUlong args = new ArgumentsUlongUlongUlongUlong();
+                args.inValue = arrayInValue[i];
+                args.inMinValue = arrayInMinValue[i];
+                args.inMaxValue = arrayInMaxValue[i];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeClamp(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (args.out != arrayOut[i * 1 + j]) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inValue: ");
+                    message.append(String.format("0x%x", args.inValue));
+                    message.append("\n");
+                    message.append("Input inMinValue: ");
+                    message.append(String.format("0x%x", args.inMinValue));
+                    message.append("\n");
+                    message.append("Input inMaxValue: ");
+                    message.append(String.format("0x%x", args.inMaxValue));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(String.format("0x%x", args.out));
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("0x%x", arrayOut[i * 1 + j]));
+                    if (args.out != arrayOut[i * 1 + j]) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkClampUlongUlongUlongUlong" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkClampUlong2Ulong2Ulong2Ulong2() {
+        Allocation inValue = createRandomAllocation(mRS, Element.DataType.UNSIGNED_64, 2, 0xa8c7fb17a09bb299l, false);
+        Allocation inMinValue = createRandomAllocation(mRS, Element.DataType.UNSIGNED_64, 2, 0x14e3c8dffe45623bl, false);
+        Allocation inMaxValue = createRandomAllocation(mRS, Element.DataType.UNSIGNED_64, 2, 0x14e3c8d48fa25441l, false);
+        enforceOrdering(inMinValue, inMaxValue);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_64, 2), INPUTSIZE);
+            script.set_gAllocInMinValue(inMinValue);
+            script.set_gAllocInMaxValue(inMaxValue);
+            script.forEach_testClampUlong2Ulong2Ulong2Ulong2(inValue, out);
+            verifyResultsClampUlong2Ulong2Ulong2Ulong2(inValue, inMinValue, inMaxValue, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testClampUlong2Ulong2Ulong2Ulong2: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_64, 2), INPUTSIZE);
+            scriptRelaxed.set_gAllocInMinValue(inMinValue);
+            scriptRelaxed.set_gAllocInMaxValue(inMaxValue);
+            scriptRelaxed.forEach_testClampUlong2Ulong2Ulong2Ulong2(inValue, out);
+            verifyResultsClampUlong2Ulong2Ulong2Ulong2(inValue, inMinValue, inMaxValue, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testClampUlong2Ulong2Ulong2Ulong2: " + e.toString());
+        }
+    }
+
+    private void verifyResultsClampUlong2Ulong2Ulong2Ulong2(Allocation inValue, Allocation inMinValue, Allocation inMaxValue, Allocation out, boolean relaxed) {
+        long[] arrayInValue = new long[INPUTSIZE * 2];
+        inValue.copyTo(arrayInValue);
+        long[] arrayInMinValue = new long[INPUTSIZE * 2];
+        inMinValue.copyTo(arrayInMinValue);
+        long[] arrayInMaxValue = new long[INPUTSIZE * 2];
+        inMaxValue.copyTo(arrayInMaxValue);
+        long[] arrayOut = new long[INPUTSIZE * 2];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 2 ; j++) {
+                // Extract the inputs.
+                ArgumentsUlongUlongUlongUlong args = new ArgumentsUlongUlongUlongUlong();
+                args.inValue = arrayInValue[i * 2 + j];
+                args.inMinValue = arrayInMinValue[i * 2 + j];
+                args.inMaxValue = arrayInMaxValue[i * 2 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeClamp(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (args.out != arrayOut[i * 2 + j]) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inValue: ");
+                    message.append(String.format("0x%x", args.inValue));
+                    message.append("\n");
+                    message.append("Input inMinValue: ");
+                    message.append(String.format("0x%x", args.inMinValue));
+                    message.append("\n");
+                    message.append("Input inMaxValue: ");
+                    message.append(String.format("0x%x", args.inMaxValue));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(String.format("0x%x", args.out));
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("0x%x", arrayOut[i * 2 + j]));
+                    if (args.out != arrayOut[i * 2 + j]) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkClampUlong2Ulong2Ulong2Ulong2" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkClampUlong3Ulong3Ulong3Ulong3() {
+        Allocation inValue = createRandomAllocation(mRS, Element.DataType.UNSIGNED_64, 3, 0x722c3c9fbd1e1f75l, false);
+        Allocation inMinValue = createRandomAllocation(mRS, Element.DataType.UNSIGNED_64, 3, 0x20d03c8e4cfc4c8fl, false);
+        Allocation inMaxValue = createRandomAllocation(mRS, Element.DataType.UNSIGNED_64, 3, 0x20d03c82de593e95l, false);
+        enforceOrdering(inMinValue, inMaxValue);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_64, 3), INPUTSIZE);
+            script.set_gAllocInMinValue(inMinValue);
+            script.set_gAllocInMaxValue(inMaxValue);
+            script.forEach_testClampUlong3Ulong3Ulong3Ulong3(inValue, out);
+            verifyResultsClampUlong3Ulong3Ulong3Ulong3(inValue, inMinValue, inMaxValue, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testClampUlong3Ulong3Ulong3Ulong3: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_64, 3), INPUTSIZE);
+            scriptRelaxed.set_gAllocInMinValue(inMinValue);
+            scriptRelaxed.set_gAllocInMaxValue(inMaxValue);
+            scriptRelaxed.forEach_testClampUlong3Ulong3Ulong3Ulong3(inValue, out);
+            verifyResultsClampUlong3Ulong3Ulong3Ulong3(inValue, inMinValue, inMaxValue, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testClampUlong3Ulong3Ulong3Ulong3: " + e.toString());
+        }
+    }
+
+    private void verifyResultsClampUlong3Ulong3Ulong3Ulong3(Allocation inValue, Allocation inMinValue, Allocation inMaxValue, Allocation out, boolean relaxed) {
+        long[] arrayInValue = new long[INPUTSIZE * 4];
+        inValue.copyTo(arrayInValue);
+        long[] arrayInMinValue = new long[INPUTSIZE * 4];
+        inMinValue.copyTo(arrayInMinValue);
+        long[] arrayInMaxValue = new long[INPUTSIZE * 4];
+        inMaxValue.copyTo(arrayInMaxValue);
+        long[] arrayOut = new long[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 3 ; j++) {
+                // Extract the inputs.
+                ArgumentsUlongUlongUlongUlong args = new ArgumentsUlongUlongUlongUlong();
+                args.inValue = arrayInValue[i * 4 + j];
+                args.inMinValue = arrayInMinValue[i * 4 + j];
+                args.inMaxValue = arrayInMaxValue[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeClamp(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (args.out != arrayOut[i * 4 + j]) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inValue: ");
+                    message.append(String.format("0x%x", args.inValue));
+                    message.append("\n");
+                    message.append("Input inMinValue: ");
+                    message.append(String.format("0x%x", args.inMinValue));
+                    message.append("\n");
+                    message.append("Input inMaxValue: ");
+                    message.append(String.format("0x%x", args.inMaxValue));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(String.format("0x%x", args.out));
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("0x%x", arrayOut[i * 4 + j]));
+                    if (args.out != arrayOut[i * 4 + j]) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkClampUlong3Ulong3Ulong3Ulong3" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkClampUlong4Ulong4Ulong4Ulong4() {
+        Allocation inValue = createRandomAllocation(mRS, Element.DataType.UNSIGNED_64, 4, 0x3b907e27d9a08c51l, false);
+        Allocation inMinValue = createRandomAllocation(mRS, Element.DataType.UNSIGNED_64, 4, 0x2cbcb03c9bb336e3l, false);
+        Allocation inMaxValue = createRandomAllocation(mRS, Element.DataType.UNSIGNED_64, 4, 0x2cbcb0312d1028e9l, false);
+        enforceOrdering(inMinValue, inMaxValue);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_64, 4), INPUTSIZE);
+            script.set_gAllocInMinValue(inMinValue);
+            script.set_gAllocInMaxValue(inMaxValue);
+            script.forEach_testClampUlong4Ulong4Ulong4Ulong4(inValue, out);
+            verifyResultsClampUlong4Ulong4Ulong4Ulong4(inValue, inMinValue, inMaxValue, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testClampUlong4Ulong4Ulong4Ulong4: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_64, 4), INPUTSIZE);
+            scriptRelaxed.set_gAllocInMinValue(inMinValue);
+            scriptRelaxed.set_gAllocInMaxValue(inMaxValue);
+            scriptRelaxed.forEach_testClampUlong4Ulong4Ulong4Ulong4(inValue, out);
+            verifyResultsClampUlong4Ulong4Ulong4Ulong4(inValue, inMinValue, inMaxValue, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testClampUlong4Ulong4Ulong4Ulong4: " + e.toString());
+        }
+    }
+
+    private void verifyResultsClampUlong4Ulong4Ulong4Ulong4(Allocation inValue, Allocation inMinValue, Allocation inMaxValue, Allocation out, boolean relaxed) {
+        long[] arrayInValue = new long[INPUTSIZE * 4];
+        inValue.copyTo(arrayInValue);
+        long[] arrayInMinValue = new long[INPUTSIZE * 4];
+        inMinValue.copyTo(arrayInMinValue);
+        long[] arrayInMaxValue = new long[INPUTSIZE * 4];
+        inMaxValue.copyTo(arrayInMaxValue);
+        long[] arrayOut = new long[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 4 ; j++) {
+                // Extract the inputs.
+                ArgumentsUlongUlongUlongUlong args = new ArgumentsUlongUlongUlongUlong();
+                args.inValue = arrayInValue[i * 4 + j];
+                args.inMinValue = arrayInMinValue[i * 4 + j];
+                args.inMaxValue = arrayInMaxValue[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeClamp(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (args.out != arrayOut[i * 4 + j]) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inValue: ");
+                    message.append(String.format("0x%x", args.inValue));
+                    message.append("\n");
+                    message.append("Input inMinValue: ");
+                    message.append(String.format("0x%x", args.inMinValue));
+                    message.append("\n");
+                    message.append("Input inMaxValue: ");
+                    message.append(String.format("0x%x", args.inMaxValue));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(String.format("0x%x", args.out));
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("0x%x", arrayOut[i * 4 + j]));
+                    if (args.out != arrayOut[i * 4 + j]) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkClampUlong4Ulong4Ulong4Ulong4" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
     private void checkClampChar2CharCharChar2() {
         Allocation inValue = createRandomAllocation(mRS, Element.DataType.SIGNED_8, 2, 0xd6884bbb7c57a5d1l, false);
         Allocation inMinValue = createRandomAllocation(mRS, Element.DataType.SIGNED_8, 1, 0x3bf8830cc3b7db63l, false);
@@ -2511,7 +3133,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeClamp(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 2 + j]) {
                     valid = false;
@@ -2587,7 +3209,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeClamp(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 4 + j]) {
                     valid = false;
@@ -2663,7 +3285,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeClamp(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 4 + j]) {
                     valid = false;
@@ -2739,7 +3361,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeClamp(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 2 + j]) {
                     valid = false;
@@ -2815,7 +3437,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeClamp(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 4 + j]) {
                     valid = false;
@@ -2891,7 +3513,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeClamp(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 4 + j]) {
                     valid = false;
@@ -2967,7 +3589,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeClamp(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 2 + j]) {
                     valid = false;
@@ -3043,7 +3665,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeClamp(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 4 + j]) {
                     valid = false;
@@ -3119,7 +3741,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeClamp(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 4 + j]) {
                     valid = false;
@@ -3195,7 +3817,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeClamp(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 2 + j]) {
                     valid = false;
@@ -3271,7 +3893,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeClamp(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 4 + j]) {
                     valid = false;
@@ -3347,7 +3969,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeClamp(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 4 + j]) {
                     valid = false;
@@ -3423,7 +4045,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeClamp(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 2 + j]) {
                     valid = false;
@@ -3499,7 +4121,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeClamp(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 4 + j]) {
                     valid = false;
@@ -3575,7 +4197,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeClamp(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 4 + j]) {
                     valid = false;
@@ -3651,7 +4273,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeClamp(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 2 + j]) {
                     valid = false;
@@ -3727,7 +4349,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeClamp(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 4 + j]) {
                     valid = false;
@@ -3803,7 +4425,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeClamp(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 4 + j]) {
                     valid = false;
@@ -3835,6 +4457,462 @@
         }
     }
 
+    private void checkClampLong2LongLongLong2() {
+        Allocation inValue = createRandomAllocation(mRS, Element.DataType.SIGNED_64, 2, 0x19353a9f7c535bb5l, false);
+        Allocation inMinValue = createRandomAllocation(mRS, Element.DataType.SIGNED_64, 1, 0xee8dc7f38f83654fl, false);
+        Allocation inMaxValue = createRandomAllocation(mRS, Element.DataType.SIGNED_64, 1, 0xee8dc7e820e05755l, false);
+        enforceOrdering(inMinValue, inMaxValue);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_64, 2), INPUTSIZE);
+            script.set_gAllocInMinValue(inMinValue);
+            script.set_gAllocInMaxValue(inMaxValue);
+            script.forEach_testClampLong2LongLongLong2(inValue, out);
+            verifyResultsClampLong2LongLongLong2(inValue, inMinValue, inMaxValue, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testClampLong2LongLongLong2: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_64, 2), INPUTSIZE);
+            scriptRelaxed.set_gAllocInMinValue(inMinValue);
+            scriptRelaxed.set_gAllocInMaxValue(inMaxValue);
+            scriptRelaxed.forEach_testClampLong2LongLongLong2(inValue, out);
+            verifyResultsClampLong2LongLongLong2(inValue, inMinValue, inMaxValue, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testClampLong2LongLongLong2: " + e.toString());
+        }
+    }
+
+    private void verifyResultsClampLong2LongLongLong2(Allocation inValue, Allocation inMinValue, Allocation inMaxValue, Allocation out, boolean relaxed) {
+        long[] arrayInValue = new long[INPUTSIZE * 2];
+        inValue.copyTo(arrayInValue);
+        long[] arrayInMinValue = new long[INPUTSIZE * 1];
+        inMinValue.copyTo(arrayInMinValue);
+        long[] arrayInMaxValue = new long[INPUTSIZE * 1];
+        inMaxValue.copyTo(arrayInMaxValue);
+        long[] arrayOut = new long[INPUTSIZE * 2];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 2 ; j++) {
+                // Extract the inputs.
+                ArgumentsLongLongLongLong args = new ArgumentsLongLongLongLong();
+                args.inValue = arrayInValue[i * 2 + j];
+                args.inMinValue = arrayInMinValue[i];
+                args.inMaxValue = arrayInMaxValue[i];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeClamp(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (args.out != arrayOut[i * 2 + j]) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inValue: ");
+                    message.append(String.format("%d", args.inValue));
+                    message.append("\n");
+                    message.append("Input inMinValue: ");
+                    message.append(String.format("%d", args.inMinValue));
+                    message.append("\n");
+                    message.append("Input inMaxValue: ");
+                    message.append(String.format("%d", args.inMaxValue));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(String.format("%d", args.out));
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%d", arrayOut[i * 2 + j]));
+                    if (args.out != arrayOut[i * 2 + j]) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkClampLong2LongLongLong2" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkClampLong3LongLongLong3() {
+        Allocation inValue = createRandomAllocation(mRS, Element.DataType.SIGNED_64, 3, 0x8d537aff659e24c9l, false);
+        Allocation inMinValue = createRandomAllocation(mRS, Element.DataType.SIGNED_64, 1, 0x3de0e38714f313cbl, false);
+        Allocation inMaxValue = createRandomAllocation(mRS, Element.DataType.SIGNED_64, 1, 0x3de0e37ba65005d1l, false);
+        enforceOrdering(inMinValue, inMaxValue);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_64, 3), INPUTSIZE);
+            script.set_gAllocInMinValue(inMinValue);
+            script.set_gAllocInMaxValue(inMaxValue);
+            script.forEach_testClampLong3LongLongLong3(inValue, out);
+            verifyResultsClampLong3LongLongLong3(inValue, inMinValue, inMaxValue, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testClampLong3LongLongLong3: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_64, 3), INPUTSIZE);
+            scriptRelaxed.set_gAllocInMinValue(inMinValue);
+            scriptRelaxed.set_gAllocInMaxValue(inMaxValue);
+            scriptRelaxed.forEach_testClampLong3LongLongLong3(inValue, out);
+            verifyResultsClampLong3LongLongLong3(inValue, inMinValue, inMaxValue, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testClampLong3LongLongLong3: " + e.toString());
+        }
+    }
+
+    private void verifyResultsClampLong3LongLongLong3(Allocation inValue, Allocation inMinValue, Allocation inMaxValue, Allocation out, boolean relaxed) {
+        long[] arrayInValue = new long[INPUTSIZE * 4];
+        inValue.copyTo(arrayInValue);
+        long[] arrayInMinValue = new long[INPUTSIZE * 1];
+        inMinValue.copyTo(arrayInMinValue);
+        long[] arrayInMaxValue = new long[INPUTSIZE * 1];
+        inMaxValue.copyTo(arrayInMaxValue);
+        long[] arrayOut = new long[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 3 ; j++) {
+                // Extract the inputs.
+                ArgumentsLongLongLongLong args = new ArgumentsLongLongLongLong();
+                args.inValue = arrayInValue[i * 4 + j];
+                args.inMinValue = arrayInMinValue[i];
+                args.inMaxValue = arrayInMaxValue[i];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeClamp(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (args.out != arrayOut[i * 4 + j]) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inValue: ");
+                    message.append(String.format("%d", args.inValue));
+                    message.append("\n");
+                    message.append("Input inMinValue: ");
+                    message.append(String.format("%d", args.inMinValue));
+                    message.append("\n");
+                    message.append("Input inMaxValue: ");
+                    message.append(String.format("%d", args.inMaxValue));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(String.format("%d", args.out));
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%d", arrayOut[i * 4 + j]));
+                    if (args.out != arrayOut[i * 4 + j]) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkClampLong3LongLongLong3" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkClampLong4LongLongLong4() {
+        Allocation inValue = createRandomAllocation(mRS, Element.DataType.SIGNED_64, 4, 0x171bb5f4ee8edddl, false);
+        Allocation inMinValue = createRandomAllocation(mRS, Element.DataType.SIGNED_64, 1, 0x8d33ff1a9a62c247l, false);
+        Allocation inMaxValue = createRandomAllocation(mRS, Element.DataType.SIGNED_64, 1, 0x8d33ff0f2bbfb44dl, false);
+        enforceOrdering(inMinValue, inMaxValue);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_64, 4), INPUTSIZE);
+            script.set_gAllocInMinValue(inMinValue);
+            script.set_gAllocInMaxValue(inMaxValue);
+            script.forEach_testClampLong4LongLongLong4(inValue, out);
+            verifyResultsClampLong4LongLongLong4(inValue, inMinValue, inMaxValue, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testClampLong4LongLongLong4: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_64, 4), INPUTSIZE);
+            scriptRelaxed.set_gAllocInMinValue(inMinValue);
+            scriptRelaxed.set_gAllocInMaxValue(inMaxValue);
+            scriptRelaxed.forEach_testClampLong4LongLongLong4(inValue, out);
+            verifyResultsClampLong4LongLongLong4(inValue, inMinValue, inMaxValue, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testClampLong4LongLongLong4: " + e.toString());
+        }
+    }
+
+    private void verifyResultsClampLong4LongLongLong4(Allocation inValue, Allocation inMinValue, Allocation inMaxValue, Allocation out, boolean relaxed) {
+        long[] arrayInValue = new long[INPUTSIZE * 4];
+        inValue.copyTo(arrayInValue);
+        long[] arrayInMinValue = new long[INPUTSIZE * 1];
+        inMinValue.copyTo(arrayInMinValue);
+        long[] arrayInMaxValue = new long[INPUTSIZE * 1];
+        inMaxValue.copyTo(arrayInMaxValue);
+        long[] arrayOut = new long[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 4 ; j++) {
+                // Extract the inputs.
+                ArgumentsLongLongLongLong args = new ArgumentsLongLongLongLong();
+                args.inValue = arrayInValue[i * 4 + j];
+                args.inMinValue = arrayInMinValue[i];
+                args.inMaxValue = arrayInMaxValue[i];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeClamp(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (args.out != arrayOut[i * 4 + j]) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inValue: ");
+                    message.append(String.format("%d", args.inValue));
+                    message.append("\n");
+                    message.append("Input inMinValue: ");
+                    message.append(String.format("%d", args.inMinValue));
+                    message.append("\n");
+                    message.append("Input inMaxValue: ");
+                    message.append(String.format("%d", args.inMaxValue));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(String.format("%d", args.out));
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%d", arrayOut[i * 4 + j]));
+                    if (args.out != arrayOut[i * 4 + j]) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkClampLong4LongLongLong4" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkClampUlong2UlongUlongUlong2() {
+        Allocation inValue = createRandomAllocation(mRS, Element.DataType.UNSIGNED_64, 2, 0xf275dabaa7fa1bf7l, false);
+        Allocation inMinValue = createRandomAllocation(mRS, Element.DataType.UNSIGNED_64, 1, 0xf08a9e698d13b735l, false);
+        Allocation inMaxValue = createRandomAllocation(mRS, Element.DataType.UNSIGNED_64, 1, 0xf08a9e5e1e70a93bl, false);
+        enforceOrdering(inMinValue, inMaxValue);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_64, 2), INPUTSIZE);
+            script.set_gAllocInMinValue(inMinValue);
+            script.set_gAllocInMaxValue(inMaxValue);
+            script.forEach_testClampUlong2UlongUlongUlong2(inValue, out);
+            verifyResultsClampUlong2UlongUlongUlong2(inValue, inMinValue, inMaxValue, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testClampUlong2UlongUlongUlong2: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_64, 2), INPUTSIZE);
+            scriptRelaxed.set_gAllocInMinValue(inMinValue);
+            scriptRelaxed.set_gAllocInMaxValue(inMaxValue);
+            scriptRelaxed.forEach_testClampUlong2UlongUlongUlong2(inValue, out);
+            verifyResultsClampUlong2UlongUlongUlong2(inValue, inMinValue, inMaxValue, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testClampUlong2UlongUlongUlong2: " + e.toString());
+        }
+    }
+
+    private void verifyResultsClampUlong2UlongUlongUlong2(Allocation inValue, Allocation inMinValue, Allocation inMaxValue, Allocation out, boolean relaxed) {
+        long[] arrayInValue = new long[INPUTSIZE * 2];
+        inValue.copyTo(arrayInValue);
+        long[] arrayInMinValue = new long[INPUTSIZE * 1];
+        inMinValue.copyTo(arrayInMinValue);
+        long[] arrayInMaxValue = new long[INPUTSIZE * 1];
+        inMaxValue.copyTo(arrayInMaxValue);
+        long[] arrayOut = new long[INPUTSIZE * 2];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 2 ; j++) {
+                // Extract the inputs.
+                ArgumentsUlongUlongUlongUlong args = new ArgumentsUlongUlongUlongUlong();
+                args.inValue = arrayInValue[i * 2 + j];
+                args.inMinValue = arrayInMinValue[i];
+                args.inMaxValue = arrayInMaxValue[i];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeClamp(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (args.out != arrayOut[i * 2 + j]) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inValue: ");
+                    message.append(String.format("0x%x", args.inValue));
+                    message.append("\n");
+                    message.append("Input inMinValue: ");
+                    message.append(String.format("0x%x", args.inMinValue));
+                    message.append("\n");
+                    message.append("Input inMaxValue: ");
+                    message.append(String.format("0x%x", args.inMaxValue));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(String.format("0x%x", args.out));
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("0x%x", arrayOut[i * 2 + j]));
+                    if (args.out != arrayOut[i * 2 + j]) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkClampUlong2UlongUlongUlong2" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkClampUlong3UlongUlongUlong3() {
+        Allocation inValue = createRandomAllocation(mRS, Element.DataType.UNSIGNED_64, 3, 0x417c2f0620b6497dl, false);
+        Allocation inMinValue = createRandomAllocation(mRS, Element.DataType.UNSIGNED_64, 1, 0xfb4069f3c0421f27l, false);
+        Allocation inMaxValue = createRandomAllocation(mRS, Element.DataType.UNSIGNED_64, 1, 0xfb4069e8519f112dl, false);
+        enforceOrdering(inMinValue, inMaxValue);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_64, 3), INPUTSIZE);
+            script.set_gAllocInMinValue(inMinValue);
+            script.set_gAllocInMaxValue(inMaxValue);
+            script.forEach_testClampUlong3UlongUlongUlong3(inValue, out);
+            verifyResultsClampUlong3UlongUlongUlong3(inValue, inMinValue, inMaxValue, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testClampUlong3UlongUlongUlong3: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_64, 3), INPUTSIZE);
+            scriptRelaxed.set_gAllocInMinValue(inMinValue);
+            scriptRelaxed.set_gAllocInMaxValue(inMaxValue);
+            scriptRelaxed.forEach_testClampUlong3UlongUlongUlong3(inValue, out);
+            verifyResultsClampUlong3UlongUlongUlong3(inValue, inMinValue, inMaxValue, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testClampUlong3UlongUlongUlong3: " + e.toString());
+        }
+    }
+
+    private void verifyResultsClampUlong3UlongUlongUlong3(Allocation inValue, Allocation inMinValue, Allocation inMaxValue, Allocation out, boolean relaxed) {
+        long[] arrayInValue = new long[INPUTSIZE * 4];
+        inValue.copyTo(arrayInValue);
+        long[] arrayInMinValue = new long[INPUTSIZE * 1];
+        inMinValue.copyTo(arrayInMinValue);
+        long[] arrayInMaxValue = new long[INPUTSIZE * 1];
+        inMaxValue.copyTo(arrayInMaxValue);
+        long[] arrayOut = new long[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 3 ; j++) {
+                // Extract the inputs.
+                ArgumentsUlongUlongUlongUlong args = new ArgumentsUlongUlongUlongUlong();
+                args.inValue = arrayInValue[i * 4 + j];
+                args.inMinValue = arrayInMinValue[i];
+                args.inMaxValue = arrayInMaxValue[i];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeClamp(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (args.out != arrayOut[i * 4 + j]) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inValue: ");
+                    message.append(String.format("0x%x", args.inValue));
+                    message.append("\n");
+                    message.append("Input inMinValue: ");
+                    message.append(String.format("0x%x", args.inMinValue));
+                    message.append("\n");
+                    message.append("Input inMaxValue: ");
+                    message.append(String.format("0x%x", args.inMaxValue));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(String.format("0x%x", args.out));
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("0x%x", arrayOut[i * 4 + j]));
+                    if (args.out != arrayOut[i * 4 + j]) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkClampUlong3UlongUlongUlong3" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkClampUlong4UlongUlongUlong4() {
+        Allocation inValue = createRandomAllocation(mRS, Element.DataType.UNSIGNED_64, 4, 0x9082835199727703l, false);
+        Allocation inMinValue = createRandomAllocation(mRS, Element.DataType.UNSIGNED_64, 1, 0x5f6357df3708719l, false);
+        Allocation inMaxValue = createRandomAllocation(mRS, Element.DataType.UNSIGNED_64, 1, 0x5f6357284cd791fl, false);
+        enforceOrdering(inMinValue, inMaxValue);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_64, 4), INPUTSIZE);
+            script.set_gAllocInMinValue(inMinValue);
+            script.set_gAllocInMaxValue(inMaxValue);
+            script.forEach_testClampUlong4UlongUlongUlong4(inValue, out);
+            verifyResultsClampUlong4UlongUlongUlong4(inValue, inMinValue, inMaxValue, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testClampUlong4UlongUlongUlong4: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_64, 4), INPUTSIZE);
+            scriptRelaxed.set_gAllocInMinValue(inMinValue);
+            scriptRelaxed.set_gAllocInMaxValue(inMaxValue);
+            scriptRelaxed.forEach_testClampUlong4UlongUlongUlong4(inValue, out);
+            verifyResultsClampUlong4UlongUlongUlong4(inValue, inMinValue, inMaxValue, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testClampUlong4UlongUlongUlong4: " + e.toString());
+        }
+    }
+
+    private void verifyResultsClampUlong4UlongUlongUlong4(Allocation inValue, Allocation inMinValue, Allocation inMaxValue, Allocation out, boolean relaxed) {
+        long[] arrayInValue = new long[INPUTSIZE * 4];
+        inValue.copyTo(arrayInValue);
+        long[] arrayInMinValue = new long[INPUTSIZE * 1];
+        inMinValue.copyTo(arrayInMinValue);
+        long[] arrayInMaxValue = new long[INPUTSIZE * 1];
+        inMaxValue.copyTo(arrayInMaxValue);
+        long[] arrayOut = new long[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 4 ; j++) {
+                // Extract the inputs.
+                ArgumentsUlongUlongUlongUlong args = new ArgumentsUlongUlongUlongUlong();
+                args.inValue = arrayInValue[i * 4 + j];
+                args.inMinValue = arrayInMinValue[i];
+                args.inMaxValue = arrayInMaxValue[i];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeClamp(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (args.out != arrayOut[i * 4 + j]) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inValue: ");
+                    message.append(String.format("0x%x", args.inValue));
+                    message.append("\n");
+                    message.append("Input inMinValue: ");
+                    message.append(String.format("0x%x", args.inMinValue));
+                    message.append("\n");
+                    message.append("Input inMaxValue: ");
+                    message.append(String.format("0x%x", args.inMaxValue));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(String.format("0x%x", args.out));
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("0x%x", arrayOut[i * 4 + j]));
+                    if (args.out != arrayOut[i * 4 + j]) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkClampUlong4UlongUlongUlong4" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
     public void testClamp() {
         checkClampFloatFloatFloatFloat();
         checkClampFloat2Float2Float2Float2();
@@ -3867,6 +4945,14 @@
         checkClampUint2Uint2Uint2Uint2();
         checkClampUint3Uint3Uint3Uint3();
         checkClampUint4Uint4Uint4Uint4();
+        checkClampLongLongLongLong();
+        checkClampLong2Long2Long2Long2();
+        checkClampLong3Long3Long3Long3();
+        checkClampLong4Long4Long4Long4();
+        checkClampUlongUlongUlongUlong();
+        checkClampUlong2Ulong2Ulong2Ulong2();
+        checkClampUlong3Ulong3Ulong3Ulong3();
+        checkClampUlong4Ulong4Ulong4Ulong4();
         checkClampChar2CharCharChar2();
         checkClampChar3CharCharChar3();
         checkClampChar4CharCharChar4();
@@ -3885,5 +4971,11 @@
         checkClampUint2UintUintUint2();
         checkClampUint3UintUintUint3();
         checkClampUint4UintUintUint4();
+        checkClampLong2LongLongLong2();
+        checkClampLong3LongLongLong3();
+        checkClampLong4LongLongLong4();
+        checkClampUlong2UlongUlongUlong2();
+        checkClampUlong3UlongUlongUlong3();
+        checkClampUlong4UlongUlongUlong4();
     }
 }
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/TestClz.java b/tests/tests/renderscript/src/android/renderscript/cts/TestClz.java
index 8e9be9a..36606a1 100644
--- a/tests/tests/renderscript/src/android/renderscript/cts/TestClz.java
+++ b/tests/tests/renderscript/src/android/renderscript/cts/TestClz.java
@@ -70,7 +70,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeClz(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 1 + j]) {
                     valid = false;
@@ -127,7 +127,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeClz(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 2 + j]) {
                     valid = false;
@@ -184,7 +184,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeClz(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 4 + j]) {
                     valid = false;
@@ -241,7 +241,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeClz(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 4 + j]) {
                     valid = false;
@@ -303,7 +303,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeClz(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 1 + j]) {
                     valid = false;
@@ -360,7 +360,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeClz(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 2 + j]) {
                     valid = false;
@@ -417,7 +417,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeClz(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 4 + j]) {
                     valid = false;
@@ -474,7 +474,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeClz(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 4 + j]) {
                     valid = false;
@@ -536,7 +536,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeClz(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 1 + j]) {
                     valid = false;
@@ -593,7 +593,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeClz(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 2 + j]) {
                     valid = false;
@@ -650,7 +650,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeClz(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 4 + j]) {
                     valid = false;
@@ -707,7 +707,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeClz(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 4 + j]) {
                     valid = false;
@@ -769,7 +769,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeClz(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 1 + j]) {
                     valid = false;
@@ -826,7 +826,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeClz(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 2 + j]) {
                     valid = false;
@@ -883,7 +883,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeClz(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 4 + j]) {
                     valid = false;
@@ -940,7 +940,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeClz(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 4 + j]) {
                     valid = false;
@@ -1002,7 +1002,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeClz(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 1 + j]) {
                     valid = false;
@@ -1059,7 +1059,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeClz(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 2 + j]) {
                     valid = false;
@@ -1116,7 +1116,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeClz(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 4 + j]) {
                     valid = false;
@@ -1173,7 +1173,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeClz(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 4 + j]) {
                     valid = false;
@@ -1235,7 +1235,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeClz(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 1 + j]) {
                     valid = false;
@@ -1292,7 +1292,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeClz(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 2 + j]) {
                     valid = false;
@@ -1349,7 +1349,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeClz(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 4 + j]) {
                     valid = false;
@@ -1406,7 +1406,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeClz(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 4 + j]) {
                     valid = false;
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/TestConvert.java b/tests/tests/renderscript/src/android/renderscript/cts/TestConvert.java
index eae020d..3945f84 100644
--- a/tests/tests/renderscript/src/android/renderscript/cts/TestConvert.java
+++ b/tests/tests/renderscript/src/android/renderscript/cts/TestConvert.java
@@ -70,7 +70,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeConvert(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 2 + j])) {
                     valid = false;
@@ -129,7 +129,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeConvert(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 4 + j])) {
                     valid = false;
@@ -188,7 +188,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeConvert(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 4 + j])) {
                     valid = false;
@@ -252,7 +252,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeConvert(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 2 + j])) {
                     valid = false;
@@ -310,7 +310,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeConvert(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 4 + j])) {
                     valid = false;
@@ -368,7 +368,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeConvert(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 4 + j])) {
                     valid = false;
@@ -431,7 +431,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeConvert(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 2 + j])) {
                     valid = false;
@@ -489,7 +489,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeConvert(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 4 + j])) {
                     valid = false;
@@ -547,7 +547,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeConvert(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 4 + j])) {
                     valid = false;
@@ -610,7 +610,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeConvert(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 2 + j])) {
                     valid = false;
@@ -668,7 +668,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeConvert(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 4 + j])) {
                     valid = false;
@@ -726,7 +726,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeConvert(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 4 + j])) {
                     valid = false;
@@ -789,7 +789,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeConvert(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 2 + j])) {
                     valid = false;
@@ -847,7 +847,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeConvert(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 4 + j])) {
                     valid = false;
@@ -905,7 +905,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeConvert(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 4 + j])) {
                     valid = false;
@@ -968,7 +968,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeConvert(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 2 + j])) {
                     valid = false;
@@ -1026,7 +1026,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeConvert(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 4 + j])) {
                     valid = false;
@@ -1084,7 +1084,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeConvert(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 4 + j])) {
                     valid = false;
@@ -1147,7 +1147,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeConvert(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 2 + j])) {
                     valid = false;
@@ -1205,7 +1205,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeConvert(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 4 + j])) {
                     valid = false;
@@ -1263,7 +1263,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeConvert(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 4 + j])) {
                     valid = false;
@@ -1326,7 +1326,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeConvert(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 2 + j]) {
                     valid = false;
@@ -1384,7 +1384,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeConvert(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 4 + j]) {
                     valid = false;
@@ -1442,7 +1442,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeConvert(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 4 + j]) {
                     valid = false;
@@ -1505,7 +1505,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeConvert(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 2 + j]) {
                     valid = false;
@@ -1562,7 +1562,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeConvert(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 4 + j]) {
                     valid = false;
@@ -1619,7 +1619,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeConvert(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 4 + j]) {
                     valid = false;
@@ -1681,7 +1681,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeConvert(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 2 + j]) {
                     valid = false;
@@ -1738,7 +1738,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeConvert(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 4 + j]) {
                     valid = false;
@@ -1795,7 +1795,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeConvert(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 4 + j]) {
                     valid = false;
@@ -1857,7 +1857,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeConvert(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 2 + j]) {
                     valid = false;
@@ -1914,7 +1914,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeConvert(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 4 + j]) {
                     valid = false;
@@ -1971,7 +1971,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeConvert(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 4 + j]) {
                     valid = false;
@@ -2033,7 +2033,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeConvert(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 2 + j]) {
                     valid = false;
@@ -2090,7 +2090,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeConvert(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 4 + j]) {
                     valid = false;
@@ -2147,7 +2147,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeConvert(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 4 + j]) {
                     valid = false;
@@ -2209,7 +2209,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeConvert(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 2 + j]) {
                     valid = false;
@@ -2266,7 +2266,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeConvert(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 4 + j]) {
                     valid = false;
@@ -2323,7 +2323,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeConvert(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 4 + j]) {
                     valid = false;
@@ -2385,7 +2385,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeConvert(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 2 + j]) {
                     valid = false;
@@ -2442,7 +2442,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeConvert(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 4 + j]) {
                     valid = false;
@@ -2499,7 +2499,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeConvert(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 4 + j]) {
                     valid = false;
@@ -2561,7 +2561,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeConvert(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 2 + j]) {
                     valid = false;
@@ -2619,7 +2619,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeConvert(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 4 + j]) {
                     valid = false;
@@ -2677,7 +2677,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeConvert(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 4 + j]) {
                     valid = false;
@@ -2740,7 +2740,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeConvert(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 2 + j]) {
                     valid = false;
@@ -2797,7 +2797,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeConvert(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 4 + j]) {
                     valid = false;
@@ -2854,7 +2854,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeConvert(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 4 + j]) {
                     valid = false;
@@ -2916,7 +2916,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeConvert(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 2 + j]) {
                     valid = false;
@@ -2973,7 +2973,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeConvert(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 4 + j]) {
                     valid = false;
@@ -3030,7 +3030,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeConvert(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 4 + j]) {
                     valid = false;
@@ -3092,7 +3092,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeConvert(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 2 + j]) {
                     valid = false;
@@ -3149,7 +3149,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeConvert(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 4 + j]) {
                     valid = false;
@@ -3206,7 +3206,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeConvert(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 4 + j]) {
                     valid = false;
@@ -3268,7 +3268,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeConvert(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 2 + j]) {
                     valid = false;
@@ -3325,7 +3325,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeConvert(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 4 + j]) {
                     valid = false;
@@ -3382,7 +3382,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeConvert(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 4 + j]) {
                     valid = false;
@@ -3444,7 +3444,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeConvert(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 2 + j]) {
                     valid = false;
@@ -3501,7 +3501,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeConvert(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 4 + j]) {
                     valid = false;
@@ -3558,7 +3558,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeConvert(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 4 + j]) {
                     valid = false;
@@ -3620,7 +3620,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeConvert(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 2 + j]) {
                     valid = false;
@@ -3677,7 +3677,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeConvert(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 4 + j]) {
                     valid = false;
@@ -3734,7 +3734,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeConvert(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 4 + j]) {
                     valid = false;
@@ -3796,7 +3796,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeConvert(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 2 + j]) {
                     valid = false;
@@ -3854,7 +3854,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeConvert(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 4 + j]) {
                     valid = false;
@@ -3912,7 +3912,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeConvert(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 4 + j]) {
                     valid = false;
@@ -3975,7 +3975,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeConvert(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 2 + j]) {
                     valid = false;
@@ -4032,7 +4032,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeConvert(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 4 + j]) {
                     valid = false;
@@ -4089,7 +4089,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeConvert(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 4 + j]) {
                     valid = false;
@@ -4151,7 +4151,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeConvert(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 2 + j]) {
                     valid = false;
@@ -4208,7 +4208,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeConvert(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 4 + j]) {
                     valid = false;
@@ -4265,7 +4265,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeConvert(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 4 + j]) {
                     valid = false;
@@ -4327,7 +4327,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeConvert(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 2 + j]) {
                     valid = false;
@@ -4384,7 +4384,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeConvert(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 4 + j]) {
                     valid = false;
@@ -4441,7 +4441,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeConvert(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 4 + j]) {
                     valid = false;
@@ -4503,7 +4503,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeConvert(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 2 + j]) {
                     valid = false;
@@ -4560,7 +4560,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeConvert(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 4 + j]) {
                     valid = false;
@@ -4617,7 +4617,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeConvert(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 4 + j]) {
                     valid = false;
@@ -4679,7 +4679,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeConvert(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 2 + j]) {
                     valid = false;
@@ -4736,7 +4736,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeConvert(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 4 + j]) {
                     valid = false;
@@ -4793,7 +4793,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeConvert(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 4 + j]) {
                     valid = false;
@@ -4855,7 +4855,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeConvert(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 2 + j]) {
                     valid = false;
@@ -4912,7 +4912,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeConvert(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 4 + j]) {
                     valid = false;
@@ -4969,7 +4969,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeConvert(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 4 + j]) {
                     valid = false;
@@ -5031,7 +5031,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeConvert(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 2 + j]) {
                     valid = false;
@@ -5089,7 +5089,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeConvert(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 4 + j]) {
                     valid = false;
@@ -5147,7 +5147,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeConvert(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 4 + j]) {
                     valid = false;
@@ -5210,7 +5210,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeConvert(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 2 + j]) {
                     valid = false;
@@ -5267,7 +5267,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeConvert(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 4 + j]) {
                     valid = false;
@@ -5324,7 +5324,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeConvert(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 4 + j]) {
                     valid = false;
@@ -5386,7 +5386,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeConvert(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 2 + j]) {
                     valid = false;
@@ -5443,7 +5443,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeConvert(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 4 + j]) {
                     valid = false;
@@ -5500,7 +5500,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeConvert(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 4 + j]) {
                     valid = false;
@@ -5562,7 +5562,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeConvert(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 2 + j]) {
                     valid = false;
@@ -5619,7 +5619,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeConvert(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 4 + j]) {
                     valid = false;
@@ -5676,7 +5676,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeConvert(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 4 + j]) {
                     valid = false;
@@ -5738,7 +5738,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeConvert(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 2 + j]) {
                     valid = false;
@@ -5795,7 +5795,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeConvert(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 4 + j]) {
                     valid = false;
@@ -5852,7 +5852,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeConvert(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 4 + j]) {
                     valid = false;
@@ -5914,7 +5914,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeConvert(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 2 + j]) {
                     valid = false;
@@ -5971,7 +5971,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeConvert(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 4 + j]) {
                     valid = false;
@@ -6028,7 +6028,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeConvert(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 4 + j]) {
                     valid = false;
@@ -6090,7 +6090,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeConvert(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 2 + j]) {
                     valid = false;
@@ -6147,7 +6147,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeConvert(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 4 + j]) {
                     valid = false;
@@ -6204,7 +6204,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeConvert(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 4 + j]) {
                     valid = false;
@@ -6266,7 +6266,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeConvert(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 2 + j]) {
                     valid = false;
@@ -6324,7 +6324,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeConvert(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 4 + j]) {
                     valid = false;
@@ -6382,7 +6382,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeConvert(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 4 + j]) {
                     valid = false;
@@ -6445,7 +6445,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeConvert(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 2 + j]) {
                     valid = false;
@@ -6502,7 +6502,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeConvert(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 4 + j]) {
                     valid = false;
@@ -6559,7 +6559,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeConvert(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 4 + j]) {
                     valid = false;
@@ -6621,7 +6621,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeConvert(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 2 + j]) {
                     valid = false;
@@ -6678,7 +6678,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeConvert(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 4 + j]) {
                     valid = false;
@@ -6735,7 +6735,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeConvert(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 4 + j]) {
                     valid = false;
@@ -6797,7 +6797,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeConvert(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 2 + j]) {
                     valid = false;
@@ -6854,7 +6854,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeConvert(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 4 + j]) {
                     valid = false;
@@ -6911,7 +6911,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeConvert(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 4 + j]) {
                     valid = false;
@@ -6973,7 +6973,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeConvert(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 2 + j]) {
                     valid = false;
@@ -7030,7 +7030,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeConvert(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 4 + j]) {
                     valid = false;
@@ -7087,7 +7087,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeConvert(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 4 + j]) {
                     valid = false;
@@ -7149,7 +7149,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeConvert(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 2 + j]) {
                     valid = false;
@@ -7206,7 +7206,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeConvert(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 4 + j]) {
                     valid = false;
@@ -7263,7 +7263,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeConvert(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 4 + j]) {
                     valid = false;
@@ -7325,7 +7325,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeConvert(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 2 + j]) {
                     valid = false;
@@ -7382,7 +7382,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeConvert(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 4 + j]) {
                     valid = false;
@@ -7439,7 +7439,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeConvert(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 4 + j]) {
                     valid = false;
@@ -7501,7 +7501,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeConvert(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 2 + j]) {
                     valid = false;
@@ -7559,7 +7559,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeConvert(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 4 + j]) {
                     valid = false;
@@ -7617,7 +7617,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeConvert(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 4 + j]) {
                     valid = false;
@@ -7680,7 +7680,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeConvert(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 2 + j]) {
                     valid = false;
@@ -7737,7 +7737,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeConvert(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 4 + j]) {
                     valid = false;
@@ -7794,7 +7794,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeConvert(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 4 + j]) {
                     valid = false;
@@ -7856,7 +7856,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeConvert(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 2 + j]) {
                     valid = false;
@@ -7913,7 +7913,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeConvert(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 4 + j]) {
                     valid = false;
@@ -7970,7 +7970,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeConvert(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 4 + j]) {
                     valid = false;
@@ -8032,7 +8032,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeConvert(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 2 + j]) {
                     valid = false;
@@ -8089,7 +8089,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeConvert(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 4 + j]) {
                     valid = false;
@@ -8146,7 +8146,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeConvert(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 4 + j]) {
                     valid = false;
@@ -8208,7 +8208,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeConvert(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 2 + j]) {
                     valid = false;
@@ -8265,7 +8265,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeConvert(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 4 + j]) {
                     valid = false;
@@ -8322,7 +8322,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeConvert(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 4 + j]) {
                     valid = false;
@@ -8384,7 +8384,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeConvert(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 2 + j]) {
                     valid = false;
@@ -8441,7 +8441,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeConvert(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 4 + j]) {
                     valid = false;
@@ -8498,7 +8498,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeConvert(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 4 + j]) {
                     valid = false;
@@ -8560,7 +8560,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeConvert(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 2 + j]) {
                     valid = false;
@@ -8617,7 +8617,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeConvert(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 4 + j]) {
                     valid = false;
@@ -8674,7 +8674,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeConvert(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 4 + j]) {
                     valid = false;
@@ -8700,6 +8700,9060 @@
         }
     }
 
+    public class ArgumentsDoubleDouble {
+        public double inV;
+        public Floaty out;
+    }
+
+    private void checkConvertDouble2Double2() {
+        Allocation inV = createRandomFloatAllocation(mRS, Element.DataType.FLOAT_64, 2, 0x345b4a823902786el, -8.5390423905960001625e+307, 8.5390423905960001625e+307);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_64, 2), INPUTSIZE);
+            script.forEach_testConvertDouble2Double2Double2(inV, out);
+            verifyResultsConvertDouble2Double2(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertDouble2Double2Double2: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_64, 2), INPUTSIZE);
+            scriptRelaxed.forEach_testConvertDouble2Double2Double2(inV, out);
+            verifyResultsConvertDouble2Double2(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertDouble2Double2Double2: " + e.toString());
+        }
+    }
+
+    private void verifyResultsConvertDouble2Double2(Allocation inV, Allocation out, boolean relaxed) {
+        double[] arrayInV = new double[INPUTSIZE * 2];
+        inV.copyTo(arrayInV);
+        double[] arrayOut = new double[INPUTSIZE * 2];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 2 ; j++) {
+                // Extract the inputs.
+                ArgumentsDoubleDouble args = new ArgumentsDoubleDouble();
+                args.inV = arrayInV[i * 2 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeConvert(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (!args.out.couldBe(arrayOut[i * 2 + j])) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("%24.8g %16x %31a",
+                            args.inV, Double.doubleToRawLongBits(args.inV), args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(args.out.toString());
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%24.8g %16x %31a",
+                            arrayOut[i * 2 + j], Double.doubleToRawLongBits(arrayOut[i * 2 + j]), arrayOut[i * 2 + j]));
+                    if (!args.out.couldBe(arrayOut[i * 2 + j])) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkConvertDouble2Double2" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkConvertDouble3Double3() {
+        Allocation inV = createRandomFloatAllocation(mRS, Element.DataType.FLOAT_64, 3, 0x34a812098f5e099al, -8.5390423905960001625e+307, 8.5390423905960001625e+307);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_64, 3), INPUTSIZE);
+            script.forEach_testConvertDouble3Double3Double3(inV, out);
+            verifyResultsConvertDouble3Double3(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertDouble3Double3Double3: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_64, 3), INPUTSIZE);
+            scriptRelaxed.forEach_testConvertDouble3Double3Double3(inV, out);
+            verifyResultsConvertDouble3Double3(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertDouble3Double3Double3: " + e.toString());
+        }
+    }
+
+    private void verifyResultsConvertDouble3Double3(Allocation inV, Allocation out, boolean relaxed) {
+        double[] arrayInV = new double[INPUTSIZE * 4];
+        inV.copyTo(arrayInV);
+        double[] arrayOut = new double[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 3 ; j++) {
+                // Extract the inputs.
+                ArgumentsDoubleDouble args = new ArgumentsDoubleDouble();
+                args.inV = arrayInV[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeConvert(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("%24.8g %16x %31a",
+                            args.inV, Double.doubleToRawLongBits(args.inV), args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(args.out.toString());
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%24.8g %16x %31a",
+                            arrayOut[i * 4 + j], Double.doubleToRawLongBits(arrayOut[i * 4 + j]), arrayOut[i * 4 + j]));
+                    if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkConvertDouble3Double3" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkConvertDouble4Double4() {
+        Allocation inV = createRandomFloatAllocation(mRS, Element.DataType.FLOAT_64, 4, 0x34f4d990e5b99ac6l, -8.5390423905960001625e+307, 8.5390423905960001625e+307);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_64, 4), INPUTSIZE);
+            script.forEach_testConvertDouble4Double4Double4(inV, out);
+            verifyResultsConvertDouble4Double4(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertDouble4Double4Double4: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_64, 4), INPUTSIZE);
+            scriptRelaxed.forEach_testConvertDouble4Double4Double4(inV, out);
+            verifyResultsConvertDouble4Double4(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertDouble4Double4Double4: " + e.toString());
+        }
+    }
+
+    private void verifyResultsConvertDouble4Double4(Allocation inV, Allocation out, boolean relaxed) {
+        double[] arrayInV = new double[INPUTSIZE * 4];
+        inV.copyTo(arrayInV);
+        double[] arrayOut = new double[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 4 ; j++) {
+                // Extract the inputs.
+                ArgumentsDoubleDouble args = new ArgumentsDoubleDouble();
+                args.inV = arrayInV[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeConvert(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("%24.8g %16x %31a",
+                            args.inV, Double.doubleToRawLongBits(args.inV), args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(args.out.toString());
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%24.8g %16x %31a",
+                            arrayOut[i * 4 + j], Double.doubleToRawLongBits(arrayOut[i * 4 + j]), arrayOut[i * 4 + j]));
+                    if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkConvertDouble4Double4" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    public class ArgumentsLongDouble {
+        public long inV;
+        public Floaty out;
+    }
+
+    private void checkConvertLong2Double2() {
+        Allocation inV = createRandomIntegerAllocation(mRS, Element.DataType.SIGNED_64, 2, 0x7b7807124c70299bl, true, 63);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_64, 2), INPUTSIZE);
+            script.forEach_testConvertDouble2Long2Double2(inV, out);
+            verifyResultsConvertLong2Double2(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertDouble2Long2Double2: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_64, 2), INPUTSIZE);
+            scriptRelaxed.forEach_testConvertDouble2Long2Double2(inV, out);
+            verifyResultsConvertLong2Double2(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertDouble2Long2Double2: " + e.toString());
+        }
+    }
+
+    private void verifyResultsConvertLong2Double2(Allocation inV, Allocation out, boolean relaxed) {
+        long[] arrayInV = new long[INPUTSIZE * 2];
+        inV.copyTo(arrayInV);
+        double[] arrayOut = new double[INPUTSIZE * 2];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 2 ; j++) {
+                // Extract the inputs.
+                ArgumentsLongDouble args = new ArgumentsLongDouble();
+                args.inV = arrayInV[i * 2 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeConvert(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (!args.out.couldBe(arrayOut[i * 2 + j])) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("%d", args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(args.out.toString());
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%24.8g %16x %31a",
+                            arrayOut[i * 2 + j], Double.doubleToRawLongBits(arrayOut[i * 2 + j]), arrayOut[i * 2 + j]));
+                    if (!args.out.couldBe(arrayOut[i * 2 + j])) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkConvertLong2Double2" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkConvertLong3Double3() {
+        Allocation inV = createRandomIntegerAllocation(mRS, Element.DataType.SIGNED_64, 3, 0x7bc4ce99a2cbbac7l, true, 63);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_64, 3), INPUTSIZE);
+            script.forEach_testConvertDouble3Long3Double3(inV, out);
+            verifyResultsConvertLong3Double3(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertDouble3Long3Double3: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_64, 3), INPUTSIZE);
+            scriptRelaxed.forEach_testConvertDouble3Long3Double3(inV, out);
+            verifyResultsConvertLong3Double3(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertDouble3Long3Double3: " + e.toString());
+        }
+    }
+
+    private void verifyResultsConvertLong3Double3(Allocation inV, Allocation out, boolean relaxed) {
+        long[] arrayInV = new long[INPUTSIZE * 4];
+        inV.copyTo(arrayInV);
+        double[] arrayOut = new double[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 3 ; j++) {
+                // Extract the inputs.
+                ArgumentsLongDouble args = new ArgumentsLongDouble();
+                args.inV = arrayInV[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeConvert(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("%d", args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(args.out.toString());
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%24.8g %16x %31a",
+                            arrayOut[i * 4 + j], Double.doubleToRawLongBits(arrayOut[i * 4 + j]), arrayOut[i * 4 + j]));
+                    if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkConvertLong3Double3" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkConvertLong4Double4() {
+        Allocation inV = createRandomIntegerAllocation(mRS, Element.DataType.SIGNED_64, 4, 0x7c119620f9274bf3l, true, 63);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_64, 4), INPUTSIZE);
+            script.forEach_testConvertDouble4Long4Double4(inV, out);
+            verifyResultsConvertLong4Double4(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertDouble4Long4Double4: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_64, 4), INPUTSIZE);
+            scriptRelaxed.forEach_testConvertDouble4Long4Double4(inV, out);
+            verifyResultsConvertLong4Double4(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertDouble4Long4Double4: " + e.toString());
+        }
+    }
+
+    private void verifyResultsConvertLong4Double4(Allocation inV, Allocation out, boolean relaxed) {
+        long[] arrayInV = new long[INPUTSIZE * 4];
+        inV.copyTo(arrayInV);
+        double[] arrayOut = new double[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 4 ; j++) {
+                // Extract the inputs.
+                ArgumentsLongDouble args = new ArgumentsLongDouble();
+                args.inV = arrayInV[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeConvert(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("%d", args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(args.out.toString());
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%24.8g %16x %31a",
+                            arrayOut[i * 4 + j], Double.doubleToRawLongBits(arrayOut[i * 4 + j]), arrayOut[i * 4 + j]));
+                    if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkConvertLong4Double4" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    public class ArgumentsUlongDouble {
+        public long inV;
+        public Floaty out;
+    }
+
+    private void checkConvertUlong2Double2() {
+        Allocation inV = createRandomIntegerAllocation(mRS, Element.DataType.UNSIGNED_64, 2, 0xaa17685979bc7954l, false, 64);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_64, 2), INPUTSIZE);
+            script.forEach_testConvertDouble2Ulong2Double2(inV, out);
+            verifyResultsConvertUlong2Double2(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertDouble2Ulong2Double2: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_64, 2), INPUTSIZE);
+            scriptRelaxed.forEach_testConvertDouble2Ulong2Double2(inV, out);
+            verifyResultsConvertUlong2Double2(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertDouble2Ulong2Double2: " + e.toString());
+        }
+    }
+
+    private void verifyResultsConvertUlong2Double2(Allocation inV, Allocation out, boolean relaxed) {
+        long[] arrayInV = new long[INPUTSIZE * 2];
+        inV.copyTo(arrayInV);
+        double[] arrayOut = new double[INPUTSIZE * 2];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 2 ; j++) {
+                // Extract the inputs.
+                ArgumentsUlongDouble args = new ArgumentsUlongDouble();
+                args.inV = arrayInV[i * 2 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeConvert(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (!args.out.couldBe(arrayOut[i * 2 + j])) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("0x%x", args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(args.out.toString());
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%24.8g %16x %31a",
+                            arrayOut[i * 2 + j], Double.doubleToRawLongBits(arrayOut[i * 2 + j]), arrayOut[i * 2 + j]));
+                    if (!args.out.couldBe(arrayOut[i * 2 + j])) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkConvertUlong2Double2" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkConvertUlong3Double3() {
+        Allocation inV = createRandomIntegerAllocation(mRS, Element.DataType.UNSIGNED_64, 3, 0xaa642fe0d0180a80l, false, 64);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_64, 3), INPUTSIZE);
+            script.forEach_testConvertDouble3Ulong3Double3(inV, out);
+            verifyResultsConvertUlong3Double3(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertDouble3Ulong3Double3: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_64, 3), INPUTSIZE);
+            scriptRelaxed.forEach_testConvertDouble3Ulong3Double3(inV, out);
+            verifyResultsConvertUlong3Double3(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertDouble3Ulong3Double3: " + e.toString());
+        }
+    }
+
+    private void verifyResultsConvertUlong3Double3(Allocation inV, Allocation out, boolean relaxed) {
+        long[] arrayInV = new long[INPUTSIZE * 4];
+        inV.copyTo(arrayInV);
+        double[] arrayOut = new double[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 3 ; j++) {
+                // Extract the inputs.
+                ArgumentsUlongDouble args = new ArgumentsUlongDouble();
+                args.inV = arrayInV[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeConvert(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("0x%x", args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(args.out.toString());
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%24.8g %16x %31a",
+                            arrayOut[i * 4 + j], Double.doubleToRawLongBits(arrayOut[i * 4 + j]), arrayOut[i * 4 + j]));
+                    if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkConvertUlong3Double3" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkConvertUlong4Double4() {
+        Allocation inV = createRandomIntegerAllocation(mRS, Element.DataType.UNSIGNED_64, 4, 0xaab0f76826739bacl, false, 64);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_64, 4), INPUTSIZE);
+            script.forEach_testConvertDouble4Ulong4Double4(inV, out);
+            verifyResultsConvertUlong4Double4(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertDouble4Ulong4Double4: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_64, 4), INPUTSIZE);
+            scriptRelaxed.forEach_testConvertDouble4Ulong4Double4(inV, out);
+            verifyResultsConvertUlong4Double4(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertDouble4Ulong4Double4: " + e.toString());
+        }
+    }
+
+    private void verifyResultsConvertUlong4Double4(Allocation inV, Allocation out, boolean relaxed) {
+        long[] arrayInV = new long[INPUTSIZE * 4];
+        inV.copyTo(arrayInV);
+        double[] arrayOut = new double[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 4 ; j++) {
+                // Extract the inputs.
+                ArgumentsUlongDouble args = new ArgumentsUlongDouble();
+                args.inV = arrayInV[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeConvert(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("0x%x", args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(args.out.toString());
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%24.8g %16x %31a",
+                            arrayOut[i * 4 + j], Double.doubleToRawLongBits(arrayOut[i * 4 + j]), arrayOut[i * 4 + j]));
+                    if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkConvertUlong4Double4" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    public class ArgumentsDoubleLong {
+        public double inV;
+        public long out;
+    }
+
+    private void checkConvertDouble2Long2() {
+        Allocation inV = createRandomFloatAllocation(mRS, Element.DataType.FLOAT_64, 2, 0xcbf84dc0430cbe95l, -9.2233720368547747840e+18, 9.2233720368547747840e+18);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_64, 2), INPUTSIZE);
+            script.forEach_testConvertLong2Double2Long2(inV, out);
+            verifyResultsConvertDouble2Long2(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertLong2Double2Long2: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_64, 2), INPUTSIZE);
+            scriptRelaxed.forEach_testConvertLong2Double2Long2(inV, out);
+            verifyResultsConvertDouble2Long2(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertLong2Double2Long2: " + e.toString());
+        }
+    }
+
+    private void verifyResultsConvertDouble2Long2(Allocation inV, Allocation out, boolean relaxed) {
+        double[] arrayInV = new double[INPUTSIZE * 2];
+        inV.copyTo(arrayInV);
+        long[] arrayOut = new long[INPUTSIZE * 2];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 2 ; j++) {
+                // Extract the inputs.
+                ArgumentsDoubleLong args = new ArgumentsDoubleLong();
+                args.inV = arrayInV[i * 2 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeConvert(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (args.out != arrayOut[i * 2 + j]) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("%24.8g %16x %31a",
+                            args.inV, Double.doubleToRawLongBits(args.inV), args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(String.format("%d", args.out));
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%d", arrayOut[i * 2 + j]));
+                    if (args.out != arrayOut[i * 2 + j]) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkConvertDouble2Long2" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkConvertDouble3Long3() {
+        Allocation inV = createRandomFloatAllocation(mRS, Element.DataType.FLOAT_64, 3, 0xcbf85861a2148389l, -9.2233720368547747840e+18, 9.2233720368547747840e+18);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_64, 3), INPUTSIZE);
+            script.forEach_testConvertLong3Double3Long3(inV, out);
+            verifyResultsConvertDouble3Long3(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertLong3Double3Long3: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_64, 3), INPUTSIZE);
+            scriptRelaxed.forEach_testConvertLong3Double3Long3(inV, out);
+            verifyResultsConvertDouble3Long3(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertLong3Double3Long3: " + e.toString());
+        }
+    }
+
+    private void verifyResultsConvertDouble3Long3(Allocation inV, Allocation out, boolean relaxed) {
+        double[] arrayInV = new double[INPUTSIZE * 4];
+        inV.copyTo(arrayInV);
+        long[] arrayOut = new long[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 3 ; j++) {
+                // Extract the inputs.
+                ArgumentsDoubleLong args = new ArgumentsDoubleLong();
+                args.inV = arrayInV[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeConvert(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (args.out != arrayOut[i * 4 + j]) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("%24.8g %16x %31a",
+                            args.inV, Double.doubleToRawLongBits(args.inV), args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(String.format("%d", args.out));
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%d", arrayOut[i * 4 + j]));
+                    if (args.out != arrayOut[i * 4 + j]) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkConvertDouble3Long3" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkConvertDouble4Long4() {
+        Allocation inV = createRandomFloatAllocation(mRS, Element.DataType.FLOAT_64, 4, 0xcbf86303011c487dl, -9.2233720368547747840e+18, 9.2233720368547747840e+18);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_64, 4), INPUTSIZE);
+            script.forEach_testConvertLong4Double4Long4(inV, out);
+            verifyResultsConvertDouble4Long4(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertLong4Double4Long4: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_64, 4), INPUTSIZE);
+            scriptRelaxed.forEach_testConvertLong4Double4Long4(inV, out);
+            verifyResultsConvertDouble4Long4(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertLong4Double4Long4: " + e.toString());
+        }
+    }
+
+    private void verifyResultsConvertDouble4Long4(Allocation inV, Allocation out, boolean relaxed) {
+        double[] arrayInV = new double[INPUTSIZE * 4];
+        inV.copyTo(arrayInV);
+        long[] arrayOut = new long[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 4 ; j++) {
+                // Extract the inputs.
+                ArgumentsDoubleLong args = new ArgumentsDoubleLong();
+                args.inV = arrayInV[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeConvert(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (args.out != arrayOut[i * 4 + j]) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("%24.8g %16x %31a",
+                            args.inV, Double.doubleToRawLongBits(args.inV), args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(String.format("%d", args.out));
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%d", arrayOut[i * 4 + j]));
+                    if (args.out != arrayOut[i * 4 + j]) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkConvertDouble4Long4" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    public class ArgumentsLongLong {
+        public long inV;
+        public long out;
+    }
+
+    private void checkConvertLong2Long2() {
+        Allocation inV = createRandomIntegerAllocation(mRS, Element.DataType.SIGNED_64, 2, 0xb570c607c81d242al, true, 63);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_64, 2), INPUTSIZE);
+            script.forEach_testConvertLong2Long2Long2(inV, out);
+            verifyResultsConvertLong2Long2(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertLong2Long2Long2: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_64, 2), INPUTSIZE);
+            scriptRelaxed.forEach_testConvertLong2Long2Long2(inV, out);
+            verifyResultsConvertLong2Long2(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertLong2Long2Long2: " + e.toString());
+        }
+    }
+
+    private void verifyResultsConvertLong2Long2(Allocation inV, Allocation out, boolean relaxed) {
+        long[] arrayInV = new long[INPUTSIZE * 2];
+        inV.copyTo(arrayInV);
+        long[] arrayOut = new long[INPUTSIZE * 2];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 2 ; j++) {
+                // Extract the inputs.
+                ArgumentsLongLong args = new ArgumentsLongLong();
+                args.inV = arrayInV[i * 2 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeConvert(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (args.out != arrayOut[i * 2 + j]) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("%d", args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(String.format("%d", args.out));
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%d", arrayOut[i * 2 + j]));
+                    if (args.out != arrayOut[i * 2 + j]) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkConvertLong2Long2" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkConvertLong3Long3() {
+        Allocation inV = createRandomIntegerAllocation(mRS, Element.DataType.SIGNED_64, 3, 0xb570d0a92724e91el, true, 63);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_64, 3), INPUTSIZE);
+            script.forEach_testConvertLong3Long3Long3(inV, out);
+            verifyResultsConvertLong3Long3(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertLong3Long3Long3: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_64, 3), INPUTSIZE);
+            scriptRelaxed.forEach_testConvertLong3Long3Long3(inV, out);
+            verifyResultsConvertLong3Long3(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertLong3Long3Long3: " + e.toString());
+        }
+    }
+
+    private void verifyResultsConvertLong3Long3(Allocation inV, Allocation out, boolean relaxed) {
+        long[] arrayInV = new long[INPUTSIZE * 4];
+        inV.copyTo(arrayInV);
+        long[] arrayOut = new long[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 3 ; j++) {
+                // Extract the inputs.
+                ArgumentsLongLong args = new ArgumentsLongLong();
+                args.inV = arrayInV[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeConvert(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (args.out != arrayOut[i * 4 + j]) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("%d", args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(String.format("%d", args.out));
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%d", arrayOut[i * 4 + j]));
+                    if (args.out != arrayOut[i * 4 + j]) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkConvertLong3Long3" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkConvertLong4Long4() {
+        Allocation inV = createRandomIntegerAllocation(mRS, Element.DataType.SIGNED_64, 4, 0xb570db4a862cae12l, true, 63);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_64, 4), INPUTSIZE);
+            script.forEach_testConvertLong4Long4Long4(inV, out);
+            verifyResultsConvertLong4Long4(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertLong4Long4Long4: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_64, 4), INPUTSIZE);
+            scriptRelaxed.forEach_testConvertLong4Long4Long4(inV, out);
+            verifyResultsConvertLong4Long4(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertLong4Long4Long4: " + e.toString());
+        }
+    }
+
+    private void verifyResultsConvertLong4Long4(Allocation inV, Allocation out, boolean relaxed) {
+        long[] arrayInV = new long[INPUTSIZE * 4];
+        inV.copyTo(arrayInV);
+        long[] arrayOut = new long[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 4 ; j++) {
+                // Extract the inputs.
+                ArgumentsLongLong args = new ArgumentsLongLong();
+                args.inV = arrayInV[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeConvert(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (args.out != arrayOut[i * 4 + j]) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("%d", args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(String.format("%d", args.out));
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%d", arrayOut[i * 4 + j]));
+                    if (args.out != arrayOut[i * 4 + j]) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkConvertLong4Long4" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    public class ArgumentsUlongLong {
+        public long inV;
+        public long out;
+    }
+
+    private void checkConvertUlong2Long2() {
+        Allocation inV = createRandomIntegerAllocation(mRS, Element.DataType.UNSIGNED_64, 2, 0x5cfe7f555a9f30abl, false, 63);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_64, 2), INPUTSIZE);
+            script.forEach_testConvertLong2Ulong2Long2(inV, out);
+            verifyResultsConvertUlong2Long2(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertLong2Ulong2Long2: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_64, 2), INPUTSIZE);
+            scriptRelaxed.forEach_testConvertLong2Ulong2Long2(inV, out);
+            verifyResultsConvertUlong2Long2(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertLong2Ulong2Long2: " + e.toString());
+        }
+    }
+
+    private void verifyResultsConvertUlong2Long2(Allocation inV, Allocation out, boolean relaxed) {
+        long[] arrayInV = new long[INPUTSIZE * 2];
+        inV.copyTo(arrayInV);
+        long[] arrayOut = new long[INPUTSIZE * 2];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 2 ; j++) {
+                // Extract the inputs.
+                ArgumentsUlongLong args = new ArgumentsUlongLong();
+                args.inV = arrayInV[i * 2 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeConvert(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (args.out != arrayOut[i * 2 + j]) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("0x%x", args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(String.format("%d", args.out));
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%d", arrayOut[i * 2 + j]));
+                    if (args.out != arrayOut[i * 2 + j]) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkConvertUlong2Long2" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkConvertUlong3Long3() {
+        Allocation inV = createRandomIntegerAllocation(mRS, Element.DataType.UNSIGNED_64, 3, 0x5cfe89f6b9a6f59fl, false, 63);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_64, 3), INPUTSIZE);
+            script.forEach_testConvertLong3Ulong3Long3(inV, out);
+            verifyResultsConvertUlong3Long3(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertLong3Ulong3Long3: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_64, 3), INPUTSIZE);
+            scriptRelaxed.forEach_testConvertLong3Ulong3Long3(inV, out);
+            verifyResultsConvertUlong3Long3(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertLong3Ulong3Long3: " + e.toString());
+        }
+    }
+
+    private void verifyResultsConvertUlong3Long3(Allocation inV, Allocation out, boolean relaxed) {
+        long[] arrayInV = new long[INPUTSIZE * 4];
+        inV.copyTo(arrayInV);
+        long[] arrayOut = new long[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 3 ; j++) {
+                // Extract the inputs.
+                ArgumentsUlongLong args = new ArgumentsUlongLong();
+                args.inV = arrayInV[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeConvert(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (args.out != arrayOut[i * 4 + j]) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("0x%x", args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(String.format("%d", args.out));
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%d", arrayOut[i * 4 + j]));
+                    if (args.out != arrayOut[i * 4 + j]) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkConvertUlong3Long3" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkConvertUlong4Long4() {
+        Allocation inV = createRandomIntegerAllocation(mRS, Element.DataType.UNSIGNED_64, 4, 0x5cfe949818aeba93l, false, 63);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_64, 4), INPUTSIZE);
+            script.forEach_testConvertLong4Ulong4Long4(inV, out);
+            verifyResultsConvertUlong4Long4(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertLong4Ulong4Long4: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_64, 4), INPUTSIZE);
+            scriptRelaxed.forEach_testConvertLong4Ulong4Long4(inV, out);
+            verifyResultsConvertUlong4Long4(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertLong4Ulong4Long4: " + e.toString());
+        }
+    }
+
+    private void verifyResultsConvertUlong4Long4(Allocation inV, Allocation out, boolean relaxed) {
+        long[] arrayInV = new long[INPUTSIZE * 4];
+        inV.copyTo(arrayInV);
+        long[] arrayOut = new long[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 4 ; j++) {
+                // Extract the inputs.
+                ArgumentsUlongLong args = new ArgumentsUlongLong();
+                args.inV = arrayInV[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeConvert(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (args.out != arrayOut[i * 4 + j]) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("0x%x", args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(String.format("%d", args.out));
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%d", arrayOut[i * 4 + j]));
+                    if (args.out != arrayOut[i * 4 + j]) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkConvertUlong4Long4" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    public class ArgumentsDoubleUlong {
+        public double inV;
+        public long out;
+    }
+
+    private void checkConvertDouble2Ulong2() {
+        Allocation inV = createRandomFloatAllocation(mRS, Element.DataType.FLOAT_64, 2, 0x42b56e3b7e12ff5el, 0.0000000000000000000e+00, 1.8446744073709549568e+19);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_64, 2), INPUTSIZE);
+            script.forEach_testConvertUlong2Double2Ulong2(inV, out);
+            verifyResultsConvertDouble2Ulong2(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertUlong2Double2Ulong2: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_64, 2), INPUTSIZE);
+            scriptRelaxed.forEach_testConvertUlong2Double2Ulong2(inV, out);
+            verifyResultsConvertDouble2Ulong2(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertUlong2Double2Ulong2: " + e.toString());
+        }
+    }
+
+    private void verifyResultsConvertDouble2Ulong2(Allocation inV, Allocation out, boolean relaxed) {
+        double[] arrayInV = new double[INPUTSIZE * 2];
+        inV.copyTo(arrayInV);
+        long[] arrayOut = new long[INPUTSIZE * 2];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 2 ; j++) {
+                // Extract the inputs.
+                ArgumentsDoubleUlong args = new ArgumentsDoubleUlong();
+                args.inV = arrayInV[i * 2 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeConvert(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (args.out != arrayOut[i * 2 + j]) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("%24.8g %16x %31a",
+                            args.inV, Double.doubleToRawLongBits(args.inV), args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(String.format("0x%x", args.out));
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("0x%x", arrayOut[i * 2 + j]));
+                    if (args.out != arrayOut[i * 2 + j]) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkConvertDouble2Ulong2" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkConvertDouble3Ulong3() {
+        Allocation inV = createRandomFloatAllocation(mRS, Element.DataType.FLOAT_64, 3, 0x42b73756742e203cl, 0.0000000000000000000e+00, 1.8446744073709549568e+19);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_64, 3), INPUTSIZE);
+            script.forEach_testConvertUlong3Double3Ulong3(inV, out);
+            verifyResultsConvertDouble3Ulong3(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertUlong3Double3Ulong3: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_64, 3), INPUTSIZE);
+            scriptRelaxed.forEach_testConvertUlong3Double3Ulong3(inV, out);
+            verifyResultsConvertDouble3Ulong3(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertUlong3Double3Ulong3: " + e.toString());
+        }
+    }
+
+    private void verifyResultsConvertDouble3Ulong3(Allocation inV, Allocation out, boolean relaxed) {
+        double[] arrayInV = new double[INPUTSIZE * 4];
+        inV.copyTo(arrayInV);
+        long[] arrayOut = new long[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 3 ; j++) {
+                // Extract the inputs.
+                ArgumentsDoubleUlong args = new ArgumentsDoubleUlong();
+                args.inV = arrayInV[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeConvert(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (args.out != arrayOut[i * 4 + j]) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("%24.8g %16x %31a",
+                            args.inV, Double.doubleToRawLongBits(args.inV), args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(String.format("0x%x", args.out));
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("0x%x", arrayOut[i * 4 + j]));
+                    if (args.out != arrayOut[i * 4 + j]) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkConvertDouble3Ulong3" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkConvertDouble4Ulong4() {
+        Allocation inV = createRandomFloatAllocation(mRS, Element.DataType.FLOAT_64, 4, 0x42b900716a49411al, 0.0000000000000000000e+00, 1.8446744073709549568e+19);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_64, 4), INPUTSIZE);
+            script.forEach_testConvertUlong4Double4Ulong4(inV, out);
+            verifyResultsConvertDouble4Ulong4(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertUlong4Double4Ulong4: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_64, 4), INPUTSIZE);
+            scriptRelaxed.forEach_testConvertUlong4Double4Ulong4(inV, out);
+            verifyResultsConvertDouble4Ulong4(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertUlong4Double4Ulong4: " + e.toString());
+        }
+    }
+
+    private void verifyResultsConvertDouble4Ulong4(Allocation inV, Allocation out, boolean relaxed) {
+        double[] arrayInV = new double[INPUTSIZE * 4];
+        inV.copyTo(arrayInV);
+        long[] arrayOut = new long[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 4 ; j++) {
+                // Extract the inputs.
+                ArgumentsDoubleUlong args = new ArgumentsDoubleUlong();
+                args.inV = arrayInV[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeConvert(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (args.out != arrayOut[i * 4 + j]) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("%24.8g %16x %31a",
+                            args.inV, Double.doubleToRawLongBits(args.inV), args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(String.format("0x%x", args.out));
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("0x%x", arrayOut[i * 4 + j]));
+                    if (args.out != arrayOut[i * 4 + j]) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkConvertDouble4Ulong4" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    public class ArgumentsLongUlong {
+        public long inV;
+        public long out;
+    }
+
+    private void checkConvertLong2Ulong2() {
+        Allocation inV = createRandomIntegerAllocation(mRS, Element.DataType.SIGNED_64, 2, 0x79f1a23ed7d40f65l, false, 63);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_64, 2), INPUTSIZE);
+            script.forEach_testConvertUlong2Long2Ulong2(inV, out);
+            verifyResultsConvertLong2Ulong2(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertUlong2Long2Ulong2: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_64, 2), INPUTSIZE);
+            scriptRelaxed.forEach_testConvertUlong2Long2Ulong2(inV, out);
+            verifyResultsConvertLong2Ulong2(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertUlong2Long2Ulong2: " + e.toString());
+        }
+    }
+
+    private void verifyResultsConvertLong2Ulong2(Allocation inV, Allocation out, boolean relaxed) {
+        long[] arrayInV = new long[INPUTSIZE * 2];
+        inV.copyTo(arrayInV);
+        long[] arrayOut = new long[INPUTSIZE * 2];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 2 ; j++) {
+                // Extract the inputs.
+                ArgumentsLongUlong args = new ArgumentsLongUlong();
+                args.inV = arrayInV[i * 2 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeConvert(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (args.out != arrayOut[i * 2 + j]) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("%d", args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(String.format("0x%x", args.out));
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("0x%x", arrayOut[i * 2 + j]));
+                    if (args.out != arrayOut[i * 2 + j]) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkConvertLong2Ulong2" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkConvertLong3Ulong3() {
+        Allocation inV = createRandomIntegerAllocation(mRS, Element.DataType.SIGNED_64, 3, 0x79f36b59cdef3043l, false, 63);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_64, 3), INPUTSIZE);
+            script.forEach_testConvertUlong3Long3Ulong3(inV, out);
+            verifyResultsConvertLong3Ulong3(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertUlong3Long3Ulong3: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_64, 3), INPUTSIZE);
+            scriptRelaxed.forEach_testConvertUlong3Long3Ulong3(inV, out);
+            verifyResultsConvertLong3Ulong3(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertUlong3Long3Ulong3: " + e.toString());
+        }
+    }
+
+    private void verifyResultsConvertLong3Ulong3(Allocation inV, Allocation out, boolean relaxed) {
+        long[] arrayInV = new long[INPUTSIZE * 4];
+        inV.copyTo(arrayInV);
+        long[] arrayOut = new long[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 3 ; j++) {
+                // Extract the inputs.
+                ArgumentsLongUlong args = new ArgumentsLongUlong();
+                args.inV = arrayInV[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeConvert(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (args.out != arrayOut[i * 4 + j]) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("%d", args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(String.format("0x%x", args.out));
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("0x%x", arrayOut[i * 4 + j]));
+                    if (args.out != arrayOut[i * 4 + j]) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkConvertLong3Ulong3" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkConvertLong4Ulong4() {
+        Allocation inV = createRandomIntegerAllocation(mRS, Element.DataType.SIGNED_64, 4, 0x79f53474c40a5121l, false, 63);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_64, 4), INPUTSIZE);
+            script.forEach_testConvertUlong4Long4Ulong4(inV, out);
+            verifyResultsConvertLong4Ulong4(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertUlong4Long4Ulong4: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_64, 4), INPUTSIZE);
+            scriptRelaxed.forEach_testConvertUlong4Long4Ulong4(inV, out);
+            verifyResultsConvertLong4Ulong4(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertUlong4Long4Ulong4: " + e.toString());
+        }
+    }
+
+    private void verifyResultsConvertLong4Ulong4(Allocation inV, Allocation out, boolean relaxed) {
+        long[] arrayInV = new long[INPUTSIZE * 4];
+        inV.copyTo(arrayInV);
+        long[] arrayOut = new long[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 4 ; j++) {
+                // Extract the inputs.
+                ArgumentsLongUlong args = new ArgumentsLongUlong();
+                args.inV = arrayInV[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeConvert(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (args.out != arrayOut[i * 4 + j]) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("%d", args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(String.format("0x%x", args.out));
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("0x%x", arrayOut[i * 4 + j]));
+                    if (args.out != arrayOut[i * 4 + j]) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkConvertLong4Ulong4" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    public class ArgumentsUlongUlong {
+        public long inV;
+        public long out;
+    }
+
+    private void checkConvertUlong2Ulong2() {
+        Allocation inV = createRandomIntegerAllocation(mRS, Element.DataType.UNSIGNED_64, 2, 0x9ebfc24673ac2910l, false, 64);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_64, 2), INPUTSIZE);
+            script.forEach_testConvertUlong2Ulong2Ulong2(inV, out);
+            verifyResultsConvertUlong2Ulong2(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertUlong2Ulong2Ulong2: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_64, 2), INPUTSIZE);
+            scriptRelaxed.forEach_testConvertUlong2Ulong2Ulong2(inV, out);
+            verifyResultsConvertUlong2Ulong2(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertUlong2Ulong2Ulong2: " + e.toString());
+        }
+    }
+
+    private void verifyResultsConvertUlong2Ulong2(Allocation inV, Allocation out, boolean relaxed) {
+        long[] arrayInV = new long[INPUTSIZE * 2];
+        inV.copyTo(arrayInV);
+        long[] arrayOut = new long[INPUTSIZE * 2];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 2 ; j++) {
+                // Extract the inputs.
+                ArgumentsUlongUlong args = new ArgumentsUlongUlong();
+                args.inV = arrayInV[i * 2 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeConvert(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (args.out != arrayOut[i * 2 + j]) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("0x%x", args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(String.format("0x%x", args.out));
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("0x%x", arrayOut[i * 2 + j]));
+                    if (args.out != arrayOut[i * 2 + j]) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkConvertUlong2Ulong2" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkConvertUlong3Ulong3() {
+        Allocation inV = createRandomIntegerAllocation(mRS, Element.DataType.UNSIGNED_64, 3, 0x9ec18b6169c749eel, false, 64);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_64, 3), INPUTSIZE);
+            script.forEach_testConvertUlong3Ulong3Ulong3(inV, out);
+            verifyResultsConvertUlong3Ulong3(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertUlong3Ulong3Ulong3: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_64, 3), INPUTSIZE);
+            scriptRelaxed.forEach_testConvertUlong3Ulong3Ulong3(inV, out);
+            verifyResultsConvertUlong3Ulong3(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertUlong3Ulong3Ulong3: " + e.toString());
+        }
+    }
+
+    private void verifyResultsConvertUlong3Ulong3(Allocation inV, Allocation out, boolean relaxed) {
+        long[] arrayInV = new long[INPUTSIZE * 4];
+        inV.copyTo(arrayInV);
+        long[] arrayOut = new long[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 3 ; j++) {
+                // Extract the inputs.
+                ArgumentsUlongUlong args = new ArgumentsUlongUlong();
+                args.inV = arrayInV[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeConvert(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (args.out != arrayOut[i * 4 + j]) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("0x%x", args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(String.format("0x%x", args.out));
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("0x%x", arrayOut[i * 4 + j]));
+                    if (args.out != arrayOut[i * 4 + j]) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkConvertUlong3Ulong3" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkConvertUlong4Ulong4() {
+        Allocation inV = createRandomIntegerAllocation(mRS, Element.DataType.UNSIGNED_64, 4, 0x9ec3547c5fe26accl, false, 64);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_64, 4), INPUTSIZE);
+            script.forEach_testConvertUlong4Ulong4Ulong4(inV, out);
+            verifyResultsConvertUlong4Ulong4(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertUlong4Ulong4Ulong4: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_64, 4), INPUTSIZE);
+            scriptRelaxed.forEach_testConvertUlong4Ulong4Ulong4(inV, out);
+            verifyResultsConvertUlong4Ulong4(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertUlong4Ulong4Ulong4: " + e.toString());
+        }
+    }
+
+    private void verifyResultsConvertUlong4Ulong4(Allocation inV, Allocation out, boolean relaxed) {
+        long[] arrayInV = new long[INPUTSIZE * 4];
+        inV.copyTo(arrayInV);
+        long[] arrayOut = new long[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 4 ; j++) {
+                // Extract the inputs.
+                ArgumentsUlongUlong args = new ArgumentsUlongUlong();
+                args.inV = arrayInV[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeConvert(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (args.out != arrayOut[i * 4 + j]) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("0x%x", args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(String.format("0x%x", args.out));
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("0x%x", arrayOut[i * 4 + j]));
+                    if (args.out != arrayOut[i * 4 + j]) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkConvertUlong4Ulong4" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    public class ArgumentsDoubleFloat {
+        public double inV;
+        public Floaty out;
+    }
+
+    private void checkConvertDouble2Float2() {
+        Allocation inV = createRandomFloatAllocation(mRS, Element.DataType.FLOAT_64, 2, 0x42b4cec67d6d9a2dl, -1.6163412428744576259e+38, 1.6163412428744576259e+38);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 2), INPUTSIZE);
+            script.forEach_testConvertFloat2Double2Float2(inV, out);
+            verifyResultsConvertDouble2Float2(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertFloat2Double2Float2: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 2), INPUTSIZE);
+            scriptRelaxed.forEach_testConvertFloat2Double2Float2(inV, out);
+            verifyResultsConvertDouble2Float2(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertFloat2Double2Float2: " + e.toString());
+        }
+    }
+
+    private void verifyResultsConvertDouble2Float2(Allocation inV, Allocation out, boolean relaxed) {
+        double[] arrayInV = new double[INPUTSIZE * 2];
+        inV.copyTo(arrayInV);
+        float[] arrayOut = new float[INPUTSIZE * 2];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 2 ; j++) {
+                // Extract the inputs.
+                ArgumentsDoubleFloat args = new ArgumentsDoubleFloat();
+                args.inV = arrayInV[i * 2 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeConvert(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (!args.out.couldBe(arrayOut[i * 2 + j])) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("%24.8g %16x %31a",
+                            args.inV, Double.doubleToRawLongBits(args.inV), args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(args.out.toString());
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            arrayOut[i * 2 + j], Float.floatToRawIntBits(arrayOut[i * 2 + j]), arrayOut[i * 2 + j]));
+                    if (!args.out.couldBe(arrayOut[i * 2 + j])) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkConvertDouble2Float2" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkConvertDouble3Float3() {
+        Allocation inV = createRandomFloatAllocation(mRS, Element.DataType.FLOAT_64, 3, 0x42b697e17388bb0bl, -1.6163412428744576259e+38, 1.6163412428744576259e+38);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 3), INPUTSIZE);
+            script.forEach_testConvertFloat3Double3Float3(inV, out);
+            verifyResultsConvertDouble3Float3(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertFloat3Double3Float3: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 3), INPUTSIZE);
+            scriptRelaxed.forEach_testConvertFloat3Double3Float3(inV, out);
+            verifyResultsConvertDouble3Float3(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertFloat3Double3Float3: " + e.toString());
+        }
+    }
+
+    private void verifyResultsConvertDouble3Float3(Allocation inV, Allocation out, boolean relaxed) {
+        double[] arrayInV = new double[INPUTSIZE * 4];
+        inV.copyTo(arrayInV);
+        float[] arrayOut = new float[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 3 ; j++) {
+                // Extract the inputs.
+                ArgumentsDoubleFloat args = new ArgumentsDoubleFloat();
+                args.inV = arrayInV[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeConvert(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("%24.8g %16x %31a",
+                            args.inV, Double.doubleToRawLongBits(args.inV), args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(args.out.toString());
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            arrayOut[i * 4 + j], Float.floatToRawIntBits(arrayOut[i * 4 + j]), arrayOut[i * 4 + j]));
+                    if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkConvertDouble3Float3" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkConvertDouble4Float4() {
+        Allocation inV = createRandomFloatAllocation(mRS, Element.DataType.FLOAT_64, 4, 0x42b860fc69a3dbe9l, -1.6163412428744576259e+38, 1.6163412428744576259e+38);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 4), INPUTSIZE);
+            script.forEach_testConvertFloat4Double4Float4(inV, out);
+            verifyResultsConvertDouble4Float4(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertFloat4Double4Float4: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 4), INPUTSIZE);
+            scriptRelaxed.forEach_testConvertFloat4Double4Float4(inV, out);
+            verifyResultsConvertDouble4Float4(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertFloat4Double4Float4: " + e.toString());
+        }
+    }
+
+    private void verifyResultsConvertDouble4Float4(Allocation inV, Allocation out, boolean relaxed) {
+        double[] arrayInV = new double[INPUTSIZE * 4];
+        inV.copyTo(arrayInV);
+        float[] arrayOut = new float[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 4 ; j++) {
+                // Extract the inputs.
+                ArgumentsDoubleFloat args = new ArgumentsDoubleFloat();
+                args.inV = arrayInV[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeConvert(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("%24.8g %16x %31a",
+                            args.inV, Double.doubleToRawLongBits(args.inV), args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(args.out.toString());
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            arrayOut[i * 4 + j], Float.floatToRawIntBits(arrayOut[i * 4 + j]), arrayOut[i * 4 + j]));
+                    if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkConvertDouble4Float4" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    public class ArgumentsLongFloat {
+        public long inV;
+        public Floaty out;
+    }
+
+    private void checkConvertLong2Float2() {
+        Allocation inV = createRandomIntegerAllocation(mRS, Element.DataType.SIGNED_64, 2, 0x79f102c9d72eaa34l, true, 63);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 2), INPUTSIZE);
+            script.forEach_testConvertFloat2Long2Float2(inV, out);
+            verifyResultsConvertLong2Float2(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertFloat2Long2Float2: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 2), INPUTSIZE);
+            scriptRelaxed.forEach_testConvertFloat2Long2Float2(inV, out);
+            verifyResultsConvertLong2Float2(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertFloat2Long2Float2: " + e.toString());
+        }
+    }
+
+    private void verifyResultsConvertLong2Float2(Allocation inV, Allocation out, boolean relaxed) {
+        long[] arrayInV = new long[INPUTSIZE * 2];
+        inV.copyTo(arrayInV);
+        float[] arrayOut = new float[INPUTSIZE * 2];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 2 ; j++) {
+                // Extract the inputs.
+                ArgumentsLongFloat args = new ArgumentsLongFloat();
+                args.inV = arrayInV[i * 2 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeConvert(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (!args.out.couldBe(arrayOut[i * 2 + j])) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("%d", args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(args.out.toString());
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            arrayOut[i * 2 + j], Float.floatToRawIntBits(arrayOut[i * 2 + j]), arrayOut[i * 2 + j]));
+                    if (!args.out.couldBe(arrayOut[i * 2 + j])) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkConvertLong2Float2" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkConvertLong3Float3() {
+        Allocation inV = createRandomIntegerAllocation(mRS, Element.DataType.SIGNED_64, 3, 0x79f2cbe4cd49cb12l, true, 63);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 3), INPUTSIZE);
+            script.forEach_testConvertFloat3Long3Float3(inV, out);
+            verifyResultsConvertLong3Float3(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertFloat3Long3Float3: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 3), INPUTSIZE);
+            scriptRelaxed.forEach_testConvertFloat3Long3Float3(inV, out);
+            verifyResultsConvertLong3Float3(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertFloat3Long3Float3: " + e.toString());
+        }
+    }
+
+    private void verifyResultsConvertLong3Float3(Allocation inV, Allocation out, boolean relaxed) {
+        long[] arrayInV = new long[INPUTSIZE * 4];
+        inV.copyTo(arrayInV);
+        float[] arrayOut = new float[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 3 ; j++) {
+                // Extract the inputs.
+                ArgumentsLongFloat args = new ArgumentsLongFloat();
+                args.inV = arrayInV[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeConvert(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("%d", args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(args.out.toString());
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            arrayOut[i * 4 + j], Float.floatToRawIntBits(arrayOut[i * 4 + j]), arrayOut[i * 4 + j]));
+                    if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkConvertLong3Float3" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkConvertLong4Float4() {
+        Allocation inV = createRandomIntegerAllocation(mRS, Element.DataType.SIGNED_64, 4, 0x79f494ffc364ebf0l, true, 63);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 4), INPUTSIZE);
+            script.forEach_testConvertFloat4Long4Float4(inV, out);
+            verifyResultsConvertLong4Float4(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertFloat4Long4Float4: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 4), INPUTSIZE);
+            scriptRelaxed.forEach_testConvertFloat4Long4Float4(inV, out);
+            verifyResultsConvertLong4Float4(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertFloat4Long4Float4: " + e.toString());
+        }
+    }
+
+    private void verifyResultsConvertLong4Float4(Allocation inV, Allocation out, boolean relaxed) {
+        long[] arrayInV = new long[INPUTSIZE * 4];
+        inV.copyTo(arrayInV);
+        float[] arrayOut = new float[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 4 ; j++) {
+                // Extract the inputs.
+                ArgumentsLongFloat args = new ArgumentsLongFloat();
+                args.inV = arrayInV[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeConvert(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("%d", args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(args.out.toString());
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            arrayOut[i * 4 + j], Float.floatToRawIntBits(arrayOut[i * 4 + j]), arrayOut[i * 4 + j]));
+                    if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkConvertLong4Float4" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    public class ArgumentsUlongFloat {
+        public long inV;
+        public Floaty out;
+    }
+
+    private void checkConvertUlong2Float2() {
+        Allocation inV = createRandomIntegerAllocation(mRS, Element.DataType.UNSIGNED_64, 2, 0x9ebf22d17306c3dfl, false, 64);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 2), INPUTSIZE);
+            script.forEach_testConvertFloat2Ulong2Float2(inV, out);
+            verifyResultsConvertUlong2Float2(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertFloat2Ulong2Float2: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 2), INPUTSIZE);
+            scriptRelaxed.forEach_testConvertFloat2Ulong2Float2(inV, out);
+            verifyResultsConvertUlong2Float2(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertFloat2Ulong2Float2: " + e.toString());
+        }
+    }
+
+    private void verifyResultsConvertUlong2Float2(Allocation inV, Allocation out, boolean relaxed) {
+        long[] arrayInV = new long[INPUTSIZE * 2];
+        inV.copyTo(arrayInV);
+        float[] arrayOut = new float[INPUTSIZE * 2];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 2 ; j++) {
+                // Extract the inputs.
+                ArgumentsUlongFloat args = new ArgumentsUlongFloat();
+                args.inV = arrayInV[i * 2 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeConvert(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (!args.out.couldBe(arrayOut[i * 2 + j])) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("0x%x", args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(args.out.toString());
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            arrayOut[i * 2 + j], Float.floatToRawIntBits(arrayOut[i * 2 + j]), arrayOut[i * 2 + j]));
+                    if (!args.out.couldBe(arrayOut[i * 2 + j])) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkConvertUlong2Float2" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkConvertUlong3Float3() {
+        Allocation inV = createRandomIntegerAllocation(mRS, Element.DataType.UNSIGNED_64, 3, 0x9ec0ebec6921e4bdl, false, 64);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 3), INPUTSIZE);
+            script.forEach_testConvertFloat3Ulong3Float3(inV, out);
+            verifyResultsConvertUlong3Float3(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertFloat3Ulong3Float3: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 3), INPUTSIZE);
+            scriptRelaxed.forEach_testConvertFloat3Ulong3Float3(inV, out);
+            verifyResultsConvertUlong3Float3(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertFloat3Ulong3Float3: " + e.toString());
+        }
+    }
+
+    private void verifyResultsConvertUlong3Float3(Allocation inV, Allocation out, boolean relaxed) {
+        long[] arrayInV = new long[INPUTSIZE * 4];
+        inV.copyTo(arrayInV);
+        float[] arrayOut = new float[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 3 ; j++) {
+                // Extract the inputs.
+                ArgumentsUlongFloat args = new ArgumentsUlongFloat();
+                args.inV = arrayInV[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeConvert(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("0x%x", args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(args.out.toString());
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            arrayOut[i * 4 + j], Float.floatToRawIntBits(arrayOut[i * 4 + j]), arrayOut[i * 4 + j]));
+                    if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkConvertUlong3Float3" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkConvertUlong4Float4() {
+        Allocation inV = createRandomIntegerAllocation(mRS, Element.DataType.UNSIGNED_64, 4, 0x9ec2b5075f3d059bl, false, 64);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 4), INPUTSIZE);
+            script.forEach_testConvertFloat4Ulong4Float4(inV, out);
+            verifyResultsConvertUlong4Float4(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertFloat4Ulong4Float4: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 4), INPUTSIZE);
+            scriptRelaxed.forEach_testConvertFloat4Ulong4Float4(inV, out);
+            verifyResultsConvertUlong4Float4(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertFloat4Ulong4Float4: " + e.toString());
+        }
+    }
+
+    private void verifyResultsConvertUlong4Float4(Allocation inV, Allocation out, boolean relaxed) {
+        long[] arrayInV = new long[INPUTSIZE * 4];
+        inV.copyTo(arrayInV);
+        float[] arrayOut = new float[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 4 ; j++) {
+                // Extract the inputs.
+                ArgumentsUlongFloat args = new ArgumentsUlongFloat();
+                args.inV = arrayInV[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeConvert(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("0x%x", args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(args.out.toString());
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            arrayOut[i * 4 + j], Float.floatToRawIntBits(arrayOut[i * 4 + j]), arrayOut[i * 4 + j]));
+                    if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkConvertUlong4Float4" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    public class ArgumentsDoubleChar {
+        public double inV;
+        public byte out;
+    }
+
+    private void checkConvertDouble2Char2() {
+        Allocation inV = createRandomFloatAllocation(mRS, Element.DataType.FLOAT_64, 2, 0xcbf84b7bef094a17l, -1.2800000000000000000e+02, 1.2700000000000000000e+02);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_8, 2), INPUTSIZE);
+            script.forEach_testConvertChar2Double2Char2(inV, out);
+            verifyResultsConvertDouble2Char2(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertChar2Double2Char2: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_8, 2), INPUTSIZE);
+            scriptRelaxed.forEach_testConvertChar2Double2Char2(inV, out);
+            verifyResultsConvertDouble2Char2(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertChar2Double2Char2: " + e.toString());
+        }
+    }
+
+    private void verifyResultsConvertDouble2Char2(Allocation inV, Allocation out, boolean relaxed) {
+        double[] arrayInV = new double[INPUTSIZE * 2];
+        inV.copyTo(arrayInV);
+        byte[] arrayOut = new byte[INPUTSIZE * 2];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 2 ; j++) {
+                // Extract the inputs.
+                ArgumentsDoubleChar args = new ArgumentsDoubleChar();
+                args.inV = arrayInV[i * 2 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeConvert(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (args.out != arrayOut[i * 2 + j]) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("%24.8g %16x %31a",
+                            args.inV, Double.doubleToRawLongBits(args.inV), args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(String.format("%d", args.out));
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%d", arrayOut[i * 2 + j]));
+                    if (args.out != arrayOut[i * 2 + j]) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkConvertDouble2Char2" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkConvertDouble3Char3() {
+        Allocation inV = createRandomFloatAllocation(mRS, Element.DataType.FLOAT_64, 3, 0xcbf8561d4e110f0bl, -1.2800000000000000000e+02, 1.2700000000000000000e+02);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_8, 3), INPUTSIZE);
+            script.forEach_testConvertChar3Double3Char3(inV, out);
+            verifyResultsConvertDouble3Char3(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertChar3Double3Char3: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_8, 3), INPUTSIZE);
+            scriptRelaxed.forEach_testConvertChar3Double3Char3(inV, out);
+            verifyResultsConvertDouble3Char3(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertChar3Double3Char3: " + e.toString());
+        }
+    }
+
+    private void verifyResultsConvertDouble3Char3(Allocation inV, Allocation out, boolean relaxed) {
+        double[] arrayInV = new double[INPUTSIZE * 4];
+        inV.copyTo(arrayInV);
+        byte[] arrayOut = new byte[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 3 ; j++) {
+                // Extract the inputs.
+                ArgumentsDoubleChar args = new ArgumentsDoubleChar();
+                args.inV = arrayInV[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeConvert(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (args.out != arrayOut[i * 4 + j]) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("%24.8g %16x %31a",
+                            args.inV, Double.doubleToRawLongBits(args.inV), args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(String.format("%d", args.out));
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%d", arrayOut[i * 4 + j]));
+                    if (args.out != arrayOut[i * 4 + j]) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkConvertDouble3Char3" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkConvertDouble4Char4() {
+        Allocation inV = createRandomFloatAllocation(mRS, Element.DataType.FLOAT_64, 4, 0xcbf860bead18d3ffl, -1.2800000000000000000e+02, 1.2700000000000000000e+02);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_8, 4), INPUTSIZE);
+            script.forEach_testConvertChar4Double4Char4(inV, out);
+            verifyResultsConvertDouble4Char4(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertChar4Double4Char4: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_8, 4), INPUTSIZE);
+            scriptRelaxed.forEach_testConvertChar4Double4Char4(inV, out);
+            verifyResultsConvertDouble4Char4(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertChar4Double4Char4: " + e.toString());
+        }
+    }
+
+    private void verifyResultsConvertDouble4Char4(Allocation inV, Allocation out, boolean relaxed) {
+        double[] arrayInV = new double[INPUTSIZE * 4];
+        inV.copyTo(arrayInV);
+        byte[] arrayOut = new byte[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 4 ; j++) {
+                // Extract the inputs.
+                ArgumentsDoubleChar args = new ArgumentsDoubleChar();
+                args.inV = arrayInV[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeConvert(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (args.out != arrayOut[i * 4 + j]) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("%24.8g %16x %31a",
+                            args.inV, Double.doubleToRawLongBits(args.inV), args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(String.format("%d", args.out));
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%d", arrayOut[i * 4 + j]));
+                    if (args.out != arrayOut[i * 4 + j]) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkConvertDouble4Char4" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    public class ArgumentsLongChar {
+        public long inV;
+        public byte out;
+    }
+
+    private void checkConvertLong2Char2() {
+        Allocation inV = createRandomIntegerAllocation(mRS, Element.DataType.SIGNED_64, 2, 0xb570c3c37419afacl, true, 7);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_8, 2), INPUTSIZE);
+            script.forEach_testConvertChar2Long2Char2(inV, out);
+            verifyResultsConvertLong2Char2(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertChar2Long2Char2: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_8, 2), INPUTSIZE);
+            scriptRelaxed.forEach_testConvertChar2Long2Char2(inV, out);
+            verifyResultsConvertLong2Char2(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertChar2Long2Char2: " + e.toString());
+        }
+    }
+
+    private void verifyResultsConvertLong2Char2(Allocation inV, Allocation out, boolean relaxed) {
+        long[] arrayInV = new long[INPUTSIZE * 2];
+        inV.copyTo(arrayInV);
+        byte[] arrayOut = new byte[INPUTSIZE * 2];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 2 ; j++) {
+                // Extract the inputs.
+                ArgumentsLongChar args = new ArgumentsLongChar();
+                args.inV = arrayInV[i * 2 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeConvert(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (args.out != arrayOut[i * 2 + j]) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("%d", args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(String.format("%d", args.out));
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%d", arrayOut[i * 2 + j]));
+                    if (args.out != arrayOut[i * 2 + j]) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkConvertLong2Char2" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkConvertLong3Char3() {
+        Allocation inV = createRandomIntegerAllocation(mRS, Element.DataType.SIGNED_64, 3, 0xb570ce64d32174a0l, true, 7);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_8, 3), INPUTSIZE);
+            script.forEach_testConvertChar3Long3Char3(inV, out);
+            verifyResultsConvertLong3Char3(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertChar3Long3Char3: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_8, 3), INPUTSIZE);
+            scriptRelaxed.forEach_testConvertChar3Long3Char3(inV, out);
+            verifyResultsConvertLong3Char3(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertChar3Long3Char3: " + e.toString());
+        }
+    }
+
+    private void verifyResultsConvertLong3Char3(Allocation inV, Allocation out, boolean relaxed) {
+        long[] arrayInV = new long[INPUTSIZE * 4];
+        inV.copyTo(arrayInV);
+        byte[] arrayOut = new byte[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 3 ; j++) {
+                // Extract the inputs.
+                ArgumentsLongChar args = new ArgumentsLongChar();
+                args.inV = arrayInV[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeConvert(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (args.out != arrayOut[i * 4 + j]) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("%d", args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(String.format("%d", args.out));
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%d", arrayOut[i * 4 + j]));
+                    if (args.out != arrayOut[i * 4 + j]) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkConvertLong3Char3" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkConvertLong4Char4() {
+        Allocation inV = createRandomIntegerAllocation(mRS, Element.DataType.SIGNED_64, 4, 0xb570d90632293994l, true, 7);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_8, 4), INPUTSIZE);
+            script.forEach_testConvertChar4Long4Char4(inV, out);
+            verifyResultsConvertLong4Char4(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertChar4Long4Char4: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_8, 4), INPUTSIZE);
+            scriptRelaxed.forEach_testConvertChar4Long4Char4(inV, out);
+            verifyResultsConvertLong4Char4(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertChar4Long4Char4: " + e.toString());
+        }
+    }
+
+    private void verifyResultsConvertLong4Char4(Allocation inV, Allocation out, boolean relaxed) {
+        long[] arrayInV = new long[INPUTSIZE * 4];
+        inV.copyTo(arrayInV);
+        byte[] arrayOut = new byte[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 4 ; j++) {
+                // Extract the inputs.
+                ArgumentsLongChar args = new ArgumentsLongChar();
+                args.inV = arrayInV[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeConvert(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (args.out != arrayOut[i * 4 + j]) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("%d", args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(String.format("%d", args.out));
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%d", arrayOut[i * 4 + j]));
+                    if (args.out != arrayOut[i * 4 + j]) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkConvertLong4Char4" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    public class ArgumentsUlongChar {
+        public long inV;
+        public byte out;
+    }
+
+    private void checkConvertUlong2Char2() {
+        Allocation inV = createRandomIntegerAllocation(mRS, Element.DataType.UNSIGNED_64, 2, 0x5cfe7d11069bbc2dl, false, 7);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_8, 2), INPUTSIZE);
+            script.forEach_testConvertChar2Ulong2Char2(inV, out);
+            verifyResultsConvertUlong2Char2(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertChar2Ulong2Char2: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_8, 2), INPUTSIZE);
+            scriptRelaxed.forEach_testConvertChar2Ulong2Char2(inV, out);
+            verifyResultsConvertUlong2Char2(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertChar2Ulong2Char2: " + e.toString());
+        }
+    }
+
+    private void verifyResultsConvertUlong2Char2(Allocation inV, Allocation out, boolean relaxed) {
+        long[] arrayInV = new long[INPUTSIZE * 2];
+        inV.copyTo(arrayInV);
+        byte[] arrayOut = new byte[INPUTSIZE * 2];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 2 ; j++) {
+                // Extract the inputs.
+                ArgumentsUlongChar args = new ArgumentsUlongChar();
+                args.inV = arrayInV[i * 2 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeConvert(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (args.out != arrayOut[i * 2 + j]) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("0x%x", args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(String.format("%d", args.out));
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%d", arrayOut[i * 2 + j]));
+                    if (args.out != arrayOut[i * 2 + j]) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkConvertUlong2Char2" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkConvertUlong3Char3() {
+        Allocation inV = createRandomIntegerAllocation(mRS, Element.DataType.UNSIGNED_64, 3, 0x5cfe87b265a38121l, false, 7);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_8, 3), INPUTSIZE);
+            script.forEach_testConvertChar3Ulong3Char3(inV, out);
+            verifyResultsConvertUlong3Char3(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertChar3Ulong3Char3: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_8, 3), INPUTSIZE);
+            scriptRelaxed.forEach_testConvertChar3Ulong3Char3(inV, out);
+            verifyResultsConvertUlong3Char3(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertChar3Ulong3Char3: " + e.toString());
+        }
+    }
+
+    private void verifyResultsConvertUlong3Char3(Allocation inV, Allocation out, boolean relaxed) {
+        long[] arrayInV = new long[INPUTSIZE * 4];
+        inV.copyTo(arrayInV);
+        byte[] arrayOut = new byte[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 3 ; j++) {
+                // Extract the inputs.
+                ArgumentsUlongChar args = new ArgumentsUlongChar();
+                args.inV = arrayInV[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeConvert(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (args.out != arrayOut[i * 4 + j]) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("0x%x", args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(String.format("%d", args.out));
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%d", arrayOut[i * 4 + j]));
+                    if (args.out != arrayOut[i * 4 + j]) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkConvertUlong3Char3" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkConvertUlong4Char4() {
+        Allocation inV = createRandomIntegerAllocation(mRS, Element.DataType.UNSIGNED_64, 4, 0x5cfe9253c4ab4615l, false, 7);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_8, 4), INPUTSIZE);
+            script.forEach_testConvertChar4Ulong4Char4(inV, out);
+            verifyResultsConvertUlong4Char4(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertChar4Ulong4Char4: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_8, 4), INPUTSIZE);
+            scriptRelaxed.forEach_testConvertChar4Ulong4Char4(inV, out);
+            verifyResultsConvertUlong4Char4(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertChar4Ulong4Char4: " + e.toString());
+        }
+    }
+
+    private void verifyResultsConvertUlong4Char4(Allocation inV, Allocation out, boolean relaxed) {
+        long[] arrayInV = new long[INPUTSIZE * 4];
+        inV.copyTo(arrayInV);
+        byte[] arrayOut = new byte[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 4 ; j++) {
+                // Extract the inputs.
+                ArgumentsUlongChar args = new ArgumentsUlongChar();
+                args.inV = arrayInV[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeConvert(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (args.out != arrayOut[i * 4 + j]) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("0x%x", args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(String.format("%d", args.out));
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%d", arrayOut[i * 4 + j]));
+                    if (args.out != arrayOut[i * 4 + j]) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkConvertUlong4Char4" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    public class ArgumentsDoubleUchar {
+        public double inV;
+        public byte out;
+    }
+
+    private void checkConvertDouble2Uchar2() {
+        Allocation inV = createRandomFloatAllocation(mRS, Element.DataType.FLOAT_64, 2, 0x42b56bf72a0f8ae0l, 0.0000000000000000000e+00, 2.5500000000000000000e+02);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_8, 2), INPUTSIZE);
+            script.forEach_testConvertUchar2Double2Uchar2(inV, out);
+            verifyResultsConvertDouble2Uchar2(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertUchar2Double2Uchar2: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_8, 2), INPUTSIZE);
+            scriptRelaxed.forEach_testConvertUchar2Double2Uchar2(inV, out);
+            verifyResultsConvertDouble2Uchar2(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertUchar2Double2Uchar2: " + e.toString());
+        }
+    }
+
+    private void verifyResultsConvertDouble2Uchar2(Allocation inV, Allocation out, boolean relaxed) {
+        double[] arrayInV = new double[INPUTSIZE * 2];
+        inV.copyTo(arrayInV);
+        byte[] arrayOut = new byte[INPUTSIZE * 2];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 2 ; j++) {
+                // Extract the inputs.
+                ArgumentsDoubleUchar args = new ArgumentsDoubleUchar();
+                args.inV = arrayInV[i * 2 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeConvert(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (args.out != arrayOut[i * 2 + j]) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("%24.8g %16x %31a",
+                            args.inV, Double.doubleToRawLongBits(args.inV), args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(String.format("0x%x", args.out));
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("0x%x", arrayOut[i * 2 + j]));
+                    if (args.out != arrayOut[i * 2 + j]) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkConvertDouble2Uchar2" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkConvertDouble3Uchar3() {
+        Allocation inV = createRandomFloatAllocation(mRS, Element.DataType.FLOAT_64, 3, 0x42b73512202aabbel, 0.0000000000000000000e+00, 2.5500000000000000000e+02);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_8, 3), INPUTSIZE);
+            script.forEach_testConvertUchar3Double3Uchar3(inV, out);
+            verifyResultsConvertDouble3Uchar3(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertUchar3Double3Uchar3: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_8, 3), INPUTSIZE);
+            scriptRelaxed.forEach_testConvertUchar3Double3Uchar3(inV, out);
+            verifyResultsConvertDouble3Uchar3(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertUchar3Double3Uchar3: " + e.toString());
+        }
+    }
+
+    private void verifyResultsConvertDouble3Uchar3(Allocation inV, Allocation out, boolean relaxed) {
+        double[] arrayInV = new double[INPUTSIZE * 4];
+        inV.copyTo(arrayInV);
+        byte[] arrayOut = new byte[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 3 ; j++) {
+                // Extract the inputs.
+                ArgumentsDoubleUchar args = new ArgumentsDoubleUchar();
+                args.inV = arrayInV[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeConvert(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (args.out != arrayOut[i * 4 + j]) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("%24.8g %16x %31a",
+                            args.inV, Double.doubleToRawLongBits(args.inV), args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(String.format("0x%x", args.out));
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("0x%x", arrayOut[i * 4 + j]));
+                    if (args.out != arrayOut[i * 4 + j]) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkConvertDouble3Uchar3" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkConvertDouble4Uchar4() {
+        Allocation inV = createRandomFloatAllocation(mRS, Element.DataType.FLOAT_64, 4, 0x42b8fe2d1645cc9cl, 0.0000000000000000000e+00, 2.5500000000000000000e+02);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_8, 4), INPUTSIZE);
+            script.forEach_testConvertUchar4Double4Uchar4(inV, out);
+            verifyResultsConvertDouble4Uchar4(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertUchar4Double4Uchar4: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_8, 4), INPUTSIZE);
+            scriptRelaxed.forEach_testConvertUchar4Double4Uchar4(inV, out);
+            verifyResultsConvertDouble4Uchar4(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertUchar4Double4Uchar4: " + e.toString());
+        }
+    }
+
+    private void verifyResultsConvertDouble4Uchar4(Allocation inV, Allocation out, boolean relaxed) {
+        double[] arrayInV = new double[INPUTSIZE * 4];
+        inV.copyTo(arrayInV);
+        byte[] arrayOut = new byte[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 4 ; j++) {
+                // Extract the inputs.
+                ArgumentsDoubleUchar args = new ArgumentsDoubleUchar();
+                args.inV = arrayInV[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeConvert(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (args.out != arrayOut[i * 4 + j]) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("%24.8g %16x %31a",
+                            args.inV, Double.doubleToRawLongBits(args.inV), args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(String.format("0x%x", args.out));
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("0x%x", arrayOut[i * 4 + j]));
+                    if (args.out != arrayOut[i * 4 + j]) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkConvertDouble4Uchar4" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    public class ArgumentsLongUchar {
+        public long inV;
+        public byte out;
+    }
+
+    private void checkConvertLong2Uchar2() {
+        Allocation inV = createRandomIntegerAllocation(mRS, Element.DataType.SIGNED_64, 2, 0x79f19ffa83d09ae7l, false, 8);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_8, 2), INPUTSIZE);
+            script.forEach_testConvertUchar2Long2Uchar2(inV, out);
+            verifyResultsConvertLong2Uchar2(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertUchar2Long2Uchar2: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_8, 2), INPUTSIZE);
+            scriptRelaxed.forEach_testConvertUchar2Long2Uchar2(inV, out);
+            verifyResultsConvertLong2Uchar2(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertUchar2Long2Uchar2: " + e.toString());
+        }
+    }
+
+    private void verifyResultsConvertLong2Uchar2(Allocation inV, Allocation out, boolean relaxed) {
+        long[] arrayInV = new long[INPUTSIZE * 2];
+        inV.copyTo(arrayInV);
+        byte[] arrayOut = new byte[INPUTSIZE * 2];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 2 ; j++) {
+                // Extract the inputs.
+                ArgumentsLongUchar args = new ArgumentsLongUchar();
+                args.inV = arrayInV[i * 2 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeConvert(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (args.out != arrayOut[i * 2 + j]) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("%d", args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(String.format("0x%x", args.out));
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("0x%x", arrayOut[i * 2 + j]));
+                    if (args.out != arrayOut[i * 2 + j]) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkConvertLong2Uchar2" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkConvertLong3Uchar3() {
+        Allocation inV = createRandomIntegerAllocation(mRS, Element.DataType.SIGNED_64, 3, 0x79f3691579ebbbc5l, false, 8);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_8, 3), INPUTSIZE);
+            script.forEach_testConvertUchar3Long3Uchar3(inV, out);
+            verifyResultsConvertLong3Uchar3(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertUchar3Long3Uchar3: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_8, 3), INPUTSIZE);
+            scriptRelaxed.forEach_testConvertUchar3Long3Uchar3(inV, out);
+            verifyResultsConvertLong3Uchar3(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertUchar3Long3Uchar3: " + e.toString());
+        }
+    }
+
+    private void verifyResultsConvertLong3Uchar3(Allocation inV, Allocation out, boolean relaxed) {
+        long[] arrayInV = new long[INPUTSIZE * 4];
+        inV.copyTo(arrayInV);
+        byte[] arrayOut = new byte[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 3 ; j++) {
+                // Extract the inputs.
+                ArgumentsLongUchar args = new ArgumentsLongUchar();
+                args.inV = arrayInV[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeConvert(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (args.out != arrayOut[i * 4 + j]) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("%d", args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(String.format("0x%x", args.out));
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("0x%x", arrayOut[i * 4 + j]));
+                    if (args.out != arrayOut[i * 4 + j]) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkConvertLong3Uchar3" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkConvertLong4Uchar4() {
+        Allocation inV = createRandomIntegerAllocation(mRS, Element.DataType.SIGNED_64, 4, 0x79f532307006dca3l, false, 8);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_8, 4), INPUTSIZE);
+            script.forEach_testConvertUchar4Long4Uchar4(inV, out);
+            verifyResultsConvertLong4Uchar4(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertUchar4Long4Uchar4: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_8, 4), INPUTSIZE);
+            scriptRelaxed.forEach_testConvertUchar4Long4Uchar4(inV, out);
+            verifyResultsConvertLong4Uchar4(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertUchar4Long4Uchar4: " + e.toString());
+        }
+    }
+
+    private void verifyResultsConvertLong4Uchar4(Allocation inV, Allocation out, boolean relaxed) {
+        long[] arrayInV = new long[INPUTSIZE * 4];
+        inV.copyTo(arrayInV);
+        byte[] arrayOut = new byte[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 4 ; j++) {
+                // Extract the inputs.
+                ArgumentsLongUchar args = new ArgumentsLongUchar();
+                args.inV = arrayInV[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeConvert(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (args.out != arrayOut[i * 4 + j]) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("%d", args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(String.format("0x%x", args.out));
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("0x%x", arrayOut[i * 4 + j]));
+                    if (args.out != arrayOut[i * 4 + j]) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkConvertLong4Uchar4" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    public class ArgumentsUlongUchar {
+        public long inV;
+        public byte out;
+    }
+
+    private void checkConvertUlong2Uchar2() {
+        Allocation inV = createRandomIntegerAllocation(mRS, Element.DataType.UNSIGNED_64, 2, 0x9ebfc0021fa8b492l, false, 8);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_8, 2), INPUTSIZE);
+            script.forEach_testConvertUchar2Ulong2Uchar2(inV, out);
+            verifyResultsConvertUlong2Uchar2(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertUchar2Ulong2Uchar2: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_8, 2), INPUTSIZE);
+            scriptRelaxed.forEach_testConvertUchar2Ulong2Uchar2(inV, out);
+            verifyResultsConvertUlong2Uchar2(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertUchar2Ulong2Uchar2: " + e.toString());
+        }
+    }
+
+    private void verifyResultsConvertUlong2Uchar2(Allocation inV, Allocation out, boolean relaxed) {
+        long[] arrayInV = new long[INPUTSIZE * 2];
+        inV.copyTo(arrayInV);
+        byte[] arrayOut = new byte[INPUTSIZE * 2];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 2 ; j++) {
+                // Extract the inputs.
+                ArgumentsUlongUchar args = new ArgumentsUlongUchar();
+                args.inV = arrayInV[i * 2 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeConvert(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (args.out != arrayOut[i * 2 + j]) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("0x%x", args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(String.format("0x%x", args.out));
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("0x%x", arrayOut[i * 2 + j]));
+                    if (args.out != arrayOut[i * 2 + j]) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkConvertUlong2Uchar2" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkConvertUlong3Uchar3() {
+        Allocation inV = createRandomIntegerAllocation(mRS, Element.DataType.UNSIGNED_64, 3, 0x9ec1891d15c3d570l, false, 8);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_8, 3), INPUTSIZE);
+            script.forEach_testConvertUchar3Ulong3Uchar3(inV, out);
+            verifyResultsConvertUlong3Uchar3(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertUchar3Ulong3Uchar3: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_8, 3), INPUTSIZE);
+            scriptRelaxed.forEach_testConvertUchar3Ulong3Uchar3(inV, out);
+            verifyResultsConvertUlong3Uchar3(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertUchar3Ulong3Uchar3: " + e.toString());
+        }
+    }
+
+    private void verifyResultsConvertUlong3Uchar3(Allocation inV, Allocation out, boolean relaxed) {
+        long[] arrayInV = new long[INPUTSIZE * 4];
+        inV.copyTo(arrayInV);
+        byte[] arrayOut = new byte[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 3 ; j++) {
+                // Extract the inputs.
+                ArgumentsUlongUchar args = new ArgumentsUlongUchar();
+                args.inV = arrayInV[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeConvert(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (args.out != arrayOut[i * 4 + j]) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("0x%x", args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(String.format("0x%x", args.out));
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("0x%x", arrayOut[i * 4 + j]));
+                    if (args.out != arrayOut[i * 4 + j]) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkConvertUlong3Uchar3" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkConvertUlong4Uchar4() {
+        Allocation inV = createRandomIntegerAllocation(mRS, Element.DataType.UNSIGNED_64, 4, 0x9ec352380bdef64el, false, 8);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_8, 4), INPUTSIZE);
+            script.forEach_testConvertUchar4Ulong4Uchar4(inV, out);
+            verifyResultsConvertUlong4Uchar4(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertUchar4Ulong4Uchar4: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_8, 4), INPUTSIZE);
+            scriptRelaxed.forEach_testConvertUchar4Ulong4Uchar4(inV, out);
+            verifyResultsConvertUlong4Uchar4(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertUchar4Ulong4Uchar4: " + e.toString());
+        }
+    }
+
+    private void verifyResultsConvertUlong4Uchar4(Allocation inV, Allocation out, boolean relaxed) {
+        long[] arrayInV = new long[INPUTSIZE * 4];
+        inV.copyTo(arrayInV);
+        byte[] arrayOut = new byte[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 4 ; j++) {
+                // Extract the inputs.
+                ArgumentsUlongUchar args = new ArgumentsUlongUchar();
+                args.inV = arrayInV[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeConvert(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (args.out != arrayOut[i * 4 + j]) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("0x%x", args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(String.format("0x%x", args.out));
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("0x%x", arrayOut[i * 4 + j]));
+                    if (args.out != arrayOut[i * 4 + j]) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkConvertUlong4Uchar4" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    public class ArgumentsDoubleShort {
+        public double inV;
+        public short out;
+    }
+
+    private void checkConvertDouble2Short2() {
+        Allocation inV = createRandomFloatAllocation(mRS, Element.DataType.FLOAT_64, 2, 0x42b557fbbf1d55f9l, -3.2768000000000000000e+04, 3.2767000000000000000e+04);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_16, 2), INPUTSIZE);
+            script.forEach_testConvertShort2Double2Short2(inV, out);
+            verifyResultsConvertDouble2Short2(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertShort2Double2Short2: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_16, 2), INPUTSIZE);
+            scriptRelaxed.forEach_testConvertShort2Double2Short2(inV, out);
+            verifyResultsConvertDouble2Short2(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertShort2Double2Short2: " + e.toString());
+        }
+    }
+
+    private void verifyResultsConvertDouble2Short2(Allocation inV, Allocation out, boolean relaxed) {
+        double[] arrayInV = new double[INPUTSIZE * 2];
+        inV.copyTo(arrayInV);
+        short[] arrayOut = new short[INPUTSIZE * 2];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 2 ; j++) {
+                // Extract the inputs.
+                ArgumentsDoubleShort args = new ArgumentsDoubleShort();
+                args.inV = arrayInV[i * 2 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeConvert(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (args.out != arrayOut[i * 2 + j]) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("%24.8g %16x %31a",
+                            args.inV, Double.doubleToRawLongBits(args.inV), args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(String.format("%d", args.out));
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%d", arrayOut[i * 2 + j]));
+                    if (args.out != arrayOut[i * 2 + j]) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkConvertDouble2Short2" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkConvertDouble3Short3() {
+        Allocation inV = createRandomFloatAllocation(mRS, Element.DataType.FLOAT_64, 3, 0x42b72116b53876d7l, -3.2768000000000000000e+04, 3.2767000000000000000e+04);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_16, 3), INPUTSIZE);
+            script.forEach_testConvertShort3Double3Short3(inV, out);
+            verifyResultsConvertDouble3Short3(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertShort3Double3Short3: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_16, 3), INPUTSIZE);
+            scriptRelaxed.forEach_testConvertShort3Double3Short3(inV, out);
+            verifyResultsConvertDouble3Short3(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertShort3Double3Short3: " + e.toString());
+        }
+    }
+
+    private void verifyResultsConvertDouble3Short3(Allocation inV, Allocation out, boolean relaxed) {
+        double[] arrayInV = new double[INPUTSIZE * 4];
+        inV.copyTo(arrayInV);
+        short[] arrayOut = new short[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 3 ; j++) {
+                // Extract the inputs.
+                ArgumentsDoubleShort args = new ArgumentsDoubleShort();
+                args.inV = arrayInV[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeConvert(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (args.out != arrayOut[i * 4 + j]) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("%24.8g %16x %31a",
+                            args.inV, Double.doubleToRawLongBits(args.inV), args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(String.format("%d", args.out));
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%d", arrayOut[i * 4 + j]));
+                    if (args.out != arrayOut[i * 4 + j]) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkConvertDouble3Short3" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkConvertDouble4Short4() {
+        Allocation inV = createRandomFloatAllocation(mRS, Element.DataType.FLOAT_64, 4, 0x42b8ea31ab5397b5l, -3.2768000000000000000e+04, 3.2767000000000000000e+04);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_16, 4), INPUTSIZE);
+            script.forEach_testConvertShort4Double4Short4(inV, out);
+            verifyResultsConvertDouble4Short4(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertShort4Double4Short4: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_16, 4), INPUTSIZE);
+            scriptRelaxed.forEach_testConvertShort4Double4Short4(inV, out);
+            verifyResultsConvertDouble4Short4(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertShort4Double4Short4: " + e.toString());
+        }
+    }
+
+    private void verifyResultsConvertDouble4Short4(Allocation inV, Allocation out, boolean relaxed) {
+        double[] arrayInV = new double[INPUTSIZE * 4];
+        inV.copyTo(arrayInV);
+        short[] arrayOut = new short[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 4 ; j++) {
+                // Extract the inputs.
+                ArgumentsDoubleShort args = new ArgumentsDoubleShort();
+                args.inV = arrayInV[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeConvert(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (args.out != arrayOut[i * 4 + j]) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("%24.8g %16x %31a",
+                            args.inV, Double.doubleToRawLongBits(args.inV), args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(String.format("%d", args.out));
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%d", arrayOut[i * 4 + j]));
+                    if (args.out != arrayOut[i * 4 + j]) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkConvertDouble4Short4" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    public class ArgumentsLongShort {
+        public long inV;
+        public short out;
+    }
+
+    private void checkConvertLong2Short2() {
+        Allocation inV = createRandomIntegerAllocation(mRS, Element.DataType.SIGNED_64, 2, 0x79f18bff18de6600l, true, 15);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_16, 2), INPUTSIZE);
+            script.forEach_testConvertShort2Long2Short2(inV, out);
+            verifyResultsConvertLong2Short2(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertShort2Long2Short2: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_16, 2), INPUTSIZE);
+            scriptRelaxed.forEach_testConvertShort2Long2Short2(inV, out);
+            verifyResultsConvertLong2Short2(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertShort2Long2Short2: " + e.toString());
+        }
+    }
+
+    private void verifyResultsConvertLong2Short2(Allocation inV, Allocation out, boolean relaxed) {
+        long[] arrayInV = new long[INPUTSIZE * 2];
+        inV.copyTo(arrayInV);
+        short[] arrayOut = new short[INPUTSIZE * 2];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 2 ; j++) {
+                // Extract the inputs.
+                ArgumentsLongShort args = new ArgumentsLongShort();
+                args.inV = arrayInV[i * 2 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeConvert(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (args.out != arrayOut[i * 2 + j]) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("%d", args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(String.format("%d", args.out));
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%d", arrayOut[i * 2 + j]));
+                    if (args.out != arrayOut[i * 2 + j]) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkConvertLong2Short2" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkConvertLong3Short3() {
+        Allocation inV = createRandomIntegerAllocation(mRS, Element.DataType.SIGNED_64, 3, 0x79f3551a0ef986del, true, 15);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_16, 3), INPUTSIZE);
+            script.forEach_testConvertShort3Long3Short3(inV, out);
+            verifyResultsConvertLong3Short3(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertShort3Long3Short3: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_16, 3), INPUTSIZE);
+            scriptRelaxed.forEach_testConvertShort3Long3Short3(inV, out);
+            verifyResultsConvertLong3Short3(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertShort3Long3Short3: " + e.toString());
+        }
+    }
+
+    private void verifyResultsConvertLong3Short3(Allocation inV, Allocation out, boolean relaxed) {
+        long[] arrayInV = new long[INPUTSIZE * 4];
+        inV.copyTo(arrayInV);
+        short[] arrayOut = new short[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 3 ; j++) {
+                // Extract the inputs.
+                ArgumentsLongShort args = new ArgumentsLongShort();
+                args.inV = arrayInV[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeConvert(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (args.out != arrayOut[i * 4 + j]) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("%d", args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(String.format("%d", args.out));
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%d", arrayOut[i * 4 + j]));
+                    if (args.out != arrayOut[i * 4 + j]) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkConvertLong3Short3" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkConvertLong4Short4() {
+        Allocation inV = createRandomIntegerAllocation(mRS, Element.DataType.SIGNED_64, 4, 0x79f51e350514a7bcl, true, 15);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_16, 4), INPUTSIZE);
+            script.forEach_testConvertShort4Long4Short4(inV, out);
+            verifyResultsConvertLong4Short4(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertShort4Long4Short4: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_16, 4), INPUTSIZE);
+            scriptRelaxed.forEach_testConvertShort4Long4Short4(inV, out);
+            verifyResultsConvertLong4Short4(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertShort4Long4Short4: " + e.toString());
+        }
+    }
+
+    private void verifyResultsConvertLong4Short4(Allocation inV, Allocation out, boolean relaxed) {
+        long[] arrayInV = new long[INPUTSIZE * 4];
+        inV.copyTo(arrayInV);
+        short[] arrayOut = new short[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 4 ; j++) {
+                // Extract the inputs.
+                ArgumentsLongShort args = new ArgumentsLongShort();
+                args.inV = arrayInV[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeConvert(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (args.out != arrayOut[i * 4 + j]) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("%d", args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(String.format("%d", args.out));
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%d", arrayOut[i * 4 + j]));
+                    if (args.out != arrayOut[i * 4 + j]) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkConvertLong4Short4" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    public class ArgumentsUlongShort {
+        public long inV;
+        public short out;
+    }
+
+    private void checkConvertUlong2Short2() {
+        Allocation inV = createRandomIntegerAllocation(mRS, Element.DataType.UNSIGNED_64, 2, 0x9ebfac06b4b67fabl, false, 15);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_16, 2), INPUTSIZE);
+            script.forEach_testConvertShort2Ulong2Short2(inV, out);
+            verifyResultsConvertUlong2Short2(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertShort2Ulong2Short2: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_16, 2), INPUTSIZE);
+            scriptRelaxed.forEach_testConvertShort2Ulong2Short2(inV, out);
+            verifyResultsConvertUlong2Short2(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertShort2Ulong2Short2: " + e.toString());
+        }
+    }
+
+    private void verifyResultsConvertUlong2Short2(Allocation inV, Allocation out, boolean relaxed) {
+        long[] arrayInV = new long[INPUTSIZE * 2];
+        inV.copyTo(arrayInV);
+        short[] arrayOut = new short[INPUTSIZE * 2];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 2 ; j++) {
+                // Extract the inputs.
+                ArgumentsUlongShort args = new ArgumentsUlongShort();
+                args.inV = arrayInV[i * 2 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeConvert(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (args.out != arrayOut[i * 2 + j]) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("0x%x", args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(String.format("%d", args.out));
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%d", arrayOut[i * 2 + j]));
+                    if (args.out != arrayOut[i * 2 + j]) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkConvertUlong2Short2" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkConvertUlong3Short3() {
+        Allocation inV = createRandomIntegerAllocation(mRS, Element.DataType.UNSIGNED_64, 3, 0x9ec17521aad1a089l, false, 15);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_16, 3), INPUTSIZE);
+            script.forEach_testConvertShort3Ulong3Short3(inV, out);
+            verifyResultsConvertUlong3Short3(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertShort3Ulong3Short3: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_16, 3), INPUTSIZE);
+            scriptRelaxed.forEach_testConvertShort3Ulong3Short3(inV, out);
+            verifyResultsConvertUlong3Short3(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertShort3Ulong3Short3: " + e.toString());
+        }
+    }
+
+    private void verifyResultsConvertUlong3Short3(Allocation inV, Allocation out, boolean relaxed) {
+        long[] arrayInV = new long[INPUTSIZE * 4];
+        inV.copyTo(arrayInV);
+        short[] arrayOut = new short[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 3 ; j++) {
+                // Extract the inputs.
+                ArgumentsUlongShort args = new ArgumentsUlongShort();
+                args.inV = arrayInV[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeConvert(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (args.out != arrayOut[i * 4 + j]) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("0x%x", args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(String.format("%d", args.out));
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%d", arrayOut[i * 4 + j]));
+                    if (args.out != arrayOut[i * 4 + j]) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkConvertUlong3Short3" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkConvertUlong4Short4() {
+        Allocation inV = createRandomIntegerAllocation(mRS, Element.DataType.UNSIGNED_64, 4, 0x9ec33e3ca0ecc167l, false, 15);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_16, 4), INPUTSIZE);
+            script.forEach_testConvertShort4Ulong4Short4(inV, out);
+            verifyResultsConvertUlong4Short4(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertShort4Ulong4Short4: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_16, 4), INPUTSIZE);
+            scriptRelaxed.forEach_testConvertShort4Ulong4Short4(inV, out);
+            verifyResultsConvertUlong4Short4(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertShort4Ulong4Short4: " + e.toString());
+        }
+    }
+
+    private void verifyResultsConvertUlong4Short4(Allocation inV, Allocation out, boolean relaxed) {
+        long[] arrayInV = new long[INPUTSIZE * 4];
+        inV.copyTo(arrayInV);
+        short[] arrayOut = new short[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 4 ; j++) {
+                // Extract the inputs.
+                ArgumentsUlongShort args = new ArgumentsUlongShort();
+                args.inV = arrayInV[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeConvert(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (args.out != arrayOut[i * 4 + j]) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("0x%x", args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(String.format("%d", args.out));
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%d", arrayOut[i * 4 + j]));
+                    if (args.out != arrayOut[i * 4 + j]) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkConvertUlong4Short4" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    public class ArgumentsDoubleUshort {
+        public double inV;
+        public short out;
+    }
+
+    private void checkConvertDouble2Ushort2() {
+        Allocation inV = createRandomFloatAllocation(mRS, Element.DataType.FLOAT_64, 2, 0x3479ccaea92a37bcl, 0.0000000000000000000e+00, 6.5535000000000000000e+04);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_16, 2), INPUTSIZE);
+            script.forEach_testConvertUshort2Double2Ushort2(inV, out);
+            verifyResultsConvertDouble2Ushort2(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertUshort2Double2Ushort2: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_16, 2), INPUTSIZE);
+            scriptRelaxed.forEach_testConvertUshort2Double2Ushort2(inV, out);
+            verifyResultsConvertDouble2Ushort2(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertUshort2Double2Ushort2: " + e.toString());
+        }
+    }
+
+    private void verifyResultsConvertDouble2Ushort2(Allocation inV, Allocation out, boolean relaxed) {
+        double[] arrayInV = new double[INPUTSIZE * 2];
+        inV.copyTo(arrayInV);
+        short[] arrayOut = new short[INPUTSIZE * 2];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 2 ; j++) {
+                // Extract the inputs.
+                ArgumentsDoubleUshort args = new ArgumentsDoubleUshort();
+                args.inV = arrayInV[i * 2 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeConvert(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (args.out != arrayOut[i * 2 + j]) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("%24.8g %16x %31a",
+                            args.inV, Double.doubleToRawLongBits(args.inV), args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(String.format("0x%x", args.out));
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("0x%x", arrayOut[i * 2 + j]));
+                    if (args.out != arrayOut[i * 2 + j]) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkConvertDouble2Ushort2" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkConvertDouble3Ushort3() {
+        Allocation inV = createRandomFloatAllocation(mRS, Element.DataType.FLOAT_64, 3, 0x34c69435ff85c8e8l, 0.0000000000000000000e+00, 6.5535000000000000000e+04);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_16, 3), INPUTSIZE);
+            script.forEach_testConvertUshort3Double3Ushort3(inV, out);
+            verifyResultsConvertDouble3Ushort3(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertUshort3Double3Ushort3: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_16, 3), INPUTSIZE);
+            scriptRelaxed.forEach_testConvertUshort3Double3Ushort3(inV, out);
+            verifyResultsConvertDouble3Ushort3(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertUshort3Double3Ushort3: " + e.toString());
+        }
+    }
+
+    private void verifyResultsConvertDouble3Ushort3(Allocation inV, Allocation out, boolean relaxed) {
+        double[] arrayInV = new double[INPUTSIZE * 4];
+        inV.copyTo(arrayInV);
+        short[] arrayOut = new short[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 3 ; j++) {
+                // Extract the inputs.
+                ArgumentsDoubleUshort args = new ArgumentsDoubleUshort();
+                args.inV = arrayInV[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeConvert(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (args.out != arrayOut[i * 4 + j]) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("%24.8g %16x %31a",
+                            args.inV, Double.doubleToRawLongBits(args.inV), args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(String.format("0x%x", args.out));
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("0x%x", arrayOut[i * 4 + j]));
+                    if (args.out != arrayOut[i * 4 + j]) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkConvertDouble3Ushort3" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkConvertDouble4Ushort4() {
+        Allocation inV = createRandomFloatAllocation(mRS, Element.DataType.FLOAT_64, 4, 0x35135bbd55e15a14l, 0.0000000000000000000e+00, 6.5535000000000000000e+04);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_16, 4), INPUTSIZE);
+            script.forEach_testConvertUshort4Double4Ushort4(inV, out);
+            verifyResultsConvertDouble4Ushort4(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertUshort4Double4Ushort4: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_16, 4), INPUTSIZE);
+            scriptRelaxed.forEach_testConvertUshort4Double4Ushort4(inV, out);
+            verifyResultsConvertDouble4Ushort4(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertUshort4Double4Ushort4: " + e.toString());
+        }
+    }
+
+    private void verifyResultsConvertDouble4Ushort4(Allocation inV, Allocation out, boolean relaxed) {
+        double[] arrayInV = new double[INPUTSIZE * 4];
+        inV.copyTo(arrayInV);
+        short[] arrayOut = new short[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 4 ; j++) {
+                // Extract the inputs.
+                ArgumentsDoubleUshort args = new ArgumentsDoubleUshort();
+                args.inV = arrayInV[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeConvert(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (args.out != arrayOut[i * 4 + j]) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("%24.8g %16x %31a",
+                            args.inV, Double.doubleToRawLongBits(args.inV), args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(String.format("0x%x", args.out));
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("0x%x", arrayOut[i * 4 + j]));
+                    if (args.out != arrayOut[i * 4 + j]) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkConvertDouble4Ushort4" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    public class ArgumentsLongUshort {
+        public long inV;
+        public short out;
+    }
+
+    private void checkConvertLong2Ushort2() {
+        Allocation inV = createRandomIntegerAllocation(mRS, Element.DataType.SIGNED_64, 2, 0x7b96893ebc97e8e9l, false, 16);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_16, 2), INPUTSIZE);
+            script.forEach_testConvertUshort2Long2Ushort2(inV, out);
+            verifyResultsConvertLong2Ushort2(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertUshort2Long2Ushort2: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_16, 2), INPUTSIZE);
+            scriptRelaxed.forEach_testConvertUshort2Long2Ushort2(inV, out);
+            verifyResultsConvertLong2Ushort2(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertUshort2Long2Ushort2: " + e.toString());
+        }
+    }
+
+    private void verifyResultsConvertLong2Ushort2(Allocation inV, Allocation out, boolean relaxed) {
+        long[] arrayInV = new long[INPUTSIZE * 2];
+        inV.copyTo(arrayInV);
+        short[] arrayOut = new short[INPUTSIZE * 2];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 2 ; j++) {
+                // Extract the inputs.
+                ArgumentsLongUshort args = new ArgumentsLongUshort();
+                args.inV = arrayInV[i * 2 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeConvert(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (args.out != arrayOut[i * 2 + j]) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("%d", args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(String.format("0x%x", args.out));
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("0x%x", arrayOut[i * 2 + j]));
+                    if (args.out != arrayOut[i * 2 + j]) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkConvertLong2Ushort2" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkConvertLong3Ushort3() {
+        Allocation inV = createRandomIntegerAllocation(mRS, Element.DataType.SIGNED_64, 3, 0x7be350c612f37a15l, false, 16);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_16, 3), INPUTSIZE);
+            script.forEach_testConvertUshort3Long3Ushort3(inV, out);
+            verifyResultsConvertLong3Ushort3(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertUshort3Long3Ushort3: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_16, 3), INPUTSIZE);
+            scriptRelaxed.forEach_testConvertUshort3Long3Ushort3(inV, out);
+            verifyResultsConvertLong3Ushort3(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertUshort3Long3Ushort3: " + e.toString());
+        }
+    }
+
+    private void verifyResultsConvertLong3Ushort3(Allocation inV, Allocation out, boolean relaxed) {
+        long[] arrayInV = new long[INPUTSIZE * 4];
+        inV.copyTo(arrayInV);
+        short[] arrayOut = new short[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 3 ; j++) {
+                // Extract the inputs.
+                ArgumentsLongUshort args = new ArgumentsLongUshort();
+                args.inV = arrayInV[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeConvert(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (args.out != arrayOut[i * 4 + j]) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("%d", args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(String.format("0x%x", args.out));
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("0x%x", arrayOut[i * 4 + j]));
+                    if (args.out != arrayOut[i * 4 + j]) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkConvertLong3Ushort3" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkConvertLong4Ushort4() {
+        Allocation inV = createRandomIntegerAllocation(mRS, Element.DataType.SIGNED_64, 4, 0x7c30184d694f0b41l, false, 16);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_16, 4), INPUTSIZE);
+            script.forEach_testConvertUshort4Long4Ushort4(inV, out);
+            verifyResultsConvertLong4Ushort4(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertUshort4Long4Ushort4: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_16, 4), INPUTSIZE);
+            scriptRelaxed.forEach_testConvertUshort4Long4Ushort4(inV, out);
+            verifyResultsConvertLong4Ushort4(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertUshort4Long4Ushort4: " + e.toString());
+        }
+    }
+
+    private void verifyResultsConvertLong4Ushort4(Allocation inV, Allocation out, boolean relaxed) {
+        long[] arrayInV = new long[INPUTSIZE * 4];
+        inV.copyTo(arrayInV);
+        short[] arrayOut = new short[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 4 ; j++) {
+                // Extract the inputs.
+                ArgumentsLongUshort args = new ArgumentsLongUshort();
+                args.inV = arrayInV[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeConvert(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (args.out != arrayOut[i * 4 + j]) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("%d", args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(String.format("0x%x", args.out));
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("0x%x", arrayOut[i * 4 + j]));
+                    if (args.out != arrayOut[i * 4 + j]) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkConvertLong4Ushort4" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    public class ArgumentsUlongUshort {
+        public long inV;
+        public short out;
+    }
+
+    private void checkConvertUlong2Ushort2() {
+        Allocation inV = createRandomIntegerAllocation(mRS, Element.DataType.UNSIGNED_64, 2, 0xaa35ea85e9e438a2l, false, 16);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_16, 2), INPUTSIZE);
+            script.forEach_testConvertUshort2Ulong2Ushort2(inV, out);
+            verifyResultsConvertUlong2Ushort2(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertUshort2Ulong2Ushort2: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_16, 2), INPUTSIZE);
+            scriptRelaxed.forEach_testConvertUshort2Ulong2Ushort2(inV, out);
+            verifyResultsConvertUlong2Ushort2(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertUshort2Ulong2Ushort2: " + e.toString());
+        }
+    }
+
+    private void verifyResultsConvertUlong2Ushort2(Allocation inV, Allocation out, boolean relaxed) {
+        long[] arrayInV = new long[INPUTSIZE * 2];
+        inV.copyTo(arrayInV);
+        short[] arrayOut = new short[INPUTSIZE * 2];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 2 ; j++) {
+                // Extract the inputs.
+                ArgumentsUlongUshort args = new ArgumentsUlongUshort();
+                args.inV = arrayInV[i * 2 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeConvert(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (args.out != arrayOut[i * 2 + j]) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("0x%x", args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(String.format("0x%x", args.out));
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("0x%x", arrayOut[i * 2 + j]));
+                    if (args.out != arrayOut[i * 2 + j]) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkConvertUlong2Ushort2" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkConvertUlong3Ushort3() {
+        Allocation inV = createRandomIntegerAllocation(mRS, Element.DataType.UNSIGNED_64, 3, 0xaa82b20d403fc9cel, false, 16);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_16, 3), INPUTSIZE);
+            script.forEach_testConvertUshort3Ulong3Ushort3(inV, out);
+            verifyResultsConvertUlong3Ushort3(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertUshort3Ulong3Ushort3: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_16, 3), INPUTSIZE);
+            scriptRelaxed.forEach_testConvertUshort3Ulong3Ushort3(inV, out);
+            verifyResultsConvertUlong3Ushort3(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertUshort3Ulong3Ushort3: " + e.toString());
+        }
+    }
+
+    private void verifyResultsConvertUlong3Ushort3(Allocation inV, Allocation out, boolean relaxed) {
+        long[] arrayInV = new long[INPUTSIZE * 4];
+        inV.copyTo(arrayInV);
+        short[] arrayOut = new short[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 3 ; j++) {
+                // Extract the inputs.
+                ArgumentsUlongUshort args = new ArgumentsUlongUshort();
+                args.inV = arrayInV[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeConvert(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (args.out != arrayOut[i * 4 + j]) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("0x%x", args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(String.format("0x%x", args.out));
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("0x%x", arrayOut[i * 4 + j]));
+                    if (args.out != arrayOut[i * 4 + j]) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkConvertUlong3Ushort3" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkConvertUlong4Ushort4() {
+        Allocation inV = createRandomIntegerAllocation(mRS, Element.DataType.UNSIGNED_64, 4, 0xaacf7994969b5afal, false, 16);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_16, 4), INPUTSIZE);
+            script.forEach_testConvertUshort4Ulong4Ushort4(inV, out);
+            verifyResultsConvertUlong4Ushort4(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertUshort4Ulong4Ushort4: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_16, 4), INPUTSIZE);
+            scriptRelaxed.forEach_testConvertUshort4Ulong4Ushort4(inV, out);
+            verifyResultsConvertUlong4Ushort4(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertUshort4Ulong4Ushort4: " + e.toString());
+        }
+    }
+
+    private void verifyResultsConvertUlong4Ushort4(Allocation inV, Allocation out, boolean relaxed) {
+        long[] arrayInV = new long[INPUTSIZE * 4];
+        inV.copyTo(arrayInV);
+        short[] arrayOut = new short[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 4 ; j++) {
+                // Extract the inputs.
+                ArgumentsUlongUshort args = new ArgumentsUlongUshort();
+                args.inV = arrayInV[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeConvert(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (args.out != arrayOut[i * 4 + j]) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("0x%x", args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(String.format("0x%x", args.out));
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("0x%x", arrayOut[i * 4 + j]));
+                    if (args.out != arrayOut[i * 4 + j]) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkConvertUlong4Ushort4" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    public class ArgumentsDoubleInt {
+        public double inV;
+        public int out;
+    }
+
+    private void checkConvertDouble2Int2() {
+        Allocation inV = createRandomFloatAllocation(mRS, Element.DataType.FLOAT_64, 2, 0xa57cd81dcaf628fcl, -2.1474836480000000000e+09, 2.1474836470000000000e+09);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_32, 2), INPUTSIZE);
+            script.forEach_testConvertInt2Double2Int2(inV, out);
+            verifyResultsConvertDouble2Int2(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertInt2Double2Int2: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_32, 2), INPUTSIZE);
+            scriptRelaxed.forEach_testConvertInt2Double2Int2(inV, out);
+            verifyResultsConvertDouble2Int2(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertInt2Double2Int2: " + e.toString());
+        }
+    }
+
+    private void verifyResultsConvertDouble2Int2(Allocation inV, Allocation out, boolean relaxed) {
+        double[] arrayInV = new double[INPUTSIZE * 2];
+        inV.copyTo(arrayInV);
+        int[] arrayOut = new int[INPUTSIZE * 2];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 2 ; j++) {
+                // Extract the inputs.
+                ArgumentsDoubleInt args = new ArgumentsDoubleInt();
+                args.inV = arrayInV[i * 2 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeConvert(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (args.out != arrayOut[i * 2 + j]) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("%24.8g %16x %31a",
+                            args.inV, Double.doubleToRawLongBits(args.inV), args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(String.format("%d", args.out));
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%d", arrayOut[i * 2 + j]));
+                    if (args.out != arrayOut[i * 2 + j]) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkConvertDouble2Int2" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkConvertDouble3Int3() {
+        Allocation inV = createRandomFloatAllocation(mRS, Element.DataType.FLOAT_64, 3, 0xa57cd85d149e3932l, -2.1474836480000000000e+09, 2.1474836470000000000e+09);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_32, 3), INPUTSIZE);
+            script.forEach_testConvertInt3Double3Int3(inV, out);
+            verifyResultsConvertDouble3Int3(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertInt3Double3Int3: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_32, 3), INPUTSIZE);
+            scriptRelaxed.forEach_testConvertInt3Double3Int3(inV, out);
+            verifyResultsConvertDouble3Int3(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertInt3Double3Int3: " + e.toString());
+        }
+    }
+
+    private void verifyResultsConvertDouble3Int3(Allocation inV, Allocation out, boolean relaxed) {
+        double[] arrayInV = new double[INPUTSIZE * 4];
+        inV.copyTo(arrayInV);
+        int[] arrayOut = new int[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 3 ; j++) {
+                // Extract the inputs.
+                ArgumentsDoubleInt args = new ArgumentsDoubleInt();
+                args.inV = arrayInV[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeConvert(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (args.out != arrayOut[i * 4 + j]) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("%24.8g %16x %31a",
+                            args.inV, Double.doubleToRawLongBits(args.inV), args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(String.format("%d", args.out));
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%d", arrayOut[i * 4 + j]));
+                    if (args.out != arrayOut[i * 4 + j]) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkConvertDouble3Int3" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkConvertDouble4Int4() {
+        Allocation inV = createRandomFloatAllocation(mRS, Element.DataType.FLOAT_64, 4, 0xa57cd89c5e464968l, -2.1474836480000000000e+09, 2.1474836470000000000e+09);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_32, 4), INPUTSIZE);
+            script.forEach_testConvertInt4Double4Int4(inV, out);
+            verifyResultsConvertDouble4Int4(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertInt4Double4Int4: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_32, 4), INPUTSIZE);
+            scriptRelaxed.forEach_testConvertInt4Double4Int4(inV, out);
+            verifyResultsConvertDouble4Int4(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertInt4Double4Int4: " + e.toString());
+        }
+    }
+
+    private void verifyResultsConvertDouble4Int4(Allocation inV, Allocation out, boolean relaxed) {
+        double[] arrayInV = new double[INPUTSIZE * 4];
+        inV.copyTo(arrayInV);
+        int[] arrayOut = new int[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 4 ; j++) {
+                // Extract the inputs.
+                ArgumentsDoubleInt args = new ArgumentsDoubleInt();
+                args.inV = arrayInV[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeConvert(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (args.out != arrayOut[i * 4 + j]) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("%24.8g %16x %31a",
+                            args.inV, Double.doubleToRawLongBits(args.inV), args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(String.format("%d", args.out));
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%d", arrayOut[i * 4 + j]));
+                    if (args.out != arrayOut[i * 4 + j]) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkConvertDouble4Int4" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    public class ArgumentsLongInt {
+        public long inV;
+        public int out;
+    }
+
+    private void checkConvertLong2Int2() {
+        Allocation inV = createRandomIntegerAllocation(mRS, Element.DataType.SIGNED_64, 2, 0xfe441c66e5deba3bl, true, 31);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_32, 2), INPUTSIZE);
+            script.forEach_testConvertInt2Long2Int2(inV, out);
+            verifyResultsConvertLong2Int2(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertInt2Long2Int2: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_32, 2), INPUTSIZE);
+            scriptRelaxed.forEach_testConvertInt2Long2Int2(inV, out);
+            verifyResultsConvertLong2Int2(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertInt2Long2Int2: " + e.toString());
+        }
+    }
+
+    private void verifyResultsConvertLong2Int2(Allocation inV, Allocation out, boolean relaxed) {
+        long[] arrayInV = new long[INPUTSIZE * 2];
+        inV.copyTo(arrayInV);
+        int[] arrayOut = new int[INPUTSIZE * 2];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 2 ; j++) {
+                // Extract the inputs.
+                ArgumentsLongInt args = new ArgumentsLongInt();
+                args.inV = arrayInV[i * 2 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeConvert(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (args.out != arrayOut[i * 2 + j]) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("%d", args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(String.format("%d", args.out));
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%d", arrayOut[i * 2 + j]));
+                    if (args.out != arrayOut[i * 2 + j]) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkConvertLong2Int2" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkConvertLong3Int3() {
+        Allocation inV = createRandomIntegerAllocation(mRS, Element.DataType.SIGNED_64, 3, 0xfe441ca62f86ca71l, true, 31);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_32, 3), INPUTSIZE);
+            script.forEach_testConvertInt3Long3Int3(inV, out);
+            verifyResultsConvertLong3Int3(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertInt3Long3Int3: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_32, 3), INPUTSIZE);
+            scriptRelaxed.forEach_testConvertInt3Long3Int3(inV, out);
+            verifyResultsConvertLong3Int3(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertInt3Long3Int3: " + e.toString());
+        }
+    }
+
+    private void verifyResultsConvertLong3Int3(Allocation inV, Allocation out, boolean relaxed) {
+        long[] arrayInV = new long[INPUTSIZE * 4];
+        inV.copyTo(arrayInV);
+        int[] arrayOut = new int[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 3 ; j++) {
+                // Extract the inputs.
+                ArgumentsLongInt args = new ArgumentsLongInt();
+                args.inV = arrayInV[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeConvert(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (args.out != arrayOut[i * 4 + j]) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("%d", args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(String.format("%d", args.out));
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%d", arrayOut[i * 4 + j]));
+                    if (args.out != arrayOut[i * 4 + j]) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkConvertLong3Int3" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkConvertLong4Int4() {
+        Allocation inV = createRandomIntegerAllocation(mRS, Element.DataType.SIGNED_64, 4, 0xfe441ce5792edaa7l, true, 31);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_32, 4), INPUTSIZE);
+            script.forEach_testConvertInt4Long4Int4(inV, out);
+            verifyResultsConvertLong4Int4(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertInt4Long4Int4: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_32, 4), INPUTSIZE);
+            scriptRelaxed.forEach_testConvertInt4Long4Int4(inV, out);
+            verifyResultsConvertLong4Int4(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertInt4Long4Int4: " + e.toString());
+        }
+    }
+
+    private void verifyResultsConvertLong4Int4(Allocation inV, Allocation out, boolean relaxed) {
+        long[] arrayInV = new long[INPUTSIZE * 4];
+        inV.copyTo(arrayInV);
+        int[] arrayOut = new int[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 4 ; j++) {
+                // Extract the inputs.
+                ArgumentsLongInt args = new ArgumentsLongInt();
+                args.inV = arrayInV[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeConvert(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (args.out != arrayOut[i * 4 + j]) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("%d", args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(String.format("%d", args.out));
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%d", arrayOut[i * 4 + j]));
+                    if (args.out != arrayOut[i * 4 + j]) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkConvertLong4Int4" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    public class ArgumentsUlongInt {
+        public long inV;
+        public int out;
+    }
+
+    private void checkConvertUlong2Int2() {
+        Allocation inV = createRandomIntegerAllocation(mRS, Element.DataType.UNSIGNED_64, 2, 0xe11d350e352de3el, false, 31);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_32, 2), INPUTSIZE);
+            script.forEach_testConvertInt2Ulong2Int2(inV, out);
+            verifyResultsConvertUlong2Int2(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertInt2Ulong2Int2: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_32, 2), INPUTSIZE);
+            scriptRelaxed.forEach_testConvertInt2Ulong2Int2(inV, out);
+            verifyResultsConvertUlong2Int2(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertInt2Ulong2Int2: " + e.toString());
+        }
+    }
+
+    private void verifyResultsConvertUlong2Int2(Allocation inV, Allocation out, boolean relaxed) {
+        long[] arrayInV = new long[INPUTSIZE * 2];
+        inV.copyTo(arrayInV);
+        int[] arrayOut = new int[INPUTSIZE * 2];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 2 ; j++) {
+                // Extract the inputs.
+                ArgumentsUlongInt args = new ArgumentsUlongInt();
+                args.inV = arrayInV[i * 2 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeConvert(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (args.out != arrayOut[i * 2 + j]) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("0x%x", args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(String.format("%d", args.out));
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%d", arrayOut[i * 2 + j]));
+                    if (args.out != arrayOut[i * 2 + j]) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkConvertUlong2Int2" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkConvertUlong3Int3() {
+        Allocation inV = createRandomIntegerAllocation(mRS, Element.DataType.UNSIGNED_64, 3, 0xe11d3902cfaee74l, false, 31);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_32, 3), INPUTSIZE);
+            script.forEach_testConvertInt3Ulong3Int3(inV, out);
+            verifyResultsConvertUlong3Int3(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertInt3Ulong3Int3: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_32, 3), INPUTSIZE);
+            scriptRelaxed.forEach_testConvertInt3Ulong3Int3(inV, out);
+            verifyResultsConvertUlong3Int3(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertInt3Ulong3Int3: " + e.toString());
+        }
+    }
+
+    private void verifyResultsConvertUlong3Int3(Allocation inV, Allocation out, boolean relaxed) {
+        long[] arrayInV = new long[INPUTSIZE * 4];
+        inV.copyTo(arrayInV);
+        int[] arrayOut = new int[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 3 ; j++) {
+                // Extract the inputs.
+                ArgumentsUlongInt args = new ArgumentsUlongInt();
+                args.inV = arrayInV[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeConvert(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (args.out != arrayOut[i * 4 + j]) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("0x%x", args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(String.format("%d", args.out));
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%d", arrayOut[i * 4 + j]));
+                    if (args.out != arrayOut[i * 4 + j]) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkConvertUlong3Int3" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkConvertUlong4Int4() {
+        Allocation inV = createRandomIntegerAllocation(mRS, Element.DataType.UNSIGNED_64, 4, 0xe11d3cf76a2feaal, false, 31);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_32, 4), INPUTSIZE);
+            script.forEach_testConvertInt4Ulong4Int4(inV, out);
+            verifyResultsConvertUlong4Int4(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertInt4Ulong4Int4: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_32, 4), INPUTSIZE);
+            scriptRelaxed.forEach_testConvertInt4Ulong4Int4(inV, out);
+            verifyResultsConvertUlong4Int4(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertInt4Ulong4Int4: " + e.toString());
+        }
+    }
+
+    private void verifyResultsConvertUlong4Int4(Allocation inV, Allocation out, boolean relaxed) {
+        long[] arrayInV = new long[INPUTSIZE * 4];
+        inV.copyTo(arrayInV);
+        int[] arrayOut = new int[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 4 ; j++) {
+                // Extract the inputs.
+                ArgumentsUlongInt args = new ArgumentsUlongInt();
+                args.inV = arrayInV[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeConvert(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (args.out != arrayOut[i * 4 + j]) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("0x%x", args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(String.format("%d", args.out));
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%d", arrayOut[i * 4 + j]));
+                    if (args.out != arrayOut[i * 4 + j]) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkConvertUlong4Int4" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    public class ArgumentsDoubleUint {
+        public double inV;
+        public int out;
+    }
+
+    private void checkConvertDouble2Uint2() {
+        Allocation inV = createRandomFloatAllocation(mRS, Element.DataType.FLOAT_64, 2, 0xcbf84ff107de7dd7l, 0.0000000000000000000e+00, 4.2949672950000000000e+09);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_32, 2), INPUTSIZE);
+            script.forEach_testConvertUint2Double2Uint2(inV, out);
+            verifyResultsConvertDouble2Uint2(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertUint2Double2Uint2: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_32, 2), INPUTSIZE);
+            scriptRelaxed.forEach_testConvertUint2Double2Uint2(inV, out);
+            verifyResultsConvertDouble2Uint2(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertUint2Double2Uint2: " + e.toString());
+        }
+    }
+
+    private void verifyResultsConvertDouble2Uint2(Allocation inV, Allocation out, boolean relaxed) {
+        double[] arrayInV = new double[INPUTSIZE * 2];
+        inV.copyTo(arrayInV);
+        int[] arrayOut = new int[INPUTSIZE * 2];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 2 ; j++) {
+                // Extract the inputs.
+                ArgumentsDoubleUint args = new ArgumentsDoubleUint();
+                args.inV = arrayInV[i * 2 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeConvert(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (args.out != arrayOut[i * 2 + j]) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("%24.8g %16x %31a",
+                            args.inV, Double.doubleToRawLongBits(args.inV), args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(String.format("0x%x", args.out));
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("0x%x", arrayOut[i * 2 + j]));
+                    if (args.out != arrayOut[i * 2 + j]) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkConvertDouble2Uint2" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkConvertDouble3Uint3() {
+        Allocation inV = createRandomFloatAllocation(mRS, Element.DataType.FLOAT_64, 3, 0xcbf85a9266e642cbl, 0.0000000000000000000e+00, 4.2949672950000000000e+09);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_32, 3), INPUTSIZE);
+            script.forEach_testConvertUint3Double3Uint3(inV, out);
+            verifyResultsConvertDouble3Uint3(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertUint3Double3Uint3: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_32, 3), INPUTSIZE);
+            scriptRelaxed.forEach_testConvertUint3Double3Uint3(inV, out);
+            verifyResultsConvertDouble3Uint3(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertUint3Double3Uint3: " + e.toString());
+        }
+    }
+
+    private void verifyResultsConvertDouble3Uint3(Allocation inV, Allocation out, boolean relaxed) {
+        double[] arrayInV = new double[INPUTSIZE * 4];
+        inV.copyTo(arrayInV);
+        int[] arrayOut = new int[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 3 ; j++) {
+                // Extract the inputs.
+                ArgumentsDoubleUint args = new ArgumentsDoubleUint();
+                args.inV = arrayInV[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeConvert(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (args.out != arrayOut[i * 4 + j]) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("%24.8g %16x %31a",
+                            args.inV, Double.doubleToRawLongBits(args.inV), args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(String.format("0x%x", args.out));
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("0x%x", arrayOut[i * 4 + j]));
+                    if (args.out != arrayOut[i * 4 + j]) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkConvertDouble3Uint3" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkConvertDouble4Uint4() {
+        Allocation inV = createRandomFloatAllocation(mRS, Element.DataType.FLOAT_64, 4, 0xcbf86533c5ee07bfl, 0.0000000000000000000e+00, 4.2949672950000000000e+09);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_32, 4), INPUTSIZE);
+            script.forEach_testConvertUint4Double4Uint4(inV, out);
+            verifyResultsConvertDouble4Uint4(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertUint4Double4Uint4: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_32, 4), INPUTSIZE);
+            scriptRelaxed.forEach_testConvertUint4Double4Uint4(inV, out);
+            verifyResultsConvertDouble4Uint4(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertUint4Double4Uint4: " + e.toString());
+        }
+    }
+
+    private void verifyResultsConvertDouble4Uint4(Allocation inV, Allocation out, boolean relaxed) {
+        double[] arrayInV = new double[INPUTSIZE * 4];
+        inV.copyTo(arrayInV);
+        int[] arrayOut = new int[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 4 ; j++) {
+                // Extract the inputs.
+                ArgumentsDoubleUint args = new ArgumentsDoubleUint();
+                args.inV = arrayInV[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeConvert(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (args.out != arrayOut[i * 4 + j]) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("%24.8g %16x %31a",
+                            args.inV, Double.doubleToRawLongBits(args.inV), args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(String.format("0x%x", args.out));
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("0x%x", arrayOut[i * 4 + j]));
+                    if (args.out != arrayOut[i * 4 + j]) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkConvertDouble4Uint4" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    public class ArgumentsLongUint {
+        public long inV;
+        public int out;
+    }
+
+    private void checkConvertLong2Uint2() {
+        Allocation inV = createRandomIntegerAllocation(mRS, Element.DataType.SIGNED_64, 2, 0xb570c8388ceee36cl, false, 32);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_32, 2), INPUTSIZE);
+            script.forEach_testConvertUint2Long2Uint2(inV, out);
+            verifyResultsConvertLong2Uint2(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertUint2Long2Uint2: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_32, 2), INPUTSIZE);
+            scriptRelaxed.forEach_testConvertUint2Long2Uint2(inV, out);
+            verifyResultsConvertLong2Uint2(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertUint2Long2Uint2: " + e.toString());
+        }
+    }
+
+    private void verifyResultsConvertLong2Uint2(Allocation inV, Allocation out, boolean relaxed) {
+        long[] arrayInV = new long[INPUTSIZE * 2];
+        inV.copyTo(arrayInV);
+        int[] arrayOut = new int[INPUTSIZE * 2];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 2 ; j++) {
+                // Extract the inputs.
+                ArgumentsLongUint args = new ArgumentsLongUint();
+                args.inV = arrayInV[i * 2 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeConvert(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (args.out != arrayOut[i * 2 + j]) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("%d", args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(String.format("0x%x", args.out));
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("0x%x", arrayOut[i * 2 + j]));
+                    if (args.out != arrayOut[i * 2 + j]) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkConvertLong2Uint2" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkConvertLong3Uint3() {
+        Allocation inV = createRandomIntegerAllocation(mRS, Element.DataType.SIGNED_64, 3, 0xb570d2d9ebf6a860l, false, 32);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_32, 3), INPUTSIZE);
+            script.forEach_testConvertUint3Long3Uint3(inV, out);
+            verifyResultsConvertLong3Uint3(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertUint3Long3Uint3: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_32, 3), INPUTSIZE);
+            scriptRelaxed.forEach_testConvertUint3Long3Uint3(inV, out);
+            verifyResultsConvertLong3Uint3(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertUint3Long3Uint3: " + e.toString());
+        }
+    }
+
+    private void verifyResultsConvertLong3Uint3(Allocation inV, Allocation out, boolean relaxed) {
+        long[] arrayInV = new long[INPUTSIZE * 4];
+        inV.copyTo(arrayInV);
+        int[] arrayOut = new int[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 3 ; j++) {
+                // Extract the inputs.
+                ArgumentsLongUint args = new ArgumentsLongUint();
+                args.inV = arrayInV[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeConvert(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (args.out != arrayOut[i * 4 + j]) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("%d", args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(String.format("0x%x", args.out));
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("0x%x", arrayOut[i * 4 + j]));
+                    if (args.out != arrayOut[i * 4 + j]) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkConvertLong3Uint3" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkConvertLong4Uint4() {
+        Allocation inV = createRandomIntegerAllocation(mRS, Element.DataType.SIGNED_64, 4, 0xb570dd7b4afe6d54l, false, 32);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_32, 4), INPUTSIZE);
+            script.forEach_testConvertUint4Long4Uint4(inV, out);
+            verifyResultsConvertLong4Uint4(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertUint4Long4Uint4: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_32, 4), INPUTSIZE);
+            scriptRelaxed.forEach_testConvertUint4Long4Uint4(inV, out);
+            verifyResultsConvertLong4Uint4(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertUint4Long4Uint4: " + e.toString());
+        }
+    }
+
+    private void verifyResultsConvertLong4Uint4(Allocation inV, Allocation out, boolean relaxed) {
+        long[] arrayInV = new long[INPUTSIZE * 4];
+        inV.copyTo(arrayInV);
+        int[] arrayOut = new int[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 4 ; j++) {
+                // Extract the inputs.
+                ArgumentsLongUint args = new ArgumentsLongUint();
+                args.inV = arrayInV[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeConvert(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (args.out != arrayOut[i * 4 + j]) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("%d", args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(String.format("0x%x", args.out));
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("0x%x", arrayOut[i * 4 + j]));
+                    if (args.out != arrayOut[i * 4 + j]) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkConvertLong4Uint4" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    public class ArgumentsUlongUint {
+        public long inV;
+        public int out;
+    }
+
+    private void checkConvertUlong2Uint2() {
+        Allocation inV = createRandomIntegerAllocation(mRS, Element.DataType.UNSIGNED_64, 2, 0x5cfe81861f70efedl, false, 32);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_32, 2), INPUTSIZE);
+            script.forEach_testConvertUint2Ulong2Uint2(inV, out);
+            verifyResultsConvertUlong2Uint2(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertUint2Ulong2Uint2: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_32, 2), INPUTSIZE);
+            scriptRelaxed.forEach_testConvertUint2Ulong2Uint2(inV, out);
+            verifyResultsConvertUlong2Uint2(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertUint2Ulong2Uint2: " + e.toString());
+        }
+    }
+
+    private void verifyResultsConvertUlong2Uint2(Allocation inV, Allocation out, boolean relaxed) {
+        long[] arrayInV = new long[INPUTSIZE * 2];
+        inV.copyTo(arrayInV);
+        int[] arrayOut = new int[INPUTSIZE * 2];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 2 ; j++) {
+                // Extract the inputs.
+                ArgumentsUlongUint args = new ArgumentsUlongUint();
+                args.inV = arrayInV[i * 2 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeConvert(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (args.out != arrayOut[i * 2 + j]) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("0x%x", args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(String.format("0x%x", args.out));
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("0x%x", arrayOut[i * 2 + j]));
+                    if (args.out != arrayOut[i * 2 + j]) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkConvertUlong2Uint2" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkConvertUlong3Uint3() {
+        Allocation inV = createRandomIntegerAllocation(mRS, Element.DataType.UNSIGNED_64, 3, 0x5cfe8c277e78b4e1l, false, 32);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_32, 3), INPUTSIZE);
+            script.forEach_testConvertUint3Ulong3Uint3(inV, out);
+            verifyResultsConvertUlong3Uint3(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertUint3Ulong3Uint3: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_32, 3), INPUTSIZE);
+            scriptRelaxed.forEach_testConvertUint3Ulong3Uint3(inV, out);
+            verifyResultsConvertUlong3Uint3(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertUint3Ulong3Uint3: " + e.toString());
+        }
+    }
+
+    private void verifyResultsConvertUlong3Uint3(Allocation inV, Allocation out, boolean relaxed) {
+        long[] arrayInV = new long[INPUTSIZE * 4];
+        inV.copyTo(arrayInV);
+        int[] arrayOut = new int[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 3 ; j++) {
+                // Extract the inputs.
+                ArgumentsUlongUint args = new ArgumentsUlongUint();
+                args.inV = arrayInV[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeConvert(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (args.out != arrayOut[i * 4 + j]) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("0x%x", args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(String.format("0x%x", args.out));
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("0x%x", arrayOut[i * 4 + j]));
+                    if (args.out != arrayOut[i * 4 + j]) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkConvertUlong3Uint3" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkConvertUlong4Uint4() {
+        Allocation inV = createRandomIntegerAllocation(mRS, Element.DataType.UNSIGNED_64, 4, 0x5cfe96c8dd8079d5l, false, 32);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_32, 4), INPUTSIZE);
+            script.forEach_testConvertUint4Ulong4Uint4(inV, out);
+            verifyResultsConvertUlong4Uint4(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertUint4Ulong4Uint4: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_32, 4), INPUTSIZE);
+            scriptRelaxed.forEach_testConvertUint4Ulong4Uint4(inV, out);
+            verifyResultsConvertUlong4Uint4(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertUint4Ulong4Uint4: " + e.toString());
+        }
+    }
+
+    private void verifyResultsConvertUlong4Uint4(Allocation inV, Allocation out, boolean relaxed) {
+        long[] arrayInV = new long[INPUTSIZE * 4];
+        inV.copyTo(arrayInV);
+        int[] arrayOut = new int[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 4 ; j++) {
+                // Extract the inputs.
+                ArgumentsUlongUint args = new ArgumentsUlongUint();
+                args.inV = arrayInV[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeConvert(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (args.out != arrayOut[i * 4 + j]) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("0x%x", args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(String.format("0x%x", args.out));
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("0x%x", arrayOut[i * 4 + j]));
+                    if (args.out != arrayOut[i * 4 + j]) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkConvertUlong4Uint4" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    public class ArgumentsFloatDouble {
+        public float inV;
+        public Floaty out;
+    }
+
+    private void checkConvertFloat2Double2() {
+        Allocation inV = createRandomFloatAllocation(mRS, Element.DataType.FLOAT_32, 2, 0x36c6372446e08221l, -1.6163412428744576259e+38, 1.6163412428744576259e+38);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_64, 2), INPUTSIZE);
+            script.forEach_testConvertDouble2Float2Double2(inV, out);
+            verifyResultsConvertFloat2Double2(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertDouble2Float2Double2: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_64, 2), INPUTSIZE);
+            scriptRelaxed.forEach_testConvertDouble2Float2Double2(inV, out);
+            verifyResultsConvertFloat2Double2(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertDouble2Float2Double2: " + e.toString());
+        }
+    }
+
+    private void verifyResultsConvertFloat2Double2(Allocation inV, Allocation out, boolean relaxed) {
+        float[] arrayInV = new float[INPUTSIZE * 2];
+        inV.copyTo(arrayInV);
+        double[] arrayOut = new double[INPUTSIZE * 2];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 2 ; j++) {
+                // Extract the inputs.
+                ArgumentsFloatDouble args = new ArgumentsFloatDouble();
+                args.inV = arrayInV[i * 2 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeConvert(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (!args.out.couldBe(arrayOut[i * 2 + j])) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            args.inV, Float.floatToRawIntBits(args.inV), args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(args.out.toString());
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%24.8g %16x %31a",
+                            arrayOut[i * 2 + j], Double.doubleToRawLongBits(arrayOut[i * 2 + j]), arrayOut[i * 2 + j]));
+                    if (!args.out.couldBe(arrayOut[i * 2 + j])) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkConvertFloat2Double2" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkConvertFloat3Double3() {
+        Allocation inV = createRandomFloatAllocation(mRS, Element.DataType.FLOAT_32, 3, 0x3712feab9d3c134dl, -1.6163412428744576259e+38, 1.6163412428744576259e+38);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_64, 3), INPUTSIZE);
+            script.forEach_testConvertDouble3Float3Double3(inV, out);
+            verifyResultsConvertFloat3Double3(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertDouble3Float3Double3: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_64, 3), INPUTSIZE);
+            scriptRelaxed.forEach_testConvertDouble3Float3Double3(inV, out);
+            verifyResultsConvertFloat3Double3(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertDouble3Float3Double3: " + e.toString());
+        }
+    }
+
+    private void verifyResultsConvertFloat3Double3(Allocation inV, Allocation out, boolean relaxed) {
+        float[] arrayInV = new float[INPUTSIZE * 4];
+        inV.copyTo(arrayInV);
+        double[] arrayOut = new double[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 3 ; j++) {
+                // Extract the inputs.
+                ArgumentsFloatDouble args = new ArgumentsFloatDouble();
+                args.inV = arrayInV[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeConvert(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            args.inV, Float.floatToRawIntBits(args.inV), args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(args.out.toString());
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%24.8g %16x %31a",
+                            arrayOut[i * 4 + j], Double.doubleToRawLongBits(arrayOut[i * 4 + j]), arrayOut[i * 4 + j]));
+                    if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkConvertFloat3Double3" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkConvertFloat4Double4() {
+        Allocation inV = createRandomFloatAllocation(mRS, Element.DataType.FLOAT_32, 4, 0x375fc632f397a479l, -1.6163412428744576259e+38, 1.6163412428744576259e+38);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_64, 4), INPUTSIZE);
+            script.forEach_testConvertDouble4Float4Double4(inV, out);
+            verifyResultsConvertFloat4Double4(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertDouble4Float4Double4: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_64, 4), INPUTSIZE);
+            scriptRelaxed.forEach_testConvertDouble4Float4Double4(inV, out);
+            verifyResultsConvertFloat4Double4(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertDouble4Float4Double4: " + e.toString());
+        }
+    }
+
+    private void verifyResultsConvertFloat4Double4(Allocation inV, Allocation out, boolean relaxed) {
+        float[] arrayInV = new float[INPUTSIZE * 4];
+        inV.copyTo(arrayInV);
+        double[] arrayOut = new double[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 4 ; j++) {
+                // Extract the inputs.
+                ArgumentsFloatDouble args = new ArgumentsFloatDouble();
+                args.inV = arrayInV[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeConvert(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            args.inV, Float.floatToRawIntBits(args.inV), args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(args.out.toString());
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%24.8g %16x %31a",
+                            arrayOut[i * 4 + j], Double.doubleToRawLongBits(arrayOut[i * 4 + j]), arrayOut[i * 4 + j]));
+                    if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkConvertFloat4Double4" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    public class ArgumentsCharDouble {
+        public byte inV;
+        public Floaty out;
+    }
+
+    private void checkConvertChar2Double2() {
+        Allocation inV = createRandomIntegerAllocation(mRS, Element.DataType.SIGNED_8, 2, 0xd86d88e268ca2f61l, true, 7);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_64, 2), INPUTSIZE);
+            script.forEach_testConvertDouble2Char2Double2(inV, out);
+            verifyResultsConvertChar2Double2(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertDouble2Char2Double2: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_64, 2), INPUTSIZE);
+            scriptRelaxed.forEach_testConvertDouble2Char2Double2(inV, out);
+            verifyResultsConvertChar2Double2(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertDouble2Char2Double2: " + e.toString());
+        }
+    }
+
+    private void verifyResultsConvertChar2Double2(Allocation inV, Allocation out, boolean relaxed) {
+        byte[] arrayInV = new byte[INPUTSIZE * 2];
+        inV.copyTo(arrayInV);
+        double[] arrayOut = new double[INPUTSIZE * 2];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 2 ; j++) {
+                // Extract the inputs.
+                ArgumentsCharDouble args = new ArgumentsCharDouble();
+                args.inV = arrayInV[i * 2 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeConvert(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (!args.out.couldBe(arrayOut[i * 2 + j])) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("%d", args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(args.out.toString());
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%24.8g %16x %31a",
+                            arrayOut[i * 2 + j], Double.doubleToRawLongBits(arrayOut[i * 2 + j]), arrayOut[i * 2 + j]));
+                    if (!args.out.couldBe(arrayOut[i * 2 + j])) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkConvertChar2Double2" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkConvertChar3Double3() {
+        Allocation inV = createRandomIntegerAllocation(mRS, Element.DataType.SIGNED_8, 3, 0xd8ba5069bf25c08dl, true, 7);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_64, 3), INPUTSIZE);
+            script.forEach_testConvertDouble3Char3Double3(inV, out);
+            verifyResultsConvertChar3Double3(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertDouble3Char3Double3: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_64, 3), INPUTSIZE);
+            scriptRelaxed.forEach_testConvertDouble3Char3Double3(inV, out);
+            verifyResultsConvertChar3Double3(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertDouble3Char3Double3: " + e.toString());
+        }
+    }
+
+    private void verifyResultsConvertChar3Double3(Allocation inV, Allocation out, boolean relaxed) {
+        byte[] arrayInV = new byte[INPUTSIZE * 4];
+        inV.copyTo(arrayInV);
+        double[] arrayOut = new double[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 3 ; j++) {
+                // Extract the inputs.
+                ArgumentsCharDouble args = new ArgumentsCharDouble();
+                args.inV = arrayInV[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeConvert(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("%d", args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(args.out.toString());
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%24.8g %16x %31a",
+                            arrayOut[i * 4 + j], Double.doubleToRawLongBits(arrayOut[i * 4 + j]), arrayOut[i * 4 + j]));
+                    if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkConvertChar3Double3" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkConvertChar4Double4() {
+        Allocation inV = createRandomIntegerAllocation(mRS, Element.DataType.SIGNED_8, 4, 0xd90717f1158151b9l, true, 7);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_64, 4), INPUTSIZE);
+            script.forEach_testConvertDouble4Char4Double4(inV, out);
+            verifyResultsConvertChar4Double4(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertDouble4Char4Double4: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_64, 4), INPUTSIZE);
+            scriptRelaxed.forEach_testConvertDouble4Char4Double4(inV, out);
+            verifyResultsConvertChar4Double4(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertDouble4Char4Double4: " + e.toString());
+        }
+    }
+
+    private void verifyResultsConvertChar4Double4(Allocation inV, Allocation out, boolean relaxed) {
+        byte[] arrayInV = new byte[INPUTSIZE * 4];
+        inV.copyTo(arrayInV);
+        double[] arrayOut = new double[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 4 ; j++) {
+                // Extract the inputs.
+                ArgumentsCharDouble args = new ArgumentsCharDouble();
+                args.inV = arrayInV[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeConvert(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("%d", args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(args.out.toString());
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%24.8g %16x %31a",
+                            arrayOut[i * 4 + j], Double.doubleToRawLongBits(arrayOut[i * 4 + j]), arrayOut[i * 4 + j]));
+                    if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkConvertChar4Double4" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    public class ArgumentsUcharDouble {
+        public byte inV;
+        public Floaty out;
+    }
+
+    private void checkConvertUchar2Double2() {
+        Allocation inV = createRandomIntegerAllocation(mRS, Element.DataType.UNSIGNED_8, 2, 0x70cea2996167f1al, false, 8);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_64, 2), INPUTSIZE);
+            script.forEach_testConvertDouble2Uchar2Double2(inV, out);
+            verifyResultsConvertUchar2Double2(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertDouble2Uchar2Double2: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_64, 2), INPUTSIZE);
+            scriptRelaxed.forEach_testConvertDouble2Uchar2Double2(inV, out);
+            verifyResultsConvertUchar2Double2(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertDouble2Uchar2Double2: " + e.toString());
+        }
+    }
+
+    private void verifyResultsConvertUchar2Double2(Allocation inV, Allocation out, boolean relaxed) {
+        byte[] arrayInV = new byte[INPUTSIZE * 2];
+        inV.copyTo(arrayInV);
+        double[] arrayOut = new double[INPUTSIZE * 2];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 2 ; j++) {
+                // Extract the inputs.
+                ArgumentsUcharDouble args = new ArgumentsUcharDouble();
+                args.inV = arrayInV[i * 2 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeConvert(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (!args.out.couldBe(arrayOut[i * 2 + j])) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("0x%x", args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(args.out.toString());
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%24.8g %16x %31a",
+                            arrayOut[i * 2 + j], Double.doubleToRawLongBits(arrayOut[i * 2 + j]), arrayOut[i * 2 + j]));
+                    if (!args.out.couldBe(arrayOut[i * 2 + j])) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkConvertUchar2Double2" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkConvertUchar3Double3() {
+        Allocation inV = createRandomIntegerAllocation(mRS, Element.DataType.UNSIGNED_8, 3, 0x759b1b0ec721046l, false, 8);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_64, 3), INPUTSIZE);
+            script.forEach_testConvertDouble3Uchar3Double3(inV, out);
+            verifyResultsConvertUchar3Double3(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertDouble3Uchar3Double3: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_64, 3), INPUTSIZE);
+            scriptRelaxed.forEach_testConvertDouble3Uchar3Double3(inV, out);
+            verifyResultsConvertUchar3Double3(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertDouble3Uchar3Double3: " + e.toString());
+        }
+    }
+
+    private void verifyResultsConvertUchar3Double3(Allocation inV, Allocation out, boolean relaxed) {
+        byte[] arrayInV = new byte[INPUTSIZE * 4];
+        inV.copyTo(arrayInV);
+        double[] arrayOut = new double[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 3 ; j++) {
+                // Extract the inputs.
+                ArgumentsUcharDouble args = new ArgumentsUcharDouble();
+                args.inV = arrayInV[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeConvert(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("0x%x", args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(args.out.toString());
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%24.8g %16x %31a",
+                            arrayOut[i * 4 + j], Double.doubleToRawLongBits(arrayOut[i * 4 + j]), arrayOut[i * 4 + j]));
+                    if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkConvertUchar3Double3" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkConvertUchar4Double4() {
+        Allocation inV = createRandomIntegerAllocation(mRS, Element.DataType.UNSIGNED_8, 4, 0x7a6793842cda172l, false, 8);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_64, 4), INPUTSIZE);
+            script.forEach_testConvertDouble4Uchar4Double4(inV, out);
+            verifyResultsConvertUchar4Double4(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertDouble4Uchar4Double4: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_64, 4), INPUTSIZE);
+            scriptRelaxed.forEach_testConvertDouble4Uchar4Double4(inV, out);
+            verifyResultsConvertUchar4Double4(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertDouble4Uchar4Double4: " + e.toString());
+        }
+    }
+
+    private void verifyResultsConvertUchar4Double4(Allocation inV, Allocation out, boolean relaxed) {
+        byte[] arrayInV = new byte[INPUTSIZE * 4];
+        inV.copyTo(arrayInV);
+        double[] arrayOut = new double[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 4 ; j++) {
+                // Extract the inputs.
+                ArgumentsUcharDouble args = new ArgumentsUcharDouble();
+                args.inV = arrayInV[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeConvert(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("0x%x", args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(args.out.toString());
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%24.8g %16x %31a",
+                            arrayOut[i * 4 + j], Double.doubleToRawLongBits(arrayOut[i * 4 + j]), arrayOut[i * 4 + j]));
+                    if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkConvertUchar4Double4" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    public class ArgumentsShortDouble {
+        public short inV;
+        public Floaty out;
+    }
+
+    private void checkConvertShort2Double2() {
+        Allocation inV = createRandomIntegerAllocation(mRS, Element.DataType.SIGNED_16, 2, 0xfdeea470023d0105l, true, 15);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_64, 2), INPUTSIZE);
+            script.forEach_testConvertDouble2Short2Double2(inV, out);
+            verifyResultsConvertShort2Double2(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertDouble2Short2Double2: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_64, 2), INPUTSIZE);
+            scriptRelaxed.forEach_testConvertDouble2Short2Double2(inV, out);
+            verifyResultsConvertShort2Double2(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertDouble2Short2Double2: " + e.toString());
+        }
+    }
+
+    private void verifyResultsConvertShort2Double2(Allocation inV, Allocation out, boolean relaxed) {
+        short[] arrayInV = new short[INPUTSIZE * 2];
+        inV.copyTo(arrayInV);
+        double[] arrayOut = new double[INPUTSIZE * 2];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 2 ; j++) {
+                // Extract the inputs.
+                ArgumentsShortDouble args = new ArgumentsShortDouble();
+                args.inV = arrayInV[i * 2 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeConvert(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (!args.out.couldBe(arrayOut[i * 2 + j])) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("%d", args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(args.out.toString());
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%24.8g %16x %31a",
+                            arrayOut[i * 2 + j], Double.doubleToRawLongBits(arrayOut[i * 2 + j]), arrayOut[i * 2 + j]));
+                    if (!args.out.couldBe(arrayOut[i * 2 + j])) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkConvertShort2Double2" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkConvertShort3Double3() {
+        Allocation inV = createRandomIntegerAllocation(mRS, Element.DataType.SIGNED_16, 3, 0xfe3b6bf758989231l, true, 15);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_64, 3), INPUTSIZE);
+            script.forEach_testConvertDouble3Short3Double3(inV, out);
+            verifyResultsConvertShort3Double3(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertDouble3Short3Double3: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_64, 3), INPUTSIZE);
+            scriptRelaxed.forEach_testConvertDouble3Short3Double3(inV, out);
+            verifyResultsConvertShort3Double3(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertDouble3Short3Double3: " + e.toString());
+        }
+    }
+
+    private void verifyResultsConvertShort3Double3(Allocation inV, Allocation out, boolean relaxed) {
+        short[] arrayInV = new short[INPUTSIZE * 4];
+        inV.copyTo(arrayInV);
+        double[] arrayOut = new double[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 3 ; j++) {
+                // Extract the inputs.
+                ArgumentsShortDouble args = new ArgumentsShortDouble();
+                args.inV = arrayInV[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeConvert(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("%d", args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(args.out.toString());
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%24.8g %16x %31a",
+                            arrayOut[i * 4 + j], Double.doubleToRawLongBits(arrayOut[i * 4 + j]), arrayOut[i * 4 + j]));
+                    if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkConvertShort3Double3" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkConvertShort4Double4() {
+        Allocation inV = createRandomIntegerAllocation(mRS, Element.DataType.SIGNED_16, 4, 0xfe88337eaef4235dl, true, 15);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_64, 4), INPUTSIZE);
+            script.forEach_testConvertDouble4Short4Double4(inV, out);
+            verifyResultsConvertShort4Double4(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertDouble4Short4Double4: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_64, 4), INPUTSIZE);
+            scriptRelaxed.forEach_testConvertDouble4Short4Double4(inV, out);
+            verifyResultsConvertShort4Double4(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertDouble4Short4Double4: " + e.toString());
+        }
+    }
+
+    private void verifyResultsConvertShort4Double4(Allocation inV, Allocation out, boolean relaxed) {
+        short[] arrayInV = new short[INPUTSIZE * 4];
+        inV.copyTo(arrayInV);
+        double[] arrayOut = new double[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 4 ; j++) {
+                // Extract the inputs.
+                ArgumentsShortDouble args = new ArgumentsShortDouble();
+                args.inV = arrayInV[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeConvert(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("%d", args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(args.out.toString());
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%24.8g %16x %31a",
+                            arrayOut[i * 4 + j], Double.doubleToRawLongBits(arrayOut[i * 4 + j]), arrayOut[i * 4 + j]));
+                    if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkConvertShort4Double4" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    public class ArgumentsUshortDouble {
+        public short inV;
+        public Floaty out;
+    }
+
+    private void checkConvertUshort2Double2() {
+        Allocation inV = createRandomIntegerAllocation(mRS, Element.DataType.UNSIGNED_16, 2, 0xd2b3fb649e0e6518l, false, 16);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_64, 2), INPUTSIZE);
+            script.forEach_testConvertDouble2Ushort2Double2(inV, out);
+            verifyResultsConvertUshort2Double2(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertDouble2Ushort2Double2: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_64, 2), INPUTSIZE);
+            scriptRelaxed.forEach_testConvertDouble2Ushort2Double2(inV, out);
+            verifyResultsConvertUshort2Double2(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertDouble2Ushort2Double2: " + e.toString());
+        }
+    }
+
+    private void verifyResultsConvertUshort2Double2(Allocation inV, Allocation out, boolean relaxed) {
+        short[] arrayInV = new short[INPUTSIZE * 2];
+        inV.copyTo(arrayInV);
+        double[] arrayOut = new double[INPUTSIZE * 2];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 2 ; j++) {
+                // Extract the inputs.
+                ArgumentsUshortDouble args = new ArgumentsUshortDouble();
+                args.inV = arrayInV[i * 2 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeConvert(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (!args.out.couldBe(arrayOut[i * 2 + j])) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("0x%x", args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(args.out.toString());
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%24.8g %16x %31a",
+                            arrayOut[i * 2 + j], Double.doubleToRawLongBits(arrayOut[i * 2 + j]), arrayOut[i * 2 + j]));
+                    if (!args.out.couldBe(arrayOut[i * 2 + j])) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkConvertUshort2Double2" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkConvertUshort3Double3() {
+        Allocation inV = createRandomIntegerAllocation(mRS, Element.DataType.UNSIGNED_16, 3, 0xd300c2ebf469f644l, false, 16);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_64, 3), INPUTSIZE);
+            script.forEach_testConvertDouble3Ushort3Double3(inV, out);
+            verifyResultsConvertUshort3Double3(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertDouble3Ushort3Double3: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_64, 3), INPUTSIZE);
+            scriptRelaxed.forEach_testConvertDouble3Ushort3Double3(inV, out);
+            verifyResultsConvertUshort3Double3(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertDouble3Ushort3Double3: " + e.toString());
+        }
+    }
+
+    private void verifyResultsConvertUshort3Double3(Allocation inV, Allocation out, boolean relaxed) {
+        short[] arrayInV = new short[INPUTSIZE * 4];
+        inV.copyTo(arrayInV);
+        double[] arrayOut = new double[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 3 ; j++) {
+                // Extract the inputs.
+                ArgumentsUshortDouble args = new ArgumentsUshortDouble();
+                args.inV = arrayInV[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeConvert(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("0x%x", args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(args.out.toString());
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%24.8g %16x %31a",
+                            arrayOut[i * 4 + j], Double.doubleToRawLongBits(arrayOut[i * 4 + j]), arrayOut[i * 4 + j]));
+                    if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkConvertUshort3Double3" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkConvertUshort4Double4() {
+        Allocation inV = createRandomIntegerAllocation(mRS, Element.DataType.UNSIGNED_16, 4, 0xd34d8a734ac58770l, false, 16);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_64, 4), INPUTSIZE);
+            script.forEach_testConvertDouble4Ushort4Double4(inV, out);
+            verifyResultsConvertUshort4Double4(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertDouble4Ushort4Double4: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_64, 4), INPUTSIZE);
+            scriptRelaxed.forEach_testConvertDouble4Ushort4Double4(inV, out);
+            verifyResultsConvertUshort4Double4(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertDouble4Ushort4Double4: " + e.toString());
+        }
+    }
+
+    private void verifyResultsConvertUshort4Double4(Allocation inV, Allocation out, boolean relaxed) {
+        short[] arrayInV = new short[INPUTSIZE * 4];
+        inV.copyTo(arrayInV);
+        double[] arrayOut = new double[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 4 ; j++) {
+                // Extract the inputs.
+                ArgumentsUshortDouble args = new ArgumentsUshortDouble();
+                args.inV = arrayInV[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeConvert(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("0x%x", args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(args.out.toString());
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%24.8g %16x %31a",
+                            arrayOut[i * 4 + j], Double.doubleToRawLongBits(arrayOut[i * 4 + j]), arrayOut[i * 4 + j]));
+                    if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkConvertUshort4Double4" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    public class ArgumentsIntDouble {
+        public int inV;
+        public Floaty out;
+    }
+
+    private void checkConvertInt2Double2() {
+        Allocation inV = createRandomIntegerAllocation(mRS, Element.DataType.SIGNED_32, 2, 0x1be423b7a40fc8f6l, true, 31);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_64, 2), INPUTSIZE);
+            script.forEach_testConvertDouble2Int2Double2(inV, out);
+            verifyResultsConvertInt2Double2(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertDouble2Int2Double2: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_64, 2), INPUTSIZE);
+            scriptRelaxed.forEach_testConvertDouble2Int2Double2(inV, out);
+            verifyResultsConvertInt2Double2(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertDouble2Int2Double2: " + e.toString());
+        }
+    }
+
+    private void verifyResultsConvertInt2Double2(Allocation inV, Allocation out, boolean relaxed) {
+        int[] arrayInV = new int[INPUTSIZE * 2];
+        inV.copyTo(arrayInV);
+        double[] arrayOut = new double[INPUTSIZE * 2];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 2 ; j++) {
+                // Extract the inputs.
+                ArgumentsIntDouble args = new ArgumentsIntDouble();
+                args.inV = arrayInV[i * 2 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeConvert(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (!args.out.couldBe(arrayOut[i * 2 + j])) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("%d", args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(args.out.toString());
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%24.8g %16x %31a",
+                            arrayOut[i * 2 + j], Double.doubleToRawLongBits(arrayOut[i * 2 + j]), arrayOut[i * 2 + j]));
+                    if (!args.out.couldBe(arrayOut[i * 2 + j])) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkConvertInt2Double2" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkConvertInt3Double3() {
+        Allocation inV = createRandomIntegerAllocation(mRS, Element.DataType.SIGNED_32, 3, 0x1c30eb3efa6b5a22l, true, 31);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_64, 3), INPUTSIZE);
+            script.forEach_testConvertDouble3Int3Double3(inV, out);
+            verifyResultsConvertInt3Double3(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertDouble3Int3Double3: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_64, 3), INPUTSIZE);
+            scriptRelaxed.forEach_testConvertDouble3Int3Double3(inV, out);
+            verifyResultsConvertInt3Double3(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertDouble3Int3Double3: " + e.toString());
+        }
+    }
+
+    private void verifyResultsConvertInt3Double3(Allocation inV, Allocation out, boolean relaxed) {
+        int[] arrayInV = new int[INPUTSIZE * 4];
+        inV.copyTo(arrayInV);
+        double[] arrayOut = new double[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 3 ; j++) {
+                // Extract the inputs.
+                ArgumentsIntDouble args = new ArgumentsIntDouble();
+                args.inV = arrayInV[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeConvert(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("%d", args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(args.out.toString());
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%24.8g %16x %31a",
+                            arrayOut[i * 4 + j], Double.doubleToRawLongBits(arrayOut[i * 4 + j]), arrayOut[i * 4 + j]));
+                    if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkConvertInt3Double3" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkConvertInt4Double4() {
+        Allocation inV = createRandomIntegerAllocation(mRS, Element.DataType.SIGNED_32, 4, 0x1c7db2c650c6eb4el, true, 31);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_64, 4), INPUTSIZE);
+            script.forEach_testConvertDouble4Int4Double4(inV, out);
+            verifyResultsConvertInt4Double4(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertDouble4Int4Double4: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_64, 4), INPUTSIZE);
+            scriptRelaxed.forEach_testConvertDouble4Int4Double4(inV, out);
+            verifyResultsConvertInt4Double4(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertDouble4Int4Double4: " + e.toString());
+        }
+    }
+
+    private void verifyResultsConvertInt4Double4(Allocation inV, Allocation out, boolean relaxed) {
+        int[] arrayInV = new int[INPUTSIZE * 4];
+        inV.copyTo(arrayInV);
+        double[] arrayOut = new double[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 4 ; j++) {
+                // Extract the inputs.
+                ArgumentsIntDouble args = new ArgumentsIntDouble();
+                args.inV = arrayInV[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeConvert(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("%d", args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(args.out.toString());
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%24.8g %16x %31a",
+                            arrayOut[i * 4 + j], Double.doubleToRawLongBits(arrayOut[i * 4 + j]), arrayOut[i * 4 + j]));
+                    if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkConvertInt4Double4" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    public class ArgumentsUintDouble {
+        public int inV;
+        public Floaty out;
+    }
+
+    private void checkConvertUint2Double2() {
+        Allocation inV = createRandomIntegerAllocation(mRS, Element.DataType.UNSIGNED_32, 2, 0x40b243bf3fe7e2a1l, false, 32);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_64, 2), INPUTSIZE);
+            script.forEach_testConvertDouble2Uint2Double2(inV, out);
+            verifyResultsConvertUint2Double2(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertDouble2Uint2Double2: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_64, 2), INPUTSIZE);
+            scriptRelaxed.forEach_testConvertDouble2Uint2Double2(inV, out);
+            verifyResultsConvertUint2Double2(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertDouble2Uint2Double2: " + e.toString());
+        }
+    }
+
+    private void verifyResultsConvertUint2Double2(Allocation inV, Allocation out, boolean relaxed) {
+        int[] arrayInV = new int[INPUTSIZE * 2];
+        inV.copyTo(arrayInV);
+        double[] arrayOut = new double[INPUTSIZE * 2];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 2 ; j++) {
+                // Extract the inputs.
+                ArgumentsUintDouble args = new ArgumentsUintDouble();
+                args.inV = arrayInV[i * 2 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeConvert(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (!args.out.couldBe(arrayOut[i * 2 + j])) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("0x%x", args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(args.out.toString());
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%24.8g %16x %31a",
+                            arrayOut[i * 2 + j], Double.doubleToRawLongBits(arrayOut[i * 2 + j]), arrayOut[i * 2 + j]));
+                    if (!args.out.couldBe(arrayOut[i * 2 + j])) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkConvertUint2Double2" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkConvertUint3Double3() {
+        Allocation inV = createRandomIntegerAllocation(mRS, Element.DataType.UNSIGNED_32, 3, 0x40ff0b46964373cdl, false, 32);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_64, 3), INPUTSIZE);
+            script.forEach_testConvertDouble3Uint3Double3(inV, out);
+            verifyResultsConvertUint3Double3(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertDouble3Uint3Double3: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_64, 3), INPUTSIZE);
+            scriptRelaxed.forEach_testConvertDouble3Uint3Double3(inV, out);
+            verifyResultsConvertUint3Double3(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertDouble3Uint3Double3: " + e.toString());
+        }
+    }
+
+    private void verifyResultsConvertUint3Double3(Allocation inV, Allocation out, boolean relaxed) {
+        int[] arrayInV = new int[INPUTSIZE * 4];
+        inV.copyTo(arrayInV);
+        double[] arrayOut = new double[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 3 ; j++) {
+                // Extract the inputs.
+                ArgumentsUintDouble args = new ArgumentsUintDouble();
+                args.inV = arrayInV[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeConvert(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("0x%x", args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(args.out.toString());
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%24.8g %16x %31a",
+                            arrayOut[i * 4 + j], Double.doubleToRawLongBits(arrayOut[i * 4 + j]), arrayOut[i * 4 + j]));
+                    if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkConvertUint3Double3" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkConvertUint4Double4() {
+        Allocation inV = createRandomIntegerAllocation(mRS, Element.DataType.UNSIGNED_32, 4, 0x414bd2cdec9f04f9l, false, 32);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_64, 4), INPUTSIZE);
+            script.forEach_testConvertDouble4Uint4Double4(inV, out);
+            verifyResultsConvertUint4Double4(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertDouble4Uint4Double4: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_64, 4), INPUTSIZE);
+            scriptRelaxed.forEach_testConvertDouble4Uint4Double4(inV, out);
+            verifyResultsConvertUint4Double4(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertDouble4Uint4Double4: " + e.toString());
+        }
+    }
+
+    private void verifyResultsConvertUint4Double4(Allocation inV, Allocation out, boolean relaxed) {
+        int[] arrayInV = new int[INPUTSIZE * 4];
+        inV.copyTo(arrayInV);
+        double[] arrayOut = new double[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 4 ; j++) {
+                // Extract the inputs.
+                ArgumentsUintDouble args = new ArgumentsUintDouble();
+                args.inV = arrayInV[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeConvert(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("0x%x", args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(args.out.toString());
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%24.8g %16x %31a",
+                            arrayOut[i * 4 + j], Double.doubleToRawLongBits(arrayOut[i * 4 + j]), arrayOut[i * 4 + j]));
+                    if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkConvertUint4Double4" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    public class ArgumentsFloatLong {
+        public float inV;
+        public long out;
+    }
+
+    private void checkConvertFloat2Long2() {
+        Allocation inV = createRandomFloatAllocation(mRS, Element.DataType.FLOAT_32, 2, 0x239cb49c7d7c0ae0l, -9.2233714870989619200e+18, 9.2233714870989619200e+18);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_64, 2), INPUTSIZE);
+            script.forEach_testConvertLong2Float2Long2(inV, out);
+            verifyResultsConvertFloat2Long2(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertLong2Float2Long2: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_64, 2), INPUTSIZE);
+            scriptRelaxed.forEach_testConvertLong2Float2Long2(inV, out);
+            verifyResultsConvertFloat2Long2(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertLong2Float2Long2: " + e.toString());
+        }
+    }
+
+    private void verifyResultsConvertFloat2Long2(Allocation inV, Allocation out, boolean relaxed) {
+        float[] arrayInV = new float[INPUTSIZE * 2];
+        inV.copyTo(arrayInV);
+        long[] arrayOut = new long[INPUTSIZE * 2];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 2 ; j++) {
+                // Extract the inputs.
+                ArgumentsFloatLong args = new ArgumentsFloatLong();
+                args.inV = arrayInV[i * 2 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeConvert(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (args.out != arrayOut[i * 2 + j]) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            args.inV, Float.floatToRawIntBits(args.inV), args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(String.format("%d", args.out));
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%d", arrayOut[i * 2 + j]));
+                    if (args.out != arrayOut[i * 2 + j]) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkConvertFloat2Long2" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkConvertFloat3Long3() {
+        Allocation inV = createRandomFloatAllocation(mRS, Element.DataType.FLOAT_32, 3, 0x239cbf3ddc83cfd4l, -9.2233714870989619200e+18, 9.2233714870989619200e+18);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_64, 3), INPUTSIZE);
+            script.forEach_testConvertLong3Float3Long3(inV, out);
+            verifyResultsConvertFloat3Long3(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertLong3Float3Long3: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_64, 3), INPUTSIZE);
+            scriptRelaxed.forEach_testConvertLong3Float3Long3(inV, out);
+            verifyResultsConvertFloat3Long3(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertLong3Float3Long3: " + e.toString());
+        }
+    }
+
+    private void verifyResultsConvertFloat3Long3(Allocation inV, Allocation out, boolean relaxed) {
+        float[] arrayInV = new float[INPUTSIZE * 4];
+        inV.copyTo(arrayInV);
+        long[] arrayOut = new long[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 3 ; j++) {
+                // Extract the inputs.
+                ArgumentsFloatLong args = new ArgumentsFloatLong();
+                args.inV = arrayInV[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeConvert(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (args.out != arrayOut[i * 4 + j]) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            args.inV, Float.floatToRawIntBits(args.inV), args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(String.format("%d", args.out));
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%d", arrayOut[i * 4 + j]));
+                    if (args.out != arrayOut[i * 4 + j]) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkConvertFloat3Long3" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkConvertFloat4Long4() {
+        Allocation inV = createRandomFloatAllocation(mRS, Element.DataType.FLOAT_32, 4, 0x239cc9df3b8b94c8l, -9.2233714870989619200e+18, 9.2233714870989619200e+18);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_64, 4), INPUTSIZE);
+            script.forEach_testConvertLong4Float4Long4(inV, out);
+            verifyResultsConvertFloat4Long4(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertLong4Float4Long4: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_64, 4), INPUTSIZE);
+            scriptRelaxed.forEach_testConvertLong4Float4Long4(inV, out);
+            verifyResultsConvertFloat4Long4(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertLong4Float4Long4: " + e.toString());
+        }
+    }
+
+    private void verifyResultsConvertFloat4Long4(Allocation inV, Allocation out, boolean relaxed) {
+        float[] arrayInV = new float[INPUTSIZE * 4];
+        inV.copyTo(arrayInV);
+        long[] arrayOut = new long[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 4 ; j++) {
+                // Extract the inputs.
+                ArgumentsFloatLong args = new ArgumentsFloatLong();
+                args.inV = arrayInV[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeConvert(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (args.out != arrayOut[i * 4 + j]) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            args.inV, Float.floatToRawIntBits(args.inV), args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(String.format("%d", args.out));
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%d", arrayOut[i * 4 + j]));
+                    if (args.out != arrayOut[i * 4 + j]) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkConvertFloat4Long4" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    public class ArgumentsCharLong {
+        public byte inV;
+        public long out;
+    }
+
+    private void checkConvertChar2Long2() {
+        Allocation inV = createRandomIntegerAllocation(mRS, Element.DataType.SIGNED_8, 2, 0xd86189bc290be220l, true, 7);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_64, 2), INPUTSIZE);
+            script.forEach_testConvertLong2Char2Long2(inV, out);
+            verifyResultsConvertChar2Long2(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertLong2Char2Long2: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_64, 2), INPUTSIZE);
+            scriptRelaxed.forEach_testConvertLong2Char2Long2(inV, out);
+            verifyResultsConvertChar2Long2(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertLong2Char2Long2: " + e.toString());
+        }
+    }
+
+    private void verifyResultsConvertChar2Long2(Allocation inV, Allocation out, boolean relaxed) {
+        byte[] arrayInV = new byte[INPUTSIZE * 2];
+        inV.copyTo(arrayInV);
+        long[] arrayOut = new long[INPUTSIZE * 2];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 2 ; j++) {
+                // Extract the inputs.
+                ArgumentsCharLong args = new ArgumentsCharLong();
+                args.inV = arrayInV[i * 2 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeConvert(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (args.out != arrayOut[i * 2 + j]) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("%d", args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(String.format("%d", args.out));
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%d", arrayOut[i * 2 + j]));
+                    if (args.out != arrayOut[i * 2 + j]) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkConvertChar2Long2" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkConvertChar3Long3() {
+        Allocation inV = createRandomIntegerAllocation(mRS, Element.DataType.SIGNED_8, 3, 0xd861945d8813a714l, true, 7);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_64, 3), INPUTSIZE);
+            script.forEach_testConvertLong3Char3Long3(inV, out);
+            verifyResultsConvertChar3Long3(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertLong3Char3Long3: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_64, 3), INPUTSIZE);
+            scriptRelaxed.forEach_testConvertLong3Char3Long3(inV, out);
+            verifyResultsConvertChar3Long3(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertLong3Char3Long3: " + e.toString());
+        }
+    }
+
+    private void verifyResultsConvertChar3Long3(Allocation inV, Allocation out, boolean relaxed) {
+        byte[] arrayInV = new byte[INPUTSIZE * 4];
+        inV.copyTo(arrayInV);
+        long[] arrayOut = new long[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 3 ; j++) {
+                // Extract the inputs.
+                ArgumentsCharLong args = new ArgumentsCharLong();
+                args.inV = arrayInV[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeConvert(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (args.out != arrayOut[i * 4 + j]) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("%d", args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(String.format("%d", args.out));
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%d", arrayOut[i * 4 + j]));
+                    if (args.out != arrayOut[i * 4 + j]) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkConvertChar3Long3" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkConvertChar4Long4() {
+        Allocation inV = createRandomIntegerAllocation(mRS, Element.DataType.SIGNED_8, 4, 0xd8619efee71b6c08l, true, 7);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_64, 4), INPUTSIZE);
+            script.forEach_testConvertLong4Char4Long4(inV, out);
+            verifyResultsConvertChar4Long4(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertLong4Char4Long4: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_64, 4), INPUTSIZE);
+            scriptRelaxed.forEach_testConvertLong4Char4Long4(inV, out);
+            verifyResultsConvertChar4Long4(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertLong4Char4Long4: " + e.toString());
+        }
+    }
+
+    private void verifyResultsConvertChar4Long4(Allocation inV, Allocation out, boolean relaxed) {
+        byte[] arrayInV = new byte[INPUTSIZE * 4];
+        inV.copyTo(arrayInV);
+        long[] arrayOut = new long[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 4 ; j++) {
+                // Extract the inputs.
+                ArgumentsCharLong args = new ArgumentsCharLong();
+                args.inV = arrayInV[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeConvert(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (args.out != arrayOut[i * 4 + j]) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("%d", args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(String.format("%d", args.out));
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%d", arrayOut[i * 4 + j]));
+                    if (args.out != arrayOut[i * 4 + j]) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkConvertChar4Long4" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    public class ArgumentsUcharLong {
+        public byte inV;
+        public long out;
+    }
+
+    private void checkConvertUchar2Long2() {
+        Allocation inV = createRandomIntegerAllocation(mRS, Element.DataType.UNSIGNED_8, 2, 0x7fef4309bb8deea1l, false, 8);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_64, 2), INPUTSIZE);
+            script.forEach_testConvertLong2Uchar2Long2(inV, out);
+            verifyResultsConvertUchar2Long2(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertLong2Uchar2Long2: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_64, 2), INPUTSIZE);
+            scriptRelaxed.forEach_testConvertLong2Uchar2Long2(inV, out);
+            verifyResultsConvertUchar2Long2(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertLong2Uchar2Long2: " + e.toString());
+        }
+    }
+
+    private void verifyResultsConvertUchar2Long2(Allocation inV, Allocation out, boolean relaxed) {
+        byte[] arrayInV = new byte[INPUTSIZE * 2];
+        inV.copyTo(arrayInV);
+        long[] arrayOut = new long[INPUTSIZE * 2];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 2 ; j++) {
+                // Extract the inputs.
+                ArgumentsUcharLong args = new ArgumentsUcharLong();
+                args.inV = arrayInV[i * 2 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeConvert(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (args.out != arrayOut[i * 2 + j]) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("0x%x", args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(String.format("%d", args.out));
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%d", arrayOut[i * 2 + j]));
+                    if (args.out != arrayOut[i * 2 + j]) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkConvertUchar2Long2" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkConvertUchar3Long3() {
+        Allocation inV = createRandomIntegerAllocation(mRS, Element.DataType.UNSIGNED_8, 3, 0x7fef4dab1a95b395l, false, 8);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_64, 3), INPUTSIZE);
+            script.forEach_testConvertLong3Uchar3Long3(inV, out);
+            verifyResultsConvertUchar3Long3(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertLong3Uchar3Long3: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_64, 3), INPUTSIZE);
+            scriptRelaxed.forEach_testConvertLong3Uchar3Long3(inV, out);
+            verifyResultsConvertUchar3Long3(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertLong3Uchar3Long3: " + e.toString());
+        }
+    }
+
+    private void verifyResultsConvertUchar3Long3(Allocation inV, Allocation out, boolean relaxed) {
+        byte[] arrayInV = new byte[INPUTSIZE * 4];
+        inV.copyTo(arrayInV);
+        long[] arrayOut = new long[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 3 ; j++) {
+                // Extract the inputs.
+                ArgumentsUcharLong args = new ArgumentsUcharLong();
+                args.inV = arrayInV[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeConvert(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (args.out != arrayOut[i * 4 + j]) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("0x%x", args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(String.format("%d", args.out));
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%d", arrayOut[i * 4 + j]));
+                    if (args.out != arrayOut[i * 4 + j]) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkConvertUchar3Long3" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkConvertUchar4Long4() {
+        Allocation inV = createRandomIntegerAllocation(mRS, Element.DataType.UNSIGNED_8, 4, 0x7fef584c799d7889l, false, 8);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_64, 4), INPUTSIZE);
+            script.forEach_testConvertLong4Uchar4Long4(inV, out);
+            verifyResultsConvertUchar4Long4(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertLong4Uchar4Long4: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_64, 4), INPUTSIZE);
+            scriptRelaxed.forEach_testConvertLong4Uchar4Long4(inV, out);
+            verifyResultsConvertUchar4Long4(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertLong4Uchar4Long4: " + e.toString());
+        }
+    }
+
+    private void verifyResultsConvertUchar4Long4(Allocation inV, Allocation out, boolean relaxed) {
+        byte[] arrayInV = new byte[INPUTSIZE * 4];
+        inV.copyTo(arrayInV);
+        long[] arrayOut = new long[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 4 ; j++) {
+                // Extract the inputs.
+                ArgumentsUcharLong args = new ArgumentsUcharLong();
+                args.inV = arrayInV[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeConvert(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (args.out != arrayOut[i * 4 + j]) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("0x%x", args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(String.format("%d", args.out));
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%d", arrayOut[i * 4 + j]));
+                    if (args.out != arrayOut[i * 4 + j]) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkConvertUchar4Long4" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    public class ArgumentsShortLong {
+        public short inV;
+        public long out;
+    }
+
+    private void checkConvertShort2Long2() {
+        Allocation inV = createRandomIntegerAllocation(mRS, Element.DataType.SIGNED_16, 2, 0x68ab674669c97ce4l, true, 15);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_64, 2), INPUTSIZE);
+            script.forEach_testConvertLong2Short2Long2(inV, out);
+            verifyResultsConvertShort2Long2(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertLong2Short2Long2: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_64, 2), INPUTSIZE);
+            scriptRelaxed.forEach_testConvertLong2Short2Long2(inV, out);
+            verifyResultsConvertShort2Long2(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertLong2Short2Long2: " + e.toString());
+        }
+    }
+
+    private void verifyResultsConvertShort2Long2(Allocation inV, Allocation out, boolean relaxed) {
+        short[] arrayInV = new short[INPUTSIZE * 2];
+        inV.copyTo(arrayInV);
+        long[] arrayOut = new long[INPUTSIZE * 2];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 2 ; j++) {
+                // Extract the inputs.
+                ArgumentsShortLong args = new ArgumentsShortLong();
+                args.inV = arrayInV[i * 2 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeConvert(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (args.out != arrayOut[i * 2 + j]) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("%d", args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(String.format("%d", args.out));
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%d", arrayOut[i * 2 + j]));
+                    if (args.out != arrayOut[i * 2 + j]) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkConvertShort2Long2" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkConvertShort3Long3() {
+        Allocation inV = createRandomIntegerAllocation(mRS, Element.DataType.SIGNED_16, 3, 0x68ab71e7c8d141d8l, true, 15);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_64, 3), INPUTSIZE);
+            script.forEach_testConvertLong3Short3Long3(inV, out);
+            verifyResultsConvertShort3Long3(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertLong3Short3Long3: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_64, 3), INPUTSIZE);
+            scriptRelaxed.forEach_testConvertLong3Short3Long3(inV, out);
+            verifyResultsConvertShort3Long3(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertLong3Short3Long3: " + e.toString());
+        }
+    }
+
+    private void verifyResultsConvertShort3Long3(Allocation inV, Allocation out, boolean relaxed) {
+        short[] arrayInV = new short[INPUTSIZE * 4];
+        inV.copyTo(arrayInV);
+        long[] arrayOut = new long[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 3 ; j++) {
+                // Extract the inputs.
+                ArgumentsShortLong args = new ArgumentsShortLong();
+                args.inV = arrayInV[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeConvert(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (args.out != arrayOut[i * 4 + j]) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("%d", args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(String.format("%d", args.out));
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%d", arrayOut[i * 4 + j]));
+                    if (args.out != arrayOut[i * 4 + j]) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkConvertShort3Long3" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkConvertShort4Long4() {
+        Allocation inV = createRandomIntegerAllocation(mRS, Element.DataType.SIGNED_16, 4, 0x68ab7c8927d906ccl, true, 15);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_64, 4), INPUTSIZE);
+            script.forEach_testConvertLong4Short4Long4(inV, out);
+            verifyResultsConvertShort4Long4(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertLong4Short4Long4: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_64, 4), INPUTSIZE);
+            scriptRelaxed.forEach_testConvertLong4Short4Long4(inV, out);
+            verifyResultsConvertShort4Long4(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertLong4Short4Long4: " + e.toString());
+        }
+    }
+
+    private void verifyResultsConvertShort4Long4(Allocation inV, Allocation out, boolean relaxed) {
+        short[] arrayInV = new short[INPUTSIZE * 4];
+        inV.copyTo(arrayInV);
+        long[] arrayOut = new long[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 4 ; j++) {
+                // Extract the inputs.
+                ArgumentsShortLong args = new ArgumentsShortLong();
+                args.inV = arrayInV[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeConvert(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (args.out != arrayOut[i * 4 + j]) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("%d", args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(String.format("%d", args.out));
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%d", arrayOut[i * 4 + j]));
+                    if (args.out != arrayOut[i * 4 + j]) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkConvertShort4Long4" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    public class ArgumentsUshortLong {
+        public short inV;
+        public long out;
+    }
+
+    private void checkConvertUshort2Long2() {
+        Allocation inV = createRandomIntegerAllocation(mRS, Element.DataType.UNSIGNED_16, 2, 0x8d79874e05a1968fl, false, 16);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_64, 2), INPUTSIZE);
+            script.forEach_testConvertLong2Ushort2Long2(inV, out);
+            verifyResultsConvertUshort2Long2(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertLong2Ushort2Long2: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_64, 2), INPUTSIZE);
+            scriptRelaxed.forEach_testConvertLong2Ushort2Long2(inV, out);
+            verifyResultsConvertUshort2Long2(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertLong2Ushort2Long2: " + e.toString());
+        }
+    }
+
+    private void verifyResultsConvertUshort2Long2(Allocation inV, Allocation out, boolean relaxed) {
+        short[] arrayInV = new short[INPUTSIZE * 2];
+        inV.copyTo(arrayInV);
+        long[] arrayOut = new long[INPUTSIZE * 2];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 2 ; j++) {
+                // Extract the inputs.
+                ArgumentsUshortLong args = new ArgumentsUshortLong();
+                args.inV = arrayInV[i * 2 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeConvert(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (args.out != arrayOut[i * 2 + j]) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("0x%x", args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(String.format("%d", args.out));
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%d", arrayOut[i * 2 + j]));
+                    if (args.out != arrayOut[i * 2 + j]) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkConvertUshort2Long2" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkConvertUshort3Long3() {
+        Allocation inV = createRandomIntegerAllocation(mRS, Element.DataType.UNSIGNED_16, 3, 0x8d7991ef64a95b83l, false, 16);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_64, 3), INPUTSIZE);
+            script.forEach_testConvertLong3Ushort3Long3(inV, out);
+            verifyResultsConvertUshort3Long3(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertLong3Ushort3Long3: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_64, 3), INPUTSIZE);
+            scriptRelaxed.forEach_testConvertLong3Ushort3Long3(inV, out);
+            verifyResultsConvertUshort3Long3(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertLong3Ushort3Long3: " + e.toString());
+        }
+    }
+
+    private void verifyResultsConvertUshort3Long3(Allocation inV, Allocation out, boolean relaxed) {
+        short[] arrayInV = new short[INPUTSIZE * 4];
+        inV.copyTo(arrayInV);
+        long[] arrayOut = new long[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 3 ; j++) {
+                // Extract the inputs.
+                ArgumentsUshortLong args = new ArgumentsUshortLong();
+                args.inV = arrayInV[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeConvert(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (args.out != arrayOut[i * 4 + j]) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("0x%x", args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(String.format("%d", args.out));
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%d", arrayOut[i * 4 + j]));
+                    if (args.out != arrayOut[i * 4 + j]) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkConvertUshort3Long3" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkConvertUshort4Long4() {
+        Allocation inV = createRandomIntegerAllocation(mRS, Element.DataType.UNSIGNED_16, 4, 0x8d799c90c3b12077l, false, 16);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_64, 4), INPUTSIZE);
+            script.forEach_testConvertLong4Ushort4Long4(inV, out);
+            verifyResultsConvertUshort4Long4(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertLong4Ushort4Long4: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_64, 4), INPUTSIZE);
+            scriptRelaxed.forEach_testConvertLong4Ushort4Long4(inV, out);
+            verifyResultsConvertUshort4Long4(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertLong4Ushort4Long4: " + e.toString());
+        }
+    }
+
+    private void verifyResultsConvertUshort4Long4(Allocation inV, Allocation out, boolean relaxed) {
+        short[] arrayInV = new short[INPUTSIZE * 4];
+        inV.copyTo(arrayInV);
+        long[] arrayOut = new long[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 4 ; j++) {
+                // Extract the inputs.
+                ArgumentsUshortLong args = new ArgumentsUshortLong();
+                args.inV = arrayInV[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeConvert(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (args.out != arrayOut[i * 4 + j]) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("0x%x", args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(String.format("%d", args.out));
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%d", arrayOut[i * 4 + j]));
+                    if (args.out != arrayOut[i * 4 + j]) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkConvertUshort4Long4" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    public class ArgumentsIntLong {
+        public int inV;
+        public long out;
+    }
+
+    private void checkConvertInt2Long2() {
+        Allocation inV = createRandomIntegerAllocation(mRS, Element.DataType.SIGNED_32, 2, 0xd74f538b8a45cb5dl, true, 31);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_64, 2), INPUTSIZE);
+            script.forEach_testConvertLong2Int2Long2(inV, out);
+            verifyResultsConvertInt2Long2(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertLong2Int2Long2: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_64, 2), INPUTSIZE);
+            scriptRelaxed.forEach_testConvertLong2Int2Long2(inV, out);
+            verifyResultsConvertInt2Long2(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertLong2Int2Long2: " + e.toString());
+        }
+    }
+
+    private void verifyResultsConvertInt2Long2(Allocation inV, Allocation out, boolean relaxed) {
+        int[] arrayInV = new int[INPUTSIZE * 2];
+        inV.copyTo(arrayInV);
+        long[] arrayOut = new long[INPUTSIZE * 2];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 2 ; j++) {
+                // Extract the inputs.
+                ArgumentsIntLong args = new ArgumentsIntLong();
+                args.inV = arrayInV[i * 2 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeConvert(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (args.out != arrayOut[i * 2 + j]) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("%d", args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(String.format("%d", args.out));
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%d", arrayOut[i * 2 + j]));
+                    if (args.out != arrayOut[i * 2 + j]) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkConvertInt2Long2" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkConvertInt3Long3() {
+        Allocation inV = createRandomIntegerAllocation(mRS, Element.DataType.SIGNED_32, 3, 0xd74f5e2ce94d9051l, true, 31);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_64, 3), INPUTSIZE);
+            script.forEach_testConvertLong3Int3Long3(inV, out);
+            verifyResultsConvertInt3Long3(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertLong3Int3Long3: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_64, 3), INPUTSIZE);
+            scriptRelaxed.forEach_testConvertLong3Int3Long3(inV, out);
+            verifyResultsConvertInt3Long3(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertLong3Int3Long3: " + e.toString());
+        }
+    }
+
+    private void verifyResultsConvertInt3Long3(Allocation inV, Allocation out, boolean relaxed) {
+        int[] arrayInV = new int[INPUTSIZE * 4];
+        inV.copyTo(arrayInV);
+        long[] arrayOut = new long[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 3 ; j++) {
+                // Extract the inputs.
+                ArgumentsIntLong args = new ArgumentsIntLong();
+                args.inV = arrayInV[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeConvert(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (args.out != arrayOut[i * 4 + j]) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("%d", args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(String.format("%d", args.out));
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%d", arrayOut[i * 4 + j]));
+                    if (args.out != arrayOut[i * 4 + j]) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkConvertInt3Long3" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkConvertInt4Long4() {
+        Allocation inV = createRandomIntegerAllocation(mRS, Element.DataType.SIGNED_32, 4, 0xd74f68ce48555545l, true, 31);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_64, 4), INPUTSIZE);
+            script.forEach_testConvertLong4Int4Long4(inV, out);
+            verifyResultsConvertInt4Long4(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertLong4Int4Long4: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_64, 4), INPUTSIZE);
+            scriptRelaxed.forEach_testConvertLong4Int4Long4(inV, out);
+            verifyResultsConvertInt4Long4(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertLong4Int4Long4: " + e.toString());
+        }
+    }
+
+    private void verifyResultsConvertInt4Long4(Allocation inV, Allocation out, boolean relaxed) {
+        int[] arrayInV = new int[INPUTSIZE * 4];
+        inV.copyTo(arrayInV);
+        long[] arrayOut = new long[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 4 ; j++) {
+                // Extract the inputs.
+                ArgumentsIntLong args = new ArgumentsIntLong();
+                args.inV = arrayInV[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeConvert(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (args.out != arrayOut[i * 4 + j]) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("%d", args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(String.format("%d", args.out));
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%d", arrayOut[i * 4 + j]));
+                    if (args.out != arrayOut[i * 4 + j]) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkConvertInt4Long4" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    public class ArgumentsUintLong {
+        public int inV;
+        public long out;
+    }
+
+    private void checkConvertUint2Long2() {
+        Allocation inV = createRandomIntegerAllocation(mRS, Element.DataType.UNSIGNED_32, 2, 0xe71d0a7587b9ef60l, false, 32);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_64, 2), INPUTSIZE);
+            script.forEach_testConvertLong2Uint2Long2(inV, out);
+            verifyResultsConvertUint2Long2(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertLong2Uint2Long2: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_64, 2), INPUTSIZE);
+            scriptRelaxed.forEach_testConvertLong2Uint2Long2(inV, out);
+            verifyResultsConvertUint2Long2(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertLong2Uint2Long2: " + e.toString());
+        }
+    }
+
+    private void verifyResultsConvertUint2Long2(Allocation inV, Allocation out, boolean relaxed) {
+        int[] arrayInV = new int[INPUTSIZE * 2];
+        inV.copyTo(arrayInV);
+        long[] arrayOut = new long[INPUTSIZE * 2];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 2 ; j++) {
+                // Extract the inputs.
+                ArgumentsUintLong args = new ArgumentsUintLong();
+                args.inV = arrayInV[i * 2 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeConvert(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (args.out != arrayOut[i * 2 + j]) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("0x%x", args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(String.format("%d", args.out));
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%d", arrayOut[i * 2 + j]));
+                    if (args.out != arrayOut[i * 2 + j]) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkConvertUint2Long2" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkConvertUint3Long3() {
+        Allocation inV = createRandomIntegerAllocation(mRS, Element.DataType.UNSIGNED_32, 3, 0xe71d1516e6c1b454l, false, 32);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_64, 3), INPUTSIZE);
+            script.forEach_testConvertLong3Uint3Long3(inV, out);
+            verifyResultsConvertUint3Long3(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertLong3Uint3Long3: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_64, 3), INPUTSIZE);
+            scriptRelaxed.forEach_testConvertLong3Uint3Long3(inV, out);
+            verifyResultsConvertUint3Long3(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertLong3Uint3Long3: " + e.toString());
+        }
+    }
+
+    private void verifyResultsConvertUint3Long3(Allocation inV, Allocation out, boolean relaxed) {
+        int[] arrayInV = new int[INPUTSIZE * 4];
+        inV.copyTo(arrayInV);
+        long[] arrayOut = new long[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 3 ; j++) {
+                // Extract the inputs.
+                ArgumentsUintLong args = new ArgumentsUintLong();
+                args.inV = arrayInV[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeConvert(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (args.out != arrayOut[i * 4 + j]) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("0x%x", args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(String.format("%d", args.out));
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%d", arrayOut[i * 4 + j]));
+                    if (args.out != arrayOut[i * 4 + j]) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkConvertUint3Long3" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkConvertUint4Long4() {
+        Allocation inV = createRandomIntegerAllocation(mRS, Element.DataType.UNSIGNED_32, 4, 0xe71d1fb845c97948l, false, 32);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_64, 4), INPUTSIZE);
+            script.forEach_testConvertLong4Uint4Long4(inV, out);
+            verifyResultsConvertUint4Long4(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertLong4Uint4Long4: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_64, 4), INPUTSIZE);
+            scriptRelaxed.forEach_testConvertLong4Uint4Long4(inV, out);
+            verifyResultsConvertUint4Long4(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertLong4Uint4Long4: " + e.toString());
+        }
+    }
+
+    private void verifyResultsConvertUint4Long4(Allocation inV, Allocation out, boolean relaxed) {
+        int[] arrayInV = new int[INPUTSIZE * 4];
+        inV.copyTo(arrayInV);
+        long[] arrayOut = new long[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 4 ; j++) {
+                // Extract the inputs.
+                ArgumentsUintLong args = new ArgumentsUintLong();
+                args.inV = arrayInV[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeConvert(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (args.out != arrayOut[i * 4 + j]) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("0x%x", args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(String.format("%d", args.out));
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%d", arrayOut[i * 4 + j]));
+                    if (args.out != arrayOut[i * 4 + j]) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkConvertUint4Long4" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    public class ArgumentsFloatUlong {
+        public float inV;
+        public long out;
+    }
+
+    private void checkConvertFloat2Ulong2() {
+        Allocation inV = createRandomFloatAllocation(mRS, Element.DataType.FLOAT_32, 2, 0xfb52b5394ec4cff7l, 0.0000000000000000000e+00, 1.8446742974197923840e+19);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_64, 2), INPUTSIZE);
+            script.forEach_testConvertUlong2Float2Ulong2(inV, out);
+            verifyResultsConvertFloat2Ulong2(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertUlong2Float2Ulong2: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_64, 2), INPUTSIZE);
+            scriptRelaxed.forEach_testConvertUlong2Float2Ulong2(inV, out);
+            verifyResultsConvertFloat2Ulong2(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertUlong2Float2Ulong2: " + e.toString());
+        }
+    }
+
+    private void verifyResultsConvertFloat2Ulong2(Allocation inV, Allocation out, boolean relaxed) {
+        float[] arrayInV = new float[INPUTSIZE * 2];
+        inV.copyTo(arrayInV);
+        long[] arrayOut = new long[INPUTSIZE * 2];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 2 ; j++) {
+                // Extract the inputs.
+                ArgumentsFloatUlong args = new ArgumentsFloatUlong();
+                args.inV = arrayInV[i * 2 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeConvert(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (args.out != arrayOut[i * 2 + j]) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            args.inV, Float.floatToRawIntBits(args.inV), args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(String.format("0x%x", args.out));
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("0x%x", arrayOut[i * 2 + j]));
+                    if (args.out != arrayOut[i * 2 + j]) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkConvertFloat2Ulong2" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkConvertFloat3Ulong3() {
+        Allocation inV = createRandomFloatAllocation(mRS, Element.DataType.FLOAT_32, 3, 0xfb547e5444dff0d5l, 0.0000000000000000000e+00, 1.8446742974197923840e+19);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_64, 3), INPUTSIZE);
+            script.forEach_testConvertUlong3Float3Ulong3(inV, out);
+            verifyResultsConvertFloat3Ulong3(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertUlong3Float3Ulong3: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_64, 3), INPUTSIZE);
+            scriptRelaxed.forEach_testConvertUlong3Float3Ulong3(inV, out);
+            verifyResultsConvertFloat3Ulong3(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertUlong3Float3Ulong3: " + e.toString());
+        }
+    }
+
+    private void verifyResultsConvertFloat3Ulong3(Allocation inV, Allocation out, boolean relaxed) {
+        float[] arrayInV = new float[INPUTSIZE * 4];
+        inV.copyTo(arrayInV);
+        long[] arrayOut = new long[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 3 ; j++) {
+                // Extract the inputs.
+                ArgumentsFloatUlong args = new ArgumentsFloatUlong();
+                args.inV = arrayInV[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeConvert(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (args.out != arrayOut[i * 4 + j]) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            args.inV, Float.floatToRawIntBits(args.inV), args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(String.format("0x%x", args.out));
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("0x%x", arrayOut[i * 4 + j]));
+                    if (args.out != arrayOut[i * 4 + j]) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkConvertFloat3Ulong3" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkConvertFloat4Ulong4() {
+        Allocation inV = createRandomFloatAllocation(mRS, Element.DataType.FLOAT_32, 4, 0xfb56476f3afb11b3l, 0.0000000000000000000e+00, 1.8446742974197923840e+19);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_64, 4), INPUTSIZE);
+            script.forEach_testConvertUlong4Float4Ulong4(inV, out);
+            verifyResultsConvertFloat4Ulong4(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertUlong4Float4Ulong4: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_64, 4), INPUTSIZE);
+            scriptRelaxed.forEach_testConvertUlong4Float4Ulong4(inV, out);
+            verifyResultsConvertFloat4Ulong4(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertUlong4Float4Ulong4: " + e.toString());
+        }
+    }
+
+    private void verifyResultsConvertFloat4Ulong4(Allocation inV, Allocation out, boolean relaxed) {
+        float[] arrayInV = new float[INPUTSIZE * 4];
+        inV.copyTo(arrayInV);
+        long[] arrayOut = new long[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 4 ; j++) {
+                // Extract the inputs.
+                ArgumentsFloatUlong args = new ArgumentsFloatUlong();
+                args.inV = arrayInV[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeConvert(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (args.out != arrayOut[i * 4 + j]) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            args.inV, Float.floatToRawIntBits(args.inV), args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(String.format("0x%x", args.out));
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("0x%x", arrayOut[i * 4 + j]));
+                    if (args.out != arrayOut[i * 4 + j]) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkConvertFloat4Ulong4" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    public class ArgumentsCharUlong {
+        public byte inV;
+        public long out;
+    }
+
+    private void checkConvertChar2Ulong2() {
+        Allocation inV = createRandomIntegerAllocation(mRS, Element.DataType.SIGNED_8, 2, 0x5862818b1fedf7b7l, false, 7);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_64, 2), INPUTSIZE);
+            script.forEach_testConvertUlong2Char2Ulong2(inV, out);
+            verifyResultsConvertChar2Ulong2(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertUlong2Char2Ulong2: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_64, 2), INPUTSIZE);
+            scriptRelaxed.forEach_testConvertUlong2Char2Ulong2(inV, out);
+            verifyResultsConvertChar2Ulong2(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertUlong2Char2Ulong2: " + e.toString());
+        }
+    }
+
+    private void verifyResultsConvertChar2Ulong2(Allocation inV, Allocation out, boolean relaxed) {
+        byte[] arrayInV = new byte[INPUTSIZE * 2];
+        inV.copyTo(arrayInV);
+        long[] arrayOut = new long[INPUTSIZE * 2];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 2 ; j++) {
+                // Extract the inputs.
+                ArgumentsCharUlong args = new ArgumentsCharUlong();
+                args.inV = arrayInV[i * 2 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeConvert(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (args.out != arrayOut[i * 2 + j]) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("%d", args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(String.format("0x%x", args.out));
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("0x%x", arrayOut[i * 2 + j]));
+                    if (args.out != arrayOut[i * 2 + j]) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkConvertChar2Ulong2" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkConvertChar3Ulong3() {
+        Allocation inV = createRandomIntegerAllocation(mRS, Element.DataType.SIGNED_8, 3, 0x58644aa616091895l, false, 7);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_64, 3), INPUTSIZE);
+            script.forEach_testConvertUlong3Char3Ulong3(inV, out);
+            verifyResultsConvertChar3Ulong3(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertUlong3Char3Ulong3: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_64, 3), INPUTSIZE);
+            scriptRelaxed.forEach_testConvertUlong3Char3Ulong3(inV, out);
+            verifyResultsConvertChar3Ulong3(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertUlong3Char3Ulong3: " + e.toString());
+        }
+    }
+
+    private void verifyResultsConvertChar3Ulong3(Allocation inV, Allocation out, boolean relaxed) {
+        byte[] arrayInV = new byte[INPUTSIZE * 4];
+        inV.copyTo(arrayInV);
+        long[] arrayOut = new long[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 3 ; j++) {
+                // Extract the inputs.
+                ArgumentsCharUlong args = new ArgumentsCharUlong();
+                args.inV = arrayInV[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeConvert(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (args.out != arrayOut[i * 4 + j]) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("%d", args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(String.format("0x%x", args.out));
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("0x%x", arrayOut[i * 4 + j]));
+                    if (args.out != arrayOut[i * 4 + j]) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkConvertChar3Ulong3" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkConvertChar4Ulong4() {
+        Allocation inV = createRandomIntegerAllocation(mRS, Element.DataType.SIGNED_8, 4, 0x586613c10c243973l, false, 7);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_64, 4), INPUTSIZE);
+            script.forEach_testConvertUlong4Char4Ulong4(inV, out);
+            verifyResultsConvertChar4Ulong4(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertUlong4Char4Ulong4: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_64, 4), INPUTSIZE);
+            scriptRelaxed.forEach_testConvertUlong4Char4Ulong4(inV, out);
+            verifyResultsConvertChar4Ulong4(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertUlong4Char4Ulong4: " + e.toString());
+        }
+    }
+
+    private void verifyResultsConvertChar4Ulong4(Allocation inV, Allocation out, boolean relaxed) {
+        byte[] arrayInV = new byte[INPUTSIZE * 4];
+        inV.copyTo(arrayInV);
+        long[] arrayOut = new long[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 4 ; j++) {
+                // Extract the inputs.
+                ArgumentsCharUlong args = new ArgumentsCharUlong();
+                args.inV = arrayInV[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeConvert(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (args.out != arrayOut[i * 4 + j]) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("%d", args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(String.format("0x%x", args.out));
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("0x%x", arrayOut[i * 4 + j]));
+                    if (args.out != arrayOut[i * 4 + j]) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkConvertChar4Ulong4" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    public class ArgumentsUcharUlong {
+        public byte inV;
+        public long out;
+    }
+
+    private void checkConvertUchar2Ulong2() {
+        Allocation inV = createRandomIntegerAllocation(mRS, Element.DataType.UNSIGNED_8, 2, 0x7d30a192bbc61162l, false, 8);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_64, 2), INPUTSIZE);
+            script.forEach_testConvertUlong2Uchar2Ulong2(inV, out);
+            verifyResultsConvertUchar2Ulong2(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertUlong2Uchar2Ulong2: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_64, 2), INPUTSIZE);
+            scriptRelaxed.forEach_testConvertUlong2Uchar2Ulong2(inV, out);
+            verifyResultsConvertUchar2Ulong2(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertUlong2Uchar2Ulong2: " + e.toString());
+        }
+    }
+
+    private void verifyResultsConvertUchar2Ulong2(Allocation inV, Allocation out, boolean relaxed) {
+        byte[] arrayInV = new byte[INPUTSIZE * 2];
+        inV.copyTo(arrayInV);
+        long[] arrayOut = new long[INPUTSIZE * 2];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 2 ; j++) {
+                // Extract the inputs.
+                ArgumentsUcharUlong args = new ArgumentsUcharUlong();
+                args.inV = arrayInV[i * 2 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeConvert(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (args.out != arrayOut[i * 2 + j]) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("0x%x", args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(String.format("0x%x", args.out));
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("0x%x", arrayOut[i * 2 + j]));
+                    if (args.out != arrayOut[i * 2 + j]) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkConvertUchar2Ulong2" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkConvertUchar3Ulong3() {
+        Allocation inV = createRandomIntegerAllocation(mRS, Element.DataType.UNSIGNED_8, 3, 0x7d326aadb1e13240l, false, 8);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_64, 3), INPUTSIZE);
+            script.forEach_testConvertUlong3Uchar3Ulong3(inV, out);
+            verifyResultsConvertUchar3Ulong3(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertUlong3Uchar3Ulong3: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_64, 3), INPUTSIZE);
+            scriptRelaxed.forEach_testConvertUlong3Uchar3Ulong3(inV, out);
+            verifyResultsConvertUchar3Ulong3(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertUlong3Uchar3Ulong3: " + e.toString());
+        }
+    }
+
+    private void verifyResultsConvertUchar3Ulong3(Allocation inV, Allocation out, boolean relaxed) {
+        byte[] arrayInV = new byte[INPUTSIZE * 4];
+        inV.copyTo(arrayInV);
+        long[] arrayOut = new long[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 3 ; j++) {
+                // Extract the inputs.
+                ArgumentsUcharUlong args = new ArgumentsUcharUlong();
+                args.inV = arrayInV[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeConvert(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (args.out != arrayOut[i * 4 + j]) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("0x%x", args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(String.format("0x%x", args.out));
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("0x%x", arrayOut[i * 4 + j]));
+                    if (args.out != arrayOut[i * 4 + j]) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkConvertUchar3Ulong3" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkConvertUchar4Ulong4() {
+        Allocation inV = createRandomIntegerAllocation(mRS, Element.DataType.UNSIGNED_8, 4, 0x7d3433c8a7fc531el, false, 8);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_64, 4), INPUTSIZE);
+            script.forEach_testConvertUlong4Uchar4Ulong4(inV, out);
+            verifyResultsConvertUchar4Ulong4(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertUlong4Uchar4Ulong4: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_64, 4), INPUTSIZE);
+            scriptRelaxed.forEach_testConvertUlong4Uchar4Ulong4(inV, out);
+            verifyResultsConvertUchar4Ulong4(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertUlong4Uchar4Ulong4: " + e.toString());
+        }
+    }
+
+    private void verifyResultsConvertUchar4Ulong4(Allocation inV, Allocation out, boolean relaxed) {
+        byte[] arrayInV = new byte[INPUTSIZE * 4];
+        inV.copyTo(arrayInV);
+        long[] arrayOut = new long[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 4 ; j++) {
+                // Extract the inputs.
+                ArgumentsUcharUlong args = new ArgumentsUcharUlong();
+                args.inV = arrayInV[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeConvert(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (args.out != arrayOut[i * 4 + j]) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("0x%x", args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(String.format("0x%x", args.out));
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("0x%x", arrayOut[i * 4 + j]));
+                    if (args.out != arrayOut[i * 4 + j]) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkConvertUchar4Ulong4" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    public class ArgumentsShortUlong {
+        public short inV;
+        public long out;
+    }
+
+    private void checkConvertShort2Ulong2() {
+        Allocation inV = createRandomIntegerAllocation(mRS, Element.DataType.SIGNED_16, 2, 0x94cab7c3ffc6f6a3l, false, 15);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_64, 2), INPUTSIZE);
+            script.forEach_testConvertUlong2Short2Ulong2(inV, out);
+            verifyResultsConvertShort2Ulong2(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertUlong2Short2Ulong2: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_64, 2), INPUTSIZE);
+            scriptRelaxed.forEach_testConvertUlong2Short2Ulong2(inV, out);
+            verifyResultsConvertShort2Ulong2(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertUlong2Short2Ulong2: " + e.toString());
+        }
+    }
+
+    private void verifyResultsConvertShort2Ulong2(Allocation inV, Allocation out, boolean relaxed) {
+        short[] arrayInV = new short[INPUTSIZE * 2];
+        inV.copyTo(arrayInV);
+        long[] arrayOut = new long[INPUTSIZE * 2];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 2 ; j++) {
+                // Extract the inputs.
+                ArgumentsShortUlong args = new ArgumentsShortUlong();
+                args.inV = arrayInV[i * 2 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeConvert(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (args.out != arrayOut[i * 2 + j]) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("%d", args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(String.format("0x%x", args.out));
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("0x%x", arrayOut[i * 2 + j]));
+                    if (args.out != arrayOut[i * 2 + j]) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkConvertShort2Ulong2" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkConvertShort3Ulong3() {
+        Allocation inV = createRandomIntegerAllocation(mRS, Element.DataType.SIGNED_16, 3, 0x94cc80def5e21781l, false, 15);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_64, 3), INPUTSIZE);
+            script.forEach_testConvertUlong3Short3Ulong3(inV, out);
+            verifyResultsConvertShort3Ulong3(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertUlong3Short3Ulong3: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_64, 3), INPUTSIZE);
+            scriptRelaxed.forEach_testConvertUlong3Short3Ulong3(inV, out);
+            verifyResultsConvertShort3Ulong3(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertUlong3Short3Ulong3: " + e.toString());
+        }
+    }
+
+    private void verifyResultsConvertShort3Ulong3(Allocation inV, Allocation out, boolean relaxed) {
+        short[] arrayInV = new short[INPUTSIZE * 4];
+        inV.copyTo(arrayInV);
+        long[] arrayOut = new long[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 3 ; j++) {
+                // Extract the inputs.
+                ArgumentsShortUlong args = new ArgumentsShortUlong();
+                args.inV = arrayInV[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeConvert(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (args.out != arrayOut[i * 4 + j]) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("%d", args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(String.format("0x%x", args.out));
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("0x%x", arrayOut[i * 4 + j]));
+                    if (args.out != arrayOut[i * 4 + j]) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkConvertShort3Ulong3" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkConvertShort4Ulong4() {
+        Allocation inV = createRandomIntegerAllocation(mRS, Element.DataType.SIGNED_16, 4, 0x94ce49f9ebfd385fl, false, 15);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_64, 4), INPUTSIZE);
+            script.forEach_testConvertUlong4Short4Ulong4(inV, out);
+            verifyResultsConvertShort4Ulong4(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertUlong4Short4Ulong4: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_64, 4), INPUTSIZE);
+            scriptRelaxed.forEach_testConvertUlong4Short4Ulong4(inV, out);
+            verifyResultsConvertShort4Ulong4(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertUlong4Short4Ulong4: " + e.toString());
+        }
+    }
+
+    private void verifyResultsConvertShort4Ulong4(Allocation inV, Allocation out, boolean relaxed) {
+        short[] arrayInV = new short[INPUTSIZE * 4];
+        inV.copyTo(arrayInV);
+        long[] arrayOut = new long[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 4 ; j++) {
+                // Extract the inputs.
+                ArgumentsShortUlong args = new ArgumentsShortUlong();
+                args.inV = arrayInV[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeConvert(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (args.out != arrayOut[i * 4 + j]) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("%d", args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(String.format("0x%x", args.out));
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("0x%x", arrayOut[i * 4 + j]));
+                    if (args.out != arrayOut[i * 4 + j]) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkConvertShort4Ulong4" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    public class ArgumentsUshortUlong {
+        public short inV;
+        public long out;
+    }
+
+    private void checkConvertUshort2Ulong2() {
+        Allocation inV = createRandomIntegerAllocation(mRS, Element.DataType.UNSIGNED_16, 2, 0xc36a190b2d13465cl, false, 16);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_64, 2), INPUTSIZE);
+            script.forEach_testConvertUlong2Ushort2Ulong2(inV, out);
+            verifyResultsConvertUshort2Ulong2(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertUlong2Ushort2Ulong2: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_64, 2), INPUTSIZE);
+            scriptRelaxed.forEach_testConvertUlong2Ushort2Ulong2(inV, out);
+            verifyResultsConvertUshort2Ulong2(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertUlong2Ushort2Ulong2: " + e.toString());
+        }
+    }
+
+    private void verifyResultsConvertUshort2Ulong2(Allocation inV, Allocation out, boolean relaxed) {
+        short[] arrayInV = new short[INPUTSIZE * 2];
+        inV.copyTo(arrayInV);
+        long[] arrayOut = new long[INPUTSIZE * 2];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 2 ; j++) {
+                // Extract the inputs.
+                ArgumentsUshortUlong args = new ArgumentsUshortUlong();
+                args.inV = arrayInV[i * 2 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeConvert(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (args.out != arrayOut[i * 2 + j]) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("0x%x", args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(String.format("0x%x", args.out));
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("0x%x", arrayOut[i * 2 + j]));
+                    if (args.out != arrayOut[i * 2 + j]) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkConvertUshort2Ulong2" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkConvertUshort3Ulong3() {
+        Allocation inV = createRandomIntegerAllocation(mRS, Element.DataType.UNSIGNED_16, 3, 0xc36be226232e673al, false, 16);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_64, 3), INPUTSIZE);
+            script.forEach_testConvertUlong3Ushort3Ulong3(inV, out);
+            verifyResultsConvertUshort3Ulong3(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertUlong3Ushort3Ulong3: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_64, 3), INPUTSIZE);
+            scriptRelaxed.forEach_testConvertUlong3Ushort3Ulong3(inV, out);
+            verifyResultsConvertUshort3Ulong3(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertUlong3Ushort3Ulong3: " + e.toString());
+        }
+    }
+
+    private void verifyResultsConvertUshort3Ulong3(Allocation inV, Allocation out, boolean relaxed) {
+        short[] arrayInV = new short[INPUTSIZE * 4];
+        inV.copyTo(arrayInV);
+        long[] arrayOut = new long[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 3 ; j++) {
+                // Extract the inputs.
+                ArgumentsUshortUlong args = new ArgumentsUshortUlong();
+                args.inV = arrayInV[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeConvert(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (args.out != arrayOut[i * 4 + j]) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("0x%x", args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(String.format("0x%x", args.out));
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("0x%x", arrayOut[i * 4 + j]));
+                    if (args.out != arrayOut[i * 4 + j]) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkConvertUshort3Ulong3" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkConvertUshort4Ulong4() {
+        Allocation inV = createRandomIntegerAllocation(mRS, Element.DataType.UNSIGNED_16, 4, 0xc36dab4119498818l, false, 16);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_64, 4), INPUTSIZE);
+            script.forEach_testConvertUlong4Ushort4Ulong4(inV, out);
+            verifyResultsConvertUshort4Ulong4(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertUlong4Ushort4Ulong4: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_64, 4), INPUTSIZE);
+            scriptRelaxed.forEach_testConvertUlong4Ushort4Ulong4(inV, out);
+            verifyResultsConvertUshort4Ulong4(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertUlong4Ushort4Ulong4: " + e.toString());
+        }
+    }
+
+    private void verifyResultsConvertUshort4Ulong4(Allocation inV, Allocation out, boolean relaxed) {
+        short[] arrayInV = new short[INPUTSIZE * 4];
+        inV.copyTo(arrayInV);
+        long[] arrayOut = new long[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 4 ; j++) {
+                // Extract the inputs.
+                ArgumentsUshortUlong args = new ArgumentsUshortUlong();
+                args.inV = arrayInV[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeConvert(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (args.out != arrayOut[i * 4 + j]) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("0x%x", args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(String.format("0x%x", args.out));
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("0x%x", arrayOut[i * 4 + j]));
+                    if (args.out != arrayOut[i * 4 + j]) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkConvertUshort4Ulong4" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    public class ArgumentsIntUlong {
+        public int inV;
+        public long out;
+    }
+
+    private void checkConvertInt2Ulong2() {
+        Allocation inV = createRandomIntegerAllocation(mRS, Element.DataType.SIGNED_32, 2, 0x2a53676074a824f6l, false, 31);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_64, 2), INPUTSIZE);
+            script.forEach_testConvertUlong2Int2Ulong2(inV, out);
+            verifyResultsConvertInt2Ulong2(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertUlong2Int2Ulong2: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_64, 2), INPUTSIZE);
+            scriptRelaxed.forEach_testConvertUlong2Int2Ulong2(inV, out);
+            verifyResultsConvertInt2Ulong2(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertUlong2Int2Ulong2: " + e.toString());
+        }
+    }
+
+    private void verifyResultsConvertInt2Ulong2(Allocation inV, Allocation out, boolean relaxed) {
+        int[] arrayInV = new int[INPUTSIZE * 2];
+        inV.copyTo(arrayInV);
+        long[] arrayOut = new long[INPUTSIZE * 2];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 2 ; j++) {
+                // Extract the inputs.
+                ArgumentsIntUlong args = new ArgumentsIntUlong();
+                args.inV = arrayInV[i * 2 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeConvert(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (args.out != arrayOut[i * 2 + j]) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("%d", args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(String.format("0x%x", args.out));
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("0x%x", arrayOut[i * 2 + j]));
+                    if (args.out != arrayOut[i * 2 + j]) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkConvertInt2Ulong2" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkConvertInt3Ulong3() {
+        Allocation inV = createRandomIntegerAllocation(mRS, Element.DataType.SIGNED_32, 3, 0x2a55307b6ac345d4l, false, 31);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_64, 3), INPUTSIZE);
+            script.forEach_testConvertUlong3Int3Ulong3(inV, out);
+            verifyResultsConvertInt3Ulong3(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertUlong3Int3Ulong3: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_64, 3), INPUTSIZE);
+            scriptRelaxed.forEach_testConvertUlong3Int3Ulong3(inV, out);
+            verifyResultsConvertInt3Ulong3(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertUlong3Int3Ulong3: " + e.toString());
+        }
+    }
+
+    private void verifyResultsConvertInt3Ulong3(Allocation inV, Allocation out, boolean relaxed) {
+        int[] arrayInV = new int[INPUTSIZE * 4];
+        inV.copyTo(arrayInV);
+        long[] arrayOut = new long[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 3 ; j++) {
+                // Extract the inputs.
+                ArgumentsIntUlong args = new ArgumentsIntUlong();
+                args.inV = arrayInV[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeConvert(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (args.out != arrayOut[i * 4 + j]) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("%d", args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(String.format("0x%x", args.out));
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("0x%x", arrayOut[i * 4 + j]));
+                    if (args.out != arrayOut[i * 4 + j]) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkConvertInt3Ulong3" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkConvertInt4Ulong4() {
+        Allocation inV = createRandomIntegerAllocation(mRS, Element.DataType.SIGNED_32, 4, 0x2a56f99660de66b2l, false, 31);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_64, 4), INPUTSIZE);
+            script.forEach_testConvertUlong4Int4Ulong4(inV, out);
+            verifyResultsConvertInt4Ulong4(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertUlong4Int4Ulong4: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_64, 4), INPUTSIZE);
+            scriptRelaxed.forEach_testConvertUlong4Int4Ulong4(inV, out);
+            verifyResultsConvertInt4Ulong4(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertUlong4Int4Ulong4: " + e.toString());
+        }
+    }
+
+    private void verifyResultsConvertInt4Ulong4(Allocation inV, Allocation out, boolean relaxed) {
+        int[] arrayInV = new int[INPUTSIZE * 4];
+        inV.copyTo(arrayInV);
+        long[] arrayOut = new long[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 4 ; j++) {
+                // Extract the inputs.
+                ArgumentsIntUlong args = new ArgumentsIntUlong();
+                args.inV = arrayInV[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeConvert(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (args.out != arrayOut[i * 4 + j]) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("%d", args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(String.format("0x%x", args.out));
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("0x%x", arrayOut[i * 4 + j]));
+                    if (args.out != arrayOut[i * 4 + j]) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkConvertInt4Ulong4" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    public class ArgumentsUintUlong {
+        public int inV;
+        public long out;
+    }
+
+    private void checkConvertUint2Ulong2() {
+        Allocation inV = createRandomIntegerAllocation(mRS, Element.DataType.UNSIGNED_32, 2, 0xd1e120ae072a3177l, false, 32);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_64, 2), INPUTSIZE);
+            script.forEach_testConvertUlong2Uint2Ulong2(inV, out);
+            verifyResultsConvertUint2Ulong2(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertUlong2Uint2Ulong2: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_64, 2), INPUTSIZE);
+            scriptRelaxed.forEach_testConvertUlong2Uint2Ulong2(inV, out);
+            verifyResultsConvertUint2Ulong2(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertUlong2Uint2Ulong2: " + e.toString());
+        }
+    }
+
+    private void verifyResultsConvertUint2Ulong2(Allocation inV, Allocation out, boolean relaxed) {
+        int[] arrayInV = new int[INPUTSIZE * 2];
+        inV.copyTo(arrayInV);
+        long[] arrayOut = new long[INPUTSIZE * 2];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 2 ; j++) {
+                // Extract the inputs.
+                ArgumentsUintUlong args = new ArgumentsUintUlong();
+                args.inV = arrayInV[i * 2 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeConvert(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (args.out != arrayOut[i * 2 + j]) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("0x%x", args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(String.format("0x%x", args.out));
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("0x%x", arrayOut[i * 2 + j]));
+                    if (args.out != arrayOut[i * 2 + j]) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkConvertUint2Ulong2" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkConvertUint3Ulong3() {
+        Allocation inV = createRandomIntegerAllocation(mRS, Element.DataType.UNSIGNED_32, 3, 0xd1e2e9c8fd455255l, false, 32);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_64, 3), INPUTSIZE);
+            script.forEach_testConvertUlong3Uint3Ulong3(inV, out);
+            verifyResultsConvertUint3Ulong3(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertUlong3Uint3Ulong3: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_64, 3), INPUTSIZE);
+            scriptRelaxed.forEach_testConvertUlong3Uint3Ulong3(inV, out);
+            verifyResultsConvertUint3Ulong3(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertUlong3Uint3Ulong3: " + e.toString());
+        }
+    }
+
+    private void verifyResultsConvertUint3Ulong3(Allocation inV, Allocation out, boolean relaxed) {
+        int[] arrayInV = new int[INPUTSIZE * 4];
+        inV.copyTo(arrayInV);
+        long[] arrayOut = new long[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 3 ; j++) {
+                // Extract the inputs.
+                ArgumentsUintUlong args = new ArgumentsUintUlong();
+                args.inV = arrayInV[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeConvert(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (args.out != arrayOut[i * 4 + j]) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("0x%x", args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(String.format("0x%x", args.out));
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("0x%x", arrayOut[i * 4 + j]));
+                    if (args.out != arrayOut[i * 4 + j]) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkConvertUint3Ulong3" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkConvertUint4Ulong4() {
+        Allocation inV = createRandomIntegerAllocation(mRS, Element.DataType.UNSIGNED_32, 4, 0xd1e4b2e3f3607333l, false, 32);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_64, 4), INPUTSIZE);
+            script.forEach_testConvertUlong4Uint4Ulong4(inV, out);
+            verifyResultsConvertUint4Ulong4(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertUlong4Uint4Ulong4: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_64, 4), INPUTSIZE);
+            scriptRelaxed.forEach_testConvertUlong4Uint4Ulong4(inV, out);
+            verifyResultsConvertUint4Ulong4(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testConvertUlong4Uint4Ulong4: " + e.toString());
+        }
+    }
+
+    private void verifyResultsConvertUint4Ulong4(Allocation inV, Allocation out, boolean relaxed) {
+        int[] arrayInV = new int[INPUTSIZE * 4];
+        inV.copyTo(arrayInV);
+        long[] arrayOut = new long[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 4 ; j++) {
+                // Extract the inputs.
+                ArgumentsUintUlong args = new ArgumentsUintUlong();
+                args.inV = arrayInV[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeConvert(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (args.out != arrayOut[i * 4 + j]) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("0x%x", args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(String.format("0x%x", args.out));
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("0x%x", arrayOut[i * 4 + j]));
+                    if (args.out != arrayOut[i * 4 + j]) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkConvertUint4Ulong4" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
     public void testConvert() {
         checkConvertFloat2Float2();
         checkConvertFloat3Float3();
@@ -8848,5 +17902,158 @@
         checkConvertUint2Uint2();
         checkConvertUint3Uint3();
         checkConvertUint4Uint4();
+        checkConvertDouble2Double2();
+        checkConvertDouble3Double3();
+        checkConvertDouble4Double4();
+        checkConvertLong2Double2();
+        checkConvertLong3Double3();
+        checkConvertLong4Double4();
+        checkConvertUlong2Double2();
+        checkConvertUlong3Double3();
+        checkConvertUlong4Double4();
+        checkConvertDouble2Long2();
+        checkConvertDouble3Long3();
+        checkConvertDouble4Long4();
+        checkConvertLong2Long2();
+        checkConvertLong3Long3();
+        checkConvertLong4Long4();
+        checkConvertUlong2Long2();
+        checkConvertUlong3Long3();
+        checkConvertUlong4Long4();
+        checkConvertDouble2Ulong2();
+        checkConvertDouble3Ulong3();
+        checkConvertDouble4Ulong4();
+        checkConvertLong2Ulong2();
+        checkConvertLong3Ulong3();
+        checkConvertLong4Ulong4();
+        checkConvertUlong2Ulong2();
+        checkConvertUlong3Ulong3();
+        checkConvertUlong4Ulong4();
+        checkConvertDouble2Float2();
+        checkConvertDouble3Float3();
+        checkConvertDouble4Float4();
+        checkConvertLong2Float2();
+        checkConvertLong3Float3();
+        checkConvertLong4Float4();
+        checkConvertUlong2Float2();
+        checkConvertUlong3Float3();
+        checkConvertUlong4Float4();
+        checkConvertDouble2Char2();
+        checkConvertDouble3Char3();
+        checkConvertDouble4Char4();
+        checkConvertLong2Char2();
+        checkConvertLong3Char3();
+        checkConvertLong4Char4();
+        checkConvertUlong2Char2();
+        checkConvertUlong3Char3();
+        checkConvertUlong4Char4();
+        checkConvertDouble2Uchar2();
+        checkConvertDouble3Uchar3();
+        checkConvertDouble4Uchar4();
+        checkConvertLong2Uchar2();
+        checkConvertLong3Uchar3();
+        checkConvertLong4Uchar4();
+        checkConvertUlong2Uchar2();
+        checkConvertUlong3Uchar3();
+        checkConvertUlong4Uchar4();
+        checkConvertDouble2Short2();
+        checkConvertDouble3Short3();
+        checkConvertDouble4Short4();
+        checkConvertLong2Short2();
+        checkConvertLong3Short3();
+        checkConvertLong4Short4();
+        checkConvertUlong2Short2();
+        checkConvertUlong3Short3();
+        checkConvertUlong4Short4();
+        checkConvertDouble2Ushort2();
+        checkConvertDouble3Ushort3();
+        checkConvertDouble4Ushort4();
+        checkConvertLong2Ushort2();
+        checkConvertLong3Ushort3();
+        checkConvertLong4Ushort4();
+        checkConvertUlong2Ushort2();
+        checkConvertUlong3Ushort3();
+        checkConvertUlong4Ushort4();
+        checkConvertDouble2Int2();
+        checkConvertDouble3Int3();
+        checkConvertDouble4Int4();
+        checkConvertLong2Int2();
+        checkConvertLong3Int3();
+        checkConvertLong4Int4();
+        checkConvertUlong2Int2();
+        checkConvertUlong3Int3();
+        checkConvertUlong4Int4();
+        checkConvertDouble2Uint2();
+        checkConvertDouble3Uint3();
+        checkConvertDouble4Uint4();
+        checkConvertLong2Uint2();
+        checkConvertLong3Uint3();
+        checkConvertLong4Uint4();
+        checkConvertUlong2Uint2();
+        checkConvertUlong3Uint3();
+        checkConvertUlong4Uint4();
+        checkConvertFloat2Double2();
+        checkConvertFloat3Double3();
+        checkConvertFloat4Double4();
+        checkConvertChar2Double2();
+        checkConvertChar3Double3();
+        checkConvertChar4Double4();
+        checkConvertUchar2Double2();
+        checkConvertUchar3Double3();
+        checkConvertUchar4Double4();
+        checkConvertShort2Double2();
+        checkConvertShort3Double3();
+        checkConvertShort4Double4();
+        checkConvertUshort2Double2();
+        checkConvertUshort3Double3();
+        checkConvertUshort4Double4();
+        checkConvertInt2Double2();
+        checkConvertInt3Double3();
+        checkConvertInt4Double4();
+        checkConvertUint2Double2();
+        checkConvertUint3Double3();
+        checkConvertUint4Double4();
+        checkConvertFloat2Long2();
+        checkConvertFloat3Long3();
+        checkConvertFloat4Long4();
+        checkConvertChar2Long2();
+        checkConvertChar3Long3();
+        checkConvertChar4Long4();
+        checkConvertUchar2Long2();
+        checkConvertUchar3Long3();
+        checkConvertUchar4Long4();
+        checkConvertShort2Long2();
+        checkConvertShort3Long3();
+        checkConvertShort4Long4();
+        checkConvertUshort2Long2();
+        checkConvertUshort3Long3();
+        checkConvertUshort4Long4();
+        checkConvertInt2Long2();
+        checkConvertInt3Long3();
+        checkConvertInt4Long4();
+        checkConvertUint2Long2();
+        checkConvertUint3Long3();
+        checkConvertUint4Long4();
+        checkConvertFloat2Ulong2();
+        checkConvertFloat3Ulong3();
+        checkConvertFloat4Ulong4();
+        checkConvertChar2Ulong2();
+        checkConvertChar3Ulong3();
+        checkConvertChar4Ulong4();
+        checkConvertUchar2Ulong2();
+        checkConvertUchar3Ulong3();
+        checkConvertUchar4Ulong4();
+        checkConvertShort2Ulong2();
+        checkConvertShort3Ulong3();
+        checkConvertShort4Ulong4();
+        checkConvertUshort2Ulong2();
+        checkConvertUshort3Ulong3();
+        checkConvertUshort4Ulong4();
+        checkConvertInt2Ulong2();
+        checkConvertInt3Ulong3();
+        checkConvertInt4Ulong4();
+        checkConvertUint2Ulong2();
+        checkConvertUint3Ulong3();
+        checkConvertUint4Ulong4();
     }
 }
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/TestCopysign.java b/tests/tests/renderscript/src/android/renderscript/cts/TestCopysign.java
index d102fb9..0f792bb 100644
--- a/tests/tests/renderscript/src/android/renderscript/cts/TestCopysign.java
+++ b/tests/tests/renderscript/src/android/renderscript/cts/TestCopysign.java
@@ -77,7 +77,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeCopysign(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 1 + j])) {
                     valid = false;
@@ -146,7 +146,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeCopysign(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 2 + j])) {
                     valid = false;
@@ -215,7 +215,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeCopysign(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 4 + j])) {
                     valid = false;
@@ -284,7 +284,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeCopysign(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 4 + j])) {
                     valid = false;
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/TestCos.java b/tests/tests/renderscript/src/android/renderscript/cts/TestCos.java
index 4bdcc4c..07da8f7 100644
--- a/tests/tests/renderscript/src/android/renderscript/cts/TestCos.java
+++ b/tests/tests/renderscript/src/android/renderscript/cts/TestCos.java
@@ -70,7 +70,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeCos(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 1 + j])) {
                     valid = false;
@@ -129,7 +129,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeCos(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 2 + j])) {
                     valid = false;
@@ -188,7 +188,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeCos(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 4 + j])) {
                     valid = false;
@@ -247,7 +247,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeCos(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 4 + j])) {
                     valid = false;
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/TestCosh.java b/tests/tests/renderscript/src/android/renderscript/cts/TestCosh.java
index f24e65d..6b2388b 100644
--- a/tests/tests/renderscript/src/android/renderscript/cts/TestCosh.java
+++ b/tests/tests/renderscript/src/android/renderscript/cts/TestCosh.java
@@ -70,7 +70,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeCosh(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 1 + j])) {
                     valid = false;
@@ -129,7 +129,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeCosh(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 2 + j])) {
                     valid = false;
@@ -188,7 +188,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeCosh(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 4 + j])) {
                     valid = false;
@@ -247,7 +247,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeCosh(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 4 + j])) {
                     valid = false;
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/TestCospi.java b/tests/tests/renderscript/src/android/renderscript/cts/TestCospi.java
index cc3f427..bec65c5 100644
--- a/tests/tests/renderscript/src/android/renderscript/cts/TestCospi.java
+++ b/tests/tests/renderscript/src/android/renderscript/cts/TestCospi.java
@@ -70,7 +70,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeCospi(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 1 + j])) {
                     valid = false;
@@ -129,7 +129,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeCospi(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 2 + j])) {
                     valid = false;
@@ -188,7 +188,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeCospi(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 4 + j])) {
                     valid = false;
@@ -247,7 +247,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeCospi(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 4 + j])) {
                     valid = false;
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/TestDegrees.java b/tests/tests/renderscript/src/android/renderscript/cts/TestDegrees.java
index f9dd198..ecbbdd4 100644
--- a/tests/tests/renderscript/src/android/renderscript/cts/TestDegrees.java
+++ b/tests/tests/renderscript/src/android/renderscript/cts/TestDegrees.java
@@ -70,7 +70,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeDegrees(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 1 + j])) {
                     valid = false;
@@ -129,7 +129,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeDegrees(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 2 + j])) {
                     valid = false;
@@ -188,7 +188,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeDegrees(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 4 + j])) {
                     valid = false;
@@ -247,7 +247,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeDegrees(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 4 + j])) {
                     valid = false;
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/TestErf.java b/tests/tests/renderscript/src/android/renderscript/cts/TestErf.java
index f846681..06b77fc 100644
--- a/tests/tests/renderscript/src/android/renderscript/cts/TestErf.java
+++ b/tests/tests/renderscript/src/android/renderscript/cts/TestErf.java
@@ -70,7 +70,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeErf(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 1 + j])) {
                     valid = false;
@@ -129,7 +129,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeErf(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 2 + j])) {
                     valid = false;
@@ -188,7 +188,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeErf(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 4 + j])) {
                     valid = false;
@@ -247,7 +247,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeErf(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 4 + j])) {
                     valid = false;
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/TestErfc.java b/tests/tests/renderscript/src/android/renderscript/cts/TestErfc.java
index e7a859a..25332aa 100644
--- a/tests/tests/renderscript/src/android/renderscript/cts/TestErfc.java
+++ b/tests/tests/renderscript/src/android/renderscript/cts/TestErfc.java
@@ -70,7 +70,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeErfc(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 1 + j])) {
                     valid = false;
@@ -129,7 +129,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeErfc(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 2 + j])) {
                     valid = false;
@@ -188,7 +188,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeErfc(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 4 + j])) {
                     valid = false;
@@ -247,7 +247,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeErfc(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 4 + j])) {
                     valid = false;
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/TestExp.java b/tests/tests/renderscript/src/android/renderscript/cts/TestExp.java
index 47a55ef..e46dab1 100644
--- a/tests/tests/renderscript/src/android/renderscript/cts/TestExp.java
+++ b/tests/tests/renderscript/src/android/renderscript/cts/TestExp.java
@@ -70,7 +70,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeExp(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 1 + j])) {
                     valid = false;
@@ -129,7 +129,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeExp(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 2 + j])) {
                     valid = false;
@@ -188,7 +188,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeExp(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 4 + j])) {
                     valid = false;
@@ -247,7 +247,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeExp(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 4 + j])) {
                     valid = false;
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/TestExp10.java b/tests/tests/renderscript/src/android/renderscript/cts/TestExp10.java
index 7784e54..eb6c3b5 100644
--- a/tests/tests/renderscript/src/android/renderscript/cts/TestExp10.java
+++ b/tests/tests/renderscript/src/android/renderscript/cts/TestExp10.java
@@ -70,7 +70,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeExp10(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 1 + j])) {
                     valid = false;
@@ -129,7 +129,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeExp10(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 2 + j])) {
                     valid = false;
@@ -188,7 +188,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeExp10(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 4 + j])) {
                     valid = false;
@@ -247,7 +247,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeExp10(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 4 + j])) {
                     valid = false;
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/TestExp2.java b/tests/tests/renderscript/src/android/renderscript/cts/TestExp2.java
index a47184b..18bd20a 100644
--- a/tests/tests/renderscript/src/android/renderscript/cts/TestExp2.java
+++ b/tests/tests/renderscript/src/android/renderscript/cts/TestExp2.java
@@ -70,7 +70,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeExp2(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 1 + j])) {
                     valid = false;
@@ -129,7 +129,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeExp2(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 2 + j])) {
                     valid = false;
@@ -188,7 +188,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeExp2(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 4 + j])) {
                     valid = false;
@@ -247,7 +247,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeExp2(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 4 + j])) {
                     valid = false;
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/TestExpm1.java b/tests/tests/renderscript/src/android/renderscript/cts/TestExpm1.java
index 4835f7e..b0d288d 100644
--- a/tests/tests/renderscript/src/android/renderscript/cts/TestExpm1.java
+++ b/tests/tests/renderscript/src/android/renderscript/cts/TestExpm1.java
@@ -70,7 +70,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeExpm1(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 1 + j])) {
                     valid = false;
@@ -129,7 +129,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeExpm1(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 2 + j])) {
                     valid = false;
@@ -188,7 +188,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeExpm1(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 4 + j])) {
                     valid = false;
@@ -247,7 +247,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeExpm1(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 4 + j])) {
                     valid = false;
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/TestFabs.java b/tests/tests/renderscript/src/android/renderscript/cts/TestFabs.java
index 12bcb78..e74eaf8 100644
--- a/tests/tests/renderscript/src/android/renderscript/cts/TestFabs.java
+++ b/tests/tests/renderscript/src/android/renderscript/cts/TestFabs.java
@@ -70,7 +70,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeFabs(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 1 + j])) {
                     valid = false;
@@ -129,7 +129,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeFabs(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 2 + j])) {
                     valid = false;
@@ -188,7 +188,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeFabs(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 4 + j])) {
                     valid = false;
@@ -247,7 +247,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeFabs(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 4 + j])) {
                     valid = false;
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/TestFdim.java b/tests/tests/renderscript/src/android/renderscript/cts/TestFdim.java
index db0a46b..13ca2c8 100644
--- a/tests/tests/renderscript/src/android/renderscript/cts/TestFdim.java
+++ b/tests/tests/renderscript/src/android/renderscript/cts/TestFdim.java
@@ -77,7 +77,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeFdim(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 1 + j])) {
                     valid = false;
@@ -146,7 +146,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeFdim(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 2 + j])) {
                     valid = false;
@@ -215,7 +215,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeFdim(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 4 + j])) {
                     valid = false;
@@ -284,7 +284,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeFdim(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 4 + j])) {
                     valid = false;
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/TestFloor.java b/tests/tests/renderscript/src/android/renderscript/cts/TestFloor.java
index 6cfb3c4..07d4409 100644
--- a/tests/tests/renderscript/src/android/renderscript/cts/TestFloor.java
+++ b/tests/tests/renderscript/src/android/renderscript/cts/TestFloor.java
@@ -70,7 +70,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeFloor(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 1 + j])) {
                     valid = false;
@@ -129,7 +129,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeFloor(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 2 + j])) {
                     valid = false;
@@ -188,7 +188,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeFloor(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 4 + j])) {
                     valid = false;
@@ -247,7 +247,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeFloor(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 4 + j])) {
                     valid = false;
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/TestFma.java b/tests/tests/renderscript/src/android/renderscript/cts/TestFma.java
index 705c54d..93ac196 100644
--- a/tests/tests/renderscript/src/android/renderscript/cts/TestFma.java
+++ b/tests/tests/renderscript/src/android/renderscript/cts/TestFma.java
@@ -84,7 +84,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeFma(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 1 + j])) {
                     valid = false;
@@ -163,7 +163,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeFma(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 2 + j])) {
                     valid = false;
@@ -242,7 +242,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeFma(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 4 + j])) {
                     valid = false;
@@ -321,7 +321,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeFma(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 4 + j])) {
                     valid = false;
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/TestFmax.java b/tests/tests/renderscript/src/android/renderscript/cts/TestFmax.java
index ca7706a..83fc48d 100644
--- a/tests/tests/renderscript/src/android/renderscript/cts/TestFmax.java
+++ b/tests/tests/renderscript/src/android/renderscript/cts/TestFmax.java
@@ -77,7 +77,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeFmax(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 1 + j])) {
                     valid = false;
@@ -146,7 +146,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeFmax(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 2 + j])) {
                     valid = false;
@@ -215,7 +215,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeFmax(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 4 + j])) {
                     valid = false;
@@ -284,7 +284,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeFmax(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 4 + j])) {
                     valid = false;
@@ -353,7 +353,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeFmax(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 2 + j])) {
                     valid = false;
@@ -422,7 +422,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeFmax(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 4 + j])) {
                     valid = false;
@@ -491,7 +491,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeFmax(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 4 + j])) {
                     valid = false;
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/TestFmin.java b/tests/tests/renderscript/src/android/renderscript/cts/TestFmin.java
index 39a100c..0c8b7a0 100644
--- a/tests/tests/renderscript/src/android/renderscript/cts/TestFmin.java
+++ b/tests/tests/renderscript/src/android/renderscript/cts/TestFmin.java
@@ -77,7 +77,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeFmin(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 1 + j])) {
                     valid = false;
@@ -146,7 +146,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeFmin(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 2 + j])) {
                     valid = false;
@@ -215,7 +215,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeFmin(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 4 + j])) {
                     valid = false;
@@ -284,7 +284,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeFmin(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 4 + j])) {
                     valid = false;
@@ -353,7 +353,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeFmin(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 2 + j])) {
                     valid = false;
@@ -422,7 +422,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeFmin(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 4 + j])) {
                     valid = false;
@@ -491,7 +491,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeFmin(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 4 + j])) {
                     valid = false;
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/TestFmod.java b/tests/tests/renderscript/src/android/renderscript/cts/TestFmod.java
index 60426e3..a211530 100644
--- a/tests/tests/renderscript/src/android/renderscript/cts/TestFmod.java
+++ b/tests/tests/renderscript/src/android/renderscript/cts/TestFmod.java
@@ -77,7 +77,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeFmod(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 1 + j])) {
                     valid = false;
@@ -146,7 +146,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeFmod(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 2 + j])) {
                     valid = false;
@@ -215,7 +215,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeFmod(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 4 + j])) {
                     valid = false;
@@ -284,7 +284,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeFmod(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 4 + j])) {
                     valid = false;
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/TestFract.java b/tests/tests/renderscript/src/android/renderscript/cts/TestFract.java
index bc1faa8..78f497c 100644
--- a/tests/tests/renderscript/src/android/renderscript/cts/TestFract.java
+++ b/tests/tests/renderscript/src/android/renderscript/cts/TestFract.java
@@ -77,7 +77,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeFract(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.outFloor.couldBe(arrayOutFloor[i * 1 + j])) {
                     valid = false;
@@ -155,7 +155,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeFract(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.outFloor.couldBe(arrayOutFloor[i * 2 + j])) {
                     valid = false;
@@ -233,7 +233,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeFract(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.outFloor.couldBe(arrayOutFloor[i * 4 + j])) {
                     valid = false;
@@ -311,7 +311,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeFract(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.outFloor.couldBe(arrayOutFloor[i * 4 + j])) {
                     valid = false;
@@ -388,7 +388,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeFract(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 1 + j])) {
                     valid = false;
@@ -447,7 +447,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeFract(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 2 + j])) {
                     valid = false;
@@ -506,7 +506,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeFract(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 4 + j])) {
                     valid = false;
@@ -565,7 +565,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeFract(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 4 + j])) {
                     valid = false;
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/TestFrexp.java b/tests/tests/renderscript/src/android/renderscript/cts/TestFrexp.java
index 1437f21..0ed1bd8 100644
--- a/tests/tests/renderscript/src/android/renderscript/cts/TestFrexp.java
+++ b/tests/tests/renderscript/src/android/renderscript/cts/TestFrexp.java
@@ -77,7 +77,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeFrexp(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.outIptr != arrayOutIptr[i * 1 + j]) {
                     valid = false;
@@ -154,7 +154,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeFrexp(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.outIptr != arrayOutIptr[i * 2 + j]) {
                     valid = false;
@@ -231,7 +231,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeFrexp(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.outIptr != arrayOutIptr[i * 4 + j]) {
                     valid = false;
@@ -308,7 +308,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeFrexp(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.outIptr != arrayOutIptr[i * 4 + j]) {
                     valid = false;
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/TestHalfRecip.java b/tests/tests/renderscript/src/android/renderscript/cts/TestHalfRecip.java
index 91cc567..1697e95 100644
--- a/tests/tests/renderscript/src/android/renderscript/cts/TestHalfRecip.java
+++ b/tests/tests/renderscript/src/android/renderscript/cts/TestHalfRecip.java
@@ -70,7 +70,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeHalfRecip(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 1 + j])) {
                     valid = false;
@@ -129,7 +129,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeHalfRecip(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 2 + j])) {
                     valid = false;
@@ -188,7 +188,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeHalfRecip(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 4 + j])) {
                     valid = false;
@@ -247,7 +247,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeHalfRecip(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 4 + j])) {
                     valid = false;
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/TestHalfRsqrt.java b/tests/tests/renderscript/src/android/renderscript/cts/TestHalfRsqrt.java
index 9f339a3..cdd67a3 100644
--- a/tests/tests/renderscript/src/android/renderscript/cts/TestHalfRsqrt.java
+++ b/tests/tests/renderscript/src/android/renderscript/cts/TestHalfRsqrt.java
@@ -70,7 +70,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeHalfRsqrt(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 1 + j])) {
                     valid = false;
@@ -129,7 +129,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeHalfRsqrt(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 2 + j])) {
                     valid = false;
@@ -188,7 +188,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeHalfRsqrt(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 4 + j])) {
                     valid = false;
@@ -247,7 +247,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeHalfRsqrt(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 4 + j])) {
                     valid = false;
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/TestHalfSqrt.java b/tests/tests/renderscript/src/android/renderscript/cts/TestHalfSqrt.java
index 00d2d92..3cac0d2 100644
--- a/tests/tests/renderscript/src/android/renderscript/cts/TestHalfSqrt.java
+++ b/tests/tests/renderscript/src/android/renderscript/cts/TestHalfSqrt.java
@@ -70,7 +70,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeHalfSqrt(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 1 + j])) {
                     valid = false;
@@ -129,7 +129,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeHalfSqrt(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 2 + j])) {
                     valid = false;
@@ -188,7 +188,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeHalfSqrt(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 4 + j])) {
                     valid = false;
@@ -247,7 +247,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeHalfSqrt(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 4 + j])) {
                     valid = false;
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/TestHypot.java b/tests/tests/renderscript/src/android/renderscript/cts/TestHypot.java
index 53d4c55..040418d 100644
--- a/tests/tests/renderscript/src/android/renderscript/cts/TestHypot.java
+++ b/tests/tests/renderscript/src/android/renderscript/cts/TestHypot.java
@@ -77,7 +77,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeHypot(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 1 + j])) {
                     valid = false;
@@ -146,7 +146,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeHypot(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 2 + j])) {
                     valid = false;
@@ -215,7 +215,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeHypot(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 4 + j])) {
                     valid = false;
@@ -284,7 +284,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeHypot(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 4 + j])) {
                     valid = false;
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/TestIlogb.java b/tests/tests/renderscript/src/android/renderscript/cts/TestIlogb.java
index c15e9f2..d86d749 100644
--- a/tests/tests/renderscript/src/android/renderscript/cts/TestIlogb.java
+++ b/tests/tests/renderscript/src/android/renderscript/cts/TestIlogb.java
@@ -67,29 +67,22 @@
                 // Extract the inputs.
                 ArgumentsFloatInt args = new ArgumentsFloatInt();
                 args.in = arrayIn[i];
-                // Figure out what the outputs should have been.
+                // Extract the outputs.
+                args.out = arrayOut[i * 1 + j];
+                // Ask the CoreMathVerifier to validate.
                 Floaty.setRelaxed(relaxed);
-                CoreMathVerifier.computeIlogb(args);
-                // Figure out what the outputs should have been.
-                boolean valid = true;
-                if (args.out != arrayOut[i * 1 + j]) {
-                    valid = false;
-                }
+                String errorMessage = CoreMathVerifier.verifyIlogb(args, relaxed);
+                boolean valid = errorMessage == null;
                 if (!valid) {
                     StringBuilder message = new StringBuilder();
                     message.append("Input in: ");
                     message.append(String.format("%14.8g %8x %15a",
                             args.in, Float.floatToRawIntBits(args.in), args.in));
                     message.append("\n");
-                    message.append("Expected output out: ");
+                    message.append("Output out: ");
                     message.append(String.format("%d", args.out));
                     message.append("\n");
-                    message.append("Actual   output out: ");
-                    message.append(String.format("%d", arrayOut[i * 1 + j]));
-                    if (args.out != arrayOut[i * 1 + j]) {
-                        message.append(" FAIL");
-                    }
-                    message.append("\n");
+                    message.append(errorMessage);
                     assertTrue("Incorrect output for checkIlogbFloatInt" +
                             (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
                 }
@@ -125,29 +118,22 @@
                 // Extract the inputs.
                 ArgumentsFloatInt args = new ArgumentsFloatInt();
                 args.in = arrayIn[i * 2 + j];
-                // Figure out what the outputs should have been.
+                // Extract the outputs.
+                args.out = arrayOut[i * 2 + j];
+                // Ask the CoreMathVerifier to validate.
                 Floaty.setRelaxed(relaxed);
-                CoreMathVerifier.computeIlogb(args);
-                // Figure out what the outputs should have been.
-                boolean valid = true;
-                if (args.out != arrayOut[i * 2 + j]) {
-                    valid = false;
-                }
+                String errorMessage = CoreMathVerifier.verifyIlogb(args, relaxed);
+                boolean valid = errorMessage == null;
                 if (!valid) {
                     StringBuilder message = new StringBuilder();
                     message.append("Input in: ");
                     message.append(String.format("%14.8g %8x %15a",
                             args.in, Float.floatToRawIntBits(args.in), args.in));
                     message.append("\n");
-                    message.append("Expected output out: ");
+                    message.append("Output out: ");
                     message.append(String.format("%d", args.out));
                     message.append("\n");
-                    message.append("Actual   output out: ");
-                    message.append(String.format("%d", arrayOut[i * 2 + j]));
-                    if (args.out != arrayOut[i * 2 + j]) {
-                        message.append(" FAIL");
-                    }
-                    message.append("\n");
+                    message.append(errorMessage);
                     assertTrue("Incorrect output for checkIlogbFloat2Int2" +
                             (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
                 }
@@ -183,29 +169,22 @@
                 // Extract the inputs.
                 ArgumentsFloatInt args = new ArgumentsFloatInt();
                 args.in = arrayIn[i * 4 + j];
-                // Figure out what the outputs should have been.
+                // Extract the outputs.
+                args.out = arrayOut[i * 4 + j];
+                // Ask the CoreMathVerifier to validate.
                 Floaty.setRelaxed(relaxed);
-                CoreMathVerifier.computeIlogb(args);
-                // Figure out what the outputs should have been.
-                boolean valid = true;
-                if (args.out != arrayOut[i * 4 + j]) {
-                    valid = false;
-                }
+                String errorMessage = CoreMathVerifier.verifyIlogb(args, relaxed);
+                boolean valid = errorMessage == null;
                 if (!valid) {
                     StringBuilder message = new StringBuilder();
                     message.append("Input in: ");
                     message.append(String.format("%14.8g %8x %15a",
                             args.in, Float.floatToRawIntBits(args.in), args.in));
                     message.append("\n");
-                    message.append("Expected output out: ");
+                    message.append("Output out: ");
                     message.append(String.format("%d", args.out));
                     message.append("\n");
-                    message.append("Actual   output out: ");
-                    message.append(String.format("%d", arrayOut[i * 4 + j]));
-                    if (args.out != arrayOut[i * 4 + j]) {
-                        message.append(" FAIL");
-                    }
-                    message.append("\n");
+                    message.append(errorMessage);
                     assertTrue("Incorrect output for checkIlogbFloat3Int3" +
                             (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
                 }
@@ -241,29 +220,22 @@
                 // Extract the inputs.
                 ArgumentsFloatInt args = new ArgumentsFloatInt();
                 args.in = arrayIn[i * 4 + j];
-                // Figure out what the outputs should have been.
+                // Extract the outputs.
+                args.out = arrayOut[i * 4 + j];
+                // Ask the CoreMathVerifier to validate.
                 Floaty.setRelaxed(relaxed);
-                CoreMathVerifier.computeIlogb(args);
-                // Figure out what the outputs should have been.
-                boolean valid = true;
-                if (args.out != arrayOut[i * 4 + j]) {
-                    valid = false;
-                }
+                String errorMessage = CoreMathVerifier.verifyIlogb(args, relaxed);
+                boolean valid = errorMessage == null;
                 if (!valid) {
                     StringBuilder message = new StringBuilder();
                     message.append("Input in: ");
                     message.append(String.format("%14.8g %8x %15a",
                             args.in, Float.floatToRawIntBits(args.in), args.in));
                     message.append("\n");
-                    message.append("Expected output out: ");
+                    message.append("Output out: ");
                     message.append(String.format("%d", args.out));
                     message.append("\n");
-                    message.append("Actual   output out: ");
-                    message.append(String.format("%d", arrayOut[i * 4 + j]));
-                    if (args.out != arrayOut[i * 4 + j]) {
-                        message.append(" FAIL");
-                    }
-                    message.append("\n");
+                    message.append(errorMessage);
                     assertTrue("Incorrect output for checkIlogbFloat4Int4" +
                             (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
                 }
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/TestLdexp.java b/tests/tests/renderscript/src/android/renderscript/cts/TestLdexp.java
index e565113..7c54ab1 100644
--- a/tests/tests/renderscript/src/android/renderscript/cts/TestLdexp.java
+++ b/tests/tests/renderscript/src/android/renderscript/cts/TestLdexp.java
@@ -77,7 +77,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeLdexp(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 1 + j])) {
                     valid = false;
@@ -145,7 +145,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeLdexp(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 2 + j])) {
                     valid = false;
@@ -213,7 +213,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeLdexp(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 4 + j])) {
                     valid = false;
@@ -281,7 +281,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeLdexp(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 4 + j])) {
                     valid = false;
@@ -349,7 +349,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeLdexp(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 2 + j])) {
                     valid = false;
@@ -417,7 +417,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeLdexp(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 4 + j])) {
                     valid = false;
@@ -485,7 +485,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeLdexp(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 4 + j])) {
                     valid = false;
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/TestLgamma.java b/tests/tests/renderscript/src/android/renderscript/cts/TestLgamma.java
index d3c44e4..a6581f1 100644
--- a/tests/tests/renderscript/src/android/renderscript/cts/TestLgamma.java
+++ b/tests/tests/renderscript/src/android/renderscript/cts/TestLgamma.java
@@ -70,7 +70,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeLgamma(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 1 + j])) {
                     valid = false;
@@ -129,7 +129,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeLgamma(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 2 + j])) {
                     valid = false;
@@ -188,7 +188,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeLgamma(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 4 + j])) {
                     valid = false;
@@ -247,7 +247,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeLgamma(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 4 + j])) {
                     valid = false;
@@ -318,7 +318,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeLgamma(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.outY != arrayOutY[i * 1 + j]) {
                     valid = false;
@@ -395,7 +395,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeLgamma(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.outY != arrayOutY[i * 2 + j]) {
                     valid = false;
@@ -472,7 +472,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeLgamma(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.outY != arrayOutY[i * 4 + j]) {
                     valid = false;
@@ -549,7 +549,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeLgamma(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.outY != arrayOutY[i * 4 + j]) {
                     valid = false;
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/TestLog.java b/tests/tests/renderscript/src/android/renderscript/cts/TestLog.java
index d9dd9fc..9728636 100644
--- a/tests/tests/renderscript/src/android/renderscript/cts/TestLog.java
+++ b/tests/tests/renderscript/src/android/renderscript/cts/TestLog.java
@@ -70,7 +70,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeLog(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 1 + j])) {
                     valid = false;
@@ -129,7 +129,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeLog(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 2 + j])) {
                     valid = false;
@@ -188,7 +188,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeLog(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 4 + j])) {
                     valid = false;
@@ -247,7 +247,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeLog(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 4 + j])) {
                     valid = false;
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/TestLog10.java b/tests/tests/renderscript/src/android/renderscript/cts/TestLog10.java
index 9711f6b..847cea3 100644
--- a/tests/tests/renderscript/src/android/renderscript/cts/TestLog10.java
+++ b/tests/tests/renderscript/src/android/renderscript/cts/TestLog10.java
@@ -70,7 +70,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeLog10(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 1 + j])) {
                     valid = false;
@@ -129,7 +129,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeLog10(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 2 + j])) {
                     valid = false;
@@ -188,7 +188,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeLog10(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 4 + j])) {
                     valid = false;
@@ -247,7 +247,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeLog10(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 4 + j])) {
                     valid = false;
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/TestLog1p.java b/tests/tests/renderscript/src/android/renderscript/cts/TestLog1p.java
index 4d455fa..fadc9d1 100644
--- a/tests/tests/renderscript/src/android/renderscript/cts/TestLog1p.java
+++ b/tests/tests/renderscript/src/android/renderscript/cts/TestLog1p.java
@@ -70,7 +70,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeLog1p(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 1 + j])) {
                     valid = false;
@@ -129,7 +129,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeLog1p(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 2 + j])) {
                     valid = false;
@@ -188,7 +188,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeLog1p(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 4 + j])) {
                     valid = false;
@@ -247,7 +247,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeLog1p(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 4 + j])) {
                     valid = false;
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/TestLog2.java b/tests/tests/renderscript/src/android/renderscript/cts/TestLog2.java
index bf7d420..61ea10f 100644
--- a/tests/tests/renderscript/src/android/renderscript/cts/TestLog2.java
+++ b/tests/tests/renderscript/src/android/renderscript/cts/TestLog2.java
@@ -70,7 +70,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeLog2(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 1 + j])) {
                     valid = false;
@@ -129,7 +129,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeLog2(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 2 + j])) {
                     valid = false;
@@ -188,7 +188,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeLog2(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 4 + j])) {
                     valid = false;
@@ -247,7 +247,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeLog2(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 4 + j])) {
                     valid = false;
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/TestLogb.java b/tests/tests/renderscript/src/android/renderscript/cts/TestLogb.java
index 4ff2eb5..41e6f21 100644
--- a/tests/tests/renderscript/src/android/renderscript/cts/TestLogb.java
+++ b/tests/tests/renderscript/src/android/renderscript/cts/TestLogb.java
@@ -70,7 +70,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeLogb(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 1 + j])) {
                     valid = false;
@@ -129,7 +129,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeLogb(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 2 + j])) {
                     valid = false;
@@ -188,7 +188,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeLogb(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 4 + j])) {
                     valid = false;
@@ -247,7 +247,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeLogb(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 4 + j])) {
                     valid = false;
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/TestMad.java b/tests/tests/renderscript/src/android/renderscript/cts/TestMad.java
index 1d0e1ac..11ef35a 100644
--- a/tests/tests/renderscript/src/android/renderscript/cts/TestMad.java
+++ b/tests/tests/renderscript/src/android/renderscript/cts/TestMad.java
@@ -84,7 +84,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeMad(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 1 + j])) {
                     valid = false;
@@ -163,7 +163,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeMad(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 2 + j])) {
                     valid = false;
@@ -242,7 +242,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeMad(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 4 + j])) {
                     valid = false;
@@ -321,7 +321,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeMad(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 4 + j])) {
                     valid = false;
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/TestMax.java b/tests/tests/renderscript/src/android/renderscript/cts/TestMax.java
index 7df768d..3ef9aeb 100644
--- a/tests/tests/renderscript/src/android/renderscript/cts/TestMax.java
+++ b/tests/tests/renderscript/src/android/renderscript/cts/TestMax.java
@@ -77,7 +77,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeMax(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 1 + j])) {
                     valid = false;
@@ -146,7 +146,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeMax(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 2 + j])) {
                     valid = false;
@@ -215,7 +215,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeMax(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 4 + j])) {
                     valid = false;
@@ -284,7 +284,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeMax(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 4 + j])) {
                     valid = false;
@@ -359,7 +359,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeMax(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 1 + j]) {
                     valid = false;
@@ -388,6 +388,204 @@
         }
     }
 
+    private void checkMaxChar2Char2Char2() {
+        Allocation inV1 = createRandomAllocation(mRS, Element.DataType.SIGNED_8, 2, 0x12084b25952bc64l, false);
+        Allocation inV2 = createRandomAllocation(mRS, Element.DataType.SIGNED_8, 2, 0x12084b25952bc65l, false);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_8, 2), INPUTSIZE);
+            script.set_gAllocInV2(inV2);
+            script.forEach_testMaxChar2Char2Char2(inV1, out);
+            verifyResultsMaxChar2Char2Char2(inV1, inV2, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testMaxChar2Char2Char2: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_8, 2), INPUTSIZE);
+            scriptRelaxed.set_gAllocInV2(inV2);
+            scriptRelaxed.forEach_testMaxChar2Char2Char2(inV1, out);
+            verifyResultsMaxChar2Char2Char2(inV1, inV2, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testMaxChar2Char2Char2: " + e.toString());
+        }
+    }
+
+    private void verifyResultsMaxChar2Char2Char2(Allocation inV1, Allocation inV2, Allocation out, boolean relaxed) {
+        byte[] arrayInV1 = new byte[INPUTSIZE * 2];
+        inV1.copyTo(arrayInV1);
+        byte[] arrayInV2 = new byte[INPUTSIZE * 2];
+        inV2.copyTo(arrayInV2);
+        byte[] arrayOut = new byte[INPUTSIZE * 2];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 2 ; j++) {
+                // Extract the inputs.
+                ArgumentsCharCharChar args = new ArgumentsCharCharChar();
+                args.inV1 = arrayInV1[i * 2 + j];
+                args.inV2 = arrayInV2[i * 2 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeMax(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (args.out != arrayOut[i * 2 + j]) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV1: ");
+                    message.append(String.format("%d", args.inV1));
+                    message.append("\n");
+                    message.append("Input inV2: ");
+                    message.append(String.format("%d", args.inV2));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(String.format("%d", args.out));
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%d", arrayOut[i * 2 + j]));
+                    if (args.out != arrayOut[i * 2 + j]) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkMaxChar2Char2Char2" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkMaxChar3Char3Char3() {
+        Allocation inV1 = createRandomAllocation(mRS, Element.DataType.SIGNED_8, 3, 0x567200e53e0a8f29l, false);
+        Allocation inV2 = createRandomAllocation(mRS, Element.DataType.SIGNED_8, 3, 0x567200e53e0a8f2al, false);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_8, 3), INPUTSIZE);
+            script.set_gAllocInV2(inV2);
+            script.forEach_testMaxChar3Char3Char3(inV1, out);
+            verifyResultsMaxChar3Char3Char3(inV1, inV2, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testMaxChar3Char3Char3: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_8, 3), INPUTSIZE);
+            scriptRelaxed.set_gAllocInV2(inV2);
+            scriptRelaxed.forEach_testMaxChar3Char3Char3(inV1, out);
+            verifyResultsMaxChar3Char3Char3(inV1, inV2, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testMaxChar3Char3Char3: " + e.toString());
+        }
+    }
+
+    private void verifyResultsMaxChar3Char3Char3(Allocation inV1, Allocation inV2, Allocation out, boolean relaxed) {
+        byte[] arrayInV1 = new byte[INPUTSIZE * 4];
+        inV1.copyTo(arrayInV1);
+        byte[] arrayInV2 = new byte[INPUTSIZE * 4];
+        inV2.copyTo(arrayInV2);
+        byte[] arrayOut = new byte[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 3 ; j++) {
+                // Extract the inputs.
+                ArgumentsCharCharChar args = new ArgumentsCharCharChar();
+                args.inV1 = arrayInV1[i * 4 + j];
+                args.inV2 = arrayInV2[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeMax(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (args.out != arrayOut[i * 4 + j]) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV1: ");
+                    message.append(String.format("%d", args.inV1));
+                    message.append("\n");
+                    message.append("Input inV2: ");
+                    message.append(String.format("%d", args.inV2));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(String.format("%d", args.out));
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%d", arrayOut[i * 4 + j]));
+                    if (args.out != arrayOut[i * 4 + j]) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkMaxChar3Char3Char3" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkMaxChar4Char4Char4() {
+        Allocation inV1 = createRandomAllocation(mRS, Element.DataType.SIGNED_8, 4, 0xabc37d1822c261eel, false);
+        Allocation inV2 = createRandomAllocation(mRS, Element.DataType.SIGNED_8, 4, 0xabc37d1822c261efl, false);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_8, 4), INPUTSIZE);
+            script.set_gAllocInV2(inV2);
+            script.forEach_testMaxChar4Char4Char4(inV1, out);
+            verifyResultsMaxChar4Char4Char4(inV1, inV2, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testMaxChar4Char4Char4: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_8, 4), INPUTSIZE);
+            scriptRelaxed.set_gAllocInV2(inV2);
+            scriptRelaxed.forEach_testMaxChar4Char4Char4(inV1, out);
+            verifyResultsMaxChar4Char4Char4(inV1, inV2, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testMaxChar4Char4Char4: " + e.toString());
+        }
+    }
+
+    private void verifyResultsMaxChar4Char4Char4(Allocation inV1, Allocation inV2, Allocation out, boolean relaxed) {
+        byte[] arrayInV1 = new byte[INPUTSIZE * 4];
+        inV1.copyTo(arrayInV1);
+        byte[] arrayInV2 = new byte[INPUTSIZE * 4];
+        inV2.copyTo(arrayInV2);
+        byte[] arrayOut = new byte[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 4 ; j++) {
+                // Extract the inputs.
+                ArgumentsCharCharChar args = new ArgumentsCharCharChar();
+                args.inV1 = arrayInV1[i * 4 + j];
+                args.inV2 = arrayInV2[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeMax(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (args.out != arrayOut[i * 4 + j]) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV1: ");
+                    message.append(String.format("%d", args.inV1));
+                    message.append("\n");
+                    message.append("Input inV2: ");
+                    message.append(String.format("%d", args.inV2));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(String.format("%d", args.out));
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%d", arrayOut[i * 4 + j]));
+                    if (args.out != arrayOut[i * 4 + j]) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkMaxChar4Char4Char4" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
     public class ArgumentsUcharUcharUchar {
         public byte inV1;
         public byte inV2;
@@ -431,7 +629,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeMax(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 1 + j]) {
                     valid = false;
@@ -460,6 +658,204 @@
         }
     }
 
+    private void checkMaxUchar2Uchar2Uchar2() {
+        Allocation inV1 = createRandomAllocation(mRS, Element.DataType.UNSIGNED_8, 2, 0x75eda605e43f8b81l, false);
+        Allocation inV2 = createRandomAllocation(mRS, Element.DataType.UNSIGNED_8, 2, 0x75eda605e43f8b82l, false);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_8, 2), INPUTSIZE);
+            script.set_gAllocInV2(inV2);
+            script.forEach_testMaxUchar2Uchar2Uchar2(inV1, out);
+            verifyResultsMaxUchar2Uchar2Uchar2(inV1, inV2, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testMaxUchar2Uchar2Uchar2: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_8, 2), INPUTSIZE);
+            scriptRelaxed.set_gAllocInV2(inV2);
+            scriptRelaxed.forEach_testMaxUchar2Uchar2Uchar2(inV1, out);
+            verifyResultsMaxUchar2Uchar2Uchar2(inV1, inV2, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testMaxUchar2Uchar2Uchar2: " + e.toString());
+        }
+    }
+
+    private void verifyResultsMaxUchar2Uchar2Uchar2(Allocation inV1, Allocation inV2, Allocation out, boolean relaxed) {
+        byte[] arrayInV1 = new byte[INPUTSIZE * 2];
+        inV1.copyTo(arrayInV1);
+        byte[] arrayInV2 = new byte[INPUTSIZE * 2];
+        inV2.copyTo(arrayInV2);
+        byte[] arrayOut = new byte[INPUTSIZE * 2];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 2 ; j++) {
+                // Extract the inputs.
+                ArgumentsUcharUcharUchar args = new ArgumentsUcharUcharUchar();
+                args.inV1 = arrayInV1[i * 2 + j];
+                args.inV2 = arrayInV2[i * 2 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeMax(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (args.out != arrayOut[i * 2 + j]) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV1: ");
+                    message.append(String.format("0x%x", args.inV1));
+                    message.append("\n");
+                    message.append("Input inV2: ");
+                    message.append(String.format("0x%x", args.inV2));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(String.format("0x%x", args.out));
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("0x%x", arrayOut[i * 2 + j]));
+                    if (args.out != arrayOut[i * 2 + j]) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkMaxUchar2Uchar2Uchar2" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkMaxUchar3Uchar3Uchar3() {
+        Allocation inV1 = createRandomAllocation(mRS, Element.DataType.UNSIGNED_8, 3, 0xa2def5663489d18cl, false);
+        Allocation inV2 = createRandomAllocation(mRS, Element.DataType.UNSIGNED_8, 3, 0xa2def5663489d18dl, false);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_8, 3), INPUTSIZE);
+            script.set_gAllocInV2(inV2);
+            script.forEach_testMaxUchar3Uchar3Uchar3(inV1, out);
+            verifyResultsMaxUchar3Uchar3Uchar3(inV1, inV2, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testMaxUchar3Uchar3Uchar3: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_8, 3), INPUTSIZE);
+            scriptRelaxed.set_gAllocInV2(inV2);
+            scriptRelaxed.forEach_testMaxUchar3Uchar3Uchar3(inV1, out);
+            verifyResultsMaxUchar3Uchar3Uchar3(inV1, inV2, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testMaxUchar3Uchar3Uchar3: " + e.toString());
+        }
+    }
+
+    private void verifyResultsMaxUchar3Uchar3Uchar3(Allocation inV1, Allocation inV2, Allocation out, boolean relaxed) {
+        byte[] arrayInV1 = new byte[INPUTSIZE * 4];
+        inV1.copyTo(arrayInV1);
+        byte[] arrayInV2 = new byte[INPUTSIZE * 4];
+        inV2.copyTo(arrayInV2);
+        byte[] arrayOut = new byte[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 3 ; j++) {
+                // Extract the inputs.
+                ArgumentsUcharUcharUchar args = new ArgumentsUcharUcharUchar();
+                args.inV1 = arrayInV1[i * 4 + j];
+                args.inV2 = arrayInV2[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeMax(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (args.out != arrayOut[i * 4 + j]) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV1: ");
+                    message.append(String.format("0x%x", args.inV1));
+                    message.append("\n");
+                    message.append("Input inV2: ");
+                    message.append(String.format("0x%x", args.inV2));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(String.format("0x%x", args.out));
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("0x%x", arrayOut[i * 4 + j]));
+                    if (args.out != arrayOut[i * 4 + j]) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkMaxUchar3Uchar3Uchar3" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkMaxUchar4Uchar4Uchar4() {
+        Allocation inV1 = createRandomAllocation(mRS, Element.DataType.UNSIGNED_8, 4, 0xcfd044c684d41797l, false);
+        Allocation inV2 = createRandomAllocation(mRS, Element.DataType.UNSIGNED_8, 4, 0xcfd044c684d41798l, false);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_8, 4), INPUTSIZE);
+            script.set_gAllocInV2(inV2);
+            script.forEach_testMaxUchar4Uchar4Uchar4(inV1, out);
+            verifyResultsMaxUchar4Uchar4Uchar4(inV1, inV2, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testMaxUchar4Uchar4Uchar4: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_8, 4), INPUTSIZE);
+            scriptRelaxed.set_gAllocInV2(inV2);
+            scriptRelaxed.forEach_testMaxUchar4Uchar4Uchar4(inV1, out);
+            verifyResultsMaxUchar4Uchar4Uchar4(inV1, inV2, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testMaxUchar4Uchar4Uchar4: " + e.toString());
+        }
+    }
+
+    private void verifyResultsMaxUchar4Uchar4Uchar4(Allocation inV1, Allocation inV2, Allocation out, boolean relaxed) {
+        byte[] arrayInV1 = new byte[INPUTSIZE * 4];
+        inV1.copyTo(arrayInV1);
+        byte[] arrayInV2 = new byte[INPUTSIZE * 4];
+        inV2.copyTo(arrayInV2);
+        byte[] arrayOut = new byte[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 4 ; j++) {
+                // Extract the inputs.
+                ArgumentsUcharUcharUchar args = new ArgumentsUcharUcharUchar();
+                args.inV1 = arrayInV1[i * 4 + j];
+                args.inV2 = arrayInV2[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeMax(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (args.out != arrayOut[i * 4 + j]) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV1: ");
+                    message.append(String.format("0x%x", args.inV1));
+                    message.append("\n");
+                    message.append("Input inV2: ");
+                    message.append(String.format("0x%x", args.inV2));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(String.format("0x%x", args.out));
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("0x%x", arrayOut[i * 4 + j]));
+                    if (args.out != arrayOut[i * 4 + j]) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkMaxUchar4Uchar4Uchar4" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
     public class ArgumentsShortShortShort {
         public short inV1;
         public short inV2;
@@ -503,7 +899,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeMax(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 1 + j]) {
                     valid = false;
@@ -532,6 +928,204 @@
         }
     }
 
+    private void checkMaxShort2Short2Short2() {
+        Allocation inV1 = createRandomAllocation(mRS, Element.DataType.SIGNED_16, 2, 0x3d46ae0799c33c02l, false);
+        Allocation inV2 = createRandomAllocation(mRS, Element.DataType.SIGNED_16, 2, 0x3d46ae0799c33c03l, false);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_16, 2), INPUTSIZE);
+            script.set_gAllocInV2(inV2);
+            script.forEach_testMaxShort2Short2Short2(inV1, out);
+            verifyResultsMaxShort2Short2Short2(inV1, inV2, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testMaxShort2Short2Short2: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_16, 2), INPUTSIZE);
+            scriptRelaxed.set_gAllocInV2(inV2);
+            scriptRelaxed.forEach_testMaxShort2Short2Short2(inV1, out);
+            verifyResultsMaxShort2Short2Short2(inV1, inV2, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testMaxShort2Short2Short2: " + e.toString());
+        }
+    }
+
+    private void verifyResultsMaxShort2Short2Short2(Allocation inV1, Allocation inV2, Allocation out, boolean relaxed) {
+        short[] arrayInV1 = new short[INPUTSIZE * 2];
+        inV1.copyTo(arrayInV1);
+        short[] arrayInV2 = new short[INPUTSIZE * 2];
+        inV2.copyTo(arrayInV2);
+        short[] arrayOut = new short[INPUTSIZE * 2];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 2 ; j++) {
+                // Extract the inputs.
+                ArgumentsShortShortShort args = new ArgumentsShortShortShort();
+                args.inV1 = arrayInV1[i * 2 + j];
+                args.inV2 = arrayInV2[i * 2 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeMax(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (args.out != arrayOut[i * 2 + j]) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV1: ");
+                    message.append(String.format("%d", args.inV1));
+                    message.append("\n");
+                    message.append("Input inV2: ");
+                    message.append(String.format("%d", args.inV2));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(String.format("%d", args.out));
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%d", arrayOut[i * 2 + j]));
+                    if (args.out != arrayOut[i * 2 + j]) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkMaxShort2Short2Short2" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkMaxShort3Short3Short3() {
+        Allocation inV1 = createRandomAllocation(mRS, Element.DataType.SIGNED_16, 3, 0x6a37fd67ea0d820dl, false);
+        Allocation inV2 = createRandomAllocation(mRS, Element.DataType.SIGNED_16, 3, 0x6a37fd67ea0d820el, false);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_16, 3), INPUTSIZE);
+            script.set_gAllocInV2(inV2);
+            script.forEach_testMaxShort3Short3Short3(inV1, out);
+            verifyResultsMaxShort3Short3Short3(inV1, inV2, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testMaxShort3Short3Short3: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_16, 3), INPUTSIZE);
+            scriptRelaxed.set_gAllocInV2(inV2);
+            scriptRelaxed.forEach_testMaxShort3Short3Short3(inV1, out);
+            verifyResultsMaxShort3Short3Short3(inV1, inV2, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testMaxShort3Short3Short3: " + e.toString());
+        }
+    }
+
+    private void verifyResultsMaxShort3Short3Short3(Allocation inV1, Allocation inV2, Allocation out, boolean relaxed) {
+        short[] arrayInV1 = new short[INPUTSIZE * 4];
+        inV1.copyTo(arrayInV1);
+        short[] arrayInV2 = new short[INPUTSIZE * 4];
+        inV2.copyTo(arrayInV2);
+        short[] arrayOut = new short[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 3 ; j++) {
+                // Extract the inputs.
+                ArgumentsShortShortShort args = new ArgumentsShortShortShort();
+                args.inV1 = arrayInV1[i * 4 + j];
+                args.inV2 = arrayInV2[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeMax(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (args.out != arrayOut[i * 4 + j]) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV1: ");
+                    message.append(String.format("%d", args.inV1));
+                    message.append("\n");
+                    message.append("Input inV2: ");
+                    message.append(String.format("%d", args.inV2));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(String.format("%d", args.out));
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%d", arrayOut[i * 4 + j]));
+                    if (args.out != arrayOut[i * 4 + j]) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkMaxShort3Short3Short3" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkMaxShort4Short4Short4() {
+        Allocation inV1 = createRandomAllocation(mRS, Element.DataType.SIGNED_16, 4, 0x97294cc83a57c818l, false);
+        Allocation inV2 = createRandomAllocation(mRS, Element.DataType.SIGNED_16, 4, 0x97294cc83a57c819l, false);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_16, 4), INPUTSIZE);
+            script.set_gAllocInV2(inV2);
+            script.forEach_testMaxShort4Short4Short4(inV1, out);
+            verifyResultsMaxShort4Short4Short4(inV1, inV2, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testMaxShort4Short4Short4: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_16, 4), INPUTSIZE);
+            scriptRelaxed.set_gAllocInV2(inV2);
+            scriptRelaxed.forEach_testMaxShort4Short4Short4(inV1, out);
+            verifyResultsMaxShort4Short4Short4(inV1, inV2, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testMaxShort4Short4Short4: " + e.toString());
+        }
+    }
+
+    private void verifyResultsMaxShort4Short4Short4(Allocation inV1, Allocation inV2, Allocation out, boolean relaxed) {
+        short[] arrayInV1 = new short[INPUTSIZE * 4];
+        inV1.copyTo(arrayInV1);
+        short[] arrayInV2 = new short[INPUTSIZE * 4];
+        inV2.copyTo(arrayInV2);
+        short[] arrayOut = new short[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 4 ; j++) {
+                // Extract the inputs.
+                ArgumentsShortShortShort args = new ArgumentsShortShortShort();
+                args.inV1 = arrayInV1[i * 4 + j];
+                args.inV2 = arrayInV2[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeMax(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (args.out != arrayOut[i * 4 + j]) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV1: ");
+                    message.append(String.format("%d", args.inV1));
+                    message.append("\n");
+                    message.append("Input inV2: ");
+                    message.append(String.format("%d", args.inV2));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(String.format("%d", args.out));
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%d", arrayOut[i * 4 + j]));
+                    if (args.out != arrayOut[i * 4 + j]) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkMaxShort4Short4Short4" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
     public class ArgumentsUshortUshortUshort {
         public short inV1;
         public short inV2;
@@ -575,7 +1169,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeMax(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 1 + j]) {
                     valid = false;
@@ -604,6 +1198,204 @@
         }
     }
 
+    private void checkMaxUshort2Ushort2Ushort2() {
+        Allocation inV1 = createRandomAllocation(mRS, Element.DataType.UNSIGNED_16, 2, 0xf42196a588de51bfl, false);
+        Allocation inV2 = createRandomAllocation(mRS, Element.DataType.UNSIGNED_16, 2, 0xf42196a588de51c0l, false);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_16, 2), INPUTSIZE);
+            script.set_gAllocInV2(inV2);
+            script.forEach_testMaxUshort2Ushort2Ushort2(inV1, out);
+            verifyResultsMaxUshort2Ushort2Ushort2(inV1, inV2, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testMaxUshort2Ushort2Ushort2: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_16, 2), INPUTSIZE);
+            scriptRelaxed.set_gAllocInV2(inV2);
+            scriptRelaxed.forEach_testMaxUshort2Ushort2Ushort2(inV1, out);
+            verifyResultsMaxUshort2Ushort2Ushort2(inV1, inV2, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testMaxUshort2Ushort2Ushort2: " + e.toString());
+        }
+    }
+
+    private void verifyResultsMaxUshort2Ushort2Ushort2(Allocation inV1, Allocation inV2, Allocation out, boolean relaxed) {
+        short[] arrayInV1 = new short[INPUTSIZE * 2];
+        inV1.copyTo(arrayInV1);
+        short[] arrayInV2 = new short[INPUTSIZE * 2];
+        inV2.copyTo(arrayInV2);
+        short[] arrayOut = new short[INPUTSIZE * 2];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 2 ; j++) {
+                // Extract the inputs.
+                ArgumentsUshortUshortUshort args = new ArgumentsUshortUshortUshort();
+                args.inV1 = arrayInV1[i * 2 + j];
+                args.inV2 = arrayInV2[i * 2 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeMax(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (args.out != arrayOut[i * 2 + j]) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV1: ");
+                    message.append(String.format("0x%x", args.inV1));
+                    message.append("\n");
+                    message.append("Input inV2: ");
+                    message.append(String.format("0x%x", args.inV2));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(String.format("0x%x", args.out));
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("0x%x", arrayOut[i * 2 + j]));
+                    if (args.out != arrayOut[i * 2 + j]) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkMaxUshort2Ushort2Ushort2" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkMaxUshort3Ushort3Ushort3() {
+        Allocation inV1 = createRandomAllocation(mRS, Element.DataType.UNSIGNED_16, 3, 0x71604884c752e61cl, false);
+        Allocation inV2 = createRandomAllocation(mRS, Element.DataType.UNSIGNED_16, 3, 0x71604884c752e61dl, false);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_16, 3), INPUTSIZE);
+            script.set_gAllocInV2(inV2);
+            script.forEach_testMaxUshort3Ushort3Ushort3(inV1, out);
+            verifyResultsMaxUshort3Ushort3Ushort3(inV1, inV2, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testMaxUshort3Ushort3Ushort3: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_16, 3), INPUTSIZE);
+            scriptRelaxed.set_gAllocInV2(inV2);
+            scriptRelaxed.forEach_testMaxUshort3Ushort3Ushort3(inV1, out);
+            verifyResultsMaxUshort3Ushort3Ushort3(inV1, inV2, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testMaxUshort3Ushort3Ushort3: " + e.toString());
+        }
+    }
+
+    private void verifyResultsMaxUshort3Ushort3Ushort3(Allocation inV1, Allocation inV2, Allocation out, boolean relaxed) {
+        short[] arrayInV1 = new short[INPUTSIZE * 4];
+        inV1.copyTo(arrayInV1);
+        short[] arrayInV2 = new short[INPUTSIZE * 4];
+        inV2.copyTo(arrayInV2);
+        short[] arrayOut = new short[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 3 ; j++) {
+                // Extract the inputs.
+                ArgumentsUshortUshortUshort args = new ArgumentsUshortUshortUshort();
+                args.inV1 = arrayInV1[i * 4 + j];
+                args.inV2 = arrayInV2[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeMax(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (args.out != arrayOut[i * 4 + j]) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV1: ");
+                    message.append(String.format("0x%x", args.inV1));
+                    message.append("\n");
+                    message.append("Input inV2: ");
+                    message.append(String.format("0x%x", args.inV2));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(String.format("0x%x", args.out));
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("0x%x", arrayOut[i * 4 + j]));
+                    if (args.out != arrayOut[i * 4 + j]) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkMaxUshort3Ushort3Ushort3" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkMaxUshort4Ushort4Ushort4() {
+        Allocation inV1 = createRandomAllocation(mRS, Element.DataType.UNSIGNED_16, 4, 0xee9efa6405c77a79l, false);
+        Allocation inV2 = createRandomAllocation(mRS, Element.DataType.UNSIGNED_16, 4, 0xee9efa6405c77a7al, false);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_16, 4), INPUTSIZE);
+            script.set_gAllocInV2(inV2);
+            script.forEach_testMaxUshort4Ushort4Ushort4(inV1, out);
+            verifyResultsMaxUshort4Ushort4Ushort4(inV1, inV2, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testMaxUshort4Ushort4Ushort4: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_16, 4), INPUTSIZE);
+            scriptRelaxed.set_gAllocInV2(inV2);
+            scriptRelaxed.forEach_testMaxUshort4Ushort4Ushort4(inV1, out);
+            verifyResultsMaxUshort4Ushort4Ushort4(inV1, inV2, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testMaxUshort4Ushort4Ushort4: " + e.toString());
+        }
+    }
+
+    private void verifyResultsMaxUshort4Ushort4Ushort4(Allocation inV1, Allocation inV2, Allocation out, boolean relaxed) {
+        short[] arrayInV1 = new short[INPUTSIZE * 4];
+        inV1.copyTo(arrayInV1);
+        short[] arrayInV2 = new short[INPUTSIZE * 4];
+        inV2.copyTo(arrayInV2);
+        short[] arrayOut = new short[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 4 ; j++) {
+                // Extract the inputs.
+                ArgumentsUshortUshortUshort args = new ArgumentsUshortUshortUshort();
+                args.inV1 = arrayInV1[i * 4 + j];
+                args.inV2 = arrayInV2[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeMax(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (args.out != arrayOut[i * 4 + j]) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV1: ");
+                    message.append(String.format("0x%x", args.inV1));
+                    message.append("\n");
+                    message.append("Input inV2: ");
+                    message.append(String.format("0x%x", args.inV2));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(String.format("0x%x", args.out));
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("0x%x", arrayOut[i * 4 + j]));
+                    if (args.out != arrayOut[i * 4 + j]) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkMaxUshort4Ushort4Ushort4" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
     public class ArgumentsIntIntInt {
         public int inV1;
         public int inV2;
@@ -647,7 +1439,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeMax(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 1 + j]) {
                     valid = false;
@@ -676,6 +1468,204 @@
         }
     }
 
+    private void checkMaxInt2Int2Int2() {
+        Allocation inV1 = createRandomAllocation(mRS, Element.DataType.SIGNED_32, 2, 0x7bba1e4a83816bd5l, false);
+        Allocation inV2 = createRandomAllocation(mRS, Element.DataType.SIGNED_32, 2, 0x7bba1e4a83816bd6l, false);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_32, 2), INPUTSIZE);
+            script.set_gAllocInV2(inV2);
+            script.forEach_testMaxInt2Int2Int2(inV1, out);
+            verifyResultsMaxInt2Int2Int2(inV1, inV2, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testMaxInt2Int2Int2: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_32, 2), INPUTSIZE);
+            scriptRelaxed.set_gAllocInV2(inV2);
+            scriptRelaxed.forEach_testMaxInt2Int2Int2(inV1, out);
+            verifyResultsMaxInt2Int2Int2(inV1, inV2, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testMaxInt2Int2Int2: " + e.toString());
+        }
+    }
+
+    private void verifyResultsMaxInt2Int2Int2(Allocation inV1, Allocation inV2, Allocation out, boolean relaxed) {
+        int[] arrayInV1 = new int[INPUTSIZE * 2];
+        inV1.copyTo(arrayInV1);
+        int[] arrayInV2 = new int[INPUTSIZE * 2];
+        inV2.copyTo(arrayInV2);
+        int[] arrayOut = new int[INPUTSIZE * 2];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 2 ; j++) {
+                // Extract the inputs.
+                ArgumentsIntIntInt args = new ArgumentsIntIntInt();
+                args.inV1 = arrayInV1[i * 2 + j];
+                args.inV2 = arrayInV2[i * 2 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeMax(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (args.out != arrayOut[i * 2 + j]) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV1: ");
+                    message.append(String.format("%d", args.inV1));
+                    message.append("\n");
+                    message.append("Input inV2: ");
+                    message.append(String.format("%d", args.inV2));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(String.format("%d", args.out));
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%d", arrayOut[i * 2 + j]));
+                    if (args.out != arrayOut[i * 2 + j]) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkMaxInt2Int2Int2" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkMaxInt3Int3Int3() {
+        Allocation inV1 = createRandomAllocation(mRS, Element.DataType.SIGNED_32, 3, 0xa647496a95547ff8l, false);
+        Allocation inV2 = createRandomAllocation(mRS, Element.DataType.SIGNED_32, 3, 0xa647496a95547ff9l, false);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_32, 3), INPUTSIZE);
+            script.set_gAllocInV2(inV2);
+            script.forEach_testMaxInt3Int3Int3(inV1, out);
+            verifyResultsMaxInt3Int3Int3(inV1, inV2, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testMaxInt3Int3Int3: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_32, 3), INPUTSIZE);
+            scriptRelaxed.set_gAllocInV2(inV2);
+            scriptRelaxed.forEach_testMaxInt3Int3Int3(inV1, out);
+            verifyResultsMaxInt3Int3Int3(inV1, inV2, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testMaxInt3Int3Int3: " + e.toString());
+        }
+    }
+
+    private void verifyResultsMaxInt3Int3Int3(Allocation inV1, Allocation inV2, Allocation out, boolean relaxed) {
+        int[] arrayInV1 = new int[INPUTSIZE * 4];
+        inV1.copyTo(arrayInV1);
+        int[] arrayInV2 = new int[INPUTSIZE * 4];
+        inV2.copyTo(arrayInV2);
+        int[] arrayOut = new int[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 3 ; j++) {
+                // Extract the inputs.
+                ArgumentsIntIntInt args = new ArgumentsIntIntInt();
+                args.inV1 = arrayInV1[i * 4 + j];
+                args.inV2 = arrayInV2[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeMax(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (args.out != arrayOut[i * 4 + j]) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV1: ");
+                    message.append(String.format("%d", args.inV1));
+                    message.append("\n");
+                    message.append("Input inV2: ");
+                    message.append(String.format("%d", args.inV2));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(String.format("%d", args.out));
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%d", arrayOut[i * 4 + j]));
+                    if (args.out != arrayOut[i * 4 + j]) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkMaxInt3Int3Int3" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkMaxInt4Int4Int4() {
+        Allocation inV1 = createRandomAllocation(mRS, Element.DataType.SIGNED_32, 4, 0xd0d4748aa727941bl, false);
+        Allocation inV2 = createRandomAllocation(mRS, Element.DataType.SIGNED_32, 4, 0xd0d4748aa727941cl, false);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_32, 4), INPUTSIZE);
+            script.set_gAllocInV2(inV2);
+            script.forEach_testMaxInt4Int4Int4(inV1, out);
+            verifyResultsMaxInt4Int4Int4(inV1, inV2, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testMaxInt4Int4Int4: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_32, 4), INPUTSIZE);
+            scriptRelaxed.set_gAllocInV2(inV2);
+            scriptRelaxed.forEach_testMaxInt4Int4Int4(inV1, out);
+            verifyResultsMaxInt4Int4Int4(inV1, inV2, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testMaxInt4Int4Int4: " + e.toString());
+        }
+    }
+
+    private void verifyResultsMaxInt4Int4Int4(Allocation inV1, Allocation inV2, Allocation out, boolean relaxed) {
+        int[] arrayInV1 = new int[INPUTSIZE * 4];
+        inV1.copyTo(arrayInV1);
+        int[] arrayInV2 = new int[INPUTSIZE * 4];
+        inV2.copyTo(arrayInV2);
+        int[] arrayOut = new int[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 4 ; j++) {
+                // Extract the inputs.
+                ArgumentsIntIntInt args = new ArgumentsIntIntInt();
+                args.inV1 = arrayInV1[i * 4 + j];
+                args.inV2 = arrayInV2[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeMax(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (args.out != arrayOut[i * 4 + j]) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV1: ");
+                    message.append(String.format("%d", args.inV1));
+                    message.append("\n");
+                    message.append("Input inV2: ");
+                    message.append(String.format("%d", args.inV2));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(String.format("%d", args.out));
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%d", arrayOut[i * 4 + j]));
+                    if (args.out != arrayOut[i * 4 + j]) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkMaxInt4Int4Int4" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
     public class ArgumentsUintUintUint {
         public int inV1;
         public int inV2;
@@ -719,7 +1709,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeMax(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 1 + j]) {
                     valid = false;
@@ -748,336 +1738,6 @@
         }
     }
 
-    private void checkMaxChar2Char2Char2() {
-        Allocation inV1 = createRandomAllocation(mRS, Element.DataType.SIGNED_8, 2, 0x12084b25952bc64l, false);
-        Allocation inV2 = createRandomAllocation(mRS, Element.DataType.SIGNED_8, 2, 0x12084b25952bc65l, false);
-        try {
-            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_8, 2), INPUTSIZE);
-            script.set_gAllocInV2(inV2);
-            script.forEach_testMaxChar2Char2Char2(inV1, out);
-            verifyResultsMaxChar2Char2Char2(inV1, inV2, out, false);
-        } catch (Exception e) {
-            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testMaxChar2Char2Char2: " + e.toString());
-        }
-        try {
-            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_8, 2), INPUTSIZE);
-            scriptRelaxed.set_gAllocInV2(inV2);
-            scriptRelaxed.forEach_testMaxChar2Char2Char2(inV1, out);
-            verifyResultsMaxChar2Char2Char2(inV1, inV2, out, true);
-        } catch (Exception e) {
-            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testMaxChar2Char2Char2: " + e.toString());
-        }
-    }
-
-    private void verifyResultsMaxChar2Char2Char2(Allocation inV1, Allocation inV2, Allocation out, boolean relaxed) {
-        byte[] arrayInV1 = new byte[INPUTSIZE * 2];
-        inV1.copyTo(arrayInV1);
-        byte[] arrayInV2 = new byte[INPUTSIZE * 2];
-        inV2.copyTo(arrayInV2);
-        byte[] arrayOut = new byte[INPUTSIZE * 2];
-        out.copyTo(arrayOut);
-        for (int i = 0; i < INPUTSIZE; i++) {
-            for (int j = 0; j < 2 ; j++) {
-                // Extract the inputs.
-                ArgumentsCharCharChar args = new ArgumentsCharCharChar();
-                args.inV1 = arrayInV1[i * 2 + j];
-                args.inV2 = arrayInV2[i * 2 + j];
-                // Figure out what the outputs should have been.
-                Floaty.setRelaxed(relaxed);
-                CoreMathVerifier.computeMax(args);
-                // Figure out what the outputs should have been.
-                boolean valid = true;
-                if (args.out != arrayOut[i * 2 + j]) {
-                    valid = false;
-                }
-                if (!valid) {
-                    StringBuilder message = new StringBuilder();
-                    message.append("Input inV1: ");
-                    message.append(String.format("%d", args.inV1));
-                    message.append("\n");
-                    message.append("Input inV2: ");
-                    message.append(String.format("%d", args.inV2));
-                    message.append("\n");
-                    message.append("Expected output out: ");
-                    message.append(String.format("%d", args.out));
-                    message.append("\n");
-                    message.append("Actual   output out: ");
-                    message.append(String.format("%d", arrayOut[i * 2 + j]));
-                    if (args.out != arrayOut[i * 2 + j]) {
-                        message.append(" FAIL");
-                    }
-                    message.append("\n");
-                    assertTrue("Incorrect output for checkMaxChar2Char2Char2" +
-                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
-                }
-            }
-        }
-    }
-
-    private void checkMaxUchar2Uchar2Uchar2() {
-        Allocation inV1 = createRandomAllocation(mRS, Element.DataType.UNSIGNED_8, 2, 0x75eda605e43f8b81l, false);
-        Allocation inV2 = createRandomAllocation(mRS, Element.DataType.UNSIGNED_8, 2, 0x75eda605e43f8b82l, false);
-        try {
-            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_8, 2), INPUTSIZE);
-            script.set_gAllocInV2(inV2);
-            script.forEach_testMaxUchar2Uchar2Uchar2(inV1, out);
-            verifyResultsMaxUchar2Uchar2Uchar2(inV1, inV2, out, false);
-        } catch (Exception e) {
-            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testMaxUchar2Uchar2Uchar2: " + e.toString());
-        }
-        try {
-            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_8, 2), INPUTSIZE);
-            scriptRelaxed.set_gAllocInV2(inV2);
-            scriptRelaxed.forEach_testMaxUchar2Uchar2Uchar2(inV1, out);
-            verifyResultsMaxUchar2Uchar2Uchar2(inV1, inV2, out, true);
-        } catch (Exception e) {
-            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testMaxUchar2Uchar2Uchar2: " + e.toString());
-        }
-    }
-
-    private void verifyResultsMaxUchar2Uchar2Uchar2(Allocation inV1, Allocation inV2, Allocation out, boolean relaxed) {
-        byte[] arrayInV1 = new byte[INPUTSIZE * 2];
-        inV1.copyTo(arrayInV1);
-        byte[] arrayInV2 = new byte[INPUTSIZE * 2];
-        inV2.copyTo(arrayInV2);
-        byte[] arrayOut = new byte[INPUTSIZE * 2];
-        out.copyTo(arrayOut);
-        for (int i = 0; i < INPUTSIZE; i++) {
-            for (int j = 0; j < 2 ; j++) {
-                // Extract the inputs.
-                ArgumentsUcharUcharUchar args = new ArgumentsUcharUcharUchar();
-                args.inV1 = arrayInV1[i * 2 + j];
-                args.inV2 = arrayInV2[i * 2 + j];
-                // Figure out what the outputs should have been.
-                Floaty.setRelaxed(relaxed);
-                CoreMathVerifier.computeMax(args);
-                // Figure out what the outputs should have been.
-                boolean valid = true;
-                if (args.out != arrayOut[i * 2 + j]) {
-                    valid = false;
-                }
-                if (!valid) {
-                    StringBuilder message = new StringBuilder();
-                    message.append("Input inV1: ");
-                    message.append(String.format("0x%x", args.inV1));
-                    message.append("\n");
-                    message.append("Input inV2: ");
-                    message.append(String.format("0x%x", args.inV2));
-                    message.append("\n");
-                    message.append("Expected output out: ");
-                    message.append(String.format("0x%x", args.out));
-                    message.append("\n");
-                    message.append("Actual   output out: ");
-                    message.append(String.format("0x%x", arrayOut[i * 2 + j]));
-                    if (args.out != arrayOut[i * 2 + j]) {
-                        message.append(" FAIL");
-                    }
-                    message.append("\n");
-                    assertTrue("Incorrect output for checkMaxUchar2Uchar2Uchar2" +
-                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
-                }
-            }
-        }
-    }
-
-    private void checkMaxShort2Short2Short2() {
-        Allocation inV1 = createRandomAllocation(mRS, Element.DataType.SIGNED_16, 2, 0x3d46ae0799c33c02l, false);
-        Allocation inV2 = createRandomAllocation(mRS, Element.DataType.SIGNED_16, 2, 0x3d46ae0799c33c03l, false);
-        try {
-            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_16, 2), INPUTSIZE);
-            script.set_gAllocInV2(inV2);
-            script.forEach_testMaxShort2Short2Short2(inV1, out);
-            verifyResultsMaxShort2Short2Short2(inV1, inV2, out, false);
-        } catch (Exception e) {
-            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testMaxShort2Short2Short2: " + e.toString());
-        }
-        try {
-            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_16, 2), INPUTSIZE);
-            scriptRelaxed.set_gAllocInV2(inV2);
-            scriptRelaxed.forEach_testMaxShort2Short2Short2(inV1, out);
-            verifyResultsMaxShort2Short2Short2(inV1, inV2, out, true);
-        } catch (Exception e) {
-            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testMaxShort2Short2Short2: " + e.toString());
-        }
-    }
-
-    private void verifyResultsMaxShort2Short2Short2(Allocation inV1, Allocation inV2, Allocation out, boolean relaxed) {
-        short[] arrayInV1 = new short[INPUTSIZE * 2];
-        inV1.copyTo(arrayInV1);
-        short[] arrayInV2 = new short[INPUTSIZE * 2];
-        inV2.copyTo(arrayInV2);
-        short[] arrayOut = new short[INPUTSIZE * 2];
-        out.copyTo(arrayOut);
-        for (int i = 0; i < INPUTSIZE; i++) {
-            for (int j = 0; j < 2 ; j++) {
-                // Extract the inputs.
-                ArgumentsShortShortShort args = new ArgumentsShortShortShort();
-                args.inV1 = arrayInV1[i * 2 + j];
-                args.inV2 = arrayInV2[i * 2 + j];
-                // Figure out what the outputs should have been.
-                Floaty.setRelaxed(relaxed);
-                CoreMathVerifier.computeMax(args);
-                // Figure out what the outputs should have been.
-                boolean valid = true;
-                if (args.out != arrayOut[i * 2 + j]) {
-                    valid = false;
-                }
-                if (!valid) {
-                    StringBuilder message = new StringBuilder();
-                    message.append("Input inV1: ");
-                    message.append(String.format("%d", args.inV1));
-                    message.append("\n");
-                    message.append("Input inV2: ");
-                    message.append(String.format("%d", args.inV2));
-                    message.append("\n");
-                    message.append("Expected output out: ");
-                    message.append(String.format("%d", args.out));
-                    message.append("\n");
-                    message.append("Actual   output out: ");
-                    message.append(String.format("%d", arrayOut[i * 2 + j]));
-                    if (args.out != arrayOut[i * 2 + j]) {
-                        message.append(" FAIL");
-                    }
-                    message.append("\n");
-                    assertTrue("Incorrect output for checkMaxShort2Short2Short2" +
-                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
-                }
-            }
-        }
-    }
-
-    private void checkMaxUshort2Ushort2Ushort2() {
-        Allocation inV1 = createRandomAllocation(mRS, Element.DataType.UNSIGNED_16, 2, 0xf42196a588de51bfl, false);
-        Allocation inV2 = createRandomAllocation(mRS, Element.DataType.UNSIGNED_16, 2, 0xf42196a588de51c0l, false);
-        try {
-            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_16, 2), INPUTSIZE);
-            script.set_gAllocInV2(inV2);
-            script.forEach_testMaxUshort2Ushort2Ushort2(inV1, out);
-            verifyResultsMaxUshort2Ushort2Ushort2(inV1, inV2, out, false);
-        } catch (Exception e) {
-            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testMaxUshort2Ushort2Ushort2: " + e.toString());
-        }
-        try {
-            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_16, 2), INPUTSIZE);
-            scriptRelaxed.set_gAllocInV2(inV2);
-            scriptRelaxed.forEach_testMaxUshort2Ushort2Ushort2(inV1, out);
-            verifyResultsMaxUshort2Ushort2Ushort2(inV1, inV2, out, true);
-        } catch (Exception e) {
-            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testMaxUshort2Ushort2Ushort2: " + e.toString());
-        }
-    }
-
-    private void verifyResultsMaxUshort2Ushort2Ushort2(Allocation inV1, Allocation inV2, Allocation out, boolean relaxed) {
-        short[] arrayInV1 = new short[INPUTSIZE * 2];
-        inV1.copyTo(arrayInV1);
-        short[] arrayInV2 = new short[INPUTSIZE * 2];
-        inV2.copyTo(arrayInV2);
-        short[] arrayOut = new short[INPUTSIZE * 2];
-        out.copyTo(arrayOut);
-        for (int i = 0; i < INPUTSIZE; i++) {
-            for (int j = 0; j < 2 ; j++) {
-                // Extract the inputs.
-                ArgumentsUshortUshortUshort args = new ArgumentsUshortUshortUshort();
-                args.inV1 = arrayInV1[i * 2 + j];
-                args.inV2 = arrayInV2[i * 2 + j];
-                // Figure out what the outputs should have been.
-                Floaty.setRelaxed(relaxed);
-                CoreMathVerifier.computeMax(args);
-                // Figure out what the outputs should have been.
-                boolean valid = true;
-                if (args.out != arrayOut[i * 2 + j]) {
-                    valid = false;
-                }
-                if (!valid) {
-                    StringBuilder message = new StringBuilder();
-                    message.append("Input inV1: ");
-                    message.append(String.format("0x%x", args.inV1));
-                    message.append("\n");
-                    message.append("Input inV2: ");
-                    message.append(String.format("0x%x", args.inV2));
-                    message.append("\n");
-                    message.append("Expected output out: ");
-                    message.append(String.format("0x%x", args.out));
-                    message.append("\n");
-                    message.append("Actual   output out: ");
-                    message.append(String.format("0x%x", arrayOut[i * 2 + j]));
-                    if (args.out != arrayOut[i * 2 + j]) {
-                        message.append(" FAIL");
-                    }
-                    message.append("\n");
-                    assertTrue("Incorrect output for checkMaxUshort2Ushort2Ushort2" +
-                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
-                }
-            }
-        }
-    }
-
-    private void checkMaxInt2Int2Int2() {
-        Allocation inV1 = createRandomAllocation(mRS, Element.DataType.SIGNED_32, 2, 0x7bba1e4a83816bd5l, false);
-        Allocation inV2 = createRandomAllocation(mRS, Element.DataType.SIGNED_32, 2, 0x7bba1e4a83816bd6l, false);
-        try {
-            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_32, 2), INPUTSIZE);
-            script.set_gAllocInV2(inV2);
-            script.forEach_testMaxInt2Int2Int2(inV1, out);
-            verifyResultsMaxInt2Int2Int2(inV1, inV2, out, false);
-        } catch (Exception e) {
-            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testMaxInt2Int2Int2: " + e.toString());
-        }
-        try {
-            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_32, 2), INPUTSIZE);
-            scriptRelaxed.set_gAllocInV2(inV2);
-            scriptRelaxed.forEach_testMaxInt2Int2Int2(inV1, out);
-            verifyResultsMaxInt2Int2Int2(inV1, inV2, out, true);
-        } catch (Exception e) {
-            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testMaxInt2Int2Int2: " + e.toString());
-        }
-    }
-
-    private void verifyResultsMaxInt2Int2Int2(Allocation inV1, Allocation inV2, Allocation out, boolean relaxed) {
-        int[] arrayInV1 = new int[INPUTSIZE * 2];
-        inV1.copyTo(arrayInV1);
-        int[] arrayInV2 = new int[INPUTSIZE * 2];
-        inV2.copyTo(arrayInV2);
-        int[] arrayOut = new int[INPUTSIZE * 2];
-        out.copyTo(arrayOut);
-        for (int i = 0; i < INPUTSIZE; i++) {
-            for (int j = 0; j < 2 ; j++) {
-                // Extract the inputs.
-                ArgumentsIntIntInt args = new ArgumentsIntIntInt();
-                args.inV1 = arrayInV1[i * 2 + j];
-                args.inV2 = arrayInV2[i * 2 + j];
-                // Figure out what the outputs should have been.
-                Floaty.setRelaxed(relaxed);
-                CoreMathVerifier.computeMax(args);
-                // Figure out what the outputs should have been.
-                boolean valid = true;
-                if (args.out != arrayOut[i * 2 + j]) {
-                    valid = false;
-                }
-                if (!valid) {
-                    StringBuilder message = new StringBuilder();
-                    message.append("Input inV1: ");
-                    message.append(String.format("%d", args.inV1));
-                    message.append("\n");
-                    message.append("Input inV2: ");
-                    message.append(String.format("%d", args.inV2));
-                    message.append("\n");
-                    message.append("Expected output out: ");
-                    message.append(String.format("%d", args.out));
-                    message.append("\n");
-                    message.append("Actual   output out: ");
-                    message.append(String.format("%d", arrayOut[i * 2 + j]));
-                    if (args.out != arrayOut[i * 2 + j]) {
-                        message.append(" FAIL");
-                    }
-                    message.append("\n");
-                    assertTrue("Incorrect output for checkMaxInt2Int2Int2" +
-                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
-                }
-            }
-        }
-    }
-
     private void checkMaxUint2Uint2Uint2() {
         Allocation inV1 = createRandomAllocation(mRS, Element.DataType.UNSIGNED_32, 2, 0xcda90384705016a4l, false);
         Allocation inV2 = createRandomAllocation(mRS, Element.DataType.UNSIGNED_32, 2, 0xcda90384705016a5l, false);
@@ -1115,7 +1775,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeMax(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 2 + j]) {
                     valid = false;
@@ -1144,336 +1804,6 @@
         }
     }
 
-    private void checkMaxChar3Char3Char3() {
-        Allocation inV1 = createRandomAllocation(mRS, Element.DataType.SIGNED_8, 3, 0x567200e53e0a8f29l, false);
-        Allocation inV2 = createRandomAllocation(mRS, Element.DataType.SIGNED_8, 3, 0x567200e53e0a8f2al, false);
-        try {
-            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_8, 3), INPUTSIZE);
-            script.set_gAllocInV2(inV2);
-            script.forEach_testMaxChar3Char3Char3(inV1, out);
-            verifyResultsMaxChar3Char3Char3(inV1, inV2, out, false);
-        } catch (Exception e) {
-            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testMaxChar3Char3Char3: " + e.toString());
-        }
-        try {
-            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_8, 3), INPUTSIZE);
-            scriptRelaxed.set_gAllocInV2(inV2);
-            scriptRelaxed.forEach_testMaxChar3Char3Char3(inV1, out);
-            verifyResultsMaxChar3Char3Char3(inV1, inV2, out, true);
-        } catch (Exception e) {
-            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testMaxChar3Char3Char3: " + e.toString());
-        }
-    }
-
-    private void verifyResultsMaxChar3Char3Char3(Allocation inV1, Allocation inV2, Allocation out, boolean relaxed) {
-        byte[] arrayInV1 = new byte[INPUTSIZE * 4];
-        inV1.copyTo(arrayInV1);
-        byte[] arrayInV2 = new byte[INPUTSIZE * 4];
-        inV2.copyTo(arrayInV2);
-        byte[] arrayOut = new byte[INPUTSIZE * 4];
-        out.copyTo(arrayOut);
-        for (int i = 0; i < INPUTSIZE; i++) {
-            for (int j = 0; j < 3 ; j++) {
-                // Extract the inputs.
-                ArgumentsCharCharChar args = new ArgumentsCharCharChar();
-                args.inV1 = arrayInV1[i * 4 + j];
-                args.inV2 = arrayInV2[i * 4 + j];
-                // Figure out what the outputs should have been.
-                Floaty.setRelaxed(relaxed);
-                CoreMathVerifier.computeMax(args);
-                // Figure out what the outputs should have been.
-                boolean valid = true;
-                if (args.out != arrayOut[i * 4 + j]) {
-                    valid = false;
-                }
-                if (!valid) {
-                    StringBuilder message = new StringBuilder();
-                    message.append("Input inV1: ");
-                    message.append(String.format("%d", args.inV1));
-                    message.append("\n");
-                    message.append("Input inV2: ");
-                    message.append(String.format("%d", args.inV2));
-                    message.append("\n");
-                    message.append("Expected output out: ");
-                    message.append(String.format("%d", args.out));
-                    message.append("\n");
-                    message.append("Actual   output out: ");
-                    message.append(String.format("%d", arrayOut[i * 4 + j]));
-                    if (args.out != arrayOut[i * 4 + j]) {
-                        message.append(" FAIL");
-                    }
-                    message.append("\n");
-                    assertTrue("Incorrect output for checkMaxChar3Char3Char3" +
-                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
-                }
-            }
-        }
-    }
-
-    private void checkMaxUchar3Uchar3Uchar3() {
-        Allocation inV1 = createRandomAllocation(mRS, Element.DataType.UNSIGNED_8, 3, 0xa2def5663489d18cl, false);
-        Allocation inV2 = createRandomAllocation(mRS, Element.DataType.UNSIGNED_8, 3, 0xa2def5663489d18dl, false);
-        try {
-            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_8, 3), INPUTSIZE);
-            script.set_gAllocInV2(inV2);
-            script.forEach_testMaxUchar3Uchar3Uchar3(inV1, out);
-            verifyResultsMaxUchar3Uchar3Uchar3(inV1, inV2, out, false);
-        } catch (Exception e) {
-            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testMaxUchar3Uchar3Uchar3: " + e.toString());
-        }
-        try {
-            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_8, 3), INPUTSIZE);
-            scriptRelaxed.set_gAllocInV2(inV2);
-            scriptRelaxed.forEach_testMaxUchar3Uchar3Uchar3(inV1, out);
-            verifyResultsMaxUchar3Uchar3Uchar3(inV1, inV2, out, true);
-        } catch (Exception e) {
-            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testMaxUchar3Uchar3Uchar3: " + e.toString());
-        }
-    }
-
-    private void verifyResultsMaxUchar3Uchar3Uchar3(Allocation inV1, Allocation inV2, Allocation out, boolean relaxed) {
-        byte[] arrayInV1 = new byte[INPUTSIZE * 4];
-        inV1.copyTo(arrayInV1);
-        byte[] arrayInV2 = new byte[INPUTSIZE * 4];
-        inV2.copyTo(arrayInV2);
-        byte[] arrayOut = new byte[INPUTSIZE * 4];
-        out.copyTo(arrayOut);
-        for (int i = 0; i < INPUTSIZE; i++) {
-            for (int j = 0; j < 3 ; j++) {
-                // Extract the inputs.
-                ArgumentsUcharUcharUchar args = new ArgumentsUcharUcharUchar();
-                args.inV1 = arrayInV1[i * 4 + j];
-                args.inV2 = arrayInV2[i * 4 + j];
-                // Figure out what the outputs should have been.
-                Floaty.setRelaxed(relaxed);
-                CoreMathVerifier.computeMax(args);
-                // Figure out what the outputs should have been.
-                boolean valid = true;
-                if (args.out != arrayOut[i * 4 + j]) {
-                    valid = false;
-                }
-                if (!valid) {
-                    StringBuilder message = new StringBuilder();
-                    message.append("Input inV1: ");
-                    message.append(String.format("0x%x", args.inV1));
-                    message.append("\n");
-                    message.append("Input inV2: ");
-                    message.append(String.format("0x%x", args.inV2));
-                    message.append("\n");
-                    message.append("Expected output out: ");
-                    message.append(String.format("0x%x", args.out));
-                    message.append("\n");
-                    message.append("Actual   output out: ");
-                    message.append(String.format("0x%x", arrayOut[i * 4 + j]));
-                    if (args.out != arrayOut[i * 4 + j]) {
-                        message.append(" FAIL");
-                    }
-                    message.append("\n");
-                    assertTrue("Incorrect output for checkMaxUchar3Uchar3Uchar3" +
-                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
-                }
-            }
-        }
-    }
-
-    private void checkMaxShort3Short3Short3() {
-        Allocation inV1 = createRandomAllocation(mRS, Element.DataType.SIGNED_16, 3, 0x6a37fd67ea0d820dl, false);
-        Allocation inV2 = createRandomAllocation(mRS, Element.DataType.SIGNED_16, 3, 0x6a37fd67ea0d820el, false);
-        try {
-            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_16, 3), INPUTSIZE);
-            script.set_gAllocInV2(inV2);
-            script.forEach_testMaxShort3Short3Short3(inV1, out);
-            verifyResultsMaxShort3Short3Short3(inV1, inV2, out, false);
-        } catch (Exception e) {
-            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testMaxShort3Short3Short3: " + e.toString());
-        }
-        try {
-            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_16, 3), INPUTSIZE);
-            scriptRelaxed.set_gAllocInV2(inV2);
-            scriptRelaxed.forEach_testMaxShort3Short3Short3(inV1, out);
-            verifyResultsMaxShort3Short3Short3(inV1, inV2, out, true);
-        } catch (Exception e) {
-            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testMaxShort3Short3Short3: " + e.toString());
-        }
-    }
-
-    private void verifyResultsMaxShort3Short3Short3(Allocation inV1, Allocation inV2, Allocation out, boolean relaxed) {
-        short[] arrayInV1 = new short[INPUTSIZE * 4];
-        inV1.copyTo(arrayInV1);
-        short[] arrayInV2 = new short[INPUTSIZE * 4];
-        inV2.copyTo(arrayInV2);
-        short[] arrayOut = new short[INPUTSIZE * 4];
-        out.copyTo(arrayOut);
-        for (int i = 0; i < INPUTSIZE; i++) {
-            for (int j = 0; j < 3 ; j++) {
-                // Extract the inputs.
-                ArgumentsShortShortShort args = new ArgumentsShortShortShort();
-                args.inV1 = arrayInV1[i * 4 + j];
-                args.inV2 = arrayInV2[i * 4 + j];
-                // Figure out what the outputs should have been.
-                Floaty.setRelaxed(relaxed);
-                CoreMathVerifier.computeMax(args);
-                // Figure out what the outputs should have been.
-                boolean valid = true;
-                if (args.out != arrayOut[i * 4 + j]) {
-                    valid = false;
-                }
-                if (!valid) {
-                    StringBuilder message = new StringBuilder();
-                    message.append("Input inV1: ");
-                    message.append(String.format("%d", args.inV1));
-                    message.append("\n");
-                    message.append("Input inV2: ");
-                    message.append(String.format("%d", args.inV2));
-                    message.append("\n");
-                    message.append("Expected output out: ");
-                    message.append(String.format("%d", args.out));
-                    message.append("\n");
-                    message.append("Actual   output out: ");
-                    message.append(String.format("%d", arrayOut[i * 4 + j]));
-                    if (args.out != arrayOut[i * 4 + j]) {
-                        message.append(" FAIL");
-                    }
-                    message.append("\n");
-                    assertTrue("Incorrect output for checkMaxShort3Short3Short3" +
-                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
-                }
-            }
-        }
-    }
-
-    private void checkMaxUshort3Ushort3Ushort3() {
-        Allocation inV1 = createRandomAllocation(mRS, Element.DataType.UNSIGNED_16, 3, 0x71604884c752e61cl, false);
-        Allocation inV2 = createRandomAllocation(mRS, Element.DataType.UNSIGNED_16, 3, 0x71604884c752e61dl, false);
-        try {
-            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_16, 3), INPUTSIZE);
-            script.set_gAllocInV2(inV2);
-            script.forEach_testMaxUshort3Ushort3Ushort3(inV1, out);
-            verifyResultsMaxUshort3Ushort3Ushort3(inV1, inV2, out, false);
-        } catch (Exception e) {
-            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testMaxUshort3Ushort3Ushort3: " + e.toString());
-        }
-        try {
-            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_16, 3), INPUTSIZE);
-            scriptRelaxed.set_gAllocInV2(inV2);
-            scriptRelaxed.forEach_testMaxUshort3Ushort3Ushort3(inV1, out);
-            verifyResultsMaxUshort3Ushort3Ushort3(inV1, inV2, out, true);
-        } catch (Exception e) {
-            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testMaxUshort3Ushort3Ushort3: " + e.toString());
-        }
-    }
-
-    private void verifyResultsMaxUshort3Ushort3Ushort3(Allocation inV1, Allocation inV2, Allocation out, boolean relaxed) {
-        short[] arrayInV1 = new short[INPUTSIZE * 4];
-        inV1.copyTo(arrayInV1);
-        short[] arrayInV2 = new short[INPUTSIZE * 4];
-        inV2.copyTo(arrayInV2);
-        short[] arrayOut = new short[INPUTSIZE * 4];
-        out.copyTo(arrayOut);
-        for (int i = 0; i < INPUTSIZE; i++) {
-            for (int j = 0; j < 3 ; j++) {
-                // Extract the inputs.
-                ArgumentsUshortUshortUshort args = new ArgumentsUshortUshortUshort();
-                args.inV1 = arrayInV1[i * 4 + j];
-                args.inV2 = arrayInV2[i * 4 + j];
-                // Figure out what the outputs should have been.
-                Floaty.setRelaxed(relaxed);
-                CoreMathVerifier.computeMax(args);
-                // Figure out what the outputs should have been.
-                boolean valid = true;
-                if (args.out != arrayOut[i * 4 + j]) {
-                    valid = false;
-                }
-                if (!valid) {
-                    StringBuilder message = new StringBuilder();
-                    message.append("Input inV1: ");
-                    message.append(String.format("0x%x", args.inV1));
-                    message.append("\n");
-                    message.append("Input inV2: ");
-                    message.append(String.format("0x%x", args.inV2));
-                    message.append("\n");
-                    message.append("Expected output out: ");
-                    message.append(String.format("0x%x", args.out));
-                    message.append("\n");
-                    message.append("Actual   output out: ");
-                    message.append(String.format("0x%x", arrayOut[i * 4 + j]));
-                    if (args.out != arrayOut[i * 4 + j]) {
-                        message.append(" FAIL");
-                    }
-                    message.append("\n");
-                    assertTrue("Incorrect output for checkMaxUshort3Ushort3Ushort3" +
-                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
-                }
-            }
-        }
-    }
-
-    private void checkMaxInt3Int3Int3() {
-        Allocation inV1 = createRandomAllocation(mRS, Element.DataType.SIGNED_32, 3, 0xa647496a95547ff8l, false);
-        Allocation inV2 = createRandomAllocation(mRS, Element.DataType.SIGNED_32, 3, 0xa647496a95547ff9l, false);
-        try {
-            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_32, 3), INPUTSIZE);
-            script.set_gAllocInV2(inV2);
-            script.forEach_testMaxInt3Int3Int3(inV1, out);
-            verifyResultsMaxInt3Int3Int3(inV1, inV2, out, false);
-        } catch (Exception e) {
-            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testMaxInt3Int3Int3: " + e.toString());
-        }
-        try {
-            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_32, 3), INPUTSIZE);
-            scriptRelaxed.set_gAllocInV2(inV2);
-            scriptRelaxed.forEach_testMaxInt3Int3Int3(inV1, out);
-            verifyResultsMaxInt3Int3Int3(inV1, inV2, out, true);
-        } catch (Exception e) {
-            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testMaxInt3Int3Int3: " + e.toString());
-        }
-    }
-
-    private void verifyResultsMaxInt3Int3Int3(Allocation inV1, Allocation inV2, Allocation out, boolean relaxed) {
-        int[] arrayInV1 = new int[INPUTSIZE * 4];
-        inV1.copyTo(arrayInV1);
-        int[] arrayInV2 = new int[INPUTSIZE * 4];
-        inV2.copyTo(arrayInV2);
-        int[] arrayOut = new int[INPUTSIZE * 4];
-        out.copyTo(arrayOut);
-        for (int i = 0; i < INPUTSIZE; i++) {
-            for (int j = 0; j < 3 ; j++) {
-                // Extract the inputs.
-                ArgumentsIntIntInt args = new ArgumentsIntIntInt();
-                args.inV1 = arrayInV1[i * 4 + j];
-                args.inV2 = arrayInV2[i * 4 + j];
-                // Figure out what the outputs should have been.
-                Floaty.setRelaxed(relaxed);
-                CoreMathVerifier.computeMax(args);
-                // Figure out what the outputs should have been.
-                boolean valid = true;
-                if (args.out != arrayOut[i * 4 + j]) {
-                    valid = false;
-                }
-                if (!valid) {
-                    StringBuilder message = new StringBuilder();
-                    message.append("Input inV1: ");
-                    message.append(String.format("%d", args.inV1));
-                    message.append("\n");
-                    message.append("Input inV2: ");
-                    message.append(String.format("%d", args.inV2));
-                    message.append("\n");
-                    message.append("Expected output out: ");
-                    message.append(String.format("%d", args.out));
-                    message.append("\n");
-                    message.append("Actual   output out: ");
-                    message.append(String.format("%d", arrayOut[i * 4 + j]));
-                    if (args.out != arrayOut[i * 4 + j]) {
-                        message.append(" FAIL");
-                    }
-                    message.append("\n");
-                    assertTrue("Incorrect output for checkMaxInt3Int3Int3" +
-                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
-                }
-            }
-        }
-    }
-
     private void checkMaxUint3Uint3Uint3() {
         Allocation inV1 = createRandomAllocation(mRS, Element.DataType.UNSIGNED_32, 3, 0x22fa7fb75507e969l, false);
         Allocation inV2 = createRandomAllocation(mRS, Element.DataType.UNSIGNED_32, 3, 0x22fa7fb75507e96al, false);
@@ -1511,7 +1841,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeMax(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 4 + j]) {
                     valid = false;
@@ -1540,336 +1870,6 @@
         }
     }
 
-    private void checkMaxChar4Char4Char4() {
-        Allocation inV1 = createRandomAllocation(mRS, Element.DataType.SIGNED_8, 4, 0xabc37d1822c261eel, false);
-        Allocation inV2 = createRandomAllocation(mRS, Element.DataType.SIGNED_8, 4, 0xabc37d1822c261efl, false);
-        try {
-            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_8, 4), INPUTSIZE);
-            script.set_gAllocInV2(inV2);
-            script.forEach_testMaxChar4Char4Char4(inV1, out);
-            verifyResultsMaxChar4Char4Char4(inV1, inV2, out, false);
-        } catch (Exception e) {
-            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testMaxChar4Char4Char4: " + e.toString());
-        }
-        try {
-            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_8, 4), INPUTSIZE);
-            scriptRelaxed.set_gAllocInV2(inV2);
-            scriptRelaxed.forEach_testMaxChar4Char4Char4(inV1, out);
-            verifyResultsMaxChar4Char4Char4(inV1, inV2, out, true);
-        } catch (Exception e) {
-            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testMaxChar4Char4Char4: " + e.toString());
-        }
-    }
-
-    private void verifyResultsMaxChar4Char4Char4(Allocation inV1, Allocation inV2, Allocation out, boolean relaxed) {
-        byte[] arrayInV1 = new byte[INPUTSIZE * 4];
-        inV1.copyTo(arrayInV1);
-        byte[] arrayInV2 = new byte[INPUTSIZE * 4];
-        inV2.copyTo(arrayInV2);
-        byte[] arrayOut = new byte[INPUTSIZE * 4];
-        out.copyTo(arrayOut);
-        for (int i = 0; i < INPUTSIZE; i++) {
-            for (int j = 0; j < 4 ; j++) {
-                // Extract the inputs.
-                ArgumentsCharCharChar args = new ArgumentsCharCharChar();
-                args.inV1 = arrayInV1[i * 4 + j];
-                args.inV2 = arrayInV2[i * 4 + j];
-                // Figure out what the outputs should have been.
-                Floaty.setRelaxed(relaxed);
-                CoreMathVerifier.computeMax(args);
-                // Figure out what the outputs should have been.
-                boolean valid = true;
-                if (args.out != arrayOut[i * 4 + j]) {
-                    valid = false;
-                }
-                if (!valid) {
-                    StringBuilder message = new StringBuilder();
-                    message.append("Input inV1: ");
-                    message.append(String.format("%d", args.inV1));
-                    message.append("\n");
-                    message.append("Input inV2: ");
-                    message.append(String.format("%d", args.inV2));
-                    message.append("\n");
-                    message.append("Expected output out: ");
-                    message.append(String.format("%d", args.out));
-                    message.append("\n");
-                    message.append("Actual   output out: ");
-                    message.append(String.format("%d", arrayOut[i * 4 + j]));
-                    if (args.out != arrayOut[i * 4 + j]) {
-                        message.append(" FAIL");
-                    }
-                    message.append("\n");
-                    assertTrue("Incorrect output for checkMaxChar4Char4Char4" +
-                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
-                }
-            }
-        }
-    }
-
-    private void checkMaxUchar4Uchar4Uchar4() {
-        Allocation inV1 = createRandomAllocation(mRS, Element.DataType.UNSIGNED_8, 4, 0xcfd044c684d41797l, false);
-        Allocation inV2 = createRandomAllocation(mRS, Element.DataType.UNSIGNED_8, 4, 0xcfd044c684d41798l, false);
-        try {
-            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_8, 4), INPUTSIZE);
-            script.set_gAllocInV2(inV2);
-            script.forEach_testMaxUchar4Uchar4Uchar4(inV1, out);
-            verifyResultsMaxUchar4Uchar4Uchar4(inV1, inV2, out, false);
-        } catch (Exception e) {
-            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testMaxUchar4Uchar4Uchar4: " + e.toString());
-        }
-        try {
-            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_8, 4), INPUTSIZE);
-            scriptRelaxed.set_gAllocInV2(inV2);
-            scriptRelaxed.forEach_testMaxUchar4Uchar4Uchar4(inV1, out);
-            verifyResultsMaxUchar4Uchar4Uchar4(inV1, inV2, out, true);
-        } catch (Exception e) {
-            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testMaxUchar4Uchar4Uchar4: " + e.toString());
-        }
-    }
-
-    private void verifyResultsMaxUchar4Uchar4Uchar4(Allocation inV1, Allocation inV2, Allocation out, boolean relaxed) {
-        byte[] arrayInV1 = new byte[INPUTSIZE * 4];
-        inV1.copyTo(arrayInV1);
-        byte[] arrayInV2 = new byte[INPUTSIZE * 4];
-        inV2.copyTo(arrayInV2);
-        byte[] arrayOut = new byte[INPUTSIZE * 4];
-        out.copyTo(arrayOut);
-        for (int i = 0; i < INPUTSIZE; i++) {
-            for (int j = 0; j < 4 ; j++) {
-                // Extract the inputs.
-                ArgumentsUcharUcharUchar args = new ArgumentsUcharUcharUchar();
-                args.inV1 = arrayInV1[i * 4 + j];
-                args.inV2 = arrayInV2[i * 4 + j];
-                // Figure out what the outputs should have been.
-                Floaty.setRelaxed(relaxed);
-                CoreMathVerifier.computeMax(args);
-                // Figure out what the outputs should have been.
-                boolean valid = true;
-                if (args.out != arrayOut[i * 4 + j]) {
-                    valid = false;
-                }
-                if (!valid) {
-                    StringBuilder message = new StringBuilder();
-                    message.append("Input inV1: ");
-                    message.append(String.format("0x%x", args.inV1));
-                    message.append("\n");
-                    message.append("Input inV2: ");
-                    message.append(String.format("0x%x", args.inV2));
-                    message.append("\n");
-                    message.append("Expected output out: ");
-                    message.append(String.format("0x%x", args.out));
-                    message.append("\n");
-                    message.append("Actual   output out: ");
-                    message.append(String.format("0x%x", arrayOut[i * 4 + j]));
-                    if (args.out != arrayOut[i * 4 + j]) {
-                        message.append(" FAIL");
-                    }
-                    message.append("\n");
-                    assertTrue("Incorrect output for checkMaxUchar4Uchar4Uchar4" +
-                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
-                }
-            }
-        }
-    }
-
-    private void checkMaxShort4Short4Short4() {
-        Allocation inV1 = createRandomAllocation(mRS, Element.DataType.SIGNED_16, 4, 0x97294cc83a57c818l, false);
-        Allocation inV2 = createRandomAllocation(mRS, Element.DataType.SIGNED_16, 4, 0x97294cc83a57c819l, false);
-        try {
-            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_16, 4), INPUTSIZE);
-            script.set_gAllocInV2(inV2);
-            script.forEach_testMaxShort4Short4Short4(inV1, out);
-            verifyResultsMaxShort4Short4Short4(inV1, inV2, out, false);
-        } catch (Exception e) {
-            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testMaxShort4Short4Short4: " + e.toString());
-        }
-        try {
-            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_16, 4), INPUTSIZE);
-            scriptRelaxed.set_gAllocInV2(inV2);
-            scriptRelaxed.forEach_testMaxShort4Short4Short4(inV1, out);
-            verifyResultsMaxShort4Short4Short4(inV1, inV2, out, true);
-        } catch (Exception e) {
-            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testMaxShort4Short4Short4: " + e.toString());
-        }
-    }
-
-    private void verifyResultsMaxShort4Short4Short4(Allocation inV1, Allocation inV2, Allocation out, boolean relaxed) {
-        short[] arrayInV1 = new short[INPUTSIZE * 4];
-        inV1.copyTo(arrayInV1);
-        short[] arrayInV2 = new short[INPUTSIZE * 4];
-        inV2.copyTo(arrayInV2);
-        short[] arrayOut = new short[INPUTSIZE * 4];
-        out.copyTo(arrayOut);
-        for (int i = 0; i < INPUTSIZE; i++) {
-            for (int j = 0; j < 4 ; j++) {
-                // Extract the inputs.
-                ArgumentsShortShortShort args = new ArgumentsShortShortShort();
-                args.inV1 = arrayInV1[i * 4 + j];
-                args.inV2 = arrayInV2[i * 4 + j];
-                // Figure out what the outputs should have been.
-                Floaty.setRelaxed(relaxed);
-                CoreMathVerifier.computeMax(args);
-                // Figure out what the outputs should have been.
-                boolean valid = true;
-                if (args.out != arrayOut[i * 4 + j]) {
-                    valid = false;
-                }
-                if (!valid) {
-                    StringBuilder message = new StringBuilder();
-                    message.append("Input inV1: ");
-                    message.append(String.format("%d", args.inV1));
-                    message.append("\n");
-                    message.append("Input inV2: ");
-                    message.append(String.format("%d", args.inV2));
-                    message.append("\n");
-                    message.append("Expected output out: ");
-                    message.append(String.format("%d", args.out));
-                    message.append("\n");
-                    message.append("Actual   output out: ");
-                    message.append(String.format("%d", arrayOut[i * 4 + j]));
-                    if (args.out != arrayOut[i * 4 + j]) {
-                        message.append(" FAIL");
-                    }
-                    message.append("\n");
-                    assertTrue("Incorrect output for checkMaxShort4Short4Short4" +
-                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
-                }
-            }
-        }
-    }
-
-    private void checkMaxUshort4Ushort4Ushort4() {
-        Allocation inV1 = createRandomAllocation(mRS, Element.DataType.UNSIGNED_16, 4, 0xee9efa6405c77a79l, false);
-        Allocation inV2 = createRandomAllocation(mRS, Element.DataType.UNSIGNED_16, 4, 0xee9efa6405c77a7al, false);
-        try {
-            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_16, 4), INPUTSIZE);
-            script.set_gAllocInV2(inV2);
-            script.forEach_testMaxUshort4Ushort4Ushort4(inV1, out);
-            verifyResultsMaxUshort4Ushort4Ushort4(inV1, inV2, out, false);
-        } catch (Exception e) {
-            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testMaxUshort4Ushort4Ushort4: " + e.toString());
-        }
-        try {
-            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_16, 4), INPUTSIZE);
-            scriptRelaxed.set_gAllocInV2(inV2);
-            scriptRelaxed.forEach_testMaxUshort4Ushort4Ushort4(inV1, out);
-            verifyResultsMaxUshort4Ushort4Ushort4(inV1, inV2, out, true);
-        } catch (Exception e) {
-            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testMaxUshort4Ushort4Ushort4: " + e.toString());
-        }
-    }
-
-    private void verifyResultsMaxUshort4Ushort4Ushort4(Allocation inV1, Allocation inV2, Allocation out, boolean relaxed) {
-        short[] arrayInV1 = new short[INPUTSIZE * 4];
-        inV1.copyTo(arrayInV1);
-        short[] arrayInV2 = new short[INPUTSIZE * 4];
-        inV2.copyTo(arrayInV2);
-        short[] arrayOut = new short[INPUTSIZE * 4];
-        out.copyTo(arrayOut);
-        for (int i = 0; i < INPUTSIZE; i++) {
-            for (int j = 0; j < 4 ; j++) {
-                // Extract the inputs.
-                ArgumentsUshortUshortUshort args = new ArgumentsUshortUshortUshort();
-                args.inV1 = arrayInV1[i * 4 + j];
-                args.inV2 = arrayInV2[i * 4 + j];
-                // Figure out what the outputs should have been.
-                Floaty.setRelaxed(relaxed);
-                CoreMathVerifier.computeMax(args);
-                // Figure out what the outputs should have been.
-                boolean valid = true;
-                if (args.out != arrayOut[i * 4 + j]) {
-                    valid = false;
-                }
-                if (!valid) {
-                    StringBuilder message = new StringBuilder();
-                    message.append("Input inV1: ");
-                    message.append(String.format("0x%x", args.inV1));
-                    message.append("\n");
-                    message.append("Input inV2: ");
-                    message.append(String.format("0x%x", args.inV2));
-                    message.append("\n");
-                    message.append("Expected output out: ");
-                    message.append(String.format("0x%x", args.out));
-                    message.append("\n");
-                    message.append("Actual   output out: ");
-                    message.append(String.format("0x%x", arrayOut[i * 4 + j]));
-                    if (args.out != arrayOut[i * 4 + j]) {
-                        message.append(" FAIL");
-                    }
-                    message.append("\n");
-                    assertTrue("Incorrect output for checkMaxUshort4Ushort4Ushort4" +
-                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
-                }
-            }
-        }
-    }
-
-    private void checkMaxInt4Int4Int4() {
-        Allocation inV1 = createRandomAllocation(mRS, Element.DataType.SIGNED_32, 4, 0xd0d4748aa727941bl, false);
-        Allocation inV2 = createRandomAllocation(mRS, Element.DataType.SIGNED_32, 4, 0xd0d4748aa727941cl, false);
-        try {
-            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_32, 4), INPUTSIZE);
-            script.set_gAllocInV2(inV2);
-            script.forEach_testMaxInt4Int4Int4(inV1, out);
-            verifyResultsMaxInt4Int4Int4(inV1, inV2, out, false);
-        } catch (Exception e) {
-            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testMaxInt4Int4Int4: " + e.toString());
-        }
-        try {
-            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_32, 4), INPUTSIZE);
-            scriptRelaxed.set_gAllocInV2(inV2);
-            scriptRelaxed.forEach_testMaxInt4Int4Int4(inV1, out);
-            verifyResultsMaxInt4Int4Int4(inV1, inV2, out, true);
-        } catch (Exception e) {
-            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testMaxInt4Int4Int4: " + e.toString());
-        }
-    }
-
-    private void verifyResultsMaxInt4Int4Int4(Allocation inV1, Allocation inV2, Allocation out, boolean relaxed) {
-        int[] arrayInV1 = new int[INPUTSIZE * 4];
-        inV1.copyTo(arrayInV1);
-        int[] arrayInV2 = new int[INPUTSIZE * 4];
-        inV2.copyTo(arrayInV2);
-        int[] arrayOut = new int[INPUTSIZE * 4];
-        out.copyTo(arrayOut);
-        for (int i = 0; i < INPUTSIZE; i++) {
-            for (int j = 0; j < 4 ; j++) {
-                // Extract the inputs.
-                ArgumentsIntIntInt args = new ArgumentsIntIntInt();
-                args.inV1 = arrayInV1[i * 4 + j];
-                args.inV2 = arrayInV2[i * 4 + j];
-                // Figure out what the outputs should have been.
-                Floaty.setRelaxed(relaxed);
-                CoreMathVerifier.computeMax(args);
-                // Figure out what the outputs should have been.
-                boolean valid = true;
-                if (args.out != arrayOut[i * 4 + j]) {
-                    valid = false;
-                }
-                if (!valid) {
-                    StringBuilder message = new StringBuilder();
-                    message.append("Input inV1: ");
-                    message.append(String.format("%d", args.inV1));
-                    message.append("\n");
-                    message.append("Input inV2: ");
-                    message.append(String.format("%d", args.inV2));
-                    message.append("\n");
-                    message.append("Expected output out: ");
-                    message.append(String.format("%d", args.out));
-                    message.append("\n");
-                    message.append("Actual   output out: ");
-                    message.append(String.format("%d", arrayOut[i * 4 + j]));
-                    if (args.out != arrayOut[i * 4 + j]) {
-                        message.append(" FAIL");
-                    }
-                    message.append("\n");
-                    assertTrue("Incorrect output for checkMaxInt4Int4Int4" +
-                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
-                }
-            }
-        }
-    }
-
     private void checkMaxUint4Uint4Uint4() {
         Allocation inV1 = createRandomAllocation(mRS, Element.DataType.UNSIGNED_32, 4, 0x784bfbea39bfbc2el, false);
         Allocation inV2 = createRandomAllocation(mRS, Element.DataType.UNSIGNED_32, 4, 0x784bfbea39bfbc2fl, false);
@@ -1907,7 +1907,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeMax(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 4 + j]) {
                     valid = false;
@@ -1936,34 +1936,582 @@
         }
     }
 
+    public class ArgumentsLongLongLong {
+        public long inV1;
+        public long inV2;
+        public long out;
+    }
+
+    private void checkMaxLongLongLong() {
+        Allocation inV1 = createRandomAllocation(mRS, Element.DataType.SIGNED_64, 1, 0xe224db3c7ecb92e4l, false);
+        Allocation inV2 = createRandomAllocation(mRS, Element.DataType.SIGNED_64, 1, 0xe224db3c7ecb92e5l, false);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_64, 1), INPUTSIZE);
+            script.set_gAllocInV2(inV2);
+            script.forEach_testMaxLongLongLong(inV1, out);
+            verifyResultsMaxLongLongLong(inV1, inV2, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testMaxLongLongLong: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_64, 1), INPUTSIZE);
+            scriptRelaxed.set_gAllocInV2(inV2);
+            scriptRelaxed.forEach_testMaxLongLongLong(inV1, out);
+            verifyResultsMaxLongLongLong(inV1, inV2, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testMaxLongLongLong: " + e.toString());
+        }
+    }
+
+    private void verifyResultsMaxLongLongLong(Allocation inV1, Allocation inV2, Allocation out, boolean relaxed) {
+        long[] arrayInV1 = new long[INPUTSIZE * 1];
+        inV1.copyTo(arrayInV1);
+        long[] arrayInV2 = new long[INPUTSIZE * 1];
+        inV2.copyTo(arrayInV2);
+        long[] arrayOut = new long[INPUTSIZE * 1];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 1 ; j++) {
+                // Extract the inputs.
+                ArgumentsLongLongLong args = new ArgumentsLongLongLong();
+                args.inV1 = arrayInV1[i];
+                args.inV2 = arrayInV2[i];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeMax(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (args.out != arrayOut[i * 1 + j]) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV1: ");
+                    message.append(String.format("%d", args.inV1));
+                    message.append("\n");
+                    message.append("Input inV2: ");
+                    message.append(String.format("%d", args.inV2));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(String.format("%d", args.out));
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%d", arrayOut[i * 1 + j]));
+                    if (args.out != arrayOut[i * 1 + j]) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkMaxLongLongLong" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkMaxLong2Long2Long2() {
+        Allocation inV1 = createRandomAllocation(mRS, Element.DataType.SIGNED_64, 2, 0x375f5f0ca264eb56l, false);
+        Allocation inV2 = createRandomAllocation(mRS, Element.DataType.SIGNED_64, 2, 0x375f5f0ca264eb57l, false);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_64, 2), INPUTSIZE);
+            script.set_gAllocInV2(inV2);
+            script.forEach_testMaxLong2Long2Long2(inV1, out);
+            verifyResultsMaxLong2Long2Long2(inV1, inV2, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testMaxLong2Long2Long2: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_64, 2), INPUTSIZE);
+            scriptRelaxed.set_gAllocInV2(inV2);
+            scriptRelaxed.forEach_testMaxLong2Long2Long2(inV1, out);
+            verifyResultsMaxLong2Long2Long2(inV1, inV2, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testMaxLong2Long2Long2: " + e.toString());
+        }
+    }
+
+    private void verifyResultsMaxLong2Long2Long2(Allocation inV1, Allocation inV2, Allocation out, boolean relaxed) {
+        long[] arrayInV1 = new long[INPUTSIZE * 2];
+        inV1.copyTo(arrayInV1);
+        long[] arrayInV2 = new long[INPUTSIZE * 2];
+        inV2.copyTo(arrayInV2);
+        long[] arrayOut = new long[INPUTSIZE * 2];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 2 ; j++) {
+                // Extract the inputs.
+                ArgumentsLongLongLong args = new ArgumentsLongLongLong();
+                args.inV1 = arrayInV1[i * 2 + j];
+                args.inV2 = arrayInV2[i * 2 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeMax(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (args.out != arrayOut[i * 2 + j]) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV1: ");
+                    message.append(String.format("%d", args.inV1));
+                    message.append("\n");
+                    message.append("Input inV2: ");
+                    message.append(String.format("%d", args.inV2));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(String.format("%d", args.out));
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%d", arrayOut[i * 2 + j]));
+                    if (args.out != arrayOut[i * 2 + j]) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkMaxLong2Long2Long2" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkMaxLong3Long3Long3() {
+        Allocation inV1 = createRandomAllocation(mRS, Element.DataType.SIGNED_64, 3, 0x8cb0db3f871cbe1bl, false);
+        Allocation inV2 = createRandomAllocation(mRS, Element.DataType.SIGNED_64, 3, 0x8cb0db3f871cbe1cl, false);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_64, 3), INPUTSIZE);
+            script.set_gAllocInV2(inV2);
+            script.forEach_testMaxLong3Long3Long3(inV1, out);
+            verifyResultsMaxLong3Long3Long3(inV1, inV2, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testMaxLong3Long3Long3: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_64, 3), INPUTSIZE);
+            scriptRelaxed.set_gAllocInV2(inV2);
+            scriptRelaxed.forEach_testMaxLong3Long3Long3(inV1, out);
+            verifyResultsMaxLong3Long3Long3(inV1, inV2, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testMaxLong3Long3Long3: " + e.toString());
+        }
+    }
+
+    private void verifyResultsMaxLong3Long3Long3(Allocation inV1, Allocation inV2, Allocation out, boolean relaxed) {
+        long[] arrayInV1 = new long[INPUTSIZE * 4];
+        inV1.copyTo(arrayInV1);
+        long[] arrayInV2 = new long[INPUTSIZE * 4];
+        inV2.copyTo(arrayInV2);
+        long[] arrayOut = new long[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 3 ; j++) {
+                // Extract the inputs.
+                ArgumentsLongLongLong args = new ArgumentsLongLongLong();
+                args.inV1 = arrayInV1[i * 4 + j];
+                args.inV2 = arrayInV2[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeMax(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (args.out != arrayOut[i * 4 + j]) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV1: ");
+                    message.append(String.format("%d", args.inV1));
+                    message.append("\n");
+                    message.append("Input inV2: ");
+                    message.append(String.format("%d", args.inV2));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(String.format("%d", args.out));
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%d", arrayOut[i * 4 + j]));
+                    if (args.out != arrayOut[i * 4 + j]) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkMaxLong3Long3Long3" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkMaxLong4Long4Long4() {
+        Allocation inV1 = createRandomAllocation(mRS, Element.DataType.SIGNED_64, 4, 0xe20257726bd490e0l, false);
+        Allocation inV2 = createRandomAllocation(mRS, Element.DataType.SIGNED_64, 4, 0xe20257726bd490e1l, false);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_64, 4), INPUTSIZE);
+            script.set_gAllocInV2(inV2);
+            script.forEach_testMaxLong4Long4Long4(inV1, out);
+            verifyResultsMaxLong4Long4Long4(inV1, inV2, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testMaxLong4Long4Long4: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_64, 4), INPUTSIZE);
+            scriptRelaxed.set_gAllocInV2(inV2);
+            scriptRelaxed.forEach_testMaxLong4Long4Long4(inV1, out);
+            verifyResultsMaxLong4Long4Long4(inV1, inV2, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testMaxLong4Long4Long4: " + e.toString());
+        }
+    }
+
+    private void verifyResultsMaxLong4Long4Long4(Allocation inV1, Allocation inV2, Allocation out, boolean relaxed) {
+        long[] arrayInV1 = new long[INPUTSIZE * 4];
+        inV1.copyTo(arrayInV1);
+        long[] arrayInV2 = new long[INPUTSIZE * 4];
+        inV2.copyTo(arrayInV2);
+        long[] arrayOut = new long[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 4 ; j++) {
+                // Extract the inputs.
+                ArgumentsLongLongLong args = new ArgumentsLongLongLong();
+                args.inV1 = arrayInV1[i * 4 + j];
+                args.inV2 = arrayInV2[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeMax(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (args.out != arrayOut[i * 4 + j]) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV1: ");
+                    message.append(String.format("%d", args.inV1));
+                    message.append("\n");
+                    message.append("Input inV2: ");
+                    message.append(String.format("%d", args.inV2));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(String.format("%d", args.out));
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%d", arrayOut[i * 4 + j]));
+                    if (args.out != arrayOut[i * 4 + j]) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkMaxLong4Long4Long4" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    public class ArgumentsUlongUlongUlong {
+        public long inV1;
+        public long inV2;
+        public long out;
+    }
+
+    private void checkMaxUlongUlongUlong() {
+        Allocation inV1 = createRandomAllocation(mRS, Element.DataType.UNSIGNED_64, 1, 0xb38270e909275f1dl, false);
+        Allocation inV2 = createRandomAllocation(mRS, Element.DataType.UNSIGNED_64, 1, 0xb38270e909275f1el, false);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_64, 1), INPUTSIZE);
+            script.set_gAllocInV2(inV2);
+            script.forEach_testMaxUlongUlongUlong(inV1, out);
+            verifyResultsMaxUlongUlongUlong(inV1, inV2, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testMaxUlongUlongUlong: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_64, 1), INPUTSIZE);
+            scriptRelaxed.set_gAllocInV2(inV2);
+            scriptRelaxed.forEach_testMaxUlongUlongUlong(inV1, out);
+            verifyResultsMaxUlongUlongUlong(inV1, inV2, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testMaxUlongUlongUlong: " + e.toString());
+        }
+    }
+
+    private void verifyResultsMaxUlongUlongUlong(Allocation inV1, Allocation inV2, Allocation out, boolean relaxed) {
+        long[] arrayInV1 = new long[INPUTSIZE * 1];
+        inV1.copyTo(arrayInV1);
+        long[] arrayInV2 = new long[INPUTSIZE * 1];
+        inV2.copyTo(arrayInV2);
+        long[] arrayOut = new long[INPUTSIZE * 1];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 1 ; j++) {
+                // Extract the inputs.
+                ArgumentsUlongUlongUlong args = new ArgumentsUlongUlongUlong();
+                args.inV1 = arrayInV1[i];
+                args.inV2 = arrayInV2[i];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeMax(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (args.out != arrayOut[i * 1 + j]) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV1: ");
+                    message.append(String.format("0x%x", args.inV1));
+                    message.append("\n");
+                    message.append("Input inV2: ");
+                    message.append(String.format("0x%x", args.inV2));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(String.format("0x%x", args.out));
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("0x%x", arrayOut[i * 1 + j]));
+                    if (args.out != arrayOut[i * 1 + j]) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkMaxUlongUlongUlong" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkMaxUlong2Ulong2Ulong2() {
+        Allocation inV1 = createRandomAllocation(mRS, Element.DataType.UNSIGNED_64, 2, 0x7f6c5ec5fee1a8afl, false);
+        Allocation inV2 = createRandomAllocation(mRS, Element.DataType.UNSIGNED_64, 2, 0x7f6c5ec5fee1a8b0l, false);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_64, 2), INPUTSIZE);
+            script.set_gAllocInV2(inV2);
+            script.forEach_testMaxUlong2Ulong2Ulong2(inV1, out);
+            verifyResultsMaxUlong2Ulong2Ulong2(inV1, inV2, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testMaxUlong2Ulong2Ulong2: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_64, 2), INPUTSIZE);
+            scriptRelaxed.set_gAllocInV2(inV2);
+            scriptRelaxed.forEach_testMaxUlong2Ulong2Ulong2(inV1, out);
+            verifyResultsMaxUlong2Ulong2Ulong2(inV1, inV2, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testMaxUlong2Ulong2Ulong2: " + e.toString());
+        }
+    }
+
+    private void verifyResultsMaxUlong2Ulong2Ulong2(Allocation inV1, Allocation inV2, Allocation out, boolean relaxed) {
+        long[] arrayInV1 = new long[INPUTSIZE * 2];
+        inV1.copyTo(arrayInV1);
+        long[] arrayInV2 = new long[INPUTSIZE * 2];
+        inV2.copyTo(arrayInV2);
+        long[] arrayOut = new long[INPUTSIZE * 2];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 2 ; j++) {
+                // Extract the inputs.
+                ArgumentsUlongUlongUlong args = new ArgumentsUlongUlongUlong();
+                args.inV1 = arrayInV1[i * 2 + j];
+                args.inV2 = arrayInV2[i * 2 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeMax(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (args.out != arrayOut[i * 2 + j]) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV1: ");
+                    message.append(String.format("0x%x", args.inV1));
+                    message.append("\n");
+                    message.append("Input inV2: ");
+                    message.append(String.format("0x%x", args.inV2));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(String.format("0x%x", args.out));
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("0x%x", arrayOut[i * 2 + j]));
+                    if (args.out != arrayOut[i * 2 + j]) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkMaxUlong2Ulong2Ulong2" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkMaxUlong3Ulong3Ulong3() {
+        Allocation inV1 = createRandomAllocation(mRS, Element.DataType.UNSIGNED_64, 3, 0xac5dae264f2beebal, false);
+        Allocation inV2 = createRandomAllocation(mRS, Element.DataType.UNSIGNED_64, 3, 0xac5dae264f2beebbl, false);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_64, 3), INPUTSIZE);
+            script.set_gAllocInV2(inV2);
+            script.forEach_testMaxUlong3Ulong3Ulong3(inV1, out);
+            verifyResultsMaxUlong3Ulong3Ulong3(inV1, inV2, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testMaxUlong3Ulong3Ulong3: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_64, 3), INPUTSIZE);
+            scriptRelaxed.set_gAllocInV2(inV2);
+            scriptRelaxed.forEach_testMaxUlong3Ulong3Ulong3(inV1, out);
+            verifyResultsMaxUlong3Ulong3Ulong3(inV1, inV2, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testMaxUlong3Ulong3Ulong3: " + e.toString());
+        }
+    }
+
+    private void verifyResultsMaxUlong3Ulong3Ulong3(Allocation inV1, Allocation inV2, Allocation out, boolean relaxed) {
+        long[] arrayInV1 = new long[INPUTSIZE * 4];
+        inV1.copyTo(arrayInV1);
+        long[] arrayInV2 = new long[INPUTSIZE * 4];
+        inV2.copyTo(arrayInV2);
+        long[] arrayOut = new long[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 3 ; j++) {
+                // Extract the inputs.
+                ArgumentsUlongUlongUlong args = new ArgumentsUlongUlongUlong();
+                args.inV1 = arrayInV1[i * 4 + j];
+                args.inV2 = arrayInV2[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeMax(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (args.out != arrayOut[i * 4 + j]) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV1: ");
+                    message.append(String.format("0x%x", args.inV1));
+                    message.append("\n");
+                    message.append("Input inV2: ");
+                    message.append(String.format("0x%x", args.inV2));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(String.format("0x%x", args.out));
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("0x%x", arrayOut[i * 4 + j]));
+                    if (args.out != arrayOut[i * 4 + j]) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkMaxUlong3Ulong3Ulong3" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkMaxUlong4Ulong4Ulong4() {
+        Allocation inV1 = createRandomAllocation(mRS, Element.DataType.UNSIGNED_64, 4, 0xd94efd869f7634c5l, false);
+        Allocation inV2 = createRandomAllocation(mRS, Element.DataType.UNSIGNED_64, 4, 0xd94efd869f7634c6l, false);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_64, 4), INPUTSIZE);
+            script.set_gAllocInV2(inV2);
+            script.forEach_testMaxUlong4Ulong4Ulong4(inV1, out);
+            verifyResultsMaxUlong4Ulong4Ulong4(inV1, inV2, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testMaxUlong4Ulong4Ulong4: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_64, 4), INPUTSIZE);
+            scriptRelaxed.set_gAllocInV2(inV2);
+            scriptRelaxed.forEach_testMaxUlong4Ulong4Ulong4(inV1, out);
+            verifyResultsMaxUlong4Ulong4Ulong4(inV1, inV2, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testMaxUlong4Ulong4Ulong4: " + e.toString());
+        }
+    }
+
+    private void verifyResultsMaxUlong4Ulong4Ulong4(Allocation inV1, Allocation inV2, Allocation out, boolean relaxed) {
+        long[] arrayInV1 = new long[INPUTSIZE * 4];
+        inV1.copyTo(arrayInV1);
+        long[] arrayInV2 = new long[INPUTSIZE * 4];
+        inV2.copyTo(arrayInV2);
+        long[] arrayOut = new long[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 4 ; j++) {
+                // Extract the inputs.
+                ArgumentsUlongUlongUlong args = new ArgumentsUlongUlongUlong();
+                args.inV1 = arrayInV1[i * 4 + j];
+                args.inV2 = arrayInV2[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeMax(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (args.out != arrayOut[i * 4 + j]) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV1: ");
+                    message.append(String.format("0x%x", args.inV1));
+                    message.append("\n");
+                    message.append("Input inV2: ");
+                    message.append(String.format("0x%x", args.inV2));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(String.format("0x%x", args.out));
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("0x%x", arrayOut[i * 4 + j]));
+                    if (args.out != arrayOut[i * 4 + j]) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkMaxUlong4Ulong4Ulong4" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
     public void testMax() {
         checkMaxFloatFloatFloat();
         checkMaxFloat2Float2Float2();
         checkMaxFloat3Float3Float3();
         checkMaxFloat4Float4Float4();
         checkMaxCharCharChar();
-        checkMaxUcharUcharUchar();
-        checkMaxShortShortShort();
-        checkMaxUshortUshortUshort();
-        checkMaxIntIntInt();
-        checkMaxUintUintUint();
         checkMaxChar2Char2Char2();
-        checkMaxUchar2Uchar2Uchar2();
-        checkMaxShort2Short2Short2();
-        checkMaxUshort2Ushort2Ushort2();
-        checkMaxInt2Int2Int2();
-        checkMaxUint2Uint2Uint2();
         checkMaxChar3Char3Char3();
-        checkMaxUchar3Uchar3Uchar3();
-        checkMaxShort3Short3Short3();
-        checkMaxUshort3Ushort3Ushort3();
-        checkMaxInt3Int3Int3();
-        checkMaxUint3Uint3Uint3();
         checkMaxChar4Char4Char4();
+        checkMaxUcharUcharUchar();
+        checkMaxUchar2Uchar2Uchar2();
+        checkMaxUchar3Uchar3Uchar3();
         checkMaxUchar4Uchar4Uchar4();
+        checkMaxShortShortShort();
+        checkMaxShort2Short2Short2();
+        checkMaxShort3Short3Short3();
         checkMaxShort4Short4Short4();
+        checkMaxUshortUshortUshort();
+        checkMaxUshort2Ushort2Ushort2();
+        checkMaxUshort3Ushort3Ushort3();
         checkMaxUshort4Ushort4Ushort4();
+        checkMaxIntIntInt();
+        checkMaxInt2Int2Int2();
+        checkMaxInt3Int3Int3();
         checkMaxInt4Int4Int4();
+        checkMaxUintUintUint();
+        checkMaxUint2Uint2Uint2();
+        checkMaxUint3Uint3Uint3();
         checkMaxUint4Uint4Uint4();
+        checkMaxLongLongLong();
+        checkMaxLong2Long2Long2();
+        checkMaxLong3Long3Long3();
+        checkMaxLong4Long4Long4();
+        checkMaxUlongUlongUlong();
+        checkMaxUlong2Ulong2Ulong2();
+        checkMaxUlong3Ulong3Ulong3();
+        checkMaxUlong4Ulong4Ulong4();
     }
 }
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/TestMin.java b/tests/tests/renderscript/src/android/renderscript/cts/TestMin.java
index 08434f3..6765c20 100644
--- a/tests/tests/renderscript/src/android/renderscript/cts/TestMin.java
+++ b/tests/tests/renderscript/src/android/renderscript/cts/TestMin.java
@@ -77,7 +77,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeMin(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 1 + j])) {
                     valid = false;
@@ -146,7 +146,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeMin(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 2 + j])) {
                     valid = false;
@@ -215,7 +215,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeMin(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 4 + j])) {
                     valid = false;
@@ -284,7 +284,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeMin(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 4 + j])) {
                     valid = false;
@@ -359,7 +359,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeMin(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 1 + j]) {
                     valid = false;
@@ -388,6 +388,204 @@
         }
     }
 
+    private void checkMinChar2Char2Char2() {
+        Allocation inV1 = createRandomAllocation(mRS, Element.DataType.SIGNED_8, 2, 0xec4705afc03447ael, false);
+        Allocation inV2 = createRandomAllocation(mRS, Element.DataType.SIGNED_8, 2, 0xec4705afc03447afl, false);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_8, 2), INPUTSIZE);
+            script.set_gAllocInV2(inV2);
+            script.forEach_testMinChar2Char2Char2(inV1, out);
+            verifyResultsMinChar2Char2Char2(inV1, inV2, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testMinChar2Char2Char2: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_8, 2), INPUTSIZE);
+            scriptRelaxed.set_gAllocInV2(inV2);
+            scriptRelaxed.forEach_testMinChar2Char2Char2(inV1, out);
+            verifyResultsMinChar2Char2Char2(inV1, inV2, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testMinChar2Char2Char2: " + e.toString());
+        }
+    }
+
+    private void verifyResultsMinChar2Char2Char2(Allocation inV1, Allocation inV2, Allocation out, boolean relaxed) {
+        byte[] arrayInV1 = new byte[INPUTSIZE * 2];
+        inV1.copyTo(arrayInV1);
+        byte[] arrayInV2 = new byte[INPUTSIZE * 2];
+        inV2.copyTo(arrayInV2);
+        byte[] arrayOut = new byte[INPUTSIZE * 2];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 2 ; j++) {
+                // Extract the inputs.
+                ArgumentsCharCharChar args = new ArgumentsCharCharChar();
+                args.inV1 = arrayInV1[i * 2 + j];
+                args.inV2 = arrayInV2[i * 2 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeMin(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (args.out != arrayOut[i * 2 + j]) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV1: ");
+                    message.append(String.format("%d", args.inV1));
+                    message.append("\n");
+                    message.append("Input inV2: ");
+                    message.append(String.format("%d", args.inV2));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(String.format("%d", args.out));
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%d", arrayOut[i * 2 + j]));
+                    if (args.out != arrayOut[i * 2 + j]) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkMinChar2Char2Char2" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkMinChar3Char3Char3() {
+        Allocation inV1 = createRandomAllocation(mRS, Element.DataType.SIGNED_8, 3, 0x419881e2a4ec1a73l, false);
+        Allocation inV2 = createRandomAllocation(mRS, Element.DataType.SIGNED_8, 3, 0x419881e2a4ec1a74l, false);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_8, 3), INPUTSIZE);
+            script.set_gAllocInV2(inV2);
+            script.forEach_testMinChar3Char3Char3(inV1, out);
+            verifyResultsMinChar3Char3Char3(inV1, inV2, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testMinChar3Char3Char3: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_8, 3), INPUTSIZE);
+            scriptRelaxed.set_gAllocInV2(inV2);
+            scriptRelaxed.forEach_testMinChar3Char3Char3(inV1, out);
+            verifyResultsMinChar3Char3Char3(inV1, inV2, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testMinChar3Char3Char3: " + e.toString());
+        }
+    }
+
+    private void verifyResultsMinChar3Char3Char3(Allocation inV1, Allocation inV2, Allocation out, boolean relaxed) {
+        byte[] arrayInV1 = new byte[INPUTSIZE * 4];
+        inV1.copyTo(arrayInV1);
+        byte[] arrayInV2 = new byte[INPUTSIZE * 4];
+        inV2.copyTo(arrayInV2);
+        byte[] arrayOut = new byte[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 3 ; j++) {
+                // Extract the inputs.
+                ArgumentsCharCharChar args = new ArgumentsCharCharChar();
+                args.inV1 = arrayInV1[i * 4 + j];
+                args.inV2 = arrayInV2[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeMin(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (args.out != arrayOut[i * 4 + j]) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV1: ");
+                    message.append(String.format("%d", args.inV1));
+                    message.append("\n");
+                    message.append("Input inV2: ");
+                    message.append(String.format("%d", args.inV2));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(String.format("%d", args.out));
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%d", arrayOut[i * 4 + j]));
+                    if (args.out != arrayOut[i * 4 + j]) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkMinChar3Char3Char3" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkMinChar4Char4Char4() {
+        Allocation inV1 = createRandomAllocation(mRS, Element.DataType.SIGNED_8, 4, 0x96e9fe1589a3ed38l, false);
+        Allocation inV2 = createRandomAllocation(mRS, Element.DataType.SIGNED_8, 4, 0x96e9fe1589a3ed39l, false);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_8, 4), INPUTSIZE);
+            script.set_gAllocInV2(inV2);
+            script.forEach_testMinChar4Char4Char4(inV1, out);
+            verifyResultsMinChar4Char4Char4(inV1, inV2, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testMinChar4Char4Char4: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_8, 4), INPUTSIZE);
+            scriptRelaxed.set_gAllocInV2(inV2);
+            scriptRelaxed.forEach_testMinChar4Char4Char4(inV1, out);
+            verifyResultsMinChar4Char4Char4(inV1, inV2, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testMinChar4Char4Char4: " + e.toString());
+        }
+    }
+
+    private void verifyResultsMinChar4Char4Char4(Allocation inV1, Allocation inV2, Allocation out, boolean relaxed) {
+        byte[] arrayInV1 = new byte[INPUTSIZE * 4];
+        inV1.copyTo(arrayInV1);
+        byte[] arrayInV2 = new byte[INPUTSIZE * 4];
+        inV2.copyTo(arrayInV2);
+        byte[] arrayOut = new byte[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 4 ; j++) {
+                // Extract the inputs.
+                ArgumentsCharCharChar args = new ArgumentsCharCharChar();
+                args.inV1 = arrayInV1[i * 4 + j];
+                args.inV2 = arrayInV2[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeMin(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (args.out != arrayOut[i * 4 + j]) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV1: ");
+                    message.append(String.format("%d", args.inV1));
+                    message.append("\n");
+                    message.append("Input inV2: ");
+                    message.append(String.format("%d", args.inV2));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(String.format("%d", args.out));
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%d", arrayOut[i * 4 + j]));
+                    if (args.out != arrayOut[i * 4 + j]) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkMinChar4Char4Char4" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
     public class ArgumentsUcharUcharUchar {
         public byte inV1;
         public byte inV2;
@@ -431,7 +629,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeMin(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 1 + j]) {
                     valid = false;
@@ -460,6 +658,204 @@
         }
     }
 
+    private void checkMinUchar2Uchar2Uchar2() {
+        Allocation inV1 = createRandomAllocation(mRS, Element.DataType.UNSIGNED_8, 2, 0x1d3c921d166e22ffl, false);
+        Allocation inV2 = createRandomAllocation(mRS, Element.DataType.UNSIGNED_8, 2, 0x1d3c921d166e2300l, false);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_8, 2), INPUTSIZE);
+            script.set_gAllocInV2(inV2);
+            script.forEach_testMinUchar2Uchar2Uchar2(inV1, out);
+            verifyResultsMinUchar2Uchar2Uchar2(inV1, inV2, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testMinUchar2Uchar2Uchar2: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_8, 2), INPUTSIZE);
+            scriptRelaxed.set_gAllocInV2(inV2);
+            scriptRelaxed.forEach_testMinUchar2Uchar2Uchar2(inV1, out);
+            verifyResultsMinUchar2Uchar2Uchar2(inV1, inV2, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testMinUchar2Uchar2Uchar2: " + e.toString());
+        }
+    }
+
+    private void verifyResultsMinUchar2Uchar2Uchar2(Allocation inV1, Allocation inV2, Allocation out, boolean relaxed) {
+        byte[] arrayInV1 = new byte[INPUTSIZE * 2];
+        inV1.copyTo(arrayInV1);
+        byte[] arrayInV2 = new byte[INPUTSIZE * 2];
+        inV2.copyTo(arrayInV2);
+        byte[] arrayOut = new byte[INPUTSIZE * 2];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 2 ; j++) {
+                // Extract the inputs.
+                ArgumentsUcharUcharUchar args = new ArgumentsUcharUcharUchar();
+                args.inV1 = arrayInV1[i * 2 + j];
+                args.inV2 = arrayInV2[i * 2 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeMin(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (args.out != arrayOut[i * 2 + j]) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV1: ");
+                    message.append(String.format("0x%x", args.inV1));
+                    message.append("\n");
+                    message.append("Input inV2: ");
+                    message.append(String.format("0x%x", args.inV2));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(String.format("0x%x", args.out));
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("0x%x", arrayOut[i * 2 + j]));
+                    if (args.out != arrayOut[i * 2 + j]) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkMinUchar2Uchar2Uchar2" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkMinUchar3Uchar3Uchar3() {
+        Allocation inV1 = createRandomAllocation(mRS, Element.DataType.UNSIGNED_8, 3, 0x4a2de17d66b8690al, false);
+        Allocation inV2 = createRandomAllocation(mRS, Element.DataType.UNSIGNED_8, 3, 0x4a2de17d66b8690bl, false);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_8, 3), INPUTSIZE);
+            script.set_gAllocInV2(inV2);
+            script.forEach_testMinUchar3Uchar3Uchar3(inV1, out);
+            verifyResultsMinUchar3Uchar3Uchar3(inV1, inV2, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testMinUchar3Uchar3Uchar3: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_8, 3), INPUTSIZE);
+            scriptRelaxed.set_gAllocInV2(inV2);
+            scriptRelaxed.forEach_testMinUchar3Uchar3Uchar3(inV1, out);
+            verifyResultsMinUchar3Uchar3Uchar3(inV1, inV2, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testMinUchar3Uchar3Uchar3: " + e.toString());
+        }
+    }
+
+    private void verifyResultsMinUchar3Uchar3Uchar3(Allocation inV1, Allocation inV2, Allocation out, boolean relaxed) {
+        byte[] arrayInV1 = new byte[INPUTSIZE * 4];
+        inV1.copyTo(arrayInV1);
+        byte[] arrayInV2 = new byte[INPUTSIZE * 4];
+        inV2.copyTo(arrayInV2);
+        byte[] arrayOut = new byte[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 3 ; j++) {
+                // Extract the inputs.
+                ArgumentsUcharUcharUchar args = new ArgumentsUcharUcharUchar();
+                args.inV1 = arrayInV1[i * 4 + j];
+                args.inV2 = arrayInV2[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeMin(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (args.out != arrayOut[i * 4 + j]) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV1: ");
+                    message.append(String.format("0x%x", args.inV1));
+                    message.append("\n");
+                    message.append("Input inV2: ");
+                    message.append(String.format("0x%x", args.inV2));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(String.format("0x%x", args.out));
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("0x%x", arrayOut[i * 4 + j]));
+                    if (args.out != arrayOut[i * 4 + j]) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkMinUchar3Uchar3Uchar3" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkMinUchar4Uchar4Uchar4() {
+        Allocation inV1 = createRandomAllocation(mRS, Element.DataType.UNSIGNED_8, 4, 0x771f30ddb702af15l, false);
+        Allocation inV2 = createRandomAllocation(mRS, Element.DataType.UNSIGNED_8, 4, 0x771f30ddb702af16l, false);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_8, 4), INPUTSIZE);
+            script.set_gAllocInV2(inV2);
+            script.forEach_testMinUchar4Uchar4Uchar4(inV1, out);
+            verifyResultsMinUchar4Uchar4Uchar4(inV1, inV2, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testMinUchar4Uchar4Uchar4: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_8, 4), INPUTSIZE);
+            scriptRelaxed.set_gAllocInV2(inV2);
+            scriptRelaxed.forEach_testMinUchar4Uchar4Uchar4(inV1, out);
+            verifyResultsMinUchar4Uchar4Uchar4(inV1, inV2, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testMinUchar4Uchar4Uchar4: " + e.toString());
+        }
+    }
+
+    private void verifyResultsMinUchar4Uchar4Uchar4(Allocation inV1, Allocation inV2, Allocation out, boolean relaxed) {
+        byte[] arrayInV1 = new byte[INPUTSIZE * 4];
+        inV1.copyTo(arrayInV1);
+        byte[] arrayInV2 = new byte[INPUTSIZE * 4];
+        inV2.copyTo(arrayInV2);
+        byte[] arrayOut = new byte[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 4 ; j++) {
+                // Extract the inputs.
+                ArgumentsUcharUcharUchar args = new ArgumentsUcharUcharUchar();
+                args.inV1 = arrayInV1[i * 4 + j];
+                args.inV2 = arrayInV2[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeMin(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (args.out != arrayOut[i * 4 + j]) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV1: ");
+                    message.append(String.format("0x%x", args.inV1));
+                    message.append("\n");
+                    message.append("Input inV2: ");
+                    message.append(String.format("0x%x", args.inV2));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(String.format("0x%x", args.out));
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("0x%x", arrayOut[i * 4 + j]));
+                    if (args.out != arrayOut[i * 4 + j]) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkMinUchar4Uchar4Uchar4" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
     public class ArgumentsShortShortShort {
         public short inV1;
         public short inV2;
@@ -503,7 +899,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeMin(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 1 + j]) {
                     valid = false;
@@ -532,6 +928,204 @@
         }
     }
 
+    private void checkMinShort2Short2Short2() {
+        Allocation inV1 = createRandomAllocation(mRS, Element.DataType.SIGNED_16, 2, 0xe4959a1ecbf1d380l, false);
+        Allocation inV2 = createRandomAllocation(mRS, Element.DataType.SIGNED_16, 2, 0xe4959a1ecbf1d381l, false);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_16, 2), INPUTSIZE);
+            script.set_gAllocInV2(inV2);
+            script.forEach_testMinShort2Short2Short2(inV1, out);
+            verifyResultsMinShort2Short2Short2(inV1, inV2, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testMinShort2Short2Short2: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_16, 2), INPUTSIZE);
+            scriptRelaxed.set_gAllocInV2(inV2);
+            scriptRelaxed.forEach_testMinShort2Short2Short2(inV1, out);
+            verifyResultsMinShort2Short2Short2(inV1, inV2, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testMinShort2Short2Short2: " + e.toString());
+        }
+    }
+
+    private void verifyResultsMinShort2Short2Short2(Allocation inV1, Allocation inV2, Allocation out, boolean relaxed) {
+        short[] arrayInV1 = new short[INPUTSIZE * 2];
+        inV1.copyTo(arrayInV1);
+        short[] arrayInV2 = new short[INPUTSIZE * 2];
+        inV2.copyTo(arrayInV2);
+        short[] arrayOut = new short[INPUTSIZE * 2];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 2 ; j++) {
+                // Extract the inputs.
+                ArgumentsShortShortShort args = new ArgumentsShortShortShort();
+                args.inV1 = arrayInV1[i * 2 + j];
+                args.inV2 = arrayInV2[i * 2 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeMin(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (args.out != arrayOut[i * 2 + j]) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV1: ");
+                    message.append(String.format("%d", args.inV1));
+                    message.append("\n");
+                    message.append("Input inV2: ");
+                    message.append(String.format("%d", args.inV2));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(String.format("%d", args.out));
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%d", arrayOut[i * 2 + j]));
+                    if (args.out != arrayOut[i * 2 + j]) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkMinShort2Short2Short2" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkMinShort3Short3Short3() {
+        Allocation inV1 = createRandomAllocation(mRS, Element.DataType.SIGNED_16, 3, 0x1186e97f1c3c198bl, false);
+        Allocation inV2 = createRandomAllocation(mRS, Element.DataType.SIGNED_16, 3, 0x1186e97f1c3c198cl, false);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_16, 3), INPUTSIZE);
+            script.set_gAllocInV2(inV2);
+            script.forEach_testMinShort3Short3Short3(inV1, out);
+            verifyResultsMinShort3Short3Short3(inV1, inV2, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testMinShort3Short3Short3: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_16, 3), INPUTSIZE);
+            scriptRelaxed.set_gAllocInV2(inV2);
+            scriptRelaxed.forEach_testMinShort3Short3Short3(inV1, out);
+            verifyResultsMinShort3Short3Short3(inV1, inV2, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testMinShort3Short3Short3: " + e.toString());
+        }
+    }
+
+    private void verifyResultsMinShort3Short3Short3(Allocation inV1, Allocation inV2, Allocation out, boolean relaxed) {
+        short[] arrayInV1 = new short[INPUTSIZE * 4];
+        inV1.copyTo(arrayInV1);
+        short[] arrayInV2 = new short[INPUTSIZE * 4];
+        inV2.copyTo(arrayInV2);
+        short[] arrayOut = new short[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 3 ; j++) {
+                // Extract the inputs.
+                ArgumentsShortShortShort args = new ArgumentsShortShortShort();
+                args.inV1 = arrayInV1[i * 4 + j];
+                args.inV2 = arrayInV2[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeMin(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (args.out != arrayOut[i * 4 + j]) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV1: ");
+                    message.append(String.format("%d", args.inV1));
+                    message.append("\n");
+                    message.append("Input inV2: ");
+                    message.append(String.format("%d", args.inV2));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(String.format("%d", args.out));
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%d", arrayOut[i * 4 + j]));
+                    if (args.out != arrayOut[i * 4 + j]) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkMinShort3Short3Short3" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkMinShort4Short4Short4() {
+        Allocation inV1 = createRandomAllocation(mRS, Element.DataType.SIGNED_16, 4, 0x3e7838df6c865f96l, false);
+        Allocation inV2 = createRandomAllocation(mRS, Element.DataType.SIGNED_16, 4, 0x3e7838df6c865f97l, false);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_16, 4), INPUTSIZE);
+            script.set_gAllocInV2(inV2);
+            script.forEach_testMinShort4Short4Short4(inV1, out);
+            verifyResultsMinShort4Short4Short4(inV1, inV2, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testMinShort4Short4Short4: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_16, 4), INPUTSIZE);
+            scriptRelaxed.set_gAllocInV2(inV2);
+            scriptRelaxed.forEach_testMinShort4Short4Short4(inV1, out);
+            verifyResultsMinShort4Short4Short4(inV1, inV2, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testMinShort4Short4Short4: " + e.toString());
+        }
+    }
+
+    private void verifyResultsMinShort4Short4Short4(Allocation inV1, Allocation inV2, Allocation out, boolean relaxed) {
+        short[] arrayInV1 = new short[INPUTSIZE * 4];
+        inV1.copyTo(arrayInV1);
+        short[] arrayInV2 = new short[INPUTSIZE * 4];
+        inV2.copyTo(arrayInV2);
+        short[] arrayOut = new short[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 4 ; j++) {
+                // Extract the inputs.
+                ArgumentsShortShortShort args = new ArgumentsShortShortShort();
+                args.inV1 = arrayInV1[i * 4 + j];
+                args.inV2 = arrayInV2[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeMin(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (args.out != arrayOut[i * 4 + j]) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV1: ");
+                    message.append(String.format("%d", args.inV1));
+                    message.append("\n");
+                    message.append("Input inV2: ");
+                    message.append(String.format("%d", args.inV2));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(String.format("%d", args.out));
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%d", arrayOut[i * 4 + j]));
+                    if (args.out != arrayOut[i * 4 + j]) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkMinShort4Short4Short4" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
     public class ArgumentsUshortUshortUshort {
         public short inV1;
         public short inV2;
@@ -575,7 +1169,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeMin(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 1 + j]) {
                     valid = false;
@@ -604,6 +1198,204 @@
         }
     }
 
+    private void checkMinUshort2Ushort2Ushort2() {
+        Allocation inV1 = createRandomAllocation(mRS, Element.DataType.UNSIGNED_16, 2, 0x98573ebbc511e319l, false);
+        Allocation inV2 = createRandomAllocation(mRS, Element.DataType.UNSIGNED_16, 2, 0x98573ebbc511e31al, false);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_16, 2), INPUTSIZE);
+            script.set_gAllocInV2(inV2);
+            script.forEach_testMinUshort2Ushort2Ushort2(inV1, out);
+            verifyResultsMinUshort2Ushort2Ushort2(inV1, inV2, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testMinUshort2Ushort2Ushort2: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_16, 2), INPUTSIZE);
+            scriptRelaxed.set_gAllocInV2(inV2);
+            scriptRelaxed.forEach_testMinUshort2Ushort2Ushort2(inV1, out);
+            verifyResultsMinUshort2Ushort2Ushort2(inV1, inV2, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testMinUshort2Ushort2Ushort2: " + e.toString());
+        }
+    }
+
+    private void verifyResultsMinUshort2Ushort2Ushort2(Allocation inV1, Allocation inV2, Allocation out, boolean relaxed) {
+        short[] arrayInV1 = new short[INPUTSIZE * 2];
+        inV1.copyTo(arrayInV1);
+        short[] arrayInV2 = new short[INPUTSIZE * 2];
+        inV2.copyTo(arrayInV2);
+        short[] arrayOut = new short[INPUTSIZE * 2];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 2 ; j++) {
+                // Extract the inputs.
+                ArgumentsUshortUshortUshort args = new ArgumentsUshortUshortUshort();
+                args.inV1 = arrayInV1[i * 2 + j];
+                args.inV2 = arrayInV2[i * 2 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeMin(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (args.out != arrayOut[i * 2 + j]) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV1: ");
+                    message.append(String.format("0x%x", args.inV1));
+                    message.append("\n");
+                    message.append("Input inV2: ");
+                    message.append(String.format("0x%x", args.inV2));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(String.format("0x%x", args.out));
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("0x%x", arrayOut[i * 2 + j]));
+                    if (args.out != arrayOut[i * 2 + j]) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkMinUshort2Ushort2Ushort2" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkMinUshort3Ushort3Ushort3() {
+        Allocation inV1 = createRandomAllocation(mRS, Element.DataType.UNSIGNED_16, 3, 0x1595f09b03867776l, false);
+        Allocation inV2 = createRandomAllocation(mRS, Element.DataType.UNSIGNED_16, 3, 0x1595f09b03867777l, false);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_16, 3), INPUTSIZE);
+            script.set_gAllocInV2(inV2);
+            script.forEach_testMinUshort3Ushort3Ushort3(inV1, out);
+            verifyResultsMinUshort3Ushort3Ushort3(inV1, inV2, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testMinUshort3Ushort3Ushort3: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_16, 3), INPUTSIZE);
+            scriptRelaxed.set_gAllocInV2(inV2);
+            scriptRelaxed.forEach_testMinUshort3Ushort3Ushort3(inV1, out);
+            verifyResultsMinUshort3Ushort3Ushort3(inV1, inV2, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testMinUshort3Ushort3Ushort3: " + e.toString());
+        }
+    }
+
+    private void verifyResultsMinUshort3Ushort3Ushort3(Allocation inV1, Allocation inV2, Allocation out, boolean relaxed) {
+        short[] arrayInV1 = new short[INPUTSIZE * 4];
+        inV1.copyTo(arrayInV1);
+        short[] arrayInV2 = new short[INPUTSIZE * 4];
+        inV2.copyTo(arrayInV2);
+        short[] arrayOut = new short[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 3 ; j++) {
+                // Extract the inputs.
+                ArgumentsUshortUshortUshort args = new ArgumentsUshortUshortUshort();
+                args.inV1 = arrayInV1[i * 4 + j];
+                args.inV2 = arrayInV2[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeMin(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (args.out != arrayOut[i * 4 + j]) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV1: ");
+                    message.append(String.format("0x%x", args.inV1));
+                    message.append("\n");
+                    message.append("Input inV2: ");
+                    message.append(String.format("0x%x", args.inV2));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(String.format("0x%x", args.out));
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("0x%x", arrayOut[i * 4 + j]));
+                    if (args.out != arrayOut[i * 4 + j]) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkMinUshort3Ushort3Ushort3" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkMinUshort4Ushort4Ushort4() {
+        Allocation inV1 = createRandomAllocation(mRS, Element.DataType.UNSIGNED_16, 4, 0x92d4a27a41fb0bd3l, false);
+        Allocation inV2 = createRandomAllocation(mRS, Element.DataType.UNSIGNED_16, 4, 0x92d4a27a41fb0bd4l, false);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_16, 4), INPUTSIZE);
+            script.set_gAllocInV2(inV2);
+            script.forEach_testMinUshort4Ushort4Ushort4(inV1, out);
+            verifyResultsMinUshort4Ushort4Ushort4(inV1, inV2, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testMinUshort4Ushort4Ushort4: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_16, 4), INPUTSIZE);
+            scriptRelaxed.set_gAllocInV2(inV2);
+            scriptRelaxed.forEach_testMinUshort4Ushort4Ushort4(inV1, out);
+            verifyResultsMinUshort4Ushort4Ushort4(inV1, inV2, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testMinUshort4Ushort4Ushort4: " + e.toString());
+        }
+    }
+
+    private void verifyResultsMinUshort4Ushort4Ushort4(Allocation inV1, Allocation inV2, Allocation out, boolean relaxed) {
+        short[] arrayInV1 = new short[INPUTSIZE * 4];
+        inV1.copyTo(arrayInV1);
+        short[] arrayInV2 = new short[INPUTSIZE * 4];
+        inV2.copyTo(arrayInV2);
+        short[] arrayOut = new short[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 4 ; j++) {
+                // Extract the inputs.
+                ArgumentsUshortUshortUshort args = new ArgumentsUshortUshortUshort();
+                args.inV1 = arrayInV1[i * 4 + j];
+                args.inV2 = arrayInV2[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeMin(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (args.out != arrayOut[i * 4 + j]) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV1: ");
+                    message.append(String.format("0x%x", args.inV1));
+                    message.append("\n");
+                    message.append("Input inV2: ");
+                    message.append(String.format("0x%x", args.inV2));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(String.format("0x%x", args.out));
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("0x%x", arrayOut[i * 4 + j]));
+                    if (args.out != arrayOut[i * 4 + j]) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkMinUshort4Ushort4Ushort4" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
     public class ArgumentsIntIntInt {
         public int inV1;
         public int inV2;
@@ -647,7 +1439,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeMin(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 1 + j]) {
                     valid = false;
@@ -676,6 +1468,204 @@
         }
     }
 
+    private void checkMinInt2Int2Int2() {
+        Allocation inV1 = createRandomAllocation(mRS, Element.DataType.SIGNED_32, 2, 0xba635b605676e7a3l, false);
+        Allocation inV2 = createRandomAllocation(mRS, Element.DataType.SIGNED_32, 2, 0xba635b605676e7a4l, false);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_32, 2), INPUTSIZE);
+            script.set_gAllocInV2(inV2);
+            script.forEach_testMinInt2Int2Int2(inV1, out);
+            verifyResultsMinInt2Int2Int2(inV1, inV2, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testMinInt2Int2Int2: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_32, 2), INPUTSIZE);
+            scriptRelaxed.set_gAllocInV2(inV2);
+            scriptRelaxed.forEach_testMinInt2Int2Int2(inV1, out);
+            verifyResultsMinInt2Int2Int2(inV1, inV2, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testMinInt2Int2Int2: " + e.toString());
+        }
+    }
+
+    private void verifyResultsMinInt2Int2Int2(Allocation inV1, Allocation inV2, Allocation out, boolean relaxed) {
+        int[] arrayInV1 = new int[INPUTSIZE * 2];
+        inV1.copyTo(arrayInV1);
+        int[] arrayInV2 = new int[INPUTSIZE * 2];
+        inV2.copyTo(arrayInV2);
+        int[] arrayOut = new int[INPUTSIZE * 2];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 2 ; j++) {
+                // Extract the inputs.
+                ArgumentsIntIntInt args = new ArgumentsIntIntInt();
+                args.inV1 = arrayInV1[i * 2 + j];
+                args.inV2 = arrayInV2[i * 2 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeMin(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (args.out != arrayOut[i * 2 + j]) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV1: ");
+                    message.append(String.format("%d", args.inV1));
+                    message.append("\n");
+                    message.append("Input inV2: ");
+                    message.append(String.format("%d", args.inV2));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(String.format("%d", args.out));
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%d", arrayOut[i * 2 + j]));
+                    if (args.out != arrayOut[i * 2 + j]) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkMinInt2Int2Int2" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkMinInt3Int3Int3() {
+        Allocation inV1 = createRandomAllocation(mRS, Element.DataType.SIGNED_32, 3, 0xe4f086806849fbc6l, false);
+        Allocation inV2 = createRandomAllocation(mRS, Element.DataType.SIGNED_32, 3, 0xe4f086806849fbc7l, false);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_32, 3), INPUTSIZE);
+            script.set_gAllocInV2(inV2);
+            script.forEach_testMinInt3Int3Int3(inV1, out);
+            verifyResultsMinInt3Int3Int3(inV1, inV2, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testMinInt3Int3Int3: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_32, 3), INPUTSIZE);
+            scriptRelaxed.set_gAllocInV2(inV2);
+            scriptRelaxed.forEach_testMinInt3Int3Int3(inV1, out);
+            verifyResultsMinInt3Int3Int3(inV1, inV2, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testMinInt3Int3Int3: " + e.toString());
+        }
+    }
+
+    private void verifyResultsMinInt3Int3Int3(Allocation inV1, Allocation inV2, Allocation out, boolean relaxed) {
+        int[] arrayInV1 = new int[INPUTSIZE * 4];
+        inV1.copyTo(arrayInV1);
+        int[] arrayInV2 = new int[INPUTSIZE * 4];
+        inV2.copyTo(arrayInV2);
+        int[] arrayOut = new int[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 3 ; j++) {
+                // Extract the inputs.
+                ArgumentsIntIntInt args = new ArgumentsIntIntInt();
+                args.inV1 = arrayInV1[i * 4 + j];
+                args.inV2 = arrayInV2[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeMin(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (args.out != arrayOut[i * 4 + j]) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV1: ");
+                    message.append(String.format("%d", args.inV1));
+                    message.append("\n");
+                    message.append("Input inV2: ");
+                    message.append(String.format("%d", args.inV2));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(String.format("%d", args.out));
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%d", arrayOut[i * 4 + j]));
+                    if (args.out != arrayOut[i * 4 + j]) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkMinInt3Int3Int3" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkMinInt4Int4Int4() {
+        Allocation inV1 = createRandomAllocation(mRS, Element.DataType.SIGNED_32, 4, 0xf7db1a07a1d0fe9l, false);
+        Allocation inV2 = createRandomAllocation(mRS, Element.DataType.SIGNED_32, 4, 0xf7db1a07a1d0feal, false);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_32, 4), INPUTSIZE);
+            script.set_gAllocInV2(inV2);
+            script.forEach_testMinInt4Int4Int4(inV1, out);
+            verifyResultsMinInt4Int4Int4(inV1, inV2, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testMinInt4Int4Int4: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_32, 4), INPUTSIZE);
+            scriptRelaxed.set_gAllocInV2(inV2);
+            scriptRelaxed.forEach_testMinInt4Int4Int4(inV1, out);
+            verifyResultsMinInt4Int4Int4(inV1, inV2, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testMinInt4Int4Int4: " + e.toString());
+        }
+    }
+
+    private void verifyResultsMinInt4Int4Int4(Allocation inV1, Allocation inV2, Allocation out, boolean relaxed) {
+        int[] arrayInV1 = new int[INPUTSIZE * 4];
+        inV1.copyTo(arrayInV1);
+        int[] arrayInV2 = new int[INPUTSIZE * 4];
+        inV2.copyTo(arrayInV2);
+        int[] arrayOut = new int[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 4 ; j++) {
+                // Extract the inputs.
+                ArgumentsIntIntInt args = new ArgumentsIntIntInt();
+                args.inV1 = arrayInV1[i * 4 + j];
+                args.inV2 = arrayInV2[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeMin(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (args.out != arrayOut[i * 4 + j]) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV1: ");
+                    message.append(String.format("%d", args.inV1));
+                    message.append("\n");
+                    message.append("Input inV2: ");
+                    message.append(String.format("%d", args.inV2));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(String.format("%d", args.out));
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%d", arrayOut[i * 4 + j]));
+                    if (args.out != arrayOut[i * 4 + j]) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkMinInt4Int4Int4" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
     public class ArgumentsUintUintUint {
         public int inV1;
         public int inV2;
@@ -719,7 +1709,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeMin(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 1 + j]) {
                     valid = false;
@@ -748,336 +1738,6 @@
         }
     }
 
-    private void checkMinChar2Char2Char2() {
-        Allocation inV1 = createRandomAllocation(mRS, Element.DataType.SIGNED_8, 2, 0xec4705afc03447ael, false);
-        Allocation inV2 = createRandomAllocation(mRS, Element.DataType.SIGNED_8, 2, 0xec4705afc03447afl, false);
-        try {
-            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_8, 2), INPUTSIZE);
-            script.set_gAllocInV2(inV2);
-            script.forEach_testMinChar2Char2Char2(inV1, out);
-            verifyResultsMinChar2Char2Char2(inV1, inV2, out, false);
-        } catch (Exception e) {
-            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testMinChar2Char2Char2: " + e.toString());
-        }
-        try {
-            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_8, 2), INPUTSIZE);
-            scriptRelaxed.set_gAllocInV2(inV2);
-            scriptRelaxed.forEach_testMinChar2Char2Char2(inV1, out);
-            verifyResultsMinChar2Char2Char2(inV1, inV2, out, true);
-        } catch (Exception e) {
-            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testMinChar2Char2Char2: " + e.toString());
-        }
-    }
-
-    private void verifyResultsMinChar2Char2Char2(Allocation inV1, Allocation inV2, Allocation out, boolean relaxed) {
-        byte[] arrayInV1 = new byte[INPUTSIZE * 2];
-        inV1.copyTo(arrayInV1);
-        byte[] arrayInV2 = new byte[INPUTSIZE * 2];
-        inV2.copyTo(arrayInV2);
-        byte[] arrayOut = new byte[INPUTSIZE * 2];
-        out.copyTo(arrayOut);
-        for (int i = 0; i < INPUTSIZE; i++) {
-            for (int j = 0; j < 2 ; j++) {
-                // Extract the inputs.
-                ArgumentsCharCharChar args = new ArgumentsCharCharChar();
-                args.inV1 = arrayInV1[i * 2 + j];
-                args.inV2 = arrayInV2[i * 2 + j];
-                // Figure out what the outputs should have been.
-                Floaty.setRelaxed(relaxed);
-                CoreMathVerifier.computeMin(args);
-                // Figure out what the outputs should have been.
-                boolean valid = true;
-                if (args.out != arrayOut[i * 2 + j]) {
-                    valid = false;
-                }
-                if (!valid) {
-                    StringBuilder message = new StringBuilder();
-                    message.append("Input inV1: ");
-                    message.append(String.format("%d", args.inV1));
-                    message.append("\n");
-                    message.append("Input inV2: ");
-                    message.append(String.format("%d", args.inV2));
-                    message.append("\n");
-                    message.append("Expected output out: ");
-                    message.append(String.format("%d", args.out));
-                    message.append("\n");
-                    message.append("Actual   output out: ");
-                    message.append(String.format("%d", arrayOut[i * 2 + j]));
-                    if (args.out != arrayOut[i * 2 + j]) {
-                        message.append(" FAIL");
-                    }
-                    message.append("\n");
-                    assertTrue("Incorrect output for checkMinChar2Char2Char2" +
-                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
-                }
-            }
-        }
-    }
-
-    private void checkMinUchar2Uchar2Uchar2() {
-        Allocation inV1 = createRandomAllocation(mRS, Element.DataType.UNSIGNED_8, 2, 0x1d3c921d166e22ffl, false);
-        Allocation inV2 = createRandomAllocation(mRS, Element.DataType.UNSIGNED_8, 2, 0x1d3c921d166e2300l, false);
-        try {
-            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_8, 2), INPUTSIZE);
-            script.set_gAllocInV2(inV2);
-            script.forEach_testMinUchar2Uchar2Uchar2(inV1, out);
-            verifyResultsMinUchar2Uchar2Uchar2(inV1, inV2, out, false);
-        } catch (Exception e) {
-            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testMinUchar2Uchar2Uchar2: " + e.toString());
-        }
-        try {
-            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_8, 2), INPUTSIZE);
-            scriptRelaxed.set_gAllocInV2(inV2);
-            scriptRelaxed.forEach_testMinUchar2Uchar2Uchar2(inV1, out);
-            verifyResultsMinUchar2Uchar2Uchar2(inV1, inV2, out, true);
-        } catch (Exception e) {
-            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testMinUchar2Uchar2Uchar2: " + e.toString());
-        }
-    }
-
-    private void verifyResultsMinUchar2Uchar2Uchar2(Allocation inV1, Allocation inV2, Allocation out, boolean relaxed) {
-        byte[] arrayInV1 = new byte[INPUTSIZE * 2];
-        inV1.copyTo(arrayInV1);
-        byte[] arrayInV2 = new byte[INPUTSIZE * 2];
-        inV2.copyTo(arrayInV2);
-        byte[] arrayOut = new byte[INPUTSIZE * 2];
-        out.copyTo(arrayOut);
-        for (int i = 0; i < INPUTSIZE; i++) {
-            for (int j = 0; j < 2 ; j++) {
-                // Extract the inputs.
-                ArgumentsUcharUcharUchar args = new ArgumentsUcharUcharUchar();
-                args.inV1 = arrayInV1[i * 2 + j];
-                args.inV2 = arrayInV2[i * 2 + j];
-                // Figure out what the outputs should have been.
-                Floaty.setRelaxed(relaxed);
-                CoreMathVerifier.computeMin(args);
-                // Figure out what the outputs should have been.
-                boolean valid = true;
-                if (args.out != arrayOut[i * 2 + j]) {
-                    valid = false;
-                }
-                if (!valid) {
-                    StringBuilder message = new StringBuilder();
-                    message.append("Input inV1: ");
-                    message.append(String.format("0x%x", args.inV1));
-                    message.append("\n");
-                    message.append("Input inV2: ");
-                    message.append(String.format("0x%x", args.inV2));
-                    message.append("\n");
-                    message.append("Expected output out: ");
-                    message.append(String.format("0x%x", args.out));
-                    message.append("\n");
-                    message.append("Actual   output out: ");
-                    message.append(String.format("0x%x", arrayOut[i * 2 + j]));
-                    if (args.out != arrayOut[i * 2 + j]) {
-                        message.append(" FAIL");
-                    }
-                    message.append("\n");
-                    assertTrue("Incorrect output for checkMinUchar2Uchar2Uchar2" +
-                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
-                }
-            }
-        }
-    }
-
-    private void checkMinShort2Short2Short2() {
-        Allocation inV1 = createRandomAllocation(mRS, Element.DataType.SIGNED_16, 2, 0xe4959a1ecbf1d380l, false);
-        Allocation inV2 = createRandomAllocation(mRS, Element.DataType.SIGNED_16, 2, 0xe4959a1ecbf1d381l, false);
-        try {
-            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_16, 2), INPUTSIZE);
-            script.set_gAllocInV2(inV2);
-            script.forEach_testMinShort2Short2Short2(inV1, out);
-            verifyResultsMinShort2Short2Short2(inV1, inV2, out, false);
-        } catch (Exception e) {
-            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testMinShort2Short2Short2: " + e.toString());
-        }
-        try {
-            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_16, 2), INPUTSIZE);
-            scriptRelaxed.set_gAllocInV2(inV2);
-            scriptRelaxed.forEach_testMinShort2Short2Short2(inV1, out);
-            verifyResultsMinShort2Short2Short2(inV1, inV2, out, true);
-        } catch (Exception e) {
-            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testMinShort2Short2Short2: " + e.toString());
-        }
-    }
-
-    private void verifyResultsMinShort2Short2Short2(Allocation inV1, Allocation inV2, Allocation out, boolean relaxed) {
-        short[] arrayInV1 = new short[INPUTSIZE * 2];
-        inV1.copyTo(arrayInV1);
-        short[] arrayInV2 = new short[INPUTSIZE * 2];
-        inV2.copyTo(arrayInV2);
-        short[] arrayOut = new short[INPUTSIZE * 2];
-        out.copyTo(arrayOut);
-        for (int i = 0; i < INPUTSIZE; i++) {
-            for (int j = 0; j < 2 ; j++) {
-                // Extract the inputs.
-                ArgumentsShortShortShort args = new ArgumentsShortShortShort();
-                args.inV1 = arrayInV1[i * 2 + j];
-                args.inV2 = arrayInV2[i * 2 + j];
-                // Figure out what the outputs should have been.
-                Floaty.setRelaxed(relaxed);
-                CoreMathVerifier.computeMin(args);
-                // Figure out what the outputs should have been.
-                boolean valid = true;
-                if (args.out != arrayOut[i * 2 + j]) {
-                    valid = false;
-                }
-                if (!valid) {
-                    StringBuilder message = new StringBuilder();
-                    message.append("Input inV1: ");
-                    message.append(String.format("%d", args.inV1));
-                    message.append("\n");
-                    message.append("Input inV2: ");
-                    message.append(String.format("%d", args.inV2));
-                    message.append("\n");
-                    message.append("Expected output out: ");
-                    message.append(String.format("%d", args.out));
-                    message.append("\n");
-                    message.append("Actual   output out: ");
-                    message.append(String.format("%d", arrayOut[i * 2 + j]));
-                    if (args.out != arrayOut[i * 2 + j]) {
-                        message.append(" FAIL");
-                    }
-                    message.append("\n");
-                    assertTrue("Incorrect output for checkMinShort2Short2Short2" +
-                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
-                }
-            }
-        }
-    }
-
-    private void checkMinUshort2Ushort2Ushort2() {
-        Allocation inV1 = createRandomAllocation(mRS, Element.DataType.UNSIGNED_16, 2, 0x98573ebbc511e319l, false);
-        Allocation inV2 = createRandomAllocation(mRS, Element.DataType.UNSIGNED_16, 2, 0x98573ebbc511e31al, false);
-        try {
-            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_16, 2), INPUTSIZE);
-            script.set_gAllocInV2(inV2);
-            script.forEach_testMinUshort2Ushort2Ushort2(inV1, out);
-            verifyResultsMinUshort2Ushort2Ushort2(inV1, inV2, out, false);
-        } catch (Exception e) {
-            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testMinUshort2Ushort2Ushort2: " + e.toString());
-        }
-        try {
-            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_16, 2), INPUTSIZE);
-            scriptRelaxed.set_gAllocInV2(inV2);
-            scriptRelaxed.forEach_testMinUshort2Ushort2Ushort2(inV1, out);
-            verifyResultsMinUshort2Ushort2Ushort2(inV1, inV2, out, true);
-        } catch (Exception e) {
-            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testMinUshort2Ushort2Ushort2: " + e.toString());
-        }
-    }
-
-    private void verifyResultsMinUshort2Ushort2Ushort2(Allocation inV1, Allocation inV2, Allocation out, boolean relaxed) {
-        short[] arrayInV1 = new short[INPUTSIZE * 2];
-        inV1.copyTo(arrayInV1);
-        short[] arrayInV2 = new short[INPUTSIZE * 2];
-        inV2.copyTo(arrayInV2);
-        short[] arrayOut = new short[INPUTSIZE * 2];
-        out.copyTo(arrayOut);
-        for (int i = 0; i < INPUTSIZE; i++) {
-            for (int j = 0; j < 2 ; j++) {
-                // Extract the inputs.
-                ArgumentsUshortUshortUshort args = new ArgumentsUshortUshortUshort();
-                args.inV1 = arrayInV1[i * 2 + j];
-                args.inV2 = arrayInV2[i * 2 + j];
-                // Figure out what the outputs should have been.
-                Floaty.setRelaxed(relaxed);
-                CoreMathVerifier.computeMin(args);
-                // Figure out what the outputs should have been.
-                boolean valid = true;
-                if (args.out != arrayOut[i * 2 + j]) {
-                    valid = false;
-                }
-                if (!valid) {
-                    StringBuilder message = new StringBuilder();
-                    message.append("Input inV1: ");
-                    message.append(String.format("0x%x", args.inV1));
-                    message.append("\n");
-                    message.append("Input inV2: ");
-                    message.append(String.format("0x%x", args.inV2));
-                    message.append("\n");
-                    message.append("Expected output out: ");
-                    message.append(String.format("0x%x", args.out));
-                    message.append("\n");
-                    message.append("Actual   output out: ");
-                    message.append(String.format("0x%x", arrayOut[i * 2 + j]));
-                    if (args.out != arrayOut[i * 2 + j]) {
-                        message.append(" FAIL");
-                    }
-                    message.append("\n");
-                    assertTrue("Incorrect output for checkMinUshort2Ushort2Ushort2" +
-                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
-                }
-            }
-        }
-    }
-
-    private void checkMinInt2Int2Int2() {
-        Allocation inV1 = createRandomAllocation(mRS, Element.DataType.SIGNED_32, 2, 0xba635b605676e7a3l, false);
-        Allocation inV2 = createRandomAllocation(mRS, Element.DataType.SIGNED_32, 2, 0xba635b605676e7a4l, false);
-        try {
-            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_32, 2), INPUTSIZE);
-            script.set_gAllocInV2(inV2);
-            script.forEach_testMinInt2Int2Int2(inV1, out);
-            verifyResultsMinInt2Int2Int2(inV1, inV2, out, false);
-        } catch (Exception e) {
-            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testMinInt2Int2Int2: " + e.toString());
-        }
-        try {
-            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_32, 2), INPUTSIZE);
-            scriptRelaxed.set_gAllocInV2(inV2);
-            scriptRelaxed.forEach_testMinInt2Int2Int2(inV1, out);
-            verifyResultsMinInt2Int2Int2(inV1, inV2, out, true);
-        } catch (Exception e) {
-            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testMinInt2Int2Int2: " + e.toString());
-        }
-    }
-
-    private void verifyResultsMinInt2Int2Int2(Allocation inV1, Allocation inV2, Allocation out, boolean relaxed) {
-        int[] arrayInV1 = new int[INPUTSIZE * 2];
-        inV1.copyTo(arrayInV1);
-        int[] arrayInV2 = new int[INPUTSIZE * 2];
-        inV2.copyTo(arrayInV2);
-        int[] arrayOut = new int[INPUTSIZE * 2];
-        out.copyTo(arrayOut);
-        for (int i = 0; i < INPUTSIZE; i++) {
-            for (int j = 0; j < 2 ; j++) {
-                // Extract the inputs.
-                ArgumentsIntIntInt args = new ArgumentsIntIntInt();
-                args.inV1 = arrayInV1[i * 2 + j];
-                args.inV2 = arrayInV2[i * 2 + j];
-                // Figure out what the outputs should have been.
-                Floaty.setRelaxed(relaxed);
-                CoreMathVerifier.computeMin(args);
-                // Figure out what the outputs should have been.
-                boolean valid = true;
-                if (args.out != arrayOut[i * 2 + j]) {
-                    valid = false;
-                }
-                if (!valid) {
-                    StringBuilder message = new StringBuilder();
-                    message.append("Input inV1: ");
-                    message.append(String.format("%d", args.inV1));
-                    message.append("\n");
-                    message.append("Input inV2: ");
-                    message.append(String.format("%d", args.inV2));
-                    message.append("\n");
-                    message.append("Expected output out: ");
-                    message.append(String.format("%d", args.out));
-                    message.append("\n");
-                    message.append("Actual   output out: ");
-                    message.append(String.format("%d", arrayOut[i * 2 + j]));
-                    if (args.out != arrayOut[i * 2 + j]) {
-                        message.append(" FAIL");
-                    }
-                    message.append("\n");
-                    assertTrue("Incorrect output for checkMinInt2Int2Int2" +
-                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
-                }
-            }
-        }
-    }
-
     private void checkMinUint2Uint2Uint2() {
         Allocation inV1 = createRandomAllocation(mRS, Element.DataType.UNSIGNED_32, 2, 0xb8cf8481d731a1eel, false);
         Allocation inV2 = createRandomAllocation(mRS, Element.DataType.UNSIGNED_32, 2, 0xb8cf8481d731a1efl, false);
@@ -1115,7 +1775,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeMin(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 2 + j]) {
                     valid = false;
@@ -1144,336 +1804,6 @@
         }
     }
 
-    private void checkMinChar3Char3Char3() {
-        Allocation inV1 = createRandomAllocation(mRS, Element.DataType.SIGNED_8, 3, 0x419881e2a4ec1a73l, false);
-        Allocation inV2 = createRandomAllocation(mRS, Element.DataType.SIGNED_8, 3, 0x419881e2a4ec1a74l, false);
-        try {
-            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_8, 3), INPUTSIZE);
-            script.set_gAllocInV2(inV2);
-            script.forEach_testMinChar3Char3Char3(inV1, out);
-            verifyResultsMinChar3Char3Char3(inV1, inV2, out, false);
-        } catch (Exception e) {
-            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testMinChar3Char3Char3: " + e.toString());
-        }
-        try {
-            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_8, 3), INPUTSIZE);
-            scriptRelaxed.set_gAllocInV2(inV2);
-            scriptRelaxed.forEach_testMinChar3Char3Char3(inV1, out);
-            verifyResultsMinChar3Char3Char3(inV1, inV2, out, true);
-        } catch (Exception e) {
-            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testMinChar3Char3Char3: " + e.toString());
-        }
-    }
-
-    private void verifyResultsMinChar3Char3Char3(Allocation inV1, Allocation inV2, Allocation out, boolean relaxed) {
-        byte[] arrayInV1 = new byte[INPUTSIZE * 4];
-        inV1.copyTo(arrayInV1);
-        byte[] arrayInV2 = new byte[INPUTSIZE * 4];
-        inV2.copyTo(arrayInV2);
-        byte[] arrayOut = new byte[INPUTSIZE * 4];
-        out.copyTo(arrayOut);
-        for (int i = 0; i < INPUTSIZE; i++) {
-            for (int j = 0; j < 3 ; j++) {
-                // Extract the inputs.
-                ArgumentsCharCharChar args = new ArgumentsCharCharChar();
-                args.inV1 = arrayInV1[i * 4 + j];
-                args.inV2 = arrayInV2[i * 4 + j];
-                // Figure out what the outputs should have been.
-                Floaty.setRelaxed(relaxed);
-                CoreMathVerifier.computeMin(args);
-                // Figure out what the outputs should have been.
-                boolean valid = true;
-                if (args.out != arrayOut[i * 4 + j]) {
-                    valid = false;
-                }
-                if (!valid) {
-                    StringBuilder message = new StringBuilder();
-                    message.append("Input inV1: ");
-                    message.append(String.format("%d", args.inV1));
-                    message.append("\n");
-                    message.append("Input inV2: ");
-                    message.append(String.format("%d", args.inV2));
-                    message.append("\n");
-                    message.append("Expected output out: ");
-                    message.append(String.format("%d", args.out));
-                    message.append("\n");
-                    message.append("Actual   output out: ");
-                    message.append(String.format("%d", arrayOut[i * 4 + j]));
-                    if (args.out != arrayOut[i * 4 + j]) {
-                        message.append(" FAIL");
-                    }
-                    message.append("\n");
-                    assertTrue("Incorrect output for checkMinChar3Char3Char3" +
-                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
-                }
-            }
-        }
-    }
-
-    private void checkMinUchar3Uchar3Uchar3() {
-        Allocation inV1 = createRandomAllocation(mRS, Element.DataType.UNSIGNED_8, 3, 0x4a2de17d66b8690al, false);
-        Allocation inV2 = createRandomAllocation(mRS, Element.DataType.UNSIGNED_8, 3, 0x4a2de17d66b8690bl, false);
-        try {
-            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_8, 3), INPUTSIZE);
-            script.set_gAllocInV2(inV2);
-            script.forEach_testMinUchar3Uchar3Uchar3(inV1, out);
-            verifyResultsMinUchar3Uchar3Uchar3(inV1, inV2, out, false);
-        } catch (Exception e) {
-            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testMinUchar3Uchar3Uchar3: " + e.toString());
-        }
-        try {
-            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_8, 3), INPUTSIZE);
-            scriptRelaxed.set_gAllocInV2(inV2);
-            scriptRelaxed.forEach_testMinUchar3Uchar3Uchar3(inV1, out);
-            verifyResultsMinUchar3Uchar3Uchar3(inV1, inV2, out, true);
-        } catch (Exception e) {
-            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testMinUchar3Uchar3Uchar3: " + e.toString());
-        }
-    }
-
-    private void verifyResultsMinUchar3Uchar3Uchar3(Allocation inV1, Allocation inV2, Allocation out, boolean relaxed) {
-        byte[] arrayInV1 = new byte[INPUTSIZE * 4];
-        inV1.copyTo(arrayInV1);
-        byte[] arrayInV2 = new byte[INPUTSIZE * 4];
-        inV2.copyTo(arrayInV2);
-        byte[] arrayOut = new byte[INPUTSIZE * 4];
-        out.copyTo(arrayOut);
-        for (int i = 0; i < INPUTSIZE; i++) {
-            for (int j = 0; j < 3 ; j++) {
-                // Extract the inputs.
-                ArgumentsUcharUcharUchar args = new ArgumentsUcharUcharUchar();
-                args.inV1 = arrayInV1[i * 4 + j];
-                args.inV2 = arrayInV2[i * 4 + j];
-                // Figure out what the outputs should have been.
-                Floaty.setRelaxed(relaxed);
-                CoreMathVerifier.computeMin(args);
-                // Figure out what the outputs should have been.
-                boolean valid = true;
-                if (args.out != arrayOut[i * 4 + j]) {
-                    valid = false;
-                }
-                if (!valid) {
-                    StringBuilder message = new StringBuilder();
-                    message.append("Input inV1: ");
-                    message.append(String.format("0x%x", args.inV1));
-                    message.append("\n");
-                    message.append("Input inV2: ");
-                    message.append(String.format("0x%x", args.inV2));
-                    message.append("\n");
-                    message.append("Expected output out: ");
-                    message.append(String.format("0x%x", args.out));
-                    message.append("\n");
-                    message.append("Actual   output out: ");
-                    message.append(String.format("0x%x", arrayOut[i * 4 + j]));
-                    if (args.out != arrayOut[i * 4 + j]) {
-                        message.append(" FAIL");
-                    }
-                    message.append("\n");
-                    assertTrue("Incorrect output for checkMinUchar3Uchar3Uchar3" +
-                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
-                }
-            }
-        }
-    }
-
-    private void checkMinShort3Short3Short3() {
-        Allocation inV1 = createRandomAllocation(mRS, Element.DataType.SIGNED_16, 3, 0x1186e97f1c3c198bl, false);
-        Allocation inV2 = createRandomAllocation(mRS, Element.DataType.SIGNED_16, 3, 0x1186e97f1c3c198cl, false);
-        try {
-            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_16, 3), INPUTSIZE);
-            script.set_gAllocInV2(inV2);
-            script.forEach_testMinShort3Short3Short3(inV1, out);
-            verifyResultsMinShort3Short3Short3(inV1, inV2, out, false);
-        } catch (Exception e) {
-            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testMinShort3Short3Short3: " + e.toString());
-        }
-        try {
-            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_16, 3), INPUTSIZE);
-            scriptRelaxed.set_gAllocInV2(inV2);
-            scriptRelaxed.forEach_testMinShort3Short3Short3(inV1, out);
-            verifyResultsMinShort3Short3Short3(inV1, inV2, out, true);
-        } catch (Exception e) {
-            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testMinShort3Short3Short3: " + e.toString());
-        }
-    }
-
-    private void verifyResultsMinShort3Short3Short3(Allocation inV1, Allocation inV2, Allocation out, boolean relaxed) {
-        short[] arrayInV1 = new short[INPUTSIZE * 4];
-        inV1.copyTo(arrayInV1);
-        short[] arrayInV2 = new short[INPUTSIZE * 4];
-        inV2.copyTo(arrayInV2);
-        short[] arrayOut = new short[INPUTSIZE * 4];
-        out.copyTo(arrayOut);
-        for (int i = 0; i < INPUTSIZE; i++) {
-            for (int j = 0; j < 3 ; j++) {
-                // Extract the inputs.
-                ArgumentsShortShortShort args = new ArgumentsShortShortShort();
-                args.inV1 = arrayInV1[i * 4 + j];
-                args.inV2 = arrayInV2[i * 4 + j];
-                // Figure out what the outputs should have been.
-                Floaty.setRelaxed(relaxed);
-                CoreMathVerifier.computeMin(args);
-                // Figure out what the outputs should have been.
-                boolean valid = true;
-                if (args.out != arrayOut[i * 4 + j]) {
-                    valid = false;
-                }
-                if (!valid) {
-                    StringBuilder message = new StringBuilder();
-                    message.append("Input inV1: ");
-                    message.append(String.format("%d", args.inV1));
-                    message.append("\n");
-                    message.append("Input inV2: ");
-                    message.append(String.format("%d", args.inV2));
-                    message.append("\n");
-                    message.append("Expected output out: ");
-                    message.append(String.format("%d", args.out));
-                    message.append("\n");
-                    message.append("Actual   output out: ");
-                    message.append(String.format("%d", arrayOut[i * 4 + j]));
-                    if (args.out != arrayOut[i * 4 + j]) {
-                        message.append(" FAIL");
-                    }
-                    message.append("\n");
-                    assertTrue("Incorrect output for checkMinShort3Short3Short3" +
-                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
-                }
-            }
-        }
-    }
-
-    private void checkMinUshort3Ushort3Ushort3() {
-        Allocation inV1 = createRandomAllocation(mRS, Element.DataType.UNSIGNED_16, 3, 0x1595f09b03867776l, false);
-        Allocation inV2 = createRandomAllocation(mRS, Element.DataType.UNSIGNED_16, 3, 0x1595f09b03867777l, false);
-        try {
-            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_16, 3), INPUTSIZE);
-            script.set_gAllocInV2(inV2);
-            script.forEach_testMinUshort3Ushort3Ushort3(inV1, out);
-            verifyResultsMinUshort3Ushort3Ushort3(inV1, inV2, out, false);
-        } catch (Exception e) {
-            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testMinUshort3Ushort3Ushort3: " + e.toString());
-        }
-        try {
-            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_16, 3), INPUTSIZE);
-            scriptRelaxed.set_gAllocInV2(inV2);
-            scriptRelaxed.forEach_testMinUshort3Ushort3Ushort3(inV1, out);
-            verifyResultsMinUshort3Ushort3Ushort3(inV1, inV2, out, true);
-        } catch (Exception e) {
-            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testMinUshort3Ushort3Ushort3: " + e.toString());
-        }
-    }
-
-    private void verifyResultsMinUshort3Ushort3Ushort3(Allocation inV1, Allocation inV2, Allocation out, boolean relaxed) {
-        short[] arrayInV1 = new short[INPUTSIZE * 4];
-        inV1.copyTo(arrayInV1);
-        short[] arrayInV2 = new short[INPUTSIZE * 4];
-        inV2.copyTo(arrayInV2);
-        short[] arrayOut = new short[INPUTSIZE * 4];
-        out.copyTo(arrayOut);
-        for (int i = 0; i < INPUTSIZE; i++) {
-            for (int j = 0; j < 3 ; j++) {
-                // Extract the inputs.
-                ArgumentsUshortUshortUshort args = new ArgumentsUshortUshortUshort();
-                args.inV1 = arrayInV1[i * 4 + j];
-                args.inV2 = arrayInV2[i * 4 + j];
-                // Figure out what the outputs should have been.
-                Floaty.setRelaxed(relaxed);
-                CoreMathVerifier.computeMin(args);
-                // Figure out what the outputs should have been.
-                boolean valid = true;
-                if (args.out != arrayOut[i * 4 + j]) {
-                    valid = false;
-                }
-                if (!valid) {
-                    StringBuilder message = new StringBuilder();
-                    message.append("Input inV1: ");
-                    message.append(String.format("0x%x", args.inV1));
-                    message.append("\n");
-                    message.append("Input inV2: ");
-                    message.append(String.format("0x%x", args.inV2));
-                    message.append("\n");
-                    message.append("Expected output out: ");
-                    message.append(String.format("0x%x", args.out));
-                    message.append("\n");
-                    message.append("Actual   output out: ");
-                    message.append(String.format("0x%x", arrayOut[i * 4 + j]));
-                    if (args.out != arrayOut[i * 4 + j]) {
-                        message.append(" FAIL");
-                    }
-                    message.append("\n");
-                    assertTrue("Incorrect output for checkMinUshort3Ushort3Ushort3" +
-                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
-                }
-            }
-        }
-    }
-
-    private void checkMinInt3Int3Int3() {
-        Allocation inV1 = createRandomAllocation(mRS, Element.DataType.SIGNED_32, 3, 0xe4f086806849fbc6l, false);
-        Allocation inV2 = createRandomAllocation(mRS, Element.DataType.SIGNED_32, 3, 0xe4f086806849fbc7l, false);
-        try {
-            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_32, 3), INPUTSIZE);
-            script.set_gAllocInV2(inV2);
-            script.forEach_testMinInt3Int3Int3(inV1, out);
-            verifyResultsMinInt3Int3Int3(inV1, inV2, out, false);
-        } catch (Exception e) {
-            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testMinInt3Int3Int3: " + e.toString());
-        }
-        try {
-            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_32, 3), INPUTSIZE);
-            scriptRelaxed.set_gAllocInV2(inV2);
-            scriptRelaxed.forEach_testMinInt3Int3Int3(inV1, out);
-            verifyResultsMinInt3Int3Int3(inV1, inV2, out, true);
-        } catch (Exception e) {
-            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testMinInt3Int3Int3: " + e.toString());
-        }
-    }
-
-    private void verifyResultsMinInt3Int3Int3(Allocation inV1, Allocation inV2, Allocation out, boolean relaxed) {
-        int[] arrayInV1 = new int[INPUTSIZE * 4];
-        inV1.copyTo(arrayInV1);
-        int[] arrayInV2 = new int[INPUTSIZE * 4];
-        inV2.copyTo(arrayInV2);
-        int[] arrayOut = new int[INPUTSIZE * 4];
-        out.copyTo(arrayOut);
-        for (int i = 0; i < INPUTSIZE; i++) {
-            for (int j = 0; j < 3 ; j++) {
-                // Extract the inputs.
-                ArgumentsIntIntInt args = new ArgumentsIntIntInt();
-                args.inV1 = arrayInV1[i * 4 + j];
-                args.inV2 = arrayInV2[i * 4 + j];
-                // Figure out what the outputs should have been.
-                Floaty.setRelaxed(relaxed);
-                CoreMathVerifier.computeMin(args);
-                // Figure out what the outputs should have been.
-                boolean valid = true;
-                if (args.out != arrayOut[i * 4 + j]) {
-                    valid = false;
-                }
-                if (!valid) {
-                    StringBuilder message = new StringBuilder();
-                    message.append("Input inV1: ");
-                    message.append(String.format("%d", args.inV1));
-                    message.append("\n");
-                    message.append("Input inV2: ");
-                    message.append(String.format("%d", args.inV2));
-                    message.append("\n");
-                    message.append("Expected output out: ");
-                    message.append(String.format("%d", args.out));
-                    message.append("\n");
-                    message.append("Actual   output out: ");
-                    message.append(String.format("%d", arrayOut[i * 4 + j]));
-                    if (args.out != arrayOut[i * 4 + j]) {
-                        message.append(" FAIL");
-                    }
-                    message.append("\n");
-                    assertTrue("Incorrect output for checkMinInt3Int3Int3" +
-                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
-                }
-            }
-        }
-    }
-
     private void checkMinUint3Uint3Uint3() {
         Allocation inV1 = createRandomAllocation(mRS, Element.DataType.UNSIGNED_32, 3, 0xe2100b4bbe974b3l, false);
         Allocation inV2 = createRandomAllocation(mRS, Element.DataType.UNSIGNED_32, 3, 0xe2100b4bbe974b4l, false);
@@ -1511,7 +1841,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeMin(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 4 + j]) {
                     valid = false;
@@ -1540,336 +1870,6 @@
         }
     }
 
-    private void checkMinChar4Char4Char4() {
-        Allocation inV1 = createRandomAllocation(mRS, Element.DataType.SIGNED_8, 4, 0x96e9fe1589a3ed38l, false);
-        Allocation inV2 = createRandomAllocation(mRS, Element.DataType.SIGNED_8, 4, 0x96e9fe1589a3ed39l, false);
-        try {
-            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_8, 4), INPUTSIZE);
-            script.set_gAllocInV2(inV2);
-            script.forEach_testMinChar4Char4Char4(inV1, out);
-            verifyResultsMinChar4Char4Char4(inV1, inV2, out, false);
-        } catch (Exception e) {
-            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testMinChar4Char4Char4: " + e.toString());
-        }
-        try {
-            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_8, 4), INPUTSIZE);
-            scriptRelaxed.set_gAllocInV2(inV2);
-            scriptRelaxed.forEach_testMinChar4Char4Char4(inV1, out);
-            verifyResultsMinChar4Char4Char4(inV1, inV2, out, true);
-        } catch (Exception e) {
-            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testMinChar4Char4Char4: " + e.toString());
-        }
-    }
-
-    private void verifyResultsMinChar4Char4Char4(Allocation inV1, Allocation inV2, Allocation out, boolean relaxed) {
-        byte[] arrayInV1 = new byte[INPUTSIZE * 4];
-        inV1.copyTo(arrayInV1);
-        byte[] arrayInV2 = new byte[INPUTSIZE * 4];
-        inV2.copyTo(arrayInV2);
-        byte[] arrayOut = new byte[INPUTSIZE * 4];
-        out.copyTo(arrayOut);
-        for (int i = 0; i < INPUTSIZE; i++) {
-            for (int j = 0; j < 4 ; j++) {
-                // Extract the inputs.
-                ArgumentsCharCharChar args = new ArgumentsCharCharChar();
-                args.inV1 = arrayInV1[i * 4 + j];
-                args.inV2 = arrayInV2[i * 4 + j];
-                // Figure out what the outputs should have been.
-                Floaty.setRelaxed(relaxed);
-                CoreMathVerifier.computeMin(args);
-                // Figure out what the outputs should have been.
-                boolean valid = true;
-                if (args.out != arrayOut[i * 4 + j]) {
-                    valid = false;
-                }
-                if (!valid) {
-                    StringBuilder message = new StringBuilder();
-                    message.append("Input inV1: ");
-                    message.append(String.format("%d", args.inV1));
-                    message.append("\n");
-                    message.append("Input inV2: ");
-                    message.append(String.format("%d", args.inV2));
-                    message.append("\n");
-                    message.append("Expected output out: ");
-                    message.append(String.format("%d", args.out));
-                    message.append("\n");
-                    message.append("Actual   output out: ");
-                    message.append(String.format("%d", arrayOut[i * 4 + j]));
-                    if (args.out != arrayOut[i * 4 + j]) {
-                        message.append(" FAIL");
-                    }
-                    message.append("\n");
-                    assertTrue("Incorrect output for checkMinChar4Char4Char4" +
-                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
-                }
-            }
-        }
-    }
-
-    private void checkMinUchar4Uchar4Uchar4() {
-        Allocation inV1 = createRandomAllocation(mRS, Element.DataType.UNSIGNED_8, 4, 0x771f30ddb702af15l, false);
-        Allocation inV2 = createRandomAllocation(mRS, Element.DataType.UNSIGNED_8, 4, 0x771f30ddb702af16l, false);
-        try {
-            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_8, 4), INPUTSIZE);
-            script.set_gAllocInV2(inV2);
-            script.forEach_testMinUchar4Uchar4Uchar4(inV1, out);
-            verifyResultsMinUchar4Uchar4Uchar4(inV1, inV2, out, false);
-        } catch (Exception e) {
-            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testMinUchar4Uchar4Uchar4: " + e.toString());
-        }
-        try {
-            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_8, 4), INPUTSIZE);
-            scriptRelaxed.set_gAllocInV2(inV2);
-            scriptRelaxed.forEach_testMinUchar4Uchar4Uchar4(inV1, out);
-            verifyResultsMinUchar4Uchar4Uchar4(inV1, inV2, out, true);
-        } catch (Exception e) {
-            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testMinUchar4Uchar4Uchar4: " + e.toString());
-        }
-    }
-
-    private void verifyResultsMinUchar4Uchar4Uchar4(Allocation inV1, Allocation inV2, Allocation out, boolean relaxed) {
-        byte[] arrayInV1 = new byte[INPUTSIZE * 4];
-        inV1.copyTo(arrayInV1);
-        byte[] arrayInV2 = new byte[INPUTSIZE * 4];
-        inV2.copyTo(arrayInV2);
-        byte[] arrayOut = new byte[INPUTSIZE * 4];
-        out.copyTo(arrayOut);
-        for (int i = 0; i < INPUTSIZE; i++) {
-            for (int j = 0; j < 4 ; j++) {
-                // Extract the inputs.
-                ArgumentsUcharUcharUchar args = new ArgumentsUcharUcharUchar();
-                args.inV1 = arrayInV1[i * 4 + j];
-                args.inV2 = arrayInV2[i * 4 + j];
-                // Figure out what the outputs should have been.
-                Floaty.setRelaxed(relaxed);
-                CoreMathVerifier.computeMin(args);
-                // Figure out what the outputs should have been.
-                boolean valid = true;
-                if (args.out != arrayOut[i * 4 + j]) {
-                    valid = false;
-                }
-                if (!valid) {
-                    StringBuilder message = new StringBuilder();
-                    message.append("Input inV1: ");
-                    message.append(String.format("0x%x", args.inV1));
-                    message.append("\n");
-                    message.append("Input inV2: ");
-                    message.append(String.format("0x%x", args.inV2));
-                    message.append("\n");
-                    message.append("Expected output out: ");
-                    message.append(String.format("0x%x", args.out));
-                    message.append("\n");
-                    message.append("Actual   output out: ");
-                    message.append(String.format("0x%x", arrayOut[i * 4 + j]));
-                    if (args.out != arrayOut[i * 4 + j]) {
-                        message.append(" FAIL");
-                    }
-                    message.append("\n");
-                    assertTrue("Incorrect output for checkMinUchar4Uchar4Uchar4" +
-                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
-                }
-            }
-        }
-    }
-
-    private void checkMinShort4Short4Short4() {
-        Allocation inV1 = createRandomAllocation(mRS, Element.DataType.SIGNED_16, 4, 0x3e7838df6c865f96l, false);
-        Allocation inV2 = createRandomAllocation(mRS, Element.DataType.SIGNED_16, 4, 0x3e7838df6c865f97l, false);
-        try {
-            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_16, 4), INPUTSIZE);
-            script.set_gAllocInV2(inV2);
-            script.forEach_testMinShort4Short4Short4(inV1, out);
-            verifyResultsMinShort4Short4Short4(inV1, inV2, out, false);
-        } catch (Exception e) {
-            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testMinShort4Short4Short4: " + e.toString());
-        }
-        try {
-            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_16, 4), INPUTSIZE);
-            scriptRelaxed.set_gAllocInV2(inV2);
-            scriptRelaxed.forEach_testMinShort4Short4Short4(inV1, out);
-            verifyResultsMinShort4Short4Short4(inV1, inV2, out, true);
-        } catch (Exception e) {
-            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testMinShort4Short4Short4: " + e.toString());
-        }
-    }
-
-    private void verifyResultsMinShort4Short4Short4(Allocation inV1, Allocation inV2, Allocation out, boolean relaxed) {
-        short[] arrayInV1 = new short[INPUTSIZE * 4];
-        inV1.copyTo(arrayInV1);
-        short[] arrayInV2 = new short[INPUTSIZE * 4];
-        inV2.copyTo(arrayInV2);
-        short[] arrayOut = new short[INPUTSIZE * 4];
-        out.copyTo(arrayOut);
-        for (int i = 0; i < INPUTSIZE; i++) {
-            for (int j = 0; j < 4 ; j++) {
-                // Extract the inputs.
-                ArgumentsShortShortShort args = new ArgumentsShortShortShort();
-                args.inV1 = arrayInV1[i * 4 + j];
-                args.inV2 = arrayInV2[i * 4 + j];
-                // Figure out what the outputs should have been.
-                Floaty.setRelaxed(relaxed);
-                CoreMathVerifier.computeMin(args);
-                // Figure out what the outputs should have been.
-                boolean valid = true;
-                if (args.out != arrayOut[i * 4 + j]) {
-                    valid = false;
-                }
-                if (!valid) {
-                    StringBuilder message = new StringBuilder();
-                    message.append("Input inV1: ");
-                    message.append(String.format("%d", args.inV1));
-                    message.append("\n");
-                    message.append("Input inV2: ");
-                    message.append(String.format("%d", args.inV2));
-                    message.append("\n");
-                    message.append("Expected output out: ");
-                    message.append(String.format("%d", args.out));
-                    message.append("\n");
-                    message.append("Actual   output out: ");
-                    message.append(String.format("%d", arrayOut[i * 4 + j]));
-                    if (args.out != arrayOut[i * 4 + j]) {
-                        message.append(" FAIL");
-                    }
-                    message.append("\n");
-                    assertTrue("Incorrect output for checkMinShort4Short4Short4" +
-                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
-                }
-            }
-        }
-    }
-
-    private void checkMinUshort4Ushort4Ushort4() {
-        Allocation inV1 = createRandomAllocation(mRS, Element.DataType.UNSIGNED_16, 4, 0x92d4a27a41fb0bd3l, false);
-        Allocation inV2 = createRandomAllocation(mRS, Element.DataType.UNSIGNED_16, 4, 0x92d4a27a41fb0bd4l, false);
-        try {
-            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_16, 4), INPUTSIZE);
-            script.set_gAllocInV2(inV2);
-            script.forEach_testMinUshort4Ushort4Ushort4(inV1, out);
-            verifyResultsMinUshort4Ushort4Ushort4(inV1, inV2, out, false);
-        } catch (Exception e) {
-            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testMinUshort4Ushort4Ushort4: " + e.toString());
-        }
-        try {
-            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_16, 4), INPUTSIZE);
-            scriptRelaxed.set_gAllocInV2(inV2);
-            scriptRelaxed.forEach_testMinUshort4Ushort4Ushort4(inV1, out);
-            verifyResultsMinUshort4Ushort4Ushort4(inV1, inV2, out, true);
-        } catch (Exception e) {
-            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testMinUshort4Ushort4Ushort4: " + e.toString());
-        }
-    }
-
-    private void verifyResultsMinUshort4Ushort4Ushort4(Allocation inV1, Allocation inV2, Allocation out, boolean relaxed) {
-        short[] arrayInV1 = new short[INPUTSIZE * 4];
-        inV1.copyTo(arrayInV1);
-        short[] arrayInV2 = new short[INPUTSIZE * 4];
-        inV2.copyTo(arrayInV2);
-        short[] arrayOut = new short[INPUTSIZE * 4];
-        out.copyTo(arrayOut);
-        for (int i = 0; i < INPUTSIZE; i++) {
-            for (int j = 0; j < 4 ; j++) {
-                // Extract the inputs.
-                ArgumentsUshortUshortUshort args = new ArgumentsUshortUshortUshort();
-                args.inV1 = arrayInV1[i * 4 + j];
-                args.inV2 = arrayInV2[i * 4 + j];
-                // Figure out what the outputs should have been.
-                Floaty.setRelaxed(relaxed);
-                CoreMathVerifier.computeMin(args);
-                // Figure out what the outputs should have been.
-                boolean valid = true;
-                if (args.out != arrayOut[i * 4 + j]) {
-                    valid = false;
-                }
-                if (!valid) {
-                    StringBuilder message = new StringBuilder();
-                    message.append("Input inV1: ");
-                    message.append(String.format("0x%x", args.inV1));
-                    message.append("\n");
-                    message.append("Input inV2: ");
-                    message.append(String.format("0x%x", args.inV2));
-                    message.append("\n");
-                    message.append("Expected output out: ");
-                    message.append(String.format("0x%x", args.out));
-                    message.append("\n");
-                    message.append("Actual   output out: ");
-                    message.append(String.format("0x%x", arrayOut[i * 4 + j]));
-                    if (args.out != arrayOut[i * 4 + j]) {
-                        message.append(" FAIL");
-                    }
-                    message.append("\n");
-                    assertTrue("Incorrect output for checkMinUshort4Ushort4Ushort4" +
-                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
-                }
-            }
-        }
-    }
-
-    private void checkMinInt4Int4Int4() {
-        Allocation inV1 = createRandomAllocation(mRS, Element.DataType.SIGNED_32, 4, 0xf7db1a07a1d0fe9l, false);
-        Allocation inV2 = createRandomAllocation(mRS, Element.DataType.SIGNED_32, 4, 0xf7db1a07a1d0feal, false);
-        try {
-            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_32, 4), INPUTSIZE);
-            script.set_gAllocInV2(inV2);
-            script.forEach_testMinInt4Int4Int4(inV1, out);
-            verifyResultsMinInt4Int4Int4(inV1, inV2, out, false);
-        } catch (Exception e) {
-            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testMinInt4Int4Int4: " + e.toString());
-        }
-        try {
-            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_32, 4), INPUTSIZE);
-            scriptRelaxed.set_gAllocInV2(inV2);
-            scriptRelaxed.forEach_testMinInt4Int4Int4(inV1, out);
-            verifyResultsMinInt4Int4Int4(inV1, inV2, out, true);
-        } catch (Exception e) {
-            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testMinInt4Int4Int4: " + e.toString());
-        }
-    }
-
-    private void verifyResultsMinInt4Int4Int4(Allocation inV1, Allocation inV2, Allocation out, boolean relaxed) {
-        int[] arrayInV1 = new int[INPUTSIZE * 4];
-        inV1.copyTo(arrayInV1);
-        int[] arrayInV2 = new int[INPUTSIZE * 4];
-        inV2.copyTo(arrayInV2);
-        int[] arrayOut = new int[INPUTSIZE * 4];
-        out.copyTo(arrayOut);
-        for (int i = 0; i < INPUTSIZE; i++) {
-            for (int j = 0; j < 4 ; j++) {
-                // Extract the inputs.
-                ArgumentsIntIntInt args = new ArgumentsIntIntInt();
-                args.inV1 = arrayInV1[i * 4 + j];
-                args.inV2 = arrayInV2[i * 4 + j];
-                // Figure out what the outputs should have been.
-                Floaty.setRelaxed(relaxed);
-                CoreMathVerifier.computeMin(args);
-                // Figure out what the outputs should have been.
-                boolean valid = true;
-                if (args.out != arrayOut[i * 4 + j]) {
-                    valid = false;
-                }
-                if (!valid) {
-                    StringBuilder message = new StringBuilder();
-                    message.append("Input inV1: ");
-                    message.append(String.format("%d", args.inV1));
-                    message.append("\n");
-                    message.append("Input inV2: ");
-                    message.append(String.format("%d", args.inV2));
-                    message.append("\n");
-                    message.append("Expected output out: ");
-                    message.append(String.format("%d", args.out));
-                    message.append("\n");
-                    message.append("Actual   output out: ");
-                    message.append(String.format("%d", arrayOut[i * 4 + j]));
-                    if (args.out != arrayOut[i * 4 + j]) {
-                        message.append(" FAIL");
-                    }
-                    message.append("\n");
-                    assertTrue("Incorrect output for checkMinInt4Int4Int4" +
-                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
-                }
-            }
-        }
-    }
-
     private void checkMinUint4Uint4Uint4() {
         Allocation inV1 = createRandomAllocation(mRS, Element.DataType.UNSIGNED_32, 4, 0x63727ce7a0a14778l, false);
         Allocation inV2 = createRandomAllocation(mRS, Element.DataType.UNSIGNED_32, 4, 0x63727ce7a0a14779l, false);
@@ -1907,7 +1907,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeMin(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.out != arrayOut[i * 4 + j]) {
                     valid = false;
@@ -1936,34 +1936,582 @@
         }
     }
 
+    public class ArgumentsLongLongLong {
+        public long inV1;
+        public long inV2;
+        public long out;
+    }
+
+    private void checkMinLongLongLong() {
+        Allocation inV1 = createRandomAllocation(mRS, Element.DataType.SIGNED_64, 1, 0x20ce185251c10eb2l, false);
+        Allocation inV2 = createRandomAllocation(mRS, Element.DataType.SIGNED_64, 1, 0x20ce185251c10eb3l, false);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_64, 1), INPUTSIZE);
+            script.set_gAllocInV2(inV2);
+            script.forEach_testMinLongLongLong(inV1, out);
+            verifyResultsMinLongLongLong(inV1, inV2, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testMinLongLongLong: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_64, 1), INPUTSIZE);
+            scriptRelaxed.set_gAllocInV2(inV2);
+            scriptRelaxed.forEach_testMinLongLongLong(inV1, out);
+            verifyResultsMinLongLongLong(inV1, inV2, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testMinLongLongLong: " + e.toString());
+        }
+    }
+
+    private void verifyResultsMinLongLongLong(Allocation inV1, Allocation inV2, Allocation out, boolean relaxed) {
+        long[] arrayInV1 = new long[INPUTSIZE * 1];
+        inV1.copyTo(arrayInV1);
+        long[] arrayInV2 = new long[INPUTSIZE * 1];
+        inV2.copyTo(arrayInV2);
+        long[] arrayOut = new long[INPUTSIZE * 1];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 1 ; j++) {
+                // Extract the inputs.
+                ArgumentsLongLongLong args = new ArgumentsLongLongLong();
+                args.inV1 = arrayInV1[i];
+                args.inV2 = arrayInV2[i];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeMin(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (args.out != arrayOut[i * 1 + j]) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV1: ");
+                    message.append(String.format("%d", args.inV1));
+                    message.append("\n");
+                    message.append("Input inV2: ");
+                    message.append(String.format("%d", args.inV2));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(String.format("%d", args.out));
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%d", arrayOut[i * 1 + j]));
+                    if (args.out != arrayOut[i * 1 + j]) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkMinLongLongLong" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkMinLong2Long2Long2() {
+        Allocation inV1 = createRandomAllocation(mRS, Element.DataType.SIGNED_64, 2, 0x2285e00a094676a0l, false);
+        Allocation inV2 = createRandomAllocation(mRS, Element.DataType.SIGNED_64, 2, 0x2285e00a094676a1l, false);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_64, 2), INPUTSIZE);
+            script.set_gAllocInV2(inV2);
+            script.forEach_testMinLong2Long2Long2(inV1, out);
+            verifyResultsMinLong2Long2Long2(inV1, inV2, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testMinLong2Long2Long2: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_64, 2), INPUTSIZE);
+            scriptRelaxed.set_gAllocInV2(inV2);
+            scriptRelaxed.forEach_testMinLong2Long2Long2(inV1, out);
+            verifyResultsMinLong2Long2Long2(inV1, inV2, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testMinLong2Long2Long2: " + e.toString());
+        }
+    }
+
+    private void verifyResultsMinLong2Long2Long2(Allocation inV1, Allocation inV2, Allocation out, boolean relaxed) {
+        long[] arrayInV1 = new long[INPUTSIZE * 2];
+        inV1.copyTo(arrayInV1);
+        long[] arrayInV2 = new long[INPUTSIZE * 2];
+        inV2.copyTo(arrayInV2);
+        long[] arrayOut = new long[INPUTSIZE * 2];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 2 ; j++) {
+                // Extract the inputs.
+                ArgumentsLongLongLong args = new ArgumentsLongLongLong();
+                args.inV1 = arrayInV1[i * 2 + j];
+                args.inV2 = arrayInV2[i * 2 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeMin(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (args.out != arrayOut[i * 2 + j]) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV1: ");
+                    message.append(String.format("%d", args.inV1));
+                    message.append("\n");
+                    message.append("Input inV2: ");
+                    message.append(String.format("%d", args.inV2));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(String.format("%d", args.out));
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%d", arrayOut[i * 2 + j]));
+                    if (args.out != arrayOut[i * 2 + j]) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkMinLong2Long2Long2" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkMinLong3Long3Long3() {
+        Allocation inV1 = createRandomAllocation(mRS, Element.DataType.SIGNED_64, 3, 0x77d75c3cedfe4965l, false);
+        Allocation inV2 = createRandomAllocation(mRS, Element.DataType.SIGNED_64, 3, 0x77d75c3cedfe4966l, false);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_64, 3), INPUTSIZE);
+            script.set_gAllocInV2(inV2);
+            script.forEach_testMinLong3Long3Long3(inV1, out);
+            verifyResultsMinLong3Long3Long3(inV1, inV2, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testMinLong3Long3Long3: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_64, 3), INPUTSIZE);
+            scriptRelaxed.set_gAllocInV2(inV2);
+            scriptRelaxed.forEach_testMinLong3Long3Long3(inV1, out);
+            verifyResultsMinLong3Long3Long3(inV1, inV2, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testMinLong3Long3Long3: " + e.toString());
+        }
+    }
+
+    private void verifyResultsMinLong3Long3Long3(Allocation inV1, Allocation inV2, Allocation out, boolean relaxed) {
+        long[] arrayInV1 = new long[INPUTSIZE * 4];
+        inV1.copyTo(arrayInV1);
+        long[] arrayInV2 = new long[INPUTSIZE * 4];
+        inV2.copyTo(arrayInV2);
+        long[] arrayOut = new long[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 3 ; j++) {
+                // Extract the inputs.
+                ArgumentsLongLongLong args = new ArgumentsLongLongLong();
+                args.inV1 = arrayInV1[i * 4 + j];
+                args.inV2 = arrayInV2[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeMin(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (args.out != arrayOut[i * 4 + j]) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV1: ");
+                    message.append(String.format("%d", args.inV1));
+                    message.append("\n");
+                    message.append("Input inV2: ");
+                    message.append(String.format("%d", args.inV2));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(String.format("%d", args.out));
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%d", arrayOut[i * 4 + j]));
+                    if (args.out != arrayOut[i * 4 + j]) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkMinLong3Long3Long3" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkMinLong4Long4Long4() {
+        Allocation inV1 = createRandomAllocation(mRS, Element.DataType.SIGNED_64, 4, 0xcd28d86fd2b61c2al, false);
+        Allocation inV2 = createRandomAllocation(mRS, Element.DataType.SIGNED_64, 4, 0xcd28d86fd2b61c2bl, false);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_64, 4), INPUTSIZE);
+            script.set_gAllocInV2(inV2);
+            script.forEach_testMinLong4Long4Long4(inV1, out);
+            verifyResultsMinLong4Long4Long4(inV1, inV2, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testMinLong4Long4Long4: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.SIGNED_64, 4), INPUTSIZE);
+            scriptRelaxed.set_gAllocInV2(inV2);
+            scriptRelaxed.forEach_testMinLong4Long4Long4(inV1, out);
+            verifyResultsMinLong4Long4Long4(inV1, inV2, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testMinLong4Long4Long4: " + e.toString());
+        }
+    }
+
+    private void verifyResultsMinLong4Long4Long4(Allocation inV1, Allocation inV2, Allocation out, boolean relaxed) {
+        long[] arrayInV1 = new long[INPUTSIZE * 4];
+        inV1.copyTo(arrayInV1);
+        long[] arrayInV2 = new long[INPUTSIZE * 4];
+        inV2.copyTo(arrayInV2);
+        long[] arrayOut = new long[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 4 ; j++) {
+                // Extract the inputs.
+                ArgumentsLongLongLong args = new ArgumentsLongLongLong();
+                args.inV1 = arrayInV1[i * 4 + j];
+                args.inV2 = arrayInV2[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeMin(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (args.out != arrayOut[i * 4 + j]) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV1: ");
+                    message.append(String.format("%d", args.inV1));
+                    message.append("\n");
+                    message.append("Input inV2: ");
+                    message.append(String.format("%d", args.inV2));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(String.format("%d", args.out));
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%d", arrayOut[i * 4 + j]));
+                    if (args.out != arrayOut[i * 4 + j]) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkMinLong4Long4Long4" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    public class ArgumentsUlongUlongUlong {
+        public long inV1;
+        public long inV2;
+        public long out;
+    }
+
+    private void checkMinUlongUlongUlong() {
+        Allocation inV1 = createRandomAllocation(mRS, Element.DataType.UNSIGNED_64, 1, 0x9ea8f1e67008ea67l, false);
+        Allocation inV2 = createRandomAllocation(mRS, Element.DataType.UNSIGNED_64, 1, 0x9ea8f1e67008ea68l, false);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_64, 1), INPUTSIZE);
+            script.set_gAllocInV2(inV2);
+            script.forEach_testMinUlongUlongUlong(inV1, out);
+            verifyResultsMinUlongUlongUlong(inV1, inV2, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testMinUlongUlongUlong: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_64, 1), INPUTSIZE);
+            scriptRelaxed.set_gAllocInV2(inV2);
+            scriptRelaxed.forEach_testMinUlongUlongUlong(inV1, out);
+            verifyResultsMinUlongUlongUlong(inV1, inV2, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testMinUlongUlongUlong: " + e.toString());
+        }
+    }
+
+    private void verifyResultsMinUlongUlongUlong(Allocation inV1, Allocation inV2, Allocation out, boolean relaxed) {
+        long[] arrayInV1 = new long[INPUTSIZE * 1];
+        inV1.copyTo(arrayInV1);
+        long[] arrayInV2 = new long[INPUTSIZE * 1];
+        inV2.copyTo(arrayInV2);
+        long[] arrayOut = new long[INPUTSIZE * 1];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 1 ; j++) {
+                // Extract the inputs.
+                ArgumentsUlongUlongUlong args = new ArgumentsUlongUlongUlong();
+                args.inV1 = arrayInV1[i];
+                args.inV2 = arrayInV2[i];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeMin(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (args.out != arrayOut[i * 1 + j]) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV1: ");
+                    message.append(String.format("0x%x", args.inV1));
+                    message.append("\n");
+                    message.append("Input inV2: ");
+                    message.append(String.format("0x%x", args.inV2));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(String.format("0x%x", args.out));
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("0x%x", arrayOut[i * 1 + j]));
+                    if (args.out != arrayOut[i * 1 + j]) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkMinUlongUlongUlong" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkMinUlong2Ulong2Ulong2() {
+        Allocation inV1 = createRandomAllocation(mRS, Element.DataType.UNSIGNED_64, 2, 0x26bb4add3110402dl, false);
+        Allocation inV2 = createRandomAllocation(mRS, Element.DataType.UNSIGNED_64, 2, 0x26bb4add3110402el, false);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_64, 2), INPUTSIZE);
+            script.set_gAllocInV2(inV2);
+            script.forEach_testMinUlong2Ulong2Ulong2(inV1, out);
+            verifyResultsMinUlong2Ulong2Ulong2(inV1, inV2, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testMinUlong2Ulong2Ulong2: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_64, 2), INPUTSIZE);
+            scriptRelaxed.set_gAllocInV2(inV2);
+            scriptRelaxed.forEach_testMinUlong2Ulong2Ulong2(inV1, out);
+            verifyResultsMinUlong2Ulong2Ulong2(inV1, inV2, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testMinUlong2Ulong2Ulong2: " + e.toString());
+        }
+    }
+
+    private void verifyResultsMinUlong2Ulong2Ulong2(Allocation inV1, Allocation inV2, Allocation out, boolean relaxed) {
+        long[] arrayInV1 = new long[INPUTSIZE * 2];
+        inV1.copyTo(arrayInV1);
+        long[] arrayInV2 = new long[INPUTSIZE * 2];
+        inV2.copyTo(arrayInV2);
+        long[] arrayOut = new long[INPUTSIZE * 2];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 2 ; j++) {
+                // Extract the inputs.
+                ArgumentsUlongUlongUlong args = new ArgumentsUlongUlongUlong();
+                args.inV1 = arrayInV1[i * 2 + j];
+                args.inV2 = arrayInV2[i * 2 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeMin(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (args.out != arrayOut[i * 2 + j]) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV1: ");
+                    message.append(String.format("0x%x", args.inV1));
+                    message.append("\n");
+                    message.append("Input inV2: ");
+                    message.append(String.format("0x%x", args.inV2));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(String.format("0x%x", args.out));
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("0x%x", arrayOut[i * 2 + j]));
+                    if (args.out != arrayOut[i * 2 + j]) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkMinUlong2Ulong2Ulong2" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkMinUlong3Ulong3Ulong3() {
+        Allocation inV1 = createRandomAllocation(mRS, Element.DataType.UNSIGNED_64, 3, 0x53ac9a3d815a8638l, false);
+        Allocation inV2 = createRandomAllocation(mRS, Element.DataType.UNSIGNED_64, 3, 0x53ac9a3d815a8639l, false);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_64, 3), INPUTSIZE);
+            script.set_gAllocInV2(inV2);
+            script.forEach_testMinUlong3Ulong3Ulong3(inV1, out);
+            verifyResultsMinUlong3Ulong3Ulong3(inV1, inV2, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testMinUlong3Ulong3Ulong3: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_64, 3), INPUTSIZE);
+            scriptRelaxed.set_gAllocInV2(inV2);
+            scriptRelaxed.forEach_testMinUlong3Ulong3Ulong3(inV1, out);
+            verifyResultsMinUlong3Ulong3Ulong3(inV1, inV2, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testMinUlong3Ulong3Ulong3: " + e.toString());
+        }
+    }
+
+    private void verifyResultsMinUlong3Ulong3Ulong3(Allocation inV1, Allocation inV2, Allocation out, boolean relaxed) {
+        long[] arrayInV1 = new long[INPUTSIZE * 4];
+        inV1.copyTo(arrayInV1);
+        long[] arrayInV2 = new long[INPUTSIZE * 4];
+        inV2.copyTo(arrayInV2);
+        long[] arrayOut = new long[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 3 ; j++) {
+                // Extract the inputs.
+                ArgumentsUlongUlongUlong args = new ArgumentsUlongUlongUlong();
+                args.inV1 = arrayInV1[i * 4 + j];
+                args.inV2 = arrayInV2[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeMin(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (args.out != arrayOut[i * 4 + j]) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV1: ");
+                    message.append(String.format("0x%x", args.inV1));
+                    message.append("\n");
+                    message.append("Input inV2: ");
+                    message.append(String.format("0x%x", args.inV2));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(String.format("0x%x", args.out));
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("0x%x", arrayOut[i * 4 + j]));
+                    if (args.out != arrayOut[i * 4 + j]) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkMinUlong3Ulong3Ulong3" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkMinUlong4Ulong4Ulong4() {
+        Allocation inV1 = createRandomAllocation(mRS, Element.DataType.UNSIGNED_64, 4, 0x809de99dd1a4cc43l, false);
+        Allocation inV2 = createRandomAllocation(mRS, Element.DataType.UNSIGNED_64, 4, 0x809de99dd1a4cc44l, false);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_64, 4), INPUTSIZE);
+            script.set_gAllocInV2(inV2);
+            script.forEach_testMinUlong4Ulong4Ulong4(inV1, out);
+            verifyResultsMinUlong4Ulong4Ulong4(inV1, inV2, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testMinUlong4Ulong4Ulong4: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.UNSIGNED_64, 4), INPUTSIZE);
+            scriptRelaxed.set_gAllocInV2(inV2);
+            scriptRelaxed.forEach_testMinUlong4Ulong4Ulong4(inV1, out);
+            verifyResultsMinUlong4Ulong4Ulong4(inV1, inV2, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testMinUlong4Ulong4Ulong4: " + e.toString());
+        }
+    }
+
+    private void verifyResultsMinUlong4Ulong4Ulong4(Allocation inV1, Allocation inV2, Allocation out, boolean relaxed) {
+        long[] arrayInV1 = new long[INPUTSIZE * 4];
+        inV1.copyTo(arrayInV1);
+        long[] arrayInV2 = new long[INPUTSIZE * 4];
+        inV2.copyTo(arrayInV2);
+        long[] arrayOut = new long[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 4 ; j++) {
+                // Extract the inputs.
+                ArgumentsUlongUlongUlong args = new ArgumentsUlongUlongUlong();
+                args.inV1 = arrayInV1[i * 4 + j];
+                args.inV2 = arrayInV2[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeMin(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (args.out != arrayOut[i * 4 + j]) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV1: ");
+                    message.append(String.format("0x%x", args.inV1));
+                    message.append("\n");
+                    message.append("Input inV2: ");
+                    message.append(String.format("0x%x", args.inV2));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(String.format("0x%x", args.out));
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("0x%x", arrayOut[i * 4 + j]));
+                    if (args.out != arrayOut[i * 4 + j]) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkMinUlong4Ulong4Ulong4" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
     public void testMin() {
         checkMinFloatFloatFloat();
         checkMinFloat2Float2Float2();
         checkMinFloat3Float3Float3();
         checkMinFloat4Float4Float4();
         checkMinCharCharChar();
-        checkMinUcharUcharUchar();
-        checkMinShortShortShort();
-        checkMinUshortUshortUshort();
-        checkMinIntIntInt();
-        checkMinUintUintUint();
         checkMinChar2Char2Char2();
-        checkMinUchar2Uchar2Uchar2();
-        checkMinShort2Short2Short2();
-        checkMinUshort2Ushort2Ushort2();
-        checkMinInt2Int2Int2();
-        checkMinUint2Uint2Uint2();
         checkMinChar3Char3Char3();
-        checkMinUchar3Uchar3Uchar3();
-        checkMinShort3Short3Short3();
-        checkMinUshort3Ushort3Ushort3();
-        checkMinInt3Int3Int3();
-        checkMinUint3Uint3Uint3();
         checkMinChar4Char4Char4();
+        checkMinUcharUcharUchar();
+        checkMinUchar2Uchar2Uchar2();
+        checkMinUchar3Uchar3Uchar3();
         checkMinUchar4Uchar4Uchar4();
+        checkMinShortShortShort();
+        checkMinShort2Short2Short2();
+        checkMinShort3Short3Short3();
         checkMinShort4Short4Short4();
+        checkMinUshortUshortUshort();
+        checkMinUshort2Ushort2Ushort2();
+        checkMinUshort3Ushort3Ushort3();
         checkMinUshort4Ushort4Ushort4();
+        checkMinIntIntInt();
+        checkMinInt2Int2Int2();
+        checkMinInt3Int3Int3();
         checkMinInt4Int4Int4();
+        checkMinUintUintUint();
+        checkMinUint2Uint2Uint2();
+        checkMinUint3Uint3Uint3();
         checkMinUint4Uint4Uint4();
+        checkMinLongLongLong();
+        checkMinLong2Long2Long2();
+        checkMinLong3Long3Long3();
+        checkMinLong4Long4Long4();
+        checkMinUlongUlongUlong();
+        checkMinUlong2Ulong2Ulong2();
+        checkMinUlong3Ulong3Ulong3();
+        checkMinUlong4Ulong4Ulong4();
     }
 }
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/TestMix.java b/tests/tests/renderscript/src/android/renderscript/cts/TestMix.java
index e9618cf..adcea66 100644
--- a/tests/tests/renderscript/src/android/renderscript/cts/TestMix.java
+++ b/tests/tests/renderscript/src/android/renderscript/cts/TestMix.java
@@ -84,7 +84,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeMix(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 1 + j])) {
                     valid = false;
@@ -163,7 +163,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeMix(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 2 + j])) {
                     valid = false;
@@ -242,7 +242,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeMix(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 4 + j])) {
                     valid = false;
@@ -321,7 +321,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeMix(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 4 + j])) {
                     valid = false;
@@ -400,7 +400,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeMix(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 2 + j])) {
                     valid = false;
@@ -479,7 +479,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeMix(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 4 + j])) {
                     valid = false;
@@ -558,7 +558,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeMix(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 4 + j])) {
                     valid = false;
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/TestModf.java b/tests/tests/renderscript/src/android/renderscript/cts/TestModf.java
index 1d2190f..bffe9b9 100644
--- a/tests/tests/renderscript/src/android/renderscript/cts/TestModf.java
+++ b/tests/tests/renderscript/src/android/renderscript/cts/TestModf.java
@@ -77,7 +77,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeModf(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.outIret.couldBe(arrayOutIret[i * 1 + j])) {
                     valid = false;
@@ -155,7 +155,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeModf(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.outIret.couldBe(arrayOutIret[i * 2 + j])) {
                     valid = false;
@@ -233,7 +233,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeModf(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.outIret.couldBe(arrayOutIret[i * 4 + j])) {
                     valid = false;
@@ -311,7 +311,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeModf(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.outIret.couldBe(arrayOutIret[i * 4 + j])) {
                     valid = false;
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/TestNan.java b/tests/tests/renderscript/src/android/renderscript/cts/TestNan.java
index d8ae72b..f00b951 100644
--- a/tests/tests/renderscript/src/android/renderscript/cts/TestNan.java
+++ b/tests/tests/renderscript/src/android/renderscript/cts/TestNan.java
@@ -70,7 +70,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeNan(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 1 + j])) {
                     valid = false;
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/TestNativeAcos.java b/tests/tests/renderscript/src/android/renderscript/cts/TestNativeAcos.java
new file mode 100644
index 0000000..793f609
--- /dev/null
+++ b/tests/tests/renderscript/src/android/renderscript/cts/TestNativeAcos.java
@@ -0,0 +1,284 @@
+/*
+ * 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.
+ */
+
+// Don't edit this file!  It is auto-generated by frameworks/rs/api/gen_runtime.
+
+package android.renderscript.cts;
+
+import android.renderscript.Allocation;
+import android.renderscript.RSRuntimeException;
+import android.renderscript.Element;
+
+public class TestNativeAcos extends RSBaseCompute {
+
+    private ScriptC_TestNativeAcos script;
+    private ScriptC_TestNativeAcosRelaxed scriptRelaxed;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        script = new ScriptC_TestNativeAcos(mRS);
+        scriptRelaxed = new ScriptC_TestNativeAcosRelaxed(mRS);
+    }
+
+    public class ArgumentsFloatFloat {
+        public float inV;
+        public Floaty out;
+    }
+
+    private void checkNativeAcosFloatFloat() {
+        Allocation inV = createRandomFloatAllocation(mRS, Element.DataType.FLOAT_32, 1, 0x7749aa14657f3d94l, -1, 1);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 1), INPUTSIZE);
+            script.forEach_testNativeAcosFloatFloat(inV, out);
+            verifyResultsNativeAcosFloatFloat(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeAcosFloatFloat: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 1), INPUTSIZE);
+            scriptRelaxed.forEach_testNativeAcosFloatFloat(inV, out);
+            verifyResultsNativeAcosFloatFloat(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeAcosFloatFloat: " + e.toString());
+        }
+    }
+
+    private void verifyResultsNativeAcosFloatFloat(Allocation inV, Allocation out, boolean relaxed) {
+        float[] arrayInV = new float[INPUTSIZE * 1];
+        inV.copyTo(arrayInV);
+        float[] arrayOut = new float[INPUTSIZE * 1];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 1 ; j++) {
+                // Extract the inputs.
+                ArgumentsFloatFloat args = new ArgumentsFloatFloat();
+                args.inV = arrayInV[i];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeNativeAcos(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (!args.out.couldBe(arrayOut[i * 1 + j])) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            args.inV, Float.floatToRawIntBits(args.inV), args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(args.out.toString());
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            arrayOut[i * 1 + j], Float.floatToRawIntBits(arrayOut[i * 1 + j]), arrayOut[i * 1 + j]));
+                    if (!args.out.couldBe(arrayOut[i * 1 + j])) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkNativeAcosFloatFloat" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkNativeAcosFloat2Float2() {
+        Allocation inV = createRandomFloatAllocation(mRS, Element.DataType.FLOAT_32, 2, 0x92e82297d87e02d8l, -1, 1);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 2), INPUTSIZE);
+            script.forEach_testNativeAcosFloat2Float2(inV, out);
+            verifyResultsNativeAcosFloat2Float2(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeAcosFloat2Float2: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 2), INPUTSIZE);
+            scriptRelaxed.forEach_testNativeAcosFloat2Float2(inV, out);
+            verifyResultsNativeAcosFloat2Float2(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeAcosFloat2Float2: " + e.toString());
+        }
+    }
+
+    private void verifyResultsNativeAcosFloat2Float2(Allocation inV, Allocation out, boolean relaxed) {
+        float[] arrayInV = new float[INPUTSIZE * 2];
+        inV.copyTo(arrayInV);
+        float[] arrayOut = new float[INPUTSIZE * 2];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 2 ; j++) {
+                // Extract the inputs.
+                ArgumentsFloatFloat args = new ArgumentsFloatFloat();
+                args.inV = arrayInV[i * 2 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeNativeAcos(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (!args.out.couldBe(arrayOut[i * 2 + j])) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            args.inV, Float.floatToRawIntBits(args.inV), args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(args.out.toString());
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            arrayOut[i * 2 + j], Float.floatToRawIntBits(arrayOut[i * 2 + j]), arrayOut[i * 2 + j]));
+                    if (!args.out.couldBe(arrayOut[i * 2 + j])) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkNativeAcosFloat2Float2" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkNativeAcosFloat3Float3() {
+        Allocation inV = createRandomFloatAllocation(mRS, Element.DataType.FLOAT_32, 3, 0x92e9ebb2ce9923b6l, -1, 1);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 3), INPUTSIZE);
+            script.forEach_testNativeAcosFloat3Float3(inV, out);
+            verifyResultsNativeAcosFloat3Float3(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeAcosFloat3Float3: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 3), INPUTSIZE);
+            scriptRelaxed.forEach_testNativeAcosFloat3Float3(inV, out);
+            verifyResultsNativeAcosFloat3Float3(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeAcosFloat3Float3: " + e.toString());
+        }
+    }
+
+    private void verifyResultsNativeAcosFloat3Float3(Allocation inV, Allocation out, boolean relaxed) {
+        float[] arrayInV = new float[INPUTSIZE * 4];
+        inV.copyTo(arrayInV);
+        float[] arrayOut = new float[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 3 ; j++) {
+                // Extract the inputs.
+                ArgumentsFloatFloat args = new ArgumentsFloatFloat();
+                args.inV = arrayInV[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeNativeAcos(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            args.inV, Float.floatToRawIntBits(args.inV), args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(args.out.toString());
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            arrayOut[i * 4 + j], Float.floatToRawIntBits(arrayOut[i * 4 + j]), arrayOut[i * 4 + j]));
+                    if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkNativeAcosFloat3Float3" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkNativeAcosFloat4Float4() {
+        Allocation inV = createRandomFloatAllocation(mRS, Element.DataType.FLOAT_32, 4, 0x92ebb4cdc4b44494l, -1, 1);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 4), INPUTSIZE);
+            script.forEach_testNativeAcosFloat4Float4(inV, out);
+            verifyResultsNativeAcosFloat4Float4(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeAcosFloat4Float4: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 4), INPUTSIZE);
+            scriptRelaxed.forEach_testNativeAcosFloat4Float4(inV, out);
+            verifyResultsNativeAcosFloat4Float4(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeAcosFloat4Float4: " + e.toString());
+        }
+    }
+
+    private void verifyResultsNativeAcosFloat4Float4(Allocation inV, Allocation out, boolean relaxed) {
+        float[] arrayInV = new float[INPUTSIZE * 4];
+        inV.copyTo(arrayInV);
+        float[] arrayOut = new float[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 4 ; j++) {
+                // Extract the inputs.
+                ArgumentsFloatFloat args = new ArgumentsFloatFloat();
+                args.inV = arrayInV[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeNativeAcos(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            args.inV, Float.floatToRawIntBits(args.inV), args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(args.out.toString());
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            arrayOut[i * 4 + j], Float.floatToRawIntBits(arrayOut[i * 4 + j]), arrayOut[i * 4 + j]));
+                    if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkNativeAcosFloat4Float4" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    public void testNativeAcos() {
+        checkNativeAcosFloatFloat();
+        checkNativeAcosFloat2Float2();
+        checkNativeAcosFloat3Float3();
+        checkNativeAcosFloat4Float4();
+    }
+}
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/TestNativeAcosh.java b/tests/tests/renderscript/src/android/renderscript/cts/TestNativeAcosh.java
new file mode 100644
index 0000000..ae56fcf
--- /dev/null
+++ b/tests/tests/renderscript/src/android/renderscript/cts/TestNativeAcosh.java
@@ -0,0 +1,284 @@
+/*
+ * 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.
+ */
+
+// Don't edit this file!  It is auto-generated by frameworks/rs/api/gen_runtime.
+
+package android.renderscript.cts;
+
+import android.renderscript.Allocation;
+import android.renderscript.RSRuntimeException;
+import android.renderscript.Element;
+
+public class TestNativeAcosh extends RSBaseCompute {
+
+    private ScriptC_TestNativeAcosh script;
+    private ScriptC_TestNativeAcoshRelaxed scriptRelaxed;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        script = new ScriptC_TestNativeAcosh(mRS);
+        scriptRelaxed = new ScriptC_TestNativeAcoshRelaxed(mRS);
+    }
+
+    public class ArgumentsFloatFloat {
+        public float in;
+        public Floaty out;
+    }
+
+    private void checkNativeAcoshFloatFloat() {
+        Allocation in = createRandomAllocation(mRS, Element.DataType.FLOAT_32, 1, 0x332857238c9c505cl, false);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 1), INPUTSIZE);
+            script.forEach_testNativeAcoshFloatFloat(in, out);
+            verifyResultsNativeAcoshFloatFloat(in, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeAcoshFloatFloat: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 1), INPUTSIZE);
+            scriptRelaxed.forEach_testNativeAcoshFloatFloat(in, out);
+            verifyResultsNativeAcoshFloatFloat(in, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeAcoshFloatFloat: " + e.toString());
+        }
+    }
+
+    private void verifyResultsNativeAcoshFloatFloat(Allocation in, Allocation out, boolean relaxed) {
+        float[] arrayIn = new float[INPUTSIZE * 1];
+        in.copyTo(arrayIn);
+        float[] arrayOut = new float[INPUTSIZE * 1];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 1 ; j++) {
+                // Extract the inputs.
+                ArgumentsFloatFloat args = new ArgumentsFloatFloat();
+                args.in = arrayIn[i];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeNativeAcosh(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (!args.out.couldBe(arrayOut[i * 1 + j])) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input in: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            args.in, Float.floatToRawIntBits(args.in), args.in));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(args.out.toString());
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            arrayOut[i * 1 + j], Float.floatToRawIntBits(arrayOut[i * 1 + j]), arrayOut[i * 1 + j]));
+                    if (!args.out.couldBe(arrayOut[i * 1 + j])) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkNativeAcoshFloatFloat" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkNativeAcoshFloat2Float2() {
+        Allocation in = createRandomAllocation(mRS, Element.DataType.FLOAT_32, 2, 0x7e5c81be4638e398l, false);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 2), INPUTSIZE);
+            script.forEach_testNativeAcoshFloat2Float2(in, out);
+            verifyResultsNativeAcoshFloat2Float2(in, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeAcoshFloat2Float2: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 2), INPUTSIZE);
+            scriptRelaxed.forEach_testNativeAcoshFloat2Float2(in, out);
+            verifyResultsNativeAcoshFloat2Float2(in, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeAcoshFloat2Float2: " + e.toString());
+        }
+    }
+
+    private void verifyResultsNativeAcoshFloat2Float2(Allocation in, Allocation out, boolean relaxed) {
+        float[] arrayIn = new float[INPUTSIZE * 2];
+        in.copyTo(arrayIn);
+        float[] arrayOut = new float[INPUTSIZE * 2];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 2 ; j++) {
+                // Extract the inputs.
+                ArgumentsFloatFloat args = new ArgumentsFloatFloat();
+                args.in = arrayIn[i * 2 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeNativeAcosh(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (!args.out.couldBe(arrayOut[i * 2 + j])) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input in: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            args.in, Float.floatToRawIntBits(args.in), args.in));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(args.out.toString());
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            arrayOut[i * 2 + j], Float.floatToRawIntBits(arrayOut[i * 2 + j]), arrayOut[i * 2 + j]));
+                    if (!args.out.couldBe(arrayOut[i * 2 + j])) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkNativeAcoshFloat2Float2" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkNativeAcoshFloat3Float3() {
+        Allocation in = createRandomAllocation(mRS, Element.DataType.FLOAT_32, 3, 0x7e5c8c5fa53f7932l, false);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 3), INPUTSIZE);
+            script.forEach_testNativeAcoshFloat3Float3(in, out);
+            verifyResultsNativeAcoshFloat3Float3(in, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeAcoshFloat3Float3: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 3), INPUTSIZE);
+            scriptRelaxed.forEach_testNativeAcoshFloat3Float3(in, out);
+            verifyResultsNativeAcoshFloat3Float3(in, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeAcoshFloat3Float3: " + e.toString());
+        }
+    }
+
+    private void verifyResultsNativeAcoshFloat3Float3(Allocation in, Allocation out, boolean relaxed) {
+        float[] arrayIn = new float[INPUTSIZE * 4];
+        in.copyTo(arrayIn);
+        float[] arrayOut = new float[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 3 ; j++) {
+                // Extract the inputs.
+                ArgumentsFloatFloat args = new ArgumentsFloatFloat();
+                args.in = arrayIn[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeNativeAcosh(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input in: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            args.in, Float.floatToRawIntBits(args.in), args.in));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(args.out.toString());
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            arrayOut[i * 4 + j], Float.floatToRawIntBits(arrayOut[i * 4 + j]), arrayOut[i * 4 + j]));
+                    if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkNativeAcoshFloat3Float3" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkNativeAcoshFloat4Float4() {
+        Allocation in = createRandomAllocation(mRS, Element.DataType.FLOAT_32, 4, 0x7e5c970104460eccl, false);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 4), INPUTSIZE);
+            script.forEach_testNativeAcoshFloat4Float4(in, out);
+            verifyResultsNativeAcoshFloat4Float4(in, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeAcoshFloat4Float4: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 4), INPUTSIZE);
+            scriptRelaxed.forEach_testNativeAcoshFloat4Float4(in, out);
+            verifyResultsNativeAcoshFloat4Float4(in, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeAcoshFloat4Float4: " + e.toString());
+        }
+    }
+
+    private void verifyResultsNativeAcoshFloat4Float4(Allocation in, Allocation out, boolean relaxed) {
+        float[] arrayIn = new float[INPUTSIZE * 4];
+        in.copyTo(arrayIn);
+        float[] arrayOut = new float[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 4 ; j++) {
+                // Extract the inputs.
+                ArgumentsFloatFloat args = new ArgumentsFloatFloat();
+                args.in = arrayIn[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeNativeAcosh(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input in: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            args.in, Float.floatToRawIntBits(args.in), args.in));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(args.out.toString());
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            arrayOut[i * 4 + j], Float.floatToRawIntBits(arrayOut[i * 4 + j]), arrayOut[i * 4 + j]));
+                    if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkNativeAcoshFloat4Float4" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    public void testNativeAcosh() {
+        checkNativeAcoshFloatFloat();
+        checkNativeAcoshFloat2Float2();
+        checkNativeAcoshFloat3Float3();
+        checkNativeAcoshFloat4Float4();
+    }
+}
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/TestNativeAcospi.java b/tests/tests/renderscript/src/android/renderscript/cts/TestNativeAcospi.java
new file mode 100644
index 0000000..b8adf58
--- /dev/null
+++ b/tests/tests/renderscript/src/android/renderscript/cts/TestNativeAcospi.java
@@ -0,0 +1,284 @@
+/*
+ * 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.
+ */
+
+// Don't edit this file!  It is auto-generated by frameworks/rs/api/gen_runtime.
+
+package android.renderscript.cts;
+
+import android.renderscript.Allocation;
+import android.renderscript.RSRuntimeException;
+import android.renderscript.Element;
+
+public class TestNativeAcospi extends RSBaseCompute {
+
+    private ScriptC_TestNativeAcospi script;
+    private ScriptC_TestNativeAcospiRelaxed scriptRelaxed;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        script = new ScriptC_TestNativeAcospi(mRS);
+        scriptRelaxed = new ScriptC_TestNativeAcospiRelaxed(mRS);
+    }
+
+    public class ArgumentsFloatFloat {
+        public float inV;
+        public Floaty out;
+    }
+
+    private void checkNativeAcospiFloatFloat() {
+        Allocation inV = createRandomFloatAllocation(mRS, Element.DataType.FLOAT_32, 1, 0xdcf67f5599c7d8ffl, -1, 1);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 1), INPUTSIZE);
+            script.forEach_testNativeAcospiFloatFloat(inV, out);
+            verifyResultsNativeAcospiFloatFloat(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeAcospiFloatFloat: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 1), INPUTSIZE);
+            scriptRelaxed.forEach_testNativeAcospiFloatFloat(inV, out);
+            verifyResultsNativeAcospiFloatFloat(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeAcospiFloatFloat: " + e.toString());
+        }
+    }
+
+    private void verifyResultsNativeAcospiFloatFloat(Allocation inV, Allocation out, boolean relaxed) {
+        float[] arrayInV = new float[INPUTSIZE * 1];
+        inV.copyTo(arrayInV);
+        float[] arrayOut = new float[INPUTSIZE * 1];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 1 ; j++) {
+                // Extract the inputs.
+                ArgumentsFloatFloat args = new ArgumentsFloatFloat();
+                args.inV = arrayInV[i];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeNativeAcospi(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (!args.out.couldBe(arrayOut[i * 1 + j])) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            args.inV, Float.floatToRawIntBits(args.inV), args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(args.out.toString());
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            arrayOut[i * 1 + j], Float.floatToRawIntBits(arrayOut[i * 1 + j]), arrayOut[i * 1 + j]));
+                    if (!args.out.couldBe(arrayOut[i * 1 + j])) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkNativeAcospiFloatFloat" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkNativeAcospiFloat2Float2() {
+        Allocation inV = createRandomFloatAllocation(mRS, Element.DataType.FLOAT_32, 2, 0xf038668a78e88aabl, -1, 1);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 2), INPUTSIZE);
+            script.forEach_testNativeAcospiFloat2Float2(inV, out);
+            verifyResultsNativeAcospiFloat2Float2(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeAcospiFloat2Float2: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 2), INPUTSIZE);
+            scriptRelaxed.forEach_testNativeAcospiFloat2Float2(inV, out);
+            verifyResultsNativeAcospiFloat2Float2(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeAcospiFloat2Float2: " + e.toString());
+        }
+    }
+
+    private void verifyResultsNativeAcospiFloat2Float2(Allocation inV, Allocation out, boolean relaxed) {
+        float[] arrayInV = new float[INPUTSIZE * 2];
+        inV.copyTo(arrayInV);
+        float[] arrayOut = new float[INPUTSIZE * 2];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 2 ; j++) {
+                // Extract the inputs.
+                ArgumentsFloatFloat args = new ArgumentsFloatFloat();
+                args.inV = arrayInV[i * 2 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeNativeAcospi(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (!args.out.couldBe(arrayOut[i * 2 + j])) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            args.inV, Float.floatToRawIntBits(args.inV), args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(args.out.toString());
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            arrayOut[i * 2 + j], Float.floatToRawIntBits(arrayOut[i * 2 + j]), arrayOut[i * 2 + j]));
+                    if (!args.out.couldBe(arrayOut[i * 2 + j])) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkNativeAcospiFloat2Float2" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkNativeAcospiFloat3Float3() {
+        Allocation inV = createRandomFloatAllocation(mRS, Element.DataType.FLOAT_32, 3, 0xf03a2fa56f03ab89l, -1, 1);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 3), INPUTSIZE);
+            script.forEach_testNativeAcospiFloat3Float3(inV, out);
+            verifyResultsNativeAcospiFloat3Float3(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeAcospiFloat3Float3: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 3), INPUTSIZE);
+            scriptRelaxed.forEach_testNativeAcospiFloat3Float3(inV, out);
+            verifyResultsNativeAcospiFloat3Float3(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeAcospiFloat3Float3: " + e.toString());
+        }
+    }
+
+    private void verifyResultsNativeAcospiFloat3Float3(Allocation inV, Allocation out, boolean relaxed) {
+        float[] arrayInV = new float[INPUTSIZE * 4];
+        inV.copyTo(arrayInV);
+        float[] arrayOut = new float[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 3 ; j++) {
+                // Extract the inputs.
+                ArgumentsFloatFloat args = new ArgumentsFloatFloat();
+                args.inV = arrayInV[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeNativeAcospi(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            args.inV, Float.floatToRawIntBits(args.inV), args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(args.out.toString());
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            arrayOut[i * 4 + j], Float.floatToRawIntBits(arrayOut[i * 4 + j]), arrayOut[i * 4 + j]));
+                    if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkNativeAcospiFloat3Float3" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkNativeAcospiFloat4Float4() {
+        Allocation inV = createRandomFloatAllocation(mRS, Element.DataType.FLOAT_32, 4, 0xf03bf8c0651ecc67l, -1, 1);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 4), INPUTSIZE);
+            script.forEach_testNativeAcospiFloat4Float4(inV, out);
+            verifyResultsNativeAcospiFloat4Float4(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeAcospiFloat4Float4: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 4), INPUTSIZE);
+            scriptRelaxed.forEach_testNativeAcospiFloat4Float4(inV, out);
+            verifyResultsNativeAcospiFloat4Float4(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeAcospiFloat4Float4: " + e.toString());
+        }
+    }
+
+    private void verifyResultsNativeAcospiFloat4Float4(Allocation inV, Allocation out, boolean relaxed) {
+        float[] arrayInV = new float[INPUTSIZE * 4];
+        inV.copyTo(arrayInV);
+        float[] arrayOut = new float[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 4 ; j++) {
+                // Extract the inputs.
+                ArgumentsFloatFloat args = new ArgumentsFloatFloat();
+                args.inV = arrayInV[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeNativeAcospi(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            args.inV, Float.floatToRawIntBits(args.inV), args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(args.out.toString());
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            arrayOut[i * 4 + j], Float.floatToRawIntBits(arrayOut[i * 4 + j]), arrayOut[i * 4 + j]));
+                    if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkNativeAcospiFloat4Float4" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    public void testNativeAcospi() {
+        checkNativeAcospiFloatFloat();
+        checkNativeAcospiFloat2Float2();
+        checkNativeAcospiFloat3Float3();
+        checkNativeAcospiFloat4Float4();
+    }
+}
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/TestNativeAsin.java b/tests/tests/renderscript/src/android/renderscript/cts/TestNativeAsin.java
new file mode 100644
index 0000000..95a4980
--- /dev/null
+++ b/tests/tests/renderscript/src/android/renderscript/cts/TestNativeAsin.java
@@ -0,0 +1,284 @@
+/*
+ * 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.
+ */
+
+// Don't edit this file!  It is auto-generated by frameworks/rs/api/gen_runtime.
+
+package android.renderscript.cts;
+
+import android.renderscript.Allocation;
+import android.renderscript.RSRuntimeException;
+import android.renderscript.Element;
+
+public class TestNativeAsin extends RSBaseCompute {
+
+    private ScriptC_TestNativeAsin script;
+    private ScriptC_TestNativeAsinRelaxed scriptRelaxed;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        script = new ScriptC_TestNativeAsin(mRS);
+        scriptRelaxed = new ScriptC_TestNativeAsinRelaxed(mRS);
+    }
+
+    public class ArgumentsFloatFloat {
+        public float inV;
+        public Floaty out;
+    }
+
+    private void checkNativeAsinFloatFloat() {
+        Allocation inV = createRandomFloatAllocation(mRS, Element.DataType.FLOAT_32, 1, 0x1167d6d8d3e5bc7l, -1, 1);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 1), INPUTSIZE);
+            script.forEach_testNativeAsinFloatFloat(inV, out);
+            verifyResultsNativeAsinFloatFloat(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeAsinFloatFloat: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 1), INPUTSIZE);
+            scriptRelaxed.forEach_testNativeAsinFloatFloat(inV, out);
+            verifyResultsNativeAsinFloatFloat(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeAsinFloatFloat: " + e.toString());
+        }
+    }
+
+    private void verifyResultsNativeAsinFloatFloat(Allocation inV, Allocation out, boolean relaxed) {
+        float[] arrayInV = new float[INPUTSIZE * 1];
+        inV.copyTo(arrayInV);
+        float[] arrayOut = new float[INPUTSIZE * 1];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 1 ; j++) {
+                // Extract the inputs.
+                ArgumentsFloatFloat args = new ArgumentsFloatFloat();
+                args.inV = arrayInV[i];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeNativeAsin(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (!args.out.couldBe(arrayOut[i * 1 + j])) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            args.inV, Float.floatToRawIntBits(args.inV), args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(args.out.toString());
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            arrayOut[i * 1 + j], Float.floatToRawIntBits(arrayOut[i * 1 + j]), arrayOut[i * 1 + j]));
+                    if (!args.out.couldBe(arrayOut[i * 1 + j])) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkNativeAsinFloatFloat" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkNativeAsinFloat2Float2() {
+        Allocation inV = createRandomFloatAllocation(mRS, Element.DataType.FLOAT_32, 2, 0xdb4aa187ebdf2133l, -1, 1);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 2), INPUTSIZE);
+            script.forEach_testNativeAsinFloat2Float2(inV, out);
+            verifyResultsNativeAsinFloat2Float2(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeAsinFloat2Float2: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 2), INPUTSIZE);
+            scriptRelaxed.forEach_testNativeAsinFloat2Float2(inV, out);
+            verifyResultsNativeAsinFloat2Float2(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeAsinFloat2Float2: " + e.toString());
+        }
+    }
+
+    private void verifyResultsNativeAsinFloat2Float2(Allocation inV, Allocation out, boolean relaxed) {
+        float[] arrayInV = new float[INPUTSIZE * 2];
+        inV.copyTo(arrayInV);
+        float[] arrayOut = new float[INPUTSIZE * 2];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 2 ; j++) {
+                // Extract the inputs.
+                ArgumentsFloatFloat args = new ArgumentsFloatFloat();
+                args.inV = arrayInV[i * 2 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeNativeAsin(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (!args.out.couldBe(arrayOut[i * 2 + j])) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            args.inV, Float.floatToRawIntBits(args.inV), args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(args.out.toString());
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            arrayOut[i * 2 + j], Float.floatToRawIntBits(arrayOut[i * 2 + j]), arrayOut[i * 2 + j]));
+                    if (!args.out.couldBe(arrayOut[i * 2 + j])) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkNativeAsinFloat2Float2" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkNativeAsinFloat3Float3() {
+        Allocation inV = createRandomFloatAllocation(mRS, Element.DataType.FLOAT_32, 3, 0xdb4c6aa2e1fa4211l, -1, 1);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 3), INPUTSIZE);
+            script.forEach_testNativeAsinFloat3Float3(inV, out);
+            verifyResultsNativeAsinFloat3Float3(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeAsinFloat3Float3: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 3), INPUTSIZE);
+            scriptRelaxed.forEach_testNativeAsinFloat3Float3(inV, out);
+            verifyResultsNativeAsinFloat3Float3(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeAsinFloat3Float3: " + e.toString());
+        }
+    }
+
+    private void verifyResultsNativeAsinFloat3Float3(Allocation inV, Allocation out, boolean relaxed) {
+        float[] arrayInV = new float[INPUTSIZE * 4];
+        inV.copyTo(arrayInV);
+        float[] arrayOut = new float[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 3 ; j++) {
+                // Extract the inputs.
+                ArgumentsFloatFloat args = new ArgumentsFloatFloat();
+                args.inV = arrayInV[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeNativeAsin(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            args.inV, Float.floatToRawIntBits(args.inV), args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(args.out.toString());
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            arrayOut[i * 4 + j], Float.floatToRawIntBits(arrayOut[i * 4 + j]), arrayOut[i * 4 + j]));
+                    if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkNativeAsinFloat3Float3" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkNativeAsinFloat4Float4() {
+        Allocation inV = createRandomFloatAllocation(mRS, Element.DataType.FLOAT_32, 4, 0xdb4e33bdd81562efl, -1, 1);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 4), INPUTSIZE);
+            script.forEach_testNativeAsinFloat4Float4(inV, out);
+            verifyResultsNativeAsinFloat4Float4(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeAsinFloat4Float4: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 4), INPUTSIZE);
+            scriptRelaxed.forEach_testNativeAsinFloat4Float4(inV, out);
+            verifyResultsNativeAsinFloat4Float4(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeAsinFloat4Float4: " + e.toString());
+        }
+    }
+
+    private void verifyResultsNativeAsinFloat4Float4(Allocation inV, Allocation out, boolean relaxed) {
+        float[] arrayInV = new float[INPUTSIZE * 4];
+        inV.copyTo(arrayInV);
+        float[] arrayOut = new float[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 4 ; j++) {
+                // Extract the inputs.
+                ArgumentsFloatFloat args = new ArgumentsFloatFloat();
+                args.inV = arrayInV[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeNativeAsin(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            args.inV, Float.floatToRawIntBits(args.inV), args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(args.out.toString());
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            arrayOut[i * 4 + j], Float.floatToRawIntBits(arrayOut[i * 4 + j]), arrayOut[i * 4 + j]));
+                    if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkNativeAsinFloat4Float4" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    public void testNativeAsin() {
+        checkNativeAsinFloatFloat();
+        checkNativeAsinFloat2Float2();
+        checkNativeAsinFloat3Float3();
+        checkNativeAsinFloat4Float4();
+    }
+}
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/TestNativeAsinh.java b/tests/tests/renderscript/src/android/renderscript/cts/TestNativeAsinh.java
new file mode 100644
index 0000000..830140e
--- /dev/null
+++ b/tests/tests/renderscript/src/android/renderscript/cts/TestNativeAsinh.java
@@ -0,0 +1,284 @@
+/*
+ * 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.
+ */
+
+// Don't edit this file!  It is auto-generated by frameworks/rs/api/gen_runtime.
+
+package android.renderscript.cts;
+
+import android.renderscript.Allocation;
+import android.renderscript.RSRuntimeException;
+import android.renderscript.Element;
+
+public class TestNativeAsinh extends RSBaseCompute {
+
+    private ScriptC_TestNativeAsinh script;
+    private ScriptC_TestNativeAsinhRelaxed scriptRelaxed;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        script = new ScriptC_TestNativeAsinh(mRS);
+        scriptRelaxed = new ScriptC_TestNativeAsinhRelaxed(mRS);
+    }
+
+    public class ArgumentsFloatFloat {
+        public float in;
+        public Floaty out;
+    }
+
+    private void checkNativeAsinhFloatFloat() {
+        Allocation in = createRandomAllocation(mRS, Element.DataType.FLOAT_32, 1, 0xbcf52a7cb45b6e8fl, false);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 1), INPUTSIZE);
+            script.forEach_testNativeAsinhFloatFloat(in, out);
+            verifyResultsNativeAsinhFloatFloat(in, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeAsinhFloatFloat: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 1), INPUTSIZE);
+            scriptRelaxed.forEach_testNativeAsinhFloatFloat(in, out);
+            verifyResultsNativeAsinhFloatFloat(in, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeAsinhFloatFloat: " + e.toString());
+        }
+    }
+
+    private void verifyResultsNativeAsinhFloatFloat(Allocation in, Allocation out, boolean relaxed) {
+        float[] arrayIn = new float[INPUTSIZE * 1];
+        in.copyTo(arrayIn);
+        float[] arrayOut = new float[INPUTSIZE * 1];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 1 ; j++) {
+                // Extract the inputs.
+                ArgumentsFloatFloat args = new ArgumentsFloatFloat();
+                args.in = arrayIn[i];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeNativeAsinh(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (!args.out.couldBe(arrayOut[i * 1 + j])) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input in: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            args.in, Float.floatToRawIntBits(args.in), args.in));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(args.out.toString());
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            arrayOut[i * 1 + j], Float.floatToRawIntBits(arrayOut[i * 1 + j]), arrayOut[i * 1 + j]));
+                    if (!args.out.couldBe(arrayOut[i * 1 + j])) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkNativeAsinhFloatFloat" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkNativeAsinhFloat2Float2() {
+        Allocation in = createRandomAllocation(mRS, Element.DataType.FLOAT_32, 2, 0xc6bf00ae599a01f3l, false);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 2), INPUTSIZE);
+            script.forEach_testNativeAsinhFloat2Float2(in, out);
+            verifyResultsNativeAsinhFloat2Float2(in, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeAsinhFloat2Float2: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 2), INPUTSIZE);
+            scriptRelaxed.forEach_testNativeAsinhFloat2Float2(in, out);
+            verifyResultsNativeAsinhFloat2Float2(in, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeAsinhFloat2Float2: " + e.toString());
+        }
+    }
+
+    private void verifyResultsNativeAsinhFloat2Float2(Allocation in, Allocation out, boolean relaxed) {
+        float[] arrayIn = new float[INPUTSIZE * 2];
+        in.copyTo(arrayIn);
+        float[] arrayOut = new float[INPUTSIZE * 2];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 2 ; j++) {
+                // Extract the inputs.
+                ArgumentsFloatFloat args = new ArgumentsFloatFloat();
+                args.in = arrayIn[i * 2 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeNativeAsinh(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (!args.out.couldBe(arrayOut[i * 2 + j])) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input in: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            args.in, Float.floatToRawIntBits(args.in), args.in));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(args.out.toString());
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            arrayOut[i * 2 + j], Float.floatToRawIntBits(arrayOut[i * 2 + j]), arrayOut[i * 2 + j]));
+                    if (!args.out.couldBe(arrayOut[i * 2 + j])) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkNativeAsinhFloat2Float2" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkNativeAsinhFloat3Float3() {
+        Allocation in = createRandomAllocation(mRS, Element.DataType.FLOAT_32, 3, 0xc6bf0b4fb8a0978dl, false);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 3), INPUTSIZE);
+            script.forEach_testNativeAsinhFloat3Float3(in, out);
+            verifyResultsNativeAsinhFloat3Float3(in, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeAsinhFloat3Float3: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 3), INPUTSIZE);
+            scriptRelaxed.forEach_testNativeAsinhFloat3Float3(in, out);
+            verifyResultsNativeAsinhFloat3Float3(in, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeAsinhFloat3Float3: " + e.toString());
+        }
+    }
+
+    private void verifyResultsNativeAsinhFloat3Float3(Allocation in, Allocation out, boolean relaxed) {
+        float[] arrayIn = new float[INPUTSIZE * 4];
+        in.copyTo(arrayIn);
+        float[] arrayOut = new float[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 3 ; j++) {
+                // Extract the inputs.
+                ArgumentsFloatFloat args = new ArgumentsFloatFloat();
+                args.in = arrayIn[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeNativeAsinh(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input in: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            args.in, Float.floatToRawIntBits(args.in), args.in));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(args.out.toString());
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            arrayOut[i * 4 + j], Float.floatToRawIntBits(arrayOut[i * 4 + j]), arrayOut[i * 4 + j]));
+                    if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkNativeAsinhFloat3Float3" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkNativeAsinhFloat4Float4() {
+        Allocation in = createRandomAllocation(mRS, Element.DataType.FLOAT_32, 4, 0xc6bf15f117a72d27l, false);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 4), INPUTSIZE);
+            script.forEach_testNativeAsinhFloat4Float4(in, out);
+            verifyResultsNativeAsinhFloat4Float4(in, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeAsinhFloat4Float4: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 4), INPUTSIZE);
+            scriptRelaxed.forEach_testNativeAsinhFloat4Float4(in, out);
+            verifyResultsNativeAsinhFloat4Float4(in, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeAsinhFloat4Float4: " + e.toString());
+        }
+    }
+
+    private void verifyResultsNativeAsinhFloat4Float4(Allocation in, Allocation out, boolean relaxed) {
+        float[] arrayIn = new float[INPUTSIZE * 4];
+        in.copyTo(arrayIn);
+        float[] arrayOut = new float[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 4 ; j++) {
+                // Extract the inputs.
+                ArgumentsFloatFloat args = new ArgumentsFloatFloat();
+                args.in = arrayIn[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeNativeAsinh(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input in: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            args.in, Float.floatToRawIntBits(args.in), args.in));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(args.out.toString());
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            arrayOut[i * 4 + j], Float.floatToRawIntBits(arrayOut[i * 4 + j]), arrayOut[i * 4 + j]));
+                    if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkNativeAsinhFloat4Float4" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    public void testNativeAsinh() {
+        checkNativeAsinhFloatFloat();
+        checkNativeAsinhFloat2Float2();
+        checkNativeAsinhFloat3Float3();
+        checkNativeAsinhFloat4Float4();
+    }
+}
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/TestNativeAsinpi.java b/tests/tests/renderscript/src/android/renderscript/cts/TestNativeAsinpi.java
new file mode 100644
index 0000000..04824d8
--- /dev/null
+++ b/tests/tests/renderscript/src/android/renderscript/cts/TestNativeAsinpi.java
@@ -0,0 +1,284 @@
+/*
+ * 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.
+ */
+
+// Don't edit this file!  It is auto-generated by frameworks/rs/api/gen_runtime.
+
+package android.renderscript.cts;
+
+import android.renderscript.Allocation;
+import android.renderscript.RSRuntimeException;
+import android.renderscript.Element;
+
+public class TestNativeAsinpi extends RSBaseCompute {
+
+    private ScriptC_TestNativeAsinpi script;
+    private ScriptC_TestNativeAsinpiRelaxed scriptRelaxed;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        script = new ScriptC_TestNativeAsinpi(mRS);
+        scriptRelaxed = new ScriptC_TestNativeAsinpiRelaxed(mRS);
+    }
+
+    public class ArgumentsFloatFloat {
+        public float inV;
+        public Floaty out;
+    }
+
+    private void checkNativeAsinpiFloatFloat() {
+        Allocation inV = createRandomFloatAllocation(mRS, Element.DataType.FLOAT_32, 1, 0x2558fe45ad28f75al, -1, 1);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 1), INPUTSIZE);
+            script.forEach_testNativeAsinpiFloatFloat(inV, out);
+            verifyResultsNativeAsinpiFloatFloat(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeAsinpiFloatFloat: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 1), INPUTSIZE);
+            scriptRelaxed.forEach_testNativeAsinpiFloatFloat(inV, out);
+            verifyResultsNativeAsinpiFloatFloat(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeAsinpiFloatFloat: " + e.toString());
+        }
+    }
+
+    private void verifyResultsNativeAsinpiFloatFloat(Allocation inV, Allocation out, boolean relaxed) {
+        float[] arrayInV = new float[INPUTSIZE * 1];
+        inV.copyTo(arrayInV);
+        float[] arrayOut = new float[INPUTSIZE * 1];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 1 ; j++) {
+                // Extract the inputs.
+                ArgumentsFloatFloat args = new ArgumentsFloatFloat();
+                args.inV = arrayInV[i];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeNativeAsinpi(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (!args.out.couldBe(arrayOut[i * 1 + j])) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            args.inV, Float.floatToRawIntBits(args.inV), args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(args.out.toString());
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            arrayOut[i * 1 + j], Float.floatToRawIntBits(arrayOut[i * 1 + j]), arrayOut[i * 1 + j]));
+                    if (!args.out.couldBe(arrayOut[i * 1 + j])) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkNativeAsinpiFloatFloat" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkNativeAsinpiFloat2Float2() {
+        Allocation inV = createRandomFloatAllocation(mRS, Element.DataType.FLOAT_32, 2, 0xbf9f3a86715cc9eel, -1, 1);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 2), INPUTSIZE);
+            script.forEach_testNativeAsinpiFloat2Float2(inV, out);
+            verifyResultsNativeAsinpiFloat2Float2(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeAsinpiFloat2Float2: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 2), INPUTSIZE);
+            scriptRelaxed.forEach_testNativeAsinpiFloat2Float2(inV, out);
+            verifyResultsNativeAsinpiFloat2Float2(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeAsinpiFloat2Float2: " + e.toString());
+        }
+    }
+
+    private void verifyResultsNativeAsinpiFloat2Float2(Allocation inV, Allocation out, boolean relaxed) {
+        float[] arrayInV = new float[INPUTSIZE * 2];
+        inV.copyTo(arrayInV);
+        float[] arrayOut = new float[INPUTSIZE * 2];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 2 ; j++) {
+                // Extract the inputs.
+                ArgumentsFloatFloat args = new ArgumentsFloatFloat();
+                args.inV = arrayInV[i * 2 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeNativeAsinpi(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (!args.out.couldBe(arrayOut[i * 2 + j])) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            args.inV, Float.floatToRawIntBits(args.inV), args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(args.out.toString());
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            arrayOut[i * 2 + j], Float.floatToRawIntBits(arrayOut[i * 2 + j]), arrayOut[i * 2 + j]));
+                    if (!args.out.couldBe(arrayOut[i * 2 + j])) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkNativeAsinpiFloat2Float2" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkNativeAsinpiFloat3Float3() {
+        Allocation inV = createRandomFloatAllocation(mRS, Element.DataType.FLOAT_32, 3, 0xbfa103a16777eaccl, -1, 1);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 3), INPUTSIZE);
+            script.forEach_testNativeAsinpiFloat3Float3(inV, out);
+            verifyResultsNativeAsinpiFloat3Float3(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeAsinpiFloat3Float3: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 3), INPUTSIZE);
+            scriptRelaxed.forEach_testNativeAsinpiFloat3Float3(inV, out);
+            verifyResultsNativeAsinpiFloat3Float3(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeAsinpiFloat3Float3: " + e.toString());
+        }
+    }
+
+    private void verifyResultsNativeAsinpiFloat3Float3(Allocation inV, Allocation out, boolean relaxed) {
+        float[] arrayInV = new float[INPUTSIZE * 4];
+        inV.copyTo(arrayInV);
+        float[] arrayOut = new float[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 3 ; j++) {
+                // Extract the inputs.
+                ArgumentsFloatFloat args = new ArgumentsFloatFloat();
+                args.inV = arrayInV[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeNativeAsinpi(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            args.inV, Float.floatToRawIntBits(args.inV), args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(args.out.toString());
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            arrayOut[i * 4 + j], Float.floatToRawIntBits(arrayOut[i * 4 + j]), arrayOut[i * 4 + j]));
+                    if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkNativeAsinpiFloat3Float3" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkNativeAsinpiFloat4Float4() {
+        Allocation inV = createRandomFloatAllocation(mRS, Element.DataType.FLOAT_32, 4, 0xbfa2ccbc5d930baal, -1, 1);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 4), INPUTSIZE);
+            script.forEach_testNativeAsinpiFloat4Float4(inV, out);
+            verifyResultsNativeAsinpiFloat4Float4(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeAsinpiFloat4Float4: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 4), INPUTSIZE);
+            scriptRelaxed.forEach_testNativeAsinpiFloat4Float4(inV, out);
+            verifyResultsNativeAsinpiFloat4Float4(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeAsinpiFloat4Float4: " + e.toString());
+        }
+    }
+
+    private void verifyResultsNativeAsinpiFloat4Float4(Allocation inV, Allocation out, boolean relaxed) {
+        float[] arrayInV = new float[INPUTSIZE * 4];
+        inV.copyTo(arrayInV);
+        float[] arrayOut = new float[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 4 ; j++) {
+                // Extract the inputs.
+                ArgumentsFloatFloat args = new ArgumentsFloatFloat();
+                args.inV = arrayInV[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeNativeAsinpi(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            args.inV, Float.floatToRawIntBits(args.inV), args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(args.out.toString());
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            arrayOut[i * 4 + j], Float.floatToRawIntBits(arrayOut[i * 4 + j]), arrayOut[i * 4 + j]));
+                    if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkNativeAsinpiFloat4Float4" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    public void testNativeAsinpi() {
+        checkNativeAsinpiFloatFloat();
+        checkNativeAsinpiFloat2Float2();
+        checkNativeAsinpiFloat3Float3();
+        checkNativeAsinpiFloat4Float4();
+    }
+}
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/TestNativeAtan.java b/tests/tests/renderscript/src/android/renderscript/cts/TestNativeAtan.java
new file mode 100644
index 0000000..c60520c
--- /dev/null
+++ b/tests/tests/renderscript/src/android/renderscript/cts/TestNativeAtan.java
@@ -0,0 +1,284 @@
+/*
+ * 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.
+ */
+
+// Don't edit this file!  It is auto-generated by frameworks/rs/api/gen_runtime.
+
+package android.renderscript.cts;
+
+import android.renderscript.Allocation;
+import android.renderscript.RSRuntimeException;
+import android.renderscript.Element;
+
+public class TestNativeAtan extends RSBaseCompute {
+
+    private ScriptC_TestNativeAtan script;
+    private ScriptC_TestNativeAtanRelaxed scriptRelaxed;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        script = new ScriptC_TestNativeAtan(mRS);
+        scriptRelaxed = new ScriptC_TestNativeAtanRelaxed(mRS);
+    }
+
+    public class ArgumentsFloatFloat {
+        public float inV;
+        public Floaty out;
+    }
+
+    private void checkNativeAtanFloatFloat() {
+        Allocation inV = createRandomFloatAllocation(mRS, Element.DataType.FLOAT_32, 1, 0xaafbf9b325b34e42l, -1, 1);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 1), INPUTSIZE);
+            script.forEach_testNativeAtanFloatFloat(inV, out);
+            verifyResultsNativeAtanFloatFloat(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeAtanFloatFloat: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 1), INPUTSIZE);
+            scriptRelaxed.forEach_testNativeAtanFloatFloat(inV, out);
+            verifyResultsNativeAtanFloatFloat(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeAtanFloatFloat: " + e.toString());
+        }
+    }
+
+    private void verifyResultsNativeAtanFloatFloat(Allocation inV, Allocation out, boolean relaxed) {
+        float[] arrayInV = new float[INPUTSIZE * 1];
+        inV.copyTo(arrayInV);
+        float[] arrayOut = new float[INPUTSIZE * 1];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 1 ; j++) {
+                // Extract the inputs.
+                ArgumentsFloatFloat args = new ArgumentsFloatFloat();
+                args.inV = arrayInV[i];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeNativeAtan(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (!args.out.couldBe(arrayOut[i * 1 + j])) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            args.inV, Float.floatToRawIntBits(args.inV), args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(args.out.toString());
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            arrayOut[i * 1 + j], Float.floatToRawIntBits(arrayOut[i * 1 + j]), arrayOut[i * 1 + j]));
+                    if (!args.out.couldBe(arrayOut[i * 1 + j])) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkNativeAtanFloatFloat" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkNativeAtanFloat2Float2() {
+        Allocation inV = createRandomFloatAllocation(mRS, Element.DataType.FLOAT_32, 2, 0xf5c93432108a7b96l, -1, 1);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 2), INPUTSIZE);
+            script.forEach_testNativeAtanFloat2Float2(inV, out);
+            verifyResultsNativeAtanFloat2Float2(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeAtanFloat2Float2: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 2), INPUTSIZE);
+            scriptRelaxed.forEach_testNativeAtanFloat2Float2(inV, out);
+            verifyResultsNativeAtanFloat2Float2(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeAtanFloat2Float2: " + e.toString());
+        }
+    }
+
+    private void verifyResultsNativeAtanFloat2Float2(Allocation inV, Allocation out, boolean relaxed) {
+        float[] arrayInV = new float[INPUTSIZE * 2];
+        inV.copyTo(arrayInV);
+        float[] arrayOut = new float[INPUTSIZE * 2];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 2 ; j++) {
+                // Extract the inputs.
+                ArgumentsFloatFloat args = new ArgumentsFloatFloat();
+                args.inV = arrayInV[i * 2 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeNativeAtan(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (!args.out.couldBe(arrayOut[i * 2 + j])) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            args.inV, Float.floatToRawIntBits(args.inV), args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(args.out.toString());
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            arrayOut[i * 2 + j], Float.floatToRawIntBits(arrayOut[i * 2 + j]), arrayOut[i * 2 + j]));
+                    if (!args.out.couldBe(arrayOut[i * 2 + j])) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkNativeAtanFloat2Float2" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkNativeAtanFloat3Float3() {
+        Allocation inV = createRandomFloatAllocation(mRS, Element.DataType.FLOAT_32, 3, 0xf5cafd4d06a59c74l, -1, 1);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 3), INPUTSIZE);
+            script.forEach_testNativeAtanFloat3Float3(inV, out);
+            verifyResultsNativeAtanFloat3Float3(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeAtanFloat3Float3: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 3), INPUTSIZE);
+            scriptRelaxed.forEach_testNativeAtanFloat3Float3(inV, out);
+            verifyResultsNativeAtanFloat3Float3(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeAtanFloat3Float3: " + e.toString());
+        }
+    }
+
+    private void verifyResultsNativeAtanFloat3Float3(Allocation inV, Allocation out, boolean relaxed) {
+        float[] arrayInV = new float[INPUTSIZE * 4];
+        inV.copyTo(arrayInV);
+        float[] arrayOut = new float[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 3 ; j++) {
+                // Extract the inputs.
+                ArgumentsFloatFloat args = new ArgumentsFloatFloat();
+                args.inV = arrayInV[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeNativeAtan(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            args.inV, Float.floatToRawIntBits(args.inV), args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(args.out.toString());
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            arrayOut[i * 4 + j], Float.floatToRawIntBits(arrayOut[i * 4 + j]), arrayOut[i * 4 + j]));
+                    if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkNativeAtanFloat3Float3" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkNativeAtanFloat4Float4() {
+        Allocation inV = createRandomFloatAllocation(mRS, Element.DataType.FLOAT_32, 4, 0xf5ccc667fcc0bd52l, -1, 1);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 4), INPUTSIZE);
+            script.forEach_testNativeAtanFloat4Float4(inV, out);
+            verifyResultsNativeAtanFloat4Float4(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeAtanFloat4Float4: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 4), INPUTSIZE);
+            scriptRelaxed.forEach_testNativeAtanFloat4Float4(inV, out);
+            verifyResultsNativeAtanFloat4Float4(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeAtanFloat4Float4: " + e.toString());
+        }
+    }
+
+    private void verifyResultsNativeAtanFloat4Float4(Allocation inV, Allocation out, boolean relaxed) {
+        float[] arrayInV = new float[INPUTSIZE * 4];
+        inV.copyTo(arrayInV);
+        float[] arrayOut = new float[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 4 ; j++) {
+                // Extract the inputs.
+                ArgumentsFloatFloat args = new ArgumentsFloatFloat();
+                args.inV = arrayInV[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeNativeAtan(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            args.inV, Float.floatToRawIntBits(args.inV), args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(args.out.toString());
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            arrayOut[i * 4 + j], Float.floatToRawIntBits(arrayOut[i * 4 + j]), arrayOut[i * 4 + j]));
+                    if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkNativeAtanFloat4Float4" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    public void testNativeAtan() {
+        checkNativeAtanFloatFloat();
+        checkNativeAtanFloat2Float2();
+        checkNativeAtanFloat3Float3();
+        checkNativeAtanFloat4Float4();
+    }
+}
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/TestNativeAtan2.java b/tests/tests/renderscript/src/android/renderscript/cts/TestNativeAtan2.java
new file mode 100644
index 0000000..6d3fca7
--- /dev/null
+++ b/tests/tests/renderscript/src/android/renderscript/cts/TestNativeAtan2.java
@@ -0,0 +1,325 @@
+/*
+ * 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.
+ */
+
+// Don't edit this file!  It is auto-generated by frameworks/rs/api/gen_runtime.
+
+package android.renderscript.cts;
+
+import android.renderscript.Allocation;
+import android.renderscript.RSRuntimeException;
+import android.renderscript.Element;
+
+public class TestNativeAtan2 extends RSBaseCompute {
+
+    private ScriptC_TestNativeAtan2 script;
+    private ScriptC_TestNativeAtan2Relaxed scriptRelaxed;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        script = new ScriptC_TestNativeAtan2(mRS);
+        scriptRelaxed = new ScriptC_TestNativeAtan2Relaxed(mRS);
+    }
+
+    public class ArgumentsFloatFloatFloat {
+        public float inY;
+        public float inX;
+        public Floaty out;
+    }
+
+    private void checkNativeAtan2FloatFloatFloat() {
+        Allocation inY = createRandomAllocation(mRS, Element.DataType.FLOAT_32, 1, 0x4ecf7d3b9e2a276fl, false);
+        Allocation inX = createRandomAllocation(mRS, Element.DataType.FLOAT_32, 1, 0x4ecf7d3b9e2a276el, false);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 1), INPUTSIZE);
+            script.set_gAllocInX(inX);
+            script.forEach_testNativeAtan2FloatFloatFloat(inY, out);
+            verifyResultsNativeAtan2FloatFloatFloat(inY, inX, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeAtan2FloatFloatFloat: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 1), INPUTSIZE);
+            scriptRelaxed.set_gAllocInX(inX);
+            scriptRelaxed.forEach_testNativeAtan2FloatFloatFloat(inY, out);
+            verifyResultsNativeAtan2FloatFloatFloat(inY, inX, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeAtan2FloatFloatFloat: " + e.toString());
+        }
+    }
+
+    private void verifyResultsNativeAtan2FloatFloatFloat(Allocation inY, Allocation inX, Allocation out, boolean relaxed) {
+        float[] arrayInY = new float[INPUTSIZE * 1];
+        inY.copyTo(arrayInY);
+        float[] arrayInX = new float[INPUTSIZE * 1];
+        inX.copyTo(arrayInX);
+        float[] arrayOut = new float[INPUTSIZE * 1];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 1 ; j++) {
+                // Extract the inputs.
+                ArgumentsFloatFloatFloat args = new ArgumentsFloatFloatFloat();
+                args.inY = arrayInY[i];
+                args.inX = arrayInX[i];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeNativeAtan2(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (!args.out.couldBe(arrayOut[i * 1 + j])) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inY: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            args.inY, Float.floatToRawIntBits(args.inY), args.inY));
+                    message.append("\n");
+                    message.append("Input inX: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            args.inX, Float.floatToRawIntBits(args.inX), args.inX));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(args.out.toString());
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            arrayOut[i * 1 + j], Float.floatToRawIntBits(arrayOut[i * 1 + j]), arrayOut[i * 1 + j]));
+                    if (!args.out.couldBe(arrayOut[i * 1 + j])) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkNativeAtan2FloatFloatFloat" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkNativeAtan2Float2Float2Float2() {
+        Allocation inY = createRandomAllocation(mRS, Element.DataType.FLOAT_32, 2, 0x3c2d1a09d1c2a8c7l, false);
+        Allocation inX = createRandomAllocation(mRS, Element.DataType.FLOAT_32, 2, 0x3c2d1a09d1c2a8c6l, false);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 2), INPUTSIZE);
+            script.set_gAllocInX(inX);
+            script.forEach_testNativeAtan2Float2Float2Float2(inY, out);
+            verifyResultsNativeAtan2Float2Float2Float2(inY, inX, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeAtan2Float2Float2Float2: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 2), INPUTSIZE);
+            scriptRelaxed.set_gAllocInX(inX);
+            scriptRelaxed.forEach_testNativeAtan2Float2Float2Float2(inY, out);
+            verifyResultsNativeAtan2Float2Float2Float2(inY, inX, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeAtan2Float2Float2Float2: " + e.toString());
+        }
+    }
+
+    private void verifyResultsNativeAtan2Float2Float2Float2(Allocation inY, Allocation inX, Allocation out, boolean relaxed) {
+        float[] arrayInY = new float[INPUTSIZE * 2];
+        inY.copyTo(arrayInY);
+        float[] arrayInX = new float[INPUTSIZE * 2];
+        inX.copyTo(arrayInX);
+        float[] arrayOut = new float[INPUTSIZE * 2];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 2 ; j++) {
+                // Extract the inputs.
+                ArgumentsFloatFloatFloat args = new ArgumentsFloatFloatFloat();
+                args.inY = arrayInY[i * 2 + j];
+                args.inX = arrayInX[i * 2 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeNativeAtan2(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (!args.out.couldBe(arrayOut[i * 2 + j])) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inY: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            args.inY, Float.floatToRawIntBits(args.inY), args.inY));
+                    message.append("\n");
+                    message.append("Input inX: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            args.inX, Float.floatToRawIntBits(args.inX), args.inX));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(args.out.toString());
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            arrayOut[i * 2 + j], Float.floatToRawIntBits(arrayOut[i * 2 + j]), arrayOut[i * 2 + j]));
+                    if (!args.out.couldBe(arrayOut[i * 2 + j])) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkNativeAtan2Float2Float2Float2" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkNativeAtan2Float3Float3Float3() {
+        Allocation inY = createRandomAllocation(mRS, Element.DataType.FLOAT_32, 3, 0x9091f829d3a0aa68l, false);
+        Allocation inX = createRandomAllocation(mRS, Element.DataType.FLOAT_32, 3, 0x9091f829d3a0aa67l, false);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 3), INPUTSIZE);
+            script.set_gAllocInX(inX);
+            script.forEach_testNativeAtan2Float3Float3Float3(inY, out);
+            verifyResultsNativeAtan2Float3Float3Float3(inY, inX, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeAtan2Float3Float3Float3: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 3), INPUTSIZE);
+            scriptRelaxed.set_gAllocInX(inX);
+            scriptRelaxed.forEach_testNativeAtan2Float3Float3Float3(inY, out);
+            verifyResultsNativeAtan2Float3Float3Float3(inY, inX, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeAtan2Float3Float3Float3: " + e.toString());
+        }
+    }
+
+    private void verifyResultsNativeAtan2Float3Float3Float3(Allocation inY, Allocation inX, Allocation out, boolean relaxed) {
+        float[] arrayInY = new float[INPUTSIZE * 4];
+        inY.copyTo(arrayInY);
+        float[] arrayInX = new float[INPUTSIZE * 4];
+        inX.copyTo(arrayInX);
+        float[] arrayOut = new float[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 3 ; j++) {
+                // Extract the inputs.
+                ArgumentsFloatFloatFloat args = new ArgumentsFloatFloatFloat();
+                args.inY = arrayInY[i * 4 + j];
+                args.inX = arrayInX[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeNativeAtan2(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inY: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            args.inY, Float.floatToRawIntBits(args.inY), args.inY));
+                    message.append("\n");
+                    message.append("Input inX: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            args.inX, Float.floatToRawIntBits(args.inX), args.inX));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(args.out.toString());
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            arrayOut[i * 4 + j], Float.floatToRawIntBits(arrayOut[i * 4 + j]), arrayOut[i * 4 + j]));
+                    if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkNativeAtan2Float3Float3Float3" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkNativeAtan2Float4Float4Float4() {
+        Allocation inY = createRandomAllocation(mRS, Element.DataType.FLOAT_32, 4, 0xe4f6d649d57eac09l, false);
+        Allocation inX = createRandomAllocation(mRS, Element.DataType.FLOAT_32, 4, 0xe4f6d649d57eac08l, false);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 4), INPUTSIZE);
+            script.set_gAllocInX(inX);
+            script.forEach_testNativeAtan2Float4Float4Float4(inY, out);
+            verifyResultsNativeAtan2Float4Float4Float4(inY, inX, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeAtan2Float4Float4Float4: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 4), INPUTSIZE);
+            scriptRelaxed.set_gAllocInX(inX);
+            scriptRelaxed.forEach_testNativeAtan2Float4Float4Float4(inY, out);
+            verifyResultsNativeAtan2Float4Float4Float4(inY, inX, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeAtan2Float4Float4Float4: " + e.toString());
+        }
+    }
+
+    private void verifyResultsNativeAtan2Float4Float4Float4(Allocation inY, Allocation inX, Allocation out, boolean relaxed) {
+        float[] arrayInY = new float[INPUTSIZE * 4];
+        inY.copyTo(arrayInY);
+        float[] arrayInX = new float[INPUTSIZE * 4];
+        inX.copyTo(arrayInX);
+        float[] arrayOut = new float[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 4 ; j++) {
+                // Extract the inputs.
+                ArgumentsFloatFloatFloat args = new ArgumentsFloatFloatFloat();
+                args.inY = arrayInY[i * 4 + j];
+                args.inX = arrayInX[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeNativeAtan2(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inY: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            args.inY, Float.floatToRawIntBits(args.inY), args.inY));
+                    message.append("\n");
+                    message.append("Input inX: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            args.inX, Float.floatToRawIntBits(args.inX), args.inX));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(args.out.toString());
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            arrayOut[i * 4 + j], Float.floatToRawIntBits(arrayOut[i * 4 + j]), arrayOut[i * 4 + j]));
+                    if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkNativeAtan2Float4Float4Float4" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    public void testNativeAtan2() {
+        checkNativeAtan2FloatFloatFloat();
+        checkNativeAtan2Float2Float2Float2();
+        checkNativeAtan2Float3Float3Float3();
+        checkNativeAtan2Float4Float4Float4();
+    }
+}
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/TestNativeAtan2pi.java b/tests/tests/renderscript/src/android/renderscript/cts/TestNativeAtan2pi.java
new file mode 100644
index 0000000..1cb13e7
--- /dev/null
+++ b/tests/tests/renderscript/src/android/renderscript/cts/TestNativeAtan2pi.java
@@ -0,0 +1,325 @@
+/*
+ * 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.
+ */
+
+// Don't edit this file!  It is auto-generated by frameworks/rs/api/gen_runtime.
+
+package android.renderscript.cts;
+
+import android.renderscript.Allocation;
+import android.renderscript.RSRuntimeException;
+import android.renderscript.Element;
+
+public class TestNativeAtan2pi extends RSBaseCompute {
+
+    private ScriptC_TestNativeAtan2pi script;
+    private ScriptC_TestNativeAtan2piRelaxed scriptRelaxed;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        script = new ScriptC_TestNativeAtan2pi(mRS);
+        scriptRelaxed = new ScriptC_TestNativeAtan2piRelaxed(mRS);
+    }
+
+    public class ArgumentsFloatFloatFloat {
+        public float inY;
+        public float inX;
+        public Floaty out;
+    }
+
+    private void checkNativeAtan2piFloatFloatFloat() {
+        Allocation inY = createRandomAllocation(mRS, Element.DataType.FLOAT_32, 1, 0x39c4f8fd35fc5dc8l, false);
+        Allocation inX = createRandomAllocation(mRS, Element.DataType.FLOAT_32, 1, 0x39c4f8fd35fc5dc7l, false);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 1), INPUTSIZE);
+            script.set_gAllocInX(inX);
+            script.forEach_testNativeAtan2piFloatFloatFloat(inY, out);
+            verifyResultsNativeAtan2piFloatFloatFloat(inY, inX, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeAtan2piFloatFloatFloat: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 1), INPUTSIZE);
+            scriptRelaxed.set_gAllocInX(inX);
+            scriptRelaxed.forEach_testNativeAtan2piFloatFloatFloat(inY, out);
+            verifyResultsNativeAtan2piFloatFloatFloat(inY, inX, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeAtan2piFloatFloatFloat: " + e.toString());
+        }
+    }
+
+    private void verifyResultsNativeAtan2piFloatFloatFloat(Allocation inY, Allocation inX, Allocation out, boolean relaxed) {
+        float[] arrayInY = new float[INPUTSIZE * 1];
+        inY.copyTo(arrayInY);
+        float[] arrayInX = new float[INPUTSIZE * 1];
+        inX.copyTo(arrayInX);
+        float[] arrayOut = new float[INPUTSIZE * 1];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 1 ; j++) {
+                // Extract the inputs.
+                ArgumentsFloatFloatFloat args = new ArgumentsFloatFloatFloat();
+                args.inY = arrayInY[i];
+                args.inX = arrayInX[i];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeNativeAtan2pi(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (!args.out.couldBe(arrayOut[i * 1 + j])) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inY: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            args.inY, Float.floatToRawIntBits(args.inY), args.inY));
+                    message.append("\n");
+                    message.append("Input inX: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            args.inX, Float.floatToRawIntBits(args.inX), args.inX));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(args.out.toString());
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            arrayOut[i * 1 + j], Float.floatToRawIntBits(arrayOut[i * 1 + j]), arrayOut[i * 1 + j]));
+                    if (!args.out.couldBe(arrayOut[i * 1 + j])) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkNativeAtan2piFloatFloatFloat" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkNativeAtan2piFloat2Float2Float2() {
+        Allocation inY = createRandomAllocation(mRS, Element.DataType.FLOAT_32, 2, 0x6aff980c8d47a3e2l, false);
+        Allocation inX = createRandomAllocation(mRS, Element.DataType.FLOAT_32, 2, 0x6aff980c8d47a3e1l, false);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 2), INPUTSIZE);
+            script.set_gAllocInX(inX);
+            script.forEach_testNativeAtan2piFloat2Float2Float2(inY, out);
+            verifyResultsNativeAtan2piFloat2Float2Float2(inY, inX, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeAtan2piFloat2Float2Float2: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 2), INPUTSIZE);
+            scriptRelaxed.set_gAllocInX(inX);
+            scriptRelaxed.forEach_testNativeAtan2piFloat2Float2Float2(inY, out);
+            verifyResultsNativeAtan2piFloat2Float2Float2(inY, inX, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeAtan2piFloat2Float2Float2: " + e.toString());
+        }
+    }
+
+    private void verifyResultsNativeAtan2piFloat2Float2Float2(Allocation inY, Allocation inX, Allocation out, boolean relaxed) {
+        float[] arrayInY = new float[INPUTSIZE * 2];
+        inY.copyTo(arrayInY);
+        float[] arrayInX = new float[INPUTSIZE * 2];
+        inX.copyTo(arrayInX);
+        float[] arrayOut = new float[INPUTSIZE * 2];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 2 ; j++) {
+                // Extract the inputs.
+                ArgumentsFloatFloatFloat args = new ArgumentsFloatFloatFloat();
+                args.inY = arrayInY[i * 2 + j];
+                args.inX = arrayInX[i * 2 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeNativeAtan2pi(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (!args.out.couldBe(arrayOut[i * 2 + j])) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inY: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            args.inY, Float.floatToRawIntBits(args.inY), args.inY));
+                    message.append("\n");
+                    message.append("Input inX: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            args.inX, Float.floatToRawIntBits(args.inX), args.inX));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(args.out.toString());
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            arrayOut[i * 2 + j], Float.floatToRawIntBits(arrayOut[i * 2 + j]), arrayOut[i * 2 + j]));
+                    if (!args.out.couldBe(arrayOut[i * 2 + j])) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkNativeAtan2piFloat2Float2Float2" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkNativeAtan2piFloat3Float3Float3() {
+        Allocation inY = createRandomAllocation(mRS, Element.DataType.FLOAT_32, 3, 0xbf64762c8f25a583l, false);
+        Allocation inX = createRandomAllocation(mRS, Element.DataType.FLOAT_32, 3, 0xbf64762c8f25a582l, false);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 3), INPUTSIZE);
+            script.set_gAllocInX(inX);
+            script.forEach_testNativeAtan2piFloat3Float3Float3(inY, out);
+            verifyResultsNativeAtan2piFloat3Float3Float3(inY, inX, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeAtan2piFloat3Float3Float3: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 3), INPUTSIZE);
+            scriptRelaxed.set_gAllocInX(inX);
+            scriptRelaxed.forEach_testNativeAtan2piFloat3Float3Float3(inY, out);
+            verifyResultsNativeAtan2piFloat3Float3Float3(inY, inX, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeAtan2piFloat3Float3Float3: " + e.toString());
+        }
+    }
+
+    private void verifyResultsNativeAtan2piFloat3Float3Float3(Allocation inY, Allocation inX, Allocation out, boolean relaxed) {
+        float[] arrayInY = new float[INPUTSIZE * 4];
+        inY.copyTo(arrayInY);
+        float[] arrayInX = new float[INPUTSIZE * 4];
+        inX.copyTo(arrayInX);
+        float[] arrayOut = new float[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 3 ; j++) {
+                // Extract the inputs.
+                ArgumentsFloatFloatFloat args = new ArgumentsFloatFloatFloat();
+                args.inY = arrayInY[i * 4 + j];
+                args.inX = arrayInX[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeNativeAtan2pi(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inY: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            args.inY, Float.floatToRawIntBits(args.inY), args.inY));
+                    message.append("\n");
+                    message.append("Input inX: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            args.inX, Float.floatToRawIntBits(args.inX), args.inX));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(args.out.toString());
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            arrayOut[i * 4 + j], Float.floatToRawIntBits(arrayOut[i * 4 + j]), arrayOut[i * 4 + j]));
+                    if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkNativeAtan2piFloat3Float3Float3" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkNativeAtan2piFloat4Float4Float4() {
+        Allocation inY = createRandomAllocation(mRS, Element.DataType.FLOAT_32, 4, 0x13c9544c9103a724l, false);
+        Allocation inX = createRandomAllocation(mRS, Element.DataType.FLOAT_32, 4, 0x13c9544c9103a723l, false);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 4), INPUTSIZE);
+            script.set_gAllocInX(inX);
+            script.forEach_testNativeAtan2piFloat4Float4Float4(inY, out);
+            verifyResultsNativeAtan2piFloat4Float4Float4(inY, inX, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeAtan2piFloat4Float4Float4: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 4), INPUTSIZE);
+            scriptRelaxed.set_gAllocInX(inX);
+            scriptRelaxed.forEach_testNativeAtan2piFloat4Float4Float4(inY, out);
+            verifyResultsNativeAtan2piFloat4Float4Float4(inY, inX, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeAtan2piFloat4Float4Float4: " + e.toString());
+        }
+    }
+
+    private void verifyResultsNativeAtan2piFloat4Float4Float4(Allocation inY, Allocation inX, Allocation out, boolean relaxed) {
+        float[] arrayInY = new float[INPUTSIZE * 4];
+        inY.copyTo(arrayInY);
+        float[] arrayInX = new float[INPUTSIZE * 4];
+        inX.copyTo(arrayInX);
+        float[] arrayOut = new float[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 4 ; j++) {
+                // Extract the inputs.
+                ArgumentsFloatFloatFloat args = new ArgumentsFloatFloatFloat();
+                args.inY = arrayInY[i * 4 + j];
+                args.inX = arrayInX[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeNativeAtan2pi(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inY: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            args.inY, Float.floatToRawIntBits(args.inY), args.inY));
+                    message.append("\n");
+                    message.append("Input inX: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            args.inX, Float.floatToRawIntBits(args.inX), args.inX));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(args.out.toString());
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            arrayOut[i * 4 + j], Float.floatToRawIntBits(arrayOut[i * 4 + j]), arrayOut[i * 4 + j]));
+                    if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkNativeAtan2piFloat4Float4Float4" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    public void testNativeAtan2pi() {
+        checkNativeAtan2piFloatFloatFloat();
+        checkNativeAtan2piFloat2Float2Float2();
+        checkNativeAtan2piFloat3Float3Float3();
+        checkNativeAtan2piFloat4Float4Float4();
+    }
+}
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/TestNativeAtanh.java b/tests/tests/renderscript/src/android/renderscript/cts/TestNativeAtanh.java
new file mode 100644
index 0000000..b6a6f4e
--- /dev/null
+++ b/tests/tests/renderscript/src/android/renderscript/cts/TestNativeAtanh.java
@@ -0,0 +1,284 @@
+/*
+ * 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.
+ */
+
+// Don't edit this file!  It is auto-generated by frameworks/rs/api/gen_runtime.
+
+package android.renderscript.cts;
+
+import android.renderscript.Allocation;
+import android.renderscript.RSRuntimeException;
+import android.renderscript.Element;
+
+public class TestNativeAtanh extends RSBaseCompute {
+
+    private ScriptC_TestNativeAtanh script;
+    private ScriptC_TestNativeAtanhRelaxed scriptRelaxed;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        script = new ScriptC_TestNativeAtanh(mRS);
+        scriptRelaxed = new ScriptC_TestNativeAtanhRelaxed(mRS);
+    }
+
+    public class ArgumentsFloatFloat {
+        public float in;
+        public Floaty out;
+    }
+
+    private void checkNativeAtanhFloatFloat() {
+        Allocation in = createRandomAllocation(mRS, Element.DataType.FLOAT_32, 1, 0x66daa6c24cd0610al, false);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 1), INPUTSIZE);
+            script.forEach_testNativeAtanhFloatFloat(in, out);
+            verifyResultsNativeAtanhFloatFloat(in, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeAtanhFloatFloat: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 1), INPUTSIZE);
+            scriptRelaxed.forEach_testNativeAtanhFloatFloat(in, out);
+            verifyResultsNativeAtanhFloatFloat(in, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeAtanhFloatFloat: " + e.toString());
+        }
+    }
+
+    private void verifyResultsNativeAtanhFloatFloat(Allocation in, Allocation out, boolean relaxed) {
+        float[] arrayIn = new float[INPUTSIZE * 1];
+        in.copyTo(arrayIn);
+        float[] arrayOut = new float[INPUTSIZE * 1];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 1 ; j++) {
+                // Extract the inputs.
+                ArgumentsFloatFloat args = new ArgumentsFloatFloat();
+                args.in = arrayIn[i];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeNativeAtanh(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (!args.out.couldBe(arrayOut[i * 1 + j])) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input in: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            args.in, Float.floatToRawIntBits(args.in), args.in));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(args.out.toString());
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            arrayOut[i * 1 + j], Float.floatToRawIntBits(arrayOut[i * 1 + j]), arrayOut[i * 1 + j]));
+                    if (!args.out.couldBe(arrayOut[i * 1 + j])) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkNativeAtanhFloatFloat" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkNativeAtanhFloat2Float2() {
+        Allocation in = createRandomAllocation(mRS, Element.DataType.FLOAT_32, 2, 0xe13d93587e455c56l, false);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 2), INPUTSIZE);
+            script.forEach_testNativeAtanhFloat2Float2(in, out);
+            verifyResultsNativeAtanhFloat2Float2(in, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeAtanhFloat2Float2: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 2), INPUTSIZE);
+            scriptRelaxed.forEach_testNativeAtanhFloat2Float2(in, out);
+            verifyResultsNativeAtanhFloat2Float2(in, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeAtanhFloat2Float2: " + e.toString());
+        }
+    }
+
+    private void verifyResultsNativeAtanhFloat2Float2(Allocation in, Allocation out, boolean relaxed) {
+        float[] arrayIn = new float[INPUTSIZE * 2];
+        in.copyTo(arrayIn);
+        float[] arrayOut = new float[INPUTSIZE * 2];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 2 ; j++) {
+                // Extract the inputs.
+                ArgumentsFloatFloat args = new ArgumentsFloatFloat();
+                args.in = arrayIn[i * 2 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeNativeAtanh(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (!args.out.couldBe(arrayOut[i * 2 + j])) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input in: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            args.in, Float.floatToRawIntBits(args.in), args.in));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(args.out.toString());
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            arrayOut[i * 2 + j], Float.floatToRawIntBits(arrayOut[i * 2 + j]), arrayOut[i * 2 + j]));
+                    if (!args.out.couldBe(arrayOut[i * 2 + j])) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkNativeAtanhFloat2Float2" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkNativeAtanhFloat3Float3() {
+        Allocation in = createRandomAllocation(mRS, Element.DataType.FLOAT_32, 3, 0xe13d9df9dd4bf1f0l, false);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 3), INPUTSIZE);
+            script.forEach_testNativeAtanhFloat3Float3(in, out);
+            verifyResultsNativeAtanhFloat3Float3(in, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeAtanhFloat3Float3: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 3), INPUTSIZE);
+            scriptRelaxed.forEach_testNativeAtanhFloat3Float3(in, out);
+            verifyResultsNativeAtanhFloat3Float3(in, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeAtanhFloat3Float3: " + e.toString());
+        }
+    }
+
+    private void verifyResultsNativeAtanhFloat3Float3(Allocation in, Allocation out, boolean relaxed) {
+        float[] arrayIn = new float[INPUTSIZE * 4];
+        in.copyTo(arrayIn);
+        float[] arrayOut = new float[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 3 ; j++) {
+                // Extract the inputs.
+                ArgumentsFloatFloat args = new ArgumentsFloatFloat();
+                args.in = arrayIn[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeNativeAtanh(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input in: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            args.in, Float.floatToRawIntBits(args.in), args.in));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(args.out.toString());
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            arrayOut[i * 4 + j], Float.floatToRawIntBits(arrayOut[i * 4 + j]), arrayOut[i * 4 + j]));
+                    if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkNativeAtanhFloat3Float3" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkNativeAtanhFloat4Float4() {
+        Allocation in = createRandomAllocation(mRS, Element.DataType.FLOAT_32, 4, 0xe13da89b3c52878al, false);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 4), INPUTSIZE);
+            script.forEach_testNativeAtanhFloat4Float4(in, out);
+            verifyResultsNativeAtanhFloat4Float4(in, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeAtanhFloat4Float4: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 4), INPUTSIZE);
+            scriptRelaxed.forEach_testNativeAtanhFloat4Float4(in, out);
+            verifyResultsNativeAtanhFloat4Float4(in, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeAtanhFloat4Float4: " + e.toString());
+        }
+    }
+
+    private void verifyResultsNativeAtanhFloat4Float4(Allocation in, Allocation out, boolean relaxed) {
+        float[] arrayIn = new float[INPUTSIZE * 4];
+        in.copyTo(arrayIn);
+        float[] arrayOut = new float[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 4 ; j++) {
+                // Extract the inputs.
+                ArgumentsFloatFloat args = new ArgumentsFloatFloat();
+                args.in = arrayIn[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeNativeAtanh(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input in: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            args.in, Float.floatToRawIntBits(args.in), args.in));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(args.out.toString());
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            arrayOut[i * 4 + j], Float.floatToRawIntBits(arrayOut[i * 4 + j]), arrayOut[i * 4 + j]));
+                    if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkNativeAtanhFloat4Float4" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    public void testNativeAtanh() {
+        checkNativeAtanhFloatFloat();
+        checkNativeAtanhFloat2Float2();
+        checkNativeAtanhFloat3Float3();
+        checkNativeAtanhFloat4Float4();
+    }
+}
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/TestNativeAtanpi.java b/tests/tests/renderscript/src/android/renderscript/cts/TestNativeAtanpi.java
new file mode 100644
index 0000000..0a17823
--- /dev/null
+++ b/tests/tests/renderscript/src/android/renderscript/cts/TestNativeAtanpi.java
@@ -0,0 +1,284 @@
+/*
+ * 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.
+ */
+
+// Don't edit this file!  It is auto-generated by frameworks/rs/api/gen_runtime.
+
+package android.renderscript.cts;
+
+import android.renderscript.Allocation;
+import android.renderscript.RSRuntimeException;
+import android.renderscript.Element;
+
+public class TestNativeAtanpi extends RSBaseCompute {
+
+    private ScriptC_TestNativeAtanpi script;
+    private ScriptC_TestNativeAtanpiRelaxed scriptRelaxed;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        script = new ScriptC_TestNativeAtanpi(mRS);
+        scriptRelaxed = new ScriptC_TestNativeAtanpiRelaxed(mRS);
+    }
+
+    public class ArgumentsFloatFloat {
+        public float inV;
+        public Floaty out;
+    }
+
+    private void checkNativeAtanpiFloatFloat() {
+        Allocation inV = createRandomFloatAllocation(mRS, Element.DataType.FLOAT_32, 1, 0x3fd790efd1d451bdl, -1, 1);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 1), INPUTSIZE);
+            script.forEach_testNativeAtanpiFloatFloat(inV, out);
+            verifyResultsNativeAtanpiFloatFloat(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeAtanpiFloatFloat: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 1), INPUTSIZE);
+            scriptRelaxed.forEach_testNativeAtanpiFloatFloat(inV, out);
+            verifyResultsNativeAtanpiFloatFloat(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeAtanpiFloatFloat: " + e.toString());
+        }
+    }
+
+    private void verifyResultsNativeAtanpiFloatFloat(Allocation inV, Allocation out, boolean relaxed) {
+        float[] arrayInV = new float[INPUTSIZE * 1];
+        inV.copyTo(arrayInV);
+        float[] arrayOut = new float[INPUTSIZE * 1];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 1 ; j++) {
+                // Extract the inputs.
+                ArgumentsFloatFloat args = new ArgumentsFloatFloat();
+                args.inV = arrayInV[i];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeNativeAtanpi(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (!args.out.couldBe(arrayOut[i * 1 + j])) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            args.inV, Float.floatToRawIntBits(args.inV), args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(args.out.toString());
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            arrayOut[i * 1 + j], Float.floatToRawIntBits(arrayOut[i * 1 + j]), arrayOut[i * 1 + j]));
+                    if (!args.out.couldBe(arrayOut[i * 1 + j])) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkNativeAtanpiFloatFloat" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkNativeAtanpiFloat2Float2() {
+        Allocation inV = createRandomFloatAllocation(mRS, Element.DataType.FLOAT_32, 2, 0x1bd089694afc9ef9l, -1, 1);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 2), INPUTSIZE);
+            script.forEach_testNativeAtanpiFloat2Float2(inV, out);
+            verifyResultsNativeAtanpiFloat2Float2(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeAtanpiFloat2Float2: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 2), INPUTSIZE);
+            scriptRelaxed.forEach_testNativeAtanpiFloat2Float2(inV, out);
+            verifyResultsNativeAtanpiFloat2Float2(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeAtanpiFloat2Float2: " + e.toString());
+        }
+    }
+
+    private void verifyResultsNativeAtanpiFloat2Float2(Allocation inV, Allocation out, boolean relaxed) {
+        float[] arrayInV = new float[INPUTSIZE * 2];
+        inV.copyTo(arrayInV);
+        float[] arrayOut = new float[INPUTSIZE * 2];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 2 ; j++) {
+                // Extract the inputs.
+                ArgumentsFloatFloat args = new ArgumentsFloatFloat();
+                args.inV = arrayInV[i * 2 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeNativeAtanpi(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (!args.out.couldBe(arrayOut[i * 2 + j])) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            args.inV, Float.floatToRawIntBits(args.inV), args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(args.out.toString());
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            arrayOut[i * 2 + j], Float.floatToRawIntBits(arrayOut[i * 2 + j]), arrayOut[i * 2 + j]));
+                    if (!args.out.couldBe(arrayOut[i * 2 + j])) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkNativeAtanpiFloat2Float2" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkNativeAtanpiFloat3Float3() {
+        Allocation inV = createRandomFloatAllocation(mRS, Element.DataType.FLOAT_32, 3, 0x1bd252844117bfd7l, -1, 1);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 3), INPUTSIZE);
+            script.forEach_testNativeAtanpiFloat3Float3(inV, out);
+            verifyResultsNativeAtanpiFloat3Float3(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeAtanpiFloat3Float3: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 3), INPUTSIZE);
+            scriptRelaxed.forEach_testNativeAtanpiFloat3Float3(inV, out);
+            verifyResultsNativeAtanpiFloat3Float3(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeAtanpiFloat3Float3: " + e.toString());
+        }
+    }
+
+    private void verifyResultsNativeAtanpiFloat3Float3(Allocation inV, Allocation out, boolean relaxed) {
+        float[] arrayInV = new float[INPUTSIZE * 4];
+        inV.copyTo(arrayInV);
+        float[] arrayOut = new float[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 3 ; j++) {
+                // Extract the inputs.
+                ArgumentsFloatFloat args = new ArgumentsFloatFloat();
+                args.inV = arrayInV[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeNativeAtanpi(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            args.inV, Float.floatToRawIntBits(args.inV), args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(args.out.toString());
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            arrayOut[i * 4 + j], Float.floatToRawIntBits(arrayOut[i * 4 + j]), arrayOut[i * 4 + j]));
+                    if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkNativeAtanpiFloat3Float3" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkNativeAtanpiFloat4Float4() {
+        Allocation inV = createRandomFloatAllocation(mRS, Element.DataType.FLOAT_32, 4, 0x1bd41b9f3732e0b5l, -1, 1);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 4), INPUTSIZE);
+            script.forEach_testNativeAtanpiFloat4Float4(inV, out);
+            verifyResultsNativeAtanpiFloat4Float4(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeAtanpiFloat4Float4: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 4), INPUTSIZE);
+            scriptRelaxed.forEach_testNativeAtanpiFloat4Float4(inV, out);
+            verifyResultsNativeAtanpiFloat4Float4(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeAtanpiFloat4Float4: " + e.toString());
+        }
+    }
+
+    private void verifyResultsNativeAtanpiFloat4Float4(Allocation inV, Allocation out, boolean relaxed) {
+        float[] arrayInV = new float[INPUTSIZE * 4];
+        inV.copyTo(arrayInV);
+        float[] arrayOut = new float[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 4 ; j++) {
+                // Extract the inputs.
+                ArgumentsFloatFloat args = new ArgumentsFloatFloat();
+                args.inV = arrayInV[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeNativeAtanpi(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            args.inV, Float.floatToRawIntBits(args.inV), args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(args.out.toString());
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            arrayOut[i * 4 + j], Float.floatToRawIntBits(arrayOut[i * 4 + j]), arrayOut[i * 4 + j]));
+                    if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkNativeAtanpiFloat4Float4" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    public void testNativeAtanpi() {
+        checkNativeAtanpiFloatFloat();
+        checkNativeAtanpiFloat2Float2();
+        checkNativeAtanpiFloat3Float3();
+        checkNativeAtanpiFloat4Float4();
+    }
+}
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/TestNativeCbrt.java b/tests/tests/renderscript/src/android/renderscript/cts/TestNativeCbrt.java
new file mode 100644
index 0000000..2f11069
--- /dev/null
+++ b/tests/tests/renderscript/src/android/renderscript/cts/TestNativeCbrt.java
@@ -0,0 +1,284 @@
+/*
+ * 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.
+ */
+
+// Don't edit this file!  It is auto-generated by frameworks/rs/api/gen_runtime.
+
+package android.renderscript.cts;
+
+import android.renderscript.Allocation;
+import android.renderscript.RSRuntimeException;
+import android.renderscript.Element;
+
+public class TestNativeCbrt extends RSBaseCompute {
+
+    private ScriptC_TestNativeCbrt script;
+    private ScriptC_TestNativeCbrtRelaxed scriptRelaxed;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        script = new ScriptC_TestNativeCbrt(mRS);
+        scriptRelaxed = new ScriptC_TestNativeCbrtRelaxed(mRS);
+    }
+
+    public class ArgumentsFloatFloat {
+        public float in;
+        public Floaty out;
+    }
+
+    private void checkNativeCbrtFloatFloat() {
+        Allocation in = createRandomAllocation(mRS, Element.DataType.FLOAT_32, 1, 0x6eed1901e2ca9d19l, false);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 1), INPUTSIZE);
+            script.forEach_testNativeCbrtFloatFloat(in, out);
+            verifyResultsNativeCbrtFloatFloat(in, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeCbrtFloatFloat: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 1), INPUTSIZE);
+            scriptRelaxed.forEach_testNativeCbrtFloatFloat(in, out);
+            verifyResultsNativeCbrtFloatFloat(in, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeCbrtFloatFloat: " + e.toString());
+        }
+    }
+
+    private void verifyResultsNativeCbrtFloatFloat(Allocation in, Allocation out, boolean relaxed) {
+        float[] arrayIn = new float[INPUTSIZE * 1];
+        in.copyTo(arrayIn);
+        float[] arrayOut = new float[INPUTSIZE * 1];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 1 ; j++) {
+                // Extract the inputs.
+                ArgumentsFloatFloat args = new ArgumentsFloatFloat();
+                args.in = arrayIn[i];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeNativeCbrt(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (!args.out.couldBe(arrayOut[i * 1 + j])) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input in: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            args.in, Float.floatToRawIntBits(args.in), args.in));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(args.out.toString());
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            arrayOut[i * 1 + j], Float.floatToRawIntBits(arrayOut[i * 1 + j]), arrayOut[i * 1 + j]));
+                    if (!args.out.couldBe(arrayOut[i * 1 + j])) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkNativeCbrtFloatFloat" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkNativeCbrtFloat2Float2() {
+        Allocation in = createRandomAllocation(mRS, Element.DataType.FLOAT_32, 2, 0x2e78c09abaa124adl, false);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 2), INPUTSIZE);
+            script.forEach_testNativeCbrtFloat2Float2(in, out);
+            verifyResultsNativeCbrtFloat2Float2(in, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeCbrtFloat2Float2: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 2), INPUTSIZE);
+            scriptRelaxed.forEach_testNativeCbrtFloat2Float2(in, out);
+            verifyResultsNativeCbrtFloat2Float2(in, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeCbrtFloat2Float2: " + e.toString());
+        }
+    }
+
+    private void verifyResultsNativeCbrtFloat2Float2(Allocation in, Allocation out, boolean relaxed) {
+        float[] arrayIn = new float[INPUTSIZE * 2];
+        in.copyTo(arrayIn);
+        float[] arrayOut = new float[INPUTSIZE * 2];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 2 ; j++) {
+                // Extract the inputs.
+                ArgumentsFloatFloat args = new ArgumentsFloatFloat();
+                args.in = arrayIn[i * 2 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeNativeCbrt(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (!args.out.couldBe(arrayOut[i * 2 + j])) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input in: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            args.in, Float.floatToRawIntBits(args.in), args.in));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(args.out.toString());
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            arrayOut[i * 2 + j], Float.floatToRawIntBits(arrayOut[i * 2 + j]), arrayOut[i * 2 + j]));
+                    if (!args.out.couldBe(arrayOut[i * 2 + j])) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkNativeCbrtFloat2Float2" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkNativeCbrtFloat3Float3() {
+        Allocation in = createRandomAllocation(mRS, Element.DataType.FLOAT_32, 3, 0x2e78cb3c19a7ba47l, false);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 3), INPUTSIZE);
+            script.forEach_testNativeCbrtFloat3Float3(in, out);
+            verifyResultsNativeCbrtFloat3Float3(in, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeCbrtFloat3Float3: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 3), INPUTSIZE);
+            scriptRelaxed.forEach_testNativeCbrtFloat3Float3(in, out);
+            verifyResultsNativeCbrtFloat3Float3(in, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeCbrtFloat3Float3: " + e.toString());
+        }
+    }
+
+    private void verifyResultsNativeCbrtFloat3Float3(Allocation in, Allocation out, boolean relaxed) {
+        float[] arrayIn = new float[INPUTSIZE * 4];
+        in.copyTo(arrayIn);
+        float[] arrayOut = new float[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 3 ; j++) {
+                // Extract the inputs.
+                ArgumentsFloatFloat args = new ArgumentsFloatFloat();
+                args.in = arrayIn[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeNativeCbrt(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input in: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            args.in, Float.floatToRawIntBits(args.in), args.in));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(args.out.toString());
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            arrayOut[i * 4 + j], Float.floatToRawIntBits(arrayOut[i * 4 + j]), arrayOut[i * 4 + j]));
+                    if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkNativeCbrtFloat3Float3" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkNativeCbrtFloat4Float4() {
+        Allocation in = createRandomAllocation(mRS, Element.DataType.FLOAT_32, 4, 0x2e78d5dd78ae4fe1l, false);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 4), INPUTSIZE);
+            script.forEach_testNativeCbrtFloat4Float4(in, out);
+            verifyResultsNativeCbrtFloat4Float4(in, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeCbrtFloat4Float4: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 4), INPUTSIZE);
+            scriptRelaxed.forEach_testNativeCbrtFloat4Float4(in, out);
+            verifyResultsNativeCbrtFloat4Float4(in, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeCbrtFloat4Float4: " + e.toString());
+        }
+    }
+
+    private void verifyResultsNativeCbrtFloat4Float4(Allocation in, Allocation out, boolean relaxed) {
+        float[] arrayIn = new float[INPUTSIZE * 4];
+        in.copyTo(arrayIn);
+        float[] arrayOut = new float[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 4 ; j++) {
+                // Extract the inputs.
+                ArgumentsFloatFloat args = new ArgumentsFloatFloat();
+                args.in = arrayIn[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeNativeCbrt(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input in: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            args.in, Float.floatToRawIntBits(args.in), args.in));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(args.out.toString());
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            arrayOut[i * 4 + j], Float.floatToRawIntBits(arrayOut[i * 4 + j]), arrayOut[i * 4 + j]));
+                    if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkNativeCbrtFloat4Float4" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    public void testNativeCbrt() {
+        checkNativeCbrtFloatFloat();
+        checkNativeCbrtFloat2Float2();
+        checkNativeCbrtFloat3Float3();
+        checkNativeCbrtFloat4Float4();
+    }
+}
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/TestNativeCos.java b/tests/tests/renderscript/src/android/renderscript/cts/TestNativeCos.java
new file mode 100644
index 0000000..347f96a
--- /dev/null
+++ b/tests/tests/renderscript/src/android/renderscript/cts/TestNativeCos.java
@@ -0,0 +1,284 @@
+/*
+ * 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.
+ */
+
+// Don't edit this file!  It is auto-generated by frameworks/rs/api/gen_runtime.
+
+package android.renderscript.cts;
+
+import android.renderscript.Allocation;
+import android.renderscript.RSRuntimeException;
+import android.renderscript.Element;
+
+public class TestNativeCos extends RSBaseCompute {
+
+    private ScriptC_TestNativeCos script;
+    private ScriptC_TestNativeCosRelaxed scriptRelaxed;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        script = new ScriptC_TestNativeCos(mRS);
+        scriptRelaxed = new ScriptC_TestNativeCosRelaxed(mRS);
+    }
+
+    public class ArgumentsFloatFloat {
+        public float in;
+        public Floaty out;
+    }
+
+    private void checkNativeCosFloatFloat() {
+        Allocation in = createRandomAllocation(mRS, Element.DataType.FLOAT_32, 1, 0x95ef4a20c73e7e3dl, false);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 1), INPUTSIZE);
+            script.forEach_testNativeCosFloatFloat(in, out);
+            verifyResultsNativeCosFloatFloat(in, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeCosFloatFloat: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 1), INPUTSIZE);
+            scriptRelaxed.forEach_testNativeCosFloatFloat(in, out);
+            verifyResultsNativeCosFloatFloat(in, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeCosFloatFloat: " + e.toString());
+        }
+    }
+
+    private void verifyResultsNativeCosFloatFloat(Allocation in, Allocation out, boolean relaxed) {
+        float[] arrayIn = new float[INPUTSIZE * 1];
+        in.copyTo(arrayIn);
+        float[] arrayOut = new float[INPUTSIZE * 1];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 1 ; j++) {
+                // Extract the inputs.
+                ArgumentsFloatFloat args = new ArgumentsFloatFloat();
+                args.in = arrayIn[i];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeNativeCos(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (!args.out.couldBe(arrayOut[i * 1 + j])) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input in: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            args.in, Float.floatToRawIntBits(args.in), args.in));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(args.out.toString());
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            arrayOut[i * 1 + j], Float.floatToRawIntBits(arrayOut[i * 1 + j]), arrayOut[i * 1 + j]));
+                    if (!args.out.couldBe(arrayOut[i * 1 + j])) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkNativeCosFloatFloat" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkNativeCosFloat2Float2() {
+        Allocation in = createRandomAllocation(mRS, Element.DataType.FLOAT_32, 2, 0xed4d88bac39641b1l, false);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 2), INPUTSIZE);
+            script.forEach_testNativeCosFloat2Float2(in, out);
+            verifyResultsNativeCosFloat2Float2(in, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeCosFloat2Float2: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 2), INPUTSIZE);
+            scriptRelaxed.forEach_testNativeCosFloat2Float2(in, out);
+            verifyResultsNativeCosFloat2Float2(in, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeCosFloat2Float2: " + e.toString());
+        }
+    }
+
+    private void verifyResultsNativeCosFloat2Float2(Allocation in, Allocation out, boolean relaxed) {
+        float[] arrayIn = new float[INPUTSIZE * 2];
+        in.copyTo(arrayIn);
+        float[] arrayOut = new float[INPUTSIZE * 2];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 2 ; j++) {
+                // Extract the inputs.
+                ArgumentsFloatFloat args = new ArgumentsFloatFloat();
+                args.in = arrayIn[i * 2 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeNativeCos(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (!args.out.couldBe(arrayOut[i * 2 + j])) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input in: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            args.in, Float.floatToRawIntBits(args.in), args.in));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(args.out.toString());
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            arrayOut[i * 2 + j], Float.floatToRawIntBits(arrayOut[i * 2 + j]), arrayOut[i * 2 + j]));
+                    if (!args.out.couldBe(arrayOut[i * 2 + j])) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkNativeCosFloat2Float2" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkNativeCosFloat3Float3() {
+        Allocation in = createRandomAllocation(mRS, Element.DataType.FLOAT_32, 3, 0xed4d935c229cd74bl, false);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 3), INPUTSIZE);
+            script.forEach_testNativeCosFloat3Float3(in, out);
+            verifyResultsNativeCosFloat3Float3(in, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeCosFloat3Float3: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 3), INPUTSIZE);
+            scriptRelaxed.forEach_testNativeCosFloat3Float3(in, out);
+            verifyResultsNativeCosFloat3Float3(in, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeCosFloat3Float3: " + e.toString());
+        }
+    }
+
+    private void verifyResultsNativeCosFloat3Float3(Allocation in, Allocation out, boolean relaxed) {
+        float[] arrayIn = new float[INPUTSIZE * 4];
+        in.copyTo(arrayIn);
+        float[] arrayOut = new float[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 3 ; j++) {
+                // Extract the inputs.
+                ArgumentsFloatFloat args = new ArgumentsFloatFloat();
+                args.in = arrayIn[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeNativeCos(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input in: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            args.in, Float.floatToRawIntBits(args.in), args.in));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(args.out.toString());
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            arrayOut[i * 4 + j], Float.floatToRawIntBits(arrayOut[i * 4 + j]), arrayOut[i * 4 + j]));
+                    if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkNativeCosFloat3Float3" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkNativeCosFloat4Float4() {
+        Allocation in = createRandomAllocation(mRS, Element.DataType.FLOAT_32, 4, 0xed4d9dfd81a36ce5l, false);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 4), INPUTSIZE);
+            script.forEach_testNativeCosFloat4Float4(in, out);
+            verifyResultsNativeCosFloat4Float4(in, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeCosFloat4Float4: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 4), INPUTSIZE);
+            scriptRelaxed.forEach_testNativeCosFloat4Float4(in, out);
+            verifyResultsNativeCosFloat4Float4(in, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeCosFloat4Float4: " + e.toString());
+        }
+    }
+
+    private void verifyResultsNativeCosFloat4Float4(Allocation in, Allocation out, boolean relaxed) {
+        float[] arrayIn = new float[INPUTSIZE * 4];
+        in.copyTo(arrayIn);
+        float[] arrayOut = new float[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 4 ; j++) {
+                // Extract the inputs.
+                ArgumentsFloatFloat args = new ArgumentsFloatFloat();
+                args.in = arrayIn[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeNativeCos(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input in: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            args.in, Float.floatToRawIntBits(args.in), args.in));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(args.out.toString());
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            arrayOut[i * 4 + j], Float.floatToRawIntBits(arrayOut[i * 4 + j]), arrayOut[i * 4 + j]));
+                    if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkNativeCosFloat4Float4" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    public void testNativeCos() {
+        checkNativeCosFloatFloat();
+        checkNativeCosFloat2Float2();
+        checkNativeCosFloat3Float3();
+        checkNativeCosFloat4Float4();
+    }
+}
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/TestNativeCosh.java b/tests/tests/renderscript/src/android/renderscript/cts/TestNativeCosh.java
new file mode 100644
index 0000000..a3ba2df
--- /dev/null
+++ b/tests/tests/renderscript/src/android/renderscript/cts/TestNativeCosh.java
@@ -0,0 +1,284 @@
+/*
+ * 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.
+ */
+
+// Don't edit this file!  It is auto-generated by frameworks/rs/api/gen_runtime.
+
+package android.renderscript.cts;
+
+import android.renderscript.Allocation;
+import android.renderscript.RSRuntimeException;
+import android.renderscript.Element;
+
+public class TestNativeCosh extends RSBaseCompute {
+
+    private ScriptC_TestNativeCosh script;
+    private ScriptC_TestNativeCoshRelaxed scriptRelaxed;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        script = new ScriptC_TestNativeCosh(mRS);
+        scriptRelaxed = new ScriptC_TestNativeCoshRelaxed(mRS);
+    }
+
+    public class ArgumentsFloatFloat {
+        public float in;
+        public Floaty out;
+    }
+
+    private void checkNativeCoshFloatFloat() {
+        Allocation in = createRandomAllocation(mRS, Element.DataType.FLOAT_32, 1, 0xeb1020909e9c475dl, false);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 1), INPUTSIZE);
+            script.forEach_testNativeCoshFloatFloat(in, out);
+            verifyResultsNativeCoshFloatFloat(in, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeCoshFloatFloat: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 1), INPUTSIZE);
+            scriptRelaxed.forEach_testNativeCoshFloatFloat(in, out);
+            verifyResultsNativeCoshFloatFloat(in, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeCoshFloatFloat: " + e.toString());
+        }
+    }
+
+    private void verifyResultsNativeCoshFloatFloat(Allocation in, Allocation out, boolean relaxed) {
+        float[] arrayIn = new float[INPUTSIZE * 1];
+        in.copyTo(arrayIn);
+        float[] arrayOut = new float[INPUTSIZE * 1];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 1 ; j++) {
+                // Extract the inputs.
+                ArgumentsFloatFloat args = new ArgumentsFloatFloat();
+                args.in = arrayIn[i];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeNativeCosh(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (!args.out.couldBe(arrayOut[i * 1 + j])) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input in: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            args.in, Float.floatToRawIntBits(args.in), args.in));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(args.out.toString());
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            arrayOut[i * 1 + j], Float.floatToRawIntBits(arrayOut[i * 1 + j]), arrayOut[i * 1 + j]));
+                    if (!args.out.couldBe(arrayOut[i * 1 + j])) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkNativeCoshFloatFloat" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkNativeCoshFloat2Float2() {
+        Allocation in = createRandomAllocation(mRS, Element.DataType.FLOAT_32, 2, 0xc77a568547f7e9d1l, false);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 2), INPUTSIZE);
+            script.forEach_testNativeCoshFloat2Float2(in, out);
+            verifyResultsNativeCoshFloat2Float2(in, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeCoshFloat2Float2: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 2), INPUTSIZE);
+            scriptRelaxed.forEach_testNativeCoshFloat2Float2(in, out);
+            verifyResultsNativeCoshFloat2Float2(in, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeCoshFloat2Float2: " + e.toString());
+        }
+    }
+
+    private void verifyResultsNativeCoshFloat2Float2(Allocation in, Allocation out, boolean relaxed) {
+        float[] arrayIn = new float[INPUTSIZE * 2];
+        in.copyTo(arrayIn);
+        float[] arrayOut = new float[INPUTSIZE * 2];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 2 ; j++) {
+                // Extract the inputs.
+                ArgumentsFloatFloat args = new ArgumentsFloatFloat();
+                args.in = arrayIn[i * 2 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeNativeCosh(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (!args.out.couldBe(arrayOut[i * 2 + j])) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input in: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            args.in, Float.floatToRawIntBits(args.in), args.in));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(args.out.toString());
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            arrayOut[i * 2 + j], Float.floatToRawIntBits(arrayOut[i * 2 + j]), arrayOut[i * 2 + j]));
+                    if (!args.out.couldBe(arrayOut[i * 2 + j])) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkNativeCoshFloat2Float2" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkNativeCoshFloat3Float3() {
+        Allocation in = createRandomAllocation(mRS, Element.DataType.FLOAT_32, 3, 0xc77a6126a6fe7f6bl, false);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 3), INPUTSIZE);
+            script.forEach_testNativeCoshFloat3Float3(in, out);
+            verifyResultsNativeCoshFloat3Float3(in, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeCoshFloat3Float3: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 3), INPUTSIZE);
+            scriptRelaxed.forEach_testNativeCoshFloat3Float3(in, out);
+            verifyResultsNativeCoshFloat3Float3(in, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeCoshFloat3Float3: " + e.toString());
+        }
+    }
+
+    private void verifyResultsNativeCoshFloat3Float3(Allocation in, Allocation out, boolean relaxed) {
+        float[] arrayIn = new float[INPUTSIZE * 4];
+        in.copyTo(arrayIn);
+        float[] arrayOut = new float[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 3 ; j++) {
+                // Extract the inputs.
+                ArgumentsFloatFloat args = new ArgumentsFloatFloat();
+                args.in = arrayIn[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeNativeCosh(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input in: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            args.in, Float.floatToRawIntBits(args.in), args.in));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(args.out.toString());
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            arrayOut[i * 4 + j], Float.floatToRawIntBits(arrayOut[i * 4 + j]), arrayOut[i * 4 + j]));
+                    if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkNativeCoshFloat3Float3" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkNativeCoshFloat4Float4() {
+        Allocation in = createRandomAllocation(mRS, Element.DataType.FLOAT_32, 4, 0xc77a6bc806051505l, false);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 4), INPUTSIZE);
+            script.forEach_testNativeCoshFloat4Float4(in, out);
+            verifyResultsNativeCoshFloat4Float4(in, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeCoshFloat4Float4: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 4), INPUTSIZE);
+            scriptRelaxed.forEach_testNativeCoshFloat4Float4(in, out);
+            verifyResultsNativeCoshFloat4Float4(in, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeCoshFloat4Float4: " + e.toString());
+        }
+    }
+
+    private void verifyResultsNativeCoshFloat4Float4(Allocation in, Allocation out, boolean relaxed) {
+        float[] arrayIn = new float[INPUTSIZE * 4];
+        in.copyTo(arrayIn);
+        float[] arrayOut = new float[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 4 ; j++) {
+                // Extract the inputs.
+                ArgumentsFloatFloat args = new ArgumentsFloatFloat();
+                args.in = arrayIn[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeNativeCosh(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input in: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            args.in, Float.floatToRawIntBits(args.in), args.in));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(args.out.toString());
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            arrayOut[i * 4 + j], Float.floatToRawIntBits(arrayOut[i * 4 + j]), arrayOut[i * 4 + j]));
+                    if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkNativeCoshFloat4Float4" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    public void testNativeCosh() {
+        checkNativeCoshFloatFloat();
+        checkNativeCoshFloat2Float2();
+        checkNativeCoshFloat3Float3();
+        checkNativeCoshFloat4Float4();
+    }
+}
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/TestNativeCospi.java b/tests/tests/renderscript/src/android/renderscript/cts/TestNativeCospi.java
new file mode 100644
index 0000000..0136d45
--- /dev/null
+++ b/tests/tests/renderscript/src/android/renderscript/cts/TestNativeCospi.java
@@ -0,0 +1,284 @@
+/*
+ * 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.
+ */
+
+// Don't edit this file!  It is auto-generated by frameworks/rs/api/gen_runtime.
+
+package android.renderscript.cts;
+
+import android.renderscript.Allocation;
+import android.renderscript.RSRuntimeException;
+import android.renderscript.Element;
+
+public class TestNativeCospi extends RSBaseCompute {
+
+    private ScriptC_TestNativeCospi script;
+    private ScriptC_TestNativeCospiRelaxed scriptRelaxed;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        script = new ScriptC_TestNativeCospi(mRS);
+        scriptRelaxed = new ScriptC_TestNativeCospiRelaxed(mRS);
+    }
+
+    public class ArgumentsFloatFloat {
+        public float in;
+        public Floaty out;
+    }
+
+    private void checkNativeCospiFloatFloat() {
+        Allocation in = createRandomAllocation(mRS, Element.DataType.FLOAT_32, 1, 0x8fc4f0187aafc9a6l, false);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 1), INPUTSIZE);
+            script.forEach_testNativeCospiFloatFloat(in, out);
+            verifyResultsNativeCospiFloatFloat(in, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeCospiFloatFloat: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 1), INPUTSIZE);
+            scriptRelaxed.forEach_testNativeCospiFloatFloat(in, out);
+            verifyResultsNativeCospiFloatFloat(in, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeCospiFloatFloat: " + e.toString());
+        }
+    }
+
+    private void verifyResultsNativeCospiFloatFloat(Allocation in, Allocation out, boolean relaxed) {
+        float[] arrayIn = new float[INPUTSIZE * 1];
+        in.copyTo(arrayIn);
+        float[] arrayOut = new float[INPUTSIZE * 1];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 1 ; j++) {
+                // Extract the inputs.
+                ArgumentsFloatFloat args = new ArgumentsFloatFloat();
+                args.in = arrayIn[i];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeNativeCospi(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (!args.out.couldBe(arrayOut[i * 1 + j])) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input in: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            args.in, Float.floatToRawIntBits(args.in), args.in));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(args.out.toString());
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            arrayOut[i * 1 + j], Float.floatToRawIntBits(arrayOut[i * 1 + j]), arrayOut[i * 1 + j]));
+                    if (!args.out.couldBe(arrayOut[i * 1 + j])) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkNativeCospiFloatFloat" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkNativeCospiFloat2Float2() {
+        Allocation in = createRandomAllocation(mRS, Element.DataType.FLOAT_32, 2, 0x656942c9d0dfeb12l, false);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 2), INPUTSIZE);
+            script.forEach_testNativeCospiFloat2Float2(in, out);
+            verifyResultsNativeCospiFloat2Float2(in, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeCospiFloat2Float2: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 2), INPUTSIZE);
+            scriptRelaxed.forEach_testNativeCospiFloat2Float2(in, out);
+            verifyResultsNativeCospiFloat2Float2(in, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeCospiFloat2Float2: " + e.toString());
+        }
+    }
+
+    private void verifyResultsNativeCospiFloat2Float2(Allocation in, Allocation out, boolean relaxed) {
+        float[] arrayIn = new float[INPUTSIZE * 2];
+        in.copyTo(arrayIn);
+        float[] arrayOut = new float[INPUTSIZE * 2];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 2 ; j++) {
+                // Extract the inputs.
+                ArgumentsFloatFloat args = new ArgumentsFloatFloat();
+                args.in = arrayIn[i * 2 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeNativeCospi(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (!args.out.couldBe(arrayOut[i * 2 + j])) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input in: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            args.in, Float.floatToRawIntBits(args.in), args.in));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(args.out.toString());
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            arrayOut[i * 2 + j], Float.floatToRawIntBits(arrayOut[i * 2 + j]), arrayOut[i * 2 + j]));
+                    if (!args.out.couldBe(arrayOut[i * 2 + j])) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkNativeCospiFloat2Float2" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkNativeCospiFloat3Float3() {
+        Allocation in = createRandomAllocation(mRS, Element.DataType.FLOAT_32, 3, 0x65694d6b2fe680acl, false);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 3), INPUTSIZE);
+            script.forEach_testNativeCospiFloat3Float3(in, out);
+            verifyResultsNativeCospiFloat3Float3(in, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeCospiFloat3Float3: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 3), INPUTSIZE);
+            scriptRelaxed.forEach_testNativeCospiFloat3Float3(in, out);
+            verifyResultsNativeCospiFloat3Float3(in, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeCospiFloat3Float3: " + e.toString());
+        }
+    }
+
+    private void verifyResultsNativeCospiFloat3Float3(Allocation in, Allocation out, boolean relaxed) {
+        float[] arrayIn = new float[INPUTSIZE * 4];
+        in.copyTo(arrayIn);
+        float[] arrayOut = new float[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 3 ; j++) {
+                // Extract the inputs.
+                ArgumentsFloatFloat args = new ArgumentsFloatFloat();
+                args.in = arrayIn[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeNativeCospi(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input in: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            args.in, Float.floatToRawIntBits(args.in), args.in));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(args.out.toString());
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            arrayOut[i * 4 + j], Float.floatToRawIntBits(arrayOut[i * 4 + j]), arrayOut[i * 4 + j]));
+                    if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkNativeCospiFloat3Float3" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkNativeCospiFloat4Float4() {
+        Allocation in = createRandomAllocation(mRS, Element.DataType.FLOAT_32, 4, 0x6569580c8eed1646l, false);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 4), INPUTSIZE);
+            script.forEach_testNativeCospiFloat4Float4(in, out);
+            verifyResultsNativeCospiFloat4Float4(in, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeCospiFloat4Float4: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 4), INPUTSIZE);
+            scriptRelaxed.forEach_testNativeCospiFloat4Float4(in, out);
+            verifyResultsNativeCospiFloat4Float4(in, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeCospiFloat4Float4: " + e.toString());
+        }
+    }
+
+    private void verifyResultsNativeCospiFloat4Float4(Allocation in, Allocation out, boolean relaxed) {
+        float[] arrayIn = new float[INPUTSIZE * 4];
+        in.copyTo(arrayIn);
+        float[] arrayOut = new float[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 4 ; j++) {
+                // Extract the inputs.
+                ArgumentsFloatFloat args = new ArgumentsFloatFloat();
+                args.in = arrayIn[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeNativeCospi(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input in: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            args.in, Float.floatToRawIntBits(args.in), args.in));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(args.out.toString());
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            arrayOut[i * 4 + j], Float.floatToRawIntBits(arrayOut[i * 4 + j]), arrayOut[i * 4 + j]));
+                    if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkNativeCospiFloat4Float4" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    public void testNativeCospi() {
+        checkNativeCospiFloatFloat();
+        checkNativeCospiFloat2Float2();
+        checkNativeCospiFloat3Float3();
+        checkNativeCospiFloat4Float4();
+    }
+}
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/TestNativeDistance.java b/tests/tests/renderscript/src/android/renderscript/cts/TestNativeDistance.java
new file mode 100644
index 0000000..975910e
--- /dev/null
+++ b/tests/tests/renderscript/src/android/renderscript/cts/TestNativeDistance.java
@@ -0,0 +1,357 @@
+/*
+ * 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.
+ */
+
+// Don't edit this file!  It is auto-generated by frameworks/rs/api/gen_runtime.
+
+package android.renderscript.cts;
+
+import android.renderscript.Allocation;
+import android.renderscript.RSRuntimeException;
+import android.renderscript.Element;
+
+public class TestNativeDistance extends RSBaseCompute {
+
+    private ScriptC_TestNativeDistance script;
+    private ScriptC_TestNativeDistanceRelaxed scriptRelaxed;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        script = new ScriptC_TestNativeDistance(mRS);
+        scriptRelaxed = new ScriptC_TestNativeDistanceRelaxed(mRS);
+    }
+
+    public class ArgumentsFloatFloatFloat {
+        public float inLhs;
+        public float inRhs;
+        public Floaty out;
+    }
+
+    private void checkNativeDistanceFloatFloatFloat() {
+        Allocation inLhs = createRandomAllocation(mRS, Element.DataType.FLOAT_32, 1, 0xe0fd4252f8556ff6l, false);
+        Allocation inRhs = createRandomAllocation(mRS, Element.DataType.FLOAT_32, 1, 0xe0fd4252f8559b4cl, false);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 1), INPUTSIZE);
+            script.set_gAllocInRhs(inRhs);
+            script.forEach_testNativeDistanceFloatFloatFloat(inLhs, out);
+            verifyResultsNativeDistanceFloatFloatFloat(inLhs, inRhs, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeDistanceFloatFloatFloat: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 1), INPUTSIZE);
+            scriptRelaxed.set_gAllocInRhs(inRhs);
+            scriptRelaxed.forEach_testNativeDistanceFloatFloatFloat(inLhs, out);
+            verifyResultsNativeDistanceFloatFloatFloat(inLhs, inRhs, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeDistanceFloatFloatFloat: " + e.toString());
+        }
+    }
+
+    private void verifyResultsNativeDistanceFloatFloatFloat(Allocation inLhs, Allocation inRhs, Allocation out, boolean relaxed) {
+        float[] arrayInLhs = new float[INPUTSIZE * 1];
+        inLhs.copyTo(arrayInLhs);
+        float[] arrayInRhs = new float[INPUTSIZE * 1];
+        inRhs.copyTo(arrayInRhs);
+        float[] arrayOut = new float[INPUTSIZE * 1];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            ArgumentsFloatFloatFloat args = new ArgumentsFloatFloatFloat();
+            // Create the appropriate sized arrays in args
+            // Fill args with the input values
+            args.inLhs = arrayInLhs[i];
+            args.inRhs = arrayInRhs[i];
+            Floaty.setRelaxed(relaxed);
+            CoreMathVerifier.computeNativeDistance(args);
+
+            // Compare the expected outputs to the actual values returned by RS.
+            boolean valid = true;
+            if (!args.out.couldBe(arrayOut[i])) {
+                valid = false;
+            }
+            if (!valid) {
+                StringBuilder message = new StringBuilder();
+                message.append("Input inLhs: ");
+                message.append(String.format("%14.8g %8x %15a",
+                        arrayInLhs[i], Float.floatToRawIntBits(arrayInLhs[i]), arrayInLhs[i]));
+                message.append("\n");
+                message.append("Input inRhs: ");
+                message.append(String.format("%14.8g %8x %15a",
+                        arrayInRhs[i], Float.floatToRawIntBits(arrayInRhs[i]), arrayInRhs[i]));
+                message.append("\n");
+                message.append("Expected output out: ");
+                message.append(args.out.toString());
+                message.append("\n");
+                message.append("Actual   output out: ");
+                message.append(String.format("%14.8g %8x %15a",
+                        arrayOut[i], Float.floatToRawIntBits(arrayOut[i]), arrayOut[i]));
+                if (!args.out.couldBe(arrayOut[i])) {
+                    message.append(" FAIL");
+                }
+                message.append("\n");
+                assertTrue("Incorrect output for checkNativeDistanceFloatFloatFloat" +
+                        (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+            }
+        }
+    }
+
+    public class ArgumentsFloatNFloatNFloat {
+        public float[] inLhs;
+        public float[] inRhs;
+        public Floaty out;
+    }
+
+    private void checkNativeDistanceFloat2Float2Float() {
+        Allocation inLhs = createRandomAllocation(mRS, Element.DataType.FLOAT_32, 2, 0x28a9ea2ea1fd926al, false);
+        Allocation inRhs = createRandomAllocation(mRS, Element.DataType.FLOAT_32, 2, 0x28a9ea2ea1fdbdc0l, false);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 1), INPUTSIZE);
+            script.set_gAllocInRhs(inRhs);
+            script.forEach_testNativeDistanceFloat2Float2Float(inLhs, out);
+            verifyResultsNativeDistanceFloat2Float2Float(inLhs, inRhs, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeDistanceFloat2Float2Float: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 1), INPUTSIZE);
+            scriptRelaxed.set_gAllocInRhs(inRhs);
+            scriptRelaxed.forEach_testNativeDistanceFloat2Float2Float(inLhs, out);
+            verifyResultsNativeDistanceFloat2Float2Float(inLhs, inRhs, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeDistanceFloat2Float2Float: " + e.toString());
+        }
+    }
+
+    private void verifyResultsNativeDistanceFloat2Float2Float(Allocation inLhs, Allocation inRhs, Allocation out, boolean relaxed) {
+        float[] arrayInLhs = new float[INPUTSIZE * 2];
+        inLhs.copyTo(arrayInLhs);
+        float[] arrayInRhs = new float[INPUTSIZE * 2];
+        inRhs.copyTo(arrayInRhs);
+        float[] arrayOut = new float[INPUTSIZE * 1];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            ArgumentsFloatNFloatNFloat args = new ArgumentsFloatNFloatNFloat();
+            // Create the appropriate sized arrays in args
+            args.inLhs = new float[2];
+            args.inRhs = new float[2];
+            // Fill args with the input values
+            for (int j = 0; j < 2 ; j++) {
+                args.inLhs[j] = arrayInLhs[i * 2 + j];
+            }
+            for (int j = 0; j < 2 ; j++) {
+                args.inRhs[j] = arrayInRhs[i * 2 + j];
+            }
+            Floaty.setRelaxed(relaxed);
+            CoreMathVerifier.computeNativeDistance(args);
+
+            // Compare the expected outputs to the actual values returned by RS.
+            boolean valid = true;
+            if (!args.out.couldBe(arrayOut[i])) {
+                valid = false;
+            }
+            if (!valid) {
+                StringBuilder message = new StringBuilder();
+                for (int j = 0; j < 2 ; j++) {
+                    message.append("Input inLhs: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            arrayInLhs[i * 2 + j], Float.floatToRawIntBits(arrayInLhs[i * 2 + j]), arrayInLhs[i * 2 + j]));
+                    message.append("\n");
+                }
+                for (int j = 0; j < 2 ; j++) {
+                    message.append("Input inRhs: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            arrayInRhs[i * 2 + j], Float.floatToRawIntBits(arrayInRhs[i * 2 + j]), arrayInRhs[i * 2 + j]));
+                    message.append("\n");
+                }
+                message.append("Expected output out: ");
+                message.append(args.out.toString());
+                message.append("\n");
+                message.append("Actual   output out: ");
+                message.append(String.format("%14.8g %8x %15a",
+                        arrayOut[i], Float.floatToRawIntBits(arrayOut[i]), arrayOut[i]));
+                if (!args.out.couldBe(arrayOut[i])) {
+                    message.append(" FAIL");
+                }
+                message.append("\n");
+                assertTrue("Incorrect output for checkNativeDistanceFloat2Float2Float" +
+                        (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+            }
+        }
+    }
+
+    private void checkNativeDistanceFloat3Float3Float() {
+        Allocation inLhs = createRandomAllocation(mRS, Element.DataType.FLOAT_32, 3, 0x559b398ef213adc4l, false);
+        Allocation inRhs = createRandomAllocation(mRS, Element.DataType.FLOAT_32, 3, 0x559b398ef213d91al, false);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 1), INPUTSIZE);
+            script.set_gAllocInRhs(inRhs);
+            script.forEach_testNativeDistanceFloat3Float3Float(inLhs, out);
+            verifyResultsNativeDistanceFloat3Float3Float(inLhs, inRhs, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeDistanceFloat3Float3Float: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 1), INPUTSIZE);
+            scriptRelaxed.set_gAllocInRhs(inRhs);
+            scriptRelaxed.forEach_testNativeDistanceFloat3Float3Float(inLhs, out);
+            verifyResultsNativeDistanceFloat3Float3Float(inLhs, inRhs, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeDistanceFloat3Float3Float: " + e.toString());
+        }
+    }
+
+    private void verifyResultsNativeDistanceFloat3Float3Float(Allocation inLhs, Allocation inRhs, Allocation out, boolean relaxed) {
+        float[] arrayInLhs = new float[INPUTSIZE * 4];
+        inLhs.copyTo(arrayInLhs);
+        float[] arrayInRhs = new float[INPUTSIZE * 4];
+        inRhs.copyTo(arrayInRhs);
+        float[] arrayOut = new float[INPUTSIZE * 1];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            ArgumentsFloatNFloatNFloat args = new ArgumentsFloatNFloatNFloat();
+            // Create the appropriate sized arrays in args
+            args.inLhs = new float[3];
+            args.inRhs = new float[3];
+            // Fill args with the input values
+            for (int j = 0; j < 3 ; j++) {
+                args.inLhs[j] = arrayInLhs[i * 4 + j];
+            }
+            for (int j = 0; j < 3 ; j++) {
+                args.inRhs[j] = arrayInRhs[i * 4 + j];
+            }
+            Floaty.setRelaxed(relaxed);
+            CoreMathVerifier.computeNativeDistance(args);
+
+            // Compare the expected outputs to the actual values returned by RS.
+            boolean valid = true;
+            if (!args.out.couldBe(arrayOut[i])) {
+                valid = false;
+            }
+            if (!valid) {
+                StringBuilder message = new StringBuilder();
+                for (int j = 0; j < 3 ; j++) {
+                    message.append("Input inLhs: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            arrayInLhs[i * 4 + j], Float.floatToRawIntBits(arrayInLhs[i * 4 + j]), arrayInLhs[i * 4 + j]));
+                    message.append("\n");
+                }
+                for (int j = 0; j < 3 ; j++) {
+                    message.append("Input inRhs: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            arrayInRhs[i * 4 + j], Float.floatToRawIntBits(arrayInRhs[i * 4 + j]), arrayInRhs[i * 4 + j]));
+                    message.append("\n");
+                }
+                message.append("Expected output out: ");
+                message.append(args.out.toString());
+                message.append("\n");
+                message.append("Actual   output out: ");
+                message.append(String.format("%14.8g %8x %15a",
+                        arrayOut[i], Float.floatToRawIntBits(arrayOut[i]), arrayOut[i]));
+                if (!args.out.couldBe(arrayOut[i])) {
+                    message.append(" FAIL");
+                }
+                message.append("\n");
+                assertTrue("Incorrect output for checkNativeDistanceFloat3Float3Float" +
+                        (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+            }
+        }
+    }
+
+    private void checkNativeDistanceFloat4Float4Float() {
+        Allocation inLhs = createRandomAllocation(mRS, Element.DataType.FLOAT_32, 4, 0x828c88ef4229c91el, false);
+        Allocation inRhs = createRandomAllocation(mRS, Element.DataType.FLOAT_32, 4, 0x828c88ef4229f474l, false);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 1), INPUTSIZE);
+            script.set_gAllocInRhs(inRhs);
+            script.forEach_testNativeDistanceFloat4Float4Float(inLhs, out);
+            verifyResultsNativeDistanceFloat4Float4Float(inLhs, inRhs, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeDistanceFloat4Float4Float: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 1), INPUTSIZE);
+            scriptRelaxed.set_gAllocInRhs(inRhs);
+            scriptRelaxed.forEach_testNativeDistanceFloat4Float4Float(inLhs, out);
+            verifyResultsNativeDistanceFloat4Float4Float(inLhs, inRhs, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeDistanceFloat4Float4Float: " + e.toString());
+        }
+    }
+
+    private void verifyResultsNativeDistanceFloat4Float4Float(Allocation inLhs, Allocation inRhs, Allocation out, boolean relaxed) {
+        float[] arrayInLhs = new float[INPUTSIZE * 4];
+        inLhs.copyTo(arrayInLhs);
+        float[] arrayInRhs = new float[INPUTSIZE * 4];
+        inRhs.copyTo(arrayInRhs);
+        float[] arrayOut = new float[INPUTSIZE * 1];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            ArgumentsFloatNFloatNFloat args = new ArgumentsFloatNFloatNFloat();
+            // Create the appropriate sized arrays in args
+            args.inLhs = new float[4];
+            args.inRhs = new float[4];
+            // Fill args with the input values
+            for (int j = 0; j < 4 ; j++) {
+                args.inLhs[j] = arrayInLhs[i * 4 + j];
+            }
+            for (int j = 0; j < 4 ; j++) {
+                args.inRhs[j] = arrayInRhs[i * 4 + j];
+            }
+            Floaty.setRelaxed(relaxed);
+            CoreMathVerifier.computeNativeDistance(args);
+
+            // Compare the expected outputs to the actual values returned by RS.
+            boolean valid = true;
+            if (!args.out.couldBe(arrayOut[i])) {
+                valid = false;
+            }
+            if (!valid) {
+                StringBuilder message = new StringBuilder();
+                for (int j = 0; j < 4 ; j++) {
+                    message.append("Input inLhs: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            arrayInLhs[i * 4 + j], Float.floatToRawIntBits(arrayInLhs[i * 4 + j]), arrayInLhs[i * 4 + j]));
+                    message.append("\n");
+                }
+                for (int j = 0; j < 4 ; j++) {
+                    message.append("Input inRhs: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            arrayInRhs[i * 4 + j], Float.floatToRawIntBits(arrayInRhs[i * 4 + j]), arrayInRhs[i * 4 + j]));
+                    message.append("\n");
+                }
+                message.append("Expected output out: ");
+                message.append(args.out.toString());
+                message.append("\n");
+                message.append("Actual   output out: ");
+                message.append(String.format("%14.8g %8x %15a",
+                        arrayOut[i], Float.floatToRawIntBits(arrayOut[i]), arrayOut[i]));
+                if (!args.out.couldBe(arrayOut[i])) {
+                    message.append(" FAIL");
+                }
+                message.append("\n");
+                assertTrue("Incorrect output for checkNativeDistanceFloat4Float4Float" +
+                        (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+            }
+        }
+    }
+
+    public void testNativeDistance() {
+        checkNativeDistanceFloatFloatFloat();
+        checkNativeDistanceFloat2Float2Float();
+        checkNativeDistanceFloat3Float3Float();
+        checkNativeDistanceFloat4Float4Float();
+    }
+}
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/TestNativeDivide.java b/tests/tests/renderscript/src/android/renderscript/cts/TestNativeDivide.java
new file mode 100644
index 0000000..e77a9cf
--- /dev/null
+++ b/tests/tests/renderscript/src/android/renderscript/cts/TestNativeDivide.java
@@ -0,0 +1,325 @@
+/*
+ * 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.
+ */
+
+// Don't edit this file!  It is auto-generated by frameworks/rs/api/gen_runtime.
+
+package android.renderscript.cts;
+
+import android.renderscript.Allocation;
+import android.renderscript.RSRuntimeException;
+import android.renderscript.Element;
+
+public class TestNativeDivide extends RSBaseCompute {
+
+    private ScriptC_TestNativeDivide script;
+    private ScriptC_TestNativeDivideRelaxed scriptRelaxed;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        script = new ScriptC_TestNativeDivide(mRS);
+        scriptRelaxed = new ScriptC_TestNativeDivideRelaxed(mRS);
+    }
+
+    public class ArgumentsFloatFloatFloat {
+        public float inLhs;
+        public float inRhs;
+        public Floaty out;
+    }
+
+    private void checkNativeDivideFloatFloatFloat() {
+        Allocation inLhs = createRandomAllocation(mRS, Element.DataType.FLOAT_32, 1, 0xe2845ef0c23d02del, false);
+        Allocation inRhs = createRandomAllocation(mRS, Element.DataType.FLOAT_32, 1, 0xe2845ef0c23d2e34l, false);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 1), INPUTSIZE);
+            script.set_gAllocInRhs(inRhs);
+            script.forEach_testNativeDivideFloatFloatFloat(inLhs, out);
+            verifyResultsNativeDivideFloatFloatFloat(inLhs, inRhs, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeDivideFloatFloatFloat: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 1), INPUTSIZE);
+            scriptRelaxed.set_gAllocInRhs(inRhs);
+            scriptRelaxed.forEach_testNativeDivideFloatFloatFloat(inLhs, out);
+            verifyResultsNativeDivideFloatFloatFloat(inLhs, inRhs, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeDivideFloatFloatFloat: " + e.toString());
+        }
+    }
+
+    private void verifyResultsNativeDivideFloatFloatFloat(Allocation inLhs, Allocation inRhs, Allocation out, boolean relaxed) {
+        float[] arrayInLhs = new float[INPUTSIZE * 1];
+        inLhs.copyTo(arrayInLhs);
+        float[] arrayInRhs = new float[INPUTSIZE * 1];
+        inRhs.copyTo(arrayInRhs);
+        float[] arrayOut = new float[INPUTSIZE * 1];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 1 ; j++) {
+                // Extract the inputs.
+                ArgumentsFloatFloatFloat args = new ArgumentsFloatFloatFloat();
+                args.inLhs = arrayInLhs[i];
+                args.inRhs = arrayInRhs[i];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeNativeDivide(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (!args.out.couldBe(arrayOut[i * 1 + j])) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inLhs: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            args.inLhs, Float.floatToRawIntBits(args.inLhs), args.inLhs));
+                    message.append("\n");
+                    message.append("Input inRhs: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            args.inRhs, Float.floatToRawIntBits(args.inRhs), args.inRhs));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(args.out.toString());
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            arrayOut[i * 1 + j], Float.floatToRawIntBits(arrayOut[i * 1 + j]), arrayOut[i * 1 + j]));
+                    if (!args.out.couldBe(arrayOut[i * 1 + j])) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkNativeDivideFloatFloatFloat" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkNativeDivideFloat2Float2Float2() {
+        Allocation inLhs = createRandomAllocation(mRS, Element.DataType.FLOAT_32, 2, 0x51c6d6ecaeab1c48l, false);
+        Allocation inRhs = createRandomAllocation(mRS, Element.DataType.FLOAT_32, 2, 0x51c6d6ecaeab479el, false);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 2), INPUTSIZE);
+            script.set_gAllocInRhs(inRhs);
+            script.forEach_testNativeDivideFloat2Float2Float2(inLhs, out);
+            verifyResultsNativeDivideFloat2Float2Float2(inLhs, inRhs, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeDivideFloat2Float2Float2: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 2), INPUTSIZE);
+            scriptRelaxed.set_gAllocInRhs(inRhs);
+            scriptRelaxed.forEach_testNativeDivideFloat2Float2Float2(inLhs, out);
+            verifyResultsNativeDivideFloat2Float2Float2(inLhs, inRhs, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeDivideFloat2Float2Float2: " + e.toString());
+        }
+    }
+
+    private void verifyResultsNativeDivideFloat2Float2Float2(Allocation inLhs, Allocation inRhs, Allocation out, boolean relaxed) {
+        float[] arrayInLhs = new float[INPUTSIZE * 2];
+        inLhs.copyTo(arrayInLhs);
+        float[] arrayInRhs = new float[INPUTSIZE * 2];
+        inRhs.copyTo(arrayInRhs);
+        float[] arrayOut = new float[INPUTSIZE * 2];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 2 ; j++) {
+                // Extract the inputs.
+                ArgumentsFloatFloatFloat args = new ArgumentsFloatFloatFloat();
+                args.inLhs = arrayInLhs[i * 2 + j];
+                args.inRhs = arrayInRhs[i * 2 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeNativeDivide(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (!args.out.couldBe(arrayOut[i * 2 + j])) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inLhs: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            args.inLhs, Float.floatToRawIntBits(args.inLhs), args.inLhs));
+                    message.append("\n");
+                    message.append("Input inRhs: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            args.inRhs, Float.floatToRawIntBits(args.inRhs), args.inRhs));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(args.out.toString());
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            arrayOut[i * 2 + j], Float.floatToRawIntBits(arrayOut[i * 2 + j]), arrayOut[i * 2 + j]));
+                    if (!args.out.couldBe(arrayOut[i * 2 + j])) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkNativeDivideFloat2Float2Float2" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkNativeDivideFloat3Float3Float3() {
+        Allocation inLhs = createRandomAllocation(mRS, Element.DataType.FLOAT_32, 3, 0xde4f2c1a2b24e021l, false);
+        Allocation inRhs = createRandomAllocation(mRS, Element.DataType.FLOAT_32, 3, 0xde4f2c1a2b250b77l, false);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 3), INPUTSIZE);
+            script.set_gAllocInRhs(inRhs);
+            script.forEach_testNativeDivideFloat3Float3Float3(inLhs, out);
+            verifyResultsNativeDivideFloat3Float3Float3(inLhs, inRhs, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeDivideFloat3Float3Float3: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 3), INPUTSIZE);
+            scriptRelaxed.set_gAllocInRhs(inRhs);
+            scriptRelaxed.forEach_testNativeDivideFloat3Float3Float3(inLhs, out);
+            verifyResultsNativeDivideFloat3Float3Float3(inLhs, inRhs, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeDivideFloat3Float3Float3: " + e.toString());
+        }
+    }
+
+    private void verifyResultsNativeDivideFloat3Float3Float3(Allocation inLhs, Allocation inRhs, Allocation out, boolean relaxed) {
+        float[] arrayInLhs = new float[INPUTSIZE * 4];
+        inLhs.copyTo(arrayInLhs);
+        float[] arrayInRhs = new float[INPUTSIZE * 4];
+        inRhs.copyTo(arrayInRhs);
+        float[] arrayOut = new float[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 3 ; j++) {
+                // Extract the inputs.
+                ArgumentsFloatFloatFloat args = new ArgumentsFloatFloatFloat();
+                args.inLhs = arrayInLhs[i * 4 + j];
+                args.inRhs = arrayInRhs[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeNativeDivide(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inLhs: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            args.inLhs, Float.floatToRawIntBits(args.inLhs), args.inLhs));
+                    message.append("\n");
+                    message.append("Input inRhs: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            args.inRhs, Float.floatToRawIntBits(args.inRhs), args.inRhs));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(args.out.toString());
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            arrayOut[i * 4 + j], Float.floatToRawIntBits(arrayOut[i * 4 + j]), arrayOut[i * 4 + j]));
+                    if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkNativeDivideFloat3Float3Float3" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkNativeDivideFloat4Float4Float4() {
+        Allocation inLhs = createRandomAllocation(mRS, Element.DataType.FLOAT_32, 4, 0x6ad78147a79ea3fal, false);
+        Allocation inRhs = createRandomAllocation(mRS, Element.DataType.FLOAT_32, 4, 0x6ad78147a79ecf50l, false);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 4), INPUTSIZE);
+            script.set_gAllocInRhs(inRhs);
+            script.forEach_testNativeDivideFloat4Float4Float4(inLhs, out);
+            verifyResultsNativeDivideFloat4Float4Float4(inLhs, inRhs, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeDivideFloat4Float4Float4: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 4), INPUTSIZE);
+            scriptRelaxed.set_gAllocInRhs(inRhs);
+            scriptRelaxed.forEach_testNativeDivideFloat4Float4Float4(inLhs, out);
+            verifyResultsNativeDivideFloat4Float4Float4(inLhs, inRhs, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeDivideFloat4Float4Float4: " + e.toString());
+        }
+    }
+
+    private void verifyResultsNativeDivideFloat4Float4Float4(Allocation inLhs, Allocation inRhs, Allocation out, boolean relaxed) {
+        float[] arrayInLhs = new float[INPUTSIZE * 4];
+        inLhs.copyTo(arrayInLhs);
+        float[] arrayInRhs = new float[INPUTSIZE * 4];
+        inRhs.copyTo(arrayInRhs);
+        float[] arrayOut = new float[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 4 ; j++) {
+                // Extract the inputs.
+                ArgumentsFloatFloatFloat args = new ArgumentsFloatFloatFloat();
+                args.inLhs = arrayInLhs[i * 4 + j];
+                args.inRhs = arrayInRhs[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeNativeDivide(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inLhs: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            args.inLhs, Float.floatToRawIntBits(args.inLhs), args.inLhs));
+                    message.append("\n");
+                    message.append("Input inRhs: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            args.inRhs, Float.floatToRawIntBits(args.inRhs), args.inRhs));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(args.out.toString());
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            arrayOut[i * 4 + j], Float.floatToRawIntBits(arrayOut[i * 4 + j]), arrayOut[i * 4 + j]));
+                    if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkNativeDivideFloat4Float4Float4" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    public void testNativeDivide() {
+        checkNativeDivideFloatFloatFloat();
+        checkNativeDivideFloat2Float2Float2();
+        checkNativeDivideFloat3Float3Float3();
+        checkNativeDivideFloat4Float4Float4();
+    }
+}
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/TestNativeExp.java b/tests/tests/renderscript/src/android/renderscript/cts/TestNativeExp.java
index 5432ce6..4f20a41 100644
--- a/tests/tests/renderscript/src/android/renderscript/cts/TestNativeExp.java
+++ b/tests/tests/renderscript/src/android/renderscript/cts/TestNativeExp.java
@@ -70,7 +70,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeNativeExp(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 1 + j])) {
                     valid = false;
@@ -129,7 +129,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeNativeExp(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 2 + j])) {
                     valid = false;
@@ -188,7 +188,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeNativeExp(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 4 + j])) {
                     valid = false;
@@ -247,7 +247,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeNativeExp(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 4 + j])) {
                     valid = false;
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/TestNativeExp10.java b/tests/tests/renderscript/src/android/renderscript/cts/TestNativeExp10.java
index 4f4d340..b6443c0 100644
--- a/tests/tests/renderscript/src/android/renderscript/cts/TestNativeExp10.java
+++ b/tests/tests/renderscript/src/android/renderscript/cts/TestNativeExp10.java
@@ -70,7 +70,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeNativeExp10(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 1 + j])) {
                     valid = false;
@@ -129,7 +129,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeNativeExp10(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 2 + j])) {
                     valid = false;
@@ -188,7 +188,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeNativeExp10(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 4 + j])) {
                     valid = false;
@@ -247,7 +247,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeNativeExp10(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 4 + j])) {
                     valid = false;
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/TestNativeExp2.java b/tests/tests/renderscript/src/android/renderscript/cts/TestNativeExp2.java
index 469140d..f1939ee 100644
--- a/tests/tests/renderscript/src/android/renderscript/cts/TestNativeExp2.java
+++ b/tests/tests/renderscript/src/android/renderscript/cts/TestNativeExp2.java
@@ -70,7 +70,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeNativeExp2(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 1 + j])) {
                     valid = false;
@@ -129,7 +129,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeNativeExp2(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 2 + j])) {
                     valid = false;
@@ -188,7 +188,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeNativeExp2(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 4 + j])) {
                     valid = false;
@@ -247,7 +247,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeNativeExp2(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 4 + j])) {
                     valid = false;
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/TestNativeExpm1.java b/tests/tests/renderscript/src/android/renderscript/cts/TestNativeExpm1.java
new file mode 100644
index 0000000..4b8b102
--- /dev/null
+++ b/tests/tests/renderscript/src/android/renderscript/cts/TestNativeExpm1.java
@@ -0,0 +1,284 @@
+/*
+ * 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.
+ */
+
+// Don't edit this file!  It is auto-generated by frameworks/rs/api/gen_runtime.
+
+package android.renderscript.cts;
+
+import android.renderscript.Allocation;
+import android.renderscript.RSRuntimeException;
+import android.renderscript.Element;
+
+public class TestNativeExpm1 extends RSBaseCompute {
+
+    private ScriptC_TestNativeExpm1 script;
+    private ScriptC_TestNativeExpm1Relaxed scriptRelaxed;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        script = new ScriptC_TestNativeExpm1(mRS);
+        scriptRelaxed = new ScriptC_TestNativeExpm1Relaxed(mRS);
+    }
+
+    public class ArgumentsFloatFloat {
+        public float in;
+        public Floaty out;
+    }
+
+    private void checkNativeExpm1FloatFloat() {
+        Allocation in = createRandomAllocation(mRS, Element.DataType.FLOAT_32, 1, 0x209e2820fcaa295fl, false);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 1), INPUTSIZE);
+            script.forEach_testNativeExpm1FloatFloat(in, out);
+            verifyResultsNativeExpm1FloatFloat(in, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeExpm1FloatFloat: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 1), INPUTSIZE);
+            scriptRelaxed.forEach_testNativeExpm1FloatFloat(in, out);
+            verifyResultsNativeExpm1FloatFloat(in, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeExpm1FloatFloat: " + e.toString());
+        }
+    }
+
+    private void verifyResultsNativeExpm1FloatFloat(Allocation in, Allocation out, boolean relaxed) {
+        float[] arrayIn = new float[INPUTSIZE * 1];
+        in.copyTo(arrayIn);
+        float[] arrayOut = new float[INPUTSIZE * 1];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 1 ; j++) {
+                // Extract the inputs.
+                ArgumentsFloatFloat args = new ArgumentsFloatFloat();
+                args.in = arrayIn[i];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeNativeExpm1(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (!args.out.couldBe(arrayOut[i * 1 + j])) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input in: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            args.in, Float.floatToRawIntBits(args.in), args.in));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(args.out.toString());
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            arrayOut[i * 1 + j], Float.floatToRawIntBits(arrayOut[i * 1 + j]), arrayOut[i * 1 + j]));
+                    if (!args.out.couldBe(arrayOut[i * 1 + j])) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkNativeExpm1FloatFloat" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkNativeExpm1Float2Float2() {
+        Allocation in = createRandomAllocation(mRS, Element.DataType.FLOAT_32, 2, 0x964ef83c9a3d4a43l, false);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 2), INPUTSIZE);
+            script.forEach_testNativeExpm1Float2Float2(in, out);
+            verifyResultsNativeExpm1Float2Float2(in, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeExpm1Float2Float2: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 2), INPUTSIZE);
+            scriptRelaxed.forEach_testNativeExpm1Float2Float2(in, out);
+            verifyResultsNativeExpm1Float2Float2(in, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeExpm1Float2Float2: " + e.toString());
+        }
+    }
+
+    private void verifyResultsNativeExpm1Float2Float2(Allocation in, Allocation out, boolean relaxed) {
+        float[] arrayIn = new float[INPUTSIZE * 2];
+        in.copyTo(arrayIn);
+        float[] arrayOut = new float[INPUTSIZE * 2];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 2 ; j++) {
+                // Extract the inputs.
+                ArgumentsFloatFloat args = new ArgumentsFloatFloat();
+                args.in = arrayIn[i * 2 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeNativeExpm1(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (!args.out.couldBe(arrayOut[i * 2 + j])) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input in: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            args.in, Float.floatToRawIntBits(args.in), args.in));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(args.out.toString());
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            arrayOut[i * 2 + j], Float.floatToRawIntBits(arrayOut[i * 2 + j]), arrayOut[i * 2 + j]));
+                    if (!args.out.couldBe(arrayOut[i * 2 + j])) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkNativeExpm1Float2Float2" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkNativeExpm1Float3Float3() {
+        Allocation in = createRandomAllocation(mRS, Element.DataType.FLOAT_32, 3, 0x964f02ddf943dfddl, false);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 3), INPUTSIZE);
+            script.forEach_testNativeExpm1Float3Float3(in, out);
+            verifyResultsNativeExpm1Float3Float3(in, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeExpm1Float3Float3: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 3), INPUTSIZE);
+            scriptRelaxed.forEach_testNativeExpm1Float3Float3(in, out);
+            verifyResultsNativeExpm1Float3Float3(in, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeExpm1Float3Float3: " + e.toString());
+        }
+    }
+
+    private void verifyResultsNativeExpm1Float3Float3(Allocation in, Allocation out, boolean relaxed) {
+        float[] arrayIn = new float[INPUTSIZE * 4];
+        in.copyTo(arrayIn);
+        float[] arrayOut = new float[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 3 ; j++) {
+                // Extract the inputs.
+                ArgumentsFloatFloat args = new ArgumentsFloatFloat();
+                args.in = arrayIn[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeNativeExpm1(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input in: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            args.in, Float.floatToRawIntBits(args.in), args.in));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(args.out.toString());
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            arrayOut[i * 4 + j], Float.floatToRawIntBits(arrayOut[i * 4 + j]), arrayOut[i * 4 + j]));
+                    if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkNativeExpm1Float3Float3" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkNativeExpm1Float4Float4() {
+        Allocation in = createRandomAllocation(mRS, Element.DataType.FLOAT_32, 4, 0x964f0d7f584a7577l, false);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 4), INPUTSIZE);
+            script.forEach_testNativeExpm1Float4Float4(in, out);
+            verifyResultsNativeExpm1Float4Float4(in, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeExpm1Float4Float4: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 4), INPUTSIZE);
+            scriptRelaxed.forEach_testNativeExpm1Float4Float4(in, out);
+            verifyResultsNativeExpm1Float4Float4(in, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeExpm1Float4Float4: " + e.toString());
+        }
+    }
+
+    private void verifyResultsNativeExpm1Float4Float4(Allocation in, Allocation out, boolean relaxed) {
+        float[] arrayIn = new float[INPUTSIZE * 4];
+        in.copyTo(arrayIn);
+        float[] arrayOut = new float[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 4 ; j++) {
+                // Extract the inputs.
+                ArgumentsFloatFloat args = new ArgumentsFloatFloat();
+                args.in = arrayIn[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeNativeExpm1(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input in: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            args.in, Float.floatToRawIntBits(args.in), args.in));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(args.out.toString());
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            arrayOut[i * 4 + j], Float.floatToRawIntBits(arrayOut[i * 4 + j]), arrayOut[i * 4 + j]));
+                    if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkNativeExpm1Float4Float4" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    public void testNativeExpm1() {
+        checkNativeExpm1FloatFloat();
+        checkNativeExpm1Float2Float2();
+        checkNativeExpm1Float3Float3();
+        checkNativeExpm1Float4Float4();
+    }
+}
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/TestNativeHypot.java b/tests/tests/renderscript/src/android/renderscript/cts/TestNativeHypot.java
new file mode 100644
index 0000000..b3538d5
--- /dev/null
+++ b/tests/tests/renderscript/src/android/renderscript/cts/TestNativeHypot.java
@@ -0,0 +1,325 @@
+/*
+ * 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.
+ */
+
+// Don't edit this file!  It is auto-generated by frameworks/rs/api/gen_runtime.
+
+package android.renderscript.cts;
+
+import android.renderscript.Allocation;
+import android.renderscript.RSRuntimeException;
+import android.renderscript.Element;
+
+public class TestNativeHypot extends RSBaseCompute {
+
+    private ScriptC_TestNativeHypot script;
+    private ScriptC_TestNativeHypotRelaxed scriptRelaxed;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        script = new ScriptC_TestNativeHypot(mRS);
+        scriptRelaxed = new ScriptC_TestNativeHypotRelaxed(mRS);
+    }
+
+    public class ArgumentsFloatFloatFloat {
+        public float inX;
+        public float inY;
+        public Floaty out;
+    }
+
+    private void checkNativeHypotFloatFloatFloat() {
+        Allocation inX = createRandomAllocation(mRS, Element.DataType.FLOAT_32, 1, 0x3d61f129bdf66018l, false);
+        Allocation inY = createRandomAllocation(mRS, Element.DataType.FLOAT_32, 1, 0x3d61f129bdf66019l, false);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 1), INPUTSIZE);
+            script.set_gAllocInY(inY);
+            script.forEach_testNativeHypotFloatFloatFloat(inX, out);
+            verifyResultsNativeHypotFloatFloatFloat(inX, inY, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeHypotFloatFloatFloat: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 1), INPUTSIZE);
+            scriptRelaxed.set_gAllocInY(inY);
+            scriptRelaxed.forEach_testNativeHypotFloatFloatFloat(inX, out);
+            verifyResultsNativeHypotFloatFloatFloat(inX, inY, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeHypotFloatFloatFloat: " + e.toString());
+        }
+    }
+
+    private void verifyResultsNativeHypotFloatFloatFloat(Allocation inX, Allocation inY, Allocation out, boolean relaxed) {
+        float[] arrayInX = new float[INPUTSIZE * 1];
+        inX.copyTo(arrayInX);
+        float[] arrayInY = new float[INPUTSIZE * 1];
+        inY.copyTo(arrayInY);
+        float[] arrayOut = new float[INPUTSIZE * 1];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 1 ; j++) {
+                // Extract the inputs.
+                ArgumentsFloatFloatFloat args = new ArgumentsFloatFloatFloat();
+                args.inX = arrayInX[i];
+                args.inY = arrayInY[i];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeNativeHypot(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (!args.out.couldBe(arrayOut[i * 1 + j])) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inX: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            args.inX, Float.floatToRawIntBits(args.inX), args.inX));
+                    message.append("\n");
+                    message.append("Input inY: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            args.inY, Float.floatToRawIntBits(args.inY), args.inY));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(args.out.toString());
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            arrayOut[i * 1 + j], Float.floatToRawIntBits(arrayOut[i * 1 + j]), arrayOut[i * 1 + j]));
+                    if (!args.out.couldBe(arrayOut[i * 1 + j])) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkNativeHypotFloatFloatFloat" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkNativeHypotFloat2Float2Float2() {
+        Allocation inX = createRandomAllocation(mRS, Element.DataType.FLOAT_32, 2, 0x92a8064760a50e64l, false);
+        Allocation inY = createRandomAllocation(mRS, Element.DataType.FLOAT_32, 2, 0x92a8064760a50e65l, false);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 2), INPUTSIZE);
+            script.set_gAllocInY(inY);
+            script.forEach_testNativeHypotFloat2Float2Float2(inX, out);
+            verifyResultsNativeHypotFloat2Float2Float2(inX, inY, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeHypotFloat2Float2Float2: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 2), INPUTSIZE);
+            scriptRelaxed.set_gAllocInY(inY);
+            scriptRelaxed.forEach_testNativeHypotFloat2Float2Float2(inX, out);
+            verifyResultsNativeHypotFloat2Float2Float2(inX, inY, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeHypotFloat2Float2Float2: " + e.toString());
+        }
+    }
+
+    private void verifyResultsNativeHypotFloat2Float2Float2(Allocation inX, Allocation inY, Allocation out, boolean relaxed) {
+        float[] arrayInX = new float[INPUTSIZE * 2];
+        inX.copyTo(arrayInX);
+        float[] arrayInY = new float[INPUTSIZE * 2];
+        inY.copyTo(arrayInY);
+        float[] arrayOut = new float[INPUTSIZE * 2];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 2 ; j++) {
+                // Extract the inputs.
+                ArgumentsFloatFloatFloat args = new ArgumentsFloatFloatFloat();
+                args.inX = arrayInX[i * 2 + j];
+                args.inY = arrayInY[i * 2 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeNativeHypot(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (!args.out.couldBe(arrayOut[i * 2 + j])) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inX: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            args.inX, Float.floatToRawIntBits(args.inX), args.inX));
+                    message.append("\n");
+                    message.append("Input inY: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            args.inY, Float.floatToRawIntBits(args.inY), args.inY));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(args.out.toString());
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            arrayOut[i * 2 + j], Float.floatToRawIntBits(arrayOut[i * 2 + j]), arrayOut[i * 2 + j]));
+                    if (!args.out.couldBe(arrayOut[i * 2 + j])) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkNativeHypotFloat2Float2Float2" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkNativeHypotFloat3Float3Float3() {
+        Allocation inX = createRandomAllocation(mRS, Element.DataType.FLOAT_32, 3, 0xe70ce46762831005l, false);
+        Allocation inY = createRandomAllocation(mRS, Element.DataType.FLOAT_32, 3, 0xe70ce46762831006l, false);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 3), INPUTSIZE);
+            script.set_gAllocInY(inY);
+            script.forEach_testNativeHypotFloat3Float3Float3(inX, out);
+            verifyResultsNativeHypotFloat3Float3Float3(inX, inY, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeHypotFloat3Float3Float3: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 3), INPUTSIZE);
+            scriptRelaxed.set_gAllocInY(inY);
+            scriptRelaxed.forEach_testNativeHypotFloat3Float3Float3(inX, out);
+            verifyResultsNativeHypotFloat3Float3Float3(inX, inY, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeHypotFloat3Float3Float3: " + e.toString());
+        }
+    }
+
+    private void verifyResultsNativeHypotFloat3Float3Float3(Allocation inX, Allocation inY, Allocation out, boolean relaxed) {
+        float[] arrayInX = new float[INPUTSIZE * 4];
+        inX.copyTo(arrayInX);
+        float[] arrayInY = new float[INPUTSIZE * 4];
+        inY.copyTo(arrayInY);
+        float[] arrayOut = new float[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 3 ; j++) {
+                // Extract the inputs.
+                ArgumentsFloatFloatFloat args = new ArgumentsFloatFloatFloat();
+                args.inX = arrayInX[i * 4 + j];
+                args.inY = arrayInY[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeNativeHypot(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inX: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            args.inX, Float.floatToRawIntBits(args.inX), args.inX));
+                    message.append("\n");
+                    message.append("Input inY: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            args.inY, Float.floatToRawIntBits(args.inY), args.inY));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(args.out.toString());
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            arrayOut[i * 4 + j], Float.floatToRawIntBits(arrayOut[i * 4 + j]), arrayOut[i * 4 + j]));
+                    if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkNativeHypotFloat3Float3Float3" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkNativeHypotFloat4Float4Float4() {
+        Allocation inX = createRandomAllocation(mRS, Element.DataType.FLOAT_32, 4, 0x3b71c287646111a6l, false);
+        Allocation inY = createRandomAllocation(mRS, Element.DataType.FLOAT_32, 4, 0x3b71c287646111a7l, false);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 4), INPUTSIZE);
+            script.set_gAllocInY(inY);
+            script.forEach_testNativeHypotFloat4Float4Float4(inX, out);
+            verifyResultsNativeHypotFloat4Float4Float4(inX, inY, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeHypotFloat4Float4Float4: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 4), INPUTSIZE);
+            scriptRelaxed.set_gAllocInY(inY);
+            scriptRelaxed.forEach_testNativeHypotFloat4Float4Float4(inX, out);
+            verifyResultsNativeHypotFloat4Float4Float4(inX, inY, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeHypotFloat4Float4Float4: " + e.toString());
+        }
+    }
+
+    private void verifyResultsNativeHypotFloat4Float4Float4(Allocation inX, Allocation inY, Allocation out, boolean relaxed) {
+        float[] arrayInX = new float[INPUTSIZE * 4];
+        inX.copyTo(arrayInX);
+        float[] arrayInY = new float[INPUTSIZE * 4];
+        inY.copyTo(arrayInY);
+        float[] arrayOut = new float[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 4 ; j++) {
+                // Extract the inputs.
+                ArgumentsFloatFloatFloat args = new ArgumentsFloatFloatFloat();
+                args.inX = arrayInX[i * 4 + j];
+                args.inY = arrayInY[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeNativeHypot(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inX: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            args.inX, Float.floatToRawIntBits(args.inX), args.inX));
+                    message.append("\n");
+                    message.append("Input inY: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            args.inY, Float.floatToRawIntBits(args.inY), args.inY));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(args.out.toString());
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            arrayOut[i * 4 + j], Float.floatToRawIntBits(arrayOut[i * 4 + j]), arrayOut[i * 4 + j]));
+                    if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkNativeHypotFloat4Float4Float4" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    public void testNativeHypot() {
+        checkNativeHypotFloatFloatFloat();
+        checkNativeHypotFloat2Float2Float2();
+        checkNativeHypotFloat3Float3Float3();
+        checkNativeHypotFloat4Float4Float4();
+    }
+}
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/TestNativeLength.java b/tests/tests/renderscript/src/android/renderscript/cts/TestNativeLength.java
new file mode 100644
index 0000000..fcba1bc
--- /dev/null
+++ b/tests/tests/renderscript/src/android/renderscript/cts/TestNativeLength.java
@@ -0,0 +1,300 @@
+/*
+ * 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.
+ */
+
+// Don't edit this file!  It is auto-generated by frameworks/rs/api/gen_runtime.
+
+package android.renderscript.cts;
+
+import android.renderscript.Allocation;
+import android.renderscript.RSRuntimeException;
+import android.renderscript.Element;
+
+public class TestNativeLength extends RSBaseCompute {
+
+    private ScriptC_TestNativeLength script;
+    private ScriptC_TestNativeLengthRelaxed scriptRelaxed;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        script = new ScriptC_TestNativeLength(mRS);
+        scriptRelaxed = new ScriptC_TestNativeLengthRelaxed(mRS);
+    }
+
+    public class ArgumentsFloatFloat {
+        public float inV;
+        public Floaty out;
+    }
+
+    private void checkNativeLengthFloatFloat() {
+        Allocation inV = createRandomAllocation(mRS, Element.DataType.FLOAT_32, 1, 0xbe51f0c4d1df20ecl, false);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 1), INPUTSIZE);
+            script.forEach_testNativeLengthFloatFloat(inV, out);
+            verifyResultsNativeLengthFloatFloat(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeLengthFloatFloat: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 1), INPUTSIZE);
+            scriptRelaxed.forEach_testNativeLengthFloatFloat(inV, out);
+            verifyResultsNativeLengthFloatFloat(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeLengthFloatFloat: " + e.toString());
+        }
+    }
+
+    private void verifyResultsNativeLengthFloatFloat(Allocation inV, Allocation out, boolean relaxed) {
+        float[] arrayInV = new float[INPUTSIZE * 1];
+        inV.copyTo(arrayInV);
+        float[] arrayOut = new float[INPUTSIZE * 1];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            ArgumentsFloatFloat args = new ArgumentsFloatFloat();
+            // Create the appropriate sized arrays in args
+            // Fill args with the input values
+            args.inV = arrayInV[i];
+            Floaty.setRelaxed(relaxed);
+            CoreMathVerifier.computeNativeLength(args);
+
+            // Compare the expected outputs to the actual values returned by RS.
+            boolean valid = true;
+            if (!args.out.couldBe(arrayOut[i])) {
+                valid = false;
+            }
+            if (!valid) {
+                StringBuilder message = new StringBuilder();
+                message.append("Input inV: ");
+                message.append(String.format("%14.8g %8x %15a",
+                        arrayInV[i], Float.floatToRawIntBits(arrayInV[i]), arrayInV[i]));
+                message.append("\n");
+                message.append("Expected output out: ");
+                message.append(args.out.toString());
+                message.append("\n");
+                message.append("Actual   output out: ");
+                message.append(String.format("%14.8g %8x %15a",
+                        arrayOut[i], Float.floatToRawIntBits(arrayOut[i]), arrayOut[i]));
+                if (!args.out.couldBe(arrayOut[i])) {
+                    message.append(" FAIL");
+                }
+                message.append("\n");
+                assertTrue("Incorrect output for checkNativeLengthFloatFloat" +
+                        (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+            }
+        }
+    }
+
+    public class ArgumentsFloatNFloat {
+        public float[] inV;
+        public Floaty out;
+    }
+
+    private void checkNativeLengthFloat2Float() {
+        Allocation inV = createRandomAllocation(mRS, Element.DataType.FLOAT_32, 2, 0xf7c2930af1b4824cl, false);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 1), INPUTSIZE);
+            script.forEach_testNativeLengthFloat2Float(inV, out);
+            verifyResultsNativeLengthFloat2Float(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeLengthFloat2Float: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 1), INPUTSIZE);
+            scriptRelaxed.forEach_testNativeLengthFloat2Float(inV, out);
+            verifyResultsNativeLengthFloat2Float(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeLengthFloat2Float: " + e.toString());
+        }
+    }
+
+    private void verifyResultsNativeLengthFloat2Float(Allocation inV, Allocation out, boolean relaxed) {
+        float[] arrayInV = new float[INPUTSIZE * 2];
+        inV.copyTo(arrayInV);
+        float[] arrayOut = new float[INPUTSIZE * 1];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            ArgumentsFloatNFloat args = new ArgumentsFloatNFloat();
+            // Create the appropriate sized arrays in args
+            args.inV = new float[2];
+            // Fill args with the input values
+            for (int j = 0; j < 2 ; j++) {
+                args.inV[j] = arrayInV[i * 2 + j];
+            }
+            Floaty.setRelaxed(relaxed);
+            CoreMathVerifier.computeNativeLength(args);
+
+            // Compare the expected outputs to the actual values returned by RS.
+            boolean valid = true;
+            if (!args.out.couldBe(arrayOut[i])) {
+                valid = false;
+            }
+            if (!valid) {
+                StringBuilder message = new StringBuilder();
+                for (int j = 0; j < 2 ; j++) {
+                    message.append("Input inV: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            arrayInV[i * 2 + j], Float.floatToRawIntBits(arrayInV[i * 2 + j]), arrayInV[i * 2 + j]));
+                    message.append("\n");
+                }
+                message.append("Expected output out: ");
+                message.append(args.out.toString());
+                message.append("\n");
+                message.append("Actual   output out: ");
+                message.append(String.format("%14.8g %8x %15a",
+                        arrayOut[i], Float.floatToRawIntBits(arrayOut[i]), arrayOut[i]));
+                if (!args.out.couldBe(arrayOut[i])) {
+                    message.append(" FAIL");
+                }
+                message.append("\n");
+                assertTrue("Incorrect output for checkNativeLengthFloat2Float" +
+                        (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+            }
+        }
+    }
+
+    private void checkNativeLengthFloat3Float() {
+        Allocation inV = createRandomAllocation(mRS, Element.DataType.FLOAT_32, 3, 0xf7c29dac50bb10adl, false);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 1), INPUTSIZE);
+            script.forEach_testNativeLengthFloat3Float(inV, out);
+            verifyResultsNativeLengthFloat3Float(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeLengthFloat3Float: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 1), INPUTSIZE);
+            scriptRelaxed.forEach_testNativeLengthFloat3Float(inV, out);
+            verifyResultsNativeLengthFloat3Float(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeLengthFloat3Float: " + e.toString());
+        }
+    }
+
+    private void verifyResultsNativeLengthFloat3Float(Allocation inV, Allocation out, boolean relaxed) {
+        float[] arrayInV = new float[INPUTSIZE * 4];
+        inV.copyTo(arrayInV);
+        float[] arrayOut = new float[INPUTSIZE * 1];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            ArgumentsFloatNFloat args = new ArgumentsFloatNFloat();
+            // Create the appropriate sized arrays in args
+            args.inV = new float[3];
+            // Fill args with the input values
+            for (int j = 0; j < 3 ; j++) {
+                args.inV[j] = arrayInV[i * 4 + j];
+            }
+            Floaty.setRelaxed(relaxed);
+            CoreMathVerifier.computeNativeLength(args);
+
+            // Compare the expected outputs to the actual values returned by RS.
+            boolean valid = true;
+            if (!args.out.couldBe(arrayOut[i])) {
+                valid = false;
+            }
+            if (!valid) {
+                StringBuilder message = new StringBuilder();
+                for (int j = 0; j < 3 ; j++) {
+                    message.append("Input inV: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            arrayInV[i * 4 + j], Float.floatToRawIntBits(arrayInV[i * 4 + j]), arrayInV[i * 4 + j]));
+                    message.append("\n");
+                }
+                message.append("Expected output out: ");
+                message.append(args.out.toString());
+                message.append("\n");
+                message.append("Actual   output out: ");
+                message.append(String.format("%14.8g %8x %15a",
+                        arrayOut[i], Float.floatToRawIntBits(arrayOut[i]), arrayOut[i]));
+                if (!args.out.couldBe(arrayOut[i])) {
+                    message.append(" FAIL");
+                }
+                message.append("\n");
+                assertTrue("Incorrect output for checkNativeLengthFloat3Float" +
+                        (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+            }
+        }
+    }
+
+    private void checkNativeLengthFloat4Float() {
+        Allocation inV = createRandomAllocation(mRS, Element.DataType.FLOAT_32, 4, 0xf7c2a84dafc19f0el, false);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 1), INPUTSIZE);
+            script.forEach_testNativeLengthFloat4Float(inV, out);
+            verifyResultsNativeLengthFloat4Float(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeLengthFloat4Float: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 1), INPUTSIZE);
+            scriptRelaxed.forEach_testNativeLengthFloat4Float(inV, out);
+            verifyResultsNativeLengthFloat4Float(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeLengthFloat4Float: " + e.toString());
+        }
+    }
+
+    private void verifyResultsNativeLengthFloat4Float(Allocation inV, Allocation out, boolean relaxed) {
+        float[] arrayInV = new float[INPUTSIZE * 4];
+        inV.copyTo(arrayInV);
+        float[] arrayOut = new float[INPUTSIZE * 1];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            ArgumentsFloatNFloat args = new ArgumentsFloatNFloat();
+            // Create the appropriate sized arrays in args
+            args.inV = new float[4];
+            // Fill args with the input values
+            for (int j = 0; j < 4 ; j++) {
+                args.inV[j] = arrayInV[i * 4 + j];
+            }
+            Floaty.setRelaxed(relaxed);
+            CoreMathVerifier.computeNativeLength(args);
+
+            // Compare the expected outputs to the actual values returned by RS.
+            boolean valid = true;
+            if (!args.out.couldBe(arrayOut[i])) {
+                valid = false;
+            }
+            if (!valid) {
+                StringBuilder message = new StringBuilder();
+                for (int j = 0; j < 4 ; j++) {
+                    message.append("Input inV: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            arrayInV[i * 4 + j], Float.floatToRawIntBits(arrayInV[i * 4 + j]), arrayInV[i * 4 + j]));
+                    message.append("\n");
+                }
+                message.append("Expected output out: ");
+                message.append(args.out.toString());
+                message.append("\n");
+                message.append("Actual   output out: ");
+                message.append(String.format("%14.8g %8x %15a",
+                        arrayOut[i], Float.floatToRawIntBits(arrayOut[i]), arrayOut[i]));
+                if (!args.out.couldBe(arrayOut[i])) {
+                    message.append(" FAIL");
+                }
+                message.append("\n");
+                assertTrue("Incorrect output for checkNativeLengthFloat4Float" +
+                        (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+            }
+        }
+    }
+
+    public void testNativeLength() {
+        checkNativeLengthFloatFloat();
+        checkNativeLengthFloat2Float();
+        checkNativeLengthFloat3Float();
+        checkNativeLengthFloat4Float();
+    }
+}
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/TestNativeLog.java b/tests/tests/renderscript/src/android/renderscript/cts/TestNativeLog.java
index 821760b..3cbd176 100644
--- a/tests/tests/renderscript/src/android/renderscript/cts/TestNativeLog.java
+++ b/tests/tests/renderscript/src/android/renderscript/cts/TestNativeLog.java
@@ -70,7 +70,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeNativeLog(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 1 + j], 0.0002)) {
                     valid = false;
@@ -129,7 +129,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeNativeLog(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 2 + j], 0.0002)) {
                     valid = false;
@@ -188,7 +188,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeNativeLog(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 4 + j], 0.0002)) {
                     valid = false;
@@ -247,7 +247,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeNativeLog(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 4 + j], 0.0002)) {
                     valid = false;
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/TestNativeLog10.java b/tests/tests/renderscript/src/android/renderscript/cts/TestNativeLog10.java
index caa1133..33bf7a6 100644
--- a/tests/tests/renderscript/src/android/renderscript/cts/TestNativeLog10.java
+++ b/tests/tests/renderscript/src/android/renderscript/cts/TestNativeLog10.java
@@ -70,7 +70,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeNativeLog10(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 1 + j], 0.00005)) {
                     valid = false;
@@ -129,7 +129,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeNativeLog10(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 2 + j], 0.00005)) {
                     valid = false;
@@ -188,7 +188,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeNativeLog10(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 4 + j], 0.00005)) {
                     valid = false;
@@ -247,7 +247,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeNativeLog10(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 4 + j], 0.00005)) {
                     valid = false;
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/TestNativeLog1p.java b/tests/tests/renderscript/src/android/renderscript/cts/TestNativeLog1p.java
new file mode 100644
index 0000000..60cb122
--- /dev/null
+++ b/tests/tests/renderscript/src/android/renderscript/cts/TestNativeLog1p.java
@@ -0,0 +1,284 @@
+/*
+ * 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.
+ */
+
+// Don't edit this file!  It is auto-generated by frameworks/rs/api/gen_runtime.
+
+package android.renderscript.cts;
+
+import android.renderscript.Allocation;
+import android.renderscript.RSRuntimeException;
+import android.renderscript.Element;
+
+public class TestNativeLog1p extends RSBaseCompute {
+
+    private ScriptC_TestNativeLog1p script;
+    private ScriptC_TestNativeLog1pRelaxed scriptRelaxed;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        script = new ScriptC_TestNativeLog1p(mRS);
+        scriptRelaxed = new ScriptC_TestNativeLog1pRelaxed(mRS);
+    }
+
+    public class ArgumentsFloatFloat {
+        public float in;
+        public Floaty out;
+    }
+
+    private void checkNativeLog1pFloatFloat() {
+        Allocation in = createRandomAllocation(mRS, Element.DataType.FLOAT_32, 1, 0x444585911437d95l, false);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 1), INPUTSIZE);
+            script.forEach_testNativeLog1pFloatFloat(in, out);
+            verifyResultsNativeLog1pFloatFloat(in, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeLog1pFloatFloat: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 1), INPUTSIZE);
+            scriptRelaxed.forEach_testNativeLog1pFloatFloat(in, out);
+            verifyResultsNativeLog1pFloatFloat(in, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeLog1pFloatFloat: " + e.toString());
+        }
+    }
+
+    private void verifyResultsNativeLog1pFloatFloat(Allocation in, Allocation out, boolean relaxed) {
+        float[] arrayIn = new float[INPUTSIZE * 1];
+        in.copyTo(arrayIn);
+        float[] arrayOut = new float[INPUTSIZE * 1];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 1 ; j++) {
+                // Extract the inputs.
+                ArgumentsFloatFloat args = new ArgumentsFloatFloat();
+                args.in = arrayIn[i];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeNativeLog1p(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (!args.out.couldBe(arrayOut[i * 1 + j])) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input in: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            args.in, Float.floatToRawIntBits(args.in), args.in));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(args.out.toString());
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            arrayOut[i * 1 + j], Float.floatToRawIntBits(arrayOut[i * 1 + j]), arrayOut[i * 1 + j]));
+                    if (!args.out.couldBe(arrayOut[i * 1 + j])) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkNativeLog1pFloatFloat" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkNativeLog1pFloat2Float2() {
+        Allocation in = createRandomAllocation(mRS, Element.DataType.FLOAT_32, 2, 0xd1a13d4961ae8449l, false);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 2), INPUTSIZE);
+            script.forEach_testNativeLog1pFloat2Float2(in, out);
+            verifyResultsNativeLog1pFloat2Float2(in, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeLog1pFloat2Float2: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 2), INPUTSIZE);
+            scriptRelaxed.forEach_testNativeLog1pFloat2Float2(in, out);
+            verifyResultsNativeLog1pFloat2Float2(in, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeLog1pFloat2Float2: " + e.toString());
+        }
+    }
+
+    private void verifyResultsNativeLog1pFloat2Float2(Allocation in, Allocation out, boolean relaxed) {
+        float[] arrayIn = new float[INPUTSIZE * 2];
+        in.copyTo(arrayIn);
+        float[] arrayOut = new float[INPUTSIZE * 2];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 2 ; j++) {
+                // Extract the inputs.
+                ArgumentsFloatFloat args = new ArgumentsFloatFloat();
+                args.in = arrayIn[i * 2 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeNativeLog1p(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (!args.out.couldBe(arrayOut[i * 2 + j])) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input in: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            args.in, Float.floatToRawIntBits(args.in), args.in));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(args.out.toString());
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            arrayOut[i * 2 + j], Float.floatToRawIntBits(arrayOut[i * 2 + j]), arrayOut[i * 2 + j]));
+                    if (!args.out.couldBe(arrayOut[i * 2 + j])) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkNativeLog1pFloat2Float2" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkNativeLog1pFloat3Float3() {
+        Allocation in = createRandomAllocation(mRS, Element.DataType.FLOAT_32, 3, 0xd1a147eac0b519e3l, false);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 3), INPUTSIZE);
+            script.forEach_testNativeLog1pFloat3Float3(in, out);
+            verifyResultsNativeLog1pFloat3Float3(in, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeLog1pFloat3Float3: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 3), INPUTSIZE);
+            scriptRelaxed.forEach_testNativeLog1pFloat3Float3(in, out);
+            verifyResultsNativeLog1pFloat3Float3(in, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeLog1pFloat3Float3: " + e.toString());
+        }
+    }
+
+    private void verifyResultsNativeLog1pFloat3Float3(Allocation in, Allocation out, boolean relaxed) {
+        float[] arrayIn = new float[INPUTSIZE * 4];
+        in.copyTo(arrayIn);
+        float[] arrayOut = new float[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 3 ; j++) {
+                // Extract the inputs.
+                ArgumentsFloatFloat args = new ArgumentsFloatFloat();
+                args.in = arrayIn[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeNativeLog1p(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input in: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            args.in, Float.floatToRawIntBits(args.in), args.in));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(args.out.toString());
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            arrayOut[i * 4 + j], Float.floatToRawIntBits(arrayOut[i * 4 + j]), arrayOut[i * 4 + j]));
+                    if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkNativeLog1pFloat3Float3" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkNativeLog1pFloat4Float4() {
+        Allocation in = createRandomAllocation(mRS, Element.DataType.FLOAT_32, 4, 0xd1a1528c1fbbaf7dl, false);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 4), INPUTSIZE);
+            script.forEach_testNativeLog1pFloat4Float4(in, out);
+            verifyResultsNativeLog1pFloat4Float4(in, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeLog1pFloat4Float4: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 4), INPUTSIZE);
+            scriptRelaxed.forEach_testNativeLog1pFloat4Float4(in, out);
+            verifyResultsNativeLog1pFloat4Float4(in, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeLog1pFloat4Float4: " + e.toString());
+        }
+    }
+
+    private void verifyResultsNativeLog1pFloat4Float4(Allocation in, Allocation out, boolean relaxed) {
+        float[] arrayIn = new float[INPUTSIZE * 4];
+        in.copyTo(arrayIn);
+        float[] arrayOut = new float[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 4 ; j++) {
+                // Extract the inputs.
+                ArgumentsFloatFloat args = new ArgumentsFloatFloat();
+                args.in = arrayIn[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeNativeLog1p(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input in: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            args.in, Float.floatToRawIntBits(args.in), args.in));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(args.out.toString());
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            arrayOut[i * 4 + j], Float.floatToRawIntBits(arrayOut[i * 4 + j]), arrayOut[i * 4 + j]));
+                    if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkNativeLog1pFloat4Float4" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    public void testNativeLog1p() {
+        checkNativeLog1pFloatFloat();
+        checkNativeLog1pFloat2Float2();
+        checkNativeLog1pFloat3Float3();
+        checkNativeLog1pFloat4Float4();
+    }
+}
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/TestNativeLog2.java b/tests/tests/renderscript/src/android/renderscript/cts/TestNativeLog2.java
index b860a5e..5e85550 100644
--- a/tests/tests/renderscript/src/android/renderscript/cts/TestNativeLog2.java
+++ b/tests/tests/renderscript/src/android/renderscript/cts/TestNativeLog2.java
@@ -70,7 +70,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeNativeLog2(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 1 + j], 0.0002)) {
                     valid = false;
@@ -129,7 +129,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeNativeLog2(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 2 + j], 0.0002)) {
                     valid = false;
@@ -188,7 +188,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeNativeLog2(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 4 + j], 0.0002)) {
                     valid = false;
@@ -247,7 +247,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeNativeLog2(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 4 + j], 0.0002)) {
                     valid = false;
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/TestNativeNormalize.java b/tests/tests/renderscript/src/android/renderscript/cts/TestNativeNormalize.java
new file mode 100644
index 0000000..2873998
--- /dev/null
+++ b/tests/tests/renderscript/src/android/renderscript/cts/TestNativeNormalize.java
@@ -0,0 +1,315 @@
+/*
+ * 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.
+ */
+
+// Don't edit this file!  It is auto-generated by frameworks/rs/api/gen_runtime.
+
+package android.renderscript.cts;
+
+import android.renderscript.Allocation;
+import android.renderscript.RSRuntimeException;
+import android.renderscript.Element;
+
+public class TestNativeNormalize extends RSBaseCompute {
+
+    private ScriptC_TestNativeNormalize script;
+    private ScriptC_TestNativeNormalizeRelaxed scriptRelaxed;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        script = new ScriptC_TestNativeNormalize(mRS);
+        scriptRelaxed = new ScriptC_TestNativeNormalizeRelaxed(mRS);
+    }
+
+    public class ArgumentsFloatFloat {
+        public float inV;
+        public Floaty out;
+    }
+
+    private void checkNativeNormalizeFloatFloat() {
+        Allocation inV = createRandomAllocation(mRS, Element.DataType.FLOAT_32, 1, 0x487756167e4aac53l, false);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 1), INPUTSIZE);
+            script.forEach_testNativeNormalizeFloatFloat(inV, out);
+            verifyResultsNativeNormalizeFloatFloat(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeNormalizeFloatFloat: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 1), INPUTSIZE);
+            scriptRelaxed.forEach_testNativeNormalizeFloatFloat(inV, out);
+            verifyResultsNativeNormalizeFloatFloat(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeNormalizeFloatFloat: " + e.toString());
+        }
+    }
+
+    private void verifyResultsNativeNormalizeFloatFloat(Allocation inV, Allocation out, boolean relaxed) {
+        float[] arrayInV = new float[INPUTSIZE * 1];
+        inV.copyTo(arrayInV);
+        float[] arrayOut = new float[INPUTSIZE * 1];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            ArgumentsFloatFloat args = new ArgumentsFloatFloat();
+            // Create the appropriate sized arrays in args
+            // Fill args with the input values
+            args.inV = arrayInV[i];
+            Floaty.setRelaxed(relaxed);
+            CoreMathVerifier.computeNativeNormalize(args);
+
+            // Compare the expected outputs to the actual values returned by RS.
+            boolean valid = true;
+            if (!args.out.couldBe(arrayOut[i])) {
+                valid = false;
+            }
+            if (!valid) {
+                StringBuilder message = new StringBuilder();
+                message.append("Input inV: ");
+                message.append(String.format("%14.8g %8x %15a",
+                        arrayInV[i], Float.floatToRawIntBits(arrayInV[i]), arrayInV[i]));
+                message.append("\n");
+                message.append("Expected output out: ");
+                message.append(args.out.toString());
+                message.append("\n");
+                message.append("Actual   output out: ");
+                message.append(String.format("%14.8g %8x %15a",
+                        arrayOut[i], Float.floatToRawIntBits(arrayOut[i]), arrayOut[i]));
+                if (!args.out.couldBe(arrayOut[i])) {
+                    message.append(" FAIL");
+                }
+                message.append("\n");
+                assertTrue("Incorrect output for checkNativeNormalizeFloatFloat" +
+                        (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+            }
+        }
+    }
+
+    public class ArgumentsFloatNFloatN {
+        public float[] inV;
+        public Floaty[] out;
+    }
+
+    private void checkNativeNormalizeFloat2Float2() {
+        Allocation inV = createRandomAllocation(mRS, Element.DataType.FLOAT_32, 2, 0x65c77dbcedd0e45fl, false);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 2), INPUTSIZE);
+            script.forEach_testNativeNormalizeFloat2Float2(inV, out);
+            verifyResultsNativeNormalizeFloat2Float2(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeNormalizeFloat2Float2: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 2), INPUTSIZE);
+            scriptRelaxed.forEach_testNativeNormalizeFloat2Float2(inV, out);
+            verifyResultsNativeNormalizeFloat2Float2(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeNormalizeFloat2Float2: " + e.toString());
+        }
+    }
+
+    private void verifyResultsNativeNormalizeFloat2Float2(Allocation inV, Allocation out, boolean relaxed) {
+        float[] arrayInV = new float[INPUTSIZE * 2];
+        inV.copyTo(arrayInV);
+        float[] arrayOut = new float[INPUTSIZE * 2];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            ArgumentsFloatNFloatN args = new ArgumentsFloatNFloatN();
+            // Create the appropriate sized arrays in args
+            args.inV = new float[2];
+            args.out = new Floaty[2];
+            // Fill args with the input values
+            for (int j = 0; j < 2 ; j++) {
+                args.inV[j] = arrayInV[i * 2 + j];
+            }
+            Floaty.setRelaxed(relaxed);
+            CoreMathVerifier.computeNativeNormalize(args);
+
+            // Compare the expected outputs to the actual values returned by RS.
+            boolean valid = true;
+            for (int j = 0; j < 2 ; j++) {
+                if (!args.out[j].couldBe(arrayOut[i * 2 + j])) {
+                    valid = false;
+                }
+            }
+            if (!valid) {
+                StringBuilder message = new StringBuilder();
+                for (int j = 0; j < 2 ; j++) {
+                    message.append("Input inV: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            arrayInV[i * 2 + j], Float.floatToRawIntBits(arrayInV[i * 2 + j]), arrayInV[i * 2 + j]));
+                    message.append("\n");
+                }
+                for (int j = 0; j < 2 ; j++) {
+                    message.append("Expected output out: ");
+                    message.append(args.out[j].toString());
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            arrayOut[i * 2 + j], Float.floatToRawIntBits(arrayOut[i * 2 + j]), arrayOut[i * 2 + j]));
+                    if (!args.out[j].couldBe(arrayOut[i * 2 + j])) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                }
+                assertTrue("Incorrect output for checkNativeNormalizeFloat2Float2" +
+                        (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+            }
+        }
+    }
+
+    private void checkNativeNormalizeFloat3Float3() {
+        Allocation inV = createRandomAllocation(mRS, Element.DataType.FLOAT_32, 3, 0x65c946d7e3ec053dl, false);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 3), INPUTSIZE);
+            script.forEach_testNativeNormalizeFloat3Float3(inV, out);
+            verifyResultsNativeNormalizeFloat3Float3(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeNormalizeFloat3Float3: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 3), INPUTSIZE);
+            scriptRelaxed.forEach_testNativeNormalizeFloat3Float3(inV, out);
+            verifyResultsNativeNormalizeFloat3Float3(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeNormalizeFloat3Float3: " + e.toString());
+        }
+    }
+
+    private void verifyResultsNativeNormalizeFloat3Float3(Allocation inV, Allocation out, boolean relaxed) {
+        float[] arrayInV = new float[INPUTSIZE * 4];
+        inV.copyTo(arrayInV);
+        float[] arrayOut = new float[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            ArgumentsFloatNFloatN args = new ArgumentsFloatNFloatN();
+            // Create the appropriate sized arrays in args
+            args.inV = new float[3];
+            args.out = new Floaty[3];
+            // Fill args with the input values
+            for (int j = 0; j < 3 ; j++) {
+                args.inV[j] = arrayInV[i * 4 + j];
+            }
+            Floaty.setRelaxed(relaxed);
+            CoreMathVerifier.computeNativeNormalize(args);
+
+            // Compare the expected outputs to the actual values returned by RS.
+            boolean valid = true;
+            for (int j = 0; j < 3 ; j++) {
+                if (!args.out[j].couldBe(arrayOut[i * 4 + j])) {
+                    valid = false;
+                }
+            }
+            if (!valid) {
+                StringBuilder message = new StringBuilder();
+                for (int j = 0; j < 3 ; j++) {
+                    message.append("Input inV: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            arrayInV[i * 4 + j], Float.floatToRawIntBits(arrayInV[i * 4 + j]), arrayInV[i * 4 + j]));
+                    message.append("\n");
+                }
+                for (int j = 0; j < 3 ; j++) {
+                    message.append("Expected output out: ");
+                    message.append(args.out[j].toString());
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            arrayOut[i * 4 + j], Float.floatToRawIntBits(arrayOut[i * 4 + j]), arrayOut[i * 4 + j]));
+                    if (!args.out[j].couldBe(arrayOut[i * 4 + j])) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                }
+                assertTrue("Incorrect output for checkNativeNormalizeFloat3Float3" +
+                        (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+            }
+        }
+    }
+
+    private void checkNativeNormalizeFloat4Float4() {
+        Allocation inV = createRandomAllocation(mRS, Element.DataType.FLOAT_32, 4, 0x65cb0ff2da07261bl, false);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 4), INPUTSIZE);
+            script.forEach_testNativeNormalizeFloat4Float4(inV, out);
+            verifyResultsNativeNormalizeFloat4Float4(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeNormalizeFloat4Float4: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 4), INPUTSIZE);
+            scriptRelaxed.forEach_testNativeNormalizeFloat4Float4(inV, out);
+            verifyResultsNativeNormalizeFloat4Float4(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeNormalizeFloat4Float4: " + e.toString());
+        }
+    }
+
+    private void verifyResultsNativeNormalizeFloat4Float4(Allocation inV, Allocation out, boolean relaxed) {
+        float[] arrayInV = new float[INPUTSIZE * 4];
+        inV.copyTo(arrayInV);
+        float[] arrayOut = new float[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            ArgumentsFloatNFloatN args = new ArgumentsFloatNFloatN();
+            // Create the appropriate sized arrays in args
+            args.inV = new float[4];
+            args.out = new Floaty[4];
+            // Fill args with the input values
+            for (int j = 0; j < 4 ; j++) {
+                args.inV[j] = arrayInV[i * 4 + j];
+            }
+            Floaty.setRelaxed(relaxed);
+            CoreMathVerifier.computeNativeNormalize(args);
+
+            // Compare the expected outputs to the actual values returned by RS.
+            boolean valid = true;
+            for (int j = 0; j < 4 ; j++) {
+                if (!args.out[j].couldBe(arrayOut[i * 4 + j])) {
+                    valid = false;
+                }
+            }
+            if (!valid) {
+                StringBuilder message = new StringBuilder();
+                for (int j = 0; j < 4 ; j++) {
+                    message.append("Input inV: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            arrayInV[i * 4 + j], Float.floatToRawIntBits(arrayInV[i * 4 + j]), arrayInV[i * 4 + j]));
+                    message.append("\n");
+                }
+                for (int j = 0; j < 4 ; j++) {
+                    message.append("Expected output out: ");
+                    message.append(args.out[j].toString());
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            arrayOut[i * 4 + j], Float.floatToRawIntBits(arrayOut[i * 4 + j]), arrayOut[i * 4 + j]));
+                    if (!args.out[j].couldBe(arrayOut[i * 4 + j])) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                }
+                assertTrue("Incorrect output for checkNativeNormalizeFloat4Float4" +
+                        (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+            }
+        }
+    }
+
+    public void testNativeNormalize() {
+        checkNativeNormalizeFloatFloat();
+        checkNativeNormalizeFloat2Float2();
+        checkNativeNormalizeFloat3Float3();
+        checkNativeNormalizeFloat4Float4();
+    }
+}
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/TestNativeRecip.java b/tests/tests/renderscript/src/android/renderscript/cts/TestNativeRecip.java
new file mode 100644
index 0000000..bef9efe
--- /dev/null
+++ b/tests/tests/renderscript/src/android/renderscript/cts/TestNativeRecip.java
@@ -0,0 +1,284 @@
+/*
+ * 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.
+ */
+
+// Don't edit this file!  It is auto-generated by frameworks/rs/api/gen_runtime.
+
+package android.renderscript.cts;
+
+import android.renderscript.Allocation;
+import android.renderscript.RSRuntimeException;
+import android.renderscript.Element;
+
+public class TestNativeRecip extends RSBaseCompute {
+
+    private ScriptC_TestNativeRecip script;
+    private ScriptC_TestNativeRecipRelaxed scriptRelaxed;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        script = new ScriptC_TestNativeRecip(mRS);
+        scriptRelaxed = new ScriptC_TestNativeRecipRelaxed(mRS);
+    }
+
+    public class ArgumentsFloatFloat {
+        public float inV;
+        public Floaty out;
+    }
+
+    private void checkNativeRecipFloatFloat() {
+        Allocation inV = createRandomAllocation(mRS, Element.DataType.FLOAT_32, 1, 0x4ec14a4fcc4ed441l, false);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 1), INPUTSIZE);
+            script.forEach_testNativeRecipFloatFloat(inV, out);
+            verifyResultsNativeRecipFloatFloat(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeRecipFloatFloat: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 1), INPUTSIZE);
+            scriptRelaxed.forEach_testNativeRecipFloatFloat(inV, out);
+            verifyResultsNativeRecipFloatFloat(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeRecipFloatFloat: " + e.toString());
+        }
+    }
+
+    private void verifyResultsNativeRecipFloatFloat(Allocation inV, Allocation out, boolean relaxed) {
+        float[] arrayInV = new float[INPUTSIZE * 1];
+        inV.copyTo(arrayInV);
+        float[] arrayOut = new float[INPUTSIZE * 1];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 1 ; j++) {
+                // Extract the inputs.
+                ArgumentsFloatFloat args = new ArgumentsFloatFloat();
+                args.inV = arrayInV[i];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeNativeRecip(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (!args.out.couldBe(arrayOut[i * 1 + j])) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            args.inV, Float.floatToRawIntBits(args.inV), args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(args.out.toString());
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            arrayOut[i * 1 + j], Float.floatToRawIntBits(arrayOut[i * 1 + j]), arrayOut[i * 1 + j]));
+                    if (!args.out.couldBe(arrayOut[i * 1 + j])) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkNativeRecipFloatFloat" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkNativeRecipFloat2Float2() {
+        Allocation inV = createRandomAllocation(mRS, Element.DataType.FLOAT_32, 2, 0xd1ec6fa169d54a5dl, false);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 2), INPUTSIZE);
+            script.forEach_testNativeRecipFloat2Float2(inV, out);
+            verifyResultsNativeRecipFloat2Float2(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeRecipFloat2Float2: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 2), INPUTSIZE);
+            scriptRelaxed.forEach_testNativeRecipFloat2Float2(inV, out);
+            verifyResultsNativeRecipFloat2Float2(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeRecipFloat2Float2: " + e.toString());
+        }
+    }
+
+    private void verifyResultsNativeRecipFloat2Float2(Allocation inV, Allocation out, boolean relaxed) {
+        float[] arrayInV = new float[INPUTSIZE * 2];
+        inV.copyTo(arrayInV);
+        float[] arrayOut = new float[INPUTSIZE * 2];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 2 ; j++) {
+                // Extract the inputs.
+                ArgumentsFloatFloat args = new ArgumentsFloatFloat();
+                args.inV = arrayInV[i * 2 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeNativeRecip(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (!args.out.couldBe(arrayOut[i * 2 + j])) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            args.inV, Float.floatToRawIntBits(args.inV), args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(args.out.toString());
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            arrayOut[i * 2 + j], Float.floatToRawIntBits(arrayOut[i * 2 + j]), arrayOut[i * 2 + j]));
+                    if (!args.out.couldBe(arrayOut[i * 2 + j])) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkNativeRecipFloat2Float2" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkNativeRecipFloat3Float3() {
+        Allocation inV = createRandomAllocation(mRS, Element.DataType.FLOAT_32, 3, 0xd1ee38bc5ff06b3bl, false);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 3), INPUTSIZE);
+            script.forEach_testNativeRecipFloat3Float3(inV, out);
+            verifyResultsNativeRecipFloat3Float3(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeRecipFloat3Float3: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 3), INPUTSIZE);
+            scriptRelaxed.forEach_testNativeRecipFloat3Float3(inV, out);
+            verifyResultsNativeRecipFloat3Float3(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeRecipFloat3Float3: " + e.toString());
+        }
+    }
+
+    private void verifyResultsNativeRecipFloat3Float3(Allocation inV, Allocation out, boolean relaxed) {
+        float[] arrayInV = new float[INPUTSIZE * 4];
+        inV.copyTo(arrayInV);
+        float[] arrayOut = new float[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 3 ; j++) {
+                // Extract the inputs.
+                ArgumentsFloatFloat args = new ArgumentsFloatFloat();
+                args.inV = arrayInV[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeNativeRecip(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            args.inV, Float.floatToRawIntBits(args.inV), args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(args.out.toString());
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            arrayOut[i * 4 + j], Float.floatToRawIntBits(arrayOut[i * 4 + j]), arrayOut[i * 4 + j]));
+                    if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkNativeRecipFloat3Float3" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkNativeRecipFloat4Float4() {
+        Allocation inV = createRandomAllocation(mRS, Element.DataType.FLOAT_32, 4, 0xd1f001d7560b8c19l, false);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 4), INPUTSIZE);
+            script.forEach_testNativeRecipFloat4Float4(inV, out);
+            verifyResultsNativeRecipFloat4Float4(inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeRecipFloat4Float4: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 4), INPUTSIZE);
+            scriptRelaxed.forEach_testNativeRecipFloat4Float4(inV, out);
+            verifyResultsNativeRecipFloat4Float4(inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeRecipFloat4Float4: " + e.toString());
+        }
+    }
+
+    private void verifyResultsNativeRecipFloat4Float4(Allocation inV, Allocation out, boolean relaxed) {
+        float[] arrayInV = new float[INPUTSIZE * 4];
+        inV.copyTo(arrayInV);
+        float[] arrayOut = new float[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 4 ; j++) {
+                // Extract the inputs.
+                ArgumentsFloatFloat args = new ArgumentsFloatFloat();
+                args.inV = arrayInV[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeNativeRecip(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            args.inV, Float.floatToRawIntBits(args.inV), args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(args.out.toString());
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            arrayOut[i * 4 + j], Float.floatToRawIntBits(arrayOut[i * 4 + j]), arrayOut[i * 4 + j]));
+                    if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkNativeRecipFloat4Float4" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    public void testNativeRecip() {
+        checkNativeRecipFloatFloat();
+        checkNativeRecipFloat2Float2();
+        checkNativeRecipFloat3Float3();
+        checkNativeRecipFloat4Float4();
+    }
+}
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/TestNativeRootn.java b/tests/tests/renderscript/src/android/renderscript/cts/TestNativeRootn.java
new file mode 100644
index 0000000..3d0320e
--- /dev/null
+++ b/tests/tests/renderscript/src/android/renderscript/cts/TestNativeRootn.java
@@ -0,0 +1,119 @@
+/*
+ * 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.
+ */
+
+// Don't edit this file!  It is auto-generated by frameworks/rs/api/gen_runtime.
+
+package android.renderscript.cts;
+
+import android.renderscript.Allocation;
+import android.renderscript.RSRuntimeException;
+import android.renderscript.Element;
+
+public class TestNativeRootn extends RSBaseCompute {
+
+    private ScriptC_TestNativeRootn script;
+    private ScriptC_TestNativeRootnRelaxed scriptRelaxed;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        script = new ScriptC_TestNativeRootn(mRS);
+        scriptRelaxed = new ScriptC_TestNativeRootnRelaxed(mRS);
+    }
+
+    private void checkNativeRootnFloatIntFloat() {
+        Allocation inV = createRandomAllocation(mRS, Element.DataType.FLOAT_32, 1, 0x6693fe5c237ac0f1l, false);
+        Allocation inN = createRandomAllocation(mRS, Element.DataType.SIGNED_32, 1, 0x6693fe5c237ac0e9l, false);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 1), INPUTSIZE);
+            script.set_gAllocInN(inN);
+            script.forEach_testNativeRootnFloatIntFloat(inV, out);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeRootnFloatIntFloat: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 1), INPUTSIZE);
+            scriptRelaxed.set_gAllocInN(inN);
+            scriptRelaxed.forEach_testNativeRootnFloatIntFloat(inV, out);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeRootnFloatIntFloat: " + e.toString());
+        }
+    }
+
+    private void checkNativeRootnFloat2Int2Float2() {
+        Allocation inV = createRandomAllocation(mRS, Element.DataType.FLOAT_32, 2, 0x5363e8c04afd5bcdl, false);
+        Allocation inN = createRandomAllocation(mRS, Element.DataType.SIGNED_32, 2, 0x5363e8c04afd5bc5l, false);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 2), INPUTSIZE);
+            script.set_gAllocInN(inN);
+            script.forEach_testNativeRootnFloat2Int2Float2(inV, out);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeRootnFloat2Int2Float2: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 2), INPUTSIZE);
+            scriptRelaxed.set_gAllocInN(inN);
+            scriptRelaxed.forEach_testNativeRootnFloat2Int2Float2(inV, out);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeRootnFloat2Int2Float2: " + e.toString());
+        }
+    }
+
+    private void checkNativeRootnFloat3Int3Float3() {
+        Allocation inV = createRandomAllocation(mRS, Element.DataType.FLOAT_32, 3, 0x791a272340afc886l, false);
+        Allocation inN = createRandomAllocation(mRS, Element.DataType.SIGNED_32, 3, 0x791a272340afc87el, false);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 3), INPUTSIZE);
+            script.set_gAllocInN(inN);
+            script.forEach_testNativeRootnFloat3Int3Float3(inV, out);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeRootnFloat3Int3Float3: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 3), INPUTSIZE);
+            scriptRelaxed.set_gAllocInN(inN);
+            scriptRelaxed.forEach_testNativeRootnFloat3Int3Float3(inV, out);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeRootnFloat3Int3Float3: " + e.toString());
+        }
+    }
+
+    private void checkNativeRootnFloat4Int4Float4() {
+        Allocation inV = createRandomAllocation(mRS, Element.DataType.FLOAT_32, 4, 0x9ed065863662353fl, false);
+        Allocation inN = createRandomAllocation(mRS, Element.DataType.SIGNED_32, 4, 0x9ed0658636623537l, false);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 4), INPUTSIZE);
+            script.set_gAllocInN(inN);
+            script.forEach_testNativeRootnFloat4Int4Float4(inV, out);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeRootnFloat4Int4Float4: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 4), INPUTSIZE);
+            scriptRelaxed.set_gAllocInN(inN);
+            scriptRelaxed.forEach_testNativeRootnFloat4Int4Float4(inV, out);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeRootnFloat4Int4Float4: " + e.toString());
+        }
+    }
+
+    public void testNativeRootn() {
+        checkNativeRootnFloatIntFloat();
+        checkNativeRootnFloat2Int2Float2();
+        checkNativeRootnFloat3Int3Float3();
+        checkNativeRootnFloat4Int4Float4();
+    }
+}
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/TestNativeRsqrt.java b/tests/tests/renderscript/src/android/renderscript/cts/TestNativeRsqrt.java
new file mode 100644
index 0000000..99c30ff
--- /dev/null
+++ b/tests/tests/renderscript/src/android/renderscript/cts/TestNativeRsqrt.java
@@ -0,0 +1,284 @@
+/*
+ * 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.
+ */
+
+// Don't edit this file!  It is auto-generated by frameworks/rs/api/gen_runtime.
+
+package android.renderscript.cts;
+
+import android.renderscript.Allocation;
+import android.renderscript.RSRuntimeException;
+import android.renderscript.Element;
+
+public class TestNativeRsqrt extends RSBaseCompute {
+
+    private ScriptC_TestNativeRsqrt script;
+    private ScriptC_TestNativeRsqrtRelaxed scriptRelaxed;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        script = new ScriptC_TestNativeRsqrt(mRS);
+        scriptRelaxed = new ScriptC_TestNativeRsqrtRelaxed(mRS);
+    }
+
+    public class ArgumentsFloatFloat {
+        public float in;
+        public Floaty out;
+    }
+
+    private void checkNativeRsqrtFloatFloat() {
+        Allocation in = createRandomAllocation(mRS, Element.DataType.FLOAT_32, 1, 0xf3cf23b51aa29de0l, false);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 1), INPUTSIZE);
+            script.forEach_testNativeRsqrtFloatFloat(in, out);
+            verifyResultsNativeRsqrtFloatFloat(in, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeRsqrtFloatFloat: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 1), INPUTSIZE);
+            scriptRelaxed.forEach_testNativeRsqrtFloatFloat(in, out);
+            verifyResultsNativeRsqrtFloatFloat(in, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeRsqrtFloatFloat: " + e.toString());
+        }
+    }
+
+    private void verifyResultsNativeRsqrtFloatFloat(Allocation in, Allocation out, boolean relaxed) {
+        float[] arrayIn = new float[INPUTSIZE * 1];
+        in.copyTo(arrayIn);
+        float[] arrayOut = new float[INPUTSIZE * 1];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 1 ; j++) {
+                // Extract the inputs.
+                ArgumentsFloatFloat args = new ArgumentsFloatFloat();
+                args.in = arrayIn[i];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeNativeRsqrt(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (!args.out.couldBe(arrayOut[i * 1 + j])) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input in: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            args.in, Float.floatToRawIntBits(args.in), args.in));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(args.out.toString());
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            arrayOut[i * 1 + j], Float.floatToRawIntBits(arrayOut[i * 1 + j]), arrayOut[i * 1 + j]));
+                    if (!args.out.couldBe(arrayOut[i * 1 + j])) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkNativeRsqrtFloatFloat" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkNativeRsqrtFloat2Float2() {
+        Allocation in = createRandomAllocation(mRS, Element.DataType.FLOAT_32, 2, 0xf318090911bec1fcl, false);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 2), INPUTSIZE);
+            script.forEach_testNativeRsqrtFloat2Float2(in, out);
+            verifyResultsNativeRsqrtFloat2Float2(in, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeRsqrtFloat2Float2: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 2), INPUTSIZE);
+            scriptRelaxed.forEach_testNativeRsqrtFloat2Float2(in, out);
+            verifyResultsNativeRsqrtFloat2Float2(in, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeRsqrtFloat2Float2: " + e.toString());
+        }
+    }
+
+    private void verifyResultsNativeRsqrtFloat2Float2(Allocation in, Allocation out, boolean relaxed) {
+        float[] arrayIn = new float[INPUTSIZE * 2];
+        in.copyTo(arrayIn);
+        float[] arrayOut = new float[INPUTSIZE * 2];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 2 ; j++) {
+                // Extract the inputs.
+                ArgumentsFloatFloat args = new ArgumentsFloatFloat();
+                args.in = arrayIn[i * 2 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeNativeRsqrt(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (!args.out.couldBe(arrayOut[i * 2 + j])) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input in: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            args.in, Float.floatToRawIntBits(args.in), args.in));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(args.out.toString());
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            arrayOut[i * 2 + j], Float.floatToRawIntBits(arrayOut[i * 2 + j]), arrayOut[i * 2 + j]));
+                    if (!args.out.couldBe(arrayOut[i * 2 + j])) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkNativeRsqrtFloat2Float2" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkNativeRsqrtFloat3Float3() {
+        Allocation in = createRandomAllocation(mRS, Element.DataType.FLOAT_32, 3, 0xf31813aa70c55796l, false);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 3), INPUTSIZE);
+            script.forEach_testNativeRsqrtFloat3Float3(in, out);
+            verifyResultsNativeRsqrtFloat3Float3(in, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeRsqrtFloat3Float3: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 3), INPUTSIZE);
+            scriptRelaxed.forEach_testNativeRsqrtFloat3Float3(in, out);
+            verifyResultsNativeRsqrtFloat3Float3(in, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeRsqrtFloat3Float3: " + e.toString());
+        }
+    }
+
+    private void verifyResultsNativeRsqrtFloat3Float3(Allocation in, Allocation out, boolean relaxed) {
+        float[] arrayIn = new float[INPUTSIZE * 4];
+        in.copyTo(arrayIn);
+        float[] arrayOut = new float[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 3 ; j++) {
+                // Extract the inputs.
+                ArgumentsFloatFloat args = new ArgumentsFloatFloat();
+                args.in = arrayIn[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeNativeRsqrt(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input in: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            args.in, Float.floatToRawIntBits(args.in), args.in));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(args.out.toString());
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            arrayOut[i * 4 + j], Float.floatToRawIntBits(arrayOut[i * 4 + j]), arrayOut[i * 4 + j]));
+                    if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkNativeRsqrtFloat3Float3" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkNativeRsqrtFloat4Float4() {
+        Allocation in = createRandomAllocation(mRS, Element.DataType.FLOAT_32, 4, 0xf3181e4bcfcbed30l, false);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 4), INPUTSIZE);
+            script.forEach_testNativeRsqrtFloat4Float4(in, out);
+            verifyResultsNativeRsqrtFloat4Float4(in, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeRsqrtFloat4Float4: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 4), INPUTSIZE);
+            scriptRelaxed.forEach_testNativeRsqrtFloat4Float4(in, out);
+            verifyResultsNativeRsqrtFloat4Float4(in, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeRsqrtFloat4Float4: " + e.toString());
+        }
+    }
+
+    private void verifyResultsNativeRsqrtFloat4Float4(Allocation in, Allocation out, boolean relaxed) {
+        float[] arrayIn = new float[INPUTSIZE * 4];
+        in.copyTo(arrayIn);
+        float[] arrayOut = new float[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 4 ; j++) {
+                // Extract the inputs.
+                ArgumentsFloatFloat args = new ArgumentsFloatFloat();
+                args.in = arrayIn[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeNativeRsqrt(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input in: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            args.in, Float.floatToRawIntBits(args.in), args.in));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(args.out.toString());
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            arrayOut[i * 4 + j], Float.floatToRawIntBits(arrayOut[i * 4 + j]), arrayOut[i * 4 + j]));
+                    if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkNativeRsqrtFloat4Float4" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    public void testNativeRsqrt() {
+        checkNativeRsqrtFloatFloat();
+        checkNativeRsqrtFloat2Float2();
+        checkNativeRsqrtFloat3Float3();
+        checkNativeRsqrtFloat4Float4();
+    }
+}
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/TestNativeSin.java b/tests/tests/renderscript/src/android/renderscript/cts/TestNativeSin.java
new file mode 100644
index 0000000..1399f7e
--- /dev/null
+++ b/tests/tests/renderscript/src/android/renderscript/cts/TestNativeSin.java
@@ -0,0 +1,284 @@
+/*
+ * 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.
+ */
+
+// Don't edit this file!  It is auto-generated by frameworks/rs/api/gen_runtime.
+
+package android.renderscript.cts;
+
+import android.renderscript.Allocation;
+import android.renderscript.RSRuntimeException;
+import android.renderscript.Element;
+
+public class TestNativeSin extends RSBaseCompute {
+
+    private ScriptC_TestNativeSin script;
+    private ScriptC_TestNativeSinRelaxed scriptRelaxed;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        script = new ScriptC_TestNativeSin(mRS);
+        scriptRelaxed = new ScriptC_TestNativeSinRelaxed(mRS);
+    }
+
+    public class ArgumentsFloatFloat {
+        public float in;
+        public Floaty out;
+    }
+
+    private void checkNativeSinFloatFloat() {
+        Allocation in = createRandomAllocation(mRS, Element.DataType.FLOAT_32, 1, 0x33ee19763354cc56l, false);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 1), INPUTSIZE);
+            script.forEach_testNativeSinFloatFloat(in, out);
+            verifyResultsNativeSinFloatFloat(in, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeSinFloatFloat: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 1), INPUTSIZE);
+            scriptRelaxed.forEach_testNativeSinFloatFloat(in, out);
+            verifyResultsNativeSinFloatFloat(in, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeSinFloatFloat: " + e.toString());
+        }
+    }
+
+    private void verifyResultsNativeSinFloatFloat(Allocation in, Allocation out, boolean relaxed) {
+        float[] arrayIn = new float[INPUTSIZE * 1];
+        in.copyTo(arrayIn);
+        float[] arrayOut = new float[INPUTSIZE * 1];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 1 ; j++) {
+                // Extract the inputs.
+                ArgumentsFloatFloat args = new ArgumentsFloatFloat();
+                args.in = arrayIn[i];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeNativeSin(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (!args.out.couldBe(arrayOut[i * 1 + j])) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input in: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            args.in, Float.floatToRawIntBits(args.in), args.in));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(args.out.toString());
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            arrayOut[i * 1 + j], Float.floatToRawIntBits(arrayOut[i * 1 + j]), arrayOut[i * 1 + j]));
+                    if (!args.out.couldBe(arrayOut[i * 1 + j])) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkNativeSinFloatFloat" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkNativeSinFloat2Float2() {
+        Allocation in = createRandomAllocation(mRS, Element.DataType.FLOAT_32, 2, 0x12b508b470b05442l, false);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 2), INPUTSIZE);
+            script.forEach_testNativeSinFloat2Float2(in, out);
+            verifyResultsNativeSinFloat2Float2(in, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeSinFloat2Float2: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 2), INPUTSIZE);
+            scriptRelaxed.forEach_testNativeSinFloat2Float2(in, out);
+            verifyResultsNativeSinFloat2Float2(in, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeSinFloat2Float2: " + e.toString());
+        }
+    }
+
+    private void verifyResultsNativeSinFloat2Float2(Allocation in, Allocation out, boolean relaxed) {
+        float[] arrayIn = new float[INPUTSIZE * 2];
+        in.copyTo(arrayIn);
+        float[] arrayOut = new float[INPUTSIZE * 2];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 2 ; j++) {
+                // Extract the inputs.
+                ArgumentsFloatFloat args = new ArgumentsFloatFloat();
+                args.in = arrayIn[i * 2 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeNativeSin(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (!args.out.couldBe(arrayOut[i * 2 + j])) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input in: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            args.in, Float.floatToRawIntBits(args.in), args.in));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(args.out.toString());
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            arrayOut[i * 2 + j], Float.floatToRawIntBits(arrayOut[i * 2 + j]), arrayOut[i * 2 + j]));
+                    if (!args.out.couldBe(arrayOut[i * 2 + j])) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkNativeSinFloat2Float2" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkNativeSinFloat3Float3() {
+        Allocation in = createRandomAllocation(mRS, Element.DataType.FLOAT_32, 3, 0x12b51355cfb6e9dcl, false);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 3), INPUTSIZE);
+            script.forEach_testNativeSinFloat3Float3(in, out);
+            verifyResultsNativeSinFloat3Float3(in, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeSinFloat3Float3: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 3), INPUTSIZE);
+            scriptRelaxed.forEach_testNativeSinFloat3Float3(in, out);
+            verifyResultsNativeSinFloat3Float3(in, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeSinFloat3Float3: " + e.toString());
+        }
+    }
+
+    private void verifyResultsNativeSinFloat3Float3(Allocation in, Allocation out, boolean relaxed) {
+        float[] arrayIn = new float[INPUTSIZE * 4];
+        in.copyTo(arrayIn);
+        float[] arrayOut = new float[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 3 ; j++) {
+                // Extract the inputs.
+                ArgumentsFloatFloat args = new ArgumentsFloatFloat();
+                args.in = arrayIn[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeNativeSin(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input in: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            args.in, Float.floatToRawIntBits(args.in), args.in));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(args.out.toString());
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            arrayOut[i * 4 + j], Float.floatToRawIntBits(arrayOut[i * 4 + j]), arrayOut[i * 4 + j]));
+                    if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkNativeSinFloat3Float3" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkNativeSinFloat4Float4() {
+        Allocation in = createRandomAllocation(mRS, Element.DataType.FLOAT_32, 4, 0x12b51df72ebd7f76l, false);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 4), INPUTSIZE);
+            script.forEach_testNativeSinFloat4Float4(in, out);
+            verifyResultsNativeSinFloat4Float4(in, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeSinFloat4Float4: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 4), INPUTSIZE);
+            scriptRelaxed.forEach_testNativeSinFloat4Float4(in, out);
+            verifyResultsNativeSinFloat4Float4(in, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeSinFloat4Float4: " + e.toString());
+        }
+    }
+
+    private void verifyResultsNativeSinFloat4Float4(Allocation in, Allocation out, boolean relaxed) {
+        float[] arrayIn = new float[INPUTSIZE * 4];
+        in.copyTo(arrayIn);
+        float[] arrayOut = new float[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 4 ; j++) {
+                // Extract the inputs.
+                ArgumentsFloatFloat args = new ArgumentsFloatFloat();
+                args.in = arrayIn[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeNativeSin(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input in: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            args.in, Float.floatToRawIntBits(args.in), args.in));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(args.out.toString());
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            arrayOut[i * 4 + j], Float.floatToRawIntBits(arrayOut[i * 4 + j]), arrayOut[i * 4 + j]));
+                    if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkNativeSinFloat4Float4" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    public void testNativeSin() {
+        checkNativeSinFloatFloat();
+        checkNativeSinFloat2Float2();
+        checkNativeSinFloat3Float3();
+        checkNativeSinFloat4Float4();
+    }
+}
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/TestNativeSincos.java b/tests/tests/renderscript/src/android/renderscript/cts/TestNativeSincos.java
new file mode 100644
index 0000000..7cb57cb
--- /dev/null
+++ b/tests/tests/renderscript/src/android/renderscript/cts/TestNativeSincos.java
@@ -0,0 +1,361 @@
+/*
+ * 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.
+ */
+
+// Don't edit this file!  It is auto-generated by frameworks/rs/api/gen_runtime.
+
+package android.renderscript.cts;
+
+import android.renderscript.Allocation;
+import android.renderscript.RSRuntimeException;
+import android.renderscript.Element;
+
+public class TestNativeSincos extends RSBaseCompute {
+
+    private ScriptC_TestNativeSincos script;
+    private ScriptC_TestNativeSincosRelaxed scriptRelaxed;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        script = new ScriptC_TestNativeSincos(mRS);
+        scriptRelaxed = new ScriptC_TestNativeSincosRelaxed(mRS);
+    }
+
+    public class ArgumentsFloatFloatFloat {
+        public float inV;
+        public Floaty outCosptr;
+        public Floaty out;
+    }
+
+    private void checkNativeSincosFloatFloatFloat() {
+        Allocation inV = createRandomAllocation(mRS, Element.DataType.FLOAT_32, 1, 0xe15df2366436cc13l, false);
+        try {
+            Allocation outCosptr = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 1), INPUTSIZE);
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 1), INPUTSIZE);
+            script.set_gAllocOutCosptr(outCosptr);
+            script.forEach_testNativeSincosFloatFloatFloat(inV, out);
+            verifyResultsNativeSincosFloatFloatFloat(inV, outCosptr, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeSincosFloatFloatFloat: " + e.toString());
+        }
+        try {
+            Allocation outCosptr = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 1), INPUTSIZE);
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 1), INPUTSIZE);
+            scriptRelaxed.set_gAllocOutCosptr(outCosptr);
+            scriptRelaxed.forEach_testNativeSincosFloatFloatFloat(inV, out);
+            verifyResultsNativeSincosFloatFloatFloat(inV, outCosptr, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeSincosFloatFloatFloat: " + e.toString());
+        }
+    }
+
+    private void verifyResultsNativeSincosFloatFloatFloat(Allocation inV, Allocation outCosptr, Allocation out, boolean relaxed) {
+        float[] arrayInV = new float[INPUTSIZE * 1];
+        inV.copyTo(arrayInV);
+        float[] arrayOutCosptr = new float[INPUTSIZE * 1];
+        outCosptr.copyTo(arrayOutCosptr);
+        float[] arrayOut = new float[INPUTSIZE * 1];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 1 ; j++) {
+                // Extract the inputs.
+                ArgumentsFloatFloatFloat args = new ArgumentsFloatFloatFloat();
+                args.inV = arrayInV[i];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeNativeSincos(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (!args.outCosptr.couldBe(arrayOutCosptr[i * 1 + j])) {
+                    valid = false;
+                }
+                if (!args.out.couldBe(arrayOut[i * 1 + j])) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            args.inV, Float.floatToRawIntBits(args.inV), args.inV));
+                    message.append("\n");
+                    message.append("Expected output outCosptr: ");
+                    message.append(args.outCosptr.toString());
+                    message.append("\n");
+                    message.append("Actual   output outCosptr: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            arrayOutCosptr[i * 1 + j], Float.floatToRawIntBits(arrayOutCosptr[i * 1 + j]), arrayOutCosptr[i * 1 + j]));
+                    if (!args.outCosptr.couldBe(arrayOutCosptr[i * 1 + j])) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(args.out.toString());
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            arrayOut[i * 1 + j], Float.floatToRawIntBits(arrayOut[i * 1 + j]), arrayOut[i * 1 + j]));
+                    if (!args.out.couldBe(arrayOut[i * 1 + j])) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkNativeSincosFloatFloatFloat" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkNativeSincosFloat2Float2Float2() {
+        Allocation inV = createRandomAllocation(mRS, Element.DataType.FLOAT_32, 2, 0xe5a1f1dcda676ea9l, false);
+        try {
+            Allocation outCosptr = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 2), INPUTSIZE);
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 2), INPUTSIZE);
+            script.set_gAllocOutCosptr(outCosptr);
+            script.forEach_testNativeSincosFloat2Float2Float2(inV, out);
+            verifyResultsNativeSincosFloat2Float2Float2(inV, outCosptr, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeSincosFloat2Float2Float2: " + e.toString());
+        }
+        try {
+            Allocation outCosptr = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 2), INPUTSIZE);
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 2), INPUTSIZE);
+            scriptRelaxed.set_gAllocOutCosptr(outCosptr);
+            scriptRelaxed.forEach_testNativeSincosFloat2Float2Float2(inV, out);
+            verifyResultsNativeSincosFloat2Float2Float2(inV, outCosptr, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeSincosFloat2Float2Float2: " + e.toString());
+        }
+    }
+
+    private void verifyResultsNativeSincosFloat2Float2Float2(Allocation inV, Allocation outCosptr, Allocation out, boolean relaxed) {
+        float[] arrayInV = new float[INPUTSIZE * 2];
+        inV.copyTo(arrayInV);
+        float[] arrayOutCosptr = new float[INPUTSIZE * 2];
+        outCosptr.copyTo(arrayOutCosptr);
+        float[] arrayOut = new float[INPUTSIZE * 2];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 2 ; j++) {
+                // Extract the inputs.
+                ArgumentsFloatFloatFloat args = new ArgumentsFloatFloatFloat();
+                args.inV = arrayInV[i * 2 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeNativeSincos(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (!args.outCosptr.couldBe(arrayOutCosptr[i * 2 + j])) {
+                    valid = false;
+                }
+                if (!args.out.couldBe(arrayOut[i * 2 + j])) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            args.inV, Float.floatToRawIntBits(args.inV), args.inV));
+                    message.append("\n");
+                    message.append("Expected output outCosptr: ");
+                    message.append(args.outCosptr.toString());
+                    message.append("\n");
+                    message.append("Actual   output outCosptr: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            arrayOutCosptr[i * 2 + j], Float.floatToRawIntBits(arrayOutCosptr[i * 2 + j]), arrayOutCosptr[i * 2 + j]));
+                    if (!args.outCosptr.couldBe(arrayOutCosptr[i * 2 + j])) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(args.out.toString());
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            arrayOut[i * 2 + j], Float.floatToRawIntBits(arrayOut[i * 2 + j]), arrayOut[i * 2 + j]));
+                    if (!args.out.couldBe(arrayOut[i * 2 + j])) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkNativeSincosFloat2Float2Float2" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkNativeSincosFloat3Float3Float3() {
+        Allocation inV = createRandomAllocation(mRS, Element.DataType.FLOAT_32, 3, 0x3a06cffcdc45704al, false);
+        try {
+            Allocation outCosptr = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 3), INPUTSIZE);
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 3), INPUTSIZE);
+            script.set_gAllocOutCosptr(outCosptr);
+            script.forEach_testNativeSincosFloat3Float3Float3(inV, out);
+            verifyResultsNativeSincosFloat3Float3Float3(inV, outCosptr, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeSincosFloat3Float3Float3: " + e.toString());
+        }
+        try {
+            Allocation outCosptr = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 3), INPUTSIZE);
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 3), INPUTSIZE);
+            scriptRelaxed.set_gAllocOutCosptr(outCosptr);
+            scriptRelaxed.forEach_testNativeSincosFloat3Float3Float3(inV, out);
+            verifyResultsNativeSincosFloat3Float3Float3(inV, outCosptr, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeSincosFloat3Float3Float3: " + e.toString());
+        }
+    }
+
+    private void verifyResultsNativeSincosFloat3Float3Float3(Allocation inV, Allocation outCosptr, Allocation out, boolean relaxed) {
+        float[] arrayInV = new float[INPUTSIZE * 4];
+        inV.copyTo(arrayInV);
+        float[] arrayOutCosptr = new float[INPUTSIZE * 4];
+        outCosptr.copyTo(arrayOutCosptr);
+        float[] arrayOut = new float[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 3 ; j++) {
+                // Extract the inputs.
+                ArgumentsFloatFloatFloat args = new ArgumentsFloatFloatFloat();
+                args.inV = arrayInV[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeNativeSincos(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (!args.outCosptr.couldBe(arrayOutCosptr[i * 4 + j])) {
+                    valid = false;
+                }
+                if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            args.inV, Float.floatToRawIntBits(args.inV), args.inV));
+                    message.append("\n");
+                    message.append("Expected output outCosptr: ");
+                    message.append(args.outCosptr.toString());
+                    message.append("\n");
+                    message.append("Actual   output outCosptr: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            arrayOutCosptr[i * 4 + j], Float.floatToRawIntBits(arrayOutCosptr[i * 4 + j]), arrayOutCosptr[i * 4 + j]));
+                    if (!args.outCosptr.couldBe(arrayOutCosptr[i * 4 + j])) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(args.out.toString());
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            arrayOut[i * 4 + j], Float.floatToRawIntBits(arrayOut[i * 4 + j]), arrayOut[i * 4 + j]));
+                    if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkNativeSincosFloat3Float3Float3" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkNativeSincosFloat4Float4Float4() {
+        Allocation inV = createRandomAllocation(mRS, Element.DataType.FLOAT_32, 4, 0x8e6bae1cde2371ebl, false);
+        try {
+            Allocation outCosptr = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 4), INPUTSIZE);
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 4), INPUTSIZE);
+            script.set_gAllocOutCosptr(outCosptr);
+            script.forEach_testNativeSincosFloat4Float4Float4(inV, out);
+            verifyResultsNativeSincosFloat4Float4Float4(inV, outCosptr, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeSincosFloat4Float4Float4: " + e.toString());
+        }
+        try {
+            Allocation outCosptr = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 4), INPUTSIZE);
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 4), INPUTSIZE);
+            scriptRelaxed.set_gAllocOutCosptr(outCosptr);
+            scriptRelaxed.forEach_testNativeSincosFloat4Float4Float4(inV, out);
+            verifyResultsNativeSincosFloat4Float4Float4(inV, outCosptr, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeSincosFloat4Float4Float4: " + e.toString());
+        }
+    }
+
+    private void verifyResultsNativeSincosFloat4Float4Float4(Allocation inV, Allocation outCosptr, Allocation out, boolean relaxed) {
+        float[] arrayInV = new float[INPUTSIZE * 4];
+        inV.copyTo(arrayInV);
+        float[] arrayOutCosptr = new float[INPUTSIZE * 4];
+        outCosptr.copyTo(arrayOutCosptr);
+        float[] arrayOut = new float[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 4 ; j++) {
+                // Extract the inputs.
+                ArgumentsFloatFloatFloat args = new ArgumentsFloatFloatFloat();
+                args.inV = arrayInV[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeNativeSincos(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (!args.outCosptr.couldBe(arrayOutCosptr[i * 4 + j])) {
+                    valid = false;
+                }
+                if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inV: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            args.inV, Float.floatToRawIntBits(args.inV), args.inV));
+                    message.append("\n");
+                    message.append("Expected output outCosptr: ");
+                    message.append(args.outCosptr.toString());
+                    message.append("\n");
+                    message.append("Actual   output outCosptr: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            arrayOutCosptr[i * 4 + j], Float.floatToRawIntBits(arrayOutCosptr[i * 4 + j]), arrayOutCosptr[i * 4 + j]));
+                    if (!args.outCosptr.couldBe(arrayOutCosptr[i * 4 + j])) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(args.out.toString());
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            arrayOut[i * 4 + j], Float.floatToRawIntBits(arrayOut[i * 4 + j]), arrayOut[i * 4 + j]));
+                    if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkNativeSincosFloat4Float4Float4" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    public void testNativeSincos() {
+        checkNativeSincosFloatFloatFloat();
+        checkNativeSincosFloat2Float2Float2();
+        checkNativeSincosFloat3Float3Float3();
+        checkNativeSincosFloat4Float4Float4();
+    }
+}
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/TestNativeSinh.java b/tests/tests/renderscript/src/android/renderscript/cts/TestNativeSinh.java
new file mode 100644
index 0000000..e94b6a0
--- /dev/null
+++ b/tests/tests/renderscript/src/android/renderscript/cts/TestNativeSinh.java
@@ -0,0 +1,284 @@
+/*
+ * 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.
+ */
+
+// Don't edit this file!  It is auto-generated by frameworks/rs/api/gen_runtime.
+
+package android.renderscript.cts;
+
+import android.renderscript.Allocation;
+import android.renderscript.RSRuntimeException;
+import android.renderscript.Element;
+
+public class TestNativeSinh extends RSBaseCompute {
+
+    private ScriptC_TestNativeSinh script;
+    private ScriptC_TestNativeSinhRelaxed scriptRelaxed;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        script = new ScriptC_TestNativeSinh(mRS);
+        scriptRelaxed = new ScriptC_TestNativeSinhRelaxed(mRS);
+    }
+
+    public class ArgumentsFloatFloat {
+        public float in;
+        public Floaty out;
+    }
+
+    private void checkNativeSinhFloatFloat() {
+        Allocation in = createRandomAllocation(mRS, Element.DataType.FLOAT_32, 1, 0x74dcf3e9c65b6590l, false);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 1), INPUTSIZE);
+            script.forEach_testNativeSinhFloatFloat(in, out);
+            verifyResultsNativeSinhFloatFloat(in, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeSinhFloatFloat: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 1), INPUTSIZE);
+            scriptRelaxed.forEach_testNativeSinhFloatFloat(in, out);
+            verifyResultsNativeSinhFloatFloat(in, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeSinhFloatFloat: " + e.toString());
+        }
+    }
+
+    private void verifyResultsNativeSinhFloatFloat(Allocation in, Allocation out, boolean relaxed) {
+        float[] arrayIn = new float[INPUTSIZE * 1];
+        in.copyTo(arrayIn);
+        float[] arrayOut = new float[INPUTSIZE * 1];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 1 ; j++) {
+                // Extract the inputs.
+                ArgumentsFloatFloat args = new ArgumentsFloatFloat();
+                args.in = arrayIn[i];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeNativeSinh(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (!args.out.couldBe(arrayOut[i * 1 + j])) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input in: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            args.in, Float.floatToRawIntBits(args.in), args.in));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(args.out.toString());
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            arrayOut[i * 1 + j], Float.floatToRawIntBits(arrayOut[i * 1 + j]), arrayOut[i * 1 + j]));
+                    if (!args.out.couldBe(arrayOut[i * 1 + j])) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkNativeSinhFloatFloat" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkNativeSinhFloat2Float2() {
+        Allocation in = createRandomAllocation(mRS, Element.DataType.FLOAT_32, 2, 0xfdcd5755b59082cl, false);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 2), INPUTSIZE);
+            script.forEach_testNativeSinhFloat2Float2(in, out);
+            verifyResultsNativeSinhFloat2Float2(in, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeSinhFloat2Float2: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 2), INPUTSIZE);
+            scriptRelaxed.forEach_testNativeSinhFloat2Float2(in, out);
+            verifyResultsNativeSinhFloat2Float2(in, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeSinhFloat2Float2: " + e.toString());
+        }
+    }
+
+    private void verifyResultsNativeSinhFloat2Float2(Allocation in, Allocation out, boolean relaxed) {
+        float[] arrayIn = new float[INPUTSIZE * 2];
+        in.copyTo(arrayIn);
+        float[] arrayOut = new float[INPUTSIZE * 2];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 2 ; j++) {
+                // Extract the inputs.
+                ArgumentsFloatFloat args = new ArgumentsFloatFloat();
+                args.in = arrayIn[i * 2 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeNativeSinh(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (!args.out.couldBe(arrayOut[i * 2 + j])) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input in: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            args.in, Float.floatToRawIntBits(args.in), args.in));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(args.out.toString());
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            arrayOut[i * 2 + j], Float.floatToRawIntBits(arrayOut[i * 2 + j]), arrayOut[i * 2 + j]));
+                    if (!args.out.couldBe(arrayOut[i * 2 + j])) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkNativeSinhFloat2Float2" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkNativeSinhFloat3Float3() {
+        Allocation in = createRandomAllocation(mRS, Element.DataType.FLOAT_32, 3, 0xfdce016ba5f9dc6l, false);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 3), INPUTSIZE);
+            script.forEach_testNativeSinhFloat3Float3(in, out);
+            verifyResultsNativeSinhFloat3Float3(in, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeSinhFloat3Float3: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 3), INPUTSIZE);
+            scriptRelaxed.forEach_testNativeSinhFloat3Float3(in, out);
+            verifyResultsNativeSinhFloat3Float3(in, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeSinhFloat3Float3: " + e.toString());
+        }
+    }
+
+    private void verifyResultsNativeSinhFloat3Float3(Allocation in, Allocation out, boolean relaxed) {
+        float[] arrayIn = new float[INPUTSIZE * 4];
+        in.copyTo(arrayIn);
+        float[] arrayOut = new float[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 3 ; j++) {
+                // Extract the inputs.
+                ArgumentsFloatFloat args = new ArgumentsFloatFloat();
+                args.in = arrayIn[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeNativeSinh(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input in: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            args.in, Float.floatToRawIntBits(args.in), args.in));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(args.out.toString());
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            arrayOut[i * 4 + j], Float.floatToRawIntBits(arrayOut[i * 4 + j]), arrayOut[i * 4 + j]));
+                    if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkNativeSinhFloat3Float3" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkNativeSinhFloat4Float4() {
+        Allocation in = createRandomAllocation(mRS, Element.DataType.FLOAT_32, 4, 0xfdceab819663360l, false);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 4), INPUTSIZE);
+            script.forEach_testNativeSinhFloat4Float4(in, out);
+            verifyResultsNativeSinhFloat4Float4(in, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeSinhFloat4Float4: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 4), INPUTSIZE);
+            scriptRelaxed.forEach_testNativeSinhFloat4Float4(in, out);
+            verifyResultsNativeSinhFloat4Float4(in, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeSinhFloat4Float4: " + e.toString());
+        }
+    }
+
+    private void verifyResultsNativeSinhFloat4Float4(Allocation in, Allocation out, boolean relaxed) {
+        float[] arrayIn = new float[INPUTSIZE * 4];
+        in.copyTo(arrayIn);
+        float[] arrayOut = new float[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 4 ; j++) {
+                // Extract the inputs.
+                ArgumentsFloatFloat args = new ArgumentsFloatFloat();
+                args.in = arrayIn[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeNativeSinh(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input in: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            args.in, Float.floatToRawIntBits(args.in), args.in));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(args.out.toString());
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            arrayOut[i * 4 + j], Float.floatToRawIntBits(arrayOut[i * 4 + j]), arrayOut[i * 4 + j]));
+                    if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkNativeSinhFloat4Float4" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    public void testNativeSinh() {
+        checkNativeSinhFloatFloat();
+        checkNativeSinhFloat2Float2();
+        checkNativeSinhFloat3Float3();
+        checkNativeSinhFloat4Float4();
+    }
+}
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/TestNativeSinpi.java b/tests/tests/renderscript/src/android/renderscript/cts/TestNativeSinpi.java
new file mode 100644
index 0000000..61e550f
--- /dev/null
+++ b/tests/tests/renderscript/src/android/renderscript/cts/TestNativeSinpi.java
@@ -0,0 +1,284 @@
+/*
+ * 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.
+ */
+
+// Don't edit this file!  It is auto-generated by frameworks/rs/api/gen_runtime.
+
+package android.renderscript.cts;
+
+import android.renderscript.Allocation;
+import android.renderscript.RSRuntimeException;
+import android.renderscript.Element;
+
+public class TestNativeSinpi extends RSBaseCompute {
+
+    private ScriptC_TestNativeSinpi script;
+    private ScriptC_TestNativeSinpiRelaxed scriptRelaxed;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        script = new ScriptC_TestNativeSinpi(mRS);
+        scriptRelaxed = new ScriptC_TestNativeSinpiRelaxed(mRS);
+    }
+
+    public class ArgumentsFloatFloat {
+        public float in;
+        public Floaty out;
+    }
+
+    private void checkNativeSinpiFloatFloat() {
+        Allocation in = createRandomAllocation(mRS, Element.DataType.FLOAT_32, 1, 0xb52c701227c9dc37l, false);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 1), INPUTSIZE);
+            script.forEach_testNativeSinpiFloatFloat(in, out);
+            verifyResultsNativeSinpiFloatFloat(in, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeSinpiFloatFloat: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 1), INPUTSIZE);
+            scriptRelaxed.forEach_testNativeSinpiFloatFloat(in, out);
+            verifyResultsNativeSinpiFloatFloat(in, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeSinpiFloatFloat: " + e.toString());
+        }
+    }
+
+    private void verifyResultsNativeSinpiFloatFloat(Allocation in, Allocation out, boolean relaxed) {
+        float[] arrayIn = new float[INPUTSIZE * 1];
+        in.copyTo(arrayIn);
+        float[] arrayOut = new float[INPUTSIZE * 1];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 1 ; j++) {
+                // Extract the inputs.
+                ArgumentsFloatFloat args = new ArgumentsFloatFloat();
+                args.in = arrayIn[i];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeNativeSinpi(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (!args.out.couldBe(arrayOut[i * 1 + j])) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input in: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            args.in, Float.floatToRawIntBits(args.in), args.in));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(args.out.toString());
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            arrayOut[i * 1 + j], Float.floatToRawIntBits(arrayOut[i * 1 + j]), arrayOut[i * 1 + j]));
+                    if (!args.out.couldBe(arrayOut[i * 1 + j])) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkNativeSinpiFloatFloat" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkNativeSinpiFloat2Float2() {
+        Allocation in = createRandomAllocation(mRS, Element.DataType.FLOAT_32, 2, 0x8df4951d1230045bl, false);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 2), INPUTSIZE);
+            script.forEach_testNativeSinpiFloat2Float2(in, out);
+            verifyResultsNativeSinpiFloat2Float2(in, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeSinpiFloat2Float2: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 2), INPUTSIZE);
+            scriptRelaxed.forEach_testNativeSinpiFloat2Float2(in, out);
+            verifyResultsNativeSinpiFloat2Float2(in, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeSinpiFloat2Float2: " + e.toString());
+        }
+    }
+
+    private void verifyResultsNativeSinpiFloat2Float2(Allocation in, Allocation out, boolean relaxed) {
+        float[] arrayIn = new float[INPUTSIZE * 2];
+        in.copyTo(arrayIn);
+        float[] arrayOut = new float[INPUTSIZE * 2];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 2 ; j++) {
+                // Extract the inputs.
+                ArgumentsFloatFloat args = new ArgumentsFloatFloat();
+                args.in = arrayIn[i * 2 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeNativeSinpi(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (!args.out.couldBe(arrayOut[i * 2 + j])) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input in: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            args.in, Float.floatToRawIntBits(args.in), args.in));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(args.out.toString());
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            arrayOut[i * 2 + j], Float.floatToRawIntBits(arrayOut[i * 2 + j]), arrayOut[i * 2 + j]));
+                    if (!args.out.couldBe(arrayOut[i * 2 + j])) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkNativeSinpiFloat2Float2" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkNativeSinpiFloat3Float3() {
+        Allocation in = createRandomAllocation(mRS, Element.DataType.FLOAT_32, 3, 0x8df49fbe713699f5l, false);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 3), INPUTSIZE);
+            script.forEach_testNativeSinpiFloat3Float3(in, out);
+            verifyResultsNativeSinpiFloat3Float3(in, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeSinpiFloat3Float3: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 3), INPUTSIZE);
+            scriptRelaxed.forEach_testNativeSinpiFloat3Float3(in, out);
+            verifyResultsNativeSinpiFloat3Float3(in, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeSinpiFloat3Float3: " + e.toString());
+        }
+    }
+
+    private void verifyResultsNativeSinpiFloat3Float3(Allocation in, Allocation out, boolean relaxed) {
+        float[] arrayIn = new float[INPUTSIZE * 4];
+        in.copyTo(arrayIn);
+        float[] arrayOut = new float[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 3 ; j++) {
+                // Extract the inputs.
+                ArgumentsFloatFloat args = new ArgumentsFloatFloat();
+                args.in = arrayIn[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeNativeSinpi(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input in: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            args.in, Float.floatToRawIntBits(args.in), args.in));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(args.out.toString());
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            arrayOut[i * 4 + j], Float.floatToRawIntBits(arrayOut[i * 4 + j]), arrayOut[i * 4 + j]));
+                    if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkNativeSinpiFloat3Float3" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkNativeSinpiFloat4Float4() {
+        Allocation in = createRandomAllocation(mRS, Element.DataType.FLOAT_32, 4, 0x8df4aa5fd03d2f8fl, false);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 4), INPUTSIZE);
+            script.forEach_testNativeSinpiFloat4Float4(in, out);
+            verifyResultsNativeSinpiFloat4Float4(in, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeSinpiFloat4Float4: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 4), INPUTSIZE);
+            scriptRelaxed.forEach_testNativeSinpiFloat4Float4(in, out);
+            verifyResultsNativeSinpiFloat4Float4(in, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeSinpiFloat4Float4: " + e.toString());
+        }
+    }
+
+    private void verifyResultsNativeSinpiFloat4Float4(Allocation in, Allocation out, boolean relaxed) {
+        float[] arrayIn = new float[INPUTSIZE * 4];
+        in.copyTo(arrayIn);
+        float[] arrayOut = new float[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 4 ; j++) {
+                // Extract the inputs.
+                ArgumentsFloatFloat args = new ArgumentsFloatFloat();
+                args.in = arrayIn[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeNativeSinpi(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input in: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            args.in, Float.floatToRawIntBits(args.in), args.in));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(args.out.toString());
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            arrayOut[i * 4 + j], Float.floatToRawIntBits(arrayOut[i * 4 + j]), arrayOut[i * 4 + j]));
+                    if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkNativeSinpiFloat4Float4" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    public void testNativeSinpi() {
+        checkNativeSinpiFloatFloat();
+        checkNativeSinpiFloat2Float2();
+        checkNativeSinpiFloat3Float3();
+        checkNativeSinpiFloat4Float4();
+    }
+}
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/TestNativeSqrt.java b/tests/tests/renderscript/src/android/renderscript/cts/TestNativeSqrt.java
new file mode 100644
index 0000000..a16ea4e
--- /dev/null
+++ b/tests/tests/renderscript/src/android/renderscript/cts/TestNativeSqrt.java
@@ -0,0 +1,284 @@
+/*
+ * 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.
+ */
+
+// Don't edit this file!  It is auto-generated by frameworks/rs/api/gen_runtime.
+
+package android.renderscript.cts;
+
+import android.renderscript.Allocation;
+import android.renderscript.RSRuntimeException;
+import android.renderscript.Element;
+
+public class TestNativeSqrt extends RSBaseCompute {
+
+    private ScriptC_TestNativeSqrt script;
+    private ScriptC_TestNativeSqrtRelaxed scriptRelaxed;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        script = new ScriptC_TestNativeSqrt(mRS);
+        scriptRelaxed = new ScriptC_TestNativeSqrtRelaxed(mRS);
+    }
+
+    public class ArgumentsFloatFloat {
+        public float in;
+        public Floaty out;
+    }
+
+    private void checkNativeSqrtFloatFloat() {
+        Allocation in = createRandomAllocation(mRS, Element.DataType.FLOAT_32, 1, 0xb4c9e7b9972ac810l, false);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 1), INPUTSIZE);
+            script.forEach_testNativeSqrtFloatFloat(in, out);
+            verifyResultsNativeSqrtFloatFloat(in, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeSqrtFloatFloat: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 1), INPUTSIZE);
+            scriptRelaxed.forEach_testNativeSqrtFloatFloat(in, out);
+            verifyResultsNativeSqrtFloatFloat(in, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeSqrtFloatFloat: " + e.toString());
+        }
+    }
+
+    private void verifyResultsNativeSqrtFloatFloat(Allocation in, Allocation out, boolean relaxed) {
+        float[] arrayIn = new float[INPUTSIZE * 1];
+        in.copyTo(arrayIn);
+        float[] arrayOut = new float[INPUTSIZE * 1];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 1 ; j++) {
+                // Extract the inputs.
+                ArgumentsFloatFloat args = new ArgumentsFloatFloat();
+                args.in = arrayIn[i];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeNativeSqrt(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (!args.out.couldBe(arrayOut[i * 1 + j])) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input in: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            args.in, Float.floatToRawIntBits(args.in), args.in));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(args.out.toString());
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            arrayOut[i * 1 + j], Float.floatToRawIntBits(arrayOut[i * 1 + j]), arrayOut[i * 1 + j]));
+                    if (!args.out.couldBe(arrayOut[i * 1 + j])) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkNativeSqrtFloatFloat" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkNativeSqrtFloat2Float2() {
+        Allocation in = createRandomAllocation(mRS, Element.DataType.FLOAT_32, 2, 0xc649cd70853776acl, false);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 2), INPUTSIZE);
+            script.forEach_testNativeSqrtFloat2Float2(in, out);
+            verifyResultsNativeSqrtFloat2Float2(in, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeSqrtFloat2Float2: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 2), INPUTSIZE);
+            scriptRelaxed.forEach_testNativeSqrtFloat2Float2(in, out);
+            verifyResultsNativeSqrtFloat2Float2(in, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeSqrtFloat2Float2: " + e.toString());
+        }
+    }
+
+    private void verifyResultsNativeSqrtFloat2Float2(Allocation in, Allocation out, boolean relaxed) {
+        float[] arrayIn = new float[INPUTSIZE * 2];
+        in.copyTo(arrayIn);
+        float[] arrayOut = new float[INPUTSIZE * 2];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 2 ; j++) {
+                // Extract the inputs.
+                ArgumentsFloatFloat args = new ArgumentsFloatFloat();
+                args.in = arrayIn[i * 2 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeNativeSqrt(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (!args.out.couldBe(arrayOut[i * 2 + j])) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input in: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            args.in, Float.floatToRawIntBits(args.in), args.in));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(args.out.toString());
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            arrayOut[i * 2 + j], Float.floatToRawIntBits(arrayOut[i * 2 + j]), arrayOut[i * 2 + j]));
+                    if (!args.out.couldBe(arrayOut[i * 2 + j])) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkNativeSqrtFloat2Float2" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkNativeSqrtFloat3Float3() {
+        Allocation in = createRandomAllocation(mRS, Element.DataType.FLOAT_32, 3, 0xc649d811e43e0c46l, false);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 3), INPUTSIZE);
+            script.forEach_testNativeSqrtFloat3Float3(in, out);
+            verifyResultsNativeSqrtFloat3Float3(in, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeSqrtFloat3Float3: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 3), INPUTSIZE);
+            scriptRelaxed.forEach_testNativeSqrtFloat3Float3(in, out);
+            verifyResultsNativeSqrtFloat3Float3(in, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeSqrtFloat3Float3: " + e.toString());
+        }
+    }
+
+    private void verifyResultsNativeSqrtFloat3Float3(Allocation in, Allocation out, boolean relaxed) {
+        float[] arrayIn = new float[INPUTSIZE * 4];
+        in.copyTo(arrayIn);
+        float[] arrayOut = new float[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 3 ; j++) {
+                // Extract the inputs.
+                ArgumentsFloatFloat args = new ArgumentsFloatFloat();
+                args.in = arrayIn[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeNativeSqrt(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input in: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            args.in, Float.floatToRawIntBits(args.in), args.in));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(args.out.toString());
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            arrayOut[i * 4 + j], Float.floatToRawIntBits(arrayOut[i * 4 + j]), arrayOut[i * 4 + j]));
+                    if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkNativeSqrtFloat3Float3" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkNativeSqrtFloat4Float4() {
+        Allocation in = createRandomAllocation(mRS, Element.DataType.FLOAT_32, 4, 0xc649e2b34344a1e0l, false);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 4), INPUTSIZE);
+            script.forEach_testNativeSqrtFloat4Float4(in, out);
+            verifyResultsNativeSqrtFloat4Float4(in, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeSqrtFloat4Float4: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 4), INPUTSIZE);
+            scriptRelaxed.forEach_testNativeSqrtFloat4Float4(in, out);
+            verifyResultsNativeSqrtFloat4Float4(in, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeSqrtFloat4Float4: " + e.toString());
+        }
+    }
+
+    private void verifyResultsNativeSqrtFloat4Float4(Allocation in, Allocation out, boolean relaxed) {
+        float[] arrayIn = new float[INPUTSIZE * 4];
+        in.copyTo(arrayIn);
+        float[] arrayOut = new float[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 4 ; j++) {
+                // Extract the inputs.
+                ArgumentsFloatFloat args = new ArgumentsFloatFloat();
+                args.in = arrayIn[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeNativeSqrt(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input in: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            args.in, Float.floatToRawIntBits(args.in), args.in));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(args.out.toString());
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            arrayOut[i * 4 + j], Float.floatToRawIntBits(arrayOut[i * 4 + j]), arrayOut[i * 4 + j]));
+                    if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkNativeSqrtFloat4Float4" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    public void testNativeSqrt() {
+        checkNativeSqrtFloatFloat();
+        checkNativeSqrtFloat2Float2();
+        checkNativeSqrtFloat3Float3();
+        checkNativeSqrtFloat4Float4();
+    }
+}
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/TestNativeTan.java b/tests/tests/renderscript/src/android/renderscript/cts/TestNativeTan.java
new file mode 100644
index 0000000..084bfeb
--- /dev/null
+++ b/tests/tests/renderscript/src/android/renderscript/cts/TestNativeTan.java
@@ -0,0 +1,284 @@
+/*
+ * 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.
+ */
+
+// Don't edit this file!  It is auto-generated by frameworks/rs/api/gen_runtime.
+
+package android.renderscript.cts;
+
+import android.renderscript.Allocation;
+import android.renderscript.RSRuntimeException;
+import android.renderscript.Element;
+
+public class TestNativeTan extends RSBaseCompute {
+
+    private ScriptC_TestNativeTan script;
+    private ScriptC_TestNativeTanRelaxed scriptRelaxed;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        script = new ScriptC_TestNativeTan(mRS);
+        scriptRelaxed = new ScriptC_TestNativeTanRelaxed(mRS);
+    }
+
+    public class ArgumentsFloatFloat {
+        public float in;
+        public Floaty out;
+    }
+
+    private void checkNativeTanFloatFloat() {
+        Allocation in = createRandomAllocation(mRS, Element.DataType.FLOAT_32, 1, 0x5b9a224e25042b47l, false);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 1), INPUTSIZE);
+            script.forEach_testNativeTanFloatFloat(in, out);
+            verifyResultsNativeTanFloatFloat(in, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeTanFloatFloat: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 1), INPUTSIZE);
+            scriptRelaxed.forEach_testNativeTanFloatFloat(in, out);
+            verifyResultsNativeTanFloatFloat(in, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeTanFloatFloat: " + e.toString());
+        }
+    }
+
+    private void verifyResultsNativeTanFloatFloat(Allocation in, Allocation out, boolean relaxed) {
+        float[] arrayIn = new float[INPUTSIZE * 1];
+        in.copyTo(arrayIn);
+        float[] arrayOut = new float[INPUTSIZE * 1];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 1 ; j++) {
+                // Extract the inputs.
+                ArgumentsFloatFloat args = new ArgumentsFloatFloat();
+                args.in = arrayIn[i];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeNativeTan(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (!args.out.couldBe(arrayOut[i * 1 + j])) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input in: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            args.in, Float.floatToRawIntBits(args.in), args.in));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(args.out.toString());
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            arrayOut[i * 1 + j], Float.floatToRawIntBits(arrayOut[i * 1 + j]), arrayOut[i * 1 + j]));
+                    if (!args.out.couldBe(arrayOut[i * 1 + j])) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkNativeTanFloatFloat" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkNativeTanFloat2Float2() {
+        Allocation in = createRandomAllocation(mRS, Element.DataType.FLOAT_32, 2, 0x9c40e8650c550eebl, false);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 2), INPUTSIZE);
+            script.forEach_testNativeTanFloat2Float2(in, out);
+            verifyResultsNativeTanFloat2Float2(in, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeTanFloat2Float2: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 2), INPUTSIZE);
+            scriptRelaxed.forEach_testNativeTanFloat2Float2(in, out);
+            verifyResultsNativeTanFloat2Float2(in, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeTanFloat2Float2: " + e.toString());
+        }
+    }
+
+    private void verifyResultsNativeTanFloat2Float2(Allocation in, Allocation out, boolean relaxed) {
+        float[] arrayIn = new float[INPUTSIZE * 2];
+        in.copyTo(arrayIn);
+        float[] arrayOut = new float[INPUTSIZE * 2];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 2 ; j++) {
+                // Extract the inputs.
+                ArgumentsFloatFloat args = new ArgumentsFloatFloat();
+                args.in = arrayIn[i * 2 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeNativeTan(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (!args.out.couldBe(arrayOut[i * 2 + j])) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input in: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            args.in, Float.floatToRawIntBits(args.in), args.in));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(args.out.toString());
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            arrayOut[i * 2 + j], Float.floatToRawIntBits(arrayOut[i * 2 + j]), arrayOut[i * 2 + j]));
+                    if (!args.out.couldBe(arrayOut[i * 2 + j])) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkNativeTanFloat2Float2" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkNativeTanFloat3Float3() {
+        Allocation in = createRandomAllocation(mRS, Element.DataType.FLOAT_32, 3, 0x9c40f3066b5ba485l, false);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 3), INPUTSIZE);
+            script.forEach_testNativeTanFloat3Float3(in, out);
+            verifyResultsNativeTanFloat3Float3(in, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeTanFloat3Float3: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 3), INPUTSIZE);
+            scriptRelaxed.forEach_testNativeTanFloat3Float3(in, out);
+            verifyResultsNativeTanFloat3Float3(in, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeTanFloat3Float3: " + e.toString());
+        }
+    }
+
+    private void verifyResultsNativeTanFloat3Float3(Allocation in, Allocation out, boolean relaxed) {
+        float[] arrayIn = new float[INPUTSIZE * 4];
+        in.copyTo(arrayIn);
+        float[] arrayOut = new float[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 3 ; j++) {
+                // Extract the inputs.
+                ArgumentsFloatFloat args = new ArgumentsFloatFloat();
+                args.in = arrayIn[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeNativeTan(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input in: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            args.in, Float.floatToRawIntBits(args.in), args.in));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(args.out.toString());
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            arrayOut[i * 4 + j], Float.floatToRawIntBits(arrayOut[i * 4 + j]), arrayOut[i * 4 + j]));
+                    if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkNativeTanFloat3Float3" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkNativeTanFloat4Float4() {
+        Allocation in = createRandomAllocation(mRS, Element.DataType.FLOAT_32, 4, 0x9c40fda7ca623a1fl, false);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 4), INPUTSIZE);
+            script.forEach_testNativeTanFloat4Float4(in, out);
+            verifyResultsNativeTanFloat4Float4(in, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeTanFloat4Float4: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 4), INPUTSIZE);
+            scriptRelaxed.forEach_testNativeTanFloat4Float4(in, out);
+            verifyResultsNativeTanFloat4Float4(in, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeTanFloat4Float4: " + e.toString());
+        }
+    }
+
+    private void verifyResultsNativeTanFloat4Float4(Allocation in, Allocation out, boolean relaxed) {
+        float[] arrayIn = new float[INPUTSIZE * 4];
+        in.copyTo(arrayIn);
+        float[] arrayOut = new float[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 4 ; j++) {
+                // Extract the inputs.
+                ArgumentsFloatFloat args = new ArgumentsFloatFloat();
+                args.in = arrayIn[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeNativeTan(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input in: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            args.in, Float.floatToRawIntBits(args.in), args.in));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(args.out.toString());
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            arrayOut[i * 4 + j], Float.floatToRawIntBits(arrayOut[i * 4 + j]), arrayOut[i * 4 + j]));
+                    if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkNativeTanFloat4Float4" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    public void testNativeTan() {
+        checkNativeTanFloatFloat();
+        checkNativeTanFloat2Float2();
+        checkNativeTanFloat3Float3();
+        checkNativeTanFloat4Float4();
+    }
+}
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/TestNativeTanh.java b/tests/tests/renderscript/src/android/renderscript/cts/TestNativeTanh.java
new file mode 100644
index 0000000..4b335d3
--- /dev/null
+++ b/tests/tests/renderscript/src/android/renderscript/cts/TestNativeTanh.java
@@ -0,0 +1,284 @@
+/*
+ * 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.
+ */
+
+// Don't edit this file!  It is auto-generated by frameworks/rs/api/gen_runtime.
+
+package android.renderscript.cts;
+
+import android.renderscript.Allocation;
+import android.renderscript.RSRuntimeException;
+import android.renderscript.Element;
+
+public class TestNativeTanh extends RSBaseCompute {
+
+    private ScriptC_TestNativeTanh script;
+    private ScriptC_TestNativeTanhRelaxed scriptRelaxed;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        script = new ScriptC_TestNativeTanh(mRS);
+        scriptRelaxed = new ScriptC_TestNativeTanhRelaxed(mRS);
+    }
+
+    public class ArgumentsFloatFloat {
+        public float in;
+        public Floaty out;
+    }
+
+    private void checkNativeTanhFloatFloat() {
+        Allocation in = createRandomAllocation(mRS, Element.DataType.FLOAT_32, 1, 0x1ec2702f5ed0580bl, false);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 1), INPUTSIZE);
+            script.forEach_testNativeTanhFloatFloat(in, out);
+            verifyResultsNativeTanhFloatFloat(in, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeTanhFloatFloat: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 1), INPUTSIZE);
+            scriptRelaxed.forEach_testNativeTanhFloatFloat(in, out);
+            verifyResultsNativeTanhFloatFloat(in, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeTanhFloatFloat: " + e.toString());
+        }
+    }
+
+    private void verifyResultsNativeTanhFloatFloat(Allocation in, Allocation out, boolean relaxed) {
+        float[] arrayIn = new float[INPUTSIZE * 1];
+        in.copyTo(arrayIn);
+        float[] arrayOut = new float[INPUTSIZE * 1];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 1 ; j++) {
+                // Extract the inputs.
+                ArgumentsFloatFloat args = new ArgumentsFloatFloat();
+                args.in = arrayIn[i];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeNativeTanh(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (!args.out.couldBe(arrayOut[i * 1 + j])) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input in: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            args.in, Float.floatToRawIntBits(args.in), args.in));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(args.out.toString());
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            arrayOut[i * 1 + j], Float.floatToRawIntBits(arrayOut[i * 1 + j]), arrayOut[i * 1 + j]));
+                    if (!args.out.couldBe(arrayOut[i * 1 + j])) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkNativeTanhFloatFloat" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkNativeTanhFloat2Float2() {
+        Allocation in = createRandomAllocation(mRS, Element.DataType.FLOAT_32, 2, 0x2a5b681f8004628fl, false);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 2), INPUTSIZE);
+            script.forEach_testNativeTanhFloat2Float2(in, out);
+            verifyResultsNativeTanhFloat2Float2(in, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeTanhFloat2Float2: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 2), INPUTSIZE);
+            scriptRelaxed.forEach_testNativeTanhFloat2Float2(in, out);
+            verifyResultsNativeTanhFloat2Float2(in, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeTanhFloat2Float2: " + e.toString());
+        }
+    }
+
+    private void verifyResultsNativeTanhFloat2Float2(Allocation in, Allocation out, boolean relaxed) {
+        float[] arrayIn = new float[INPUTSIZE * 2];
+        in.copyTo(arrayIn);
+        float[] arrayOut = new float[INPUTSIZE * 2];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 2 ; j++) {
+                // Extract the inputs.
+                ArgumentsFloatFloat args = new ArgumentsFloatFloat();
+                args.in = arrayIn[i * 2 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeNativeTanh(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (!args.out.couldBe(arrayOut[i * 2 + j])) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input in: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            args.in, Float.floatToRawIntBits(args.in), args.in));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(args.out.toString());
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            arrayOut[i * 2 + j], Float.floatToRawIntBits(arrayOut[i * 2 + j]), arrayOut[i * 2 + j]));
+                    if (!args.out.couldBe(arrayOut[i * 2 + j])) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkNativeTanhFloat2Float2" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkNativeTanhFloat3Float3() {
+        Allocation in = createRandomAllocation(mRS, Element.DataType.FLOAT_32, 3, 0x2a5b72c0df0af829l, false);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 3), INPUTSIZE);
+            script.forEach_testNativeTanhFloat3Float3(in, out);
+            verifyResultsNativeTanhFloat3Float3(in, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeTanhFloat3Float3: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 3), INPUTSIZE);
+            scriptRelaxed.forEach_testNativeTanhFloat3Float3(in, out);
+            verifyResultsNativeTanhFloat3Float3(in, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeTanhFloat3Float3: " + e.toString());
+        }
+    }
+
+    private void verifyResultsNativeTanhFloat3Float3(Allocation in, Allocation out, boolean relaxed) {
+        float[] arrayIn = new float[INPUTSIZE * 4];
+        in.copyTo(arrayIn);
+        float[] arrayOut = new float[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 3 ; j++) {
+                // Extract the inputs.
+                ArgumentsFloatFloat args = new ArgumentsFloatFloat();
+                args.in = arrayIn[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeNativeTanh(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input in: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            args.in, Float.floatToRawIntBits(args.in), args.in));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(args.out.toString());
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            arrayOut[i * 4 + j], Float.floatToRawIntBits(arrayOut[i * 4 + j]), arrayOut[i * 4 + j]));
+                    if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkNativeTanhFloat3Float3" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkNativeTanhFloat4Float4() {
+        Allocation in = createRandomAllocation(mRS, Element.DataType.FLOAT_32, 4, 0x2a5b7d623e118dc3l, false);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 4), INPUTSIZE);
+            script.forEach_testNativeTanhFloat4Float4(in, out);
+            verifyResultsNativeTanhFloat4Float4(in, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeTanhFloat4Float4: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 4), INPUTSIZE);
+            scriptRelaxed.forEach_testNativeTanhFloat4Float4(in, out);
+            verifyResultsNativeTanhFloat4Float4(in, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeTanhFloat4Float4: " + e.toString());
+        }
+    }
+
+    private void verifyResultsNativeTanhFloat4Float4(Allocation in, Allocation out, boolean relaxed) {
+        float[] arrayIn = new float[INPUTSIZE * 4];
+        in.copyTo(arrayIn);
+        float[] arrayOut = new float[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 4 ; j++) {
+                // Extract the inputs.
+                ArgumentsFloatFloat args = new ArgumentsFloatFloat();
+                args.in = arrayIn[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeNativeTanh(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input in: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            args.in, Float.floatToRawIntBits(args.in), args.in));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(args.out.toString());
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            arrayOut[i * 4 + j], Float.floatToRawIntBits(arrayOut[i * 4 + j]), arrayOut[i * 4 + j]));
+                    if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkNativeTanhFloat4Float4" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    public void testNativeTanh() {
+        checkNativeTanhFloatFloat();
+        checkNativeTanhFloat2Float2();
+        checkNativeTanhFloat3Float3();
+        checkNativeTanhFloat4Float4();
+    }
+}
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/TestNativeTanpi.java b/tests/tests/renderscript/src/android/renderscript/cts/TestNativeTanpi.java
new file mode 100644
index 0000000..00b9ca1
--- /dev/null
+++ b/tests/tests/renderscript/src/android/renderscript/cts/TestNativeTanpi.java
@@ -0,0 +1,284 @@
+/*
+ * 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.
+ */
+
+// Don't edit this file!  It is auto-generated by frameworks/rs/api/gen_runtime.
+
+package android.renderscript.cts;
+
+import android.renderscript.Allocation;
+import android.renderscript.RSRuntimeException;
+import android.renderscript.Element;
+
+public class TestNativeTanpi extends RSBaseCompute {
+
+    private ScriptC_TestNativeTanpi script;
+    private ScriptC_TestNativeTanpiRelaxed scriptRelaxed;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        script = new ScriptC_TestNativeTanpi(mRS);
+        scriptRelaxed = new ScriptC_TestNativeTanpiRelaxed(mRS);
+    }
+
+    public class ArgumentsFloatFloat {
+        public float in;
+        public Floaty out;
+    }
+
+    private void checkNativeTanpiFloatFloat() {
+        Allocation in = createRandomAllocation(mRS, Element.DataType.FLOAT_32, 1, 0x3eb84fc2c36e96e0l, false);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 1), INPUTSIZE);
+            script.forEach_testNativeTanpiFloatFloat(in, out);
+            verifyResultsNativeTanpiFloatFloat(in, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeTanpiFloatFloat: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 1), INPUTSIZE);
+            scriptRelaxed.forEach_testNativeTanpiFloatFloat(in, out);
+            verifyResultsNativeTanpiFloatFloat(in, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeTanpiFloatFloat: " + e.toString());
+        }
+    }
+
+    private void verifyResultsNativeTanpiFloatFloat(Allocation in, Allocation out, boolean relaxed) {
+        float[] arrayIn = new float[INPUTSIZE * 1];
+        in.copyTo(arrayIn);
+        float[] arrayOut = new float[INPUTSIZE * 1];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 1 ; j++) {
+                // Extract the inputs.
+                ArgumentsFloatFloat args = new ArgumentsFloatFloat();
+                args.in = arrayIn[i];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeNativeTanpi(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (!args.out.couldBe(arrayOut[i * 1 + j])) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input in: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            args.in, Float.floatToRawIntBits(args.in), args.in));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(args.out.toString());
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            arrayOut[i * 1 + j], Float.floatToRawIntBits(arrayOut[i * 1 + j]), arrayOut[i * 1 + j]));
+                    if (!args.out.couldBe(arrayOut[i * 1 + j])) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkNativeTanpiFloatFloat" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkNativeTanpiFloat2Float2() {
+        Allocation in = createRandomAllocation(mRS, Element.DataType.FLOAT_32, 2, 0x13737b13af832fcl, false);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 2), INPUTSIZE);
+            script.forEach_testNativeTanpiFloat2Float2(in, out);
+            verifyResultsNativeTanpiFloat2Float2(in, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeTanpiFloat2Float2: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 2), INPUTSIZE);
+            scriptRelaxed.forEach_testNativeTanpiFloat2Float2(in, out);
+            verifyResultsNativeTanpiFloat2Float2(in, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeTanpiFloat2Float2: " + e.toString());
+        }
+    }
+
+    private void verifyResultsNativeTanpiFloat2Float2(Allocation in, Allocation out, boolean relaxed) {
+        float[] arrayIn = new float[INPUTSIZE * 2];
+        in.copyTo(arrayIn);
+        float[] arrayOut = new float[INPUTSIZE * 2];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 2 ; j++) {
+                // Extract the inputs.
+                ArgumentsFloatFloat args = new ArgumentsFloatFloat();
+                args.in = arrayIn[i * 2 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeNativeTanpi(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (!args.out.couldBe(arrayOut[i * 2 + j])) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input in: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            args.in, Float.floatToRawIntBits(args.in), args.in));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(args.out.toString());
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            arrayOut[i * 2 + j], Float.floatToRawIntBits(arrayOut[i * 2 + j]), arrayOut[i * 2 + j]));
+                    if (!args.out.couldBe(arrayOut[i * 2 + j])) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkNativeTanpiFloat2Float2" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkNativeTanpiFloat3Float3() {
+        Allocation in = createRandomAllocation(mRS, Element.DataType.FLOAT_32, 3, 0x137425299fec896l, false);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 3), INPUTSIZE);
+            script.forEach_testNativeTanpiFloat3Float3(in, out);
+            verifyResultsNativeTanpiFloat3Float3(in, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeTanpiFloat3Float3: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 3), INPUTSIZE);
+            scriptRelaxed.forEach_testNativeTanpiFloat3Float3(in, out);
+            verifyResultsNativeTanpiFloat3Float3(in, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeTanpiFloat3Float3: " + e.toString());
+        }
+    }
+
+    private void verifyResultsNativeTanpiFloat3Float3(Allocation in, Allocation out, boolean relaxed) {
+        float[] arrayIn = new float[INPUTSIZE * 4];
+        in.copyTo(arrayIn);
+        float[] arrayOut = new float[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 3 ; j++) {
+                // Extract the inputs.
+                ArgumentsFloatFloat args = new ArgumentsFloatFloat();
+                args.in = arrayIn[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeNativeTanpi(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input in: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            args.in, Float.floatToRawIntBits(args.in), args.in));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(args.out.toString());
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            arrayOut[i * 4 + j], Float.floatToRawIntBits(arrayOut[i * 4 + j]), arrayOut[i * 4 + j]));
+                    if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkNativeTanpiFloat3Float3" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkNativeTanpiFloat4Float4() {
+        Allocation in = createRandomAllocation(mRS, Element.DataType.FLOAT_32, 4, 0x1374cf3f9055e30l, false);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 4), INPUTSIZE);
+            script.forEach_testNativeTanpiFloat4Float4(in, out);
+            verifyResultsNativeTanpiFloat4Float4(in, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeTanpiFloat4Float4: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 4), INPUTSIZE);
+            scriptRelaxed.forEach_testNativeTanpiFloat4Float4(in, out);
+            verifyResultsNativeTanpiFloat4Float4(in, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testNativeTanpiFloat4Float4: " + e.toString());
+        }
+    }
+
+    private void verifyResultsNativeTanpiFloat4Float4(Allocation in, Allocation out, boolean relaxed) {
+        float[] arrayIn = new float[INPUTSIZE * 4];
+        in.copyTo(arrayIn);
+        float[] arrayOut = new float[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 4 ; j++) {
+                // Extract the inputs.
+                ArgumentsFloatFloat args = new ArgumentsFloatFloat();
+                args.in = arrayIn[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeNativeTanpi(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input in: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            args.in, Float.floatToRawIntBits(args.in), args.in));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(args.out.toString());
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            arrayOut[i * 4 + j], Float.floatToRawIntBits(arrayOut[i * 4 + j]), arrayOut[i * 4 + j]));
+                    if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkNativeTanpiFloat4Float4" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    public void testNativeTanpi() {
+        checkNativeTanpiFloatFloat();
+        checkNativeTanpiFloat2Float2();
+        checkNativeTanpiFloat3Float3();
+        checkNativeTanpiFloat4Float4();
+    }
+}
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/TestNextafter.java b/tests/tests/renderscript/src/android/renderscript/cts/TestNextafter.java
index fb76646..00381cf 100644
--- a/tests/tests/renderscript/src/android/renderscript/cts/TestNextafter.java
+++ b/tests/tests/renderscript/src/android/renderscript/cts/TestNextafter.java
@@ -77,7 +77,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeNextafter(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 1 + j])) {
                     valid = false;
@@ -146,7 +146,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeNextafter(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 2 + j])) {
                     valid = false;
@@ -215,7 +215,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeNextafter(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 4 + j])) {
                     valid = false;
@@ -284,7 +284,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeNextafter(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 4 + j])) {
                     valid = false;
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/TestPow.java b/tests/tests/renderscript/src/android/renderscript/cts/TestPow.java
index 33cec67..46cb0ca 100644
--- a/tests/tests/renderscript/src/android/renderscript/cts/TestPow.java
+++ b/tests/tests/renderscript/src/android/renderscript/cts/TestPow.java
@@ -77,7 +77,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computePow(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 1 + j])) {
                     valid = false;
@@ -146,7 +146,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computePow(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 2 + j])) {
                     valid = false;
@@ -215,7 +215,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computePow(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 4 + j])) {
                     valid = false;
@@ -284,7 +284,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computePow(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 4 + j])) {
                     valid = false;
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/TestPown.java b/tests/tests/renderscript/src/android/renderscript/cts/TestPown.java
index 722dc00..bf74366 100644
--- a/tests/tests/renderscript/src/android/renderscript/cts/TestPown.java
+++ b/tests/tests/renderscript/src/android/renderscript/cts/TestPown.java
@@ -77,7 +77,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computePown(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 1 + j])) {
                     valid = false;
@@ -145,7 +145,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computePown(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 2 + j])) {
                     valid = false;
@@ -213,7 +213,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computePown(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 4 + j])) {
                     valid = false;
@@ -281,7 +281,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computePown(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 4 + j])) {
                     valid = false;
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/TestPowr.java b/tests/tests/renderscript/src/android/renderscript/cts/TestPowr.java
index dac9b10..bdb375a 100644
--- a/tests/tests/renderscript/src/android/renderscript/cts/TestPowr.java
+++ b/tests/tests/renderscript/src/android/renderscript/cts/TestPowr.java
@@ -77,7 +77,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computePowr(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 1 + j])) {
                     valid = false;
@@ -146,7 +146,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computePowr(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 2 + j])) {
                     valid = false;
@@ -215,7 +215,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computePowr(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 4 + j])) {
                     valid = false;
@@ -284,7 +284,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computePowr(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 4 + j])) {
                     valid = false;
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/TestRadians.java b/tests/tests/renderscript/src/android/renderscript/cts/TestRadians.java
index c5820fc..8081ca1 100644
--- a/tests/tests/renderscript/src/android/renderscript/cts/TestRadians.java
+++ b/tests/tests/renderscript/src/android/renderscript/cts/TestRadians.java
@@ -70,7 +70,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeRadians(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 1 + j])) {
                     valid = false;
@@ -129,7 +129,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeRadians(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 2 + j])) {
                     valid = false;
@@ -188,7 +188,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeRadians(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 4 + j])) {
                     valid = false;
@@ -247,7 +247,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeRadians(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 4 + j])) {
                     valid = false;
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/TestRemainder.java b/tests/tests/renderscript/src/android/renderscript/cts/TestRemainder.java
index 420fb0f..1e692b9 100644
--- a/tests/tests/renderscript/src/android/renderscript/cts/TestRemainder.java
+++ b/tests/tests/renderscript/src/android/renderscript/cts/TestRemainder.java
@@ -77,7 +77,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeRemainder(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 1 + j])) {
                     valid = false;
@@ -146,7 +146,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeRemainder(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 2 + j])) {
                     valid = false;
@@ -215,7 +215,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeRemainder(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 4 + j])) {
                     valid = false;
@@ -284,7 +284,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeRemainder(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 4 + j])) {
                     valid = false;
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/TestRemquo.java b/tests/tests/renderscript/src/android/renderscript/cts/TestRemquo.java
index ff86966..e3f11ce 100644
--- a/tests/tests/renderscript/src/android/renderscript/cts/TestRemquo.java
+++ b/tests/tests/renderscript/src/android/renderscript/cts/TestRemquo.java
@@ -84,7 +84,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeRemquo(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.outD != arrayOutD[i * 1 + j] && args.out.isNaN()) {
                     valid = false;
@@ -171,7 +171,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeRemquo(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.outD != arrayOutD[i * 2 + j] && args.out.isNaN()) {
                     valid = false;
@@ -258,7 +258,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeRemquo(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.outD != arrayOutD[i * 4 + j] && args.out.isNaN()) {
                     valid = false;
@@ -345,7 +345,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeRemquo(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (args.outD != arrayOutD[i * 4 + j] && args.out.isNaN()) {
                     valid = false;
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/TestRint.java b/tests/tests/renderscript/src/android/renderscript/cts/TestRint.java
index b6b9ce5..bc21130 100644
--- a/tests/tests/renderscript/src/android/renderscript/cts/TestRint.java
+++ b/tests/tests/renderscript/src/android/renderscript/cts/TestRint.java
@@ -70,7 +70,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeRint(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 1 + j])) {
                     valid = false;
@@ -129,7 +129,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeRint(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 2 + j])) {
                     valid = false;
@@ -188,7 +188,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeRint(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 4 + j])) {
                     valid = false;
@@ -247,7 +247,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeRint(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 4 + j])) {
                     valid = false;
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/TestRound.java b/tests/tests/renderscript/src/android/renderscript/cts/TestRound.java
index 08207cc..63e4769 100644
--- a/tests/tests/renderscript/src/android/renderscript/cts/TestRound.java
+++ b/tests/tests/renderscript/src/android/renderscript/cts/TestRound.java
@@ -70,7 +70,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeRound(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 1 + j])) {
                     valid = false;
@@ -129,7 +129,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeRound(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 2 + j])) {
                     valid = false;
@@ -188,7 +188,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeRound(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 4 + j])) {
                     valid = false;
@@ -247,7 +247,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeRound(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 4 + j])) {
                     valid = false;
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/TestRsqrt.java b/tests/tests/renderscript/src/android/renderscript/cts/TestRsqrt.java
index bc904e1..f0a8c7a 100644
--- a/tests/tests/renderscript/src/android/renderscript/cts/TestRsqrt.java
+++ b/tests/tests/renderscript/src/android/renderscript/cts/TestRsqrt.java
@@ -70,7 +70,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeRsqrt(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 1 + j])) {
                     valid = false;
@@ -129,7 +129,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeRsqrt(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 2 + j])) {
                     valid = false;
@@ -188,7 +188,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeRsqrt(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 4 + j])) {
                     valid = false;
@@ -247,7 +247,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeRsqrt(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 4 + j])) {
                     valid = false;
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/TestSign.java b/tests/tests/renderscript/src/android/renderscript/cts/TestSign.java
index c62cb7a..d8c54a1 100644
--- a/tests/tests/renderscript/src/android/renderscript/cts/TestSign.java
+++ b/tests/tests/renderscript/src/android/renderscript/cts/TestSign.java
@@ -70,7 +70,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeSign(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 1 + j])) {
                     valid = false;
@@ -129,7 +129,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeSign(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 2 + j])) {
                     valid = false;
@@ -188,7 +188,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeSign(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 4 + j])) {
                     valid = false;
@@ -247,7 +247,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeSign(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 4 + j])) {
                     valid = false;
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/TestSin.java b/tests/tests/renderscript/src/android/renderscript/cts/TestSin.java
index ca6cb72..131b5d9 100644
--- a/tests/tests/renderscript/src/android/renderscript/cts/TestSin.java
+++ b/tests/tests/renderscript/src/android/renderscript/cts/TestSin.java
@@ -70,7 +70,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeSin(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 1 + j])) {
                     valid = false;
@@ -129,7 +129,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeSin(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 2 + j])) {
                     valid = false;
@@ -188,7 +188,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeSin(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 4 + j])) {
                     valid = false;
@@ -247,7 +247,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeSin(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 4 + j])) {
                     valid = false;
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/TestSincos.java b/tests/tests/renderscript/src/android/renderscript/cts/TestSincos.java
index 3b0d09e..8f9b7d7 100644
--- a/tests/tests/renderscript/src/android/renderscript/cts/TestSincos.java
+++ b/tests/tests/renderscript/src/android/renderscript/cts/TestSincos.java
@@ -77,7 +77,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeSincos(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.outCosptr.couldBe(arrayOutCosptr[i * 1 + j])) {
                     valid = false;
@@ -155,7 +155,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeSincos(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.outCosptr.couldBe(arrayOutCosptr[i * 2 + j])) {
                     valid = false;
@@ -233,7 +233,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeSincos(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.outCosptr.couldBe(arrayOutCosptr[i * 4 + j])) {
                     valid = false;
@@ -311,7 +311,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeSincos(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.outCosptr.couldBe(arrayOutCosptr[i * 4 + j])) {
                     valid = false;
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/TestSinh.java b/tests/tests/renderscript/src/android/renderscript/cts/TestSinh.java
index 54fa31e..39668be 100644
--- a/tests/tests/renderscript/src/android/renderscript/cts/TestSinh.java
+++ b/tests/tests/renderscript/src/android/renderscript/cts/TestSinh.java
@@ -70,7 +70,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeSinh(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 1 + j])) {
                     valid = false;
@@ -129,7 +129,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeSinh(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 2 + j])) {
                     valid = false;
@@ -188,7 +188,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeSinh(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 4 + j])) {
                     valid = false;
@@ -247,7 +247,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeSinh(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 4 + j])) {
                     valid = false;
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/TestSinpi.java b/tests/tests/renderscript/src/android/renderscript/cts/TestSinpi.java
index dec28d9..24f21cf 100644
--- a/tests/tests/renderscript/src/android/renderscript/cts/TestSinpi.java
+++ b/tests/tests/renderscript/src/android/renderscript/cts/TestSinpi.java
@@ -70,7 +70,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeSinpi(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 1 + j])) {
                     valid = false;
@@ -129,7 +129,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeSinpi(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 2 + j])) {
                     valid = false;
@@ -188,7 +188,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeSinpi(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 4 + j])) {
                     valid = false;
@@ -247,7 +247,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeSinpi(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 4 + j])) {
                     valid = false;
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/TestSqrt.java b/tests/tests/renderscript/src/android/renderscript/cts/TestSqrt.java
index f7029c0..ca9638f 100644
--- a/tests/tests/renderscript/src/android/renderscript/cts/TestSqrt.java
+++ b/tests/tests/renderscript/src/android/renderscript/cts/TestSqrt.java
@@ -70,7 +70,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeSqrt(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 1 + j])) {
                     valid = false;
@@ -129,7 +129,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeSqrt(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 2 + j])) {
                     valid = false;
@@ -188,7 +188,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeSqrt(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 4 + j])) {
                     valid = false;
@@ -247,7 +247,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeSqrt(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 4 + j])) {
                     valid = false;
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/TestStep.java b/tests/tests/renderscript/src/android/renderscript/cts/TestStep.java
index 9f640de..022208d 100644
--- a/tests/tests/renderscript/src/android/renderscript/cts/TestStep.java
+++ b/tests/tests/renderscript/src/android/renderscript/cts/TestStep.java
@@ -77,7 +77,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeStep(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 1 + j])) {
                     valid = false;
@@ -146,7 +146,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeStep(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 2 + j])) {
                     valid = false;
@@ -215,7 +215,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeStep(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 4 + j])) {
                     valid = false;
@@ -284,7 +284,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeStep(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 4 + j])) {
                     valid = false;
@@ -353,7 +353,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeStep(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 2 + j])) {
                     valid = false;
@@ -422,7 +422,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeStep(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 4 + j])) {
                     valid = false;
@@ -491,7 +491,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeStep(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 4 + j])) {
                     valid = false;
@@ -523,6 +523,213 @@
         }
     }
 
+    private void checkStepFloatFloat2Float2() {
+        Allocation inEdge = createRandomAllocation(mRS, Element.DataType.FLOAT_32, 1, 0x70a0554e664b1852l, false);
+        Allocation inV = createRandomAllocation(mRS, Element.DataType.FLOAT_32, 2, 0xdd7f0d444e2f7c5l, false);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 2), INPUTSIZE);
+            script.set_gAllocInV(inV);
+            script.forEach_testStepFloatFloat2Float2(inEdge, out);
+            verifyResultsStepFloatFloat2Float2(inEdge, inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testStepFloatFloat2Float2: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 2), INPUTSIZE);
+            scriptRelaxed.set_gAllocInV(inV);
+            scriptRelaxed.forEach_testStepFloatFloat2Float2(inEdge, out);
+            verifyResultsStepFloatFloat2Float2(inEdge, inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testStepFloatFloat2Float2: " + e.toString());
+        }
+    }
+
+    private void verifyResultsStepFloatFloat2Float2(Allocation inEdge, Allocation inV, Allocation out, boolean relaxed) {
+        float[] arrayInEdge = new float[INPUTSIZE * 1];
+        inEdge.copyTo(arrayInEdge);
+        float[] arrayInV = new float[INPUTSIZE * 2];
+        inV.copyTo(arrayInV);
+        float[] arrayOut = new float[INPUTSIZE * 2];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 2 ; j++) {
+                // Extract the inputs.
+                ArgumentsFloatFloatFloat args = new ArgumentsFloatFloatFloat();
+                args.inEdge = arrayInEdge[i];
+                args.inV = arrayInV[i * 2 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeStep(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (!args.out.couldBe(arrayOut[i * 2 + j])) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inEdge: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            args.inEdge, Float.floatToRawIntBits(args.inEdge), args.inEdge));
+                    message.append("\n");
+                    message.append("Input inV: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            args.inV, Float.floatToRawIntBits(args.inV), args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(args.out.toString());
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            arrayOut[i * 2 + j], Float.floatToRawIntBits(arrayOut[i * 2 + j]), arrayOut[i * 2 + j]));
+                    if (!args.out.couldBe(arrayOut[i * 2 + j])) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkStepFloatFloat2Float2" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkStepFloatFloat3Float3() {
+        Allocation inEdge = createRandomAllocation(mRS, Element.DataType.FLOAT_32, 1, 0x9b2d75ce91abcbccl, false);
+        Allocation inV = createRandomAllocation(mRS, Element.DataType.FLOAT_32, 3, 0xdd9b9ef3afe18a3l, false);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 3), INPUTSIZE);
+            script.set_gAllocInV(inV);
+            script.forEach_testStepFloatFloat3Float3(inEdge, out);
+            verifyResultsStepFloatFloat3Float3(inEdge, inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testStepFloatFloat3Float3: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 3), INPUTSIZE);
+            scriptRelaxed.set_gAllocInV(inV);
+            scriptRelaxed.forEach_testStepFloatFloat3Float3(inEdge, out);
+            verifyResultsStepFloatFloat3Float3(inEdge, inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testStepFloatFloat3Float3: " + e.toString());
+        }
+    }
+
+    private void verifyResultsStepFloatFloat3Float3(Allocation inEdge, Allocation inV, Allocation out, boolean relaxed) {
+        float[] arrayInEdge = new float[INPUTSIZE * 1];
+        inEdge.copyTo(arrayInEdge);
+        float[] arrayInV = new float[INPUTSIZE * 4];
+        inV.copyTo(arrayInV);
+        float[] arrayOut = new float[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 3 ; j++) {
+                // Extract the inputs.
+                ArgumentsFloatFloatFloat args = new ArgumentsFloatFloatFloat();
+                args.inEdge = arrayInEdge[i];
+                args.inV = arrayInV[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeStep(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inEdge: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            args.inEdge, Float.floatToRawIntBits(args.inEdge), args.inEdge));
+                    message.append("\n");
+                    message.append("Input inV: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            args.inV, Float.floatToRawIntBits(args.inV), args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(args.out.toString());
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            arrayOut[i * 4 + j], Float.floatToRawIntBits(arrayOut[i * 4 + j]), arrayOut[i * 4 + j]));
+                    if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkStepFloatFloat3Float3" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
+    private void checkStepFloatFloat4Float4() {
+        Allocation inEdge = createRandomAllocation(mRS, Element.DataType.FLOAT_32, 1, 0xc5ba964ebd0c7f46l, false);
+        Allocation inV = createRandomAllocation(mRS, Element.DataType.FLOAT_32, 4, 0xddb830a31193981l, false);
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 4), INPUTSIZE);
+            script.set_gAllocInV(inV);
+            script.forEach_testStepFloatFloat4Float4(inEdge, out);
+            verifyResultsStepFloatFloat4Float4(inEdge, inV, out, false);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testStepFloatFloat4Float4: " + e.toString());
+        }
+        try {
+            Allocation out = Allocation.createSized(mRS, getElement(mRS, Element.DataType.FLOAT_32, 4), INPUTSIZE);
+            scriptRelaxed.set_gAllocInV(inV);
+            scriptRelaxed.forEach_testStepFloatFloat4Float4(inEdge, out);
+            verifyResultsStepFloatFloat4Float4(inEdge, inV, out, true);
+        } catch (Exception e) {
+            throw new RSRuntimeException("RenderScript. Can't invoke forEach_testStepFloatFloat4Float4: " + e.toString());
+        }
+    }
+
+    private void verifyResultsStepFloatFloat4Float4(Allocation inEdge, Allocation inV, Allocation out, boolean relaxed) {
+        float[] arrayInEdge = new float[INPUTSIZE * 1];
+        inEdge.copyTo(arrayInEdge);
+        float[] arrayInV = new float[INPUTSIZE * 4];
+        inV.copyTo(arrayInV);
+        float[] arrayOut = new float[INPUTSIZE * 4];
+        out.copyTo(arrayOut);
+        for (int i = 0; i < INPUTSIZE; i++) {
+            for (int j = 0; j < 4 ; j++) {
+                // Extract the inputs.
+                ArgumentsFloatFloatFloat args = new ArgumentsFloatFloatFloat();
+                args.inEdge = arrayInEdge[i];
+                args.inV = arrayInV[i * 4 + j];
+                // Figure out what the outputs should have been.
+                Floaty.setRelaxed(relaxed);
+                CoreMathVerifier.computeStep(args);
+                // Validate the outputs.
+                boolean valid = true;
+                if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+                    valid = false;
+                }
+                if (!valid) {
+                    StringBuilder message = new StringBuilder();
+                    message.append("Input inEdge: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            args.inEdge, Float.floatToRawIntBits(args.inEdge), args.inEdge));
+                    message.append("\n");
+                    message.append("Input inV: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            args.inV, Float.floatToRawIntBits(args.inV), args.inV));
+                    message.append("\n");
+                    message.append("Expected output out: ");
+                    message.append(args.out.toString());
+                    message.append("\n");
+                    message.append("Actual   output out: ");
+                    message.append(String.format("%14.8g %8x %15a",
+                            arrayOut[i * 4 + j], Float.floatToRawIntBits(arrayOut[i * 4 + j]), arrayOut[i * 4 + j]));
+                    if (!args.out.couldBe(arrayOut[i * 4 + j])) {
+                        message.append(" FAIL");
+                    }
+                    message.append("\n");
+                    assertTrue("Incorrect output for checkStepFloatFloat4Float4" +
+                            (relaxed ? "_relaxed" : "") + ":\n" + message.toString(), valid);
+                }
+            }
+        }
+    }
+
     public void testStep() {
         checkStepFloatFloatFloat();
         checkStepFloat2Float2Float2();
@@ -531,5 +738,8 @@
         checkStepFloat2FloatFloat2();
         checkStepFloat3FloatFloat3();
         checkStepFloat4FloatFloat4();
+        checkStepFloatFloat2Float2();
+        checkStepFloatFloat3Float3();
+        checkStepFloatFloat4Float4();
     }
 }
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/TestTan.java b/tests/tests/renderscript/src/android/renderscript/cts/TestTan.java
index 6a3ae69..b4a9096 100644
--- a/tests/tests/renderscript/src/android/renderscript/cts/TestTan.java
+++ b/tests/tests/renderscript/src/android/renderscript/cts/TestTan.java
@@ -70,7 +70,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeTan(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 1 + j])) {
                     valid = false;
@@ -129,7 +129,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeTan(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 2 + j])) {
                     valid = false;
@@ -188,7 +188,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeTan(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 4 + j])) {
                     valid = false;
@@ -247,7 +247,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeTan(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 4 + j])) {
                     valid = false;
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/TestTanh.java b/tests/tests/renderscript/src/android/renderscript/cts/TestTanh.java
index 525b654..1c6893c 100644
--- a/tests/tests/renderscript/src/android/renderscript/cts/TestTanh.java
+++ b/tests/tests/renderscript/src/android/renderscript/cts/TestTanh.java
@@ -70,7 +70,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeTanh(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 1 + j])) {
                     valid = false;
@@ -129,7 +129,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeTanh(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 2 + j])) {
                     valid = false;
@@ -188,7 +188,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeTanh(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 4 + j])) {
                     valid = false;
@@ -247,7 +247,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeTanh(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 4 + j])) {
                     valid = false;
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/TestTanpi.java b/tests/tests/renderscript/src/android/renderscript/cts/TestTanpi.java
index b4d474d..f9e7ac2 100644
--- a/tests/tests/renderscript/src/android/renderscript/cts/TestTanpi.java
+++ b/tests/tests/renderscript/src/android/renderscript/cts/TestTanpi.java
@@ -70,7 +70,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeTanpi(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 1 + j])) {
                     valid = false;
@@ -129,7 +129,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeTanpi(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 2 + j])) {
                     valid = false;
@@ -188,7 +188,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeTanpi(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 4 + j])) {
                     valid = false;
@@ -247,7 +247,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeTanpi(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 4 + j])) {
                     valid = false;
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/TestTgamma.java b/tests/tests/renderscript/src/android/renderscript/cts/TestTgamma.java
index db3b5c6..04c4a7f 100644
--- a/tests/tests/renderscript/src/android/renderscript/cts/TestTgamma.java
+++ b/tests/tests/renderscript/src/android/renderscript/cts/TestTgamma.java
@@ -70,7 +70,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeTgamma(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 1 + j])) {
                     valid = false;
@@ -129,7 +129,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeTgamma(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 2 + j])) {
                     valid = false;
@@ -188,7 +188,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeTgamma(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 4 + j])) {
                     valid = false;
@@ -247,7 +247,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeTgamma(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 4 + j])) {
                     valid = false;
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/TestTrunc.java b/tests/tests/renderscript/src/android/renderscript/cts/TestTrunc.java
index 8ab10e6..8a0105f 100644
--- a/tests/tests/renderscript/src/android/renderscript/cts/TestTrunc.java
+++ b/tests/tests/renderscript/src/android/renderscript/cts/TestTrunc.java
@@ -70,7 +70,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeTrunc(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 1 + j])) {
                     valid = false;
@@ -129,7 +129,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeTrunc(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 2 + j])) {
                     valid = false;
@@ -188,7 +188,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeTrunc(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 4 + j])) {
                     valid = false;
@@ -247,7 +247,7 @@
                 // Figure out what the outputs should have been.
                 Floaty.setRelaxed(relaxed);
                 CoreMathVerifier.computeTrunc(args);
-                // Figure out what the outputs should have been.
+                // Validate the outputs.
                 boolean valid = true;
                 if (!args.out.couldBe(arrayOut[i * 4 + j])) {
                     valid = false;
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/rscpp/Android.mk b/tests/tests/rscpp/Android.mk
index 6f01cab..1f32dd5 100644
--- a/tests/tests/rscpp/Android.mk
+++ b/tests/tests/rscpp/Android.mk
@@ -25,9 +25,6 @@
 # When built, explicitly put it in the data partition.
 LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
 
-# All tests should include android.test.runner.
-LOCAL_JAVA_LIBRARIES := android.test.runner
-
 LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner
 LOCAL_JNI_SHARED_LIBRARIES := librscpptest_jni
 
diff --git a/tests/tests/rscpp/AndroidManifest.xml b/tests/tests/rscpp/AndroidManifest.xml
index b3ab43a..c014382 100644
--- a/tests/tests/rscpp/AndroidManifest.xml
+++ b/tests/tests/rscpp/AndroidManifest.xml
@@ -23,9 +23,12 @@
     </application>
 
     <!-- This is a self-instrumenting test package. -->
-    <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
                      android:targetPackage="com.android.cts.stub"
-                     android:label="CTS tests of RenderScript C++ component"/>
+                     android:label="CTS tests of RenderScript C++ component">
+        <meta-data android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
 
 </manifest>
 
diff --git a/tests/tests/rsg/Android.mk b/tests/tests/rsg/Android.mk
index 9ff554c..c58a4b0 100644
--- a/tests/tests/rsg/Android.mk
+++ b/tests/tests/rsg/Android.mk
@@ -25,9 +25,6 @@
 # When built, explicitly put it in the data partition.
 LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
 
-# All tests should include android.test.runner.
-LOCAL_JAVA_LIBRARIES := android.test.runner
-
 LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
diff --git a/tests/tests/rsg/AndroidManifest.xml b/tests/tests/rsg/AndroidManifest.xml
index 886a395..031cbc2 100644
--- a/tests/tests/rsg/AndroidManifest.xml
+++ b/tests/tests/rsg/AndroidManifest.xml
@@ -27,9 +27,12 @@
     </application>
 
     <!-- This is a self-instrumenting test package. -->
-    <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
                      android:targetPackage="com.android.cts.stub"
-                     android:label="CTS tests of Renderscript Graphics component"/>
+                     android:label="CTS tests of Renderscript Graphics component">
+        <meta-data android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
 
 </manifest>
 
diff --git a/tests/tests/sax/Android.mk b/tests/tests/sax/Android.mk
index 5270ae5..2ed7644 100644
--- a/tests/tests/sax/Android.mk
+++ b/tests/tests/sax/Android.mk
@@ -21,8 +21,6 @@
 # and when built explicitly put it in the data partition
 LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
 
-LOCAL_JAVA_LIBRARIES := android.test.runner
-
 LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
@@ -31,4 +29,6 @@
 
 LOCAL_INSTRUMENTATION_FOR := CtsTestStubs
 
+LOCAL_SDK_VERSION := current
+
 include $(BUILD_CTS_PACKAGE)
diff --git a/tests/tests/sax/AndroidManifest.xml b/tests/tests/sax/AndroidManifest.xml
index 4fbf840..d1a6f91 100644
--- a/tests/tests/sax/AndroidManifest.xml
+++ b/tests/tests/sax/AndroidManifest.xml
@@ -22,8 +22,11 @@
         <uses-library android:name="android.test.runner" />
     </application>
 
-    <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
                      android:targetPackage="com.android.cts.stub"
-                     android:label="CTS tests of android.sax"/>
+                     android:label="CTS tests of android.sax">
+        <meta-data android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
 
 </manifest>
diff --git a/tests/tests/security/Android.mk b/tests/tests/security/Android.mk
index f1a6bfb..d0fefa1 100644
--- a/tests/tests/security/Android.mk
+++ b/tests/tests/security/Android.mk
@@ -18,8 +18,6 @@
 
 LOCAL_MODULE_TAGS := tests
 
-LOCAL_JAVA_LIBRARIES := android.test.runner
-
 LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner guava
 
 LOCAL_JNI_SHARED_LIBRARIES := libctssecurity_jni
@@ -32,8 +30,6 @@
 
 LOCAL_SDK_VERSION := current
 
-LOCAL_ASSET_DIR := $(LOCAL_PATH)/assets
-
 include $(BUILD_CTS_PACKAGE)
 
 include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/tests/tests/security/AndroidManifest.xml b/tests/tests/security/AndroidManifest.xml
index 101c01c..da95e5c 100644
--- a/tests/tests/security/AndroidManifest.xml
+++ b/tests/tests/security/AndroidManifest.xml
@@ -31,9 +31,12 @@
                  android:exported="true"/>
     </application>
 
-    <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
                      android:targetPackage="com.android.cts.stub"
-                     android:label="CTS tests of com.android.cts.stub"/>
+                     android:label="CTS tests of com.android.cts.security">
+        <meta-data android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
 
 </manifest>
 
diff --git a/tests/tests/security/jni/android_security_cts_NativeCodeTest.cpp b/tests/tests/security/jni/android_security_cts_NativeCodeTest.cpp
index 485993d..dde06a2 100644
--- a/tests/tests/security/jni/android_security_cts_NativeCodeTest.cpp
+++ b/tests/tests/security/jni/android_security_cts_NativeCodeTest.cpp
@@ -15,21 +15,18 @@
  */
 
 #include <jni.h>
-#include <netlink.h>
-#include <sock_diag.h>
+#include <linux/futex.h>
+#include <linux/netlink.h>
+#include <linux/sock_diag.h>
 #include <stdio.h>
 #include <sys/socket.h>
 #include <sys/types.h>
 #include <sys/syscall.h>
+#include <unistd.h>
 #include <sys/prctl.h>
 #include <sys/ptrace.h>
 #include <sys/wait.h>
 #include <signal.h>
-#include <unistd.h>
-#include <errno.h>
-
-#define PASSED 0
-#define UNKNOWN_ERROR -1
 #include <stdlib.h>
 #include <sys/mman.h>
 #include <sys/stat.h>
@@ -40,6 +37,9 @@
 #include <inttypes.h>
 #include <linux/sysctl.h>
 
+#define PASSED 0
+#define UNKNOWN_ERROR -1
+
 /*
  * Returns true iff this device is vulnerable to CVE-2013-2094.
  * A patch for CVE-2013-2094 can be found at
@@ -89,6 +89,73 @@
 }
 
 /*
+ * 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;
+}
+
+/*
  * Prior to https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/arch/arm/include/asm/uaccess.h?id=8404663f81d212918ff85f493649a7991209fa04
  * there was a flaw in the kernel's handling of get_user and put_user
  * requests. Normally, get_user and put_user are supposed to guarantee
@@ -178,76 +245,6 @@
     return result;
 }
 
-/*
- * 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;
-}
-
-/* This isn't defined in linux/futex.h on JB */
-#define FUTEX_CMP_REQUEUE_PI    12
-
 static inline int futex_syscall(volatile int* uaddr, int op, int val, const struct timespec* ts,
                                 volatile int* uaddr2, int val3) {
     return syscall(__NR_futex, uaddr, op, val, ts, uaddr2, val3);
@@ -273,6 +270,7 @@
     return (ret == -1 && errno == EINVAL);
 }
 
+
 static JNINativeMethod gMethods[] = {
     {  "doPerfEventTest", "()Z",
             (void *) android_security_cts_NativeCodeTest_doPerfEventTest },
diff --git a/tests/tests/security/jni/netlink.h b/tests/tests/security/jni/netlink.h
deleted file mode 100644
index b5567b0..0000000
--- a/tests/tests/security/jni/netlink.h
+++ /dev/null
@@ -1,170 +0,0 @@
-/****************************************************************************
- ****************************************************************************
- ***
- ***   This header was automatically generated from a Linux kernel header
- ***   of the same name, to make information necessary for userspace to
- ***   call into the kernel available to libc.  It contains only constants,
- ***   structures, and macros generated from the original header, and thus,
- ***   contains no copyrightable information.
- ***
- ***   To edit the content of this header, modify the corresponding
- ***   source file (e.g. under external/kernel-headers/original/) then
- ***   run bionic/libc/kernel/tools/update_all.py
- ***
- ***   Any manual change here will be lost the next time this script will
- ***   be run. You've been warned!
- ***
- ****************************************************************************
- ****************************************************************************/
-#ifndef _UAPI__LINUX_NETLINK_H
-#define _UAPI__LINUX_NETLINK_H
-#include <linux/kernel.h>
-#include <linux/socket.h>
-/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
-#include <linux/types.h>
-#define NETLINK_ROUTE 0
-#define NETLINK_UNUSED 1
-#define NETLINK_USERSOCK 2
-/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
-#define NETLINK_FIREWALL 3
-#define NETLINK_SOCK_DIAG 4
-#define NETLINK_NFLOG 5
-#define NETLINK_XFRM 6
-/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
-#define NETLINK_SELINUX 7
-#define NETLINK_ISCSI 8
-#define NETLINK_AUDIT 9
-#define NETLINK_FIB_LOOKUP 10
-/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
-#define NETLINK_CONNECTOR 11
-#define NETLINK_NETFILTER 12
-#define NETLINK_IP6_FW 13
-#define NETLINK_DNRTMSG 14
-/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
-#define NETLINK_KOBJECT_UEVENT 15
-#define NETLINK_GENERIC 16
-#define NETLINK_SCSITRANSPORT 18
-#define NETLINK_ECRYPTFS 19
-/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
-#define NETLINK_RDMA 20
-#define NETLINK_CRYPTO 21
-#define NETLINK_INET_DIAG NETLINK_SOCK_DIAG
-#define MAX_LINKS 32
-/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
-struct sockaddr_nl {
- __kernel_sa_family_t nl_family;
- unsigned short nl_pad;
- __u32 nl_pid;
-/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
- __u32 nl_groups;
-};
-struct nlmsghdr {
- __u32 nlmsg_len;
-/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
- __u16 nlmsg_type;
- __u16 nlmsg_flags;
- __u32 nlmsg_seq;
- __u32 nlmsg_pid;
-/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
-};
-#define NLM_F_REQUEST 1
-#define NLM_F_MULTI 2
-#define NLM_F_ACK 4
-/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
-#define NLM_F_ECHO 8
-#define NLM_F_DUMP_INTR 16
-#define NLM_F_ROOT 0x100
-#define NLM_F_MATCH 0x200
-/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
-#define NLM_F_ATOMIC 0x400
-#define NLM_F_DUMP (NLM_F_ROOT|NLM_F_MATCH)
-#define NLM_F_REPLACE 0x100
-#define NLM_F_EXCL 0x200
-/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
-#define NLM_F_CREATE 0x400
-#define NLM_F_APPEND 0x800
-#define NLMSG_ALIGNTO 4U
-#define NLMSG_ALIGN(len) ( ((len)+NLMSG_ALIGNTO-1) & ~(NLMSG_ALIGNTO-1) )
-/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
-#define NLMSG_HDRLEN ((int) NLMSG_ALIGN(sizeof(struct nlmsghdr)))
-#define NLMSG_LENGTH(len) ((len) + NLMSG_HDRLEN)
-#define NLMSG_SPACE(len) NLMSG_ALIGN(NLMSG_LENGTH(len))
-#define NLMSG_DATA(nlh) ((void*)(((char*)nlh) + NLMSG_LENGTH(0)))
-/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
-#define NLMSG_NEXT(nlh,len) ((len) -= NLMSG_ALIGN((nlh)->nlmsg_len),   (struct nlmsghdr*)(((char*)(nlh)) + NLMSG_ALIGN((nlh)->nlmsg_len)))
-#define NLMSG_OK(nlh,len) ((len) >= (int)sizeof(struct nlmsghdr) &&   (nlh)->nlmsg_len >= sizeof(struct nlmsghdr) &&   (nlh)->nlmsg_len <= (len))
-#define NLMSG_PAYLOAD(nlh,len) ((nlh)->nlmsg_len - NLMSG_SPACE((len)))
-#define NLMSG_NOOP 0x1
-/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
-#define NLMSG_ERROR 0x2
-#define NLMSG_DONE 0x3
-#define NLMSG_OVERRUN 0x4
-#define NLMSG_MIN_TYPE 0x10
-/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
-struct nlmsgerr {
- int error;
- struct nlmsghdr msg;
-};
-/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
-#define NETLINK_ADD_MEMBERSHIP 1
-#define NETLINK_DROP_MEMBERSHIP 2
-#define NETLINK_PKTINFO 3
-#define NETLINK_BROADCAST_ERROR 4
-/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
-#define NETLINK_NO_ENOBUFS 5
-#define NETLINK_RX_RING 6
-#define NETLINK_TX_RING 7
-struct nl_pktinfo {
-/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
- __u32 group;
-};
-struct nl_mmap_req {
- unsigned int nm_block_size;
-/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
- unsigned int nm_block_nr;
- unsigned int nm_frame_size;
- unsigned int nm_frame_nr;
-};
-/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
-struct nl_mmap_hdr {
- unsigned int nm_status;
- unsigned int nm_len;
- __u32 nm_group;
-/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
- __u32 nm_pid;
- __u32 nm_uid;
- __u32 nm_gid;
-};
-/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
-enum nl_mmap_status {
- NL_MMAP_STATUS_UNUSED,
- NL_MMAP_STATUS_RESERVED,
- NL_MMAP_STATUS_VALID,
-/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
- NL_MMAP_STATUS_COPY,
- NL_MMAP_STATUS_SKIP,
-};
-#define NL_MMAP_MSG_ALIGNMENT NLMSG_ALIGNTO
-/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
-#define NL_MMAP_MSG_ALIGN(sz) __ALIGN_KERNEL(sz, NL_MMAP_MSG_ALIGNMENT)
-#define NL_MMAP_HDRLEN NL_MMAP_MSG_ALIGN(sizeof(struct nl_mmap_hdr))
-#define NET_MAJOR 36
-enum {
-/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
- NETLINK_UNCONNECTED = 0,
- NETLINK_CONNECTED,
-};
-struct nlattr {
-/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
- __u16 nla_len;
- __u16 nla_type;
-};
-#define NLA_F_NESTED (1 << 15)
-/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
-#define NLA_F_NET_BYTEORDER (1 << 14)
-#define NLA_TYPE_MASK ~(NLA_F_NESTED | NLA_F_NET_BYTEORDER)
-#define NLA_ALIGNTO 4
-#define NLA_ALIGN(len) (((len) + NLA_ALIGNTO - 1) & ~(NLA_ALIGNTO - 1))
-/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
-#define NLA_HDRLEN ((int) NLA_ALIGN(sizeof(struct nlattr)))
-#endif
diff --git a/tests/tests/security/jni/sock_diag.h b/tests/tests/security/jni/sock_diag.h
deleted file mode 100644
index 0dc2902..0000000
--- a/tests/tests/security/jni/sock_diag.h
+++ /dev/null
@@ -1,43 +0,0 @@
-/****************************************************************************
- ****************************************************************************
- ***
- ***   This header was automatically generated from a Linux kernel header
- ***   of the same name, to make information necessary for userspace to
- ***   call into the kernel available to libc.  It contains only constants,
- ***   structures, and macros generated from the original header, and thus,
- ***   contains no copyrightable information.
- ***
- ***   To edit the content of this header, modify the corresponding
- ***   source file (e.g. under external/kernel-headers/original/) then
- ***   run bionic/libc/kernel/tools/update_all.py
- ***
- ***   Any manual change here will be lost the next time this script will
- ***   be run. You've been warned!
- ***
- ****************************************************************************
- ****************************************************************************/
-#ifndef _UAPI__SOCK_DIAG_H__
-#define _UAPI__SOCK_DIAG_H__
-#include <linux/types.h>
-#define SOCK_DIAG_BY_FAMILY 20
-/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
-struct sock_diag_req {
- __u8 sdiag_family;
- __u8 sdiag_protocol;
-};
-/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
-enum {
- SK_MEMINFO_RMEM_ALLOC,
- SK_MEMINFO_RCVBUF,
- SK_MEMINFO_WMEM_ALLOC,
-/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
- SK_MEMINFO_SNDBUF,
- SK_MEMINFO_FWD_ALLOC,
- SK_MEMINFO_WMEM_QUEUED,
- SK_MEMINFO_OPTMEM,
-/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
- SK_MEMINFO_BACKLOG,
- SK_MEMINFO_VARS,
-};
-#endif
-/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
diff --git a/tests/tests/security/src/android/security/cts/NativeCodeTest.java b/tests/tests/security/src/android/security/cts/NativeCodeTest.java
index 4c1da05..4be00b6 100644
--- a/tests/tests/security/src/android/security/cts/NativeCodeTest.java
+++ b/tests/tests/security/src/android/security/cts/NativeCodeTest.java
@@ -79,6 +79,12 @@
     private static native boolean doPerfEventTest2();
 
     /**
+     * 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, false otherwise.
@@ -108,10 +114,4 @@
      * false if the device is vulnerable.
      */
     private static native boolean doCVE20141710Test();
-
-    /**
-     * Hangs if device is vulnerable to CVE-2013-1763, returns -1 if
-     * unexpected error occurs, 0 otherwise.
-     */
-    private static native int doSockDiagTest();
 }
diff --git a/tests/tests/security/src/android/security/cts/SELinuxDomainTest.java b/tests/tests/security/src/android/security/cts/SELinuxDomainTest.java
index be25201..9679f82 100644
--- a/tests/tests/security/src/android/security/cts/SELinuxDomainTest.java
+++ b/tests/tests/security/src/android/security/cts/SELinuxDomainTest.java
@@ -160,7 +160,7 @@
 
     /* Debuggerd is always there */
     public void testDebuggerdDomain() throws FileNotFoundException {
-        assertDomainOne("u:r:debuggerd:s0", "/system/bin/debuggerd");
+        assertDomainN("u:r:debuggerd:s0", "/system/bin/debuggerd", "/system/bin/debuggerd64");
     }
 
     /* Surface flinger is always there */
@@ -170,7 +170,7 @@
 
     /* Zygote is always running */
     public void testZygoteDomain() throws FileNotFoundException {
-        assertDomainOne("u:r:zygote:s0", "zygote");
+        assertDomainN("u:r:zygote:s0", "zygote", "zygote64");
     }
 
     /* drm server is always present */
diff --git a/tests/tests/speech/Android.mk b/tests/tests/speech/Android.mk
index 60acf90..75f7e4c 100755
--- a/tests/tests/speech/Android.mk
+++ b/tests/tests/speech/Android.mk
@@ -21,8 +21,6 @@
 # and when built explicitly put it in the data partition
 LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
 
-LOCAL_JAVA_LIBRARIES := android.test.runner
-
 LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
diff --git a/tests/tests/speech/AndroidManifest.xml b/tests/tests/speech/AndroidManifest.xml
index 93576b1..788f7cc 100755
--- a/tests/tests/speech/AndroidManifest.xml
+++ b/tests/tests/speech/AndroidManifest.xml
@@ -25,9 +25,12 @@
 
     <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
 
-    <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
                      android:targetPackage="com.android.cts.stub"
-                     android:label="CTS tests of android.speech"/>
+                     android:label="CTS tests of android.speech">
+        <meta-data android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
 
 </manifest>
 
diff --git a/tests/tests/telephony/Android.mk b/tests/tests/telephony/Android.mk
index e7a3336..676138d 100644
--- a/tests/tests/telephony/Android.mk
+++ b/tests/tests/telephony/Android.mk
@@ -22,7 +22,7 @@
 # and when built explicitly put it in the data partition
 LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
 
-LOCAL_JAVA_LIBRARIES := android.test.runner telephony-common mms-common
+LOCAL_JAVA_LIBRARIES := telephony-common
 
 LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner
 
@@ -32,7 +32,8 @@
 
 LOCAL_INSTRUMENTATION_FOR := CtsTestStubs
 
-# uncomment when dalvik.annotation.Test* are removed or part of SDK
-# #LOCAL_SDK_VERSION := current
+# uncomment when b/13250611 is fixed
+#LOCAL_SDK_VERSION := current
+LOCAL_JAVA_LIBRARIES += android.test.runner
 
 include $(BUILD_CTS_PACKAGE)
diff --git a/tests/tests/telephony/AndroidManifest.xml b/tests/tests/telephony/AndroidManifest.xml
index 1dfd68d..36d1e5e 100644
--- a/tests/tests/telephony/AndroidManifest.xml
+++ b/tests/tests/telephony/AndroidManifest.xml
@@ -22,9 +22,12 @@
         <uses-library android:name="android.test.runner" />
     </application>
 
-    <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
                      android:targetPackage="com.android.cts.stub"
-                     android:label="CTS tests of android.telephony"/>
+                     android:label="CTS tests of android.telephony">
+        <meta-data android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
 
 </manifest>
 
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/text/AndroidManifest.xml b/tests/tests/text/AndroidManifest.xml
index 16ba2d9..63f6d59 100644
--- a/tests/tests/text/AndroidManifest.xml
+++ b/tests/tests/text/AndroidManifest.xml
@@ -23,9 +23,12 @@
         <uses-library android:name="android.test.runner" />
     </application>
 
-    <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
                      android:targetPackage="com.android.cts.stub"
-                     android:label="CTS tests of android.text"/>
+                     android:label="CTS tests of android.text">
+        <meta-data android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
 
 </manifest>
 
diff --git a/tests/tests/text/src/android/text/cts/SpannableStringBuilderTest.java b/tests/tests/text/src/android/text/cts/SpannableStringBuilderTest.java
index cfc000d..4895ca9 100644
--- a/tests/tests/text/src/android/text/cts/SpannableStringBuilderTest.java
+++ b/tests/tests/text/src/android/text/cts/SpannableStringBuilderTest.java
@@ -259,6 +259,8 @@
             fail("should throw IndexOutOfBoundsException here");
         } catch (IndexOutOfBoundsException e) {
             // expected exception
+        } catch (NullPointerException e) {
+            // expected exception
         }
     }
 
diff --git a/tests/tests/text/src/android/text/cts/StaticLayoutLineBreakingTest.java b/tests/tests/text/src/android/text/cts/StaticLayoutLineBreakingTest.java
index 2add424..6e89ee5 100644
--- a/tests/tests/text/src/android/text/cts/StaticLayoutLineBreakingTest.java
+++ b/tests/tests/text/src/android/text/cts/StaticLayoutLineBreakingTest.java
@@ -48,7 +48,7 @@
 
         @Override
         public float getTextRunAdvances(char[] chars, int index, int count,
-                int contextIndex, int contextCount, int flags, float[] advances,
+                int contextIndex, int contextCount, boolean isRtl, float[] advances,
                 int advancesIndex) {
 
             // Conditions copy pasted from Paint
@@ -56,10 +56,6 @@
                 throw new IllegalArgumentException("text cannot be null");
             }
 
-            if (flags != DIRECTION_LTR && flags != DIRECTION_RTL) {
-                throw new IllegalArgumentException("unknown flags value: " + flags);
-            }
-
             if ((index | count | contextIndex | contextCount | advancesIndex
                     | (index - contextIndex) | (contextCount - count)
                     | ((contextIndex + contextCount) - (index + count))
diff --git a/tests/tests/text/src/android/text/format/cts/TimeTest.java b/tests/tests/text/src/android/text/format/cts/TimeTest.java
index e287270..ad779e2 100644
--- a/tests/tests/text/src/android/text/format/cts/TimeTest.java
+++ b/tests/tests/text/src/android/text/format/cts/TimeTest.java
@@ -16,17 +16,37 @@
 
 package android.text.format.cts;
 
-import java.lang.Math;
-import java.util.Calendar;
-import java.util.TimeZone;
-
 import android.test.AndroidTestCase;
 import android.text.format.Time;
 import android.util.Log;
 import android.util.TimeFormatException;
 
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Calendar;
+import java.util.List;
+import java.util.Locale;
+import java.util.Objects;
+import java.util.TimeZone;
+
 public class TimeTest extends AndroidTestCase {
     private static final String TAG = "TimeTest";
+
+    private Locale originalLocale;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        originalLocale = Locale.getDefault();
+    }
+
+    @Override
+    public void tearDown() throws Exception {
+        // The Locale may be changed by tests. Revert to the original.
+        Locale.setDefault(originalLocale);
+        super.tearDown();
+    }
+
     public void testConstructor() {
         Time time = new Time();
         new Time(Time.getCurrentTimezone());
@@ -194,7 +214,7 @@
         public int minute1;
         public int dst1;
 
-        public int offset;
+        public int delta;
 
         public int year2;
         public int month2;
@@ -204,7 +224,7 @@
         public int dst2;
 
         public DateTest(int year1, int month1, int day1, int hour1, int minute1, int dst1,
-                int offset, int year2, int month2, int day2, int hour2, int minute2,
+                int delta, int year2, int month2, int day2, int hour2, int minute2,
                 int dst2) {
             this.year1 = year1;
             this.month1 = month1;
@@ -212,7 +232,7 @@
             this.hour1 = hour1;
             this.minute1 = minute1;
             this.dst1 = dst1;
-            this.offset = offset;
+            this.delta = delta;
             this.year2 = year2;
             this.month2 = month2;
             this.day2 = day2;
@@ -222,14 +242,14 @@
         }
 
         public DateTest(int year1, int month1, int day1, int hour1, int minute1,
-                int offset, int year2, int month2, int day2, int hour2, int minute2) {
+                int delta, int year2, int month2, int day2, int hour2, int minute2) {
             this.year1 = year1;
             this.month1 = month1;
             this.day1 = day1;
             this.hour1 = hour1;
             this.minute1 = minute1;
             this.dst1 = -1;
-            this.offset = offset;
+            this.delta = delta;
             this.year2 = year2;
             this.month2 = month2;
             this.day2 = day2;
@@ -322,7 +342,8 @@
     };
 
     public void testNormalize1() throws Exception {
-        Time local = new Time("America/Los_Angeles");
+        String tz = "America/Los_Angeles";
+        Time local = new Time(tz);
 
         int len = dayTests.length;
         for (int index = 0; index < len; index++) {
@@ -330,37 +351,27 @@
             local.set(0, test.minute1, test.hour1, test.day1, test.month1, test.year1);
             // call normalize() to make sure that isDst is set
             local.normalize(false /* use isDst */);
-            local.monthDay += test.offset;
+            local.monthDay += test.delta;
             local.normalize(true /* ignore isDst */);
-            if (local.year != test.year2 || local.month != test.month2
-                    || local.monthDay != test.day2 || local.hour != test.hour2
-                    || local.minute != test.minute2) {
-                String expectedTime = String.format("%d-%02d-%02d %02d:%02d",
-                        test.year2, test.month2, test.day2, test.hour2, test.minute2);
-                String actualTime = String.format("%d-%02d-%02d %02d:%02d",
-                        local.year, local.month, local.monthDay, local.hour, local.minute);
-                throw new RuntimeException(
-                        "day test index " + index + ", normalize(): expected local " + expectedTime
-                                + " got: " + actualTime);
-            }
+
+            Time expected = new Time(tz);
+            Fields.setDateTime(expected, test.year2, test.month2, test.day2, test.hour2,
+                    test.minute2, 0);
+            Fields.assertTimeEquals("day test index " + index + ", normalize():",
+                    Fields.MAIN_DATE_TIME, expected, local);
 
             local.set(0, test.minute1, test.hour1, test.day1, test.month1, test.year1);
             // call normalize() to make sure that isDst is set
             local.normalize(false /* use isDst */);
-            local.monthDay += test.offset;
+            local.monthDay += test.delta;
             long millis = local.toMillis(true /* ignore isDst */);
             local.set(millis);
-            if (local.year != test.year2 || local.month != test.month2
-                    || local.monthDay != test.day2 || local.hour != test.hour2
-                    || local.minute != test.minute2) {
-                String expectedTime = String.format("%d-%02d-%02d %02d:%02d",
-                        test.year2, test.month2, test.day2, test.hour2, test.minute2);
-                String actualTime = String.format("%d-%02d-%02d %02d:%02d",
-                        local.year, local.month, local.monthDay, local.hour, local.minute);
-                throw new RuntimeException(
-                        "day test index " + index + ", toMillis(): expected local " + expectedTime
-                                + " got: " + actualTime);
-            }
+
+            expected = new Time(tz);
+            Fields.setDateTime(expected, test.year2, test.month2, test.day2, test.hour2,
+                    test.minute2, 0);
+            Fields.assertTimeEquals("day test index " + index + ", toMillis():",
+                    Fields.MAIN_DATE_TIME, expected, local);
         }
 
         len = minuteTests.length;
@@ -371,60 +382,166 @@
             // call normalize() to make sure that isDst is set
             local.normalize(false /* use isDst */);
             if (test.dst2 == -1) test.dst2 = local.isDst;
-            local.minute += test.offset;
+            local.minute += test.delta;
             local.normalize(false /* use isDst */);
-            if (local.year != test.year2 || local.month != test.month2
-                    || local.monthDay != test.day2 || local.hour != test.hour2
-                    || local.minute != test.minute2 || local.isDst != test.dst2) {
-                String expectedTime = String.format("%d-%02d-%02d %02d:%02d isDst: %d",
-                        test.year2, test.month2, test.day2, test.hour2, test.minute2,
-                        test.dst2);
-                String actualTime = String.format("%d-%02d-%02d %02d:%02d isDst: %d",
-                        local.year, local.month, local.monthDay, local.hour, local.minute,
-                        local.isDst);
-                throw new RuntimeException(
-                        "minute test index " + index + ", normalize(): expected local "
-                                + expectedTime + " got: " + actualTime);
-            }
+
+            Time expected = new Time(tz);
+            Fields.setDateTime(expected, test.year2, test.month2, test.day2, test.hour2,
+                    test.minute2, 0);
+            Fields.setDst(expected, test.dst2 /* isDst */, PstPdt.getUtcOffsetSeconds(test.dst2));
+            Fields.assertTimeEquals("minute test index " + index + ", normalize():",
+                    Fields.MAIN_DATE_TIME | Fields.DST_FIELDS, expected, local);
 
             local.set(0, test.minute1, test.hour1, test.day1, test.month1, test.year1);
             local.isDst = test.dst1;
             // call normalize() to make sure that isDst is set
             local.normalize(false /* use isDst */);
             if (test.dst2 == -1) test.dst2 = local.isDst;
-            local.minute += test.offset;
+            local.minute += test.delta;
             long millis = local.toMillis(false /* use isDst */);
             local.set(millis);
-            if (local.year != test.year2 || local.month != test.month2
-                    || local.monthDay != test.day2 || local.hour != test.hour2
-                    || local.minute != test.minute2 || local.isDst != test.dst2) {
-                String expectedTime = String.format("%d-%02d-%02d %02d:%02d isDst: %d",
-                        test.year2, test.month2, test.day2, test.hour2, test.minute2,
-                        test.dst2);
-                String actualTime = String.format("%d-%02d-%02d %02d:%02d isDst: %d",
-                        local.year, local.month, local.monthDay, local.hour, local.minute,
-                        local.isDst);
-                throw new RuntimeException(
-                        "minute test index " + index + ", toMillis(): expected local "
-                                + expectedTime + " got: " + actualTime);
-            }
+
+            expected = new Time(tz);
+            Fields.setDateTime(expected, test.year2, test.month2, test.day2, test.hour2,
+                    test.minute2, 0);
+            Fields.setDst(expected, test.dst2 /* isDst */, PstPdt.getUtcOffsetSeconds(test.dst2));
+            Fields.assertTimeEquals("minute test index " + index + ", toMillis():",
+                    Fields.MAIN_DATE_TIME | Fields.DST_FIELDS, expected, local);
         }
     }
 
-    public void testSwitchTimezone0() throws Exception {
-        final String timeZone = "America/Los_Angeles";
-        Time t = new Time(Time.TIMEZONE_UTC);
-        t.parse("20061005T120000");
-        t.switchTimezone(timeZone);
-        assertEquals(timeZone, t.timezone);
+    public void testSwitchTimezone_simpleUtc() throws Exception {
+        String originalTz = Time.TIMEZONE_UTC;
+        Time t = new Time(originalTz);
+        Fields.set(t, 2006, 9, 5, 12, 0, 0, -1 /* isDst */, 0, 0, 0);
+
+        String newTz = "America/Los_Angeles";
+        t.switchTimezone(newTz);
+
+        Time expected1 = new Time(newTz);
+        Fields.set(expected1, 2006, 9, 5, 5, 0, 0, 1 /* isDst */, -25200, 277, 4);
+        Fields.assertTimeEquals(expected1, t);
+
+        t.switchTimezone(originalTz);
+
+        Time expected2 = new Time(originalTz);
+        Fields.set(expected2, 2006, 9, 5, 12, 0, 0, 0 /* isDst */, 0, 277, 4);
+        Fields.assertTimeEquals(expected2, t);
     }
 
-    public void testCtor0() throws Exception {
-        Time t = new Time(Time.TIMEZONE_UTC);
-        assertEquals(Time.TIMEZONE_UTC, t.timezone);
+    public void testSwitchTimezone_standardToStandardTime() throws Exception {
+        String zone1 = "Europe/London";
+        String zone2 = "America/Los_Angeles";
+
+        // A time unambiguously in standard time in both zones.
+        Time t = new Time(zone1);
+        Fields.set(t, 2007, 2, 10, 12, 0, 0, -1 /* isDst */, 0, 0, 0);
+
+        t.switchTimezone(zone2);
+
+        Time expected1 = new Time(zone2);
+        Fields.set(expected1, 2007, 2, 10, 4, 0, 0, 0 /* isDst */, -28800, 68, 6);
+        Fields.assertTimeEquals(expected1, t);
+
+        t.switchTimezone(zone1);
+
+        Time expected2 = new Time(zone1);
+        Fields.set(expected2, 2007, 2, 10, 12, 0, 0, 0 /* isDst */, 0, 68, 6);
+        Fields.assertTimeEquals(expected2, t);
     }
 
-    public void testGetActualMaximum0() throws Exception {
+    public void testSwitchTimezone_dstToDstTime() throws Exception {
+        String zone1 = "Europe/London";
+        String zone2 = "America/Los_Angeles";
+
+        // A time unambiguously in DST in both zones.
+        Time t = new Time(zone1);
+        Fields.set(t, 2007, 2, 26, 12, 0, 0, -1 /* isDst */, 0, 0, 0);
+
+        t.switchTimezone(zone2);
+
+        Time expected1 = new Time(zone2);
+        Fields.set(expected1, 2007, 2, 26, 4, 0, 0, 1 /* isDst */, -25200, 84, 1);
+        Fields.assertTimeEquals(expected1, t);
+
+        t.switchTimezone(zone1);
+
+        Time expected2 = new Time(zone1);
+        Fields.set(expected2, 2007, 2, 26, 12, 0, 0, 1 /* isDst */, 3600, 84, 1);
+        Fields.assertTimeEquals(expected2, t);
+    }
+
+    public void testSwitchTimezone_standardToDstTime() throws Exception {
+        String zone1 = "Europe/London";
+        String zone2 = "America/Los_Angeles";
+
+        // A time that is in standard time in zone1, but in DST in zone2.
+        Time t = new Time(zone1);
+        Fields.set(t, 2007, 2, 24, 12, 0, 0, -1 /* isDst */, 0, 0, 0);
+
+        t.switchTimezone(zone2);
+
+        Time expected1 = new Time(zone2);
+        Fields.set(expected1, 2007, 2, 24, 5, 0, 0, 1 /* isDst */, -25200, 82, 6);
+        Fields.assertTimeEquals(expected1, t);
+
+        t.switchTimezone(zone1);
+
+        Time expected2 = new Time(zone1);
+        Fields.set(expected2, 2007, 2, 24, 12, 0, 0, 0 /* isDst */, 0, 82, 6);
+        Fields.assertTimeEquals(expected2, t);
+    }
+
+    public void testSwitchTimezone_sourceDateInvalid() throws Exception {
+        String zone1 = "Europe/London";
+        String zone2 = "America/Los_Angeles";
+
+        // A source wall time known not to normalize because it doesn't "exist" locally.
+        Time t = new Time(zone1);
+        Fields.set(t, 2007, 2, 25, 1, 30, 0, -1 /* isDst */, 0, 0, 0);
+        assertEquals(-1, t.toMillis(false));
+
+        t.switchTimezone(zone2);
+
+        // It is assumed a sad trombone noise is also emitted from the device at this point.
+        // This illustrates why using -1 to indicate a problem, when -1 is in range, is a poor idea.
+        Time expected1 = new Time(zone2);
+        Fields.set(expected1, 1969, 11, 31, 15, 59, 59, 0 /* isDst */, -28800, 364, 3);
+        Fields.assertTimeEquals(expected1, t);
+    }
+
+    public void testSwitchTimezone_dstToStandardTime() throws Exception {
+        String zone1 = "America/Los_Angeles";
+        String zone2 = "Europe/London";
+
+        // A time that is in DST in zone1, but in standard in zone2.
+        Time t = new Time(zone1);
+        Fields.set(t, 2007, 2, 12, 12, 0, 0, -1 /* isDst */, 0, 0, 0);
+
+        t.switchTimezone(zone2);
+
+        Time expected1 = new Time(zone2);
+        Fields.set(expected1, 2007, 2, 12, 19, 0, 0, 0 /* isDst */, 0, 70, 1);
+        Fields.assertTimeEquals(expected1, t);
+
+        t.switchTimezone(zone1);
+
+        Time expected2 = new Time(zone1);
+        Fields.set(expected2, 2007, 2, 12, 12, 0, 0, 1 /* isDst */, -25200, 70, 1);
+        Fields.assertTimeEquals(expected2, t);
+    }
+
+    public void testCtor() throws Exception {
+        String tz = Time.TIMEZONE_UTC;
+        Time t = new Time(tz);
+        assertEquals(tz, t.timezone);
+
+        Time expected = new Time(tz);
+        Fields.set(expected, 1970, 0, 1, 0, 0, 0, -1 /* isDst */, 0, 0, 0);
+        Fields.assertTimeEquals(expected, t);
+    }
+
+    public void testGetActualMaximum() throws Exception {
         Time t = new Time(Time.TIMEZONE_UTC);
         assertEquals(59, t.getActualMaximum(Time.SECOND));
         assertEquals(59, t.getActualMaximum(Time.MINUTE));
@@ -484,13 +601,81 @@
         assertEquals(-1, t.isDst);
     }
 
-    public void testCompare0() throws Exception {
-        Time a = new Time(Time.TIMEZONE_UTC);
-        Time b = new Time("America/Los_Angeles");
-        assertTrue(Time.compare(a, b) < 0);
-        assertTrue(Time.compare(b, a) > 0);
-        assertTrue(Time.compare(b, b) == 0);
-        assertTrue(Time.compare(a, a) == 0);
+    public void testClear() throws Exception {
+        Time t = new Time("America/Los_Angeles");
+        Fields.set(t, 1, 2, 3, 4, 5, 6, 7 /* isDst */, 8, 9, 10);
+
+        t.clear(Time.TIMEZONE_UTC);
+
+        Time expected = new Time(Time.TIMEZONE_UTC);
+        Fields.set(expected, 0, 0, 0, 0, 0, 0, -1 /* isDst */, 0, 0, 0);
+        Fields.assertTimeEquals(expected, t);
+    }
+
+    public void testCompare() throws Exception {
+        String timezone = "America/New_York";
+        int[] aDateTimeFields = new int[] { 2005, 2, 3, 4, 5, 6 };
+
+        Time a = new Time(timezone);
+        Fields.setDateTime(a, aDateTimeFields);
+        Fields.setDst(a, 7, 8);
+        Fields.setDerivedDateTime(a, 9, 10);
+
+        int[] bDateTimeFields = new int[aDateTimeFields.length];
+        System.arraycopy(aDateTimeFields, 0, bDateTimeFields, 0, aDateTimeFields.length);
+        Time b = new Time(timezone);
+        Fields.setDateTime(b, bDateTimeFields);
+        Fields.setDst(b, 7, 8);
+        Fields.setDerivedDateTime(b, 9, 10);
+
+        // Confirm timezone behavior: When timezones differ the result depends on the millis time.
+        // This means that all date/time and the isDst field can impact compare() when timezones
+        // are different. The result is always -1, 0 or 1.
+
+        // East of New York. Millis goes down for a given wall time.
+        b.timezone = "Europe/London";
+        assertEquals(1, Time.compare(a, b));
+        assertEquals(-1, Time.compare(b, a));
+
+        // West of New York. Millis goes up for a given wall time.
+        b.timezone = "America/Los_Angeles";
+        assertEquals(-1, Time.compare(a, b));
+        assertEquals(1, Time.compare(b, a));
+
+        b.timezone = timezone;
+        assertEquals(0, Time.compare(a, b));
+        assertEquals(0, Time.compare(b, a));
+
+        // Now confirm behavior when timezones are the same: Only the date/time fields are checked
+        // and the result depends on the difference between the fields and the order in which they
+        // are checked.
+
+        // Check date/time fields
+        for (int i = 0; i < aDateTimeFields.length; i++) {
+            bDateTimeFields[i] = 99999;
+            Fields.setDateTime(b, bDateTimeFields);
+
+            assertEquals(aDateTimeFields[i] - bDateTimeFields[i], Time.compare(a, b));
+            assertEquals(bDateTimeFields[i] - aDateTimeFields[i], Time.compare(b, a));
+
+            bDateTimeFields[i] = -99999;
+            Fields.setDateTime(b, bDateTimeFields);
+
+            assertEquals(aDateTimeFields[i] - bDateTimeFields[i], Time.compare(a, b));
+            assertEquals(bDateTimeFields[i] - aDateTimeFields[i], Time.compare(b, a));
+
+            bDateTimeFields[i] = aDateTimeFields[i];
+            Fields.setDateTime(b, bDateTimeFields);
+
+            assertEquals(0, Time.compare(a, b));
+            assertEquals(0, Time.compare(b, a));
+        }
+
+        // Show that the derived fields have no effect on compare when timezones are the same.
+        Fields.setDst(b, 999, 999);
+        Fields.setDerivedDateTime(b, 999, 999);
+        assertEquals(0, Time.compare(a, b));
+        assertEquals(0, Time.compare(b, a));
     }
 
     public void testCompareNullFailure() throws Exception {
@@ -518,86 +703,1079 @@
         }
     }
 
-    public void testFormat0() throws Exception {
+    public void testCompare_invalidDatesAreEqualIfTimezoneDiffers() throws Exception {
+        String timezone = "America/New_York";
+        // This date is outside of the valid set of dates that can be calculated so toMillis()
+        // returns -1.
+        int[] dateTimeFields = new int[] { 1, 2, 3, 4, 5, 6 };
+
+        Time a = new Time(timezone);
+        Fields.setDateTime(a, dateTimeFields);
+        Fields.setDst(a, 7, 8);
+        Fields.setDerivedDateTime(a, 9, 10);
+        assertEquals(-1, a.toMillis(false));
+
+        Time b = new Time(timezone);
+        Fields.setDateTime(b, dateTimeFields);
+        Fields.setDst(b, 11, 12);
+        Fields.setDerivedDateTime(b, 13, 14);
+        assertEquals(-1, b.toMillis(false));
+
+        // DST and derived-date time fields are always ignored.
+        assertEquals(0, Time.compare(a, b));
+
+        // Set a different invalid set of date fields.
+        Fields.setDateTime(b, new int[] { 6, 5, 4, 3, 2, 1 });
+        assertEquals(-5, Time.compare(a, b));
+
+        // Now change the timezone
+        b.timezone = Time.TIMEZONE_UTC;
+
+        // >:-(
+        assertEquals(0, Time.compare(a, b));
+    }
+
+    public void testFormat() throws Exception {
         Time t = new Time(Time.TIMEZONE_UTC);
         String r = t.format("%Y%m%dT%H%M%S");
         assertEquals("19700101T000000", r);
     }
 
-    public void testSetToNow0() throws Exception {
+    public void testFormat_null() {
         Time t = new Time(Time.TIMEZONE_UTC);
-        // Time has resolution of 1 second. So round-off to second and compare
-        long currentTime = System.currentTimeMillis() / 1000;
+        assertEquals(t.format("%c"), t.format(null));
+    }
+
+    public void testFormat_badPatterns() throws Exception {
+        Time t = new Time(Time.TIMEZONE_UTC);
+        assertFormatEquals(t, "%~Y", "~Y");
+        assertFormatEquals(t, "%", "%");
+    }
+
+    public void testFormat_doesNotNormalize() throws Exception {
+        Time t = new Time(Time.TIMEZONE_UTC);
+        Fields.set(t, 2005, 13, 32, -1, -1, -1, -2, -2, -2, -2);
+
+        Time tCopy = new Time(t);
+        Fields.assertTimeEquals(t, tCopy);
+
+        assertFormatEquals(t, "%Y%m%dT%H%M%S", "20051432T-1-1-1");
+
+        Fields.assertTimeEquals(t, tCopy);
+    }
+
+    private static void assertFormatEquals(Time t, String formatArg, String expected) {
+        assertEquals(expected, t.format(formatArg));
+    }
+
+    public void testFormat_tokensUkLocale() throws Exception {
+        Locale.setDefault(Locale.UK);
+
+        Time t = new Time("Europe/London");
+        Fields.setDateTime(t, 2005, 5, 1, 12, 30, 15);
+
+        // Prove the un-normalized fields are used.
+        assertFormatEquals(t, "%A", "Sunday");
+
+        // Set fields like weekday.
+        t.normalize(true);
+
+        assertFormatEquals(t, "%A", "Wednesday");
+        assertFormatEquals(t, "%a", "Wed");
+        assertFormatEquals(t, "%B", "June");
+        assertFormatEquals(t, "%b", "Jun");
+        assertFormatEquals(t, "%C", "20");
+        assertFormatEquals(t, "%c", "1 Jun 2005, 12:30:15");
+        assertFormatEquals(t, "%D", "06/01/05");
+        assertFormatEquals(t, "%d", "01");
+        assertFormatEquals(t, "%E", "E");
+        assertFormatEquals(t, "%e", " 1");
+        assertFormatEquals(t, "%F", "2005-06-01");
+        assertFormatEquals(t, "%G", "2005");
+        assertFormatEquals(t, "%g", "05");
+        assertFormatEquals(t, "%H", "12");
+        assertFormatEquals(t, "%h", "Jun");
+        assertFormatEquals(t, "%I", "12");
+        assertFormatEquals(t, "%j", "152");
+        assertFormatEquals(t, "%K", "K");
+        assertFormatEquals(t, "%k", "12");
+        assertFormatEquals(t, "%l", "12");
+        assertFormatEquals(t, "%M", "30");
+        assertFormatEquals(t, "%m", "06");
+        assertFormatEquals(t, "%n", "\n");
+        assertFormatEquals(t, "%O", "O");
+        assertFormatEquals(t, "%p", "pm");
+        assertFormatEquals(t, "%P", "pm");
+        assertFormatEquals(t, "%R", "12:30");
+        assertFormatEquals(t, "%r", "12:30:15 pm");
+        assertFormatEquals(t, "%S", "15");
+        // The original C implementation uses the (native) system default TZ, not the timezone of
+        // the Time and is therefore not stable. The commented value below is the one using the
+        // Time's timezone which is probably the expected answer.
+        // assertFormatEquals(t, "%s", "1117625415");
+        assertFormatEquals(t, "%T", "12:30:15");
+        assertFormatEquals(t, "%t", "\t");
+        assertFormatEquals(t, "%U", "22");
+        assertFormatEquals(t, "%u", "3");
+        assertFormatEquals(t, "%V", "22");
+        assertFormatEquals(t, "%v", " 1-Jun-2005");
+        assertFormatEquals(t, "%W", "22");
+        assertFormatEquals(t, "%w", "3");
+        assertFormatEquals(t, "%X", "12:30:15");
+        assertFormatEquals(t, "%x", "1 June 2005");
+        assertFormatEquals(t, "%y", "05");
+        assertFormatEquals(t, "%Y", "2005");
+        assertFormatEquals(t, "%Z", "BST");
+        assertFormatEquals(t, "%z", "+0100");
+        assertFormatEquals(t, "%+", "Wed Jun  1 12:30:15 BST 2005");
+        assertFormatEquals(t, "%%", "%");
+
+        // Modifiers
+
+        assertFormatEquals(t, "%EC", "20");
+        assertFormatEquals(t, "%OC", "20");
+
+        assertFormatEquals(t, "%_+", "Wed Jun  1 12:30:15 BST 2005");
+        assertFormatEquals(t, "%-+", "Wed Jun  1 12:30:15 BST 2005");
+        assertFormatEquals(t, "%0+", "Wed Jun  1 12:30:15 BST 2005");
+        assertFormatEquals(t, "%^+", "Wed Jun  1 12:30:15 BST 2005");
+        assertFormatEquals(t, "%#+", "Wed Jun  1 12:30:15 BST 2005");
+
+        assertFormatEquals(t, "%_A", "Wednesday");
+        assertFormatEquals(t, "%-A", "Wednesday");
+        assertFormatEquals(t, "%0A", "Wednesday");
+        assertFormatEquals(t, "%^A", "WEDNESDAY");
+        assertFormatEquals(t, "%#A", "wEDNESDAY");
+
+        assertFormatEquals(t, "%_Y", "20 5");
+        assertFormatEquals(t, "%-Y", "205");
+        assertFormatEquals(t, "%0Y", "2005");
+        assertFormatEquals(t, "%^Y", "2005");
+        assertFormatEquals(t, "%#Y", "2005");
+
+        assertFormatEquals(t, "%_d", " 1");
+        assertFormatEquals(t, "%-d", "1");
+        assertFormatEquals(t, "%0d", "01");
+        assertFormatEquals(t, "%^d", "01");
+        assertFormatEquals(t, "%#d", "01");
+    }
+
+    public void testFormat_tokensUsLocale() throws Exception {
+        Locale.setDefault(Locale.US);
+
+        Time t = new Time("America/New_York");
+        Fields.setDateTime(t, 2005, 5, 1, 12, 30, 15);
+
+        // Prove the un-normalized fields are used.
+        assertFormatEquals(t, "%A", "Sunday");
+
+        // Set fields like weekday.
+        t.normalize(true);
+
+        assertFormatEquals(t, "%A", "Wednesday");
+        assertFormatEquals(t, "%a", "Wed");
+        assertFormatEquals(t, "%B", "June");
+        assertFormatEquals(t, "%b", "Jun");
+        assertFormatEquals(t, "%C", "20");
+        assertFormatEquals(t, "%c", "1 Jun 2005, 12:30:15");
+        assertFormatEquals(t, "%D", "06/01/05");
+        assertFormatEquals(t, "%d", "01");
+        assertFormatEquals(t, "%E", "E");
+        assertFormatEquals(t, "%e", " 1");
+        assertFormatEquals(t, "%F", "2005-06-01");
+        assertFormatEquals(t, "%G", "2005");
+        assertFormatEquals(t, "%g", "05");
+        assertFormatEquals(t, "%H", "12");
+        assertFormatEquals(t, "%h", "Jun");
+        assertFormatEquals(t, "%I", "12");
+        assertFormatEquals(t, "%j", "152");
+        assertFormatEquals(t, "%K", "K");
+        assertFormatEquals(t, "%k", "12");
+        assertFormatEquals(t, "%l", "12");
+        assertFormatEquals(t, "%M", "30");
+        assertFormatEquals(t, "%m", "06");
+        assertFormatEquals(t, "%n", "\n");
+        assertFormatEquals(t, "%O", "O");
+        assertFormatEquals(t, "%p", "PM");
+        assertFormatEquals(t, "%P", "pm");
+        assertFormatEquals(t, "%R", "12:30");
+        assertFormatEquals(t, "%r", "12:30:15 PM");
+        assertFormatEquals(t, "%S", "15");
+        // The original C implementation uses the (native) system default TZ, not the timezone of
+        // the Time and is therefore not stable. The commented value below is the one using the
+        // Time's timezone which is probably the expected answer.
+        // assertFormatEquals(t, "%s", "1117643415");
+        assertFormatEquals(t, "%T", "12:30:15");
+        assertFormatEquals(t, "%t", "\t");
+        assertFormatEquals(t, "%U", "22");
+        assertFormatEquals(t, "%u", "3");
+        assertFormatEquals(t, "%V", "22");
+        assertFormatEquals(t, "%v", " 1-Jun-2005");
+        assertFormatEquals(t, "%W", "22");
+        assertFormatEquals(t, "%w", "3");
+        assertFormatEquals(t, "%X", "12:30:15");
+        assertFormatEquals(t, "%x", "1 June 2005");
+        assertFormatEquals(t, "%y", "05");
+        assertFormatEquals(t, "%Y", "2005");
+        assertFormatEquals(t, "%Z", "EDT");
+        assertFormatEquals(t, "%z", "-0400");
+        assertFormatEquals(t, "%+", "Wed Jun  1 12:30:15 EDT 2005");
+        assertFormatEquals(t, "%%", "%");
+
+        // Modifiers
+
+        assertFormatEquals(t, "%EC", "20");
+        assertFormatEquals(t, "%OC", "20");
+
+        assertFormatEquals(t, "%_+", "Wed Jun  1 12:30:15 EDT 2005");
+        assertFormatEquals(t, "%-+", "Wed Jun  1 12:30:15 EDT 2005");
+        assertFormatEquals(t, "%0+", "Wed Jun  1 12:30:15 EDT 2005");
+        assertFormatEquals(t, "%^+", "Wed Jun  1 12:30:15 EDT 2005");
+        assertFormatEquals(t, "%#+", "Wed Jun  1 12:30:15 EDT 2005");
+
+        assertFormatEquals(t, "%_A", "Wednesday");
+        assertFormatEquals(t, "%-A", "Wednesday");
+        assertFormatEquals(t, "%0A", "Wednesday");
+        assertFormatEquals(t, "%^A", "WEDNESDAY");
+        assertFormatEquals(t, "%#A", "wEDNESDAY");
+
+        assertFormatEquals(t, "%_Y", "20 5");
+        assertFormatEquals(t, "%-Y", "205");
+        assertFormatEquals(t, "%0Y", "2005");
+        assertFormatEquals(t, "%^Y", "2005");
+        assertFormatEquals(t, "%#Y", "2005");
+
+        assertFormatEquals(t, "%_d", " 1");
+        assertFormatEquals(t, "%-d", "1");
+        assertFormatEquals(t, "%0d", "01");
+        assertFormatEquals(t, "%^d", "01");
+        assertFormatEquals(t, "%#d", "01");
+    }
+
+    public void testFormat_tokensFranceLocale() throws Exception {
+        Locale.setDefault(Locale.FRANCE);
+
+        Time t = new Time("Europe/Paris");
+        Fields.setDateTime(t, 2005, 5, 1, 12, 30, 15);
+
+        // Prove the un-normalized fields are used.
+        assertFormatEquals(t, "%A", "dimanche");
+
+        // Set fields like weekday.
+        t.normalize(true);
+
+        assertFormatEquals(t, "%A", "mercredi");
+        assertFormatEquals(t, "%a", "mer.");
+        assertFormatEquals(t, "%B", "juin");
+        assertFormatEquals(t, "%b", "juin");
+        assertFormatEquals(t, "%C", "20");
+        assertFormatEquals(t, "%c", "1 juin 2005, 12:30:15");
+        assertFormatEquals(t, "%D", "06/01/05");
+        assertFormatEquals(t, "%d", "01");
+        assertFormatEquals(t, "%E", "E");
+        assertFormatEquals(t, "%e", " 1");
+        assertFormatEquals(t, "%F", "2005-06-01");
+        assertFormatEquals(t, "%G", "2005");
+        assertFormatEquals(t, "%g", "05");
+        assertFormatEquals(t, "%H", "12");
+        assertFormatEquals(t, "%h", "juin");
+        assertFormatEquals(t, "%I", "12");
+        assertFormatEquals(t, "%j", "152");
+        assertFormatEquals(t, "%K", "K");
+        assertFormatEquals(t, "%k", "12");
+        assertFormatEquals(t, "%l", "12");
+        assertFormatEquals(t, "%M", "30");
+        assertFormatEquals(t, "%m", "06");
+        assertFormatEquals(t, "%n", "\n");
+        assertFormatEquals(t, "%O", "O");
+        assertFormatEquals(t, "%p", "PM");
+        assertFormatEquals(t, "%P", "pm");
+        assertFormatEquals(t, "%R", "12:30");
+        assertFormatEquals(t, "%r", "12:30:15 PM");
+        assertFormatEquals(t, "%S", "15");
+        // The original C implementation uses the (native) system default TZ, not the timezone of
+        // the Time and is therefore not stable. The commented value below is the one using the
+        // Time's timezone which is probably the expected answer.
+        // assertFormatEquals(t, "%s", "1117621815");
+        assertFormatEquals(t, "%T", "12:30:15");
+        assertFormatEquals(t, "%t", "\t");
+        assertFormatEquals(t, "%U", "22");
+        assertFormatEquals(t, "%u", "3");
+        assertFormatEquals(t, "%V", "22");
+        assertFormatEquals(t, "%v", " 1-juin-2005");
+        assertFormatEquals(t, "%W", "22");
+        assertFormatEquals(t, "%w", "3");
+        assertFormatEquals(t, "%X", "12:30:15");
+        assertFormatEquals(t, "%x", "1 juin 2005");
+        assertFormatEquals(t, "%y", "05");
+        assertFormatEquals(t, "%Y", "2005");
+        assertFormatEquals(t, "%Z", "GMT+01:00");
+        assertFormatEquals(t, "%z", "+0200");
+        assertFormatEquals(t, "%+", "mer. juin  1 12:30:15 GMT+01:00 2005");
+        assertFormatEquals(t, "%%", "%");
+
+        // Modifiers
+
+        assertFormatEquals(t, "%EC", "20");
+        assertFormatEquals(t, "%OC", "20");
+
+        assertFormatEquals(t, "%_+", "mer. juin  1 12:30:15 GMT+01:00 2005");
+        assertFormatEquals(t, "%-+", "mer. juin  1 12:30:15 GMT+01:00 2005");
+        assertFormatEquals(t, "%0+", "mer. juin  1 12:30:15 GMT+01:00 2005");
+        assertFormatEquals(t, "%^+", "mer. juin  1 12:30:15 GMT+01:00 2005");
+        assertFormatEquals(t, "%#+", "mer. juin  1 12:30:15 GMT+01:00 2005");
+
+        assertFormatEquals(t, "%_A", "mercredi");
+        assertFormatEquals(t, "%-A", "mercredi");
+        assertFormatEquals(t, "%0A", "mercredi");
+        assertFormatEquals(t, "%^A", "MERCREDI");
+        assertFormatEquals(t, "%#A", "MERCREDI");
+
+        assertFormatEquals(t, "%_Y", "20 5");
+        assertFormatEquals(t, "%-Y", "205");
+        assertFormatEquals(t, "%0Y", "2005");
+        assertFormatEquals(t, "%^Y", "2005");
+        assertFormatEquals(t, "%#Y", "2005");
+
+        assertFormatEquals(t, "%_d", " 1");
+        assertFormatEquals(t, "%-d", "1");
+        assertFormatEquals(t, "%0d", "01");
+        assertFormatEquals(t, "%^d", "01");
+        assertFormatEquals(t, "%#d", "01");
+    }
+
+    public void testFormat_tokensJapanLocale() throws Exception {
+        Locale.setDefault(Locale.JAPAN);
+
+        Time t = new Time("Asia/Tokyo");
+        Fields.setDateTime(t, 2005, 5, 1, 12, 30, 15);
+
+        // Prove the un-normalized fields are used.
+        assertFormatEquals(t, "%A", "日曜日");
+
+        // Set fields like weekday.
+        t.normalize(true);
+
+        assertFormatEquals(t, "%A", "水曜日");
+        assertFormatEquals(t, "%a", "水");
+        assertFormatEquals(t, "%B", "6月");
+        assertFormatEquals(t, "%b", "6月");
+        assertFormatEquals(t, "%C", "20");
+        assertFormatEquals(t, "%c", "1 6月 2005, 12:30:15");
+        assertFormatEquals(t, "%D", "06/01/05");
+        assertFormatEquals(t, "%d", "01");
+        assertFormatEquals(t, "%E", "E");
+        assertFormatEquals(t, "%e", " 1");
+        assertFormatEquals(t, "%F", "2005-06-01");
+        assertFormatEquals(t, "%G", "2005");
+        assertFormatEquals(t, "%g", "05");
+        assertFormatEquals(t, "%H", "12");
+        assertFormatEquals(t, "%h", "6月");
+        assertFormatEquals(t, "%I", "12");
+        assertFormatEquals(t, "%j", "152");
+        assertFormatEquals(t, "%k", "12");
+        assertFormatEquals(t, "%l", "12");
+        assertFormatEquals(t, "%M", "30");
+        assertFormatEquals(t, "%m", "06");
+        assertFormatEquals(t, "%n", "\n");
+        assertFormatEquals(t, "%O", "O");
+        assertFormatEquals(t, "%p", "午後");
+        assertFormatEquals(t, "%P", "午後");
+        assertFormatEquals(t, "%R", "12:30");
+        assertFormatEquals(t, "%r", "12:30:15 午後");
+        assertFormatEquals(t, "%S", "15");
+        // The original C implementation uses the (native) system default TZ, not the timezone of
+        // the Time and is therefore not stable. The commented value below is the one using the
+        // Time's timezone which is probably the expected answer.
+        // assertFormatEquals(t, "%s", "1117596615");
+        assertFormatEquals(t, "%T", "12:30:15");
+        assertFormatEquals(t, "%t", "\t");
+        assertFormatEquals(t, "%U", "22");
+        assertFormatEquals(t, "%u", "3");
+        assertFormatEquals(t, "%V", "22");
+        assertFormatEquals(t, "%v", " 1-6月-2005");
+        assertFormatEquals(t, "%W", "22");
+        assertFormatEquals(t, "%w", "3");
+        assertFormatEquals(t, "%X", "12:30:15");
+        assertFormatEquals(t, "%x", "1 6月 2005");
+        assertFormatEquals(t, "%y", "05");
+        assertFormatEquals(t, "%Y", "2005");
+        assertFormatEquals(t, "%Z", "JST");
+        assertFormatEquals(t, "%z", "+0900");
+        assertFormatEquals(t, "%+", "水 6月  1 12:30:15 JST 2005");
+        assertFormatEquals(t, "%%", "%");
+
+        // Modifiers
+
+        assertFormatEquals(t, "%EC", "20");
+        assertFormatEquals(t, "%OC", "20");
+
+        assertFormatEquals(t, "%_+", "水 6月  1 12:30:15 JST 2005");
+        assertFormatEquals(t, "%-+", "水 6月  1 12:30:15 JST 2005");
+        assertFormatEquals(t, "%0+", "水 6月  1 12:30:15 JST 2005");
+        assertFormatEquals(t, "%^+", "水 6月  1 12:30:15 JST 2005");
+        assertFormatEquals(t, "%#+", "水 6月  1 12:30:15 JST 2005");
+
+        assertFormatEquals(t, "%_A", "水曜日");
+        assertFormatEquals(t, "%-A", "水曜日");
+        assertFormatEquals(t, "%0A", "水曜日");
+        assertFormatEquals(t, "%^A", "水曜日");
+        assertFormatEquals(t, "%#A", "水曜日");
+
+        assertFormatEquals(t, "%_Y", "20 5");
+        assertFormatEquals(t, "%-Y", "205");
+        assertFormatEquals(t, "%0Y", "2005");
+        assertFormatEquals(t, "%^Y", "2005");
+        assertFormatEquals(t, "%#Y", "2005");
+
+        assertFormatEquals(t, "%_d", " 1");
+        assertFormatEquals(t, "%-d", "1");
+        assertFormatEquals(t, "%0d", "01");
+        assertFormatEquals(t, "%^d", "01");
+        assertFormatEquals(t, "%#d", "01");
+    }
+
+    public void testFormat2445() throws Exception {
+        Time t = new Time(Time.TIMEZONE_UTC);
+        Fields.setDateTime(t, 2005, 5, 1, 12, 30, 15);
+
+        // UTC behavior is to add a trailing Z.
+        String expected = t.format("%Y%m%dT%H%M%SZ");
+        assertEquals(expected, t.format2445());
+
+        // Only UTC includes the Z.
+        t.timezone = "America/Los_Angeles";
+        expected = t.format("%Y%m%dT%H%M%S");
+        assertEquals(expected, t.format2445());
+
+        // There is odd behavior around negative values.
+        Fields.setDateTime(t, 2005, -1, -1, -1, -1, -1);
+        assertEquals("2005000 T0 0 0 ", t.format2445());
+    }
+
+    public void testFormat2445_doesNotNormalize() throws Exception {
+        Time t = new Time(Time.TIMEZONE_UTC);
+        Fields.set(t, 2005, 13, 32, 25, 61, 61, -2, -2, -2, -2);
+
+        Time tCopy = new Time(t);
+        Fields.assertTimeEquals(t, tCopy);
+
+        assertEquals("20051432T256161Z", t.format2445());
+        Fields.assertTimeEquals(t, tCopy);
+
+        t.timezone = tCopy.timezone = "America/Los_Angeles";
+        assertEquals("20051432T256161", t.format2445());
+        Fields.assertTimeEquals(t, tCopy);
+    }
+
+    public void testToString() throws Exception {
+        Time t = new Time(Time.TIMEZONE_UTC);
+        assertEquals("19700101T000000UTC(0,0,0,-1,0)", t.toString());
+
+        t.timezone = "America/Los_Angeles";
+        assertEquals("19700101T000000America/Los_Angeles(0,0,0,-1,28800)", t.toString());
+    }
+
+    public void testToString_doesNotNormalize() throws Exception {
+        Time t = new Time(Time.TIMEZONE_UTC);
+        Fields.set(t, 2005, 13, 32, -1, -1, -1, -2, -2, -2, -2);
+
+        Time tCopy = new Time(t);
+        Fields.assertTimeEquals(t, tCopy);
+
+        String r = t.toString();
+        assertEquals("20051432T-1-1-1UTC(-2,-2,-2,-2,1141426739)", r);
+
+        Fields.assertTimeEquals(t, tCopy);
+    }
+
+    public void testGetCurrentTimezone() throws Exception {
+        String r = Time.getCurrentTimezone();
+        assertEquals(TimeZone.getDefault().getID(), r);
+    }
+
+    public void testSetToNow() throws Exception {
+        Time t = new Time(Time.TIMEZONE_UTC);
+
+        // Time works in seconds so all millis values have to be divided by 1000, otherwise
+        // the greater accuracy of System.currentTimeMillis() causes the test to fail.
+
+        long lowerBound = System.currentTimeMillis() / 1000;
+
         t.setToNow();
-        long time = t.toMillis(false) / 1000;
-        // 1 sec of delta can happen
-        if (Math.abs(currentTime - time) > 1) {
-            fail("currentTime " + currentTime + " time " + time);
+
+        long upperBound = System.currentTimeMillis() / 1000;
+
+        long actual = t.toMillis(true /* ignore isDst */) / 1000;
+        assertTrue(lowerBound <= actual && actual <= upperBound);
+    }
+
+    public void testToMillis_utc() throws Exception {
+        Time t = new Time(Time.TIMEZONE_UTC);
+
+        long winterTimeUtcMillis = 1167613323000L;
+
+        Fields.set(t, 2007, 0, 1, 1, 2, 3, 0 /* isDst */, 1, 9, 9);
+        long r = t.toMillis(true /* ignore isDst */);
+        assertEquals(winterTimeUtcMillis, r);
+
+        Fields.set(t, 2007, 0, 1, 1, 2, 3, 1 /* isDst */, 1, 9, 9);
+        r = t.toMillis(true /* ignore isDst */);
+        assertEquals(winterTimeUtcMillis, r);
+
+        Fields.set(t, 2007, 0, 1, 1, 2, 3, -1 /* isDst */, 1, 9, 9);
+        r = t.toMillis(true /* ignore isDst */);
+        assertEquals(winterTimeUtcMillis, r);
+
+        Fields.set(t, 2007, 0, 1, 1, 2, 3, 0 /* isDst */, 1, 9, 9);
+        r = t.toMillis(false /* ignore isDst */);
+        assertEquals(winterTimeUtcMillis, r);
+
+        Fields.set(t, 2007, 0, 1, 1, 2, 3, 1 /* isDst */, 1, 9, 9);
+        r = t.toMillis(false /* ignore isDst */);
+        assertEquals(-1, r);
+
+        Fields.set(t, 2007, 0, 1, 1, 2, 3, -1 /* isDst */, 1, 9, 9);
+        r = t.toMillis(false /* ignore isDst */);
+        assertEquals(winterTimeUtcMillis, r);
+
+        long summerTimeUtcMillis = 1180659723000L;
+
+        Fields.set(t, 2007, 5, 1, 1, 2, 3, 0 /* isDst */, 1, 9, 9);
+        r = t.toMillis(true /* ignore isDst */);
+        assertEquals(summerTimeUtcMillis, r);
+
+        Fields.set(t, 2007, 5, 1, 1, 2, 3, 1 /* isDst */, 1, 9, 9);
+        r = t.toMillis(true /* ignore isDst */);
+        assertEquals(summerTimeUtcMillis, r);
+
+        Fields.set(t, 2007, 5, 1, 1, 2, 3, -1 /* isDst */, 1, 9, 9);
+        r = t.toMillis(true /* ignore isDst */);
+        assertEquals(summerTimeUtcMillis, r);
+
+        Fields.set(t, 2007, 5, 1, 1, 2, 3, 0 /* isDst */, 1, 9, 9);
+        r = t.toMillis(false /* ignore isDst */);
+        assertEquals(summerTimeUtcMillis, r);
+
+        Fields.set(t, 2007, 5, 1, 1, 2, 3, 1 /* isDst */, 1, 9, 9);
+        r = t.toMillis(false /* ignore isDst */);
+        assertEquals(-1, r);
+
+        Fields.set(t, 2007, 5, 1, 1, 2, 3, -1 /* isDst */, 1, 9, 9);
+        r = t.toMillis(false /* ignore isDst */);
+        assertEquals(summerTimeUtcMillis, r);
+    }
+
+    public void testToMillis_dstTz() throws Exception {
+        Time t = new Time(PstPdt.ID);
+
+        // A STD time
+        long stdTimeUtcMillis = 1167613323000L;
+        long stdTimeMillis = stdTimeUtcMillis - PstPdt.getUtcOffsetMillis(false);
+
+        Fields.set(t, 2007, 0, 1, 1, 2, 3, 0 /* isDst */, 9, 9, 9);
+        long r = t.toMillis(true /* ignore isDst */);
+        assertEquals(stdTimeMillis, r);
+
+        Fields.set(t, 2007, 0, 1, 1, 2, 3, 1 /* isDst */, 9, 9, 9);
+        assertToMillisResult(true, t, stdTimeMillis);
+
+        Fields.set(t, 2007, 0, 1, 1, 2, 3, -1 /* isDst */, 9, 9, 9);
+        assertToMillisResult(true, t, stdTimeMillis);
+
+        long dstToStdCorrectionMillis =
+                PstPdt.getUtcOffsetMillis(false) - PstPdt.getUtcOffsetMillis(true);
+
+        Fields.set(t, 2007, 0, 1, 1, 2, 3, 0 /* isDst */, 9, 9, 9);
+        assertToMillisResult(false, t, stdTimeMillis);
+
+        Fields.set(t, 2007, 0, 1, 1, 2, 3, 1 /* isDst */, 9, 9, 9);
+        assertToMillisResult(false, t, stdTimeMillis + dstToStdCorrectionMillis);
+
+        Fields.set(t, 2007, 0, 1, 1, 2, 3, -1 /* isDst */, 9, 9, 9);
+        assertToMillisResult(false, t, stdTimeMillis);
+
+        // A DST time
+        long dstTimeUtcMillis = 1180659723000L;
+        long dstTimeMillis = dstTimeUtcMillis - PstPdt.getUtcOffsetMillis(true);
+
+        Fields.set(t, 2007, 5, 1, 1, 2, 3, 0 /* isDst */, 9, 9, 9);
+        assertToMillisResult(true, t, dstTimeMillis);
+
+        Fields.set(t, 2007, 5, 1, 1, 2, 3, 1 /* isDst */, 9, 9, 9);
+        assertToMillisResult(true, t, dstTimeMillis);
+
+        Fields.set(t, 2007, 5, 1, 1, 2, 3, -1 /* isDst */, 9, 9, 9);
+        assertToMillisResult(true, t, dstTimeMillis);
+
+        long stdToDstCorrectionMillis = -dstToStdCorrectionMillis;
+
+        Fields.set(t, 2007, 5, 1, 1, 2, 3, 0 /* isDst */, 9, 9, 9);
+        assertToMillisResult(false, t, dstTimeMillis + stdToDstCorrectionMillis);
+
+        Fields.set(t, 2007, 5, 1, 1, 2, 3, 1 /* isDst */, 9, 9, 9);
+        assertToMillisResult(false, t, dstTimeMillis);
+
+        Fields.set(t, 2007, 5, 1, 1, 2, 3, -1 /* isDst */, 9, 9, 9);
+        assertToMillisResult(false, t, dstTimeMillis);
+    }
+
+    private static void assertToMillisResult(boolean toMillisArgument, Time t, long expectedResult) {
+        long r = t.toMillis(toMillisArgument /* ignore isDst */);
+        assertEquals(expectedResult, r);
+    }
+
+    public void testToMillis_doesNotNormalize() {
+        Time t = new Time(Time.TIMEZONE_UTC);
+
+        Fields.set(t, 2007, 13, 32, 25, 60, 60, -2 /* isDst */, Integer.MAX_VALUE, 367, 7);
+
+        Time originalTime = new Time(t);
+        Fields.assertTimeEquals(t, originalTime);
+
+        t.toMillis(true);
+        Fields.assertTimeEquals(originalTime, t);
+
+        t.toMillis(false);
+        Fields.assertTimeEquals(originalTime, t);
+    }
+
+    public void testToMillis_skippedTime() {
+        // Tests behavior around a transition from STD to DST that introduces an hour of "skipped"
+        // time from 01:00 to 01:59.
+        String timezone = PstPdt.ID;
+        long stdBaseTimeMillis = 1173607200000L;
+        long dstBaseTimeMillis = 1173603600000L;
+
+        // Try each minute from one minute before the skipped hour until one after.
+        for (int i = -1; i <= 60; i++) {
+            int minutesInMillis = i * 60000;
+            int[] timeFields = new int[] { 2007, 2, 11, 2, i, 0, -999 /* not used */, 9, 9, 9 };
+
+            Time time = new Time(timezone);
+
+            // isDst = 0, toMillis(true)
+            Fields.set(time, timeFields);
+            time.isDst = 0;
+            long expectedTimeMillis;
+            if (i == -1) {
+                expectedTimeMillis = stdBaseTimeMillis + minutesInMillis;
+            } else if (i == 60) {
+                expectedTimeMillis = dstBaseTimeMillis + minutesInMillis;
+            } else {
+                expectedTimeMillis = -1;
+            }
+            assertToMillisResult(true, time, expectedTimeMillis);
+
+            // isDst = 0, toMillis(false)
+            Fields.set(time, timeFields);
+            time.isDst = 0;
+            expectedTimeMillis = stdBaseTimeMillis + minutesInMillis;
+            assertToMillisResult(false, time, expectedTimeMillis);
+
+            // isDst = 1, toMillis(true)
+            Fields.set(time, timeFields);
+            time.isDst = 1;
+            if (i == -1) {
+                expectedTimeMillis = stdBaseTimeMillis + minutesInMillis;
+            } else if (i == 60) {
+                expectedTimeMillis = dstBaseTimeMillis + minutesInMillis;
+            } else {
+                expectedTimeMillis = -1;
+            }
+            assertToMillisResult(true, time, expectedTimeMillis);
+
+            // isDst = 1, toMillis(false)
+            Fields.set(time, timeFields);
+            time.isDst = 1;
+            expectedTimeMillis = dstBaseTimeMillis + minutesInMillis;
+            assertToMillisResult(false, time, expectedTimeMillis);
+
+            // isDst = -1, toMillis(true)
+            Fields.set(time, timeFields);
+            time.isDst = -1;
+
+            if (i == -1) {
+                expectedTimeMillis = stdBaseTimeMillis + minutesInMillis;
+            } else if (i == 60) {
+                expectedTimeMillis = dstBaseTimeMillis + minutesInMillis;
+            } else {
+                expectedTimeMillis = -1;
+            }
+            assertToMillisResult(false, time, expectedTimeMillis);
+
+            // isDst = -1, toMillis(false)
+            Fields.set(time, timeFields);
+            time.isDst = -1;
+            assertToMillisResult(false, time, expectedTimeMillis);
         }
     }
 
-    public void testMillis0() throws Exception {
-        Time t = new Time(Time.TIMEZONE_UTC);
-        t.set(0, 0, 0, 1, 1, 2006);
-        long r = t.toMillis(true);
-        assertEquals(1138752000000l, r);
+    public void testToMillis_duplicateWallTime() {
+        // 1:00 in standard / 2:00 in DST
+        long timeBaseMillis = 1194163200000L;
+        long dstCorrectionMillis = 3600000;
 
-        t.set(1, 0, 0, 1, 1, 2006);
-        r = t.toMillis(true);
-        assertEquals(1138752001000l, r);
+        // Try each minute from one minute before the duplicated hour until one after.
+        for (int i = -1; i <= 60; i++) {
+            int minutesInMillis = i * 60000;
 
-        t.set(1, 0, 0, 1, 0, 1970);
-        r = t.toMillis(true);
-        assertEquals(1000, r);
+            Time time = new Time(PstPdt.ID);
+            int[] timeFields = new int[] { 2007, 10, 4, 1, i, 0, -999 /* not used */, 9, 9, 9};
+
+            // isDst = 0, toMillis(true)
+            Fields.set(time, timeFields);
+            time.isDst = 0;
+            long timeMillis = time.toMillis(true);
+            if (i == -1) {
+                assertEquals("i = " + i, timeBaseMillis + minutesInMillis, timeMillis);
+            } else if (i == 60) {
+                assertEquals("i = " + i, timeBaseMillis + minutesInMillis + dstCorrectionMillis,
+                        timeMillis);
+            } else {
+                // When ignoreDst the choice between DST and STD is arbitrary when both are
+                // possible.
+                assertTrue("i = " + i, timeMillis == timeBaseMillis + minutesInMillis
+                        || timeMillis == timeBaseMillis + minutesInMillis + dstCorrectionMillis);
+            }
+
+            // isDst = 0, toMillis(false)
+            Fields.set(time, timeFields);
+            time.isDst = 0;
+            assertToMillisResult(false, time,
+                    timeBaseMillis + minutesInMillis + dstCorrectionMillis);
+
+            // isDst = 1, toMillis(true)
+            Fields.set(time, timeFields);
+            time.isDst = 1;
+            if (i == -1) {
+                assertEquals("i = " + i, timeBaseMillis + minutesInMillis, timeMillis);
+            } else if (i == 60) {
+                assertEquals("i = " + i, timeBaseMillis + minutesInMillis + dstCorrectionMillis,
+                        timeMillis);
+            } else {
+                // When ignoreDst or isDst == -1 the choice between DST and STD is arbitrary when
+                // both are possible.
+                assertTrue("i = " + i, timeMillis == timeBaseMillis + minutesInMillis
+                        || timeMillis == timeBaseMillis + minutesInMillis + dstCorrectionMillis);
+            }
+
+            // isDst = 1, toMillis(false)
+            Fields.set(time, timeFields);
+            time.isDst = 1;
+            assertToMillisResult(false, time, timeBaseMillis + minutesInMillis);
+
+            // isDst = -1, toMillis(true)
+            Fields.set(time, timeFields);
+            time.isDst = -1;
+            timeMillis = time.toMillis(true);
+            if (i == -1) {
+                assertEquals("i = " + i, timeBaseMillis + minutesInMillis, timeMillis);
+            } else if (i == 60) {
+                assertEquals("i = " + i, timeBaseMillis + minutesInMillis + dstCorrectionMillis,
+                        timeMillis);
+            } else {
+                // When ignoreDst or isDst == -1 the choice between DST and STD is arbitrary when
+                // both are possible.
+                assertTrue("i = " + i, timeMillis == timeBaseMillis + minutesInMillis
+                        || timeMillis == timeBaseMillis + minutesInMillis + dstCorrectionMillis);
+            }
+
+            // isDst = -1, toMillis(false)
+            Fields.set(time, timeFields);
+            time.isDst = -1;
+            timeMillis = time.toMillis(false);
+            if (i == -1) {
+                assertEquals("i = " + i, timeBaseMillis + minutesInMillis, timeMillis);
+            } else if (i == 60) {
+                assertEquals("i = " + i, timeBaseMillis + minutesInMillis + dstCorrectionMillis,
+                        timeMillis);
+            } else {
+                // When ignoreDst or isDst == -1 the choice between DST and STD is arbitrary when
+                // both are possible.
+                assertTrue("i = " + i, timeMillis == timeBaseMillis + minutesInMillis
+                        || timeMillis == timeBaseMillis + minutesInMillis + dstCorrectionMillis);
+            }
+        }
     }
 
-    public void testParse0() throws Exception {
+    public void testToMillis_beforeTzRecords() {
+        int[] timeFields = new int[] { 1900, 0, 1, 2, 3, 4, -999 /* not used */, 9, 9, 9 };
+        assertToMillisInvalid(timeFields, PstPdt.ID);
+        assertToMillisInvalid(timeFields, Time.TIMEZONE_UTC);
+    }
+
+    private static void assertToMillisInvalid(int[] timeFields, String timezone) {
+        Time time = new Time(timezone);
+
+        // isDst = 0, toMillis(true)
+        Fields.set(time, timeFields);
+        time.isDst = 0;
+        assertToMillisResult(true, time, -1);
+
+        // isDst = 0, toMillis(false)
+        Fields.set(time, timeFields);
+        time.isDst = 0;
+        assertToMillisResult(false, time, -1);
+
+        // isDst = 1, toMillis(true)
+        Fields.set(time, timeFields);
+        time.isDst = 1;
+        assertToMillisResult(true, time, -1);
+
+        // isDst = 1, toMillis(false)
+        Fields.set(time, timeFields);
+        time.isDst = 1;
+        assertToMillisResult(false, time, -1);
+
+        // isDst = -1, toMillis(true)
+        Fields.set(time, timeFields);
+        time.isDst = -1;
+        assertToMillisResult(true, time, -1);
+
+        // isDst = -1, toMillis(false)
+        Fields.set(time, timeFields);
+        time.isDst = -1;
+        assertToMillisResult(false, time, -1);
+    }
+
+    public void testToMillis_afterTzRecords() {
+        int[] timeFields = new int[] { 2039, 0, 1, 2, 3, 4, -999 /* not used */, 9, 9, 9 };
+        assertToMillisInvalid(timeFields, PstPdt.ID);
+        assertToMillisInvalid(timeFields, Time.TIMEZONE_UTC);
+    }
+
+    public void testToMillis_invalid() {
+        int[] timeFields = new int[] { 0, 0, 0, 0, 0, 0, -999 /* not used */, 9, 9, 9 };
+        assertToMillisInvalid(timeFields, PstPdt.ID);
+        assertToMillisInvalid(timeFields, Time.TIMEZONE_UTC);
+    }
+
+    public void testParse_date() throws Exception {
+        String nonUtcTz = PstPdt.ID;
+        Time t = new Time(nonUtcTz);
+        assertFalse(t.parse("12345678"));
+        Time expected = new Time(nonUtcTz);
+        Fields.setAllDayDate(expected, 1234, 55, 78);
+        Fields.setDst(expected, -1 /* isDst */, 0);
+        Fields.setDerivedDateTime(expected, 0, 0);
+        Fields.assertTimeEquals(expected, t);
+    }
+
+    public void testParse_null() throws Exception {
         Time t = new Time(Time.TIMEZONE_UTC);
+        try {
+            t.parse(null);
+            fail();
+        } catch (NullPointerException e) {
+        }
+    }
+
+    public void testParse() throws Exception {
+        Time t = new Time(Time.TIMEZONE_UTC);
+        t.parse("20061005T120000");
+
+        Time expected = new Time(Time.TIMEZONE_UTC);
+        Fields.set(expected, 2006, 9, 5, 12, 0, 0, -1 /* isDst */, 0, 0, 0);
+        Fields.assertTimeEquals(expected, t);
+    }
+
+    public void testParse_dateTime() throws Exception {
+        String nonUtcTz = PstPdt.ID;
+        Time t = new Time(nonUtcTz);
         assertFalse(t.parse("12345678T901234"));
-        assertTrue(t.parse("20081013T160000Z"));
-        assertEquals(2008, t.year);
-        assertEquals(9, t.month);
-        assertEquals(13, t.monthDay);
-        assertEquals(16, t.hour);
-        assertEquals(0, t.minute);
-        assertEquals(0, t.second);
+        Time expected = new Time(nonUtcTz);
+        Fields.set(expected, 1234, 55, 78, 90, 12, 34, -1 /* isDst */, 0, 0, 0);
+        Fields.assertTimeEquals(expected, t);
 
+        Time t2 = new Time(nonUtcTz);
+        assertTrue(t2.parse("12345678T901234Z"));
+        Time utcExpected = new Time(Time.TIMEZONE_UTC);
+        Fields.set(utcExpected, 1234, 55, 78, 90, 12, 34, -1 /* isDst */, 0, 0, 0);
+        Fields.assertTimeEquals(utcExpected, t2);
     }
 
-    public void testSet0() throws Exception {
-        Time t = new Time(Time.TIMEZONE_UTC);
-        long time = System.currentTimeMillis();
-        Calendar date = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
-        date.setTimeInMillis(time);
-        t.set(time);
-        assertEquals(date.get(Calendar.YEAR), t.year);
-        assertEquals(date.get(Calendar.MONTH), t.month);
-        assertEquals(date.get(Calendar.DAY_OF_MONTH), t.monthDay);
-        assertEquals(date.get(Calendar.HOUR_OF_DAY), t.hour);
-        assertEquals(date.get(Calendar.MINUTE), t.minute);
-        assertEquals(date.get(Calendar.SECOND), t.second);
+    public void testParse_errors() throws Exception {
+        String nonUtcTz = PstPdt.ID;
+        try {
+            Time t = new Time(nonUtcTz);
+            t.parse(null);
+            fail();
+        } catch (NullPointerException e) {
+        }
+
+        // Too short
+        assertParseError("");
+        assertParseError("1");
+        assertParseError("12");
+        assertParseError("123");
+        assertParseError("1234");
+        assertParseError("12345");
+        assertParseError("123456");
+        assertParseError("1234567");
+
+        // No "T" in the expected place
+        assertParseError("12345678S");
+
+        // Invalid character in the first 8 characters.
+        assertParseError("12X45678");
+
+        // Too short for a date/time (15 or 16 characters allowed)
+        assertParseError("12345678T");
+        assertParseError("12345678T0");
+        assertParseError("12345678T01");
+        assertParseError("12345678T012");
+        assertParseError("12345678T0123");
+        assertParseError("12345678T01234");
+
+        // Invalid character
+        assertParseError("12345678T0X2345");
     }
 
-    public void testSet1() throws Exception {
-        final int year = 2008;
-        final int month = 6;
-        final int date = 12;
-        final int hour = 16;
-        final int minute = 21;
-        final int second = 35;
+    private static void assertParseError(String s) {
         Time t = new Time(Time.TIMEZONE_UTC);
-        t.set(second, minute, hour, date, month, year);
-        assertEquals(year, t.year);
-        assertEquals(month, t.month);
-        assertEquals(date, t.monthDay);
-        assertEquals(hour, t.hour);
-        assertEquals(minute, t.minute);
-        assertEquals(second, t.second);
+        try {
+            t.parse(s);
+            fail();
+        } catch (TimeFormatException expected) {
+        }
+    }
+
+    public void testParse3339() throws Exception {
+        String tz = Time.TIMEZONE_UTC;
+        Time expected = new Time(tz);
+        Fields.setAllDayDate(expected, 1980, 4, 23);
+        Fields.setDst(expected, -1 /* isDst */, 0);
+        Fields.setDerivedDateTime(expected, 0, 0);
+        assertParse3339Succeeds(tz, "1980-05-23", expected);
+
+        Fields.setDateTime(expected, 1980, 4, 23, 9, 50, 50);
+        Fields.setDst(expected, -1 /* isDst */, 0);
+        Fields.setDerivedDateTime(expected, 0, 0);
+        assertParse3339Succeeds(tz, "1980-05-23T09:50:50", expected);
+
+        Fields.setDateTime(expected, 1980, 4, 23, 9, 50, 50);
+        Fields.setDst(expected, -1 /* isDst */, 0);
+        Fields.setDerivedDateTime(expected, 0, 0);
+        assertParse3339Succeeds(tz, "1980-05-23T09:50:50Z", expected);
+
+        Fields.setDateTime(expected, 1980, 4, 23, 9, 50, 50);
+        Fields.setDst(expected, -1 /* isDst */, 0);
+        Fields.setDerivedDateTime(expected, 0, 0);
+        assertParse3339Succeeds(tz, "1980-05-23T09:50:50.0Z", expected);
+
+        Fields.setDateTime(expected, 1980, 4, 23, 9, 50, 50);
+        Fields.setDst(expected, -1 /* isDst */, 0);
+        Fields.setDerivedDateTime(expected, 0, 0);
+        assertParse3339Succeeds(tz, "1980-05-23T09:50:50.12Z", expected);
+
+        Fields.setDateTime(expected, 1980, 4, 23, 9, 50, 50);
+        Fields.setDst(expected, -1 /* isDst */, 0);
+        Fields.setDerivedDateTime(expected, 0, 0);
+        assertParse3339Succeeds(tz, "1980-05-23T09:50:50.123Z", expected);
+
+        // The time should be normalized to UTC
+        Fields.setDateTime(expected, 1980, 4, 23, 10, 55, 50);
+        Fields.setDst(expected, -1 /* isDst */, 0);
+        Fields.setDerivedDateTime(expected, 0, 0);
+        assertParse3339Succeeds(tz, "1980-05-23T09:50:50-01:05", expected);
+
+        // The time should be normalized to UTC
+        Fields.setDateTime(expected, 1980, 4, 23, 10, 55, 50);
+        Fields.setDst(expected, -1 /* isDst */, 0);
+        Fields.setDerivedDateTime(expected, 0, 0);
+        assertParse3339Succeeds(tz, "1980-05-23T09:50:50.123-01:05", expected);
+    }
+
+    private static void assertParse3339Succeeds(String timeZone, String toParse, Time expected) {
+        Time t = new Time(timeZone);
+        t.parse3339(toParse);
+        Fields.assertTimeEquals(expected, t);
+    }
+
+    public void testParse3339_parseErrors() {
+        // Too short
+        assertParse3339Error("1980");
+
+        // Timezone too short
+        assertParse3339Error("1980-05-23T09:50:50.123+");
+        assertParse3339Error("1980-05-23T09:50:50.123+05:0");
+    }
+
+    public void testParse3339_null() {
+        Time t = new Time(Time.TIMEZONE_UTC);
+        try {
+            t.parse3339(null);
+            fail();
+        } catch (NullPointerException e) {
+        }
+    }
+
+    private void assertParse3339Error(String s) {
+        String tz = Time.TIMEZONE_UTC;
+        Time t = new Time(tz);
+        try {
+            t.parse3339(s);
+            fail();
+        } catch (TimeFormatException expected) {
+        }
+    }
+
+    public void testSetMillis_utc() throws Exception {
+        String tz = Time.TIMEZONE_UTC;
+        Time t = new Time(tz);
+        t.set(1000L);
+
+        Time expected = new Time(tz);
+        Fields.set(expected, 1970, 0, 1, 0, 0, 1, 0 /* isDst */, 0, 0, 4);
+        Fields.assertTimeEquals(expected, t);
+
+        t.set(2000L);
+        Fields.set(expected, 1970, 0, 1, 0, 0, 2, 0 /* isDst */, 0, 0, 4);
+        Fields.assertTimeEquals(expected, t);
+
+        t.set(1000L * 60);
+        Fields.set(expected, 1970, 0, 1, 0, 1, 0, 0 /* isDst */, 0, 0, 4);
+        Fields.assertTimeEquals(expected, t);
+
+        t.set((1000L * 60 * 60 * 24) + 1000L);
+        Fields.set(expected, 1970, 0, 2, 0, 0, 1, 0 /* isDst */, 0, 1, 5);
+        Fields.assertTimeEquals(expected, t);
+    }
+
+    public void testSetMillis_utc_edgeCases() throws Exception {
+        String tz = Time.TIMEZONE_UTC;
+        Time t = new Time(tz);
+        t.set(Integer.MAX_VALUE + 1L);
+
+        Time expected = new Time(tz);
+        // This a 32-bit int overflow bug.
+        Fields.set(expected, 1970, 0, 25, 20, 31, 23, 0 /* isDst */, 0, 24, 0);
+        Fields.assertTimeEquals(expected, t);
+
+        t = new Time(tz);
+        t.set(Integer.MIN_VALUE - 1L);
+        // This a 32-bit int underflow bug.
+        Fields.set(expected, 1969, 11, 7, 3, 28, 37, 0 /* isDst */, 0, 340, 0);
+        Fields.assertTimeEquals(expected, t);
+    }
+
+    public void testSetFields() throws Exception {
+        String tz = Time.TIMEZONE_UTC;
+        Time t = new Time(tz);
+        Fields.set(t, 9, 9, 9, 9, 9, 9, 9 /* isDst */, 9, 9, 9);
+
+        t.set(1, 2, 3, 4, 5, 6);
+
+        Time expected = new Time(tz);
+        Fields.set(expected, 6, 5, 4, 3, 2, 1, -1 /* isDst */, 0, 0, 0);
+        Fields.assertTimeEquals(expected, t);
     }
 
     // Timezones that cover the world.  Some GMT offsets occur more than
     // once in case some cities decide to change their GMT offset.
     private static final String[] mTimeZones = {
+        "UTC",
         "Pacific/Kiritimati",
         "Pacific/Enderbury",
         "Pacific/Fiji",
@@ -672,14 +1850,14 @@
                 // We leave the "month" as zero because we are changing the
                 // "monthDay" from 1 to 366. The call to normalize() will
                 // then change the "month" (but we don't really care).
-                time.set(0, 0, 0, monthDay, 0, 2008);
+                time.set(0, 0, 12, monthDay, 0, 2008);
                 time.timezone = mTimeZones[zoneIndex];
                 long millis = time.normalize(true);
                 if (zoneIndex == 0) {
                     Log.i(TAG, time.format("%B %d, %Y"));
                 }
 
-                // This is the Julian day for 12am for this day of the year
+                // This is the Julian day for 12pm for this day of the year
                 int julianDay = Time.getJulianDay(millis, time.gmtoff);
 
                 // Change the time during the day and check that we get the same
@@ -688,13 +1866,17 @@
                     for (int minute = 0; minute < 60; minute += 15) {
                         time.set(0, minute, hour, monthDay, 0, 2008);
                         millis = time.normalize(true);
-                        int day = Time.getJulianDay(millis, time.gmtoff);
-                        if (day != julianDay) {
-                            Log.e("TimeTest", "Julian day: " + day + " at time " + time.hour + ":"
-                                    + time.minute + " != today's Julian day: " + julianDay
-                                    + " timezone: " + time.timezone);
+                        if (millis == -1) {
+                            // millis == -1 means the wall time does not exist in the chosen
+                            // timezone due to a DST change. We cannot calculate a JulianDay for -1.
+                            continue;
                         }
-                        assertEquals(day, julianDay);
+
+                        int day = Time.getJulianDay(millis, time.gmtoff);
+                        assertEquals("Julian day: " + day + " at time "
+                                + time.hour + ":" + time.minute
+                                + " != today's Julian day: " + julianDay
+                                + " timezone: " + time.timezone, day, julianDay);
                     }
                 }
             }
@@ -711,12 +1893,13 @@
                 // We leave the "month" as zero because we are changing the
                 // "monthDay" from 1 to 366. The call to normalize() will
                 // then change the "month" (but we don't really care).
-                time.set(0, 0, 0, monthDay, 0, 2008);
+                time.set(0, 0, 12, monthDay, 0, 2008);
                 time.timezone = mTimeZones[zoneIndex];
                 long millis = time.normalize(true);
                 if (zoneIndex == 0) {
                     Log.i(TAG, time.format("%B %d, %Y"));
                 }
+                // This is the Julian day for 12pm for this day of the year
                 int julianDay = Time.getJulianDay(millis, time.gmtoff);
 
                 time.setJulianDay(julianDay);
@@ -732,14 +1915,929 @@
                 assertEquals(0, time.second);
 
                 millis = time.toMillis(false);
-                int day = Time.getJulianDay(millis, time.gmtoff);
-                if (day != julianDay) {
-                    Log.i(TAG, "Error: gmtoff " + (time.gmtoff / 3600.0) + " day "
-                            + julianDay + " millis " + millis + " " + time.format("%B %d, %Y")
-                            + " " + time.timezone);
+                if (millis == -1) {
+                    // millis == -1 means the wall time does not exist in the chosen
+                    // timezone due to a DST change. We cannot calculate a JulianDay for -1.
+                    continue;
                 }
-                assertEquals(day, julianDay);
+                int day = Time.getJulianDay(millis, time.gmtoff);
+                assertEquals("Error: gmtoff " + (time.gmtoff / 3600.0) + " day " + julianDay
+                                + " millis " + millis+ " " + time.format("%B %d, %Y")
+                                + " " + time.timezone,
+                        day, julianDay);
             }
         }
     }
+
+    public void testNormalize_utc() throws Exception {
+        Time t = new Time(Time.TIMEZONE_UTC);
+        Time expected = new Time(Time.TIMEZONE_UTC);
+
+        long winterTimeUtcMillis = 1167613323000L;
+
+        Fields.set(t, 2007, 0, 1, 1, 2, 3, 0 /* isDst */, 9, 9, 9);
+        Fields.set(expected, 2007, 0, 1, 1, 2, 3, 0 /* isDst */, 0, 0, 1);
+        assertNormalizeResult(true, t, expected, winterTimeUtcMillis);
+
+        Fields.set(t, 2007, 0, 1, 1, 2, 3, 1 /* isDst */, 9, 9, 9);
+        Fields.set(expected, 2007, 0, 1, 1, 2, 3, 0 /* isDst */, 0, 0, 1);
+        assertNormalizeResult(true, t, expected, winterTimeUtcMillis);
+
+        Fields.set(t, 2007, 0, 1, 1, 2, 3, -1 /* isDst */, 9, 9, 9);
+        Fields.set(expected, 2007, 0, 1, 1, 2, 3, 0 /* isDst */, 0, 0, 1);
+        assertNormalizeResult(true, t, expected, winterTimeUtcMillis);
+
+        Fields.set(t, 2007, 0, 1, 1, 2, 3, 0 /* isDst */, 9, 9, 9);
+        Fields.set(expected, 2007, 0, 1, 1, 2, 3, 0 /* isDst */, 0, 0, 1);
+        assertNormalizeResult(false, t, expected, winterTimeUtcMillis);
+
+        Fields.set(t, 2007, 0, 1, 1, 2, 3, 1 /* isDst */, 9, 9, 9);
+        Fields.set(expected, 2007, 0, 1, 1, 2, 3, 1 /* isDst */, 9, 9, 9);
+        assertNormalizeResult(false, t, expected, -1);
+
+        Fields.set(t, 2007, 0, 1, 1, 2, 3, -1 /* isDst */, 9, 9, 9);
+        Fields.set(expected, 2007, 0, 1, 1, 2, 3, 0 /* isDst */, 0, 0, 1);
+        assertNormalizeResult(false, t, expected, winterTimeUtcMillis);
+
+        long summerTimeUtcMillis = 1180659723000L;
+
+        Fields.set(t, 2007, 5, 1, 1, 2, 3, 0 /* isDst */, 9, 9, 9);
+        Fields.set(expected, 2007, 5, 1, 1, 2, 3, 0 /* isDst */, 0, 151, 5);
+        assertNormalizeResult(true, t, expected, summerTimeUtcMillis);
+
+        Fields.set(t, 2007, 5, 1, 1, 2, 3, 1 /* isDst */, 9, 9, 9);
+        Fields.set(expected, 2007, 5, 1, 1, 2, 3, 0 /* isDst */, 0, 151, 5);
+        assertNormalizeResult(true, t, expected, summerTimeUtcMillis);
+
+        Fields.set(t, 2007, 5, 1, 1, 2, 3, -1 /* isDst */, 9, 9, 9);
+        Fields.set(expected, 2007, 5, 1, 1, 2, 3, 0 /* isDst */, 0, 151, 5);
+        assertNormalizeResult(true, t, expected, summerTimeUtcMillis);
+
+        Fields.set(t, 2007, 5, 1, 1, 2, 3, 0 /* isDst */, 9, 9, 9);
+        Fields.set(expected, 2007, 5, 1, 1, 2, 3, 0 /* isDst */, 0, 151, 5);
+        assertNormalizeResult(false, t, expected, summerTimeUtcMillis);
+
+        Fields.set(t, 2007, 5, 1, 1, 2, 3, 1 /* isDst */, 9, 9, 9);
+        Fields.set(expected, 2007, 5, 1, 1, 2, 3, 1 /* isDst */, 9, 9, 9);
+        assertNormalizeResult(false, t, expected, -1);
+
+        Fields.set(t, 2007, 5, 1, 1, 2, 3, -1 /* isDst */, 1, 9, 9);
+        Fields.set(expected, 2007, 5, 1, 1, 2, 3, 0 /* isDst */, 0, 151, 5);
+        assertNormalizeResult(false, t, expected, summerTimeUtcMillis);
+    }
+
+    public void testNormalize_dstTz() throws Exception {
+        Time t = new Time(PstPdt.ID);
+        Time expected = new Time(PstPdt.ID);
+
+        // A STD time
+        long stdTimeUtcMillis = 1167613323000L;
+        long stdTimeMillis = stdTimeUtcMillis - PstPdt.getUtcOffsetMillis(false);
+
+        Fields.set(t, 2007, 0, 1, 1, 2, 3, 0 /* isDst */, 9, 9, 9);
+        Fields.set(expected, 2007, 0, 1, 1, 2, 3, 0 /* isDst */, -28800, 0, 1);
+        assertNormalizeResult(true, t, expected, stdTimeMillis);
+
+        Fields.set(t, 2007, 0, 1, 1, 2, 3, 1 /* isDst */, 9, 9, 9);
+        Fields.set(expected, 2007, 0, 1, 1, 2, 3, 0 /* isDst */, -28800, 0, 1);
+        assertNormalizeResult(true, t, expected, stdTimeMillis);
+
+        Fields.set(t, 2007, 0, 1, 1, 2, 3, -1 /* isDst */, 9, 9, 9);
+        Fields.set(expected, 2007, 0, 1, 1, 2, 3, 0 /* isDst */, -28800, 0, 1);
+        assertNormalizeResult(true, t, expected, stdTimeMillis);
+
+        long dstToStdCorrectionMillis =
+                PstPdt.getUtcOffsetMillis(false) - PstPdt.getUtcOffsetMillis(true);
+
+        Fields.set(t, 2007, 0, 1, 1, 2, 3, 0 /* isDst */, 9, 9, 9);
+        Fields.set(expected, 2007, 0, 1, 1, 2, 3, 0 /* isDst */, -28800, 0, 1);
+        assertNormalizeResult(false, t, expected, stdTimeMillis);
+
+        Fields.set(t, 2007, 0, 1, 1, 2, 3, 1 /* isDst */, 9, 9, 9);
+        Fields.set(expected, 2007, 0, 1, 0, 2, 3, 0 /* isDst */, -28800, 0, 1);
+        assertNormalizeResult(false, t, expected, stdTimeMillis + dstToStdCorrectionMillis);
+
+        Fields.set(t, 2007, 0, 1, 1, 2, 3, -1 /* isDst */, 9, 9, 9);
+        Fields.set(expected, 2007, 0, 1, 1, 2, 3, 0 /* isDst */, -28800, 0, 1);
+        assertNormalizeResult(false, t, expected, stdTimeMillis);
+
+        // A DST time
+        long dstTimeUtcMillis = 1180659723000L;
+        long dstTimeMillis = dstTimeUtcMillis - PstPdt.getUtcOffsetMillis(true);
+
+        Fields.set(t, 2007, 5, 1, 1, 2, 3, 0 /* isDst */, 9, 9, 9);
+        Fields.set(expected, 2007, 5, 1, 1, 2, 3, 1 /* isDst */, -25200, 151, 5);
+        assertNormalizeResult(true, t, expected, dstTimeMillis);
+
+        Fields.set(t, 2007, 5, 1, 1, 2, 3, 1 /* isDst */, 9, 9, 9);
+        Fields.set(expected, 2007, 5, 1, 1, 2, 3, 1 /* isDst */, -25200, 151, 5);
+        assertNormalizeResult(true, t, expected, dstTimeMillis);
+
+        Fields.set(t, 2007, 5, 1, 1, 2, 3, -1 /* isDst */, 9, 9, 9);
+        Fields.set(expected, 2007, 5, 1, 1, 2, 3, 1 /* isDst */, -25200, 151, 5);
+        assertNormalizeResult(true, t, expected, dstTimeMillis);
+
+        long stdToDstCorrectionMillis = -dstToStdCorrectionMillis;
+
+        Fields.set(t, 2007, 5, 1, 1, 2, 3, 0 /* isDst */, 9, 9, 9);
+        Fields.set(expected, 2007, 5, 1, 2, 2, 3, 1 /* isDst */, -25200, 151, 5);
+        assertNormalizeResult(false, t, expected, dstTimeMillis + stdToDstCorrectionMillis);
+
+        Fields.set(t, 2007, 5, 1, 1, 2, 3, 1 /* isDst */, -25200, 151, 5);
+        Fields.set(expected, 2007, 5, 1, 1, 2, 3, 1 /* isDst */, -25200, 151, 5);
+        assertNormalizeResult(false, t, expected, dstTimeMillis);
+
+        Fields.set(t, 2007, 5, 1, 1, 2, 3, -1 /* isDst */, -25200, 151, 5);
+        Fields.set(expected, 2007, 5, 1, 1, 2, 3, 1 /* isDst */, -25200, 151, 5);
+        assertNormalizeResult(false, t, expected, dstTimeMillis);
+    }
+
+    public void testNormalize_skippedTime() {
+        // Tests behavior around a transition from STD to DST that introduces an hour of "skipped"
+        // time from 01:00 to 01:59.
+        String timezone = PstPdt.ID;
+        long stdBaseTimeMillis = 1173607200000L;
+        long dstBaseTimeMillis = 1173603600000L;
+
+        // Try each minute from one minute before the skipped hour until one after.
+        for (int i = -1; i <= 60; i++) {
+            int minutesInMillis = i * 60000;
+            int[] dateTimeArgs = new int[] { 2007, 2, 11, 2, i, 0 };
+
+            int[] normalizedAdjustedBackwardDateTimeArgs;
+            int[] normalizedDateTimeArgs;
+            int[] normalizedAdjustedForwardDateTimeArgs;
+            if (i == -1) {
+                normalizedAdjustedBackwardDateTimeArgs = new int[] { 2007, 2, 11, 0, 59, 0 };
+                normalizedDateTimeArgs = new int[] { 2007, 2, 11, 1, 59, 0 };
+                normalizedAdjustedForwardDateTimeArgs = null;
+            } else if (i == 60) {
+                normalizedAdjustedBackwardDateTimeArgs = null;
+                normalizedDateTimeArgs = new int[] { 2007, 2, 11, 3, 0, 0 };
+                normalizedAdjustedForwardDateTimeArgs = new int[] { 2007, 2, 11, 4, 0, 0 };
+            } else {
+                normalizedAdjustedBackwardDateTimeArgs = new int[] { 2007, 2, 11, 1, i, 0 };
+                normalizedDateTimeArgs = null;
+                normalizedAdjustedForwardDateTimeArgs =  new int[] { 2007, 2, 11, 3, i, 0 };
+            }
+
+            Time time = new Time(timezone);
+            Time expected = new Time(timezone);
+
+            // isDst = 0, normalize(true)
+            Fields.setDateTime(time, dateTimeArgs);
+            Fields.setDst(time, 0 /* isDst */, 9);
+            Fields.setDerivedDateTime(time, 9, 9);
+
+            long timeMillis = time.normalize(true);
+            long expectedTimeMillis;
+            if (i == -1) {
+                expectedTimeMillis = stdBaseTimeMillis + minutesInMillis;
+                Fields.setDateTime(expected, normalizedDateTimeArgs);
+                Fields.setDst(expected, 0, PstPdt.getUtcOffsetSeconds(0));
+                Fields.setDerivedDateTime(expected, 69, 0);
+            } else if (i == 60) {
+                expectedTimeMillis = dstBaseTimeMillis + minutesInMillis;
+                Fields.setDateTime(expected, normalizedDateTimeArgs);
+                Fields.setDst(expected, 1, PstPdt.getUtcOffsetSeconds(1));
+                Fields.setDerivedDateTime(expected, 69, 0);
+            } else {
+                expectedTimeMillis = -1;
+                Fields.setDateTime(expected, dateTimeArgs);
+                Fields.setDst(expected, -1, 9);
+                Fields.setDerivedDateTime(expected, 9, 9);
+            }
+            assertEquals("i = " + i, expectedTimeMillis, timeMillis);
+            Fields.assertTimeEquals("i = " + i, expected, time);
+
+            // isDst = 0, normalize(false)
+            Fields.setDateTime(time, dateTimeArgs);
+            Fields.setDst(time, 0 /* isDst */, 9);
+            Fields.setDerivedDateTime(time, 9, 9);
+
+            timeMillis = time.normalize(false);
+            expectedTimeMillis = stdBaseTimeMillis + minutesInMillis;
+            assertEquals("i = " + i, expectedTimeMillis, timeMillis);
+            if (i == -1) {
+                Fields.setDateTime(expected, normalizedDateTimeArgs);
+                Fields.setDst(expected, 0, PstPdt.getUtcOffsetSeconds(0));
+            } else {
+                Fields.setDateTime(expected, normalizedAdjustedForwardDateTimeArgs);
+                Fields.setDst(expected, 1, PstPdt.getUtcOffsetSeconds(1));
+            }
+            Fields.setDerivedDateTime(expected, 69, 0);
+            Fields.assertTimeEquals("i = " + i, expected, time);
+
+            // isDst = 1, normalize(true)
+            Fields.setDateTime(time, dateTimeArgs);
+            Fields.setDst(time, 1 /* isDst */, 9);
+            Fields.setDerivedDateTime(time, 9, 9);
+
+            timeMillis = time.normalize(true);
+            if (i == -1) {
+                expectedTimeMillis = stdBaseTimeMillis + minutesInMillis;
+                Fields.setDateTime(expected, normalizedDateTimeArgs);
+                Fields.setDst(expected, 0, PstPdt.getUtcOffsetSeconds(0));
+                Fields.setDerivedDateTime(expected, 69, 0);
+            } else if (i == 60) {
+                expectedTimeMillis = dstBaseTimeMillis + minutesInMillis;
+                Fields.setDateTime(expected, normalizedDateTimeArgs);
+                Fields.setDst(expected, 1, PstPdt.getUtcOffsetSeconds(1));
+                Fields.setDerivedDateTime(expected, 69, 0);
+            } else {
+                expectedTimeMillis = -1;
+                Fields.setDateTime(expected, dateTimeArgs);
+                Fields.setDst(expected, -1, 9);
+                Fields.setDerivedDateTime(expected, 9, 9);
+            }
+            assertEquals("i = " + i, expectedTimeMillis, timeMillis);
+            Fields.assertTimeEquals("i = " + i, expected, time);
+
+            // isDst = 1, normalize(false)
+            Fields.setDateTime(time, dateTimeArgs);
+            Fields.setDst(time, 1 /* isDst */, 9);
+            Fields.setDerivedDateTime(time, 9, 9);
+
+            timeMillis = time.normalize(false);
+            expectedTimeMillis = dstBaseTimeMillis + minutesInMillis;
+            assertEquals("i = " + i, expectedTimeMillis, timeMillis);
+            if (i == 60) {
+                Fields.setDateTime(expected, normalizedDateTimeArgs);
+                Fields.setDst(expected, 1, PstPdt.getUtcOffsetSeconds(1));
+            } else {
+                Fields.setDateTime(expected, normalizedAdjustedBackwardDateTimeArgs);
+                Fields.setDst(expected, 0, PstPdt.getUtcOffsetSeconds(0));
+            }
+            Fields.setDerivedDateTime(expected, 69, 0);
+            Fields.assertTimeEquals("i = " + i, expected, time);
+
+            // isDst = -1, normalize(true)
+            Fields.setDateTime(time, dateTimeArgs);
+            Fields.setDst(time, -1 /* isDst */, 9);
+            Fields.setDerivedDateTime(time, 9, 9);
+
+            timeMillis = time.normalize(true);
+            if (i == -1) {
+                expectedTimeMillis = stdBaseTimeMillis + minutesInMillis;
+                Fields.setDateTime(expected, normalizedDateTimeArgs);
+                Fields.setDst(expected, 0, PstPdt.getUtcOffsetSeconds(0));
+                Fields.setDerivedDateTime(expected, 69, 0);
+            } else if (i == 60) {
+                expectedTimeMillis = dstBaseTimeMillis + minutesInMillis;
+                Fields.setDateTime(expected, normalizedDateTimeArgs);
+                Fields.setDst(expected, 1, PstPdt.getUtcOffsetSeconds(1));
+                Fields.setDerivedDateTime(expected, 69, 0);
+            } else {
+                expectedTimeMillis = -1;
+                Fields.setDateTime(expected, dateTimeArgs);
+                Fields.setDst(expected, -1, 9);
+                Fields.setDerivedDateTime(expected, 9, 9);
+            }
+            assertEquals("i = " + i, expectedTimeMillis, timeMillis);
+            Fields.assertTimeEquals("i = " + i, expected, time);
+
+            // isDst = -1, normalize(false)
+            Fields.setDateTime(time, dateTimeArgs);
+            Fields.setDst(time, -1 /* isDst */, 9);
+            Fields.setDerivedDateTime(time, 9, 9);
+
+            timeMillis = time.normalize(false);
+            if (i == -1) {
+                expectedTimeMillis = stdBaseTimeMillis + minutesInMillis;
+                Fields.setDateTime(expected, normalizedDateTimeArgs);
+                Fields.setDst(expected, 0, PstPdt.getUtcOffsetSeconds(0));
+                Fields.setDerivedDateTime(expected, 69, 0);
+            } else if (i == 60) {
+                expectedTimeMillis = dstBaseTimeMillis + minutesInMillis;
+                Fields.setDateTime(expected, normalizedDateTimeArgs);
+                Fields.setDst(expected, 1, PstPdt.getUtcOffsetSeconds(1));
+                Fields.setDerivedDateTime(expected, 69, 0);
+            } else {
+                expectedTimeMillis = -1;
+                Fields.setDateTime(expected, dateTimeArgs);
+                Fields.setDst(expected, -1, 9);
+                Fields.setDerivedDateTime(expected, 9, 9);
+            }
+            assertEquals("i = " + i, expectedTimeMillis, timeMillis);
+            Fields.assertTimeEquals("i = " + i, expected, time);
+        }
+    }
+
+    public void testNormalize_duplicateWallTime() {
+        // 1:00 in standard / 2:00 in DST
+        long timeBaseMillis = 1194163200000L;
+        long dstCorrectionMillis = 3600000;
+
+        // Try each minute from one minute before the duplicated hour until one after.
+        for (int i = -1; i <= 60; i++) {
+            Time time = new Time(PstPdt.ID);
+            Time expected = new Time(PstPdt.ID);
+
+            int[] dateTimeArgs = new int[] { 2007, 10, 4, 1, i, 0 };
+            int[] normalizedAdjustedBackwardDateTimeArgs;
+            int[] normalizedDateTimeArgs;
+            int[] normalizedAdjustedForwardDateTimeArgs;
+            if (i == -1) {
+                normalizedAdjustedBackwardDateTimeArgs = null;
+                normalizedDateTimeArgs = new int[] { 2007, 10, 4, 0, 59, 0 };
+                normalizedAdjustedForwardDateTimeArgs = new int[] { 2007, 10, 4, 1, 59, 0 };
+            } else if (i == 60) {
+                normalizedAdjustedBackwardDateTimeArgs = new int[] { 2007, 10, 4, 1, 0, 0 };
+                normalizedDateTimeArgs = new int[] { 2007, 10, 4, 2, 0, 0 };
+                normalizedAdjustedForwardDateTimeArgs = null;
+            } else {
+                normalizedAdjustedBackwardDateTimeArgs = null;
+                normalizedDateTimeArgs = dateTimeArgs;
+                normalizedAdjustedForwardDateTimeArgs =  null;
+            }
+
+            int minutesInMillis = i * 60000;
+
+            // isDst = 0, normalize(true)
+            Fields.setDateTime(time, dateTimeArgs);
+            Fields.setDst(time, 0 /* isDst */, 9);
+            Fields.setDerivedDateTime(time, 9, 9);
+
+            long timeMillis = time.normalize(true);
+
+            Fields.setDateTime(expected, normalizedDateTimeArgs);
+            // When ignoreDst == true the choice between DST and STD is arbitrary when both answers
+            // are possible.
+            if (timeMillis == timeBaseMillis + minutesInMillis) {
+                // i == 60 is unambiguous
+                assertTrue("i =" + i, i < 60);
+                Fields.setDst(expected, 1 /* isDst */, PstPdt.getUtcOffsetSeconds(1));
+            } else if (timeMillis == timeBaseMillis + minutesInMillis + dstCorrectionMillis) {
+                // i == -1 is unambiguous
+                assertTrue("i =" + i, i > -1);
+                Fields.setDst(expected, 0 /* isDst */, PstPdt.getUtcOffsetSeconds(0));
+            } else {
+                fail("i =" + i);
+            }
+            Fields.setDerivedDateTime(expected, 307, 0);
+            Fields.assertTimeEquals("i = " + i, expected, time);
+
+            // isDst = 0, normalize(false)
+            Fields.setDateTime(time, dateTimeArgs);
+            Fields.setDst(time, 0 /* isDst */, 9);
+            Fields.setDerivedDateTime(time, 9, 9);
+
+            timeMillis = time.normalize(false);
+            long expectedTimeMillis = timeBaseMillis + minutesInMillis + dstCorrectionMillis;
+            assertEquals("i = " + i, expectedTimeMillis, timeMillis);
+            if (i == -1) {
+                Fields.setDateTime(expected, normalizedAdjustedForwardDateTimeArgs);
+                Fields.setDst(expected, 1 /* isDst */, PstPdt.getUtcOffsetSeconds(1));
+            } else {
+                Fields.setDateTime(expected, normalizedDateTimeArgs);
+                Fields.setDst(expected, 0 /* isDst */, PstPdt.getUtcOffsetSeconds(0));
+            }
+            Fields.setDerivedDateTime(expected, 307, 0);
+            Fields.assertTimeEquals("i = " + i, expected, time);
+
+            // isDst = 1, normalize(true)
+            Fields.setDateTime(time, dateTimeArgs);
+            Fields.setDst(time, 1 /* isDst */, 9);
+            Fields.setDerivedDateTime(time, 9, 9);
+
+            timeMillis = time.normalize(true);
+            Fields.setDateTime(expected, normalizedDateTimeArgs);
+            // When ignoreDst == true the choice between DST and STD is arbitrary when both answers
+            // are possible.
+            if (timeMillis == timeBaseMillis + minutesInMillis) {
+                // i == 60 is unambiguous
+                assertTrue("i =" + i, i < 60);
+                Fields.setDst(expected, 1 /* isDst */, PstPdt.getUtcOffsetSeconds(1));
+            } else if (timeMillis == timeBaseMillis + minutesInMillis + dstCorrectionMillis) {
+                // i == -1 is unambiguous
+                assertTrue("i =" + i, i > -1);
+                Fields.setDst(expected, 0 /* isDst */, PstPdt.getUtcOffsetSeconds(0));
+            } else {
+                fail("i =" + i);
+            }
+            Fields.setDerivedDateTime(expected, 307, 0);
+            Fields.assertTimeEquals("i = " + i, expected, time);
+
+            // isDst = 1, normalize(false)
+            Fields.setDateTime(time, dateTimeArgs);
+            Fields.setDst(time, 1 /* isDst */, 9);
+            Fields.setDerivedDateTime(time, 9, 9);
+
+            timeMillis = time.normalize(false);
+            expectedTimeMillis = timeBaseMillis + minutesInMillis;
+            if (i == 60) {
+                Fields.setDateTime(expected, normalizedAdjustedBackwardDateTimeArgs);
+                Fields.setDst(expected, 0 /* isDst */, PstPdt.getUtcOffsetSeconds(0));
+            } else {
+                Fields.setDateTime(expected, normalizedDateTimeArgs);
+                Fields.setDst(expected, 1 /* isDst */, PstPdt.getUtcOffsetSeconds(1));
+            }
+            Fields.setDerivedDateTime(expected, 307, 0);
+            assertEquals("i = " + i, expectedTimeMillis, timeMillis);
+            Fields.assertTimeEquals("i = " + i, expected, time);
+
+            // isDst = -1, normalize(true)
+            Fields.setDateTime(time, dateTimeArgs);
+            Fields.setDst(time, -1 /* isDst */, 9);
+            Fields.setDerivedDateTime(time, 9, 9);
+
+            timeMillis = time.normalize(true);
+            Fields.setDateTime(expected, normalizedDateTimeArgs);
+            // When isDst == -1 the choice between DST and STD is arbitrary when both answers
+            // are possible.
+            if (timeMillis == timeBaseMillis + minutesInMillis) {
+                // i == 60 is unambiguous
+                assertTrue("i =" + i, i < 60);
+                Fields.setDst(expected, 1 /* isDst */, PstPdt.getUtcOffsetSeconds(1));
+            } else if (timeMillis == timeBaseMillis + minutesInMillis + dstCorrectionMillis) {
+                // i == -1 is unambiguous
+                assertTrue("i =" + i, i > -1);
+                Fields.setDst(expected, 0 /* isDst */, PstPdt.getUtcOffsetSeconds(0));
+            } else {
+                fail("i =" + i);
+            }
+            Fields.setDerivedDateTime(expected, 307, 0);
+            Fields.assertTimeEquals("i = " + i, expected, time);
+
+            // isDst = -1, normalize(false)
+            Fields.setDateTime(time, dateTimeArgs);
+            Fields.setDst(time, -1 /* isDst */, 9);
+            Fields.setDerivedDateTime(time, 9, 9);
+
+            timeMillis = time.normalize(false);
+            // When isDst == -1 the choice between DST and STD is arbitrary when both answers
+            // are possible.
+            if (timeMillis == timeBaseMillis + minutesInMillis) {
+                // i == 60 is unambiguous
+                assertTrue("i =" + i, i < 60);
+                Fields.setDst(expected, 1 /* isDst */, PstPdt.getUtcOffsetSeconds(1));
+            } else if (timeMillis == timeBaseMillis + minutesInMillis + dstCorrectionMillis) {
+                // i == -1 is unambiguous
+                assertTrue("i =" + i, i > -1);
+                Fields.setDst(expected, 0 /* isDst */, PstPdt.getUtcOffsetSeconds(0));
+            } else {
+                fail("i =" + i);
+            }
+            Fields.setDerivedDateTime(expected, 307, 0);
+            Fields.assertTimeEquals("i = " + i, expected, time);
+        }
+    }
+
+    public void testNormalize_beforeTzRecords() {
+        int[] timeFields = new int[] { 1900, 0, 1, 2, 3, 4, -999 /* not used */, 9, 9, 9 };
+        assertNormalizeInvalid(timeFields, PstPdt.ID);
+        assertNormalizeInvalid(timeFields, Time.TIMEZONE_UTC);
+    }
+
+    private static void assertNormalizeInvalid(int[] timeFields, String timezone) {
+        Time time = new Time(timezone);
+        Time expected = new Time(timezone);
+
+        // isDst = 0, normalize(true)
+        Fields.set(time, timeFields);
+        time.isDst = 0;
+        Fields.set(expected, timeFields);
+        expected.isDst = -1;
+        assertNormalizeResult(true, time, expected, -1);
+
+        // isDst = 0, normalize(false)
+        Fields.set(time, timeFields);
+        time.isDst = 0;
+        Fields.set(expected, timeFields);
+        expected.isDst = 0;
+        assertNormalizeResult(false, time, expected, -1);
+
+        // isDst = 1, normalize(true)
+        Fields.set(time, timeFields);
+        time.isDst = 1;
+        Fields.set(expected, timeFields);
+        expected.isDst = -1;
+        assertNormalizeResult(true, time, expected, -1);
+
+        // isDst = 1, normalize(false)
+        Fields.set(time, timeFields);
+        time.isDst = 1;
+        Fields.set(expected, timeFields);
+        expected.isDst = 1;
+        assertNormalizeResult(false, time, expected, -1);
+
+        // isDst = -1, normalize(true)
+        Fields.set(time, timeFields);
+        time.isDst = -1;
+        Fields.set(expected, timeFields);
+        expected.isDst = -1;
+        assertNormalizeResult(true, time, expected, -1);
+
+        // isDst = -1, normalize(false)
+        Fields.set(time, timeFields);
+        time.isDst = -1;
+        Fields.set(expected, timeFields);
+        expected.isDst = -1;
+        assertNormalizeResult(false, time, expected, -1);
+    }
+
+    public void testNormalize_afterTzRecords() {
+        int[] timeFields = new int[] { 2039, 0, 1, 2, 3, 4, -999 /* not used */, 9, 9, 9 };
+        assertNormalizeInvalid(timeFields, PstPdt.ID);
+        assertNormalizeInvalid(timeFields, Time.TIMEZONE_UTC);
+    }
+
+    public void testNormalize_invalid() {
+        int[] timeFields = new int[] { 0, 0, 0, 0, 0, 0, -999 /* not used */, 9, 9, 9 };
+        assertNormalizeInvalid(timeFields, PstPdt.ID);
+        assertNormalizeInvalid(timeFields, Time.TIMEZONE_UTC);
+    }
+
+    public void testNormalize_dstToDstSkip() {
+        // In London, 4th May 1941 02:00 - 03:00 was a skip from DST -> DST (+1 hour -> +2 hours)
+        String timezone = "Europe/London";
+        Time t = new Time(timezone);
+        Time expected = new Time(timezone);
+
+        // Demonstrate the data we expect either side of the skipped interval: 01:59
+        Fields.set(t, 1941, 4, 4, 1, 59, 0, -1 /* isDst */, 9, 9, 9);
+        Fields.set(expected, 1941, 4, 4, 1, 59, 0, 1 /* isDst */, 3600, 123, 0);
+        assertNormalizeResult(true, t, expected, -904518060000L);
+
+        // Demonstrate the data we expect either side of the skipped interval: 03:00
+        Fields.set(t, 1941, 4, 4, 3, 0, 0, -1 /* isDst */, 9, 9, 9);
+        Fields.set(expected, 1941, 4, 4, 3, 0, 0, 1 /* isDst */, 7200, 123, 0);
+        assertNormalizeResult(true, t, expected, -904518000000L);
+
+        // isDst = 1, normalize(false)
+        Fields.set(t, 1941, 4, 4, 2, 30, 0, 1 /* isDst */, 9, 9, 9);
+        Fields.set(expected, 1941, 4, 4, 2, 30, 0, 1 /* isDst */, 9, 9, 9);
+        assertNormalizeResult(false, t, expected, -1);
+
+        // isDst = -1, normalize(false)
+        Fields.set(t, 1941, 4, 4, 2, 30, 0, -1 /* isDst */, 9, 9, 9);
+        Fields.set(expected, 1941, 4, 4, 2, 30, 0, -1 /* isDst */, 9, 9, 9);
+        assertNormalizeResult(false, t, expected, -1);
+
+        // The results below are potentially arbitrary: 01:30 and 02:30 are not a valid standard
+        // times so normalize() must apply one of the possible STD -> DST adjustments to arrive at a
+        // date / time.
+
+        // isDst = 0, normalize(false) @ 01:30
+        Fields.set(t, 1941, 4, 4, 1, 30, 0, 0 /* isDst */, 9, 9, 9);
+        Fields.set(expected, 1941, 4, 4, 3, 30, 0, 1 /* isDst */, 7200, 123, 0);
+        assertNormalizeResult(false, t, expected, -904516200000L);
+
+        // isDst = 0, normalize(false) @ 02:30
+        Fields.set(t, 1941, 4, 4, 2, 30, 0, 0 /* isDst */, 9, 9, 9);
+        long timeMillis = t.normalize(false);
+
+        if (timeMillis == -904516200000L) {
+            // The original C implementation chooses this one.
+            Fields.set(expected, 1941, 4, 4, 3, 30, 0, 1 /* isDst */, 7200, 123, 0);
+        } else if (timeMillis == -904512600000L) {
+            Fields.set(expected, 1941, 4, 4, 4, 30, 0, 1 /* isDst */, 7200, 123, 0);
+        } else {
+            fail();
+        }
+        Fields.assertTimeEquals(expected, t);
+    }
+
+    public void testNormalize_dstToDstRepeat() {
+        // In London, 10th August 1941 02:00 - 03:00 was a repeat from DST -> DST
+        // (+2 hour -> +1 hour)
+        String timezone = "Europe/London";
+        Time t = new Time(timezone);
+        Time expected = new Time(timezone);
+
+        // Demonstrate the data we expect during the repeated interval: 02:30 (first)
+        t.set(-896052600000L);
+        Fields.set(expected, 1941, 7, 10, 2, 30, 0, 1 /* isDst */, 7200, 221, 0);
+        Fields.assertTimeEquals(expected, t);
+
+        // Demonstrate the data we expect during the repeated interval: 02:30 (second)
+        t.set(-896049000000L);
+        Fields.set(expected, 1941, 7, 10, 2, 30, 0, 1 /* isDst */, 3600, 221, 0);
+        Fields.assertTimeEquals(expected, t);
+
+        // Now check times in the repeated hour with different isDst assertions...
+
+        // isDst = 1, normalize(false) @ 02:30
+        Fields.set(t, 1941, 7, 10, 2, 30, 0, 1 /* isDst */, 9, 9, 9);
+        Fields.set(expected, 1941, 7, 10, 2, 30, 0, 1 /* isDst */, 3600, 221, 0);
+        assertNormalizeResult(false, t, expected, -896049000000L);
+
+        // isDst = -1, normalize(false) @ 02:30
+        Fields.set(t, 1941, 7, 10, 2, 30, 0, -1 /* isDst */, 9, 9, 9);
+        Fields.set(expected, 1941, 7, 10, 2, 30, 0, 1 /* isDst */, 3600, 221, 0);
+        assertNormalizeResult(false, t, expected, -896049000000L);
+
+        // The results below are potentially arbitrary: 01:30 and 02:30 are not a valid standard
+        // times so normalize() must apply one of the possible STD -> DST adjustments to arrive at a
+        // date / time.
+
+        // isDst = 0, normalize(false) @ 01:30
+        Fields.set(t, 1941, 7, 10, 1, 30, 0, 0 /* isDst */, 9, 9, 9);
+        long timeMillis = t.normalize(false);
+        if (timeMillis == -896052600000L) {
+            // The original C implementation chooses this one.
+            Fields.set(expected, 1941, 7, 10, 2, 30, 0, 1 /* isDst */, 7200, 221, 0);
+        } else if (timeMillis == -896049000000L) {
+            Fields.set(expected, 1941, 7, 10, 2, 30, 0, 1 /* isDst */, 3600, 221, 0);
+        } else {
+            fail();
+        }
+        Fields.assertTimeEquals(expected, t);
+
+        // isDst = 0, normalize(false) @ 02:30
+        Fields.set(t, 1941, 7, 10, 2, 30, 0, 0 /* isDst */, 9, 9, 9);
+        Fields.set(expected, 1941, 7, 10, 3, 30, 0, 1 /* isDst */, 3600, 221, 0);
+        assertNormalizeResult(false, t, expected, -896045400000L);
+    }
+
+    public void testNormalize_stdToStdRepeat() {
+        // In London, 31st October 1971 02:00 - 03:00 was a repeat from STD -> STD
+        String timezone = "Europe/London";
+        Time t = new Time(timezone);
+        Time expected = new Time(timezone);
+
+        // Demonstrate the data we expect during the repeated interval: 02:30 (first)
+        t.set(57720600000L);
+        Fields.set(expected, 1971, 9, 31, 2, 30, 0, 0 /* isDst */, 3600, 303, 0);
+        Fields.assertTimeEquals(expected, t);
+
+        // Demonstrate the data we expect during the repeated interval: 02:30 (second)
+        t.set(57724200000L);
+        Fields.set(expected, 1971, 9, 31, 2, 30, 0, 0 /* isDst */, 0, 303, 0);
+        Fields.assertTimeEquals(expected, t);
+
+        // isDst = 0, normalize(false) @ 02:30
+        Fields.set(t, 1971, 9, 31, 2, 30, 0, 0 /* isDst */, 9, 9, 9);
+
+        long timeMillis = t.normalize(false);
+
+        // Either answer is valid: the choice is arbitrary.
+        if (57720600000L == timeMillis) {
+            // The original C implementation chooses this one.
+            Fields.set(expected, 1971, 9, 31, 2, 30, 0, 0 /* isDst */, 3600, 303, 0);
+        } else if (57724200000L == timeMillis) {
+            Fields.set(expected, 1971, 9, 31, 2, 30, 0, 0 /* isDst */, 0, 303, 0);
+        } else {
+            fail();
+        }
+        Fields.assertTimeEquals(expected, t);
+
+        // isDst = -1, normalize(false) @ 02:30
+        Fields.set(t, 1971, 9, 31, 2, 30, 0, -1 /* isDst */, 9, 9, 9);
+
+        timeMillis = t.normalize(false);
+
+        Fields.setDateTime(expected, 1971, 9, 31, 2, 30, 0);
+        // Either answer is valid: the choice is arbitrary.
+        if (57720600000L == timeMillis) {
+            // The original C implementation chooses this one.
+            Fields.set(expected, 1971, 9, 31, 2, 30, 0, 0 /* isDst */, 3600, 303, 0);
+        } else if (57724200000L == timeMillis) {
+            Fields.set(expected, 1971, 9, 31, 2, 30, 0, 0 /* isDst */, 0, 303, 0);
+        } else {
+            fail();
+        }
+        Fields.assertTimeEquals(expected, t);
+
+        // The results below are potentially arbitrary: 01:30 and 02:30 are not a valid DST
+        // so normalize() must apply one of the possible STD -> DST adjustments to arrive at a
+        // date / time.
+
+        // isDst = 1, normalize(false) @ 01:30
+        Fields.set(t, 1971, 9, 31, 1, 30, 0, 1 /* isDst */, 9, 9, 9);
+
+        timeMillis = t.normalize(false);
+
+        if (timeMillis == 57713400000L) {
+            // Original C implementation chooses this one.
+            Fields.set(expected, 1971, 9, 31, 0, 30, 0, 0 /* isDst */, 3600, 303, 0);
+        } else if (timeMillis == 57717000000L) {
+            Fields.set(expected, 1971, 9, 31, 1, 30, 0, 0 /* isDst */, 3600, 303, 0);
+        } else {
+            fail();
+        }
+        Fields.assertTimeEquals(expected, t);
+
+        // isDst = 1, normalize(false) @ 02:30
+        Fields.set(t, 1971, 9, 31, 2, 30, 0, 1 /* isDst */, 9, 9, 9);
+        timeMillis = t.normalize(false);
+        if (timeMillis == 57717000000L) {
+            // The original C implementation chooses this one.
+            Fields.set(expected, 1971, 9, 31, 1, 30, 0, 0 /* isDst */, 3600, 303, 0);
+        } else if (timeMillis == 57720600000L) {
+            Fields.set(expected, 1971, 9, 31, 2, 30, 0, 0 /* isDst */, 3600, 303, 0);
+        } else {
+            fail();
+        }
+        Fields.assertTimeEquals(expected, t);
+
+        // isDst = 1, normalize(false) @ 03:30
+        Fields.set(t, 1971, 9, 31, 3, 30, 0, 1 /* isDst */, 9, 9, 9);
+        Fields.set(expected, 1971, 9, 31, 2, 30, 0, 0 /* isDst */, 0, 303, 0);
+        assertNormalizeResult(false, t, expected, 57724200000L);
+    }
+
+    public void testNormalize_stdToStdSkip() {
+        // In Kiritimati, 1st Jan 1995 10:00 - 10:40 was a skip from STD -> STD (plus they do not
+        // observe DST).
+        String timezone = "Pacific/Kiritimati";
+        Time t = new Time(timezone);
+        Time expected = new Time(timezone);
+
+        // isDst = 0, normalize(false)
+        Fields.set(t, 1995, 0, 1, 10, 20, 0, 0 /* isDst */, 9, 9, 9);
+        Fields.set(expected, 1995, 0, 1, 10, 20, 0, 0 /* isDst */, 9, 9, 9);
+        assertNormalizeResult(false, t, expected, -1);
+
+        // isDst = 1, normalize(false)
+        Fields.set(t, 1995, 0, 1, 10, 20, 0, 1 /* isDst */, 9, 9, 9);
+        Fields.set(expected, 1995, 0, 1, 10, 20, 0, 1 /* isDst */, 9, 9, 9);
+        assertNormalizeResult(false, t, expected, -1);
+
+        // isDst = -1, normalize(false)
+        Fields.set(t, 1995, 0, 1, 10, 20, 0, -1 /* isDst */, 9, 9, 9);
+        Fields.set(expected, 1995, 0, 1, 10, 20, 0, -1 /* isDst */, 9, 9, 9);
+        assertNormalizeResult(false, t, expected, -1);
+    }
+
+    public void testNormalize_utcWithDst() {
+        // In UTC (or other zone without DST), what happens when a DST time is specified and there
+        // is no DST offset available in the timezone data.
+        Time t = new Time(Time.TIMEZONE_UTC);
+        Time expected = new Time(Time.TIMEZONE_UTC);
+
+        // isDst = 1, normalize(false)
+        Fields.set(t, 2005, 6, 22, 1, 30, 0, 1 /* isDst */, 9, 9, 9);
+        Fields.set(expected, 2005, 6, 22, 1, 30, 0, 1 /* isDst */, 9, 9, 9);
+        assertNormalizeResult(false, t, expected, -1);
+
+        // isDst = -1, normalize(false)
+        Fields.set(t, 2005, 6, 22, 1, 30, 0, -1 /* isDst */, 9, 9, 9);
+        Fields.set(expected, 2005, 6, 22, 1, 30, 0, 0 /* isDst */, 0, 202, 5);
+        assertNormalizeResult(false, t, expected, 1121995800000L);
+    }
+
+    public void testUnknownTz() {
+        // Historically the code used UTC if the timezone is unrecognized.
+
+        String unknownTimezoneId = "THIS_ID_IS_NOT_A_VALID_TZ";
+        Time t = new Time(unknownTimezoneId);
+        assertEquals(unknownTimezoneId, t.timezone);
+        Fields.set(t, 2007, 5, 1, 1, 2, 3, -1 /* isDst */, 9, 9, 9);
+
+        // We can't know for sure which time zone is being used, but we assume it is UTC if the date
+        // normalizes to isDst == 0 and with an offset of 0 during the summer months.
+
+        long timeMillis = t.normalize(true);
+        assertEquals(unknownTimezoneId, t.timezone);
+        assertEquals(1180659723000L, timeMillis);
+
+        Time expected = new Time(unknownTimezoneId);
+        Fields.set(expected, 2007, 5, 1, 1, 2, 3, 0 /* isDst */, 0, 151, 5);
+        Fields.assertTimeEquals(expected, t);
+    }
+
+    private static void assertNormalizeResult(boolean normalizeArgument, Time toNormalize,
+            Time expectedTime, long expectedTimeMillis) {
+        long actualTimeMillis = toNormalize.normalize(normalizeArgument /* ignore isDst */);
+        assertEquals(expectedTimeMillis, actualTimeMillis);
+        Fields.assertTimeEquals(expectedTime, toNormalize);
+    }
+
+    /** A helper class for manipulating / testing fields on Time objects. */
+    private static class Fields {
+        final static int MAIN_DATE_TIME = 1;
+        final static int DST_FIELDS = 2;
+        final static int DERIVED_DATE_TIME = 4;
+
+        final static int ALL = MAIN_DATE_TIME | DST_FIELDS | DERIVED_DATE_TIME;
+
+        public static void assertTimeEquals(Time expected, Time actual) {
+            assertTimeEquals("", ALL, expected, actual);
+        }
+
+        public static void assertTimeEquals(int fields, Time expected, Time actual) {
+            assertTimeEquals("", fields, expected, actual);
+        }
+
+        public static void assertTimeEquals(String message, Time expected, Time actual) {
+            assertTimeEquals(message, Fields.ALL, expected, actual);
+        }
+
+        public static void assertTimeEquals(String message, int fields, Time expected,
+                Time actual) {
+            boolean mainDateTimeOk = (fields & Fields.MAIN_DATE_TIME) == 0
+                    || (Objects.equals(expected.timezone, actual.timezone)
+                    && expected.year == actual.year
+                    && expected.month == actual.month
+                    && expected.monthDay == actual.monthDay
+                    && expected.hour == actual.hour
+                    && expected.minute == actual.minute
+                    && expected.second == actual.second
+                    && expected.allDay == actual.allDay);
+
+            boolean dstFieldsOk = (fields & Fields.DST_FIELDS) == 0
+                    || (expected.isDst == actual.isDst && expected.gmtoff == actual.gmtoff);
+
+            boolean derivedDateTimeOk = (fields & Fields.DERIVED_DATE_TIME) == 0
+                    || (expected.yearDay == actual.yearDay && expected.weekDay == actual.weekDay);
+
+            if (!mainDateTimeOk || !dstFieldsOk || !derivedDateTimeOk) {
+                String expectedTime = timeToString(fields, expected);
+                String actualTime = timeToString(fields, actual);
+                fail(message + " [Time fields differed. Expected: " + expectedTime + "Actual: "
+                        + actualTime + "]");
+            }
+        }
+
+        private static String timeToString(int fields, Time time) {
+            List<Object> values = new ArrayList<Object>();
+            StringBuilder format = new StringBuilder();
+            if ((fields & Fields.MAIN_DATE_TIME) > 0) {
+                format.append("%d-%02d-%02d %02d:%02d:%02d allDay=%b timezone=%s ");
+                values.addAll(
+                        Arrays.asList(time.year, time.month, time.monthDay, time.hour, time.minute,
+                                time.second, time.allDay, time.timezone));
+            }
+            if ((fields & Fields.DST_FIELDS) > 0) {
+                format.append("isDst=%d, gmtoff=%d ");
+                values.add(time.isDst);
+                values.add(time.gmtoff);
+            }
+            if ((fields & Fields.DERIVED_DATE_TIME) > 0) {
+                format.append("yearDay=%d, weekDay=%d ");
+                values.add(time.yearDay);
+                values.add(time.weekDay);
+
+            }
+            return String.format(format.toString(), values.toArray());
+        }
+
+        public static void setDateTime(Time t, int year, int month, int monthDay, int hour,
+                int minute, int second) {
+            t.year = year;
+            t.month = month;
+            t.monthDay = monthDay;
+            t.hour = hour;
+            t.minute = minute;
+            t.second = second;
+            t.allDay = false;
+        }
+
+        public static void setDateTime(Time t, int[] args) {
+            assertEquals(6, args.length);
+            setDateTime(t, args[0], args[1], args[2], args[3], args[4], args[5]);
+        }
+
+        public static void setDst(Time t, int isDst, int gmtoff) {
+            t.isDst = isDst;
+            t.gmtoff = gmtoff;
+        }
+
+        public static void setDerivedDateTime(Time t, int yearDay, int weekDay) {
+            t.yearDay = yearDay;
+            t.weekDay = weekDay;
+        }
+
+        public static void setAllDayDate(Time t, int year, int month, int monthDay) {
+            t.year = year;
+            t.month = month;
+            t.monthDay = monthDay;
+            t.allDay = true;
+        }
+
+        public static void set(Time t, int[] args) {
+            assertEquals(10, args.length);
+            set(t, args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8],
+                    args[9]);
+        }
+
+        public static void set(Time t, int year, int month, int monthDay, int hour, int minute,
+                int second, int isDst, int gmtoff, int yearDay, int weekDay) {
+            setDateTime(t, year, month, monthDay, hour, minute, second);
+            setDst(t, isDst, gmtoff);
+            setDerivedDateTime(t, yearDay, weekDay);
+        }
+    }
+
+    private static class PstPdt {
+        public static final String ID = "America/Los_Angeles";
+
+        public static int getUtcOffsetSeconds(int isDst) {
+            if (isDst == 0) {
+                return -28800;
+            } else if (isDst == 1) {
+                return -25200;
+            }
+            throw new IllegalArgumentException();
+        }
+
+        public static int getUtcOffsetSeconds(boolean isDst) {
+            return getUtcOffsetSeconds(isDst ? 1 : 0);
+        }
+
+        public static int getUtcOffsetMillis(boolean isDst) {
+            return getUtcOffsetSeconds(isDst) * 1000;
+        }
+    }
 }
diff --git a/tests/tests/text/src/android/text/style/cts/TtsSpanTest.java b/tests/tests/text/src/android/text/style/cts/TtsSpanTest.java
new file mode 100644
index 0000000..3c320f1
--- /dev/null
+++ b/tests/tests/text/src/android/text/style/cts/TtsSpanTest.java
@@ -0,0 +1,81 @@
+/*
+ * 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.text.style.cts;
+
+import android.os.Parcel;
+import android.os.PersistableBundle;
+import android.text.style.TtsSpan;
+import junit.framework.TestCase;
+
+public class TtsSpanTest extends TestCase {
+
+    PersistableBundle bundle;
+
+    protected void setUp() {
+        bundle = new PersistableBundle();
+        bundle.putString("argument.one", "value.one");
+        bundle.putString("argument.two", "value.two");
+        bundle.putLong("argument.three", 3);
+        bundle.putLong("argument.four", 4);
+    }
+
+    public void testGetArgs() {
+        TtsSpan t = new TtsSpan("test.type.one", bundle);
+        PersistableBundle args = t.getArgs();
+        assertEquals(4, args.size());
+        assertEquals("value.one", args.getString("argument.one"));
+        assertEquals("value.two", args.getString("argument.two"));
+        assertEquals(3, args.getLong("argument.three"));
+        assertEquals(4, args.getLong("argument.four"));
+    }
+
+    public void testGetType() {
+        TtsSpan t = new TtsSpan("test.type.two", bundle);
+        assertEquals("test.type.two", t.getType());
+    }
+
+    public void testDescribeContents() {
+        TtsSpan span = new TtsSpan("test.type.three", bundle);
+        span.describeContents();
+    }
+
+    public void testGetSpanTypeId() {
+        TtsSpan span = new TtsSpan("test.type.four", bundle);
+        span.getSpanTypeId();
+    }
+
+    public void testWriteAndReadParcel() {
+        Parcel p = Parcel.obtain();
+        try {
+            TtsSpan span = new TtsSpan("test.type.five", bundle);
+            span.writeToParcel(p, 0);
+            p.setDataPosition(0);
+
+            TtsSpan t = new TtsSpan(p);
+
+            assertEquals("test.type.five", t.getType());
+            PersistableBundle args = t.getArgs();
+            assertEquals(4, args.size());
+            assertEquals("value.one", args.getString("argument.one"));
+            assertEquals("value.two", args.getString("argument.two"));
+            assertEquals(3, args.getLong("argument.three"));
+            assertEquals(4, args.getLong("argument.four"));
+        } finally {
+            p.recycle();
+        }
+    }
+}
diff --git a/tests/tests/text/src/android/text/util/cts/LinkifyTest.java b/tests/tests/text/src/android/text/util/cts/LinkifyTest.java
index 45111f2..72cd139 100644
--- a/tests/tests/text/src/android/text/util/cts/LinkifyTest.java
+++ b/tests/tests/text/src/android/text/util/cts/LinkifyTest.java
@@ -73,14 +73,26 @@
     }
 
     public void testAddLinks1() {
+        // Verify URLs including the ones that have new gTLDs, and the
+        // ones that look like gTLDs (and so are accepted by linkify)
+        // and the ones that should not be linkified due to non-compliant
+        // gTLDs
+        final String longGTLD =
+                "abcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabcdefghijabc";
         SpannableString spannable = new SpannableString("name@gmail.com, "
-                + "www.google.com, http://www.google.com/language_tools?hl=en, ");
+                + "www.google.com, http://www.google.com/language_tools?hl=en, "
+                + "a.bc, "   // a URL with accepted gTLD so should be linkified
+                + "d.e, f.1, g.12, "  // not valid, so should not be linkified
+                + "h." + longGTLD + " "  // valid, should be linkified
+                + "j." + longGTLD + "a"); // not a valid URL (gtld too long), no linkify
 
         assertTrue(Linkify.addLinks(spannable, Linkify.WEB_URLS));
         URLSpan[] spans = spannable.getSpans(0, spannable.length(), URLSpan.class);
-        assertEquals(2, spans.length);
+        assertEquals(4, spans.length);
         assertEquals("http://www.google.com", spans[0].getURL());
         assertEquals("http://www.google.com/language_tools?hl=en", spans[1].getURL());
+        assertEquals("http://a.bc", spans[2].getURL());
+        assertEquals("http://h." + longGTLD, spans[3].getURL());
 
         assertTrue(Linkify.addLinks(spannable, Linkify.EMAIL_ADDRESSES));
         spans = spannable.getSpans(0, spannable.length(), URLSpan.class);
diff --git a/tests/tests/textureview/Android.mk b/tests/tests/textureview/Android.mk
index 30cc4ff..f85a738 100644
--- a/tests/tests/textureview/Android.mk
+++ b/tests/tests/textureview/Android.mk
@@ -21,8 +21,6 @@
 # and when built explicitly put it in the data partition
 LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
 
-LOCAL_JAVA_LIBRARIES := android.test.runner
-
 LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
diff --git a/tests/tests/textureview/AndroidManifest.xml b/tests/tests/textureview/AndroidManifest.xml
index 63cd233..9ec3f17 100644
--- a/tests/tests/textureview/AndroidManifest.xml
+++ b/tests/tests/textureview/AndroidManifest.xml
@@ -25,7 +25,10 @@
 
     <instrumentation
         android:targetPackage="com.android.cts.textureview"
-        android:name="android.test.InstrumentationCtsTestRunner" />
+        android:name="android.support.test.runner.AndroidJUnitRunner" >
+        <meta-data android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
 
     <application
         android:label="@string/app_name"
diff --git a/tests/tests/theme/Android.mk b/tests/tests/theme/Android.mk
index 5846426..134af7c 100644
--- a/tests/tests/theme/Android.mk
+++ b/tests/tests/theme/Android.mk
@@ -24,9 +24,6 @@
 # When built, explicitly put it in the data partition.
 LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
 
-# All tests should include android.test.runner.
-LOCAL_JAVA_LIBRARIES := android.test.runner
-
 LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
diff --git a/tests/tests/theme/AndroidManifest.xml b/tests/tests/theme/AndroidManifest.xml
index 0edc836..8232d2b 100644
--- a/tests/tests/theme/AndroidManifest.xml
+++ b/tests/tests/theme/AndroidManifest.xml
@@ -19,12 +19,15 @@
 
     <uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
     <application>
-        <uses-library android:name="android.test.runner" />        
-        <activity android:name="android.theme.cts.DeviceDefaultActivity" />        
+        <uses-library android:name="android.test.runner" />
+        <activity android:name="android.theme.cts.DeviceDefaultActivity" />
     </application>
 
-    <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
             android:targetPackage="com.android.cts.theme"
-            android:label="CTS tests for themes"/>
+            android:label="CTS tests for themes">
+        <meta-data android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
 </manifest>
 
diff --git a/tests/tests/tv/Android.mk b/tests/tests/tv/Android.mk
new file mode 100644
index 0000000..5df50ca
--- /dev/null
+++ b/tests/tests/tv/Android.mk
@@ -0,0 +1,31 @@
+# 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_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_PACKAGE_NAME := CtsTvTestCases
+
+LOCAL_STATIC_JAVA_LIBRARIES := ctsdeviceutil ctstestrunner
+
+LOCAL_SDK_VERSION := current
+
+include $(BUILD_CTS_PACKAGE)
diff --git a/tests/tests/tv/AndroidManifest.xml b/tests/tests/tv/AndroidManifest.xml
new file mode 100644
index 0000000..66f4ac0
--- /dev/null
+++ b/tests/tests/tv/AndroidManifest.xml
@@ -0,0 +1,92 @@
+<?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.tv">
+
+    <uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
+
+    <uses-permission android:name="android.permission.READ_EPG_DATA" />
+    <uses-permission android:name="android.permission.WRITE_EPG_DATA" />
+
+    <application>
+        <uses-library android:name="android.test.runner" />
+
+        <activity android:name="android.media.tv.cts.TvInputSetupActivityStub">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+            </intent-filter>
+        </activity>
+
+        <activity android:name="android.media.tv.cts.TvInputSettingsActivityStub">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+            </intent-filter>
+        </activity>
+
+        <service android:name="android.media.tv.cts.StubTunerTvInputService"
+                 android:permission="android.permission.BIND_TV_INPUT"
+                 android:label="TV input stub"
+                 android:icon="@drawable/robot"
+                 android:process=":tunerTvInputStub">
+            <intent-filter>
+                <action android:name="android.media.tv.TvInputService" />
+            </intent-filter>
+            <meta-data android:name="android.media.tv.input"
+                       android:resource="@xml/stub_tv_input_service" />
+        </service>
+
+        <service android:name="android.media.tv.cts.NoMetadataTvInputService"
+                 android:permission="android.permission.BIND_TV_INPUT">
+            <intent-filter>
+                <action android:name="android.media.tv.TvInputService" />
+            </intent-filter>
+        </service>
+
+        <service android:name="android.media.tv.cts.NoPermissionTvInputService">
+            <intent-filter>
+                <action android:name="android.media.tv.TvInputService" />
+            </intent-filter>
+            <meta-data android:name="android.media.tv.input"
+                       android:resource="@xml/stub_tv_input_service" />
+        </service>
+
+        <service android:name="android.media.tv.cts.TvInputServiceTest$CountingTvInputService"
+                 android:permission="android.permission.BIND_TV_INPUT">
+            <intent-filter>
+                <action android:name="android.media.tv.TvInputService" />
+            </intent-filter>
+            <meta-data android:name="android.media.tv.input"
+                       android:resource="@xml/stub_tv_input_service" />
+        </service>
+
+        <activity android:name="android.media.tv.cts.TvViewStubActivity">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN"/>
+                <category android:name="android.intent.category.FRAMEWORK_INSTRUMENTATION_TEST" />
+            </intent-filter>
+        </activity>
+    </application>
+
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+            android:targetPackage="com.android.cts.tv"
+            android:label="Tests for the TV APIs.">
+        <meta-data android:name="listener"
+                android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
+</manifest>
diff --git a/tests/tests/tv/res/drawable/robot.png b/tests/tests/tv/res/drawable/robot.png
new file mode 100644
index 0000000..8a9e698
--- /dev/null
+++ b/tests/tests/tv/res/drawable/robot.png
Binary files differ
diff --git a/tests/tests/tv/res/layout/tvview_layout.xml b/tests/tests/tv/res/layout/tvview_layout.xml
new file mode 100644
index 0000000..a15d8ff
--- /dev/null
+++ b/tests/tests/tv/res/layout/tvview_layout.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+
+    <android.media.tv.TvView
+        android:id="@+id/tvview"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"/>
+</LinearLayout>
diff --git a/tests/tests/tv/res/xml/stub_tv_input_service.xml b/tests/tests/tv/res/xml/stub_tv_input_service.xml
new file mode 100644
index 0000000..8ad10cc
--- /dev/null
+++ b/tests/tests/tv/res/xml/stub_tv_input_service.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 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.
+-->
+
+<tv-input xmlns:android="http://schemas.android.com/apk/res/android"
+    android:setupActivity="android.media.tv.cts.TvInputSetupActivityStub"
+    android:settingsActivity="android.media.tv.cts.TvInputSettingsActivityStub" />
diff --git a/tests/tests/tv/src/android/media/tv/cts/NoMetadataTvInputService.java b/tests/tests/tv/src/android/media/tv/cts/NoMetadataTvInputService.java
new file mode 100644
index 0000000..2c5b6e0
--- /dev/null
+++ b/tests/tests/tv/src/android/media/tv/cts/NoMetadataTvInputService.java
@@ -0,0 +1,20 @@
+/*
+ * 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.media.tv.cts;
+
+public class NoMetadataTvInputService extends StubTvInputService {
+}
diff --git a/tests/tests/tv/src/android/media/tv/cts/NoPermissionTvInputService.java b/tests/tests/tv/src/android/media/tv/cts/NoPermissionTvInputService.java
new file mode 100644
index 0000000..1909e4b
--- /dev/null
+++ b/tests/tests/tv/src/android/media/tv/cts/NoPermissionTvInputService.java
@@ -0,0 +1,20 @@
+/*
+ * 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.media.tv.cts;
+
+public class NoPermissionTvInputService extends StubTvInputService {
+}
diff --git a/tests/tests/tv/src/android/media/tv/cts/StubTunerTvInputService.java b/tests/tests/tv/src/android/media/tv/cts/StubTunerTvInputService.java
new file mode 100644
index 0000000..1241993
--- /dev/null
+++ b/tests/tests/tv/src/android/media/tv/cts/StubTunerTvInputService.java
@@ -0,0 +1,170 @@
+/*
+ * 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.media.tv.cts;
+
+import android.content.ContentResolver;
+import android.content.ContentValues;
+import android.content.Context;
+import android.database.Cursor;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.media.tv.TvContract;
+import android.media.tv.TvInputInfo;
+import android.media.tv.TvInputManager;
+import android.media.tv.TvInputService;
+import android.media.tv.TvTrackInfo;
+import android.net.Uri;
+import android.view.Surface;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+public class StubTunerTvInputService extends TvInputService {
+    private static final List<TvTrackInfo> mTrackList = new ArrayList<>();
+
+    public static void clearTracks() {
+        mTrackList.clear();
+    }
+
+    public static void injectTrack(TvTrackInfo... tracks) {
+        mTrackList.addAll(Arrays.asList(tracks));
+    }
+
+    public static void insertChannels(ContentResolver resolver, TvInputInfo info) {
+        if (!info.getServiceInfo().name.equals(StubTunerTvInputService.class.getName())) {
+            throw new IllegalArgumentException("info mismatch");
+        }
+        ContentValues redValues = new ContentValues();
+        redValues.put(TvContract.Channels.COLUMN_INPUT_ID, info.getId());
+        redValues.put(TvContract.Channels.COLUMN_DISPLAY_NUMBER, "0");
+        redValues.put(TvContract.Channels.COLUMN_DISPLAY_NAME, "Red");
+        redValues.put(TvContract.Channels.COLUMN_INTERNAL_PROVIDER_DATA, new byte[] { 0 });
+        ContentValues greenValues = new ContentValues();
+        greenValues.put(TvContract.Channels.COLUMN_INPUT_ID, info.getId());
+        greenValues.put(TvContract.Channels.COLUMN_DISPLAY_NUMBER, "1");
+        greenValues.put(TvContract.Channels.COLUMN_DISPLAY_NAME, "Green");
+        greenValues.put(TvContract.Channels.COLUMN_INTERNAL_PROVIDER_DATA, new byte[] { 1 });
+        ContentValues blueValues = new ContentValues();
+        blueValues.put(TvContract.Channels.COLUMN_INPUT_ID, info.getId());
+        blueValues.put(TvContract.Channels.COLUMN_DISPLAY_NUMBER, "2");
+        blueValues.put(TvContract.Channels.COLUMN_DISPLAY_NAME, "Blue");
+        blueValues.put(TvContract.Channels.COLUMN_INTERNAL_PROVIDER_DATA, new byte[] { 2 });
+        resolver.bulkInsert(TvContract.Channels.CONTENT_URI,
+                new ContentValues[] { redValues, greenValues, blueValues });
+    }
+
+    public static void deleteChannels(ContentResolver resolver, TvInputInfo info) {
+        if (!info.getServiceInfo().name.equals(StubTunerTvInputService.class.getName())) {
+            throw new IllegalArgumentException("info mismatch");
+        }
+        resolver.delete(TvContract.buildChannelsUriForInput(info.getId()), null, null);
+    }
+
+    @Override
+    public Session onCreateSession(String inputId) {
+        return new StubSessionImpl(this);
+    }
+
+    static class StubSessionImpl extends Session {
+        private static final int[] COLORS = { Color.RED, Color.GREEN, Color.BLUE };
+        private Surface mSurface;
+        private Object mLock = new Object();
+        private int mCurrentIndex = -1;
+        private Context mContext;
+
+        StubSessionImpl(Context context) {
+            super(context);
+            mContext = context;
+        }
+
+        @Override
+        public void onRelease() {
+        }
+
+        private void updateSurfaceLocked() {
+            if (mCurrentIndex >= 0 && mSurface != null) {
+                Canvas c = mSurface.lockCanvas(null);
+                c.drawColor(COLORS[mCurrentIndex]);
+                mSurface.unlockCanvasAndPost(c);
+            }
+        }
+
+        @Override
+        public boolean onSetSurface(Surface surface) {
+            synchronized (mLock) {
+                mSurface = surface;
+                updateSurfaceLocked();
+                return true;
+            }
+        }
+
+        @Override
+        public void onSetStreamVolume(float volume) {
+        }
+
+        @Override
+        public boolean onTune(Uri channelUri) {
+            synchronized (mLock) {
+                notifyVideoUnavailable(TvInputManager.VIDEO_UNAVAILABLE_REASON_TUNING);
+                String[] projection = { TvContract.Channels.COLUMN_INTERNAL_PROVIDER_DATA };
+                Cursor cursor = mContext.getContentResolver().query(
+                        channelUri, projection, null, null, null);
+                try {
+                    if (cursor != null && cursor.moveToNext()) {
+                        mCurrentIndex = cursor.getBlob(0)[0];
+                    } else {
+                        mCurrentIndex = -1;
+                    }
+                } finally {
+                    if (cursor != null) {
+                        cursor.close();
+                    }
+                }
+                updateSurfaceLocked();
+                // Notify tracks
+                if (mCurrentIndex == 0) {
+                    notifyTracksChanged(mTrackList);
+                    for (TvTrackInfo track : mTrackList) {
+                        if (track.getType() == TvTrackInfo.TYPE_VIDEO) {
+                            notifyTrackSelected(TvTrackInfo.TYPE_VIDEO, track.getId());
+                            break;
+                        }
+                    }
+                    for (TvTrackInfo track : mTrackList) {
+                        if (track.getType() == TvTrackInfo.TYPE_AUDIO) {
+                            notifyTrackSelected(TvTrackInfo.TYPE_AUDIO, track.getId());
+                            break;
+                        }
+                    }
+                }
+                notifyVideoAvailable();
+                return true;
+            }
+        }
+
+        @Override
+        public boolean onSelectTrack(int type, String trackId) {
+            notifyTrackSelected(type, trackId);
+            return true;
+        }
+
+        @Override
+        public void onSetCaptionEnabled(boolean enabled) {
+        }
+    }
+}
diff --git a/tests/tests/tv/src/android/media/tv/cts/StubTvInputService.java b/tests/tests/tv/src/android/media/tv/cts/StubTvInputService.java
new file mode 100644
index 0000000..b70dc62
--- /dev/null
+++ b/tests/tests/tv/src/android/media/tv/cts/StubTvInputService.java
@@ -0,0 +1,60 @@
+/*
+ * 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.media.tv.cts;
+
+import android.content.Context;
+import android.media.tv.TvInputService;
+import android.net.Uri;
+import android.view.Surface;
+
+/**
+ * Stub implementation of (@link android.media.tv.TvInputService}.
+ */
+public class StubTvInputService extends TvInputService {
+    @Override
+    public Session onCreateSession(String inputId) {
+        return new StubSessionImpl(this);
+    }
+
+    private static class StubSessionImpl extends Session {
+        StubSessionImpl(Context context) {
+            super(context);
+        }
+
+        @Override
+        public void onRelease() {
+        }
+
+        @Override
+        public boolean onSetSurface(Surface surface) {
+            return false;
+        }
+
+        @Override
+        public void onSetStreamVolume(float volume) {
+        }
+
+        @Override
+        public boolean onTune(Uri channelUri) {
+            return false;
+        }
+
+        @Override
+        public void onSetCaptionEnabled(boolean enabled) {
+        }
+    }
+}
diff --git a/tests/tests/tv/src/android/media/tv/cts/TvContentRatingTest.java b/tests/tests/tv/src/android/media/tv/cts/TvContentRatingTest.java
new file mode 100644
index 0000000..5e6478b
--- /dev/null
+++ b/tests/tests/tv/src/android/media/tv/cts/TvContentRatingTest.java
@@ -0,0 +1,71 @@
+/*
+ * 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.media.tv.cts;
+
+import android.media.tv.TvContentRating;
+
+import java.util.List;
+
+import junit.framework.TestCase;
+
+/**
+ * Test for {@link android.media.tv.TvContentRating}.
+ */
+public class TvContentRatingTest extends TestCase {
+
+    public void testCreateRating() throws Exception {
+        final String DOMAIN = "android.media.tv";
+        final String RATING_SYSTEM = "US_TVPG";
+        final String MAIN_RATING = "US_TVPG_TV_MA";
+        final String SUB_RATING_1 = "US_TVPG_TV_S";
+        final String SUB_RATING_2 = "US_TVPG_TV_V";
+
+        TvContentRating rating = TvContentRating.createRating(DOMAIN, RATING_SYSTEM, MAIN_RATING,
+                SUB_RATING_1, SUB_RATING_2);
+        assertEquals(DOMAIN, rating.getDomain());
+        assertEquals(RATING_SYSTEM, rating.getRatingSystem());
+        assertEquals(MAIN_RATING, rating.getMainRating());
+        List<String> subRatings = rating.getSubRatings();
+        assertEquals(2, subRatings.size());
+        assertTrue("Sub-ratings does not contain " + SUB_RATING_1,
+                subRatings.contains(SUB_RATING_1));
+        assertTrue("Sub-ratings does not contain " + SUB_RATING_2,
+                subRatings.contains(SUB_RATING_2));
+    }
+
+    public void testFlattenAndUnflatten() throws Exception {
+        final String DOMAIN = "android.media.tv";
+        final String RATING_SYSTEM = "US_TVPG";
+        final String MAIN_RATING = "US_TVPG_TV_MA";
+        final String SUB_RATING_1 = "US_TVPG_TV_S";
+        final String SUB_RATING_2 = "US_TVPG_TV_V";
+
+        String flattened = TvContentRating.createRating(DOMAIN, RATING_SYSTEM, MAIN_RATING,
+                SUB_RATING_1, SUB_RATING_2).flattenToString();
+        TvContentRating rating = TvContentRating.unflattenFromString(flattened);
+
+        assertEquals(DOMAIN, rating.getDomain());
+        assertEquals(RATING_SYSTEM, rating.getRatingSystem());
+        assertEquals(MAIN_RATING, rating.getMainRating());
+        List<String> subRatings = rating.getSubRatings();
+        assertEquals(2, subRatings.size());
+        assertTrue("Sub-ratings does not contain " + SUB_RATING_1,
+                subRatings.contains(SUB_RATING_1));
+        assertTrue("Sub-ratings does not contain " + SUB_RATING_2,
+                subRatings.contains(SUB_RATING_2));
+    }
+}
diff --git a/tests/tests/tv/src/android/media/tv/cts/TvContractTest.java b/tests/tests/tv/src/android/media/tv/cts/TvContractTest.java
new file mode 100644
index 0000000..e9f7423
--- /dev/null
+++ b/tests/tests/tv/src/android/media/tv/cts/TvContractTest.java
@@ -0,0 +1,369 @@
+/*
+ * 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.media.tv.cts;
+
+import android.content.ComponentName;
+import android.content.ContentResolver;
+import android.content.ContentUris;
+import android.content.ContentValues;
+import android.content.res.AssetFileDescriptor;
+import android.database.Cursor;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.media.tv.TvContentRating;
+import android.media.tv.TvContract;
+import android.net.Uri;
+import android.os.RemoteException;
+import android.test.AndroidTestCase;
+
+import com.android.cts.tv.R;
+
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.Arrays;
+
+/**
+ * Test for {@link android.media.tv.TvContract}.
+ */
+public class TvContractTest extends AndroidTestCase {
+    private static final String[] CHANNELS_PROJECTION = {
+        TvContract.Channels._ID,
+        TvContract.Channels.COLUMN_INPUT_ID,
+        TvContract.Channels.COLUMN_TYPE,
+        TvContract.Channels.COLUMN_SERVICE_TYPE,
+        TvContract.Channels.COLUMN_ORIGINAL_NETWORK_ID,
+        TvContract.Channels.COLUMN_TRANSPORT_STREAM_ID,
+        TvContract.Channels.COLUMN_SERVICE_ID,
+        TvContract.Channels.COLUMN_DISPLAY_NUMBER,
+        TvContract.Channels.COLUMN_DISPLAY_NAME,
+        TvContract.Channels.COLUMN_NETWORK_AFFILIATION,
+        TvContract.Channels.COLUMN_DESCRIPTION,
+        TvContract.Channels.COLUMN_VIDEO_FORMAT,
+        TvContract.Channels.COLUMN_INTERNAL_PROVIDER_DATA,
+        TvContract.Channels.COLUMN_VERSION_NUMBER,
+    };
+
+    private static final String[] PROGRAMS_PROJECTION = {
+        TvContract.Programs._ID,
+        TvContract.Programs.COLUMN_CHANNEL_ID,
+        TvContract.Programs.COLUMN_TITLE,
+        TvContract.Programs.COLUMN_SEASON_NUMBER,
+        TvContract.Programs.COLUMN_EPISODE_NUMBER,
+        TvContract.Programs.COLUMN_EPISODE_TITLE,
+        TvContract.Programs.COLUMN_START_TIME_UTC_MILLIS,
+        TvContract.Programs.COLUMN_END_TIME_UTC_MILLIS,
+        TvContract.Programs.COLUMN_BROADCAST_GENRE,
+        TvContract.Programs.COLUMN_CANONICAL_GENRE,
+        TvContract.Programs.COLUMN_SHORT_DESCRIPTION,
+        TvContract.Programs.COLUMN_LONG_DESCRIPTION,
+        TvContract.Programs.COLUMN_VIDEO_WIDTH,
+        TvContract.Programs.COLUMN_VIDEO_HEIGHT,
+        TvContract.Programs.COLUMN_AUDIO_LANGUAGE,
+        TvContract.Programs.COLUMN_CONTENT_RATING,
+        TvContract.Programs.COLUMN_POSTER_ART_URI,
+        TvContract.Programs.COLUMN_THUMBNAIL_URI,
+        TvContract.Programs.COLUMN_INTERNAL_PROVIDER_DATA,
+        TvContract.Programs.COLUMN_VERSION_NUMBER,
+    };
+
+    private static long OPERATION_TIME = 1000l;
+
+    private String mInputId;
+    private ContentResolver mContentResolver;
+    private Uri mChannelsUri;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mInputId = TvContract.buildInputId(
+                new ComponentName(getContext(), StubTunerTvInputService.class));
+        mContentResolver = getContext().getContentResolver();
+        mChannelsUri = TvContract.buildChannelsUriForInput(mInputId);
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        // Clean up, just in case we failed to delete the entry when a test failed.
+        // The cotentUris are specific to this package, so this will delete only the
+        // entries inserted by this package.
+        String[] projection = { TvContract.Channels._ID };
+        try (Cursor cursor = mContentResolver.query(mChannelsUri, projection, null, null, null)) {
+            while (cursor != null && cursor.moveToNext()) {
+                long channelId = cursor.getLong(0);
+                mContentResolver.delete(
+                        TvContract.buildProgramsUriForChannel(channelId), null, null);
+            }
+        }
+        mContentResolver.delete(mChannelsUri, null, null);
+        super.tearDown();
+    }
+
+    private static ContentValues createDummyChannelValues(String inputId) {
+        ContentValues values = new ContentValues();
+        values.put(TvContract.Channels.COLUMN_INPUT_ID, inputId);
+        values.put(TvContract.Channels.COLUMN_TYPE, TvContract.Channels.TYPE_OTHER);
+        values.put(TvContract.Channels.COLUMN_SERVICE_TYPE,
+                TvContract.Channels.SERVICE_TYPE_AUDIO_VIDEO);
+        values.put(TvContract.Channels.COLUMN_DISPLAY_NUMBER, "1");
+        values.put(TvContract.Channels.COLUMN_VIDEO_FORMAT, TvContract.Channels.VIDEO_FORMAT_480P);
+
+        return values;
+    }
+
+    private static ContentValues createDummyProgramValues(long channelId) {
+        ContentValues values = new ContentValues();
+        values.put(TvContract.Programs.COLUMN_CHANNEL_ID, channelId);
+        values.put(TvContract.Programs.COLUMN_EPISODE_TITLE, "Title");
+        values.put(TvContract.Programs.COLUMN_CANONICAL_GENRE, TvContract.Programs.Genres.encode(
+                TvContract.Programs.Genres.MOVIES, TvContract.Programs.Genres.DRAMA));
+        TvContentRating rating = TvContentRating.createRating("android.media.tv", "US_TVPG",
+                "US_TVPG_TV_MA", "US_TVPG_S", "US_TVPG_V");
+        values.put(TvContract.Programs.COLUMN_CONTENT_RATING, rating.flattenToString());
+
+        return values;
+    }
+
+    private static void verifyStringColumn(Cursor cursor, ContentValues expectedValues,
+            String columnName) {
+        if (expectedValues.containsKey(columnName)) {
+            assertEquals(expectedValues.getAsString(columnName),
+                    cursor.getString(cursor.getColumnIndex(columnName)));
+        }
+    }
+
+    private static void verifyIntegerColumn(Cursor cursor, ContentValues expectedValues,
+            String columnName) {
+        if (expectedValues.containsKey(columnName)) {
+            assertEquals(expectedValues.getAsInteger(columnName).intValue(),
+                    cursor.getInt(cursor.getColumnIndex(columnName)));
+        }
+    }
+
+    private static void verifyLongColumn(Cursor cursor, ContentValues expectedValues,
+            String columnName) {
+        if (expectedValues.containsKey(columnName)) {
+            assertEquals(expectedValues.getAsLong(columnName).longValue(),
+                    cursor.getLong(cursor.getColumnIndex(columnName)));
+        }
+    }
+
+    private static void verifyBlobColumn(Cursor cursor, ContentValues expectedValues,
+            String columnName) {
+        if (expectedValues.containsKey(columnName)) {
+            byte[] expected = expectedValues.getAsByteArray(columnName);
+            byte[] actual = cursor.getBlob(cursor.getColumnIndex(columnName));
+            assertEquals(expected.length, actual.length);
+            for (int i = 0; i < expected.length; ++i) {
+                assertEquals(expected[i], actual[i]);
+            }
+        }
+    }
+
+    private void verifyChannel(Uri channelUri, ContentValues expectedValues, long channelId) {
+        try (Cursor cursor = mContentResolver.query(
+                channelUri, CHANNELS_PROJECTION, null, null, null)) {
+            assertNotNull(cursor);
+            assertEquals(cursor.getCount(), 1);
+            assertTrue(cursor.moveToNext());
+            assertEquals(channelId, cursor.getLong(cursor.getColumnIndex(TvContract.Channels._ID)));
+            verifyStringColumn(cursor, expectedValues, TvContract.Channels.COLUMN_INPUT_ID);
+            verifyStringColumn(cursor, expectedValues, TvContract.Channels.COLUMN_TYPE);
+            verifyStringColumn(cursor, expectedValues, TvContract.Channels.COLUMN_SERVICE_TYPE);
+            verifyIntegerColumn(cursor, expectedValues,
+                    TvContract.Channels.COLUMN_ORIGINAL_NETWORK_ID);
+            verifyIntegerColumn(cursor, expectedValues,
+                    TvContract.Channels.COLUMN_TRANSPORT_STREAM_ID);
+            verifyIntegerColumn(cursor, expectedValues,
+                    TvContract.Channels.COLUMN_SERVICE_ID);
+            verifyStringColumn(cursor, expectedValues, TvContract.Channels.COLUMN_DISPLAY_NUMBER);
+            verifyStringColumn(cursor, expectedValues, TvContract.Channels.COLUMN_DISPLAY_NAME);
+            verifyStringColumn(cursor, expectedValues,
+                    TvContract.Channels.COLUMN_NETWORK_AFFILIATION);
+            verifyStringColumn(cursor, expectedValues, TvContract.Channels.COLUMN_DESCRIPTION);
+            verifyStringColumn(cursor, expectedValues, TvContract.Channels.COLUMN_VIDEO_FORMAT);
+            verifyBlobColumn(cursor, expectedValues,
+                    TvContract.Channels.COLUMN_INTERNAL_PROVIDER_DATA);
+            verifyIntegerColumn(cursor, expectedValues, TvContract.Channels.COLUMN_VERSION_NUMBER);
+        }
+    }
+
+    public void testChannelsTable() throws Exception {
+        // Test: insert
+        ContentValues values = createDummyChannelValues(mInputId);
+
+        Uri rowUri = mContentResolver.insert(mChannelsUri, values);
+        long channelId = ContentUris.parseId(rowUri);
+        Uri channelUri = TvContract.buildChannelUri(channelId);
+        verifyChannel(channelUri, values, channelId);
+
+        // Test: update
+        values.put(TvContract.Channels.COLUMN_DISPLAY_NUMBER, "1-1");
+        values.put(TvContract.Channels.COLUMN_DISPLAY_NAME, "One dash one");
+        values.put(TvContract.Channels.COLUMN_INTERNAL_PROVIDER_DATA, "Coffee".getBytes());
+
+        mContentResolver.update(channelUri, values, null, null);
+        verifyChannel(channelUri, values, channelId);
+
+        // Test: delete
+        mContentResolver.delete(mChannelsUri, null, null);
+        try (Cursor cursor = mContentResolver.query(
+                mChannelsUri, CHANNELS_PROJECTION, null, null, null)) {
+            assertEquals(0, cursor.getCount());
+        }
+    }
+
+    private void verifyProgram(Uri programUri, ContentValues expectedValues, long programId) {
+        try (Cursor cursor = mContentResolver.query(
+                programUri, PROGRAMS_PROJECTION, null, null, null)) {
+            assertNotNull(cursor);
+            assertEquals(cursor.getCount(), 1);
+            assertTrue(cursor.moveToNext());
+            assertEquals(programId, cursor.getLong(cursor.getColumnIndex(TvContract.Programs._ID)));
+            verifyLongColumn(cursor, expectedValues, TvContract.Programs.COLUMN_CHANNEL_ID);
+            verifyStringColumn(cursor, expectedValues, TvContract.Programs.COLUMN_TITLE);
+            verifyIntegerColumn(cursor, expectedValues, TvContract.Programs.COLUMN_SEASON_NUMBER);
+            verifyIntegerColumn(cursor, expectedValues, TvContract.Programs.COLUMN_EPISODE_NUMBER);
+            verifyStringColumn(cursor, expectedValues, TvContract.Programs.COLUMN_EPISODE_TITLE);
+            verifyLongColumn(cursor, expectedValues,
+                    TvContract.Programs.COLUMN_START_TIME_UTC_MILLIS);
+            verifyLongColumn(cursor, expectedValues,
+                    TvContract.Programs.COLUMN_END_TIME_UTC_MILLIS);
+            verifyStringColumn(cursor, expectedValues, TvContract.Programs.COLUMN_BROADCAST_GENRE);
+            verifyStringColumn(cursor, expectedValues, TvContract.Programs.COLUMN_CANONICAL_GENRE);
+            verifyStringColumn(cursor, expectedValues,
+                    TvContract.Programs.COLUMN_SHORT_DESCRIPTION);
+            verifyStringColumn(cursor, expectedValues, TvContract.Programs.COLUMN_LONG_DESCRIPTION);
+            verifyIntegerColumn(cursor, expectedValues, TvContract.Programs.COLUMN_VIDEO_WIDTH);
+            verifyIntegerColumn(cursor, expectedValues, TvContract.Programs.COLUMN_VIDEO_HEIGHT);
+            verifyStringColumn(cursor, expectedValues, TvContract.Programs.COLUMN_AUDIO_LANGUAGE);
+            verifyStringColumn(cursor, expectedValues, TvContract.Programs.COLUMN_CONTENT_RATING);
+            verifyStringColumn(cursor, expectedValues, TvContract.Programs.COLUMN_POSTER_ART_URI);
+            verifyStringColumn(cursor, expectedValues, TvContract.Programs.COLUMN_THUMBNAIL_URI);
+            verifyBlobColumn(cursor, expectedValues,
+                    TvContract.Programs.COLUMN_INTERNAL_PROVIDER_DATA);
+            verifyIntegerColumn(cursor, expectedValues, TvContract.Programs.COLUMN_VERSION_NUMBER);
+        }
+    }
+
+    public void testChannelLogo() throws Exception {
+        // Set-up: add a channel.
+        ContentValues values = createDummyChannelValues(mInputId);
+        Uri logoUri = TvContract.buildChannelLogoUri(mContentResolver.insert(mChannelsUri, values));
+        Bitmap logo = BitmapFactory.decodeResource(getContext().getResources(), R.drawable.robot);
+
+        // Write
+        try (AssetFileDescriptor fd = mContentResolver.openAssetFileDescriptor(logoUri, "w")) {
+            try (OutputStream os = fd.createOutputStream()) {
+                logo.compress(Bitmap.CompressFormat.PNG, 100, os);
+            }
+        }
+
+        // Give some time for TvProvider to process the logo.
+        Thread.sleep(OPERATION_TIME);
+
+        // Read and verify
+        try (AssetFileDescriptor fd = mContentResolver.openAssetFileDescriptor(logoUri, "r")) {
+            try (InputStream is = fd.createInputStream()) {
+                // Assure that the stream is decodable as a Bitmap.
+                BitmapFactory.decodeStream(is);
+            }
+        }
+    }
+
+    public void testProgramsTable() throws Exception {
+        // Set-up: add a channel.
+        ContentValues values = createDummyChannelValues(mInputId);
+        long channelId = ContentUris.parseId(mContentResolver.insert(mChannelsUri, values));
+        Uri programsUri = TvContract.buildProgramsUriForChannel(channelId);
+
+        // Test: insert
+        values = createDummyProgramValues(channelId);
+
+        Uri rowUri = mContentResolver.insert(programsUri, values);
+        long programId = ContentUris.parseId(rowUri);
+        Uri programUri = TvContract.buildProgramUri(programId);
+        verifyProgram(programUri, values, programId);
+
+        // Test: update
+        values.put(TvContract.Programs.COLUMN_EPISODE_TITLE, "Sample title");
+        values.put(TvContract.Programs.COLUMN_SHORT_DESCRIPTION, "Short description");
+        values.put(TvContract.Programs.COLUMN_INTERNAL_PROVIDER_DATA, "Coffee".getBytes());
+
+        mContentResolver.update(programUri, values, null, null);
+        verifyProgram(programUri, values, programId);
+
+        // Test: delete
+        mContentResolver.delete(programsUri, null, null);
+        try (Cursor cursor = mContentResolver.query(
+                programsUri, PROGRAMS_PROJECTION, null, null, null)) {
+            assertEquals(0, cursor.getCount());
+        }
+    }
+
+    public void testProgramsScheduleOverlap() throws Exception {
+        final long startMillis = 1403712000000l;  // Jun 25 2014 16:00 UTC
+        final long endMillis = 1403719200000l;  // Jun 25 2014 18:00 UTC
+        final long hour = 3600000l;
+
+        // Set-up: add a channel and program.
+        ContentValues values = createDummyChannelValues(mInputId);
+        long channelId = ContentUris.parseId(mContentResolver.insert(mChannelsUri, values));
+        Uri programsUri = TvContract.buildProgramsUriForChannel(channelId);
+        values = createDummyProgramValues(channelId);
+        values.put(TvContract.Programs.COLUMN_START_TIME_UTC_MILLIS, startMillis);
+        values.put(TvContract.Programs.COLUMN_END_TIME_UTC_MILLIS, endMillis);
+        mContentResolver.insert(programsUri, values);
+
+        // Overlap 1: starts early, ends early.
+        try (Cursor cursor = mContentResolver.query(TvContract.buildProgramsUriForChannel(channelId,
+                startMillis - hour, endMillis - hour), PROGRAMS_PROJECTION, null, null, null)) {
+            assertEquals(1, cursor.getCount());
+        }
+
+        // Overlap 2: starts early, ends late.
+        try (Cursor cursor = mContentResolver.query(TvContract.buildProgramsUriForChannel(channelId,
+                startMillis - hour, endMillis + hour), PROGRAMS_PROJECTION, null, null, null)) {
+            assertEquals(1, cursor.getCount());
+        }
+
+        // Overlap 3: starts early, ends late.
+        try (Cursor cursor = mContentResolver.query(TvContract.buildProgramsUriForChannel(channelId,
+                startMillis + hour / 2, endMillis - hour / 2), PROGRAMS_PROJECTION,
+                null, null, null)) {
+            assertEquals(1, cursor.getCount());
+        }
+
+        // Overlap 4: starts late, ends late.
+        try (Cursor cursor = mContentResolver.query(TvContract.buildProgramsUriForChannel(channelId,
+                startMillis + hour, endMillis + hour), PROGRAMS_PROJECTION, null, null, null)) {
+            assertEquals(1, cursor.getCount());
+        }
+
+        // Non-overlap 1: ends too early.
+        try (Cursor cursor = mContentResolver.query(TvContract.buildProgramsUriForChannel(channelId,
+                startMillis - hour, startMillis - hour / 2), PROGRAMS_PROJECTION,
+                null, null, null)) {
+            assertEquals(0, cursor.getCount());
+        }
+
+        // Non-overlap 2: starts too late
+        try (Cursor cursor = mContentResolver.query(TvContract.buildProgramsUriForChannel(channelId,
+                endMillis + hour, endMillis + hour * 2), PROGRAMS_PROJECTION, null, null, null)) {
+            assertEquals(0, cursor.getCount());
+        }
+    }
+}
diff --git a/tests/tests/tv/src/android/media/tv/cts/TvInputInfoTest.java b/tests/tests/tv/src/android/media/tv/cts/TvInputInfoTest.java
new file mode 100644
index 0000000..ba6a824
--- /dev/null
+++ b/tests/tests/tv/src/android/media/tv/cts/TvInputInfoTest.java
@@ -0,0 +1,111 @@
+/*
+ * 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.media.tv.cts;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.media.tv.TvInputInfo;
+import android.media.tv.TvInputManager;
+import android.test.AndroidTestCase;
+
+/**
+ * Test for {@link android.media.tv.TvInputInfo}.
+ */
+public class TvInputInfoTest extends AndroidTestCase {
+    private TvInputInfo mStubInfo;
+    private PackageManager mPackageManager;
+
+    @Override
+    public void setUp() throws Exception {
+        if (!Utils.hasTvInputFramework(getContext())) {
+            return;
+        }
+        TvInputManager manager =
+                (TvInputManager) getContext().getSystemService(Context.TV_INPUT_SERVICE);
+        for (TvInputInfo info : manager.getTvInputList()) {
+            if (info.getServiceInfo().name.equals(
+                    StubTunerTvInputService.class.getName())) {
+                mStubInfo = info;
+                break;
+            }
+        }
+        mPackageManager = getContext().getPackageManager();
+    }
+
+    public void testGetIntentForSettingsActivity() throws Exception {
+        if (!Utils.hasTvInputFramework(getContext())) {
+            return;
+        }
+        Intent intent = mStubInfo.getIntentForSettingsActivity();
+
+        assertEquals(intent.getComponent(), new ComponentName(getContext(),
+                TvInputSettingsActivityStub.class));
+        String inputId = intent.getStringExtra(TvInputInfo.EXTRA_INPUT_ID);
+        assertEquals(mStubInfo.getId(), inputId);
+    }
+
+    public void testGetIntentForSetupActivity() throws Exception {
+        if (!Utils.hasTvInputFramework(getContext())) {
+            return;
+        }
+        Intent intent = mStubInfo.getIntentForSetupActivity();
+
+        assertEquals(intent.getComponent(), new ComponentName(getContext(),
+                TvInputSetupActivityStub.class));
+        String inputId = intent.getStringExtra(TvInputInfo.EXTRA_INPUT_ID);
+        assertEquals(mStubInfo.getId(), inputId);
+    }
+
+    public void testTunerHasNoParentId() throws Exception {
+        if (!Utils.hasTvInputFramework(getContext())) {
+            return;
+        }
+        assertNull(mStubInfo.getParentId());
+    }
+
+    public void testGetTypeForTuner() throws Exception {
+        if (!Utils.hasTvInputFramework(getContext())) {
+            return;
+        }
+        assertEquals(mStubInfo.getType(), TvInputInfo.TYPE_TUNER);
+    }
+
+    public void testTunerIsNotPassthroughInput() throws Exception {
+        if (!Utils.hasTvInputFramework(getContext())) {
+            return;
+        }
+        assertFalse(mStubInfo.isPassthroughInputType());
+    }
+
+    public void testLoadIcon() throws Exception {
+        if (!Utils.hasTvInputFramework(getContext())) {
+            return;
+        }
+        assertEquals(mStubInfo.loadIcon(getContext()).getConstantState(),
+                mStubInfo.getServiceInfo().loadIcon(mPackageManager).getConstantState());
+    }
+
+    public void testLoadLabel() throws Exception {
+        if (!Utils.hasTvInputFramework(getContext())) {
+            return;
+        }
+        assertEquals(mStubInfo.loadLabel(getContext()),
+                mStubInfo.getServiceInfo().loadLabel(mPackageManager));
+    }
+}
diff --git a/tests/tests/tv/src/android/media/tv/cts/TvInputManagerTest.java b/tests/tests/tv/src/android/media/tv/cts/TvInputManagerTest.java
new file mode 100644
index 0000000..3b486f6
--- /dev/null
+++ b/tests/tests/tv/src/android/media/tv/cts/TvInputManagerTest.java
@@ -0,0 +1,93 @@
+/*
+ * 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.media.tv.cts;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.test.AndroidTestCase;
+import android.media.tv.TvInputInfo;
+import android.media.tv.TvInputManager;
+import android.media.tv.TvInputService;
+import android.media.tv.cts.Utils;
+
+import java.util.List;
+
+/**
+ * Test for {@link android.media.tv.TvInputManager}.
+ */
+public class TvInputManagerTest extends AndroidTestCase {
+    private static final String[] VALID_TV_INPUT_SERVICES = {
+        StubTunerTvInputService.class.getName()
+    };
+    private static final String[] INVALID_TV_INPUT_SERVICES = {
+        NoMetadataTvInputService.class.getName(), NoPermissionTvInputService.class.getName()
+    };
+
+    private String mStubId;
+    private TvInputManager mManager;
+
+    private static TvInputInfo getInfoForClassName(List<TvInputInfo> list, String name) {
+        for (TvInputInfo info : list) {
+            if (info.getServiceInfo().name.equals(name)) {
+                return info;
+            }
+        }
+        return null;
+    }
+
+    @Override
+    public void setUp() throws Exception {
+        if (!Utils.hasTvInputFramework(getContext())) {
+            return;
+        }
+        mManager = (TvInputManager) mContext.getSystemService(Context.TV_INPUT_SERVICE);
+        mStubId = getInfoForClassName(
+                mManager.getTvInputList(), StubTunerTvInputService.class.getName()).getId();
+    }
+
+    public void testGetInputState() throws Exception {
+        if (!Utils.hasTvInputFramework(getContext())) {
+            return;
+        }
+        assertEquals(mManager.getInputState(mStubId), TvInputManager.INPUT_STATE_CONNECTED);
+    }
+
+    public void testGetTvInputInfo() throws Exception {
+        if (!Utils.hasTvInputFramework(getContext())) {
+            return;
+        }
+        assertEquals(mManager.getTvInputInfo(mStubId), getInfoForClassName(
+                mManager.getTvInputList(), StubTunerTvInputService.class.getName()));
+    }
+
+    public void testGetTvInputList() throws Exception {
+        if (!Utils.hasTvInputFramework(getContext())) {
+            return;
+        }
+        List<TvInputInfo> list = mManager.getTvInputList();
+        for (String name : VALID_TV_INPUT_SERVICES) {
+            assertNotNull("getTvInputList() doesn't contain valid input: " + name,
+                    getInfoForClassName(list, name));
+        }
+        for (String name : INVALID_TV_INPUT_SERVICES) {
+            assertNull("getTvInputList() contains invalind input: " + name,
+                    getInfoForClassName(list, name));
+        }
+    }
+}
diff --git a/tests/tests/tv/src/android/media/tv/cts/TvInputServiceTest.java b/tests/tests/tv/src/android/media/tv/cts/TvInputServiceTest.java
new file mode 100644
index 0000000..202319f
--- /dev/null
+++ b/tests/tests/tv/src/android/media/tv/cts/TvInputServiceTest.java
@@ -0,0 +1,367 @@
+/*
+ * 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.media.tv.cts;
+
+import android.app.Activity;
+import android.app.Instrumentation;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.cts.util.PollingCheck;
+import android.database.Cursor;
+import android.test.ActivityInstrumentationTestCase2;
+import android.media.tv.TvContentRating;
+import android.media.tv.TvContract;
+import android.media.tv.TvInputInfo;
+import android.media.tv.TvInputManager;
+import android.media.tv.TvInputService;
+import android.media.tv.TvTrackInfo;
+import android.media.tv.TvView;
+import android.media.tv.TvInputService.Session;
+import android.media.tv.cts.TvInputServiceTest.CountingTvInputService.CountingSession;
+import android.media.tv.cts.Utils;
+import android.net.Uri;
+import android.util.ArrayMap;
+import android.util.SparseIntArray;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+import android.view.Surface;
+
+import com.android.cts.tv.R;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Test {@link android.media.tv.TvInputService}.
+ */
+public class TvInputServiceTest extends ActivityInstrumentationTestCase2<TvViewStubActivity> {
+    /** The maximum time to wait for an operation. */
+    private static final long TIME_OUT = 15000L;
+
+    private TvView mTvView;
+    private Activity mActivity;
+    private Instrumentation mInstrumentation;
+    private TvInputManager mManager;
+    private TvInputInfo mStubInfo;
+    private final StubListener mListener = new StubListener();
+
+    private static class StubListener extends TvView.TvInputListener {
+        private int mChannelRetunedCount;
+        private int mVideoAvailableCount;
+        private int mVideoUnavailableCount;
+        private int mTrackSelectedCount;
+        private int mTrackChangedCount;
+        private int mContentAllowedCount;
+        private int mContentBlockedCount;
+
+        @Override
+        public void onChannelRetuned(String inputId, Uri channelUri) {
+            mChannelRetunedCount++;
+        }
+
+        @Override
+        public void onVideoAvailable(String inputId) {
+            mVideoAvailableCount++;
+        }
+
+        @Override
+        public void onVideoUnavailable(String inputId, int reason) {
+            mVideoUnavailableCount++;
+        }
+
+        @Override
+        public void onTrackSelected(String inputId, int type, String trackId) {
+            mTrackSelectedCount++;
+        }
+
+        @Override
+        public void onTracksChanged(String inputId, List<TvTrackInfo> trackList) {
+            mTrackChangedCount++;
+        }
+
+        @Override
+        public void onContentAllowed(String inputId) {
+            mContentAllowedCount++;
+        }
+
+        @Override
+        public void onContentBlocked(String inputId, TvContentRating rating) {
+            mContentBlockedCount++;
+        }
+    }
+
+    public TvInputServiceTest() {
+        super(TvViewStubActivity.class);
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        if (!Utils.hasTvInputFramework(getActivity())) {
+            return;
+        }
+        mActivity = getActivity();
+        mInstrumentation = getInstrumentation();
+        mTvView = (TvView) mActivity.findViewById(R.id.tvview);
+        mManager = (TvInputManager) mActivity.getSystemService(Context.TV_INPUT_SERVICE);
+        for (TvInputInfo info : mManager.getTvInputList()) {
+            if (info.getServiceInfo().name.equals(CountingTvInputService.class.getName())) {
+                mStubInfo = info;
+                break;
+            }
+        }
+        assertNotNull(mStubInfo);
+        mTvView.setTvInputListener(mListener);
+
+        CountingTvInputService.sSession = null;
+    }
+
+    public void testTvInputService() throws Throwable {
+        if (!Utils.hasTvInputFramework(getActivity())) {
+            return;
+        }
+        verifyCommandTune();
+        verifyCommandSetStreamVolume();
+        verifyCommandSetCaptionEnabled();
+        verifyCommandSelectTrack();
+        verifyCommandDispatchKeyEvent();
+        verifyCallbackChannelRetuned();
+        verifyCallbackVideoAvailable();
+        verifyCallbackVideoUnavailable();
+        verifyCallbackTracksChanged();
+        verifyCallbackTrackSelected();
+        verifyCallbackContentAllowed();
+        verifyCallbackContentBlocked();
+
+        runTestOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                mTvView.reset();
+            }
+        });
+        mInstrumentation.waitForIdleSync();
+    }
+
+    public void verifyCommandTune() {
+        Uri fakeChannelUri = TvContract.buildChannelUri(0);
+        mTvView.tune(mStubInfo.getId(), fakeChannelUri);
+        mInstrumentation.waitForIdleSync();
+        new PollingCheck(TIME_OUT) {
+            @Override
+            protected boolean check() {
+                CountingSession session = CountingTvInputService.sSession;
+                return session != null && session.mTuneCount > 0;
+            }
+        }.run();
+    }
+
+    public void verifyCommandSetStreamVolume() {
+        mTvView.setStreamVolume(1.0f);;
+        mInstrumentation.waitForIdleSync();
+        new PollingCheck(TIME_OUT) {
+            @Override
+            protected boolean check() {
+                CountingSession session = CountingTvInputService.sSession;
+                return session != null && session.mSetStreamVolumeCount > 0;
+            }
+        }.run();
+    }
+
+    public void verifyCommandSetCaptionEnabled() {
+        mTvView.setCaptionEnabled(true);
+        mInstrumentation.waitForIdleSync();
+        new PollingCheck(TIME_OUT) {
+            @Override
+            protected boolean check() {
+                CountingSession session = CountingTvInputService.sSession;
+                return session != null && session.mSetCaptionEnabledCount > 0;
+            }
+        }.run();
+    }
+
+    public void verifyCommandSelectTrack() {
+        mTvView.selectTrack(TvTrackInfo.TYPE_AUDIO, "dummyTrackId");
+        mInstrumentation.waitForIdleSync();
+        new PollingCheck(TIME_OUT) {
+            @Override
+            protected boolean check() {
+                CountingSession session = CountingTvInputService.sSession;
+                return session != null && session.mSetStreamVolumeCount > 0;
+            }
+        }.run();
+    }
+
+    public void verifyCommandDispatchKeyEvent() {
+        mTvView.dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_K));
+        mInstrumentation.waitForIdleSync();
+        new PollingCheck(TIME_OUT) {
+            @Override
+            protected boolean check() {
+                CountingSession session = CountingTvInputService.sSession;
+                return session != null && session.mKeyDownCount > 0;
+            }
+        }.run();
+    }
+
+    public void verifyCallbackChannelRetuned() {
+        CountingSession session = CountingTvInputService.sSession;
+        assertNotNull(session);
+        Uri fakeChannelUri = TvContract.buildChannelUri(0);
+        session.notifyChannelRetuned(fakeChannelUri);
+        new PollingCheck(TIME_OUT) {
+            @Override
+            protected boolean check() {
+                return mListener.mChannelRetunedCount > 0;
+            }
+        }.run();
+    }
+
+    public void verifyCallbackVideoAvailable() {
+        CountingSession session = CountingTvInputService.sSession;
+        assertNotNull(session);
+        session.notifyVideoAvailable();
+        new PollingCheck(TIME_OUT) {
+            @Override
+            protected boolean check() {
+                return mListener.mVideoAvailableCount > 0;
+            }
+        }.run();
+    }
+
+    public void verifyCallbackVideoUnavailable() {
+        CountingSession session = CountingTvInputService.sSession;
+        assertNotNull(session);
+        session.notifyVideoUnavailable(TvInputManager.VIDEO_UNAVAILABLE_REASON_TUNING);
+        new PollingCheck(TIME_OUT) {
+            @Override
+            protected boolean check() {
+                return mListener.mVideoUnavailableCount > 0;
+            }
+        }.run();
+    }
+
+    public void verifyCallbackTracksChanged() {
+        CountingSession session = CountingTvInputService.sSession;
+        assertNotNull(session);
+        session.notifyTracksChanged(new ArrayList<TvTrackInfo>());
+        new PollingCheck(TIME_OUT) {
+            @Override
+            protected boolean check() {
+                return mListener.mTrackChangedCount > 0;
+            }
+        }.run();
+    }
+
+    public void verifyCallbackTrackSelected() {
+        CountingSession session = CountingTvInputService.sSession;
+        assertNotNull(session);
+        session.notifyTrackSelected(TvTrackInfo.TYPE_SUBTITLE, null);
+        new PollingCheck(TIME_OUT) {
+            @Override
+            protected boolean check() {
+                return mListener.mTrackSelectedCount > 0;
+            }
+        }.run();
+    }
+
+    public void verifyCallbackContentAllowed() {
+        CountingSession session = CountingTvInputService.sSession;
+        assertNotNull(session);
+        session.notifyContentAllowed();
+        new PollingCheck(TIME_OUT) {
+            @Override
+            protected boolean check() {
+                return mListener.mContentAllowedCount > 0;
+            }
+        }.run();
+    }
+
+    public void verifyCallbackContentBlocked() {
+        CountingSession session = CountingTvInputService.sSession;
+        assertNotNull(session);
+        TvContentRating rating = TvContentRating.createRating("android.media.tv", "US_TVPG",
+                "US_TVPG_TV_MA", "US_TVPG_S", "US_TVPG_V");
+        session.notifyContentBlocked(rating);
+        new PollingCheck(TIME_OUT) {
+            @Override
+            protected boolean check() {
+                return mListener.mContentBlockedCount > 0;
+            }
+        }.run();
+    }
+
+    public static class CountingTvInputService extends StubTvInputService {
+        static CountingTvInputService sInstance;
+        static CountingSession sSession;
+
+        @Override
+        public Session onCreateSession(String inputId) {
+            sSession = new CountingSession(this);
+            return sSession;
+        }
+
+        public static class CountingSession extends Session {
+            public volatile int mTuneCount;
+            public volatile int mSetStreamVolumeCount;
+            public volatile int mSetCaptionEnabledCount;
+            public volatile int mSelectTrackCount;
+            public volatile int mKeyDownCount;
+
+            CountingSession(Context context) {
+                super(context);
+            }
+
+            @Override
+            public void onRelease() {
+            }
+
+            @Override
+            public boolean onSetSurface(Surface surface) {
+                return false;
+            }
+
+            @Override
+            public boolean onTune(Uri channelUri) {
+                mTuneCount++;
+                return false;
+            }
+
+            @Override
+            public void onSetStreamVolume(float volume) {
+                mSetStreamVolumeCount++;
+            }
+
+            @Override
+            public void onSetCaptionEnabled(boolean enabled) {
+                mSetCaptionEnabledCount++;
+            }
+
+            @Override
+            public boolean onSelectTrack(int type, String id) {
+                mSelectTrackCount++;
+                return false;
+            }
+
+            @Override
+            public boolean onKeyDown(int keyCode, KeyEvent event) {
+                mKeyDownCount++;
+                return false;
+            }
+        }
+    }
+}
diff --git a/tests/tests/tv/src/android/media/tv/cts/TvInputSettingsActivityStub.java b/tests/tests/tv/src/android/media/tv/cts/TvInputSettingsActivityStub.java
new file mode 100644
index 0000000..00dfd8b
--- /dev/null
+++ b/tests/tests/tv/src/android/media/tv/cts/TvInputSettingsActivityStub.java
@@ -0,0 +1,23 @@
+/*
+ * 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.media.tv.cts;
+
+import android.preference.PreferenceActivity;
+
+public class TvInputSettingsActivityStub extends PreferenceActivity {
+
+}
diff --git a/tests/tests/tv/src/android/media/tv/cts/TvInputSetupActivityStub.java b/tests/tests/tv/src/android/media/tv/cts/TvInputSetupActivityStub.java
new file mode 100644
index 0000000..2e5467b
--- /dev/null
+++ b/tests/tests/tv/src/android/media/tv/cts/TvInputSetupActivityStub.java
@@ -0,0 +1,23 @@
+/*
+ * 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.media.tv.cts;
+
+import android.preference.PreferenceActivity;
+
+public class TvInputSetupActivityStub extends PreferenceActivity {
+
+}
diff --git a/tests/tests/tv/src/android/media/tv/cts/TvViewStubActivity.java b/tests/tests/tv/src/android/media/tv/cts/TvViewStubActivity.java
new file mode 100644
index 0000000..5035e14
--- /dev/null
+++ b/tests/tests/tv/src/android/media/tv/cts/TvViewStubActivity.java
@@ -0,0 +1,30 @@
+/*
+ * 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.media.tv.cts;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+import com.android.cts.tv.R;
+
+public class TvViewStubActivity extends Activity {
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.tvview_layout);
+    }
+}
diff --git a/tests/tests/tv/src/android/media/tv/cts/TvViewTest.java b/tests/tests/tv/src/android/media/tv/cts/TvViewTest.java
new file mode 100644
index 0000000..3083f88
--- /dev/null
+++ b/tests/tests/tv/src/android/media/tv/cts/TvViewTest.java
@@ -0,0 +1,311 @@
+/*
+ * 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.media.tv.cts;
+
+import android.app.Activity;
+import android.app.Instrumentation;
+import android.content.Context;
+import android.cts.util.PollingCheck;
+import android.database.Cursor;
+import android.test.ActivityInstrumentationTestCase2;
+import android.media.tv.TvContentRating;
+import android.media.tv.TvContract;
+import android.media.tv.TvInputInfo;
+import android.media.tv.TvInputManager;
+import android.media.tv.TvTrackInfo;
+import android.media.tv.TvView;
+import android.media.tv.cts.Utils;
+import android.net.Uri;
+import android.util.ArrayMap;
+import android.util.SparseIntArray;
+
+import com.android.cts.tv.R;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Test {@link android.media.tv.TvView}.
+ */
+public class TvViewTest extends ActivityInstrumentationTestCase2<TvViewStubActivity> {
+    /** The maximum time to wait for an operation. */
+    private static final long TIME_OUT = 15000L;
+
+    private TvView mTvView;
+    private Activity mActivity;
+    private Instrumentation mInstrumentation;
+    private TvInputManager mManager;
+    private TvInputInfo mStubInfo;
+    private final MockListener mListener = new MockListener();
+
+    private static class MockListener extends TvView.TvInputListener {
+        private final Map<String, Boolean> mVideoAvailableMap = new ArrayMap<>();
+        private final Map<String, SparseIntArray> mSelectedTrackGenerationMap = new ArrayMap<>();
+        private final Map<String, Integer> mTracksGenerationMap = new ArrayMap<>();
+        private Object mLock = new Object();
+
+        public boolean isVideoAvailable(String inputId) {
+            synchronized (mLock) {
+                Boolean available = mVideoAvailableMap.get(inputId);
+                return available == null ? true : available.booleanValue();
+            }
+        }
+
+        public int getSelectedTrackGeneration(String inputId, int type) {
+            synchronized (mLock) {
+                SparseIntArray selectedTrackGenerationMap =
+                        mSelectedTrackGenerationMap.get(inputId);
+                if (selectedTrackGenerationMap == null) {
+                    return 0;
+                }
+                return selectedTrackGenerationMap.get(type, 0);
+            }
+        }
+
+        public int getTrackGeneration(String inputId) {
+            synchronized (mLock) {
+                Integer tracksGeneration = mTracksGenerationMap.get(inputId);
+                return tracksGeneration == null ? 0 : tracksGeneration.intValue();
+            }
+        }
+
+        @Override
+        public void onVideoAvailable(String inputId) {
+            synchronized (mLock) {
+                mVideoAvailableMap.put(inputId, true);
+            }
+        }
+
+        @Override
+        public void onVideoUnavailable(String inputId, int reason) {
+            synchronized (mLock) {
+                mVideoAvailableMap.put(inputId, false);
+            }
+        }
+
+        @Override
+        public void onTrackSelected(String inputId, int type, String trackId) {
+            synchronized (mLock) {
+                SparseIntArray selectedTrackGenerationMap =
+                        mSelectedTrackGenerationMap.get(inputId);
+                if (selectedTrackGenerationMap == null) {
+                    selectedTrackGenerationMap = new SparseIntArray();
+                    mSelectedTrackGenerationMap.put(inputId, selectedTrackGenerationMap);
+                }
+                int currentGeneration = selectedTrackGenerationMap.get(type, 0);
+                selectedTrackGenerationMap.put(type, currentGeneration + 1);
+            }
+        }
+
+        @Override
+        public void onTracksChanged(String inputId, List<TvTrackInfo> trackList) {
+            synchronized (mLock) {
+                Integer tracksGeneration = mTracksGenerationMap.get(inputId);
+                mTracksGenerationMap.put(inputId,
+                        tracksGeneration == null ? 1 : (tracksGeneration + 1));
+            }
+        }
+    }
+
+    /**
+     * Instantiates a new TV view test.
+     */
+    public TvViewTest() {
+        super(TvViewStubActivity.class);
+    }
+
+    /**
+     * Find the TV view specified by id.
+     *
+     * @param id the id
+     * @return the TV view
+     */
+    private TvView findTvViewById(int id) {
+        return (TvView) mActivity.findViewById(id);
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mActivity = getActivity();
+        if (!Utils.hasTvInputFramework(getActivity())) {
+            return;
+        }
+        mInstrumentation = getInstrumentation();
+        mTvView = findTvViewById(R.id.tvview);
+        mManager = (TvInputManager) mActivity.getSystemService(Context.TV_INPUT_SERVICE);
+        for (TvInputInfo info : mManager.getTvInputList()) {
+            if (info.getServiceInfo().name.equals(StubTunerTvInputService.class.getName())) {
+                mStubInfo = info;
+                break;
+            }
+        }
+        assertNotNull(mStubInfo);
+        mTvView.setTvInputListener(mListener);
+        StubTunerTvInputService.clearTracks();
+    }
+
+    public void testConstructor() throws Exception {
+        new TvView(mActivity);
+
+        new TvView(mActivity, null);
+
+        new TvView(mActivity, null, 0);
+    }
+
+    private void tryTuneAllChannels(Runnable runOnEachChannel) throws Throwable {
+        StubTunerTvInputService.insertChannels(mActivity.getContentResolver(), mStubInfo);
+
+        Uri uri = TvContract.buildChannelsUriForInput(mStubInfo.getId());
+        String[] projection = { TvContract.Channels._ID };
+        Cursor cursor = mActivity.getContentResolver().query(
+                uri, projection, null, null, null);
+        try {
+            while (cursor != null && cursor.moveToNext()) {
+                long channelId = cursor.getLong(0);
+                Uri channelUri = TvContract.buildChannelUri(channelId);
+                mTvView.tune(mStubInfo.getId(), channelUri);
+                mInstrumentation.waitForIdleSync();
+                new PollingCheck(TIME_OUT) {
+                    @Override
+                    protected boolean check() {
+                        return mListener.isVideoAvailable(mStubInfo.getId());
+                    }
+                }.run();
+
+                if (runOnEachChannel != null) {
+                    runOnEachChannel.run();
+                }
+            }
+        } finally {
+            if (cursor != null) {
+                cursor.close();
+            }
+        }
+        runTestOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                mTvView.reset();
+            }
+        });
+        mInstrumentation.waitForIdleSync();
+
+        StubTunerTvInputService.deleteChannels(mActivity.getContentResolver(), mStubInfo);
+    }
+
+    public void testSimpleTune() throws Throwable {
+        if (!Utils.hasTvInputFramework(getActivity())) {
+            return;
+        }
+        tryTuneAllChannels(null);
+    }
+
+    private void selectTrackAndVerify(final int type, final TvTrackInfo track,
+            List<TvTrackInfo> tracks) {
+        final int previousGeneration = mListener.getSelectedTrackGeneration(
+                mStubInfo.getId(), type);
+        mTvView.selectTrack(type, track == null ? null : track.getId());
+        new PollingCheck(TIME_OUT) {
+            @Override
+            protected boolean check() {
+                return mListener.getSelectedTrackGeneration(
+                        mStubInfo.getId(), type) > previousGeneration;
+            }
+        }.run();
+        String selectedTrackId = mTvView.getSelectedTrack(type);
+        assertEquals(selectedTrackId, track == null ? null : track.getId());
+        if (selectedTrackId != null) {
+            TvTrackInfo selectedTrack = null;
+            for (TvTrackInfo item : tracks) {
+                if (item.getId().equals(selectedTrackId)) {
+                    selectedTrack = item;
+                    break;
+                }
+            }
+            assertNotNull(selectedTrack);
+            assertEquals(track.getType(), selectedTrack.getType());
+            assertEquals(track.getExtra(), selectedTrack.getExtra());
+            switch (track.getType()) {
+                case TvTrackInfo.TYPE_VIDEO:
+                    assertEquals(track.getVideoHeight(), selectedTrack.getVideoHeight());
+                    assertEquals(track.getVideoWidth(), selectedTrack.getVideoWidth());
+                    break;
+                case TvTrackInfo.TYPE_AUDIO:
+                    assertEquals(track.getAudioChannelCount(),
+                            selectedTrack.getAudioChannelCount());
+                    assertEquals(track.getAudioSampleRate(), selectedTrack.getAudioSampleRate());
+                    assertEquals(track.getLanguage(), selectedTrack.getLanguage());
+                    break;
+                case TvTrackInfo.TYPE_SUBTITLE:
+                    assertEquals(track.getLanguage(), selectedTrack.getLanguage());
+                    break;
+                default:
+                    fail("Unrecognized type: " + track.getType());
+            }
+        }
+    }
+
+    public void testTrackChange() throws Throwable {
+        if (!Utils.hasTvInputFramework(getActivity())) {
+            return;
+        }
+        TvTrackInfo videoTrack1 = new TvTrackInfo.Builder(TvTrackInfo.TYPE_VIDEO, "video-HD")
+                .setVideoHeight(1920).setVideoWidth(1080).build();
+        TvTrackInfo videoTrack2 = new TvTrackInfo.Builder(TvTrackInfo.TYPE_VIDEO, "video-SD")
+                .setVideoHeight(640).setVideoWidth(360).build();
+        TvTrackInfo audioTrack1 =
+                new TvTrackInfo.Builder(TvTrackInfo.TYPE_AUDIO, "audio-stereo-eng")
+                .setLanguage("eng").setAudioChannelCount(2).setAudioSampleRate(48000).build();
+        TvTrackInfo audioTrack2 = new TvTrackInfo.Builder(TvTrackInfo.TYPE_AUDIO, "audio-mono-esp")
+                .setLanguage("esp").setAudioChannelCount(1).setAudioSampleRate(48000).build();
+        TvTrackInfo subtitleTrack1 =
+                new TvTrackInfo.Builder(TvTrackInfo.TYPE_SUBTITLE, "subtitle-eng")
+                .setLanguage("eng").build();
+        TvTrackInfo subtitleTrack2 =
+                new TvTrackInfo.Builder(TvTrackInfo.TYPE_SUBTITLE, "subtitle-esp")
+                .setLanguage("esp").build();
+
+        StubTunerTvInputService.injectTrack(videoTrack1, videoTrack2, audioTrack1, audioTrack2,
+                subtitleTrack1, subtitleTrack2);
+
+        final List<TvTrackInfo> tracks = new ArrayList<TvTrackInfo>();
+        Collections.addAll(tracks, videoTrack1, videoTrack2, audioTrack1, audioTrack2,
+                subtitleTrack1, subtitleTrack2);
+        tryTuneAllChannels(new Runnable() {
+            @Override
+            public void run() {
+                new PollingCheck(TIME_OUT) {
+                    @Override
+                    protected boolean check() {
+                        return mTvView.getTracks(TvTrackInfo.TYPE_AUDIO) != null;
+                    }
+                }.run();
+                final int[] types = { TvTrackInfo.TYPE_AUDIO, TvTrackInfo.TYPE_VIDEO,
+                    TvTrackInfo.TYPE_SUBTITLE };
+                for (int type : types) {
+                    final int typeF = type;
+                    for (TvTrackInfo track : mTvView.getTracks(type)) {
+                        selectTrackAndVerify(type, track, tracks);
+                    }
+                    selectTrackAndVerify(TvTrackInfo.TYPE_SUBTITLE, null, tracks);
+                }
+            }
+        });
+    }
+}
diff --git a/tests/tests/tv/src/android/media/tv/cts/Utils.java b/tests/tests/tv/src/android/media/tv/cts/Utils.java
new file mode 100644
index 0000000..0eb28c3
--- /dev/null
+++ b/tests/tests/tv/src/android/media/tv/cts/Utils.java
@@ -0,0 +1,12 @@
+package android.media.tv.cts;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+
+public class Utils {
+    private Utils() { }
+
+    public static boolean hasTvInputFramework(Context context) {
+        return context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_LEANBACK);
+    }
+}
diff --git a/tests/tests/uiautomation/Android.mk b/tests/tests/uiautomation/Android.mk
new file mode 100644
index 0000000..bb0fc19
--- /dev/null
+++ b/tests/tests/uiautomation/Android.mk
@@ -0,0 +1,30 @@
+# 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_STATIC_JAVA_LIBRARIES := ctstestrunner ub-uiautomator
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_PACKAGE_NAME := CtsUiAutomationTestCases
+
+LOCAL_SDK_VERSION := current
+
+include $(BUILD_CTS_PACKAGE)
+
diff --git a/tests/tests/uiautomation/AndroidManifest.xml b/tests/tests/uiautomation/AndroidManifest.xml
new file mode 100644
index 0000000..06b31c8
--- /dev/null
+++ b/tests/tests/uiautomation/AndroidManifest.xml
@@ -0,0 +1,44 @@
+<?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="android.app.cts.uiautomation">
+
+  <application android:theme="@android:style/Theme.Holo.NoActionBar" >
+
+      <uses-library android:name="android.test.runner"/>
+
+      <activity
+          android:name="android.app.uiautomation.cts.UiAutomationTestFirstActivity"
+          android:exported="true">
+      </activity>
+
+      <activity
+          android:name="android.app.uiautomation.cts.UiAutomationTestSecondActivity"
+          android:exported="true">
+      </activity>
+
+  </application>
+
+  <instrumentation android:name="android.support.test.uiautomator.UiAutomatorInstrumentationTestRunner"
+                   android:targetPackage="android.app.cts.uiautomation">
+        <meta-data android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
+
+</manifest>
diff --git a/tests/tests/uiautomation/res/layout/ui_automation_test.xml b/tests/tests/uiautomation/res/layout/ui_automation_test.xml
new file mode 100644
index 0000000..fb9621d
--- /dev/null
+++ b/tests/tests/uiautomation/res/layout/ui_automation_test.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** 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.
+*/
+-->
+<ListView xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/list_view"
+    android:layout_width="fill_parent"
+    android:layout_height="fill_parent">
+</ListView>
diff --git a/tests/tests/uiautomation/res/values/strings.xml b/tests/tests/uiautomation/res/values/strings.xml
new file mode 100644
index 0000000..7e4e4e4
--- /dev/null
+++ b/tests/tests/uiautomation/res/values/strings.xml
@@ -0,0 +1,55 @@
+<?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="uiautomation_test_activity">Cheeses</string>
+
+    <string-array name="some_cheeses">
+        <item>Abbaye de Belloc</item>
+        <item>Abbaye de Belval</item>
+        <item>Abbaye de Citeaux</item>
+        <item>Abbaye du Mont des Cats</item>
+        <item>Abbot’s Gold</item>
+        <item>Acapella</item>
+        <item>Acorn</item>
+        <item>Adelost</item>
+        <item>Affidelice au Chablis</item>
+        <item>Afuega\'l Pitu</item>
+        <item>Aged Gouda</item>
+        <item>Airag</item>
+        <item>Airedale</item>
+        <item>Aisy Cendre</item>
+        <item>Allgauer Emmentaler</item>
+        <item>Babybel</item>
+        <item>Baby Swiss</item>
+        <item>Baguette Laonnaise</item>
+        <item>Bakers</item>
+        <item>Balaton</item>
+        <item>Bandal</item>
+        <item>Banon</item>
+        <item>Barry\'s Bay Cheddar</item>
+        <item>Basing</item>
+        <item>Basket Cheese</item>
+        <item>Bath Cheese</item>
+        <item>Bavarian Bergkase</item>
+        <item>Baylough</item>
+        <item>Beauvoorde</item>
+        <item>Beemster 2% Milk</item>
+    </string-array>
+
+</resources>
diff --git a/tests/tests/uiautomation/src/android/app/uiautomation/cts/UiAutomationTest.java b/tests/tests/uiautomation/src/android/app/uiautomation/cts/UiAutomationTest.java
new file mode 100644
index 0000000..6d80819
--- /dev/null
+++ b/tests/tests/uiautomation/src/android/app/uiautomation/cts/UiAutomationTest.java
@@ -0,0 +1,291 @@
+/*
+ * 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.app.uiautomation.cts;
+
+import android.accessibilityservice.AccessibilityServiceInfo;
+import android.app.Activity;
+import android.app.UiAutomation;
+import android.content.Intent;
+import android.view.FrameStats;
+import android.view.WindowAnimationFrameStats;
+import android.view.WindowContentFrameStats;
+import android.view.accessibility.AccessibilityWindowInfo;
+import android.support.test.uiautomator.UiScrollable;
+import android.support.test.uiautomator.UiSelector;
+import android.support.test.uiautomator.UiAutomatorTestCase;
+
+import java.util.List;
+
+/**
+ * Tests for the UiAutomation APIs.
+ */
+public class UiAutomationTest extends UiAutomatorTestCase {
+
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+        AccessibilityServiceInfo info = getInstrumentation().getUiAutomation().getServiceInfo();
+        info.flags |= AccessibilityServiceInfo.FLAG_RETRIEVE_INTERACTIVE_WINDOWS;
+        getInstrumentation().getUiAutomation().setServiceInfo(info);
+    }
+
+    public void testWindowContentFrameStats() throws Exception {
+        Activity activity = null;
+        try {
+            UiAutomation uiAutomation = getInstrumentation().getUiAutomation();
+
+            // Start an activity.
+            Intent intent = new Intent(getInstrumentation().getContext(),
+                    UiAutomationTestFirstActivity.class);
+            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+            activity = getInstrumentation().startActivitySync(intent);
+
+            // Wait for things to settle.
+            getUiDevice().waitForIdle();
+
+            // Find the application window.
+            final int windowId = findAppWindowId(uiAutomation.getWindows());
+            assertTrue(windowId >= 0);
+
+            // Clear stats to be with a clean slate.
+            assertTrue(uiAutomation.clearWindowContentFrameStats(windowId));
+
+            // Find the list to scroll around.
+            UiScrollable listView = new UiScrollable(new UiSelector().resourceId(
+                    "android.app.cts.uiautomation:id/list_view"));
+
+            // Scoll a bit.
+            listView.scrollToEnd(Integer.MAX_VALUE);
+            listView.scrollToBeginning(Integer.MAX_VALUE);
+
+            // Get the frame stats.
+            WindowContentFrameStats stats = uiAutomation.getWindowContentFrameStats(windowId);
+
+            // Check the frame stats...
+
+            // We should have somethong.
+            assertNotNull(stats);
+
+            // The refresh presiod is always positive.
+            assertTrue(stats.getRefreshPeriodNano() > 0);
+
+            // There is some frame data.
+            final int frameCount = stats.getFrameCount();
+            assertTrue(frameCount > 0);
+
+            // The frames are ordered in ascending order.
+            assertWindowContentTimestampsInAscendingOrder(stats);
+
+            // The start and end times are based on first and last frame.
+            assertEquals(stats.getStartTimeNano(), stats.getFramePresentedTimeNano(0));
+            assertEquals(stats.getEndTimeNano(), stats.getFramePresentedTimeNano(frameCount - 1));
+        } finally {
+            // Clean up.
+            if (activity != null) {
+                activity.finish();
+            }
+        }
+    }
+
+    public void testWindowContentFrameStatsNoAnimation() throws Exception {
+        Activity activity = null;
+        try {
+            UiAutomation uiAutomation = getInstrumentation().getUiAutomation();
+
+            // Start an activity.
+            Intent intent = new Intent(getInstrumentation().getContext(),
+                    UiAutomationTestFirstActivity.class);
+            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+            activity = getInstrumentation().startActivitySync(intent);
+
+            // Wait for things to settle.
+            getUiDevice().waitForIdle();
+
+            // Find the application window.
+            final int windowId = findAppWindowId(uiAutomation.getWindows());
+            assertTrue(windowId >= 0);
+
+            // Clear stats to be with a clean slate.
+            assertTrue(uiAutomation.clearWindowContentFrameStats(windowId));
+
+            // Get the frame stats.
+            WindowContentFrameStats stats = uiAutomation.getWindowContentFrameStats(windowId);
+
+            // Check the frame stats...
+
+            // We should have somethong.
+            assertNotNull(stats);
+
+            // The refresh presiod is always positive.
+            assertTrue(stats.getRefreshPeriodNano() > 0);
+
+            // There is no data.
+            assertTrue(stats.getFrameCount() == 0);
+
+            // The start and end times are undefibed as we have no data.
+            assertEquals(stats.getStartTimeNano(), FrameStats.UNDEFINED_TIME_NANO);
+            assertEquals(stats.getEndTimeNano(), FrameStats.UNDEFINED_TIME_NANO);
+        } finally {
+            // Clean up.
+            if (activity != null) {
+                activity.finish();
+            }
+        }
+    }
+
+    public void testWindowAnimationFrameStats() throws Exception {
+        Activity firstActivity = null;
+        Activity secondActivity = null;
+        try {
+            UiAutomation uiAutomation = getInstrumentation().getUiAutomation();
+
+            // Start the frist activity.
+            Intent firstIntent = new Intent(getInstrumentation().getContext(),
+                    UiAutomationTestFirstActivity.class);
+            firstIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+            firstActivity = getInstrumentation().startActivitySync(firstIntent);
+
+            // Wait for things to settle.
+            getUiDevice().waitForIdle();
+
+            // Clear the window animation stats to be with a clean slate.
+            uiAutomation.clearWindowAnimationFrameStats();
+
+            // Start the second activity
+            Intent secondIntent = new Intent(getInstrumentation().getContext(),
+                    UiAutomationTestSecondActivity.class);
+            secondIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+            secondActivity = getInstrumentation().startActivitySync(secondIntent);
+
+            // Wait for things to settle.
+            getUiDevice().waitForIdle();
+
+            // Get the frame stats.
+            WindowAnimationFrameStats stats = uiAutomation.getWindowAnimationFrameStats();
+
+            // Check the frame stats...
+
+            // We should have somethong.
+            assertNotNull(stats);
+
+            // The refresh presiod is always positive.
+            assertTrue(stats.getRefreshPeriodNano() > 0);
+
+            // There is some frame data.
+            final int frameCount = stats.getFrameCount();
+            assertTrue(frameCount > 0);
+
+            // The frames are ordered in ascending order.
+            assertWindowAnimationTimestampsInAscendingOrder(stats);
+
+            // The start and end times are based on first and last frame.
+            assertEquals(stats.getStartTimeNano(), stats.getFramePresentedTimeNano(0));
+            assertEquals(stats.getEndTimeNano(), stats.getFramePresentedTimeNano(frameCount - 1));
+        } finally {
+            // Clean up.
+            if (firstActivity != null) {
+                firstActivity.finish();
+            }
+            if (secondActivity != null) {
+                secondActivity.finish();
+            }
+        }
+    }
+
+    public void testWindowAnimationFrameStatsNoAnimation() throws Exception {
+        UiAutomation uiAutomation = getInstrumentation().getUiAutomation();
+
+        // Wait for things to settle.
+        getUiDevice().waitForIdle();
+
+        // Clear the window animation stats to be with a clean slate.
+        uiAutomation.clearWindowAnimationFrameStats();
+
+        // Get the frame stats.
+        WindowAnimationFrameStats stats = uiAutomation.getWindowAnimationFrameStats();
+
+        // Check the frame stats...
+
+        // We should have somethong.
+        assertNotNull(stats);
+
+        // The refresh presiod is always positive.
+        assertTrue(stats.getRefreshPeriodNano() > 0);
+
+        // There is no data.
+        assertTrue(stats.getFrameCount() == 0);
+
+        // The start and end times are undefibed as we have no data.
+        assertEquals(stats.getStartTimeNano(), FrameStats.UNDEFINED_TIME_NANO);
+        assertEquals(stats.getEndTimeNano(), FrameStats.UNDEFINED_TIME_NANO);
+    }
+
+    private void assertWindowContentTimestampsInAscendingOrder(WindowContentFrameStats stats) {
+        long lastExpectedTimeNano = 0;
+        long lastPresentedTimeNano = 0;
+        long lastPreparedTimeNano = 0;
+
+        final int frameCount = stats.getFrameCount();
+        for (int i = 0; i < frameCount; i++) {
+            final long expectedTimeNano = stats.getFramePostedTimeNano(i);
+            assertTrue(expectedTimeNano > lastExpectedTimeNano);
+            lastExpectedTimeNano = expectedTimeNano;
+
+            final long presentedTimeNano = stats.getFramePresentedTimeNano(i);
+            if (lastPresentedTimeNano == FrameStats.UNDEFINED_TIME_NANO) {
+                assertTrue(presentedTimeNano == FrameStats.UNDEFINED_TIME_NANO);
+            } else if (presentedTimeNano != FrameStats.UNDEFINED_TIME_NANO) {
+                assertTrue(presentedTimeNano > lastPresentedTimeNano);
+            }
+            lastPresentedTimeNano = presentedTimeNano;
+
+            final long preparedTimeNano = stats.getFrameReadyTimeNano(i);
+            if (lastPreparedTimeNano == FrameStats.UNDEFINED_TIME_NANO) {
+                assertTrue(preparedTimeNano == FrameStats.UNDEFINED_TIME_NANO);
+            } else if (preparedTimeNano != FrameStats.UNDEFINED_TIME_NANO) {
+                assertTrue(preparedTimeNano > lastPreparedTimeNano);
+            }
+            lastPreparedTimeNano = preparedTimeNano;
+        }
+    }
+
+    private void assertWindowAnimationTimestampsInAscendingOrder(WindowAnimationFrameStats stats) {
+        long lastPresentedTimeNano = 0;
+
+        final int frameCount = stats.getFrameCount();
+        for (int i = 0; i < frameCount; i++) {
+            final long presentedTimeNano = stats.getFramePresentedTimeNano(i);
+            if (lastPresentedTimeNano == FrameStats.UNDEFINED_TIME_NANO) {
+                assertTrue(presentedTimeNano == FrameStats.UNDEFINED_TIME_NANO);
+            } else if (presentedTimeNano != FrameStats.UNDEFINED_TIME_NANO) {
+                assertTrue(presentedTimeNano > lastPresentedTimeNano);
+            }
+            lastPresentedTimeNano = presentedTimeNano;
+        }
+    }
+
+    private int findAppWindowId(List<AccessibilityWindowInfo> windows) {
+        final int windowCount = windows.size();
+        for (int i = 0; i < windowCount; i++) {
+            AccessibilityWindowInfo window = windows.get(i);
+            if (window.getType() == AccessibilityWindowInfo.TYPE_APPLICATION) {
+                return window.getId();
+            }
+        }
+        return -1;
+    }
+}
diff --git a/tests/tests/uiautomation/src/android/app/uiautomation/cts/UiAutomationTestFirstActivity.java b/tests/tests/uiautomation/src/android/app/uiautomation/cts/UiAutomationTestFirstActivity.java
new file mode 100644
index 0000000..49791ab
--- /dev/null
+++ b/tests/tests/uiautomation/src/android/app/uiautomation/cts/UiAutomationTestFirstActivity.java
@@ -0,0 +1,42 @@
+/*
+* 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.app.uiautomation.cts;
+
+import android.app.cts.uiautomation.R;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.widget.ArrayAdapter;
+import android.widget.ListView;
+
+/**
+* Activity for testing the UiAutomatoin APIs.
+*/
+public class UiAutomationTestFirstActivity extends Activity {
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.ui_automation_test);
+
+        String[] cheeses = getResources().getStringArray(R.array.some_cheeses);
+        ArrayAdapter<String> cheeseAdapter = new ArrayAdapter<String>(this,
+                android.R.layout.simple_list_item_1, cheeses);
+
+        ListView listView = (ListView) findViewById(R.id.list_view);
+        listView.setAdapter(cheeseAdapter);
+    }
+}
diff --git a/tests/tests/uiautomation/src/android/app/uiautomation/cts/UiAutomationTestSecondActivity.java b/tests/tests/uiautomation/src/android/app/uiautomation/cts/UiAutomationTestSecondActivity.java
new file mode 100644
index 0000000..7def379
--- /dev/null
+++ b/tests/tests/uiautomation/src/android/app/uiautomation/cts/UiAutomationTestSecondActivity.java
@@ -0,0 +1,41 @@
+/*
+* 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.app.uiautomation.cts;
+
+import android.app.Activity;
+import android.app.cts.uiautomation.R;
+import android.os.Bundle;
+import android.widget.ArrayAdapter;
+import android.widget.ListView;
+
+/**
+* Activity for testing the UiAutomatoin APIs.
+*/
+public class UiAutomationTestSecondActivity extends Activity {
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.ui_automation_test);
+
+        String[] cheeses = getResources().getStringArray(R.array.some_cheeses);
+        ArrayAdapter<String> cheeseAdapter = new ArrayAdapter<String>(this,
+                android.R.layout.simple_list_item_1, cheeses);
+
+        ListView listView = (ListView) findViewById(R.id.list_view);
+        listView.setAdapter(cheeseAdapter);
+    }
+}
diff --git a/tests/tests/uidisolation/Android.mk b/tests/tests/uidisolation/Android.mk
index ba82eb5..8529407 100644
--- a/tests/tests/uidisolation/Android.mk
+++ b/tests/tests/uidisolation/Android.mk
@@ -21,12 +21,12 @@
 # and when built explicitly put it in the data partition
 LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
 
-LOCAL_JAVA_LIBRARIES := android.test.runner
-
 LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner ctstestserver
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
 LOCAL_PACKAGE_NAME := CtsUidIsolationTestCases
 
+LOCAL_SDK_VERSION := current
+
 include $(BUILD_CTS_PACKAGE)
diff --git a/tests/tests/uidisolation/AndroidManifest.xml b/tests/tests/uidisolation/AndroidManifest.xml
index e456a50..a8c6848 100644
--- a/tests/tests/uidisolation/AndroidManifest.xml
+++ b/tests/tests/uidisolation/AndroidManifest.xml
@@ -31,8 +31,11 @@
 
    <uses-permission android:name="android.permission.INTERNET"/>
 
-    <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
                      android:targetPackage="com.android.cts.uidisolation"
-                     android:label="CTS tests of android.uidisolation"/>
+                     android:label="CTS tests of android.uidisolation">
+        <meta-data android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
 </manifest>
 
diff --git a/tests/tests/uirendering/Android.mk b/tests/tests/uirendering/Android.mk
new file mode 100644
index 0000000..76707df
--- /dev/null
+++ b/tests/tests/uirendering/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)
+
+# don't include this package in any target
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_JAVA_LIBRARIES := android.test.runner
+
+LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src) $(call all-renderscript-files-under, src)
+
+LOCAL_PACKAGE_NAME := CtsUiRenderingTestCases
+
+# uncomment when dalvik.annotation.Test* are removed or part of SDK
+#LOCAL_SDK_VERSION := current
+
+include $(BUILD_CTS_PACKAGE)
diff --git a/tests/tests/uirendering/AndroidManifest.xml b/tests/tests/uirendering/AndroidManifest.xml
new file mode 100644
index 0000000..413dfba
--- /dev/null
+++ b/tests/tests/uirendering/AndroidManifest.xml
@@ -0,0 +1,34 @@
+<?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.uirendering">
+    <uses-permission android:name="android.permission.INJECT_EVENTS" />
+    <uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
+    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+    <application>
+        <activity android:name="android.uirendering.cts.testinfrastructure.DrawActivity"
+                android:theme="@style/WhiteBackgroundTheme"></activity>
+        <uses-library android:name="android.test.runner" />
+    </application>
+
+    <instrumentation android:name="android.test.InstrumentationTestRunner"
+                     android:targetPackage="com.android.cts.uirendering">
+    </instrumentation>
+
+</manifest>
+
diff --git a/tests/tests/uirendering/assets/blue_padded_square.html b/tests/tests/uirendering/assets/blue_padded_square.html
new file mode 100644
index 0000000..8609af7
--- /dev/null
+++ b/tests/tests/uirendering/assets/blue_padded_square.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<html>
+<head lang="en">
+  <meta charset="UTF-8">
+  <title></title>
+</head>
+<body style="padding:10px 10px 10px 10px">
+    <div style="height: 80px; width: 80px; background-color:blue"/>
+</body>
+</html>
diff --git a/tests/tests/uirendering/res/drawable-nodpi/blue_padded_square.9.png b/tests/tests/uirendering/res/drawable-nodpi/blue_padded_square.9.png
new file mode 100644
index 0000000..d69869d
--- /dev/null
+++ b/tests/tests/uirendering/res/drawable-nodpi/blue_padded_square.9.png
Binary files differ
diff --git a/tests/tests/uirendering/res/drawable/sunset1.jpg b/tests/tests/uirendering/res/drawable/sunset1.jpg
new file mode 100644
index 0000000..92851f3
--- /dev/null
+++ b/tests/tests/uirendering/res/drawable/sunset1.jpg
Binary files differ
diff --git a/tests/tests/uirendering/res/layout/blue_padded_layout.xml b/tests/tests/uirendering/res/layout/blue_padded_layout.xml
new file mode 100644
index 0000000..1cd1b21
--- /dev/null
+++ b/tests/tests/uirendering/res/layout/blue_padded_layout.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.
+  -->
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+        android:layout_width="200px"
+        android:layout_height="200px"
+        android:clipChildren="false">
+    <android.uirendering.cts.testclasses.view.UnclippedBlueView
+            android:id="@+id/child"
+            android:layout_width="100px"
+            android:layout_height="100px"
+            android:paddingLeft="15px"
+            android:paddingTop="16px"
+            android:paddingRight="17px"
+            android:paddingBottom="18px"/>
+</FrameLayout>
diff --git a/tests/tests/uirendering/res/layout/blue_padded_square.xml b/tests/tests/uirendering/res/layout/blue_padded_square.xml
new file mode 100644
index 0000000..0f254d4
--- /dev/null
+++ b/tests/tests/uirendering/res/layout/blue_padded_square.xml
@@ -0,0 +1,19 @@
+<?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.
+  -->
+<View xmlns:android="http://schemas.android.com/apk/res/android"
+        android:layout_width="100px"
+        android:layout_height="100px"
+        android:background="@drawable/blue_padded_square"/>
diff --git a/tests/tests/uirendering/res/layout/draw_activity_view.xml b/tests/tests/uirendering/res/layout/draw_activity_view.xml
new file mode 100644
index 0000000..54a72e3
--- /dev/null
+++ b/tests/tests/uirendering/res/layout/draw_activity_view.xml
@@ -0,0 +1,17 @@
+<?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.
+-->
+
+<android.uirendering.cts.CanvasClientView xmlns:android="http://schemas.android.com/apk/res/android"/>
diff --git a/tests/tests/uirendering/res/layout/simple_rect_layout.xml b/tests/tests/uirendering/res/layout/simple_rect_layout.xml
new file mode 100644
index 0000000..24c9b6b
--- /dev/null
+++ b/tests/tests/uirendering/res/layout/simple_rect_layout.xml
@@ -0,0 +1,27 @@
+<?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="vertical"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:background="#f00">
+
+    <View android:layout_width="180px"
+        android:layout_height="120px"
+        android:background="#0f0" />
+
+</LinearLayout>
diff --git a/tests/tests/uirendering/res/layout/simple_red_layout.xml b/tests/tests/uirendering/res/layout/simple_red_layout.xml
new file mode 100644
index 0000000..a785552
--- /dev/null
+++ b/tests/tests/uirendering/res/layout/simple_red_layout.xml
@@ -0,0 +1,19 @@
+<?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.
+  -->
+<View xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:background="#f00"         />
diff --git a/tests/tests/uirendering/res/values/themes.xml b/tests/tests/uirendering/res/values/themes.xml
new file mode 100644
index 0000000..751b7cb
--- /dev/null
+++ b/tests/tests/uirendering/res/values/themes.xml
@@ -0,0 +1,26 @@
+<?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>
+    <style name="WhiteBackgroundTheme" parent="@android:style/Theme.Holo.NoActionBar.Fullscreen">
+        <item name="android:windowNoTitle">true</item>
+        <item name="android:windowFullscreen">true</item>
+        <item name="android:fadingEdge">none</item>
+        <item name="android:windowBackground">@android:color/white</item>
+        <!--This shouldn't be necessary currently a hack for an existing bug with transitions-->
+        <item name="android:windowContentTransitions">false</item>
+        <item name="android:windowAnimationStyle">@null</item>
+    </style>
+</resources>
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/bitmapcomparers/BaseRenderScriptComparer.java b/tests/tests/uirendering/src/android/uirendering/cts/bitmapcomparers/BaseRenderScriptComparer.java
new file mode 100644
index 0000000..3f9a9f6
--- /dev/null
+++ b/tests/tests/uirendering/src/android/uirendering/cts/bitmapcomparers/BaseRenderScriptComparer.java
@@ -0,0 +1,78 @@
+package android.uirendering.cts.bitmapcomparers;
+
+import android.content.res.Resources;
+import android.renderscript.Allocation;
+import android.renderscript.Element;
+import android.renderscript.RenderScript;
+
+/**
+ * Base class for calculators that want to implement renderscript
+ */
+public abstract class BaseRenderScriptComparer extends BitmapComparer {
+    private Allocation mRowInputs;
+    private Allocation mRowOutputs;
+    private int mHeight;
+
+    public abstract boolean verifySame(int[] ideal, int[] given, int offset, int stride, int width,
+            int height);
+
+    /**
+     * The subclasses must implement this method, which will say that the rows follow their specific
+     * algorithm
+     */
+    public abstract boolean verifySameRowsRS(Resources resources, Allocation ideal,
+            Allocation given, int offset, int stride, int width, int height,
+            RenderScript renderScript, Allocation inputAllocation, Allocation outputAllocation);
+
+    public boolean verifySameRS(Resources resources, Allocation ideal,
+            Allocation given, int offset, int stride, int width, int height,
+            RenderScript renderScript) {
+        if (mRowInputs == null) {
+            mHeight = height;
+            mRowInputs = createInputRowIndexAllocation(renderScript);
+            mRowOutputs = createOutputRowAllocation(renderScript);
+        }
+        return verifySameRowsRS(resources, ideal, given, offset, stride, width, height,
+                renderScript, mRowInputs, mRowOutputs);
+    }
+
+    public boolean supportsRenderScript() {
+        return true;
+    }
+
+    /**
+     * Sums the values in the output Allocation
+     */
+    public float sum1DFloatAllocation(Allocation rowOutputs) {
+        //Get the values returned from the function
+        float[] returnValue = new float[mHeight];
+        rowOutputs.copyTo(returnValue);
+        float sum = 0;
+        //If any row had any different pixels, then it fails
+        for (int i = 0; i < mHeight; i++) {
+            sum += returnValue[i];
+        }
+        return sum;
+    }
+
+    /**
+     * Creates an allocation where the values in it are the indices of each row
+     */
+    private Allocation createInputRowIndexAllocation(RenderScript renderScript) {
+        //Create an array with the index of each row
+        int[] inputIndices = new int[mHeight];
+        for (int i = 0; i < mHeight; i++) {
+            inputIndices[i] = i;
+        }
+        //Create the allocation from that given array
+        Allocation inputAllocation = Allocation.createSized(renderScript, Element.I32(renderScript),
+                inputIndices.length, Allocation.USAGE_SCRIPT);
+        inputAllocation.copyFrom(inputIndices);
+        return inputAllocation;
+    }
+
+    private Allocation createOutputRowAllocation(RenderScript renderScript) {
+        return Allocation.createSized(renderScript, Element.F32(renderScript), mHeight,
+                Allocation.USAGE_SCRIPT);
+    }
+}
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/bitmapcomparers/BitmapComparer.java b/tests/tests/uirendering/src/android/uirendering/cts/bitmapcomparers/BitmapComparer.java
new file mode 100644
index 0000000..8d74aa5
--- /dev/null
+++ b/tests/tests/uirendering/src/android/uirendering/cts/bitmapcomparers/BitmapComparer.java
@@ -0,0 +1,62 @@
+/*
+ * 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.uirendering.cts.bitmapcomparers;
+
+import android.content.res.Resources;
+import android.renderscript.Allocation;
+import android.renderscript.RenderScript;
+
+/**
+ * This abstract class can be used by the tester to implement their own comparison methods
+ */
+public abstract class BitmapComparer {
+    /**
+     * Compares the two bitmaps given using Java.
+     * @param offset where in the bitmaps to start
+     * @param stride how much to skip between two different rows
+     * @param width the width of the subsection being tested
+     * @param height the height of the subsection being tested
+     * @return
+     */
+    public abstract boolean verifySame(int[] ideal, int[] given, int offset, int stride, int width,
+            int height);
+
+    /**
+     * Compare the two bitmaps using RenderScript, if the comparer
+     * {@link supportsRenderScript() supports it}. If it does not, this method will throw an
+     * UnsupportedOperationException
+     */
+    public boolean verifySameRS(Resources resources, Allocation ideal,
+            Allocation given, int offset, int stride, int width, int height,
+            RenderScript renderScript) {
+        throw new UnsupportedOperationException("Renderscript not supported for this calculator");
+    }
+
+    /**
+     * This calculates the position in an array that would represent a bitmap given the parameters.
+     */
+    protected static int indexFromXAndY(int x, int y, int stride, int offset) {
+        return x + (y * stride) + offset;
+    }
+
+    /**
+     * Returns whether the verifySameRS() is implemented, and may be used on a RenderScript enabled
+     * system
+     */
+    public boolean supportsRenderScript() {
+        return false;
+    }
+}
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/bitmapcomparers/ExactComparer.java b/tests/tests/uirendering/src/android/uirendering/cts/bitmapcomparers/ExactComparer.java
new file mode 100644
index 0000000..c4ea6d2
--- /dev/null
+++ b/tests/tests/uirendering/src/android/uirendering/cts/bitmapcomparers/ExactComparer.java
@@ -0,0 +1,78 @@
+/*
+ * 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.uirendering.cts.bitmapcomparers;
+
+import com.android.cts.uirendering.R;
+import com.android.cts.uirendering.ScriptC_ExactComparer;
+
+import android.content.res.Resources;
+import android.renderscript.Allocation;
+import android.renderscript.RenderScript;
+import android.util.Log;
+
+/**
+ * This class does an exact comparison of the pixels in a bitmap.
+ */
+public class ExactComparer extends BaseRenderScriptComparer {
+    private static final String TAG = "ExactComparer";
+    private ScriptC_ExactComparer mScript;
+
+    /**
+     * This method does an exact 1 to 1 comparison of the two bitmaps
+     */
+    public boolean verifySame(int[] ideal, int[] given, int offset, int stride, int width,
+            int height) {
+        int count = 0;
+
+        for (int y = 0 ; y < height ; y++) {
+            for (int x = 0 ; x < width ; x++) {
+                int index = indexFromXAndY(x, y, stride, offset);
+                if (ideal[index] != given[index]) {
+                    Log.d(TAG, "Failure on position x = " + x + " y = " + y);
+                    Log.d(TAG, "Expected color : " + Integer.toHexString(ideal[index]) +
+                            " given color : " + Integer.toHexString(given[index]));
+                    count++;
+                }
+            }
+        }
+        Log.d(TAG, "Number of different pixels : " + count);
+
+        return (count == 0);
+    }
+
+    @Override
+    public boolean verifySameRowsRS(Resources resources, Allocation ideal,
+            Allocation given, int offset, int stride, int width, int height,
+            RenderScript renderScript, Allocation inputAllocation, Allocation outputAllocation) {
+        if (mScript == null) {
+            mScript = new ScriptC_ExactComparer(renderScript, resources, R.raw.exactcomparer);
+        }
+        mScript.set_WIDTH(width);
+        mScript.set_OFFSET(offset);
+
+        //Set the bitmap allocations
+        mScript.set_ideal(ideal);
+        mScript.set_given(given);
+
+        //Call the renderscript function on each row
+        mScript.forEach_exactCompare(inputAllocation, outputAllocation);
+
+        float val = sum1DFloatAllocation(outputAllocation);
+        Log.d(TAG, "Number of different pixels RS : " + val);
+
+        return val == 0;
+    }
+}
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/bitmapcomparers/ExactComparer.rs b/tests/tests/uirendering/src/android/uirendering/cts/bitmapcomparers/ExactComparer.rs
new file mode 100644
index 0000000..899a5cc
--- /dev/null
+++ b/tests/tests/uirendering/src/android/uirendering/cts/bitmapcomparers/ExactComparer.rs
@@ -0,0 +1,25 @@
+#pragma version(1)
+#pragma rs java_package_name(com.android.cts.uirendering)
+
+int WIDTH;
+int OFFSET;
+
+rs_allocation ideal;
+rs_allocation given;
+
+// This method does a simple comparison of all the values in the given and ideal allocations.
+// If any of the pixels are off, then the test will fail.
+void exactCompare(const int32_t *v_in, float *v_out){
+    int y = v_in[0];
+    v_out[0] = 0;
+
+    for(int i = 0 ; i < WIDTH ; i ++){
+        uchar4 idealPixel = rsGetElementAt_uchar4(ideal, i + OFFSET, y);
+        uchar4 givenPixel = rsGetElementAt_uchar4(given, i + OFFSET, y);
+        uchar4 diff = idealPixel - givenPixel;
+        int totalDiff = diff.x + diff.y + diff.z;
+        if(totalDiff != 0){
+            v_out[0] ++;
+        }
+    }
+}
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/bitmapcomparers/MSSIMComparer.java b/tests/tests/uirendering/src/android/uirendering/cts/bitmapcomparers/MSSIMComparer.java
new file mode 100644
index 0000000..28e62ee
--- /dev/null
+++ b/tests/tests/uirendering/src/android/uirendering/cts/bitmapcomparers/MSSIMComparer.java
@@ -0,0 +1,193 @@
+/*
+ * 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.uirendering.cts.bitmapcomparers;
+
+import com.android.cts.uirendering.R;
+import com.android.cts.uirendering.ScriptC_MSSIMComparer;
+
+import android.content.res.Resources;
+import android.graphics.Color;
+import android.renderscript.Allocation;
+import android.renderscript.RenderScript;
+import android.util.Log;
+
+/**
+ * Image comparison using Structural Similarity Index, developed by Wang, Bovik, Sheikh, and
+ * Simoncelli. Details can be read in their paper :
+ *
+ * https://ece.uwaterloo.ca/~z70wang/publications/ssim.pdf
+ */
+public class MSSIMComparer extends BaseRenderScriptComparer {
+    // These values were taken from the publication
+    public static final String TAG_NAME = "MSSIM";
+    public static final double CONSTANT_L = 254;
+    public static final double CONSTANT_K1 = 0.00001;
+    public static final double CONSTANT_K2 = 0.00003;
+    public static final double CONSTANT_C1 = Math.pow(CONSTANT_L * CONSTANT_K1, 2);
+    public static final double CONSTANT_C2 = Math.pow(CONSTANT_L * CONSTANT_K2, 2);
+    public static final int WINDOW_SIZE = 10;
+
+    private double mThreshold;
+    private ScriptC_MSSIMComparer mScript;
+
+    public MSSIMComparer(double threshold) {
+        mThreshold = threshold;
+    }
+
+    @Override
+    public boolean verifySame(int[] ideal, int[] given, int offset, int stride, int width,
+            int height) {
+        double SSIMTotal = 0;
+        int windows = 0;
+
+        for (int currentWindowY = 0 ; currentWindowY < height ; currentWindowY += WINDOW_SIZE) {
+            for (int currentWindowX = 0 ; currentWindowX < width ; currentWindowX += WINDOW_SIZE) {
+                int start = indexFromXAndY(currentWindowX, currentWindowY, stride, offset);
+                if (isWindowWhite(ideal, start, stride) && isWindowWhite(given, start, stride)) {
+                    continue;
+                }
+                windows++;
+                double[] means = getMeans(ideal, given, start, stride);
+                double meanX = means[0];
+                double meanY = means[1];
+                double[] variances = getVariances(ideal, given, meanX, meanY, start, stride);
+                double varX = variances[0];
+                double varY = variances[1];
+                double stdBoth = variances[2];
+                double SSIM = SSIM(meanX, meanY, varX, varY, stdBoth);
+                SSIMTotal += SSIM;
+            }
+        }
+
+        if (windows == 0) {
+            return true;
+        }
+
+        SSIMTotal /= windows;
+
+        Log.d(TAG_NAME, "MSSIM = " + SSIMTotal);
+
+        return (SSIMTotal >= mThreshold);
+    }
+
+    @Override
+    public boolean verifySameRowsRS(Resources resources, Allocation ideal,
+            Allocation given, int offset, int stride, int width, int height,
+            RenderScript renderScript, Allocation inputAllocation, Allocation outputAllocation) {
+        if (mScript == null) {
+            mScript = new ScriptC_MSSIMComparer(renderScript, resources,
+                    R.raw.mssimcomparer);
+        }
+        mScript.set_WIDTH(width);
+        mScript.set_HEIGHT(height);
+
+        //Set the bitmap allocations
+        mScript.set_ideal(ideal);
+        mScript.set_given(given);
+
+        //Call the renderscript function on each row
+        mScript.forEach_calcSSIM(inputAllocation, outputAllocation);
+
+        float MSSIM = sum1DFloatAllocation(outputAllocation);
+        MSSIM /= height;
+
+        Log.d(TAG_NAME, "MSSIM RS : " + MSSIM);
+
+        return (MSSIM >= mThreshold);
+    }
+
+    private boolean isWindowWhite(int[] colors, int start, int stride) {
+        for (int y = 0 ; y < WINDOW_SIZE ; y++) {
+            for (int x = 0 ; x < WINDOW_SIZE ; x++) {
+                if (colors[indexFromXAndY(x, y, stride, start)] != Color.WHITE) {
+                    return false;
+                }
+            }
+        }
+        return true;
+    }
+
+    private double SSIM(double muX, double muY, double sigX, double sigY, double sigXY) {
+        double SSIM = (((2 * muX * muY) + CONSTANT_C1) * ((2 * sigXY) + CONSTANT_C2));
+        double denom = ((muX * muX) + (muY * muY) + CONSTANT_C1)
+                * (sigX + sigY + CONSTANT_C2);
+        SSIM /= denom;
+        return SSIM;
+    }
+
+
+    /**
+     * This method will find the mean of a window in both sets of pixels. The return is an array
+     * where the first double is the mean of the first set and the second double is the mean of the
+     * second set.
+     */
+    private double[] getMeans(int[] pixels0, int[] pixels1, int start, int stride) {
+        double avg0 = 0;
+        double avg1 = 0;
+        for (int y = 0 ; y < WINDOW_SIZE ; y++) {
+            for (int x = 0 ; x < WINDOW_SIZE ; x++) {
+                int index = indexFromXAndY(x, y, stride, start);
+                avg0 += getIntensity(pixels0[index]);
+                avg1 += getIntensity(pixels1[index]);
+            }
+        }
+        avg0 /= WINDOW_SIZE * WINDOW_SIZE;
+        avg1 /= WINDOW_SIZE * WINDOW_SIZE;
+        return new double[] {avg0, avg1};
+    }
+
+    /**
+     * Finds the variance of the two sets of pixels, as well as the covariance of the windows. The
+     * return value is an array of doubles, the first is the variance of the first set of pixels,
+     * the second is the variance of the second set of pixels, and the third is the covariance.
+     */
+    private double[] getVariances(int[] pixels0, int[] pixels1, double mean0, double mean1,
+            int start, int stride) {
+        double var0 = 0;
+        double var1 = 0;
+        double varBoth = 0;
+        for (int y = 0 ; y < WINDOW_SIZE ; y++) {
+            for (int x = 0 ; x < WINDOW_SIZE ; x++) {
+                int index = indexFromXAndY(x, y, stride, start);
+                double v0 = getIntensity(pixels0[index]) - mean0;
+                double v1 = getIntensity(pixels1[index]) - mean1;
+                var0 += v0 * v0;
+                var1 += v1 * v1;
+                varBoth += v0 * v1;
+            }
+        }
+        var0 /= (WINDOW_SIZE * WINDOW_SIZE) - 1;
+        var1 /= (WINDOW_SIZE * WINDOW_SIZE) - 1;
+        varBoth /= (WINDOW_SIZE * WINDOW_SIZE) - 1;
+        return new double[] {var0, var1, varBoth};
+    }
+
+    /**
+     * Gets the intensity of a given pixel in RGB using luminosity formula
+     *
+     * l = 0.21R' + 0.72G' + 0.07B'
+     *
+     * The prime symbols dictate a gamma correction of 1.
+     */
+    private double getIntensity(int pixel) {
+        final double gamma = 1;
+        double l = 0;
+        l += (0.21f * Math.pow(Color.red(pixel) / 255f, gamma));
+        l += (0.72f * Math.pow(Color.green(pixel) / 255f, gamma));
+        l += (0.07f * Math.pow(Color.blue(pixel) / 255f, gamma));
+        return l;
+    }
+}
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/bitmapcomparers/MSSIMComparer.rs b/tests/tests/uirendering/src/android/uirendering/cts/bitmapcomparers/MSSIMComparer.rs
new file mode 100644
index 0000000..4d8c41b
--- /dev/null
+++ b/tests/tests/uirendering/src/android/uirendering/cts/bitmapcomparers/MSSIMComparer.rs
@@ -0,0 +1,66 @@
+#pragma version(1)
+#pragma rs java_package_name(com.android.cts.uirendering)
+
+int WIDTH;
+int HEIGHT;
+
+rs_allocation ideal;
+rs_allocation given;
+
+static float getPixelWeight(uchar4 pixel) {
+    const float MAX_VALUE_COLOR = 255;
+    const float RED_WEIGHT = 0.21f / MAX_VALUE_COLOR;
+    const float GREEN_WEIGHT = 0.72f / MAX_VALUE_COLOR;
+    const float BLUE_WEIGHT = 0.07f / MAX_VALUE_COLOR;
+    return (pixel.r * RED_WEIGHT) + (pixel.g * GREEN_WEIGHT) + (pixel.b * BLUE_WEIGHT);
+}
+
+// Calculates SSIM of a row of pixels
+void calcSSIM(const int32_t *v_in, float *v_out) {
+    // TODO Test values for these constants
+    const float C1 = 0.0000064516;
+    const float C2 = 0.0000580644;
+
+    int y = v_in[0];
+    v_out[0] = 0;
+
+    float meanIdeal = 0;
+    float meanGiven = 0;
+
+    for (int i = 0 ; i < WIDTH ; i++) {
+        uchar4 idealPixel = rsGetElementAt_uchar4(ideal, i, y);
+        uchar4 givenPixel = rsGetElementAt_uchar4(given, i, y);
+        meanIdeal += getPixelWeight(idealPixel);
+        meanGiven += getPixelWeight(givenPixel);
+    }
+
+    meanIdeal /= WIDTH;
+    meanGiven /= WIDTH;
+
+    float varIdeal = 0;
+    float varGiven = 0;
+    float varBoth = 0;
+
+    for (int i = 0 ; i < WIDTH ; i++) {
+        uchar4 idealPixel = rsGetElementAt_uchar4(ideal, i, y);
+        uchar4 givenPixel = rsGetElementAt_uchar4(given, i, y);
+        float idealWeight = getPixelWeight(idealPixel);
+        float givenWeight = getPixelWeight(givenPixel);
+        idealWeight -= meanIdeal;
+        givenWeight -= meanGiven;
+        varIdeal +=  idealWeight * idealWeight;
+        varGiven += givenWeight * givenWeight;
+        varBoth += idealWeight * givenWeight;
+    }
+
+    varIdeal /= WIDTH - 1;
+    varGiven /= WIDTH - 1;
+    varBoth /= WIDTH - 1;
+
+    float SSIM = ((2 * meanIdeal * meanGiven) + C1) * ((2 * varBoth) + C2);
+    float denom = ((meanIdeal * meanIdeal) + (meanGiven * meanGiven) + C1)
+                    * (varIdeal + varGiven + C2);
+    SSIM /= denom;
+
+    v_out[0] = SSIM;
+}
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/bitmapcomparers/MeanSquaredComparer.java b/tests/tests/uirendering/src/android/uirendering/cts/bitmapcomparers/MeanSquaredComparer.java
new file mode 100644
index 0000000..ae826d3
--- /dev/null
+++ b/tests/tests/uirendering/src/android/uirendering/cts/bitmapcomparers/MeanSquaredComparer.java
@@ -0,0 +1,103 @@
+/*
+ * 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.uirendering.cts.bitmapcomparers;
+
+import com.android.cts.uirendering.R;
+import com.android.cts.uirendering.ScriptC_MeanSquaredComparer;
+
+import android.content.res.Resources;
+import android.graphics.Color;
+import android.renderscript.Allocation;
+import android.renderscript.RenderScript;
+import android.util.Log;
+
+/**
+ * Finds the MSE using two images.
+ */
+public class MeanSquaredComparer extends BaseRenderScriptComparer {
+    private static final String TAG = "MeanSquared";
+    private ScriptC_MeanSquaredComparer mScript;
+    private float mErrorPerPixel;
+
+    /**
+     * @param errorPerPixel threshold for which the test will pass/fail. This is the mean-squared
+     *                      error averaged across all of those before comparing.
+     */
+    public MeanSquaredComparer(float errorPerPixel) {
+        mErrorPerPixel = errorPerPixel;
+    }
+
+    @Override
+    public boolean verifySame(int[] ideal, int[] given, int offset, int stride, int width,
+            int height) {
+        float totalError = getMSE(ideal, given, offset, stride, width, height);
+        Log.d(TAG, "Error : " + totalError);
+        return (totalError < (mErrorPerPixel));
+    }
+
+    @Override
+    public boolean verifySameRowsRS(Resources resources, Allocation ideal,
+            Allocation given, int offset, int stride, int width, int height,
+            RenderScript renderScript, Allocation inputAllocation, Allocation outputAllocation) {
+        if (mScript == null) {
+            mScript = new ScriptC_MeanSquaredComparer(renderScript, resources,
+                    R.raw.meansquaredcomparer);
+        }
+        mScript.set_WIDTH(width);
+
+        //Set the bitmap allocations
+        mScript.set_ideal(ideal);
+        mScript.set_given(given);
+
+        //Call the renderscript function on each row
+        mScript.forEach_calcMSE(inputAllocation, outputAllocation);
+
+        float error = sum1DFloatAllocation(outputAllocation);
+        error /= (height * width);
+
+        Log.d(TAG, "Error RS : " + error);
+
+        return (error < mErrorPerPixel);
+    }
+
+    /**
+     * Gets the Mean Squared Error between two data sets.
+     */
+    public static float getMSE(int[] ideal, int[] given, int offset, int stride, int width,
+            int height) {
+        float totalError = 0;
+
+        for (int y = 0 ; y < height ; y++) {
+            for (int x = 0 ; x < width ; x++) {
+                int index = indexFromXAndY(x, y, stride, offset);
+                float idealSum = getColorSum(ideal[index]);
+                float givenSum = getColorSum(given[index]);
+                float difference = idealSum - givenSum;
+                totalError += (difference * difference);
+            }
+        }
+
+        totalError /= (width * height);
+        return totalError;
+    }
+
+    private static float getColorSum(int color) {
+        float red = Color.red(color) / 255.0f;
+        float green = Color.green(color) / 255.0f;
+        float blue = Color.blue(color) / 255.0f;
+        return (red + green + blue);
+    }
+}
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/bitmapcomparers/MeanSquaredComparer.rs b/tests/tests/uirendering/src/android/uirendering/cts/bitmapcomparers/MeanSquaredComparer.rs
new file mode 100644
index 0000000..b37ad13
--- /dev/null
+++ b/tests/tests/uirendering/src/android/uirendering/cts/bitmapcomparers/MeanSquaredComparer.rs
@@ -0,0 +1,21 @@
+#pragma version(1)
+#pragma rs java_package_name(com.android.cts.uirendering)
+
+int REGION_SIZE;
+int WIDTH;
+
+rs_allocation ideal;
+rs_allocation given;
+
+// This method does a threshold comparison of the values
+void calcMSE(const int32_t *v_in, float *v_out){
+    int y = v_in[0];
+    v_out[0] = 0.0f;
+    for (int x = 0 ; x < WIDTH ; x++) {
+        float4 idealFloats = rsUnpackColor8888(rsGetElementAt_uchar4(ideal, x, y));
+        float4 givenFloats = rsUnpackColor8888(rsGetElementAt_uchar4(given, x, y));
+        float difference = (idealFloats.r - givenFloats.r) + (idealFloats.g - givenFloats.g) +
+              (idealFloats.b - givenFloats.b);
+        v_out[0] += (difference * difference);
+    }
+}
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/bitmapcomparers/NearPixelComparer.java b/tests/tests/uirendering/src/android/uirendering/cts/bitmapcomparers/NearPixelComparer.java
new file mode 100644
index 0000000..bfb2c77
--- /dev/null
+++ b/tests/tests/uirendering/src/android/uirendering/cts/bitmapcomparers/NearPixelComparer.java
@@ -0,0 +1,66 @@
+/*
+ * 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.uirendering.cts.bitmapcomparers;
+
+import android.graphics.Color;
+import android.util.Log;
+
+/**
+ * Checks to see that a pixel at a given location is the same as the corresponding pixel. If the
+ * pixel is different, it checks the pixels around it to see if it is the same.
+ */
+public class NearPixelComparer extends BitmapComparer {
+    private static final String TAG = "NearPixelComparer";
+    private static final int THRESHOLD = 10;
+    private static final int NEAR_PIXEL_RADIUS = 1;
+
+    @Override
+    public boolean verifySame(int[] ideal, int[] given, int offset, int stride, int width,
+            int height) {
+        for (int y = 0 ; y < height ; y++) {
+            for (int x = 0 ; x < width ; x++) {
+                boolean success = false;
+
+                // we need to check the surrounding pixels
+                for (int dx = -NEAR_PIXEL_RADIUS ; dx <= NEAR_PIXEL_RADIUS ; dx++) {
+                    for (int dy = -NEAR_PIXEL_RADIUS ; dy <= NEAR_PIXEL_RADIUS ; dy++) {
+                        // need to be sure we don't hit pixels that aren't there
+                        if (x + dx >= width || x + dx < 0 || y + dy >= height || y + dy < 0) {
+                            continue;
+                        }
+                        int index = indexFromXAndY(x + dx, y + dy, stride, offset);
+                        if (!pixelsAreSame(ideal[index], given[index])) {
+                            success = true;
+                            break;
+                        }
+                    }
+                }
+                if (!success) {
+                    Log.d(TAG, "Failure at pixel (" + x + "," + y + ")");
+                    return false;
+                }
+            }
+        }
+        return true;
+    }
+
+    private boolean pixelsAreSame(int ideal, int given) {
+        int error = Math.abs(Color.red(ideal) - Color.red(given));
+        error += Math.abs(Color.green(ideal) - Color.green(given));
+        error += Math.abs(Color.blue(ideal) - Color.blue(given));
+        return (error < THRESHOLD);
+    }
+}
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/bitmapcomparers/PSNRComparer.java b/tests/tests/uirendering/src/android/uirendering/cts/bitmapcomparers/PSNRComparer.java
new file mode 100644
index 0000000..a38a381
--- /dev/null
+++ b/tests/tests/uirendering/src/android/uirendering/cts/bitmapcomparers/PSNRComparer.java
@@ -0,0 +1,91 @@
+/*
+ * 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.uirendering.cts.bitmapcomparers;
+
+import android.graphics.Color;
+import android.util.Log;
+
+/**
+ * Uses the Peak Signal-to-Noise Ratio approach to determine if two images are considered the same.
+ */
+public class PSNRComparer extends BitmapComparer {
+    private static final String TAG = "PSNR";
+    private final float MAX = 255;
+    private final int REGION_SIZE = 10;
+
+    private float mThreshold;
+
+    /**
+     * @param threshold the PSNR necessary to pass the test, if the calculated PSNR is below this
+     *                  value, then the test will fail.
+     */
+    public PSNRComparer(float threshold) {
+        mThreshold = threshold;
+    }
+
+    @Override
+    public boolean verifySame(int[] ideal, int[] given, int offset, int stride, int width,
+            int height) {
+        float MSE = 0f;
+        int interestingRegions = 0;
+        for (int y = 0 ; y < height ; y += REGION_SIZE) {
+            for (int x = 0 ; x < width ; x += REGION_SIZE) {
+                int index = indexFromXAndY(x, y, stride, offset);
+                if (inspectRegion(ideal, index)) {
+                    interestingRegions++;
+                }
+            }
+        }
+
+        if (interestingRegions == 0) {
+            return true;
+        }
+
+        for (int y = 0 ; y < height ; y += REGION_SIZE) {
+            for (int x = 0 ; x < width ; x += REGION_SIZE) {
+                int index = indexFromXAndY(x, y, stride, offset);
+                if (ideal[index] == given[index]) {
+                    continue;
+                }
+                MSE += (Color.red(ideal[index]) - Color.red(given[index])) *
+                        (Color.red(ideal[index]) - Color.red(given[index]));
+                MSE += (Color.blue(ideal[index]) - Color.blue(given[index])) *
+                        (Color.blue(ideal[index]) - Color.blue(given[index]));
+                MSE += (Color.green(ideal[index]) - Color.green(given[index])) *
+                        (Color.green(ideal[index]) - Color.green(given[index]));
+            }
+        }
+        MSE /= (interestingRegions * REGION_SIZE * 3);
+
+        float fraction = (MAX * MAX) / MSE;
+        fraction = (float) Math.log(fraction);
+        fraction *= 10;
+
+        Log.d(TAG, "PSNR : " + fraction);
+
+        return (fraction > mThreshold);
+    }
+
+    private boolean inspectRegion(int[] ideal, int index) {
+        int regionColor = ideal[index];
+        for (int i = 0 ; i < REGION_SIZE ; i++) {
+            if (regionColor != ideal[index + i]) {
+                return true;
+            }
+        }
+        return false;
+    }
+}
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/bitmapcomparers/PassComparer.java b/tests/tests/uirendering/src/android/uirendering/cts/bitmapcomparers/PassComparer.java
new file mode 100644
index 0000000..79923cd
--- /dev/null
+++ b/tests/tests/uirendering/src/android/uirendering/cts/bitmapcomparers/PassComparer.java
@@ -0,0 +1,27 @@
+/*
+ * 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.uirendering.cts.bitmapcomparers;
+
+/**
+ * This class is purely for debug purposes. It will automatically pass any tests.
+ */
+public class PassComparer extends BitmapComparer {
+    @Override
+    public boolean verifySame(int[] ideal, int[] given, int offset, int stride, int width,
+            int height) {
+        return true;
+    }
+}
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/bitmapcomparers/ThresholdDifferenceComparer.java b/tests/tests/uirendering/src/android/uirendering/cts/bitmapcomparers/ThresholdDifferenceComparer.java
new file mode 100644
index 0000000..835798c
--- /dev/null
+++ b/tests/tests/uirendering/src/android/uirendering/cts/bitmapcomparers/ThresholdDifferenceComparer.java
@@ -0,0 +1,90 @@
+/*
+ * 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.uirendering.cts.bitmapcomparers;
+
+import com.android.cts.uirendering.R;
+import com.android.cts.uirendering.ScriptC_ThresholdDifferenceComparer;
+
+import android.content.res.Resources;
+import android.graphics.Color;
+import android.renderscript.Allocation;
+import android.renderscript.RenderScript;
+import android.uirendering.cts.bitmapcomparers.BaseRenderScriptComparer;
+import android.util.Log;
+
+/**
+ * Compares two images to see if each pixel is the same, within a certain threshold value
+ */
+public class ThresholdDifferenceComparer extends BaseRenderScriptComparer {
+    private static final String TAG = "ThresholdDifference";
+    private ScriptC_ThresholdDifferenceComparer mScript;
+    private int mThreshold;
+
+    /**
+     * @param threshold Each pixel is compared against each other, in each of the individual
+     *                  channels. If the sum of the errors amongst the channels is greater than some
+     *                  threshold, then this test will fail.
+     */
+    public ThresholdDifferenceComparer(int threshold) {
+        mThreshold = threshold;
+    }
+
+    @Override
+    public boolean verifySame(int[] ideal, int[] given, int offset, int stride, int width,
+            int height) {
+        int differentPixels = 0;
+        for (int y = 0 ; y < height ; y++) {
+            for (int x = 0 ; x < width ; x++) {
+                int index = indexFromXAndY(x, y, stride, offset);
+                int error = Math.abs(Color.red(ideal[index]) - Color.red(given[index]));
+                error += Math.abs(Color.blue(ideal[index]) - Color.blue(given[index]));
+                error += Math.abs(Color.green(ideal[index]) - Color.green(given[index]));
+                if (error > mThreshold) {
+                    Log.d(TAG, "Failure at position x = " + x + " y = " + y);
+                    Log.d(TAG, "Expected color " + Integer.toHexString(ideal[index]) +
+                            " given color " + Integer.toHexString(given[index]));
+                    differentPixels++;
+                }
+            }
+        }
+        Log.d(TAG, "Number of different pixels : " + differentPixels);
+        return (differentPixels == 0);
+    }
+
+    @Override
+    public boolean verifySameRowsRS(Resources resources, Allocation ideal,
+            Allocation given, int offset, int stride, int width, int height,
+            RenderScript renderScript, Allocation inputAllocation, Allocation outputAllocation) {
+        if (mScript == null) {
+            mScript = new ScriptC_ThresholdDifferenceComparer(renderScript, resources,
+                    R.raw.thresholddifferencecomparer);
+        }
+
+        mScript.set_THRESHOLD(mThreshold);
+        mScript.set_WIDTH(width);
+
+        //Set the bitmap allocations
+        mScript.set_ideal(ideal);
+        mScript.set_given(given);
+
+        //Call the renderscript function on each row
+        mScript.forEach_thresholdCompare(inputAllocation, outputAllocation);
+
+        float differentPixels = sum1DFloatAllocation(outputAllocation);
+        Log.d(TAG, "Number of different pixels RS : " + differentPixels);
+        return (differentPixels == 0);
+    }
+}
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/bitmapcomparers/ThresholdDifferenceComparer.rs b/tests/tests/uirendering/src/android/uirendering/cts/bitmapcomparers/ThresholdDifferenceComparer.rs
new file mode 100644
index 0000000..de1a129
--- /dev/null
+++ b/tests/tests/uirendering/src/android/uirendering/cts/bitmapcomparers/ThresholdDifferenceComparer.rs
@@ -0,0 +1,25 @@
+#pragma version(1)
+#pragma rs java_package_name(com.android.cts.uirendering)
+
+int WIDTH;
+int THRESHOLD;
+
+rs_allocation ideal;
+rs_allocation given;
+
+// This method does a threshold comparison of the values
+void thresholdCompare(const int32_t *v_in, float *v_out){
+    int y = v_in[0];
+    v_out[0] = 0;
+
+    for(int i = 0 ; i < WIDTH ; i ++){
+        uchar4 idealPixel = rsGetElementAt_uchar4(ideal, i, y);
+        uchar4 givenPixel = rsGetElementAt_uchar4(given, i, y);
+        float l1 = (idealPixel.x * 0.21f) + (idealPixel.y * 0.72f) + (idealPixel.z * 0.07f);
+        float l2 = (givenPixel.x * 0.21f) + (givenPixel.y * 0.72f) + (givenPixel.z * 0.07f);
+        float diff = l1 - l2;
+        if (fabs(diff) >= THRESHOLD) {
+            v_out[0]++;
+        }
+    }
+}
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/bitmapcomparers/WeightedPixelDifference.java b/tests/tests/uirendering/src/android/uirendering/cts/bitmapcomparers/WeightedPixelDifference.java
new file mode 100644
index 0000000..f243c29
--- /dev/null
+++ b/tests/tests/uirendering/src/android/uirendering/cts/bitmapcomparers/WeightedPixelDifference.java
@@ -0,0 +1,108 @@
+/*
+ * 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.uirendering.cts.bitmapcomparers;
+
+import android.graphics.Color;
+import android.util.Log;
+
+/**
+ * This class contains methods to add the error amongst all pixels in two images while taking into
+ * account the number of pixels that are non-white. Note only use this if the content background is
+ * white.
+ */
+public class WeightedPixelDifference extends BitmapComparer {
+    private static final String TAG = "WeightedPixel";
+    private static final int NUM_OF_COLUMNS = 10;
+    private static final float TOTAL_ERROR_DIVISOR = 1024.0f;
+
+    private float mThreshold;
+
+    public WeightedPixelDifference(float threshold) {
+        mThreshold = threshold;
+    }
+
+    /**
+     * Calculates if pixels in a specific line are the same color
+     * @return true if the pixels are the same color
+     */
+    private static boolean inspectRegions(int[] ideal, int start, int stride, int regionSize) {
+        int regionColor = ideal[start];
+        for (int y = 0 ; y < regionSize ; y++) {
+            for (int x = 0 ; x < regionSize ; x++) {
+                int index = indexFromXAndY(x, y, stride, start);
+                if (ideal[index] != regionColor) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Finds the error between each individual channel in the color.
+     */
+    private static float errorBetweenPixels(int color1, int color2) {
+        float error = 0f;
+        error += Math.abs(Color.red(color1) - Color.red(color2));
+        error += Math.abs(Color.green(color1) - Color.green(color2));
+        error += Math.abs(Color.blue(color1) - Color.blue(color2));
+        error += Math.abs(Color.alpha(color1) - Color.alpha(color2));
+        return error;
+    }
+
+    /**
+     * Calculates the error between the pixels in the ideal and given
+     * @return true if the accumulated error is smaller than the threshold
+     */
+    @Override
+    public boolean verifySame(int[] ideal, int[] given, int offset, int stride, int width,
+            int height) {
+        int interestingRegions = 0;
+        int regionSize = width / NUM_OF_COLUMNS;
+
+        for (int y = 0 ; y < height ; y += regionSize) {
+            for (int x = 0 ; x < width ; x += regionSize) {
+                int index = indexFromXAndY(x, y,stride, offset);
+                if (inspectRegions(ideal, index, stride, regionSize)) {
+                    interestingRegions++;
+                }
+            }
+        }
+
+        int interestingPixels = Math.max(1, interestingRegions) * regionSize * regionSize;
+
+        float totalError = 0;
+
+        for (int y = 0 ; y < height ; y++) {
+            for (int x = 0 ; x < width ; x++) {
+                int index = indexFromXAndY(x, y, stride, offset);
+                int idealColor = ideal[index];
+                int givenColor = given[index];
+                if (idealColor == givenColor) {
+                    continue;
+                }
+                totalError += errorBetweenPixels(idealColor, givenColor);
+            }
+        }
+
+        totalError /= TOTAL_ERROR_DIVISOR;
+        totalError /= interestingPixels;
+
+        Log.d(TAG, "Total error : " + totalError);
+
+        return totalError < mThreshold;
+    }
+}
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/bitmapverifiers/BitmapVerifier.java b/tests/tests/uirendering/src/android/uirendering/cts/bitmapverifiers/BitmapVerifier.java
new file mode 100644
index 0000000..2f7acd5
--- /dev/null
+++ b/tests/tests/uirendering/src/android/uirendering/cts/bitmapverifiers/BitmapVerifier.java
@@ -0,0 +1,45 @@
+/*
+ * 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.uirendering.cts.bitmapverifiers;
+
+import android.graphics.Bitmap;
+import android.graphics.Color;
+
+/**
+ * Checks to see if a Bitmap follows the algorithm provided by the verifier
+ */
+public abstract class BitmapVerifier {
+    protected static final int PASS_COLOR = Color.WHITE;
+    protected static final int FAIL_COLOR = Color.RED;
+
+    protected Bitmap mDifferenceBitmap;
+
+    /**
+     * This will test if the bitmap is good or not.
+     */
+    public abstract boolean verify(int[] bitmap, int offset, int stride, int width, int height);
+
+    /**
+     * This calculates the position in an array that would represent a bitmap given the parameters.
+     */
+    protected static int indexFromXAndY(int x, int y, int stride, int offset) {
+        return x + (y * stride) + offset;
+    }
+
+    public Bitmap getDifferenceBitmap() {
+        return mDifferenceBitmap;
+    }
+}
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/bitmapverifiers/ColorVerifier.java b/tests/tests/uirendering/src/android/uirendering/cts/bitmapverifiers/ColorVerifier.java
new file mode 100644
index 0000000..f8d624f
--- /dev/null
+++ b/tests/tests/uirendering/src/android/uirendering/cts/bitmapverifiers/ColorVerifier.java
@@ -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.
+ */
+package android.uirendering.cts.bitmapverifiers;
+
+/**
+ * Checks to see if a bitmap is entirely a single color
+ */
+public class ColorVerifier extends PerPixelBitmapVerifier {
+    private int mColor;
+
+    public ColorVerifier(int color) {
+        this(color, 0);
+    }
+
+    public ColorVerifier(int color, int threshold) {
+        super(threshold);
+        mColor = color;
+    }
+
+    @Override
+    protected int getExpectedColor(int x, int y) {
+        return mColor;
+    }
+}
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/bitmapverifiers/GoldenImageVerifier.java b/tests/tests/uirendering/src/android/uirendering/cts/bitmapverifiers/GoldenImageVerifier.java
new file mode 100644
index 0000000..672b3f6
--- /dev/null
+++ b/tests/tests/uirendering/src/android/uirendering/cts/bitmapverifiers/GoldenImageVerifier.java
@@ -0,0 +1,44 @@
+/*
+ * 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.uirendering.cts.bitmapverifiers;
+
+import android.graphics.Bitmap;
+import android.uirendering.cts.bitmapcomparers.BitmapComparer;
+import android.uirendering.cts.differencevisualizers.PassFailVisualizer;
+
+public class GoldenImageVerifier extends BitmapVerifier {
+    private BitmapComparer mBitmapComparer;
+    private int[] mGoldenBitmapArray;
+
+    public GoldenImageVerifier(Bitmap goldenBitmap, BitmapComparer bitmapComparer) {
+        mGoldenBitmapArray = new int[goldenBitmap.getWidth() * goldenBitmap.getHeight()];
+        goldenBitmap.getPixels(mGoldenBitmapArray, 0, goldenBitmap.getWidth(), 0, 0,
+                goldenBitmap.getWidth(), goldenBitmap.getHeight());
+        mBitmapComparer = bitmapComparer;
+    }
+
+    @Override
+    public boolean verify(int[] bitmap, int offset, int stride, int width, int height) {
+        boolean success = mBitmapComparer.verifySame(mGoldenBitmapArray, bitmap, offset, stride,
+                width, height);
+        if (!success) {
+            mDifferenceBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
+            int[] differences = new PassFailVisualizer().getDifferences(mGoldenBitmapArray, bitmap);
+            mDifferenceBitmap.setPixels(differences, 0, width, 0, 0, width, height);
+        }
+        return success;
+    }
+}
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/bitmapverifiers/InvertVerifier.java b/tests/tests/uirendering/src/android/uirendering/cts/bitmapverifiers/InvertVerifier.java
new file mode 100644
index 0000000..fe0db96
--- /dev/null
+++ b/tests/tests/uirendering/src/android/uirendering/cts/bitmapverifiers/InvertVerifier.java
@@ -0,0 +1,34 @@
+/*
+ * 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.uirendering.cts.bitmapverifiers;
+
+/**
+ * Used when the tester wants to find the opposite result from a Verifier
+ */
+public class InvertVerifier extends BitmapVerifier {
+    private BitmapVerifier mBitmapVerifier;
+
+    public InvertVerifier(BitmapVerifier bitmapVerifier) {
+        mBitmapVerifier = bitmapVerifier;
+    }
+
+    @Override
+    public boolean verify(int[] bitmap, int offset, int stride, int width, int height) {
+        boolean success = mBitmapVerifier.verify(bitmap, offset, stride, width, height);
+        mDifferenceBitmap = mBitmapVerifier.getDifferenceBitmap();
+        return !success;
+    }
+}
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/bitmapverifiers/PerPixelBitmapVerifier.java b/tests/tests/uirendering/src/android/uirendering/cts/bitmapverifiers/PerPixelBitmapVerifier.java
new file mode 100644
index 0000000..ab809f4
--- /dev/null
+++ b/tests/tests/uirendering/src/android/uirendering/cts/bitmapverifiers/PerPixelBitmapVerifier.java
@@ -0,0 +1,70 @@
+/*
+ * 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.uirendering.cts.bitmapverifiers;
+
+import android.graphics.Bitmap;
+import android.graphics.Color;
+import android.uirendering.cts.testinfrastructure.ActivityTestBase;
+import android.uirendering.cts.util.CompareUtils;
+import android.util.Log;
+
+/**
+ * This class looks at every pixel in a given bitmap and verifies that it is correct.
+ */
+public abstract class PerPixelBitmapVerifier extends BitmapVerifier {
+    private static final String TAG = "PerPixelBitmapVerifer";
+    private int mTolerance;
+
+    public PerPixelBitmapVerifier(int tolerance) {
+        mTolerance = tolerance;
+    }
+
+    protected int getExpectedColor(int x, int y) {
+        return Color.WHITE;
+    }
+
+
+    public boolean verify(int[] bitmap, int offset, int stride, int width, int height) {
+        boolean res = true;
+        int[] differenceMap = new int[bitmap.length];
+        for (int y = 0 ; y < height ; y++) {
+            for (int x = 0 ; x < width ; x++) {
+                int index = indexFromXAndY(x, y, stride, offset);
+                int expectedColor = getExpectedColor(x, y);
+                if (!verifyPixel(bitmap[index], expectedColor)) {
+                    Log.d(TAG, "Expected : " + Integer.toHexString(expectedColor)
+                            + " received : " + Integer.toHexString(bitmap[index])
+                            + " at position (" + x + "," + y + ")");
+                    res = false;
+                    differenceMap[index] = FAIL_COLOR;
+                } else {
+                    differenceMap[index] = PASS_COLOR;
+                }
+            }
+        }
+        if (!res) {
+            mDifferenceBitmap = Bitmap.createBitmap(ActivityTestBase.TEST_WIDTH,
+                    ActivityTestBase.TEST_HEIGHT, Bitmap.Config.ARGB_8888);
+            mDifferenceBitmap.setPixels(differenceMap, offset, stride, 0, 0,
+                    ActivityTestBase.TEST_WIDTH, ActivityTestBase.TEST_HEIGHT);
+        }
+        return res;
+    }
+
+    protected boolean verifyPixel(int color, int expectedColor) {
+        return CompareUtils.verifyPixelWithThreshold(color, expectedColor, mTolerance);
+    }
+}
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/bitmapverifiers/RectVerifier.java b/tests/tests/uirendering/src/android/uirendering/cts/bitmapverifiers/RectVerifier.java
new file mode 100644
index 0000000..bb4b07c
--- /dev/null
+++ b/tests/tests/uirendering/src/android/uirendering/cts/bitmapverifiers/RectVerifier.java
@@ -0,0 +1,43 @@
+/*
+ * 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.uirendering.cts.bitmapverifiers;
+
+import android.graphics.Rect;
+
+/**
+ * Tests to see if there is rectangle of a certain color, with a background given
+ */
+public class RectVerifier extends PerPixelBitmapVerifier {
+    private int mOuterColor;
+    private int mInnerColor;
+    private Rect mInnerRect;
+
+    public RectVerifier(int outerColor, int innerColor, Rect innerRect) {
+        this(outerColor, innerColor, innerRect, 0);
+    }
+
+    public RectVerifier(int outerColor, int innerColor, Rect innerRect, int tolerance) {
+        super(tolerance);
+        mOuterColor = outerColor;
+        mInnerColor = innerColor;
+        mInnerRect = innerRect;
+    }
+
+    @Override
+    protected int getExpectedColor(int x, int y) {
+        return mInnerRect.contains(x, y) ? mInnerColor : mOuterColor;
+    }
+}
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/bitmapverifiers/SamplePointVerifier.java b/tests/tests/uirendering/src/android/uirendering/cts/bitmapverifiers/SamplePointVerifier.java
new file mode 100644
index 0000000..7be2987
--- /dev/null
+++ b/tests/tests/uirendering/src/android/uirendering/cts/bitmapverifiers/SamplePointVerifier.java
@@ -0,0 +1,65 @@
+/*
+ * 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.uirendering.cts.bitmapverifiers;
+
+import android.graphics.Bitmap;
+import android.graphics.Point;
+import android.uirendering.cts.testinfrastructure.ActivityTestBase;
+import android.util.Log;
+
+import java.util.Arrays;
+
+/**
+ * This class will test specific points, and ensure that they match up perfectly with the input colors
+ */
+public class SamplePointVerifier extends BitmapVerifier {
+    private static final String TAG = "SamplePoint";
+    private Point[] mTestPoints;
+    private int[] mExpectedColors;
+
+    public SamplePointVerifier(Point[] testPoints, int[] expectedColors) {
+        mTestPoints = testPoints;
+        mExpectedColors = expectedColors;
+    }
+
+    @Override
+    public boolean verify(int[] bitmap, int offset, int stride, int width, int height) {
+        boolean success = true;
+        int[] differenceMap = new int[bitmap.length];
+        Arrays.fill(differenceMap, PASS_COLOR);
+        for (int i = 0 ; i < mTestPoints.length ; i++) {
+            int x = mTestPoints[i].x;
+            int y = mTestPoints[i].y;
+            int index = indexFromXAndY(x, y, stride, offset);
+            if (mExpectedColors[i] != bitmap[index]) {
+                Log.d(TAG, "Expected : " + Integer.toHexString(mExpectedColors[i]) +
+                        " at position x = " + x + " y = " + y + " , tested color : " +
+                        Integer.toHexString(bitmap[index]));
+                differenceMap[index] = FAIL_COLOR;
+                success = false;
+            } else {
+                differenceMap[index] = PASS_COLOR;
+            }
+        }
+        if (!success) {
+            mDifferenceBitmap = Bitmap.createBitmap(ActivityTestBase.TEST_WIDTH,
+                    ActivityTestBase.TEST_HEIGHT, Bitmap.Config.ARGB_8888);
+            mDifferenceBitmap.setPixels(differenceMap, offset, stride, 0, 0,
+                    ActivityTestBase.TEST_WIDTH, ActivityTestBase.TEST_HEIGHT);
+        }
+        return success;
+    }
+}
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/differencevisualizers/DifferenceVisualizer.java b/tests/tests/uirendering/src/android/uirendering/cts/differencevisualizers/DifferenceVisualizer.java
new file mode 100644
index 0000000..348a1c0
--- /dev/null
+++ b/tests/tests/uirendering/src/android/uirendering/cts/differencevisualizers/DifferenceVisualizer.java
@@ -0,0 +1,23 @@
+/*
+ * 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.uirendering.cts.differencevisualizers;
+
+/**
+ * This class can be extended by the tester, to allow for various ways to debug.
+ */
+public abstract class DifferenceVisualizer {
+    public abstract int[] getDifferences(int[] ideal, int[] given);
+}
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/differencevisualizers/PassFailVisualizer.java b/tests/tests/uirendering/src/android/uirendering/cts/differencevisualizers/PassFailVisualizer.java
new file mode 100644
index 0000000..7641519
--- /dev/null
+++ b/tests/tests/uirendering/src/android/uirendering/cts/differencevisualizers/PassFailVisualizer.java
@@ -0,0 +1,41 @@
+/*
+ * 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.uirendering.cts.differencevisualizers;
+
+import android.graphics.Color;
+
+/**
+ * This class creates difference maps that show which pixels were correct, and which weren't
+ */
+public class PassFailVisualizer extends DifferenceVisualizer {
+    /**
+     * This method will return a bitmap where white is same red is different
+     * @param ideal the desired result
+     * @param given the produced result
+     */
+    @Override
+    public int[] getDifferences(int[] ideal, int[] given) {
+        int[] output = new int[ideal.length];
+        for (int y = 0; y < output.length; y++) {
+            if (ideal[y] == given[y]) {
+                output[y] = Color.WHITE;
+            } else {
+                output[y] = Color.RED;
+            }
+        }
+        return output;
+    }
+}
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/BitmapFilterTests.java b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/BitmapFilterTests.java
new file mode 100644
index 0000000..833e618
--- /dev/null
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/BitmapFilterTests.java
@@ -0,0 +1,123 @@
+/*
+ * 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.uirendering.cts.testclasses;
+
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.PaintFlagsDrawFilter;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.uirendering.cts.bitmapverifiers.BitmapVerifier;
+import android.uirendering.cts.bitmapverifiers.ColorVerifier;
+import android.uirendering.cts.bitmapverifiers.PerPixelBitmapVerifier;
+import android.uirendering.cts.testinfrastructure.ActivityTestBase;
+import android.uirendering.cts.testinfrastructure.CanvasClient;
+
+public class BitmapFilterTests extends ActivityTestBase {
+    private static final int THRESHOLD = 20;
+    private static final int WHITE_WEIGHT = 255 * 3;
+    private enum FilterEnum {
+        // Creates Paint object that will have bitmap filtering
+        PAINT_FILTER,
+        // First uses a Paint object with bitmap filtering, then uses canvas.setDrawFilter to remove
+        // the bitmap filtering
+        REMOVE_FILTER,
+        // Sets DrawFilter to use Paint.FILTER_BITMAP_FLAG
+        ADD_FILTER
+    }
+
+    /**
+     * Verifies that a Bitmap only contains white and black, within a certain threshold
+     */
+    private static BitmapVerifier mBlackWhiteVerifier = new PerPixelBitmapVerifier(THRESHOLD) {
+        @Override
+        protected boolean verifyPixel(int color, int expectedColor) {
+            int weight = Color.red(color) + Color.blue(color) + Color.green(color);
+            return weight < THRESHOLD // is approx Color.BLACK
+                    || weight > WHITE_WEIGHT - THRESHOLD; // is approx Color.WHITE
+        }
+    };
+
+    private static Bitmap createGridBitmap(int width, int height) {
+        Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
+        for (int i = 0 ; i < width ; i++) {
+            for (int j = 0 ; j < height ; j++) {
+                bitmap.setPixel(i, j, ((i + j * width)) % 2 == 0 ?
+                        Color.WHITE : Color.BLACK);
+            }
+        }
+        return bitmap;
+    }
+
+    private static final int SMALL_GRID_SIZE = 5;
+    private static Bitmap mSmallGridBitmap = createGridBitmap(SMALL_GRID_SIZE, SMALL_GRID_SIZE);
+    private static final int BIG_GRID_SIZE = 360;
+    private static Bitmap mBigGridBitmap = createGridBitmap(BIG_GRID_SIZE, BIG_GRID_SIZE);
+    private static final int HALFWAY_COLOR = Color.argb(255, 127, 127, 127);
+
+    @SmallTest
+    public void testPaintFilterScaleUp() {
+        runScaleTest(FilterEnum.PAINT_FILTER, true, mBlackWhiteVerifier);
+    }
+
+    @SmallTest
+    public void testPaintFilterScaleDown() {
+        runScaleTest(FilterEnum.PAINT_FILTER, false, new ColorVerifier(HALFWAY_COLOR));
+    }
+
+    @SmallTest
+    public void testDrawFilterRemoveFilterScaleUp() {
+        runScaleTest(FilterEnum.REMOVE_FILTER, true, mBlackWhiteVerifier);
+    }
+
+    @SmallTest
+    public void testDrawFilterRemoveFilterScaleDown() {
+        runScaleTest(FilterEnum.REMOVE_FILTER, false, mBlackWhiteVerifier);
+    }
+
+    @SmallTest
+    public void testDrawFilterScaleUp() {
+        runScaleTest(FilterEnum.ADD_FILTER, true, mBlackWhiteVerifier);
+    }
+
+    @SmallTest
+    public void testDrawFilterScaleDown() {
+        runScaleTest(FilterEnum.ADD_FILTER, false, new ColorVerifier(HALFWAY_COLOR));
+    }
+
+    private void runScaleTest(final FilterEnum filterEnum, final boolean scaleUp,
+            BitmapVerifier bitmapVerifier) {
+        final int gridWidth = scaleUp ? BIG_GRID_SIZE : SMALL_GRID_SIZE;
+        final Paint paint = new Paint(filterEnum.equals(FilterEnum.ADD_FILTER) ?
+                0 : Paint.FILTER_BITMAP_FLAG);
+        CanvasClient canvasClient = new CanvasClient() {
+            @Override
+            public void draw(Canvas canvas, int width, int height) {
+                canvas.scale(1.0f * width / gridWidth, 1.0f * height / gridWidth);
+                if (filterEnum.equals(FilterEnum.ADD_FILTER)) {
+                    canvas.setDrawFilter(new PaintFlagsDrawFilter(0, Paint.FILTER_BITMAP_FLAG));
+                } else if (filterEnum.equals(FilterEnum.REMOVE_FILTER)) {
+                    canvas.setDrawFilter(new PaintFlagsDrawFilter(Paint.FILTER_BITMAP_FLAG, 0));
+                }
+                canvas.drawBitmap(scaleUp ? mSmallGridBitmap : mBigGridBitmap, 0, 0, paint);
+            }
+        };
+        createTest()
+                .addCanvasClient(canvasClient)
+                .runWithVerifier(bitmapVerifier);
+    }
+}
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/ExactCanvasTests.java b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/ExactCanvasTests.java
new file mode 100644
index 0000000..f1d6661
--- /dev/null
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/ExactCanvasTests.java
@@ -0,0 +1,226 @@
+/*
+ * 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.uirendering.cts.testclasses;
+
+import com.android.cts.uirendering.R;
+
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.PorterDuff;
+import android.graphics.Rect;
+import android.graphics.drawable.NinePatchDrawable;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.uirendering.cts.bitmapcomparers.BitmapComparer;
+import android.uirendering.cts.bitmapcomparers.ExactComparer;
+import android.uirendering.cts.bitmapverifiers.BitmapVerifier;
+import android.uirendering.cts.bitmapverifiers.RectVerifier;
+import android.uirendering.cts.testinfrastructure.ActivityTestBase;
+import android.uirendering.cts.testinfrastructure.CanvasClient;
+import android.uirendering.cts.testinfrastructure.ViewInitializer;
+import android.view.View;
+
+public class ExactCanvasTests extends ActivityTestBase {
+    private final BitmapComparer mExactComparer = new ExactComparer();
+
+    @SmallTest
+    public void testBlueRect() {
+        final Rect rect = new Rect(10, 10, 100, 100);
+        createTest()
+                .addCanvasClient(new CanvasClient() {
+                    @Override
+                    public void draw(Canvas canvas, int width, int height) {
+                        Paint p = new Paint();
+                        p.setAntiAlias(false);
+                        p.setColor(Color.BLUE);
+                        canvas.drawRect(rect, p);
+                    }
+                })
+                .runWithVerifier(new RectVerifier(Color.WHITE, Color.BLUE, rect));
+    }
+
+    @SmallTest
+    public void testPoints() {
+        createTest()
+                .addCanvasClient(new CanvasClient() {
+                    @Override
+                    public void draw(Canvas canvas, int width, int height) {
+                        Paint p = new Paint();
+                        p.setAntiAlias(false);
+                        p.setColor(Color.WHITE);
+                        canvas.drawRect(0, 0, 100, 100, p);
+                        p.setStrokeWidth(1f);
+                        p.setColor(Color.BLACK);
+                        for (int i = 0; i < 10; i++) {
+                            canvas.drawPoint(i * 10, i * 10, p);
+                        }
+                    }
+                })
+                .runWithComparer(mExactComparer);
+    }
+
+    @SmallTest
+    public void testBlackRectWithStroke() {
+        createTest()
+                .addCanvasClient(new CanvasClient() {
+                    @Override
+                    public void draw(Canvas canvas, int width, int height) {
+                        Paint p = new Paint();
+                        p.setColor(Color.RED);
+                        canvas.drawRect(0, 0, ActivityTestBase.TEST_WIDTH,
+                                ActivityTestBase.TEST_HEIGHT, p);
+                        p.setColor(Color.BLACK);
+                        p.setStrokeWidth(10);
+                        canvas.drawRect(10, 10, 20, 20, p);
+                    }
+                })
+                .runWithComparer(mExactComparer);
+    }
+
+    @SmallTest
+    public void testBlackLineOnGreenBack() {
+        createTest()
+                .addCanvasClient(new CanvasClient() {
+                    @Override
+                    public void draw(Canvas canvas, int width, int height) {
+                        Paint p = new Paint();
+                        p.setColor(Color.GREEN);
+                        canvas.drawRect(0, 0, ActivityTestBase.TEST_WIDTH,
+                                ActivityTestBase.TEST_HEIGHT, p);
+                        p.setColor(Color.BLACK);
+                        p.setStrokeWidth(10);
+                        canvas.drawLine(0, 0, 50, 0, p);
+                    }
+                })
+                .runWithComparer(mExactComparer);
+    }
+
+    @SmallTest
+    public void testDrawRedRectOnBlueBack() {
+        createTest()
+                .addCanvasClient(new CanvasClient() {
+                    @Override
+                    public void draw(Canvas canvas, int width, int height) {
+                        canvas.drawColor(Color.BLUE);
+                        Paint p = new Paint();
+                        p.setColor(Color.RED);
+                        canvas.drawRect(10, 10, 40, 40, p);
+                    }
+                })
+                .runWithComparer(mExactComparer);
+    }
+
+    @SmallTest
+    public void testDrawLine() {
+        createTest()
+                .addCanvasClient(new CanvasClient() {
+                    @Override
+                    public void draw(Canvas canvas, int width, int height) {
+                        Paint p = new Paint();
+                        canvas.drawColor(Color.WHITE);
+                        p.setColor(Color.BLACK);
+                        float[] pts = {
+                                0, 0, 100, 100, 100, 0, 0, 100, 50, 50, 75, 75
+                        };
+                        canvas.drawLines(pts, p);
+                    }
+                })
+                .runWithComparer(mExactComparer);
+    }
+
+    @SmallTest
+    public void testDrawWhiteScreen() {
+        createTest()
+                .addCanvasClient(new CanvasClient() {
+                    @Override
+                    public void draw(Canvas canvas, int width, int height) {
+                        canvas.drawColor(Color.WHITE);
+                    }
+                })
+                .runWithComparer(mExactComparer);
+    }
+
+    @SmallTest
+    public void testBasicText() {
+        final String testString = "THIS IS A TEST";
+        createTest()
+                .addCanvasClient(new CanvasClient() {
+                    @Override
+                    public void draw(Canvas canvas, int width, int height) {
+                        Paint p = new Paint();
+                        canvas.drawColor(Color.BLACK);
+                        p.setColor(Color.WHITE);
+                        p.setStrokeWidth(5);
+                        canvas.drawText(testString, 30, 50, p);
+                    }
+                })
+                .runWithComparer(mExactComparer);
+    }
+
+    @SmallTest
+    public void testBasicColorXfermode() {
+        createTest()
+                .addCanvasClient(new CanvasClient() {
+                    @Override
+                    public void draw(Canvas canvas, int width, int height) {
+                        canvas.drawColor(Color.GRAY);
+                        canvas.drawColor(Color.BLUE, PorterDuff.Mode.MULTIPLY);
+                    }
+                })
+                .runWithComparer(mExactComparer);
+    }
+
+    @SmallTest
+    public void testBluePaddedSquare() {
+        final NinePatchDrawable ninePatchDrawable = (NinePatchDrawable)
+            getActivity().getResources().getDrawable(R.drawable.blue_padded_square);
+        ninePatchDrawable.setBounds(0, 0, 100, 100);
+
+        BitmapVerifier verifier = new RectVerifier(Color.WHITE, Color.BLUE,
+                new Rect(10, 10, 90, 90));
+
+        createTest()
+                .addCanvasClient(new CanvasClient() {
+                    @Override
+                    public void draw(Canvas canvas, int width, int height) {
+                        canvas.drawColor(Color.WHITE);
+                        Paint p = new Paint();
+                        p.setColor(Color.BLUE);
+                        canvas.drawRect(10, 10, 90, 90, p);
+                    }
+                })
+                .addCanvasClient(new CanvasClient() {
+                    @Override
+                    public void draw(Canvas canvas, int width, int height) {
+                        ninePatchDrawable.draw(canvas);
+                    }
+                })
+                .addLayout(R.layout.blue_padded_square, null)
+                .addWebView("file:///android_asset/blue_padded_square.html", null)
+                .runWithVerifier(verifier);
+    }
+
+    @SmallTest
+    public void testClipping() {
+        createTest().addLayout(R.layout.simple_red_layout, new ViewInitializer() {
+            @Override
+            public void intializeView(View view) {
+                view.setClipBounds(new Rect(0, 0, 50, 50));
+            }
+        }).runWithComparer(mExactComparer);
+    }
+}
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/InfrastructureTests.java b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/InfrastructureTests.java
new file mode 100644
index 0000000..a77fa5f
--- /dev/null
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/InfrastructureTests.java
@@ -0,0 +1,87 @@
+/*
+ * 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.uirendering.cts.testclasses;
+
+import com.android.cts.uirendering.R;
+
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Rect;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.uirendering.cts.bitmapcomparers.BitmapComparer;
+import android.uirendering.cts.bitmapcomparers.ExactComparer;
+import android.uirendering.cts.bitmapcomparers.MSSIMComparer;
+import android.uirendering.cts.bitmapverifiers.RectVerifier;
+import android.uirendering.cts.testinfrastructure.ActivityTestBase;
+import android.uirendering.cts.testinfrastructure.CanvasClient;
+import android.uirendering.cts.testinfrastructure.ViewInitializer;
+import android.util.Log;
+import android.view.View;
+
+public class InfrastructureTests extends ActivityTestBase {
+
+    @SmallTest
+    public void testScreenshot() {
+        for (int i = 0 ; i < 1000 ; i ++) {
+            takeScreenshot();
+            System.gc();
+        }
+    }
+
+    /**
+     * Ensure that both render paths are producing independent output. We do this
+     * by verifying that two paths that should render differently *do* render
+     * differently.
+     */
+    @SmallTest
+    public void testRenderSpecIsolation() {
+        CanvasClient canvasClient = new CanvasClient() {
+            @Override
+            public void draw(Canvas canvas, int width, int height) {
+                canvas.drawColor(canvas.isHardwareAccelerated() ? Color.WHITE : Color.BLACK);
+            }
+        };
+        // This is considered a very high threshold and as such, the test should still fail because
+        // they are completely different images.
+        final float threshold = 0.1f;
+        BitmapComparer inverseComparer = new BitmapComparer() {
+            @Override
+            public boolean verifySame(int[] ideal, int[] given, int offset, int stride, int width,
+                    int height) {
+                return !(new MSSIMComparer(threshold)).verifySame(ideal, given, offset, stride,
+                        width, height);
+            }
+        };
+        createTest()
+                .addCanvasClient(canvasClient)
+                .runWithComparer(inverseComparer);
+    }
+
+    @SmallTest
+    public void testViewInitializer() {
+        final Rect clipRect = new Rect(0, 0, 50, 50);
+        ViewInitializer viewInitializer = new ViewInitializer() {
+            @Override
+            public void intializeView(View view) {
+                view.setClipBounds(clipRect);
+            }
+        };
+        createTest()
+                .addLayout(R.layout.simple_red_layout, viewInitializer)
+                .runWithVerifier(new RectVerifier(Color.WHITE, Color.RED, clipRect));
+    }
+}
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/LayoutTests.java b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/LayoutTests.java
new file mode 100644
index 0000000..0ba0f69
--- /dev/null
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/LayoutTests.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.uirendering.cts.testclasses;
+
+import com.android.cts.uirendering.R;
+
+import android.test.suitebuilder.annotation.SmallTest;
+import android.uirendering.cts.bitmapcomparers.BitmapComparer;
+import android.uirendering.cts.bitmapcomparers.ExactComparer;
+import android.uirendering.cts.testinfrastructure.ActivityTestBase;
+
+
+/**
+ * Created to see how custom views made with XML and programatic code will work.
+ */
+public class LayoutTests extends ActivityTestBase {
+    private BitmapComparer mBitmapComparer;
+
+    public LayoutTests() {
+        mBitmapComparer = new ExactComparer();
+    }
+
+    @SmallTest
+    public void testSimpleRedLayout() {
+        createTest().addLayout(R.layout.simple_red_layout, null).runWithComparer(mBitmapComparer);
+    }
+
+    @SmallTest
+    public void testSimpleRectLayout() {
+        createTest().addLayout(R.layout.simple_rect_layout, null).runWithComparer(mBitmapComparer);
+    }
+}
+
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/SweepTests.java b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/SweepTests.java
new file mode 100644
index 0000000..8b7618e
--- /dev/null
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/SweepTests.java
@@ -0,0 +1,390 @@
+/*
+* 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/LICENSE2.0
+*
+* Unless required by applicable law or agreed to in riting, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT 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.uirendering.cts.testclasses;
+
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.LinearGradient;
+import android.graphics.Paint;
+import android.graphics.Point;
+import android.graphics.PorterDuff;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.graphics.Shader;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.uirendering.cts.bitmapcomparers.BitmapComparer;
+import android.uirendering.cts.bitmapcomparers.MSSIMComparer;
+import android.uirendering.cts.bitmapverifiers.BitmapVerifier;
+import android.uirendering.cts.bitmapverifiers.SamplePointVerifier;
+import android.uirendering.cts.testinfrastructure.ActivityTestBase;
+import android.uirendering.cts.testinfrastructure.CanvasClient;
+import android.uirendering.cts.testinfrastructure.DisplayModifier;
+import android.uirendering.cts.testinfrastructure.ResourceModifier;
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+/**
+ * Test cases of all combination of resource modifications.
+ */
+public class SweepTests extends ActivityTestBase {
+    private static final String TAG = "SweepTests";
+
+    public static final int BG_COLOR = 0xFFFFFFFF;
+    public static final int DST_COLOR = 0xFFFFCC44;
+    public static final int SRC_COLOR = 0xFF66AAFF;
+    public static final int MULTIPLY_COLOR = 0xFF668844;
+    public static final int SCREEN_COLOR = 0xFFFFEEFF;
+
+    public static final int FILTER_COLOR = 0xFFBB0000;
+    public static final int RECT0_COLOR = 0x33808080;
+    public static final int RECT1_COLOR = 0x66808080;
+    public static final int RECT2_COLOR = 0x99808080;
+    public static final int RECT3_COLOR = 0xCC808080;
+
+    // These points are in pairs, the first being the lower left corner, the second is only in the
+    // Destination bitmap, the third is the intersection of the two bitmaps, and the fourth is in
+    // the Source bitmap.
+    private final static Point[] XFERMODE_TEST_POINTS = new Point[] {
+            new Point(1, 160), new Point(50, 50), new Point(70, 70), new Point(140, 140)
+    };
+
+    /**
+     * There are 4 locations we care about in any filter testing.
+     *
+     * 1) Both empty
+     * 2) Only src, dst empty
+     * 3) Both src + dst
+     * 4) Only dst, src empty
+     */
+    private final Map<PorterDuff.Mode, int[]> XFERMODE_COLOR_MAP = new LinkedHashMap<PorterDuff.Mode, int[]>() {
+        {
+            put(PorterDuff.Mode.SRC, new int[] {
+                    BG_COLOR, BG_COLOR, SRC_COLOR, SRC_COLOR
+            });
+
+            put(PorterDuff.Mode.DST, new int[] {
+                    BG_COLOR, DST_COLOR, DST_COLOR, BG_COLOR
+            });
+
+            put(PorterDuff.Mode.SRC_OVER, new int[] {
+                    BG_COLOR, DST_COLOR, SRC_COLOR, SRC_COLOR
+            });
+
+            put(PorterDuff.Mode.DST_OVER, new int[] {
+                    BG_COLOR, DST_COLOR, DST_COLOR, SRC_COLOR
+            });
+
+            put(PorterDuff.Mode.SRC_IN, new int[] {
+                    BG_COLOR, BG_COLOR, SRC_COLOR, BG_COLOR
+            });
+
+            put(PorterDuff.Mode.DST_IN, new int[] {
+                    BG_COLOR, BG_COLOR, DST_COLOR, BG_COLOR
+            });
+
+            put(PorterDuff.Mode.SRC_OUT, new int[] {
+                    BG_COLOR, BG_COLOR, BG_COLOR, SRC_COLOR
+            });
+
+            put(PorterDuff.Mode.DST_OUT, new int[] {
+                    BG_COLOR, DST_COLOR, BG_COLOR, BG_COLOR
+            });
+
+            put(PorterDuff.Mode.SRC_ATOP, new int[] {
+                    BG_COLOR, DST_COLOR, SRC_COLOR, BG_COLOR
+            });
+
+            put(PorterDuff.Mode.DST_ATOP, new int[] {
+                    BG_COLOR, BG_COLOR, DST_COLOR, SRC_COLOR
+            });
+
+            put(PorterDuff.Mode.XOR, new int[] {
+                    BG_COLOR, DST_COLOR, BG_COLOR, SRC_COLOR
+            });
+
+            put(PorterDuff.Mode.MULTIPLY, new int[] {
+                    BG_COLOR, BG_COLOR, MULTIPLY_COLOR, BG_COLOR
+            });
+
+            put(PorterDuff.Mode.SCREEN, new int[] {
+                    BG_COLOR, DST_COLOR, SCREEN_COLOR, SRC_COLOR
+            });
+        }
+    };
+
+    private final static DisplayModifier XFERMODE_MODIFIER = new DisplayModifier() {
+        private final RectF mSrcRect = new RectF(60, 60, 160, 160);
+        private final RectF mDstRect = new RectF(20, 20, 120, 120);
+        private final Bitmap mSrcBitmap = createSrc();
+        private final Bitmap mDstBitmap = createDst();
+
+        @Override
+        public void modifyDrawing(Paint paint, Canvas canvas) {
+            int sc = canvas.saveLayer(0, 0, TEST_WIDTH, TEST_HEIGHT, null);
+
+            canvas.drawBitmap(mDstBitmap, 0, 0, null);
+            canvas.drawBitmap(mSrcBitmap, 0, 0, paint);
+
+            canvas.restoreToCount(sc);
+        }
+
+        private Bitmap createSrc() {
+            Bitmap srcB = Bitmap.createBitmap(MODIFIER_WIDTH, MODIFIER_HEIGHT,
+                    Bitmap.Config.ARGB_8888);
+            Canvas srcCanvas = new Canvas(srcB);
+            Paint srcPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+            srcPaint.setColor(SRC_COLOR);
+            srcCanvas.drawRect(mSrcRect, srcPaint);
+            return srcB;
+        }
+
+        private Bitmap createDst() {
+            Bitmap dstB = Bitmap.createBitmap(MODIFIER_WIDTH, MODIFIER_HEIGHT,
+                    Bitmap.Config.ARGB_8888);
+            Canvas dstCanvas = new Canvas(dstB);
+            Paint dstPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+            dstPaint.setColor(DST_COLOR);
+            dstCanvas.drawOval(mDstRect, dstPaint);
+            return dstB;
+        }
+    };
+
+
+    // We care about one point in each of the four rectangles of different alpha values, as well as
+    // the area outside the rectangles
+    private final static Point[] COLOR_FILTER_ALPHA_POINTS = new Point[] {
+            new Point(15, 90), new Point(45, 90), new Point(75, 90), new Point(105, 90),
+            new Point(135, 90)
+    };
+
+    private final Map<PorterDuff.Mode, int[]> COLOR_FILTER_ALPHA_MAP = new LinkedHashMap<PorterDuff.Mode, int[]>() {
+        {
+            put(PorterDuff.Mode.SRC, new int[] {
+                FILTER_COLOR, FILTER_COLOR, FILTER_COLOR, FILTER_COLOR, FILTER_COLOR
+            });
+
+            put(PorterDuff.Mode.DST, new int[] {
+                    0xFFE6E6E6, 0xFFCCCCCC, 0xFFB3B3B3, 0xFF999999, 0xFFFFFFFF
+            });
+
+            put(PorterDuff.Mode.SRC_OVER, new int[] {
+                    0xFFBB0000, 0xFFBB0000, 0xFFBB0000, 0xFFBB0000, 0xFFBB0000
+            });
+
+            put(PorterDuff.Mode.DST_OVER, new int[] {
+                    0xFFAF1A1A, 0xFFA33333, 0xFF984D4D, 0xFF8B6666, 0xFFBB0000
+            });
+
+            put(PorterDuff.Mode.SRC_IN, new int[] {
+                    0xFFF1CCCC, 0xFFE49999, 0xFFD66666, 0xFFC83333, 0xFFFFFFFF
+            });
+
+            put(PorterDuff.Mode.DST_IN, new int[] {
+                    0xFFE6E6E6, 0xFFCCCCCC, 0xFFB3B3B3, 0xFF999999, 0xFFFFFFFF
+            });
+
+            put(PorterDuff.Mode.SRC_OUT, new int[] {
+                    0xFFC83333, 0xFFD66666, 0xFFE49999, 0xFFF1CCCC, 0xFFBB0000
+            });
+
+            put(PorterDuff.Mode.DST_OUT, new int[] {
+                    0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF
+            });
+
+            put(PorterDuff.Mode.SRC_ATOP, new int[] {
+                    0xFFF1CCCC, 0xFFE49999, 0xFFD66666, 0xFFC93333, 0xFFFFFFFF
+            });
+
+            put(PorterDuff.Mode.DST_ATOP, new int[] {
+                    0xFFB01A1A, 0xFFA33333, 0xFF984D4D, 0xFF8B6666, 0xFFBB0000
+            });
+
+            put(PorterDuff.Mode.XOR, new int[] {
+                    0xFFC93333, 0xFFD66666, 0xFFE49999, 0xFFF1CCCC, 0xFFBB0000
+            });
+
+            put(PorterDuff.Mode.MULTIPLY, new int[] {
+                    0xFFDFCCCC, 0xFFBE9999, 0xFF9E6666, 0xFF7E3333, 0xFFFFFFFF
+            });
+
+            put(PorterDuff.Mode.SCREEN, new int[] {
+                    0xFFC21A1A, 0xFFC93333, 0xFFD04D4D, 0xFFD66666, 0xFFBB0000
+            });
+        }
+    };
+
+    private final static DisplayModifier COLOR_FILTER_ALPHA_MODIFIER = new DisplayModifier() {
+        private final static int mBlockWidths = 30;
+        private final int[] mColorValues = new int[] {RECT0_COLOR, RECT1_COLOR, RECT2_COLOR,
+                RECT3_COLOR};
+
+        private final Bitmap mBitmap = createQuadRectBitmap();
+
+        public void modifyDrawing(Paint paint, Canvas canvas) {
+            canvas.drawBitmap(mBitmap, 0, 0, paint);
+        }
+
+        private Bitmap createQuadRectBitmap() {
+            Bitmap bitmap = Bitmap.createBitmap(MODIFIER_WIDTH, MODIFIER_HEIGHT,
+                    Bitmap.Config.ARGB_8888);
+            Canvas canvas = new Canvas(bitmap);
+            Paint paint = new Paint();
+            for (int i = 0 ; i < 4 ; i++) {
+                paint.setColor(mColorValues[i]);
+                canvas.drawRect(i * mBlockWidths, 0, (i + 1) * mBlockWidths, MODIFIER_HEIGHT, paint);
+            }
+            return bitmap;
+        }
+    };
+
+    private final static DisplayModifier COLOR_FILTER_GRADIENT_MODIFIER = new DisplayModifier() {
+        private final Rect mBounds = new Rect(30, 30, 150, 150);
+        private final int[] mColors = new int[] {
+                Color.RED, Color.GREEN, Color.BLUE
+        };
+
+        private final Bitmap mBitmap = createGradient();
+
+        @Override
+        public void modifyDrawing(Paint paint, Canvas canvas) {
+            canvas.drawBitmap(mBitmap, 0, 0, paint);
+        }
+
+        private Bitmap createGradient() {
+            LinearGradient gradient = new LinearGradient(30, 90, 150, 90, mColors, null,
+                    Shader.TileMode.REPEAT);
+            Bitmap bitmap = Bitmap.createBitmap(MODIFIER_WIDTH, MODIFIER_HEIGHT,
+                    Bitmap.Config.ARGB_8888);
+            Paint p = new Paint();
+            p.setShader(gradient);
+            Canvas c = new Canvas(bitmap);
+            c.drawRect(mBounds, p);
+            return bitmap;
+        }
+    };
+
+    public static final DisplayModifier mCircleDrawModifier = new DisplayModifier() {
+        @Override
+        public void modifyDrawing(Paint paint, Canvas canvas) {
+            canvas.drawCircle(ActivityTestBase.TEST_WIDTH / 2,
+                    ActivityTestBase.TEST_HEIGHT / 2,
+                    ActivityTestBase.TEST_HEIGHT / 2, paint);
+        }
+    };
+
+    /**
+     * 0.5 defines minimum similarity as 50%
+     */
+    private static final float HIGH_THRESHOLD = 0.5f;
+
+    private static final BitmapComparer[] DEFAULT_MSSIM_COMPARER = new BitmapComparer[] {
+            new MSSIMComparer(HIGH_THRESHOLD)
+    };
+
+    @SmallTest
+    public void testBasicDraws() {
+        sweepModifiersForMask(DisplayModifier.Accessor.SHAPES_MASK, null, DEFAULT_MSSIM_COMPARER,
+                null);
+    }
+
+    @SmallTest
+    public void testBasicShaders() {
+        sweepModifiersForMask(DisplayModifier.Accessor.SHADER_MASK, mCircleDrawModifier,
+                DEFAULT_MSSIM_COMPARER, null);
+    }
+
+    @SmallTest
+    public void testColorFilterUsingGradient() {
+        sweepModifiersForMask(DisplayModifier.Accessor.COLOR_FILTER_MASK,
+                COLOR_FILTER_GRADIENT_MODIFIER, DEFAULT_MSSIM_COMPARER, null);
+    }
+
+    @SmallTest
+    public void testColorFiltersAlphas() {
+        BitmapVerifier[] bitmapVerifiers =
+                new BitmapVerifier[DisplayModifier.PORTERDUFF_MODES.length];
+        int index = 0;
+        for (PorterDuff.Mode mode : DisplayModifier.PORTERDUFF_MODES) {
+            bitmapVerifiers[index] = new SamplePointVerifier(COLOR_FILTER_ALPHA_POINTS,
+                    COLOR_FILTER_ALPHA_MAP.get(mode));
+            index++;
+        }
+        sweepModifiersForMask(DisplayModifier.Accessor.COLOR_FILTER_MASK,
+                COLOR_FILTER_ALPHA_MODIFIER, null, bitmapVerifiers);
+    }
+
+    @SmallTest
+    public void testXfermodes() {
+        BitmapVerifier[] bitmapVerifiers =
+                new BitmapVerifier[DisplayModifier.PORTERDUFF_MODES.length];
+        int index = 0;
+        for (PorterDuff.Mode mode : DisplayModifier.PORTERDUFF_MODES) {
+            bitmapVerifiers[index] = new SamplePointVerifier(XFERMODE_TEST_POINTS,
+                    XFERMODE_COLOR_MAP.get(mode));
+            index++;
+        }
+        sweepModifiersForMask(DisplayModifier.Accessor.XFERMODE_MASK, XFERMODE_MODIFIER,
+                null, bitmapVerifiers);
+    }
+
+    @SmallTest
+    public void testShaderSweeps() {
+        int mask = DisplayModifier.Accessor.AA_MASK |
+                DisplayModifier.Accessor.SHADER_MASK |
+                DisplayModifier.Accessor.XFERMODE_MASK |
+                DisplayModifier.Accessor.SHAPES_MASK;
+        sweepModifiersForMask(mask, null, DEFAULT_MSSIM_COMPARER, null);
+    }
+
+    protected void sweepModifiersForMask(int mask, final DisplayModifier drawOp,
+            BitmapComparer[] bitmapComparers, BitmapVerifier[] bitmapVerifiers) {
+        if ((mask & DisplayModifier.Accessor.ALL_OPTIONS_MASK) == 0) {
+            throw new IllegalArgumentException("Attempt to test with a mask that is invalid");
+        }
+        // Get the accessor of all the different modifications possible
+        final DisplayModifier.Accessor modifierAccessor = new DisplayModifier.Accessor(mask);
+        // Initialize the resources that we will need to access
+        ResourceModifier.init(getActivity().getResources());
+        // For each modification combination, we will get the CanvasClient associated with it and
+        // from there execute a normal canvas test with that.
+        CanvasClient canvasClient = new CanvasClient() {
+            @Override
+            public void draw(Canvas canvas, int width, int height) {
+                Paint paint = new Paint();
+                modifierAccessor.modifyDrawing(canvas, paint);
+                if (drawOp != null) {
+                    drawOp.modifyDrawing(paint, canvas);
+                }
+            }
+        };
+
+        int index = 0;
+        // Create the test cases with each combination
+        do {
+            canvasClient.setDebugString(modifierAccessor.getDebugString());
+            if (bitmapComparers != null) {
+                int arrIndex = Math.min(index, bitmapComparers.length - 1);
+                createTest().addCanvasClient(canvasClient).runWithComparer(bitmapComparers[arrIndex]);
+            } else {
+                int arrIndex = Math.min(index, bitmapVerifiers.length - 1);
+                createTest().addCanvasClient(canvasClient).runWithVerifier(bitmapVerifiers[arrIndex]);
+            }
+            index++;
+        } while (modifierAccessor.step());
+    }
+}
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/ViewClippingTests.java b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/ViewClippingTests.java
new file mode 100644
index 0000000..1acdc20
--- /dev/null
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/ViewClippingTests.java
@@ -0,0 +1,107 @@
+package android.uirendering.cts.testclasses;
+
+import android.graphics.Color;
+import android.graphics.Outline;
+import android.graphics.Rect;
+import android.uirendering.cts.bitmapverifiers.BitmapVerifier;
+import android.uirendering.cts.bitmapverifiers.RectVerifier;
+import android.uirendering.cts.testclasses.view.UnclippedBlueView;
+import android.uirendering.cts.testinfrastructure.ActivityTestBase;
+import android.uirendering.cts.testinfrastructure.ViewInitializer;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewOutlineProvider;
+import com.android.cts.uirendering.R;
+
+/**
+ * This tests view clipping by modifying properties of blue_padded_layout, and validating
+ * the resulting rect of content.
+ *
+ * Since the layout is blue on a white background, this is always done with a RectVerifier.
+ */
+public class ViewClippingTests extends ActivityTestBase {
+    final Rect FULL_RECT = new Rect(0, 0, 200, 200);
+    final Rect BOUNDS_RECT = new Rect(0, 0, 100, 100);
+    final Rect PADDED_RECT = new Rect(15, 16, 83, 82);
+    final Rect OUTLINE_RECT = new Rect(1, 2, 98, 99);
+    final Rect CLIP_BOUNDS_RECT = new Rect(10, 20, 70, 80);
+
+    final ViewInitializer BOUNDS_CLIP_INIT = new ViewInitializer() {
+        @Override
+        public void intializeView(View rootView) {
+            ((ViewGroup)rootView).setClipChildren(true);
+        }
+    };
+    final ViewInitializer PADDING_CLIP_INIT = new ViewInitializer() {
+        @Override
+        public void intializeView(View rootView) {
+            ViewGroup child = (ViewGroup) rootView.findViewById(R.id.child);
+            child.setClipToPadding(true);
+            child.setWillNotDraw(true);
+            child.addView(new UnclippedBlueView(rootView.getContext()));
+        }
+    };
+    final ViewInitializer OUTLINE_CLIP_INIT = new ViewInitializer() {
+        @Override
+        public void intializeView(View rootView) {
+            View child = rootView.findViewById(R.id.child);
+            child.setOutlineProvider(new ViewOutlineProvider() {
+                @Override
+                public void getOutline(View view, Outline outline) {
+                    outline.setRect(OUTLINE_RECT);
+                }
+            });
+            child.setClipToOutline(true);
+        }
+    };
+    final ViewInitializer CLIP_BOUNDS_CLIP_INIT = new ViewInitializer() {
+        @Override
+        public void intializeView(View view) {
+            view.setClipBounds(CLIP_BOUNDS_RECT);
+        }
+    };
+
+    // TODO: attempt to reduce
+    static final int TOLERANCE = 10;
+    static BitmapVerifier makeClipVerifier(Rect blueBoundsRect) {
+        return new RectVerifier(Color.WHITE, Color.BLUE, blueBoundsRect, TOLERANCE);
+    }
+
+    public void testSimpleUnclipped() {
+        createTest()
+                .addLayout(R.layout.blue_padded_layout, null)
+                .runWithVerifier(makeClipVerifier(FULL_RECT));
+    }
+
+    public void testSimpleBoundsClip() {
+        createTest()
+                .addLayout(R.layout.blue_padded_layout, BOUNDS_CLIP_INIT)
+                .runWithVerifier(makeClipVerifier(BOUNDS_RECT));
+    }
+
+    public void testSimpleClipBoundsClip() {
+        createTest()
+                .addLayout(R.layout.blue_padded_layout, CLIP_BOUNDS_CLIP_INIT)
+                .runWithVerifier(makeClipVerifier(CLIP_BOUNDS_RECT));
+    }
+
+    public void testSimplePaddingClip() {
+        createTest()
+                .addLayout(R.layout.blue_padded_layout, PADDING_CLIP_INIT)
+                .runWithVerifier(makeClipVerifier(PADDED_RECT));
+    }
+
+    public void testSimpleOutlineClip() {
+        // NOTE: Only HW is supported
+        createTest()
+                .addLayout(R.layout.blue_padded_layout, OUTLINE_CLIP_INIT, true)
+                .runWithVerifier(makeClipVerifier(OUTLINE_RECT));
+
+        // SW ignores the outline clip
+        createTest()
+                .addLayout(R.layout.blue_padded_layout, OUTLINE_CLIP_INIT, false)
+                .runWithVerifier(makeClipVerifier(FULL_RECT));
+    }
+
+    // TODO: add tests with clip + scroll, and with interesting combinations of the above
+}
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/view/UnclippedBlueView.java b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/view/UnclippedBlueView.java
new file mode 100644
index 0000000..e2037f7
--- /dev/null
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/view/UnclippedBlueView.java
@@ -0,0 +1,33 @@
+package android.uirendering.cts.testclasses.view;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.FrameLayout;
+
+public class UnclippedBlueView extends FrameLayout {
+    public UnclippedBlueView(Context context) {
+        this(context, null);
+    }
+
+    public UnclippedBlueView(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public UnclippedBlueView(Context context, AttributeSet attrs, int defStyleAttr) {
+        this(context, attrs, defStyleAttr, 0);
+        setWillNotDraw(false);
+    }
+
+    public UnclippedBlueView(Context context, AttributeSet attrs,
+            int defStyleAttr, int defStyleRes) {
+        super(context, attrs, defStyleAttr, defStyleRes);
+    }
+
+    @Override
+    protected void onDraw(Canvas canvas) {
+        canvas.drawColor(Color.BLUE);
+    }
+}
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/ActivityTestBase.java b/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/ActivityTestBase.java
new file mode 100644
index 0000000..b6665b0
--- /dev/null
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/ActivityTestBase.java
@@ -0,0 +1,311 @@
+/*
+ * 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.uirendering.cts.testinfrastructure;
+
+import android.annotation.Nullable;
+import android.graphics.Bitmap;
+import android.renderscript.Allocation;
+import android.renderscript.RenderScript;
+import android.test.ActivityInstrumentationTestCase2;
+import android.uirendering.cts.bitmapcomparers.BitmapComparer;
+import android.uirendering.cts.bitmapverifiers.BitmapVerifier;
+import android.uirendering.cts.differencevisualizers.DifferenceVisualizer;
+import android.uirendering.cts.differencevisualizers.PassFailVisualizer;
+import android.uirendering.cts.util.BitmapDumper;
+import android.util.Log;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * This class contains the basis for the graphics hardware test classes. Contained within this class
+ * are several methods that help with the execution of tests, and should be extended to gain the
+ * functionality built in.
+ */
+public abstract class ActivityTestBase extends
+        ActivityInstrumentationTestCase2<DrawActivity> {
+    public static final String TAG_NAME = "ActivityTestBase";
+    public static final boolean DEBUG = false;
+    public static final boolean USE_RS = false;
+    public static final int TEST_WIDTH = 180;
+    public static final int TEST_HEIGHT = 180; //The minimum height and width of a device
+
+    private int[] mHardwareArray = new int[TEST_HEIGHT * TEST_WIDTH];
+    private int[] mSoftwareArray = new int[TEST_HEIGHT * TEST_WIDTH];
+    private DifferenceVisualizer mDifferenceVisualizer;
+    private Allocation mIdealAllocation;
+    private Allocation mGivenAllocation;
+    private RenderScript mRenderScript;
+    private TestCaseBuilder mTestCaseBuilder;
+
+    /**
+     * The default constructor creates the package name and sets the DrawActivity as the class that
+     * we would use.
+     */
+    public ActivityTestBase() {
+        super(DrawActivity.class);
+        mDifferenceVisualizer = new PassFailVisualizer();
+
+        // Create a location for the files to be held, if it doesn't exist already
+        BitmapDumper.createSubDirectory(this.getClass().getSimpleName());
+
+        // If we have a test currently, let's remove the older files if they exist
+        if (getName() != null) {
+            BitmapDumper.deleteFileInClassFolder(this.getClass().getSimpleName(), getName());
+        }
+    }
+
+    /**
+     * This method is called before each test case and should be called from the test class that
+     * extends this class.
+     */
+    @Override
+    public void setUp() {
+        mDifferenceVisualizer = new PassFailVisualizer();
+        if (USE_RS) {
+            mRenderScript = RenderScript.create(getActivity().getApplicationContext());
+        }
+    }
+
+    /**
+     * This method will kill the activity so that it can be reset depending on the test.
+     */
+    @Override
+    public void tearDown() {
+        List<TestCase> testCases = mTestCaseBuilder.getTestCases();
+
+        if (testCases.size() == 0) {
+            throw new IllegalStateException("Must have at least one test case");
+        }
+
+        for (TestCase testCase : testCases) {
+            if (!testCase.wasTestRan) {
+                Log.w(TAG_NAME, getName() + " not all of the tests were ran");
+                break;
+            }
+        }
+
+        mTestCaseBuilder = null;
+
+        Runnable finishRunnable = new Runnable() {
+
+            @Override
+            public void run() {
+                getActivity().finish();
+            }
+        };
+
+        getActivity().runOnUiThread(finishRunnable);
+    }
+
+    public Bitmap takeScreenshot() {
+        return getInstrumentation().getUiAutomation().takeScreenshot();
+    }
+
+    /**
+     * Sets the current DifferenceVisualizer for use in current test.
+     */
+    public void setDifferenceVisualizer(DifferenceVisualizer differenceVisualizer) {
+        mDifferenceVisualizer = differenceVisualizer;
+    }
+
+    /**
+     * Used to execute a specific part of a test and get the resultant bitmap
+     */
+    protected Bitmap captureRenderSpec(TestCase testCase) {
+        getActivity().enqueueRenderSpecAndWait(testCase.layoutID, testCase.canvasClient,
+                testCase.webViewUrl, testCase.viewInitializer, testCase.useHardware);
+        testCase.wasTestRan = true;
+        return takeScreenshot();
+    }
+
+    /**
+     * Compares the two bitmaps saved using the given test. If they fail, the files are saved using
+     * the test name.
+     */
+    protected void assertBitmapsAreSimilar(Bitmap bitmap1, Bitmap bitmap2,
+            BitmapComparer comparer, String debugMessage) {
+        boolean success;
+
+        if (USE_RS && comparer.supportsRenderScript()) {
+            mIdealAllocation = Allocation.createFromBitmap(mRenderScript, bitmap1,
+                    Allocation.MipmapControl.MIPMAP_NONE, Allocation.USAGE_SCRIPT);
+            mGivenAllocation = Allocation.createFromBitmap(mRenderScript, bitmap2,
+                    Allocation.MipmapControl.MIPMAP_NONE, Allocation.USAGE_SCRIPT);
+            success = comparer.verifySameRS(getActivity().getResources(), mIdealAllocation,
+                    mGivenAllocation, 0, TEST_WIDTH, TEST_WIDTH, TEST_HEIGHT, mRenderScript);
+        } else {
+            bitmap1.getPixels(mSoftwareArray, 0, TEST_WIDTH, 0, 0, TEST_WIDTH, TEST_HEIGHT);
+            bitmap2.getPixels(mHardwareArray, 0, TEST_WIDTH, 0, 0, TEST_WIDTH, TEST_HEIGHT);
+            success = comparer.verifySame(mSoftwareArray, mHardwareArray, 0, TEST_WIDTH, TEST_WIDTH,
+                    TEST_HEIGHT);
+        }
+
+        if (!success) {
+            BitmapDumper.dumpBitmaps(bitmap1, bitmap2, getName(), this.getClass().getSimpleName(),
+                    mDifferenceVisualizer);
+        }
+
+        assertTrue(debugMessage, success);
+    }
+
+    /**
+     * Tests to see if a bitmap passes a verifier's test. If it doesn't the bitmap is saved to the
+     * sdcard.
+     */
+    protected void assertBitmapIsVerified(Bitmap bitmap, BitmapVerifier bitmapVerifier,
+            String debugMessage) {
+        bitmap.getPixels(mSoftwareArray, 0, TEST_WIDTH, 0, 0,
+                TEST_WIDTH, TEST_HEIGHT);
+        boolean success = bitmapVerifier.verify(mSoftwareArray, 0, TEST_WIDTH, TEST_WIDTH, TEST_HEIGHT);
+        if (!success) {
+            BitmapDumper.dumpBitmap(bitmap, getName(), this.getClass().getSimpleName());
+            BitmapDumper.dumpBitmap(bitmapVerifier.getDifferenceBitmap(), getName() + "_verifier",
+                    this.getClass().getSimpleName());
+        }
+        assertTrue(debugMessage, success);
+    }
+
+    protected TestCaseBuilder createTest() {
+        mTestCaseBuilder = new TestCaseBuilder();
+        return mTestCaseBuilder;
+    }
+
+    /**
+     * Defines a group of CanvasClients, XML layouts, and WebView html files for testing.
+     */
+    protected class TestCaseBuilder {
+        private List<TestCase> mTestCases;
+
+        private TestCaseBuilder() {
+            mTestCases = new ArrayList<TestCase>();
+        }
+
+        /**
+         * Runs a test where the first test case is considered the "ideal" image and from there,
+         * every test case is tested against it.
+         */
+        public void runWithComparer(BitmapComparer bitmapComparer) {
+            if (mTestCases.size() == 0) {
+                throw new IllegalStateException("Need at least one test to run");
+            }
+
+            Bitmap idealBitmap = captureRenderSpec(mTestCases.remove(0));
+
+            for (TestCase testCase : mTestCases) {
+                Bitmap testCaseBitmap = captureRenderSpec(testCase);
+                assertBitmapsAreSimilar(idealBitmap, testCaseBitmap, bitmapComparer,
+                        testCase.getDebugString());
+            }
+        }
+
+        /**
+         * Runs a test where each testcase is independent of the others and each is checked against
+         * the verifier given.
+         */
+        public void runWithVerifier(BitmapVerifier bitmapVerifier) {
+            if (mTestCases.size() == 0) {
+                throw new IllegalStateException("Need at least one test to run");
+            }
+
+            for (TestCase testCase : mTestCases) {
+                Bitmap testCaseBitmap = captureRenderSpec(testCase);
+                assertBitmapIsVerified(testCaseBitmap, bitmapVerifier, testCase.getDebugString());
+            }
+        }
+
+        public TestCaseBuilder addWebView(String webViewUrl,
+                @Nullable ViewInitializer viewInitializer) {
+            return addWebView(webViewUrl, viewInitializer, false)
+                    .addWebView(webViewUrl, viewInitializer, true);
+        }
+
+        public TestCaseBuilder addLayout(int layoutId, @Nullable ViewInitializer viewInitializer) {
+            return addLayout(layoutId, viewInitializer, false)
+                    .addLayout(layoutId, viewInitializer, true);
+        }
+
+        public TestCaseBuilder addCanvasClient(CanvasClient canvasClient) {
+            return addCanvasClient(canvasClient, false)
+                    .addCanvasClient(canvasClient, true);
+        }
+
+        public TestCaseBuilder addWebView(String webViewUrl,
+                @Nullable ViewInitializer viewInitializer, boolean useHardware) {
+            mTestCases.add(new TestCase(null, 0, webViewUrl, viewInitializer, useHardware));
+            return this;
+        }
+
+        public TestCaseBuilder addLayout(int layoutId, @Nullable ViewInitializer viewInitializer,
+                boolean useHardware) {
+            mTestCases.add(new TestCase(null, layoutId, null, viewInitializer, useHardware));
+            return this;
+        }
+
+        public TestCaseBuilder addCanvasClient(CanvasClient canvasClient, boolean useHardware) {
+            mTestCases.add(new TestCase(canvasClient, 0, null, null, useHardware));
+            return this;
+        }
+
+        private List<TestCase> getTestCases() {
+            return mTestCases;
+        }
+    }
+
+    private class TestCase {
+        public int layoutID;
+        public CanvasClient canvasClient;
+        public String webViewUrl;
+        public ViewInitializer viewInitializer;
+        public boolean useHardware;
+        public boolean wasTestRan;
+
+        public TestCase(CanvasClient client, int id, String viewUrl,
+                ViewInitializer viewInitializer, boolean useHardware) {
+            int count = 0;
+            count += (client == null ? 0 : 1);
+            count += (viewUrl == null ? 0 : 1);
+            count += (id == 0 ? 0 : 1);
+            assert(count == 1);
+            assert(client == null || viewInitializer == null);
+            this.layoutID = id;
+            this.canvasClient = client;
+            this.webViewUrl = viewUrl;
+            this.viewInitializer = viewInitializer;
+            this.useHardware = useHardware;
+            this.wasTestRan = false;
+        }
+
+        public String getDebugString() {
+            String debug = "";
+            if (canvasClient != null) {
+                debug += "CanvasClient : ";
+                if (canvasClient.getDebugString() != null) {
+                    debug += canvasClient.getDebugString();
+                } else {
+                    debug += "no debug string given";
+                }
+            } else if (webViewUrl != null) {
+                debug += "WebView URL : " + webViewUrl;
+            } else {
+                debug += "Layout resource : " +
+                        getActivity().getResources().getResourceName(layoutID);
+            }
+            debug += "\nTest ran in " + (useHardware ? "hardware" : "software") + "\n";
+            return debug;
+        }
+    }
+}
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/CanvasClient.java b/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/CanvasClient.java
new file mode 100644
index 0000000..a99c576
--- /dev/null
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/CanvasClient.java
@@ -0,0 +1,35 @@
+/*
+ * 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.uirendering.cts.testinfrastructure;
+
+import android.graphics.Canvas;
+
+/**
+ * A class that the tester will implement and create a set of drawing calls the tests would use
+ */
+public abstract class CanvasClient {
+    private String mDebugString;
+
+    public abstract void draw(Canvas canvas, int width, int height);
+
+    public String getDebugString() {
+        return mDebugString;
+    }
+
+    public void setDebugString(String debugString) {
+        mDebugString = debugString;
+    }
+}
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/CanvasClientView.java b/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/CanvasClientView.java
new file mode 100644
index 0000000..92242f0
--- /dev/null
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/CanvasClientView.java
@@ -0,0 +1,55 @@
+/*
+ * 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.uirendering.cts.testinfrastructure;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.view.View;
+
+/**
+ * A simple View that uses a CanvasClient to draw its contents
+ */
+public class CanvasClientView extends View {
+    private CanvasClient mCanvasClient;
+    private int mWidth;
+    private int mHeight;
+
+    public CanvasClientView(Context context, CanvasClient canvasClient, int width, int height) {
+        super(context);
+        mCanvasClient = canvasClient;
+        mWidth = width;
+        mHeight = height;
+    }
+
+    @Override
+    public void onDraw(Canvas canvas) {
+        if (ActivityTestBase.DEBUG) {
+            String s = canvas.isHardwareAccelerated() ? "HARDWARE" : "SOFTWARE";
+            Paint paint = new Paint();
+            paint.setColor(Color.BLACK);
+            paint.setTextSize(20);
+            canvas.drawText(s, 200, 200, paint);
+        }
+        if (mCanvasClient != null) {
+            canvas.save();
+            canvas.clipRect(0, 0, mWidth, mHeight);
+            mCanvasClient.draw(canvas, mWidth, mHeight);
+            canvas.restore();
+        }
+    }
+}
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/DisplayModifier.java b/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/DisplayModifier.java
new file mode 100644
index 0000000..50880e9
--- /dev/null
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/DisplayModifier.java
@@ -0,0 +1,579 @@
+/*
+     * 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/LICENSE2.0
+     *
+     * Unless required by applicable law or agreed to in riting, software
+     * distributed under the License is distributed on an "AS IS" BASIS,
+     * WITHOUT 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.uirendering.cts.testinfrastructure;
+
+import android.graphics.Canvas;
+import android.graphics.ColorFilter;
+import android.graphics.Paint;
+import android.graphics.PorterDuff;
+import android.graphics.PorterDuffColorFilter;
+import android.graphics.PorterDuffXfermode;
+import android.graphics.RectF;
+import android.graphics.Xfermode;
+
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+/**
+ * Modifies the canvas and paint objects when called.
+ */
+public abstract class DisplayModifier {
+    private static final RectF gRect = new RectF(0, 0, 100, 100);
+    private static final float[] gPts = new float[]{
+            0, 100, 100, 0, 100, 200, 200, 100
+    };
+    private static final float[] gTriPts = new float[]{
+            75, 0, 130, 130, 130, 130, 0, 130, 0, 130, 75, 0
+    };
+    private static final int NUM_PARALLEL_LINES = 24;
+    private static final float[] gLinePts = new float[NUM_PARALLEL_LINES * 8 + gTriPts.length];
+    protected static final int MODIFIER_WIDTH = 180;
+    protected static final int MODIFIER_HEIGHT = 180;
+
+    public static final PorterDuff.Mode[] PORTERDUFF_MODES = new PorterDuff.Mode[] {
+        PorterDuff.Mode.SRC, PorterDuff.Mode.DST, PorterDuff.Mode.SRC_OVER,
+        PorterDuff.Mode.DST_OVER, PorterDuff.Mode.SRC_IN, PorterDuff.Mode.DST_IN,
+        PorterDuff.Mode.SRC_OUT, PorterDuff.Mode.DST_OUT, PorterDuff.Mode.SRC_ATOP,
+        PorterDuff.Mode.DST_ATOP, PorterDuff.Mode.XOR, PorterDuff.Mode.MULTIPLY,
+        PorterDuff.Mode.SCREEN
+    };
+
+    static {
+        int index;
+        for (index = 0; index < gTriPts.length; index++) {
+            gLinePts[index] = gTriPts[index];
+        }
+        float val = 0;
+        for (int i = 0; i < NUM_PARALLEL_LINES; i++) {
+            gLinePts[index + 0] = 150;
+            gLinePts[index + 1] = val;
+            gLinePts[index + 2] = 300;
+            gLinePts[index + 3] = val;
+            index += 4;
+            val += 8 + (2.0f / NUM_PARALLEL_LINES);
+        }
+        val = 0;
+        for (int i = 0; i < NUM_PARALLEL_LINES; i++) {
+            gLinePts[index + 0] = val;
+            gLinePts[index + 1] = 150;
+            gLinePts[index + 2] = val;
+            gLinePts[index + 3] = 300;
+            index += 4;
+            val += 8 + (2.0f / NUM_PARALLEL_LINES);
+        }
+    }
+
+    // This linked hash map contains each of the different things that can be done to a canvas and
+    // paint object, like anti-aliasing or drawing. Within those LinkedHashMaps are the various
+    // options for that specific topic, which contains a displaymodifier which will affect the
+    // given canvas and paint objects.
+    public static final LinkedHashMap<String, LinkedHashMap<String, DisplayModifier>> sMaps =
+            new LinkedHashMap<String, LinkedHashMap<String,DisplayModifier>>() {
+                {
+                    put("aa", new LinkedHashMap<String, DisplayModifier>() {
+                        {
+                            put("true", new DisplayModifier() {
+                                @Override
+                                public void modifyDrawing(Paint paint, Canvas canvas) {
+                                    paint.setAntiAlias(true);
+                                }
+                            });
+                            put("false", new DisplayModifier() {
+                                @Override
+                                public void modifyDrawing(Paint paint, Canvas canvas) {
+                                    paint.setAntiAlias(false);
+                                }
+                            });
+                        }
+                    });
+                    put("style", new LinkedHashMap<String, DisplayModifier>() {
+                        {
+                            put("fill", new DisplayModifier() {
+                                @Override
+                                public void modifyDrawing(Paint paint, Canvas canvas) {
+                                    paint.setStyle(Paint.Style.FILL);
+                                }
+                            });
+                            put("stroke", new DisplayModifier() {
+                                @Override
+                                public void modifyDrawing(Paint paint, Canvas canvas) {
+                                    paint.setStyle(Paint.Style.STROKE);
+                                }
+                            });
+                            put("fillAndStroke", new DisplayModifier() {
+                                @Override
+                                public void modifyDrawing(Paint paint, Canvas canvas) {
+                                    paint.setStyle(Paint.Style.FILL_AND_STROKE);
+                                }
+                            });
+                        }
+                    });
+                    put("strokeWidth", new LinkedHashMap<String, DisplayModifier>() {
+                        {
+                            put("hair", new DisplayModifier() {
+                                @Override
+                                public void modifyDrawing(Paint paint, Canvas canvas) {
+                                    paint.setStrokeWidth(0);
+                                }
+                            });
+                            put("0.3", new DisplayModifier() {
+                                @Override
+                                public void modifyDrawing(Paint paint, Canvas canvas) {
+                                    paint.setStrokeWidth(0.3f);
+                                }
+                            });
+                            put("1", new DisplayModifier() {
+                                @Override
+                                public void modifyDrawing(Paint paint, Canvas canvas) {
+                                    paint.setStrokeWidth(1);
+                                }
+                            });
+                            put("5", new DisplayModifier() {
+                                @Override
+                                public void modifyDrawing(Paint paint, Canvas canvas) {
+                                    paint.setStrokeWidth(5);
+                                }
+                            });
+                            put("30", new DisplayModifier() {
+                                @Override
+                                public void modifyDrawing(Paint paint, Canvas canvas) {
+                                    paint.setStrokeWidth(30);
+                                }
+                            });
+                        }
+                    });
+                    put("strokeCap", new LinkedHashMap<String, DisplayModifier>() {
+                        {
+                            put("butt", new DisplayModifier() {
+                                @Override
+                                public void modifyDrawing(Paint paint, Canvas canvas) {
+                                    paint.setStrokeCap(Paint.Cap.BUTT);
+                                }
+                            });
+                            put("round", new DisplayModifier() {
+                                @Override
+                                public void modifyDrawing(Paint paint, Canvas canvas) {
+                                    paint.setStrokeCap(Paint.Cap.ROUND);
+                                }
+                            });
+                            put("square", new DisplayModifier() {
+                                @Override
+                                public void modifyDrawing(Paint paint, Canvas canvas) {
+                                    paint.setStrokeCap(Paint.Cap.SQUARE);
+                                }
+                            });
+                        }
+                    });
+                    put("strokeJoin", new LinkedHashMap<String, DisplayModifier>() {
+                        {
+                            put("bevel", new DisplayModifier() {
+                                @Override
+                                public void modifyDrawing(Paint paint, Canvas canvas) {
+                                    paint.setStrokeJoin(Paint.Join.BEVEL);
+                                }
+                            });
+                            put("round", new DisplayModifier() {
+                                @Override
+                                public void modifyDrawing(Paint paint, Canvas canvas) {
+                                    paint.setStrokeJoin(Paint.Join.ROUND);
+                                }
+                            });
+                            put("miter", new DisplayModifier() {
+                                @Override
+                                public void modifyDrawing(Paint paint, Canvas canvas) {
+                                    paint.setStrokeJoin(Paint.Join.MITER);
+                                }
+                            });
+                            // TODO: add miter0, miter1 etc to test miter distances
+                        }
+                    });
+
+                    put("transform", new LinkedHashMap<String, DisplayModifier>() {
+                        {
+                            put("noTransform", new DisplayModifier() {
+                                @Override
+                                public void modifyDrawing(Paint paint, Canvas canvas) {
+                                }
+                            });
+                            put("rotate5", new DisplayModifier() {
+                                @Override
+                                public void modifyDrawing(Paint paint, Canvas canvas) {
+                                    canvas.rotate(5);
+                                }
+                            });
+                            put("rotate45", new DisplayModifier() {
+                                @Override
+                                public void modifyDrawing(Paint paint, Canvas canvas) {
+                                    canvas.rotate(45);
+                                }
+                            });
+                            put("rotate90", new DisplayModifier() {
+                                @Override
+                                public void modifyDrawing(Paint paint, Canvas canvas) {
+                                    canvas.rotate(90);
+                                    canvas.translate(0, -200);
+                                }
+                            });
+                            put("scale2x2", new DisplayModifier() {
+                                @Override
+                                public void modifyDrawing(Paint paint, Canvas canvas) {
+                                    canvas.scale(2, 2);
+                                }
+                            });
+                            put("rot20scl1x4", new DisplayModifier() {
+                                @Override
+                                public void modifyDrawing(Paint paint, Canvas canvas) {
+                                    canvas.rotate(20);
+                                    canvas.scale(1, 4);
+                                }
+                            });
+                        }
+                    });
+
+                    put("shader", new LinkedHashMap<String, DisplayModifier>() {
+                        {
+                            put("noShader", new DisplayModifier() {
+                                @Override
+                                public void modifyDrawing(Paint paint, Canvas canvas) {
+                                }
+                            });
+                            put("repeatShader", new DisplayModifier() {
+                                @Override
+                                public void modifyDrawing(Paint paint, Canvas canvas) {
+                                    paint.setShader(ResourceModifier.instance().repeatShader);
+                                }
+                            });
+                            put("translatedShader", new DisplayModifier() {
+                                @Override
+                                public void modifyDrawing(Paint paint, Canvas canvas) {
+                                    paint.setShader(ResourceModifier.instance().translatedShader);
+                                }
+                            });
+                            put("scaledShader", new DisplayModifier() {
+                                @Override
+                                public void modifyDrawing(Paint paint, Canvas canvas) {
+                                    paint.setShader(ResourceModifier.instance().scaledShader);
+                                }
+                            });
+                            put("composeShader", new DisplayModifier() {
+                                @Override
+                                public void modifyDrawing(Paint paint, Canvas canvas) {
+                                    paint.setShader(ResourceModifier.instance().composeShader);
+                                }
+                            });
+                            put("bad composeShader", new DisplayModifier() {
+                                @Override
+                                public void modifyDrawing(Paint paint, Canvas canvas) {
+                                    paint.setShader(ResourceModifier.instance().nestedComposeShader);
+                                }
+                            });
+                            put("bad composeShader 2", new DisplayModifier() {
+                                @Override
+                                public void modifyDrawing(Paint paint, Canvas canvas) {
+                                    paint.setShader(
+                                            ResourceModifier.instance().doubleGradientComposeShader);
+                                }
+                            });
+                            put("horGradient", new DisplayModifier() {
+                                @Override
+                                public void modifyDrawing(Paint paint, Canvas canvas) {
+                                    paint.setShader(ResourceModifier.instance().horGradient);
+                                }
+                            });
+                            put("diagGradient", new DisplayModifier() {
+                                @Override
+                                public void modifyDrawing(Paint paint, Canvas canvas) {
+                                    paint.setShader(ResourceModifier.instance().diagGradient);
+                                }
+                            });
+                            put("vertGradient", new DisplayModifier() {
+                                @Override
+                                public void modifyDrawing(Paint paint, Canvas canvas) {
+                                    paint.setShader(ResourceModifier.instance().vertGradient);
+                                }
+                            });
+                            put("radGradient", new DisplayModifier() {
+                                @Override
+                                public void modifyDrawing(Paint paint, Canvas canvas) {
+                                    paint.setShader(ResourceModifier.instance().radGradient);
+                                }
+                            });
+                            put("sweepGradient", new DisplayModifier() {
+                                @Override
+                                public void modifyDrawing(Paint paint, Canvas canvas) {
+                                    paint.setShader(ResourceModifier.instance().sweepGradient);
+                                }
+                            });
+                        }
+                    });
+
+                    put("xfermodes", new LinkedHashMap<String, DisplayModifier>() {
+                        {
+                            for (int i = 0 ; i < PORTERDUFF_MODES.length ; i++) {
+                                put(PORTERDUFF_MODES[i].toString(),
+                                        new XfermodeModifier(PORTERDUFF_MODES[i]));
+                            }
+                        }
+                    });
+
+                    put("colorfilters", new LinkedHashMap<String, DisplayModifier>() {
+                        {
+                            for (int i = 0 ; i < PORTERDUFF_MODES.length ; i++) {
+                                put(PORTERDUFF_MODES[i].toString(),
+                                        new ColorFilterModifier(PORTERDUFF_MODES[i]));
+                            }
+                        }
+                    });
+
+                    // FINAL MAP: DOES ACTUAL DRAWING
+                    put("drawing", new LinkedHashMap<String, DisplayModifier>() {
+                        {
+                            put("roundRect", new DisplayModifier() {
+                                @Override
+                                public void modifyDrawing(Paint paint, Canvas canvas) {
+                                    canvas.drawRoundRect(gRect, 20, 20, paint);
+                                }
+                            });
+                            put("rect", new DisplayModifier() {
+                                @Override
+                                public void modifyDrawing(Paint paint, Canvas canvas) {
+                                    canvas.drawRect(gRect, paint);
+                                }
+                            });
+                            put("circle", new DisplayModifier() {
+                                @Override
+                                public void modifyDrawing(Paint paint, Canvas canvas) {
+                                    canvas.drawCircle(100, 100, 75, paint);
+                                }
+                            });
+                            put("oval", new DisplayModifier() {
+                                @Override
+                                public void modifyDrawing(Paint paint, Canvas canvas) {
+                                    canvas.drawOval(gRect, paint);
+                                }
+                            });
+                            put("lines", new DisplayModifier() {
+                                @Override
+                                public void modifyDrawing(Paint paint, Canvas canvas) {
+                                    canvas.drawLines(gLinePts, paint);
+                                }
+                            });
+                            put("plusPoints", new DisplayModifier() {
+                                @Override
+                                public void modifyDrawing(Paint paint, Canvas canvas) {
+                                    canvas.drawPoints(gPts, paint);
+                                }
+                            });
+                            put("text", new DisplayModifier() {
+                                @Override
+                                public void modifyDrawing(Paint paint, Canvas canvas) {
+                                    paint.setTextSize(36);
+                                    canvas.drawText("TEXTTEST", 0, 50, paint);
+                                }
+                            });
+                            put("shadowtext", new DisplayModifier() {
+                                @Override
+                                public void modifyDrawing(Paint paint, Canvas canvas) {
+                                    paint.setTextSize(36);
+                                    paint.setShadowLayer(3.0f, 0.0f, 3.0f, 0xffff00ff);
+                                    canvas.drawText("TEXTTEST", 0, 50, paint);
+                                }
+                            });
+                            put("bitmapMesh", new DisplayModifier() {
+                                @Override
+                                public void modifyDrawing(Paint paint, Canvas canvas) {
+                                    canvas.drawBitmapMesh(ResourceModifier.instance().bitmap, 3, 3,
+                                            ResourceModifier.instance().bitmapVertices, 0, null, 0,
+                                            null);
+                                }
+                            });
+                            put("arc", new DisplayModifier() {
+                                @Override
+                                public void modifyDrawing(Paint paint, Canvas canvas) {
+                                    canvas.drawArc(gRect, 260, 285, false, paint);
+                                }
+                            });
+                            put("arcFromCenter", new DisplayModifier() {
+                                @Override
+                                public void modifyDrawing(Paint paint, Canvas canvas) {
+                                    canvas.drawArc(gRect, 260, 285, true, paint);
+                                }
+                            });
+                        }
+                    });
+                    // WARNING: DON'T PUT MORE MAPS BELOW THIS
+                }
+            };
+
+    abstract public void modifyDrawing(Paint paint, Canvas canvas);
+
+    public static class Accessor {
+        public final static int AA_MASK =               0x1 << 0;
+        public final static int STYLE_MASK =            0x1 << 1;
+        public final static int STROKE_WIDTH_MASK =     0x1 << 2;
+        public final static int STROKE_CAP_MASK =       0x1 << 3;
+        public final static int STROKE_JOIN_MASK =      0x1 << 4;
+        public final static int TRANSFORM_MASK =        0x1 << 5;
+        public final static int SHADER_MASK =           0x1 << 6;
+        public final static int XFERMODE_MASK =         0x1 << 7;
+        public final static int COLOR_FILTER_MASK =     0x1 << 8;
+        public final static int SHAPES_MASK =           0x1 << 9;
+        public final static int ALL_OPTIONS_MASK =      (0x1 << 10) - 1;
+        public final static int SHAPES_INDEX = 9;
+        public final static int XFERMODE_INDEX = 7;
+        private final int mMask;
+
+        private String mDebugString;
+        private int[] mIndices;
+        private LinkedHashMap<String, LinkedHashMap<String, DisplayModifier>> mDisplayMap;
+
+        public Accessor(int mask) {
+            int totalModifiers = Integer.bitCount(mask);
+            mIndices = new int[totalModifiers];
+            mMask = mask;
+            // Create a Display Map of the valid indices
+            mDisplayMap = new LinkedHashMap<String, LinkedHashMap<String, DisplayModifier>>();
+            int index = 0;
+            for (String key : DisplayModifier.sMaps.keySet()) {
+                if (validIndex(index)) {
+                    mDisplayMap.put(key, DisplayModifier.sMaps.get(key));
+                }
+                index++;
+            }
+            mDebugString = "";
+        }
+
+        private LinkedHashMap<String, DisplayModifier> getMapAtIndex(int index) {
+            int i = 0;
+            for (LinkedHashMap<String, DisplayModifier> map : mDisplayMap.values()) {
+                if (i == index) {
+                    return map;
+                }
+                i++;
+            }
+            return null;
+        }
+
+        /**
+         * This will create the next combination of drawing commands. If we have done every combination,
+         * then we will return false.
+         * @return true if there is more combinations to do
+         */
+        public boolean step() {
+            int modifierMapIndex = mIndices.length - 1;
+            // Start from the last map, and loop until it is at the front
+            while (modifierMapIndex >= 0) {
+                LinkedHashMap<String, DisplayModifier> map = getMapAtIndex(modifierMapIndex);
+                mIndices[modifierMapIndex]++;
+
+                // If we are still at a valid index, then we don't need to update any others
+                if (mIndices[modifierMapIndex] < map.size()) {
+                    break;
+                }
+
+                // If we updated and it was outside the boundary, and it was the last index then
+                // we are done
+                if (modifierMapIndex == 0) {
+                    return false;
+                }
+                // If we ran off the end of the map, we need to update one more down the list
+                mIndices[modifierMapIndex] = 0;
+
+                modifierMapIndex--;
+            }
+            return true;
+        }
+
+        /**
+         * Modifies the canvas and paint given for the particular combination currently
+         */
+        public void modifyDrawing(Canvas canvas, Paint paint) {
+            final ArrayList<DisplayModifier> modifierArrayList = getModifierList();
+            for (DisplayModifier modifier : modifierArrayList) {
+                modifier.modifyDrawing(paint, canvas);
+            }
+        }
+
+        /**
+         * Gets a list of all the current modifications to be used.
+         */
+        private ArrayList<DisplayModifier> getModifierList() {
+            ArrayList<DisplayModifier> modifierArrayList = new ArrayList<DisplayModifier>();
+            int mapIndex = 0;
+            mDebugString = "";
+
+            // Through each possible category of modification
+            for (Map.Entry<String, LinkedHashMap<String, DisplayModifier>> entry :
+                    mDisplayMap.entrySet()) {
+                int displayModifierIndex = mIndices[mapIndex];
+                mDebugString += "Modification : " + entry.getKey();
+                // Loop until we find the modification we are going to use
+                for (Map.Entry<String, DisplayModifier> modifierEntry :
+                        entry.getValue().entrySet()) {
+                    // Once we find the modification we want, then we will add it to the list,
+                    // and the last applied modifications
+                    if (displayModifierIndex == 0) {
+                        mDebugString += " value : " + modifierEntry.getKey() + " ";
+                        modifierArrayList.add(modifierEntry.getValue());
+                        break;
+                    }
+                    displayModifierIndex--;
+                }
+                mapIndex++;
+            }
+            return modifierArrayList;
+        }
+
+        public String getDebugString() {
+            return mDebugString;
+        }
+
+        /**
+         * Using the given masks, it tells if the map at the given index should be used, or not.
+         */
+        private boolean validIndex(int index) {
+            return (mMask & (0x1 << index)) != 0;
+        }
+    }
+
+    private static class XfermodeModifier extends DisplayModifier {
+        private Xfermode mXfermode;
+
+        public XfermodeModifier(PorterDuff.Mode mode) {
+            mXfermode = new PorterDuffXfermode(mode);
+        }
+
+        @Override
+        public void modifyDrawing(Paint paint, Canvas canvas) {
+            paint.setXfermode(mXfermode);
+        }
+    }
+
+    private static class ColorFilterModifier extends DisplayModifier {
+        private static final int FILTER_COLOR = 0xFFBB0000;
+        private ColorFilter mColorFilter;
+
+        public ColorFilterModifier(PorterDuff.Mode mode) {
+            mColorFilter = new PorterDuffColorFilter(FILTER_COLOR, mode);
+        }
+
+        @Override
+        public void modifyDrawing(Paint paint, Canvas canvas) {
+            paint.setColorFilter(mColorFilter);
+        }
+    }
+}
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/DrawActivity.java b/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/DrawActivity.java
new file mode 100644
index 0000000..1f12dcb
--- /dev/null
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/DrawActivity.java
@@ -0,0 +1,142 @@
+/*
+ * 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.uirendering.cts.testinfrastructure;
+
+import android.annotation.Nullable;
+import android.app.Activity;
+import android.content.Context;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.ViewTreeObserver;
+import android.webkit.WebView;
+
+/**
+ * A generic activity that uses a view specified by the user.
+ */
+public class DrawActivity extends Activity {
+    private final static long TIME_OUT = 10000;
+    private final Object mLock = new Object();
+    public static final int MIN_NUMBER_OF_DRAWS = 5;
+
+    private Handler mHandler;
+    private View mView;
+
+    public void onCreate(Bundle bundle){
+        super.onCreate(bundle);
+        mHandler = new RenderSpecHandler();
+    }
+
+    @Override
+    public View onCreateView(View parent, String name, Context context, AttributeSet attrs) {
+        mView = parent;
+        return super.onCreateView(parent, name, context, attrs);
+    }
+
+    public void enqueueRenderSpecAndWait(int layoutId, CanvasClient canvasClient, String webViewUrl,
+            @Nullable ViewInitializer viewInitializer, boolean useHardware) {
+        ((RenderSpecHandler) mHandler).setViewInitializer(viewInitializer);
+        int arg2 = (useHardware ? View.LAYER_TYPE_NONE : View.LAYER_TYPE_SOFTWARE);
+        if (canvasClient != null) {
+            mHandler.obtainMessage(RenderSpecHandler.CANVAS_MSG, 0, arg2, canvasClient).sendToTarget();
+        } else if (webViewUrl != null) {
+            mHandler.obtainMessage(RenderSpecHandler.WEB_VIEW_MSG, 0, arg2, webViewUrl).sendToTarget();
+        } else {
+            mHandler.obtainMessage(RenderSpecHandler.LAYOUT_MSG, layoutId, arg2).sendToTarget();
+        }
+
+        synchronized (mLock) {
+            try {
+                mLock.wait(TIME_OUT);
+            } catch (InterruptedException e) {
+                e.printStackTrace();
+            }
+        }
+    }
+
+    private class RenderSpecHandler extends Handler {
+        public static final int LAYOUT_MSG = 1;
+        public static final int CANVAS_MSG = 2;
+        public static final int WEB_VIEW_MSG = 3;
+
+        private ViewInitializer mViewInitializer;
+
+        public void setViewInitializer(ViewInitializer viewInitializer) {
+            mViewInitializer = viewInitializer;
+        }
+
+        public void handleMessage(Message message) {
+            int webViewBuffer = 0;
+            switch (message.what) {
+                case LAYOUT_MSG: {
+                    setContentView(message.arg1);
+                } break;
+
+                case CANVAS_MSG: {
+                    mView = new CanvasClientView(getApplicationContext(),
+                            (CanvasClient) (message.obj), ActivityTestBase.TEST_WIDTH,
+                            ActivityTestBase.TEST_HEIGHT);
+                    setContentView(mView);
+                } break;
+
+                case WEB_VIEW_MSG: {
+                    mView = new WebView(getApplicationContext());
+                    ((WebView) mView).loadUrl((String) message.obj);
+                    ((WebView) mView).setInitialScale(100);
+                    setContentView(mView);
+                    webViewBuffer = 10;
+                } break;
+            }
+
+            if (mViewInitializer != null) {
+                mViewInitializer.intializeView(mView);
+            }
+
+            mView.setLayerType(message.arg2, null);
+
+            DrawCounterListener onDrawListener = new DrawCounterListener(webViewBuffer);
+
+            mView.getViewTreeObserver().addOnPreDrawListener(onDrawListener);
+
+            mView.postInvalidate();
+        }
+    }
+
+    private class DrawCounterListener implements ViewTreeObserver.OnPreDrawListener {
+        private int mCurrentDraws = 0;
+        private int mExtraDraws;
+
+        public DrawCounterListener(int extraDraws) {
+            mExtraDraws = extraDraws;
+        }
+
+        @Override
+        public boolean onPreDraw() {
+            mCurrentDraws++;
+            if (mCurrentDraws < MIN_NUMBER_OF_DRAWS + mExtraDraws) {
+                mView.postInvalidate();
+            } else {
+                synchronized (mLock) {
+                    mLock.notify();
+                }
+                mView.getViewTreeObserver().removeOnPreDrawListener(this);
+            }
+            return true;
+        }
+    }
+}
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/ResourceModifier.java b/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/ResourceModifier.java
new file mode 100644
index 0000000..5564b96
--- /dev/null
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/ResourceModifier.java
@@ -0,0 +1,133 @@
+/*
+  * 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.uirendering.cts.testinfrastructure;
+
+import com.android.cts.uirendering.R;
+
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.BitmapShader;
+import android.graphics.Color;
+import android.graphics.ComposeShader;
+import android.graphics.LinearGradient;
+import android.graphics.Matrix;
+import android.graphics.PorterDuff;
+import android.graphics.RadialGradient;
+import android.graphics.Shader;
+import android.graphics.SweepGradient;
+
+/**
+ * This will contain the resource-based content for the DisplayModifiers
+ */
+public class ResourceModifier {
+    private static ResourceModifier sInstance = null;
+
+    public final BitmapShader repeatShader;
+    public final BitmapShader translatedShader;
+    public final BitmapShader scaledShader;
+    public final LinearGradient horGradient;
+    public final LinearGradient diagGradient;
+    public final LinearGradient vertGradient;
+    public final RadialGradient radGradient;
+    public final SweepGradient sweepGradient;
+    public final ComposeShader composeShader;
+    public final ComposeShader nestedComposeShader;
+    public final ComposeShader doubleGradientComposeShader;
+    public final Bitmap bitmap;
+    public final float[] bitmapVertices;
+    public final int[] bitmapColors;
+
+    public ResourceModifier(Resources resources) {
+        bitmap = BitmapFactory.decodeResource(resources, R.drawable.sunset1);
+        int texWidth = bitmap.getWidth();
+        int texHeight = bitmap.getHeight();
+
+        int drawWidth = ActivityTestBase.TEST_WIDTH;
+        int drawHeight = ActivityTestBase.TEST_HEIGHT;
+
+        repeatShader = new BitmapShader(bitmap, Shader.TileMode.REPEAT,
+                Shader.TileMode.REPEAT);
+
+        translatedShader = new BitmapShader(bitmap, Shader.TileMode.REPEAT,
+                Shader.TileMode.REPEAT);
+        Matrix matrix = new Matrix();
+        matrix.setTranslate(texWidth / 2.0f, texHeight / 2.0f);
+        matrix.postRotate(45, 0, 0);
+        translatedShader.setLocalMatrix(matrix);
+
+        scaledShader = new BitmapShader(bitmap, Shader.TileMode.MIRROR,
+                Shader.TileMode.MIRROR);
+        matrix = new Matrix();
+        matrix.setScale(0.5f, 0.5f);
+        scaledShader.setLocalMatrix(matrix);
+
+        horGradient = new LinearGradient(0.0f, 0.0f, 1.0f, 0.0f,
+                Color.RED, Color.GREEN, Shader.TileMode.CLAMP);
+        matrix = new Matrix();
+        matrix.setScale(drawHeight, 1.0f);
+        matrix.postRotate(-90.0f);
+        matrix.postTranslate(0.0f, drawHeight);
+        horGradient.setLocalMatrix(matrix);
+
+        diagGradient = new LinearGradient(0.0f, 0.0f, drawWidth / 2.0f, drawHeight / 2.0f,
+                Color.BLUE, Color.RED, Shader.TileMode.CLAMP);
+
+        vertGradient = new LinearGradient(0.0f, 0.0f, 0.0f, drawHeight / 2.0f,
+                Color.YELLOW, Color.MAGENTA, Shader.TileMode.MIRROR);
+
+        sweepGradient = new SweepGradient(drawWidth / 2.0f, drawHeight / 2.0f,
+                Color.YELLOW, Color.MAGENTA);
+
+        composeShader = new ComposeShader(repeatShader, horGradient,
+                PorterDuff.Mode.MULTIPLY);
+
+        final float width = bitmap.getWidth() / 8.0f;
+        final float height = bitmap.getHeight() / 8.0f;
+
+        bitmapVertices = new float[]{
+                0.0f, 0.0f, width, 0.0f, width * 2, 0.0f, width * 3, 0.0f,
+                0.0f, height, width, height, width * 2, height, width * 4, height,
+                0.0f, height * 2, width, height * 2, width * 2, height * 2, width * 3, height * 2,
+                0.0f, height * 4, width, height * 4, width * 2, height * 4, width * 4, height * 4,
+        };
+
+        bitmapColors = new int[]{
+                0xffff0000, 0xff00ff00, 0xff0000ff, 0xffff0000,
+                0xff0000ff, 0xffff0000, 0xff00ff00, 0xff00ff00,
+                0xff00ff00, 0xff0000ff, 0xffff0000, 0xff00ff00,
+                0x00ff0000, 0x0000ff00, 0x000000ff, 0x00ff0000,
+        };
+
+        // Use a repeating gradient with many colors to test the non simple case.
+        radGradient = new RadialGradient(drawWidth / 4.0f, drawHeight / 4.0f, 4.0f,
+                bitmapColors, null, Shader.TileMode.REPEAT);
+
+        nestedComposeShader = new ComposeShader(radGradient, composeShader,
+                PorterDuff.Mode.MULTIPLY);
+
+        doubleGradientComposeShader = new ComposeShader(radGradient, vertGradient,
+                PorterDuff.Mode.MULTIPLY);
+    }
+
+    public static ResourceModifier instance() {
+        return sInstance;
+    }
+
+    public static void init(Resources resources) {
+        sInstance = new ResourceModifier(resources);
+    }
+}
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/ViewInitializer.java b/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/ViewInitializer.java
new file mode 100644
index 0000000..8980df3
--- /dev/null
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/ViewInitializer.java
@@ -0,0 +1,25 @@
+/*
+ * 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.uirendering.cts.testinfrastructure;
+
+import android.view.View;
+
+/**
+ * Called after a view is created to set various properties on the view
+ */
+public abstract class ViewInitializer {
+    public abstract void intializeView(View view);
+}
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/util/BitmapDumper.java b/tests/tests/uirendering/src/android/uirendering/cts/util/BitmapDumper.java
new file mode 100644
index 0000000..be680d9
--- /dev/null
+++ b/tests/tests/uirendering/src/android/uirendering/cts/util/BitmapDumper.java
@@ -0,0 +1,119 @@
+/*
+ * 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.uirendering.cts.util;
+
+import android.graphics.Bitmap;
+import android.uirendering.cts.differencevisualizers.DifferenceVisualizer;
+import android.util.Log;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+
+import libcore.io.IoUtils;
+
+/**
+ * A utility class that will allow the user to save bitmaps to the sdcard on the device.
+ */
+public final class BitmapDumper {
+    private final static String TAG = "BitmapDumper";
+    private final static String IDEAL_RENDERING_FILE_NAME = "idealCapture.png";
+    private final static String TESTED_RENDERING_FILE_NAME = "testedCapture.png";
+    private final static String VISUALIZER_RENDERING_FILE_NAME = "visualizer.png";
+    private final static String SINGULAR_FILE_NAME = "capture.png";
+    private final static String CAPTURE_SUB_DIRECTORY = "/sdcard/UiRenderingCaptures/";
+
+    private BitmapDumper() {}
+
+    /**
+     * Deletes the specific files for the given test in a given class.
+     */
+    public static void deleteFileInClassFolder(String className, String testName) {
+        File directory = new File(CAPTURE_SUB_DIRECTORY + className);
+
+        String[] children = directory.list();
+        if (children == null) {
+            return;
+        }
+        for (String file : children) {
+            if (file.startsWith(testName)) {
+                new File(directory, file).delete();
+            }
+        }
+    }
+
+    public static void createSubDirectory(String className) {
+        File saveDirectory = new File(CAPTURE_SUB_DIRECTORY + className);
+        if (saveDirectory.exists()) {
+            return;
+        }
+        // Create the directory if it isn't already created.
+        saveDirectory.mkdirs();
+    }
+
+    /**
+     * Saves two files, one the capture of an ideal drawing, and one the capture of the tested
+     * drawing. The third file saved is a bitmap that is returned from the given visualizer's
+     * method.
+     * The files are saved to the sdcard directory
+     */
+    public static void dumpBitmaps(Bitmap idealBitmap, Bitmap testedBitmap, String testName,
+            String className, DifferenceVisualizer differenceVisualizer) {
+        Bitmap visualizerBitmap;
+
+        int width = idealBitmap.getWidth();
+        int height = idealBitmap.getHeight();
+        int[] testedArray = new int[width * height];
+        int[] idealArray = new int[width * height];
+        idealBitmap.getPixels(testedArray, 0, width, 0, 0, width, height);
+        testedBitmap.getPixels(idealArray, 0, width, 0, 0, width, height);
+        int[] visualizerArray = differenceVisualizer.getDifferences(idealArray, testedArray);
+        visualizerBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
+        visualizerBitmap.setPixels(visualizerArray, 0, width, 0, 0, width, height);
+
+        saveFile(className, testName, IDEAL_RENDERING_FILE_NAME, idealBitmap);
+        saveFile(className, testName, TESTED_RENDERING_FILE_NAME, testedBitmap);
+        saveFile(className, testName, VISUALIZER_RENDERING_FILE_NAME, visualizerBitmap);
+    }
+
+    public static void dumpBitmap(Bitmap bitmap, String testName, String className) {
+        if (bitmap == null) {
+            Log.d(TAG, "File not saved, bitmap was null for test : " + testName);
+            return;
+        }
+        saveFile(className, testName, SINGULAR_FILE_NAME, bitmap);
+    }
+
+    private static void saveFile(String className, String testName, String fileName, Bitmap bitmap) {
+        Log.d(TAG, "Saving file : " + testName + "_" + fileName + " in directory : " + className);
+        File file = new File(CAPTURE_SUB_DIRECTORY + className, testName + "_" + fileName);
+        FileOutputStream fileStream = null;
+        try {
+            fileStream = new FileOutputStream(file);
+            bitmap.compress(Bitmap.CompressFormat.PNG, 85, fileStream);
+            fileStream.flush();
+        } catch (FileNotFoundException e) {
+            e.printStackTrace();
+        } catch (IOException e) {
+            e.printStackTrace();
+        } finally {
+            if (fileStream != null) {
+                IoUtils.closeQuietly(fileStream);
+            }
+        }
+    }
+}
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/util/CompareUtils.java b/tests/tests/uirendering/src/android/uirendering/cts/util/CompareUtils.java
new file mode 100644
index 0000000..d68566d
--- /dev/null
+++ b/tests/tests/uirendering/src/android/uirendering/cts/util/CompareUtils.java
@@ -0,0 +1,15 @@
+package android.uirendering.cts.util;
+
+import android.graphics.Color;
+
+public class CompareUtils {
+    /**
+     * @return True if close enough
+     */
+    public static boolean verifyPixelWithThreshold(int color, int expectedColor, int threshold) {
+        int diff = Math.abs(Color.red(color) - Color.red(expectedColor))
+                + Math.abs(Color.green(color) - Color.green(expectedColor))
+                + Math.abs(Color.blue(color) - Color.blue(expectedColor));
+        return diff < threshold;
+    }
+}
diff --git a/tests/tests/util/Android.mk b/tests/tests/util/Android.mk
index f1c75dc..6ede3fb 100644
--- a/tests/tests/util/Android.mk
+++ b/tests/tests/util/Android.mk
@@ -21,8 +21,6 @@
 # and when built explicitly put it in the data partition
 LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
 
-LOCAL_JAVA_LIBRARIES := android.test.runner
-
 LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
diff --git a/tests/tests/util/AndroidManifest.xml b/tests/tests/util/AndroidManifest.xml
index 3969ac8..ab417bd 100644
--- a/tests/tests/util/AndroidManifest.xml
+++ b/tests/tests/util/AndroidManifest.xml
@@ -23,9 +23,12 @@
         <uses-library android:name="android.test.runner" />
     </application>
 
-    <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
                      android:targetPackage="com.android.cts.stub"
-                     android:label="CTS tests of android.util"/>
+                     android:label="CTS tests of android.util">
+        <meta-data android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
 
 </manifest>
 
diff --git a/tests/tests/util/src/android/util/cts/RangeTest.java b/tests/tests/util/src/android/util/cts/RangeTest.java
new file mode 100644
index 0000000..abab17b
--- /dev/null
+++ b/tests/tests/util/src/android/util/cts/RangeTest.java
@@ -0,0 +1,168 @@
+/*
+ * 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.util.cts;
+
+import android.test.suitebuilder.annotation.SmallTest;
+import android.util.Range;
+import android.util.Rational;
+
+public class RangeTest extends junit.framework.TestCase {
+
+    @SmallTest
+    public void testConstructor() {
+        // Trivial, same range
+        Range<Integer> intRange = new Range<Integer>(1, 1);
+
+        assertLower(intRange, 1);
+        assertUpper(intRange, 1);
+
+        // Different values in range
+        Range<Integer> intRange2 = new Range<Integer>(100, 200);
+        assertLower(intRange2, 100);
+        assertUpper(intRange2, 200);
+
+        Range<Float> floatRange = new Range<Float>(Float.NEGATIVE_INFINITY,
+                Float.POSITIVE_INFINITY);
+        assertLower(floatRange, Float.NEGATIVE_INFINITY);
+        assertUpper(floatRange, Float.POSITIVE_INFINITY);
+    }
+
+    @SmallTest
+    public void testIllegalValues() {
+        // Test NPEs
+        try {
+            new Range<Integer>(null, null);
+            fail("Expected exception to be thrown for (null, null)");
+        } catch (NullPointerException e) {
+            // OK: both args are null
+        }
+
+        try {
+            new Range<Integer>(null, 0);
+            fail("Expected exception to be thrown for (null, 0)");
+        } catch (NullPointerException e) {
+            // OK: left arg is null
+        }
+
+        try {
+            new Range<Integer>(0, null);
+            fail("Expected exception to be thrown for (0, null)");
+        } catch (NullPointerException e) {
+            // OK: right arg is null
+        }
+
+        // Test IAEs
+
+        try {
+            new Range<Integer>(50, -50);
+            fail("Expected exception to be thrown for (50, -50)");
+        } catch (IllegalArgumentException e) {
+            // OK: 50 > -50 so it fails
+        }
+
+        try {
+            new Range<Float>(0.0f, Float.NEGATIVE_INFINITY);
+            fail("Expected exception to be thrown for (0.0f, -Infinity)");
+        } catch (IllegalArgumentException e) {
+            // OK: 0.0f is > NEGATIVE_INFINITY, so it fails
+        }
+    }
+
+    @SmallTest
+    public void testEquals() {
+        Range<Float> oneHalf = Range.create(1.0f, 2.0f);
+        Range<Float> oneHalf2 = new Range<Float>(1.0f, 2.0f);
+        assertEquals(oneHalf, oneHalf2);
+        assertHashCodeEquals(oneHalf, oneHalf2);
+
+        Range<Float> twoThirds = new Range<Float>(2.0f, 3.0f);
+        Range<Float> twoThirds2 = Range.create(2.0f, 3.0f);
+        assertEquals(twoThirds, twoThirds2);
+        assertHashCodeEquals(twoThirds, twoThirds2);
+
+        Range<Rational> negativeOneTenthPositiveOneTenth =
+                new Range<Rational>(new Rational(-1, 10), new Rational(1, 10));
+        Range<Rational> negativeOneTenthPositiveOneTenth2 =
+                Range.create(new Rational(-1, 10), new Rational(1, 10));
+        assertEquals(negativeOneTenthPositiveOneTenth, negativeOneTenthPositiveOneTenth2);
+        assertHashCodeEquals(negativeOneTenthPositiveOneTenth, negativeOneTenthPositiveOneTenth2);
+    }
+
+    @SmallTest
+    public void testInRange() {
+        Range<Integer> hundredOneTwo = Range.create(100, 200);
+
+        assertInRange(hundredOneTwo, 100);
+        assertInRange(hundredOneTwo, 200);
+        assertInRange(hundredOneTwo, 150);
+        assertOutOfRange(hundredOneTwo, 99);
+        assertOutOfRange(hundredOneTwo, 201);
+        assertOutOfRange(hundredOneTwo, 100000);
+
+        Range<Float> infinities = Range.create(Float.NEGATIVE_INFINITY, Float.POSITIVE_INFINITY);
+
+        assertInRange(infinities, Float.NEGATIVE_INFINITY);
+        assertInRange(infinities, Float.POSITIVE_INFINITY);
+        assertInRange(infinities, 0.0f);
+        assertOutOfRange(infinities, Float.NaN);
+
+        Range<Rational> negativeOneTenthPositiveOneTenth =
+                new Range<Rational>(new Rational(-1, 10), new Rational(1, 10));
+        assertInRange(negativeOneTenthPositiveOneTenth, new Rational(-1, 10));
+        assertInRange(negativeOneTenthPositiveOneTenth, new Rational(1, 10));
+        assertInRange(negativeOneTenthPositiveOneTenth, Rational.ZERO);
+        assertOutOfRange(negativeOneTenthPositiveOneTenth, new Rational(-100, 1));
+        assertOutOfRange(negativeOneTenthPositiveOneTenth, new Rational(100, 1));
+    }
+
+    private static <T extends Comparable<? super T>> void assertInRange(Range<T> object, T needle) {
+        assertAction("in-range", object, needle, true, object.contains(needle));
+    }
+
+    private static <T extends Comparable<? super T>> void assertOutOfRange(Range<T> object,
+            T needle) {
+        assertAction("out-of-range", object, needle, false, object.contains(needle));
+    }
+
+    private static <T extends Comparable<? super T>> void assertUpper(Range<T> object, T expected) {
+        assertAction("upper", object, expected, object.getUpper());
+    }
+
+    private static <T extends Comparable<? super T>> void assertLower(Range<T> object, T expected) {
+        assertAction("lower", object, expected, object.getLower());
+    }
+
+    private static <T, T2> void assertAction(String action, T object, T2 expected,
+            T2 actual) {
+        assertEquals("Expected " + object + " " + action + " to be ",
+                expected, actual);
+    }
+
+    private static <T, T2> void assertAction(String action, T object, T2 needle, boolean expected,
+            boolean actual) {
+        String expectedMessage = expected ? action : ("not " + action);
+        assertEquals("Expected " + needle + " to be " + expectedMessage + " of " + object,
+                expected, actual);
+    }
+
+    private static <T extends Comparable<? super T>> void assertHashCodeEquals(
+            Range<T> left, Range<T> right) {
+        assertEquals("Left hash code for " + left +
+                " expected to be equal to right hash code for " + right,
+                left.hashCode(), right.hashCode());
+    }
+}
diff --git a/tests/tests/util/src/android/util/cts/RationalTest.java b/tests/tests/util/src/android/util/cts/RationalTest.java
new file mode 100644
index 0000000..ab5c063
--- /dev/null
+++ b/tests/tests/util/src/android/util/cts/RationalTest.java
@@ -0,0 +1,492 @@
+/*
+ * 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.util.cts;
+
+import android.test.suitebuilder.annotation.SmallTest;
+import android.util.Rational;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InvalidObjectException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.Serializable;
+import java.lang.reflect.Field;
+
+import static android.util.Rational.*;
+
+public class RationalTest extends junit.framework.TestCase {
+
+    /** (1,1) */
+    private static final Rational UNIT = new Rational(1, 1);
+
+    @SmallTest
+    public void testConstructor() {
+
+        // Simple case
+        Rational r = new Rational(1, 2);
+        assertEquals(1, r.getNumerator());
+        assertEquals(2, r.getDenominator());
+
+        // Denominator negative
+        r = new Rational(-1, 2);
+        assertEquals(-1, r.getNumerator());
+        assertEquals(2, r.getDenominator());
+
+        // Numerator negative
+        r = new Rational(1, -2);
+        assertEquals(-1, r.getNumerator());
+        assertEquals(2, r.getDenominator());
+
+        // Both negative
+        r = new Rational(-1, -2);
+        assertEquals(1, r.getNumerator());
+        assertEquals(2, r.getDenominator());
+
+        // Infinity.
+        r = new Rational(1, 0);
+        assertEquals(1, r.getNumerator());
+        assertEquals(0, r.getDenominator());
+
+        // Negative infinity.
+        r = new Rational(-1, 0);
+        assertEquals(-1, r.getNumerator());
+        assertEquals(0, r.getDenominator());
+
+        // NaN.
+        r = new Rational(0, 0);
+        assertEquals(0, r.getNumerator());
+        assertEquals(0, r.getDenominator());
+    }
+
+    @SmallTest
+    public void testEquals() {
+        Rational r = new Rational(1, 2);
+        assertEquals(1, r.getNumerator());
+        assertEquals(2, r.getDenominator());
+
+        assertEquals(r, r);
+        assertFalse(r.equals(null));
+        assertFalse(r.equals(new Object()));
+
+        Rational twoThirds = new Rational(2, 3);
+        assertFalse(r.equals(twoThirds));
+        assertFalse(twoThirds.equals(r));
+
+        Rational fourSixths = new Rational(4, 6);
+        assertEquals(twoThirds, fourSixths);
+        assertEquals(fourSixths, twoThirds);
+
+        Rational moreComplicated = new Rational(5*6*7*8*9, 1*2*3*4*5);
+        Rational moreComplicated2 = new Rational(5*6*7*8*9*78, 1*2*3*4*5*78);
+        assertEquals(moreComplicated, moreComplicated2);
+        assertEquals(moreComplicated2, moreComplicated);
+
+        // Ensure negatives are fine
+        twoThirds = new Rational(-2, 3);
+        fourSixths = new Rational(-4, 6);
+        assertEquals(twoThirds, fourSixths);
+        assertEquals(fourSixths, twoThirds);
+
+        moreComplicated = new Rational(-5*6*7*8*9, 1*2*3*4*5);
+        moreComplicated2 = new Rational(-5*6*7*8*9*78, 1*2*3*4*5*78);
+        assertEquals(moreComplicated, moreComplicated2);
+        assertEquals(moreComplicated2, moreComplicated);
+
+        // Zero is always equal to itself
+        Rational zero2 = new Rational(0, 100);
+        assertEquals(ZERO, zero2);
+        assertEquals(zero2, ZERO);
+
+        // NaN is always equal to itself
+        Rational nan = NaN;
+        Rational nan2 = new Rational(0, 0);
+        assertTrue(nan.equals(nan));
+        assertTrue(nan.equals(nan2));
+        assertTrue(nan2.equals(nan));
+        assertFalse(nan.equals(r));
+        assertFalse(r.equals(nan));
+
+        // Infinities of the same sign are equal.
+        Rational posInf = POSITIVE_INFINITY;
+        Rational posInf2 = new Rational(2, 0);
+        Rational negInf = NEGATIVE_INFINITY;
+        Rational negInf2 = new Rational(-2, 0);
+        assertEquals(posInf, posInf);
+        assertEquals(negInf, negInf);
+        assertEquals(posInf, posInf2);
+        assertEquals(negInf, negInf2);
+
+        // Infinities aren't equal to anything else.
+        assertFalse(posInf.equals(negInf));
+        assertFalse(negInf.equals(posInf));
+        assertFalse(negInf.equals(r));
+        assertFalse(posInf.equals(r));
+        assertFalse(r.equals(negInf));
+        assertFalse(r.equals(posInf));
+        assertFalse(posInf.equals(nan));
+        assertFalse(negInf.equals(nan));
+        assertFalse(nan.equals(posInf));
+        assertFalse(nan.equals(negInf));
+    }
+
+    @SmallTest
+    public void testReduction() {
+        Rational moreComplicated = new Rational(5 * 78, 7 * 78);
+        assertEquals(new Rational(5, 7), moreComplicated);
+        assertEquals(5, moreComplicated.getNumerator());
+        assertEquals(7, moreComplicated.getDenominator());
+
+        Rational posInf = new Rational(5, 0);
+        assertEquals(1, posInf.getNumerator());
+        assertEquals(0, posInf.getDenominator());
+        assertEquals(POSITIVE_INFINITY, posInf);
+
+        Rational negInf = new Rational(-100, 0);
+        assertEquals(-1, negInf.getNumerator());
+        assertEquals(0, negInf.getDenominator());
+        assertEquals(NEGATIVE_INFINITY, negInf);
+
+        Rational zero = new Rational(0, -100);
+        assertEquals(0, zero.getNumerator());
+        assertEquals(1, zero.getDenominator());
+        assertEquals(ZERO, zero);
+
+        Rational flipSigns = new Rational(1, -1);
+        assertEquals(-1, flipSigns.getNumerator());
+        assertEquals(1, flipSigns.getDenominator());
+
+        Rational flipAndReduce = new Rational(100, -200);
+        assertEquals(-1, flipAndReduce.getNumerator());
+        assertEquals(2, flipAndReduce.getDenominator());
+    }
+
+    @SmallTest
+    public void testCompareTo() {
+        // unit is equal to itself
+        assertCompareEquals(UNIT, new Rational(1, 1));
+
+        // NaN is greater than anything but NaN
+        assertCompareEquals(NaN, new Rational(0, 0));
+        assertGreaterThan(NaN, UNIT);
+        assertGreaterThan(NaN, POSITIVE_INFINITY);
+        assertGreaterThan(NaN, NEGATIVE_INFINITY);
+        assertGreaterThan(NaN, ZERO);
+
+        // Positive infinity is greater than any other non-NaN
+        assertCompareEquals(POSITIVE_INFINITY, new Rational(1, 0));
+        assertGreaterThan(POSITIVE_INFINITY, UNIT);
+        assertGreaterThan(POSITIVE_INFINITY, NEGATIVE_INFINITY);
+        assertGreaterThan(POSITIVE_INFINITY, ZERO);
+
+        // Negative infinity is smaller than any other non-NaN
+        assertCompareEquals(NEGATIVE_INFINITY, new Rational(-1, 0));
+        assertLessThan(NEGATIVE_INFINITY, UNIT);
+        assertLessThan(NEGATIVE_INFINITY, POSITIVE_INFINITY);
+        assertLessThan(NEGATIVE_INFINITY, ZERO);
+
+        // A finite number with the same denominator is trivially comparable
+        assertGreaterThan(new Rational(3, 100), new Rational(1, 100));
+        assertGreaterThan(new Rational(3, 100), ZERO);
+
+        // Compare finite numbers with different divisors
+        assertGreaterThan(new Rational(5, 25), new Rational(1, 10));
+        assertGreaterThan(new Rational(5, 25), ZERO);
+
+        // Compare finite numbers with different signs
+        assertGreaterThan(new Rational(5, 25), new Rational(-1, 10));
+        assertLessThan(new Rational(-5, 25), ZERO);
+    }
+
+    @SmallTest
+    public void testConvenienceMethods() {
+        // isFinite
+        assertFinite(ZERO, true);
+        assertFinite(NaN, false);
+        assertFinite(NEGATIVE_INFINITY, false);
+        assertFinite(POSITIVE_INFINITY, false);
+        assertFinite(UNIT, true);
+
+        // isInfinite
+        assertInfinite(ZERO, false);
+        assertInfinite(NaN, false);
+        assertInfinite(NEGATIVE_INFINITY, true);
+        assertInfinite(POSITIVE_INFINITY, true);
+        assertInfinite(UNIT, false);
+
+        // isNaN
+        assertNaN(ZERO, false);
+        assertNaN(NaN, true);
+        assertNaN(NEGATIVE_INFINITY, false);
+        assertNaN(POSITIVE_INFINITY, false);
+        assertNaN(UNIT, false);
+
+        // isZero
+        assertZero(ZERO, true);
+        assertZero(NaN, false);
+        assertZero(NEGATIVE_INFINITY, false);
+        assertZero(POSITIVE_INFINITY, false);
+        assertZero(UNIT, false);
+    }
+
+    @SmallTest
+    public void testValueConversions() {
+        // Unit, simple case
+        assertValueEquals(UNIT, 1.0f);
+        assertValueEquals(UNIT, 1.0);
+        assertValueEquals(UNIT, 1L);
+        assertValueEquals(UNIT, 1);
+        assertValueEquals(UNIT, (short)1);
+
+        // Zero, simple case
+        assertValueEquals(ZERO, 0.0f);
+        assertValueEquals(ZERO, 0.0);
+        assertValueEquals(ZERO, 0L);
+        assertValueEquals(ZERO, 0);
+        assertValueEquals(ZERO, (short)0);
+
+        // NaN is 0 for integers, not-a-number for floating point
+        assertValueEquals(NaN, Float.NaN);
+        assertValueEquals(NaN, Double.NaN);
+        assertValueEquals(NaN, 0L);
+        assertValueEquals(NaN, 0);
+        assertValueEquals(NaN, (short)0);
+
+        // Positive infinity, saturates upwards for integers
+        assertValueEquals(POSITIVE_INFINITY, Float.POSITIVE_INFINITY);
+        assertValueEquals(POSITIVE_INFINITY, Double.POSITIVE_INFINITY);
+        assertValueEquals(POSITIVE_INFINITY, Long.MAX_VALUE);
+        assertValueEquals(POSITIVE_INFINITY, Integer.MAX_VALUE);
+        assertValueEquals(POSITIVE_INFINITY, (short)-1);
+
+        // Negative infinity, saturates downwards for integers
+        assertValueEquals(NEGATIVE_INFINITY, Float.NEGATIVE_INFINITY);
+        assertValueEquals(NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY);
+        assertValueEquals(NEGATIVE_INFINITY, Long.MIN_VALUE);
+        assertValueEquals(NEGATIVE_INFINITY, Integer.MIN_VALUE);
+        assertValueEquals(NEGATIVE_INFINITY, (short)0);
+
+        // Normal finite values, round down for integers
+        final Rational oneQuarter = new Rational(1, 4);
+        assertValueEquals(oneQuarter, 1.0f / 4.0f);
+        assertValueEquals(oneQuarter, 1.0 / 4.0);
+        assertValueEquals(oneQuarter, 0L);
+        assertValueEquals(oneQuarter, 0);
+        assertValueEquals(oneQuarter, (short)0);
+
+        final Rational nineFifths = new Rational(9, 5);
+        assertValueEquals(nineFifths, 9.0f / 5.0f);
+        assertValueEquals(nineFifths, 9.0 / 5.0);
+        assertValueEquals(nineFifths, 1L);
+        assertValueEquals(nineFifths, 1);
+        assertValueEquals(nineFifths, (short)1);
+
+        final Rational negativeHundred = new Rational(-1000, 10);
+        assertValueEquals(negativeHundred, -100.f / 1.f);
+        assertValueEquals(negativeHundred, -100.0 / 1.0);
+        assertValueEquals(negativeHundred, -100L);
+        assertValueEquals(negativeHundred, -100);
+        assertValueEquals(negativeHundred, (short)-100);
+
+        // Short truncates if the result is too large
+        assertValueEquals(new Rational(Integer.MAX_VALUE, 1), (short)Integer.MAX_VALUE);
+        assertValueEquals(new Rational(0x00FFFFFF, 1), (short)0x00FFFFFF);
+        assertValueEquals(new Rational(0x00FF00FF, 1), (short)0x00FF00FF);
+    }
+
+    @SmallTest
+    public void testSerialize() throws ClassNotFoundException, IOException {
+        /*
+         * Check correct [de]serialization
+         */
+        assertEqualsAfterSerializing(ZERO);
+        assertEqualsAfterSerializing(NaN);
+        assertEqualsAfterSerializing(NEGATIVE_INFINITY);
+        assertEqualsAfterSerializing(POSITIVE_INFINITY);
+        assertEqualsAfterSerializing(UNIT);
+        assertEqualsAfterSerializing(new Rational(100, 200));
+        assertEqualsAfterSerializing(new Rational(-100, 200));
+        assertEqualsAfterSerializing(new Rational(5, 1));
+        assertEqualsAfterSerializing(new Rational(Integer.MAX_VALUE, Integer.MIN_VALUE));
+
+        /*
+         * Check bad deserialization fails
+         */
+        try {
+            Rational badZero = createIllegalRational(0, 100); // [0, 100] , should be [0, 1]
+            Rational results = serializeRoundTrip(badZero);
+            fail("Deserializing " + results + " should not have succeeded");
+        } catch (InvalidObjectException e) {
+            // OK
+        }
+
+        try {
+            Rational badPosInfinity = createIllegalRational(100, 0); // [100, 0] , should be [1, 0]
+            Rational results = serializeRoundTrip(badPosInfinity);
+            fail("Deserializing " + results + " should not have succeeded");
+        } catch (InvalidObjectException e) {
+            // OK
+        }
+
+        try {
+            Rational badNegInfinity =
+                    createIllegalRational(-100, 0); // [-100, 0] , should be [-1, 0]
+            Rational results = serializeRoundTrip(badNegInfinity);
+            fail("Deserializing " + results + " should not have succeeded");
+        } catch (InvalidObjectException e) {
+            // OK
+        }
+
+        try {
+            Rational badReduced = createIllegalRational(2, 4); // [2,4] , should be [1, 2]
+            Rational results = serializeRoundTrip(badReduced);
+            fail("Deserializing " + results + " should not have succeeded");
+        } catch (InvalidObjectException e) {
+            // OK
+        }
+
+        try {
+            Rational badReducedNeg = createIllegalRational(-2, 4); // [-2, 4] should be [-1, 2]
+            Rational results = serializeRoundTrip(badReducedNeg);
+            fail("Deserializing " + results + " should not have succeeded");
+        } catch (InvalidObjectException e) {
+            // OK
+        }
+    }
+
+    private static void assertValueEquals(Rational object, float expected) {
+        assertEquals("Checking floatValue() for " + object + ";",
+                expected, object.floatValue());
+    }
+
+    private static void assertValueEquals(Rational object, double expected) {
+        assertEquals("Checking doubleValue() for " + object + ";",
+                expected, object.doubleValue());
+    }
+
+    private static void assertValueEquals(Rational object, long expected) {
+        assertEquals("Checking longValue() for " + object + ";",
+                expected, object.longValue());
+    }
+
+    private static void assertValueEquals(Rational object, int expected) {
+        assertEquals("Checking intValue() for " + object + ";",
+                expected, object.intValue());
+    }
+
+    private static void assertValueEquals(Rational object, short expected) {
+        assertEquals("Checking shortValue() for " + object + ";",
+                expected, object.shortValue());
+    }
+
+    private static void assertFinite(Rational object, boolean expected) {
+        assertAction("finite", object, expected, object.isFinite());
+    }
+
+    private static void assertInfinite(Rational object, boolean expected) {
+        assertAction("infinite", object, expected, object.isInfinite());
+    }
+
+    private static void assertNaN(Rational object, boolean expected) {
+        assertAction("NaN", object, expected, object.isNaN());
+    }
+
+    private static void assertZero(Rational object, boolean expected) {
+        assertAction("zero", object, expected, object.isZero());
+    }
+
+    private static <T> void assertAction(String action, T object, boolean expected,
+            boolean actual) {
+        String expectedMessage = expected ? action : ("not " + action);
+        assertEquals("Expected " + object + " to be " + expectedMessage,
+                expected, actual);
+    }
+
+    private static <T extends Comparable<? super T>> void assertLessThan(T left, T right) {
+        assertTrue("Expected (LR) left " + left + " to be less than right " + right,
+                left.compareTo(right) < 0);
+        assertTrue("Expected (RL) left " + left + " to be less than right " + right,
+                right.compareTo(left) > 0);
+    }
+
+    private static <T extends Comparable<? super T>> void assertGreaterThan(T left, T right) {
+        assertTrue("Expected (LR) left " + left + " to be greater than right " + right,
+                left.compareTo(right) > 0);
+        assertTrue("Expected (RL) left " + left + " to be greater than right " + right,
+                right.compareTo(left) < 0);
+    }
+
+    private static <T extends Comparable<? super T>> void assertCompareEquals(T left, T right) {
+        assertTrue("Expected (LR) left " + left + " to be compareEquals to right " + right,
+                left.compareTo(right) == 0);
+        assertTrue("Expected (RL) left " + left + " to be compareEquals to right " + right,
+                right.compareTo(left) == 0);
+    }
+
+    private static <T extends Serializable> byte[] serialize(T obj) throws IOException {
+        ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
+        try (ObjectOutputStream objectStream = new ObjectOutputStream(byteStream)) {
+            objectStream.writeObject(obj);
+        }
+        return byteStream.toByteArray();
+    }
+
+    private static <T extends Serializable> T deserialize(byte[] array, Class<T> klass)
+            throws IOException, ClassNotFoundException {
+        ByteArrayInputStream bais = new ByteArrayInputStream(array);
+        ObjectInputStream ois = new ObjectInputStream(bais);
+        Object obj = ois.readObject();
+        return klass.cast(obj);
+    }
+
+    @SuppressWarnings("unchecked")
+    private static <T extends Serializable> T serializeRoundTrip(T obj)
+            throws IOException, ClassNotFoundException {
+        Class<T> klass = (Class<T>) obj.getClass();
+        byte[] arr = serialize(obj);
+        T serialized = deserialize(arr, klass);
+        return serialized;
+    }
+
+    private static <T extends Serializable> void assertEqualsAfterSerializing(T obj)
+            throws ClassNotFoundException, IOException {
+        T serialized = serializeRoundTrip(obj);
+        assertEquals("Expected values to be equal after serialization round-trip", obj, serialized);
+    }
+
+    private static Rational createIllegalRational(int numerator, int denominator) {
+        Rational r = new Rational(numerator, denominator);
+        mutateField(r, "mNumerator", numerator);
+        mutateField(r, "mDenominator", denominator);
+        return r;
+    }
+
+    private static <T> void mutateField(T object, String name, int value) {
+        try {
+            Field f = object.getClass().getDeclaredField(name);
+            f.setAccessible(true);
+            f.set(object, value);
+        } catch (NoSuchFieldException e) {
+            throw new AssertionError(e);
+        } catch (IllegalAccessException e) {
+            throw new AssertionError(e);
+        } catch (IllegalArgumentException e) {
+            throw new AssertionError(e);
+        }
+    }
+}
diff --git a/tests/tests/view/AndroidManifest.xml b/tests/tests/view/AndroidManifest.xml
index 233dc44..4cdeab2 100644
--- a/tests/tests/view/AndroidManifest.xml
+++ b/tests/tests/view/AndroidManifest.xml
@@ -23,9 +23,12 @@
         <uses-library android:name="android.test.runner" />
     </application>
 
-    <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
                      android:targetPackage="com.android.cts.stub"
-                     android:label="CTS tests of android.view"/>
+                     android:label="CTS tests of android.view">
+        <meta-data android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
 
 </manifest>
 
diff --git a/tests/tests/view/src/android/view/cts/LayoutInflaterTest.java b/tests/tests/view/src/android/view/cts/LayoutInflaterTest.java
index b33a312..07c6e8c 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,12 @@
 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.text.TextUtils;
 import android.util.AttributeSet;
+import android.util.TypedValue;
 import android.util.Xml;
 import android.view.Gravity;
 import android.view.InflateException;
@@ -42,20 +44,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 +151,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 +311,99 @@
     }
 
     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);
+    }
+
+    public void testInflateTags() {
+        final View view = mLayoutInflater.inflate(
+                com.android.cts.stub.R.layout.inflater_layout_tags, null);
+        assertNotNull(view);
+
+        checkViewTag(view, R.id.viewlayout_root, R.id.tag_viewlayout_root, R.string.tag1);
+        checkViewTag(view, R.id.mock_view, R.id.tag_mock_view, R.string.tag2);
+    }
+
+    private void checkViewTag(View parent, int viewId, int tagId, int valueResId) {
+        final View target = parent.findViewById(viewId);
+        assertNotNull("Found target view for " + viewId, target);
+
+        final Object tag = target.getTag(tagId);
+        assertNotNull("Tag is set", tag);
+        assertTrue("Tag is a character sequence", tag instanceof CharSequence);
+
+        final Context targetContext = target.getContext();
+        final CharSequence expectedValue = targetContext.getString(valueResId);
+        assertEquals(tagId + " has tag " + expectedValue, expectedValue, tag);
+    }
 
     static class MockLayoutInflater extends LayoutInflater {
 
diff --git a/tests/tests/view/src/android/view/cts/ViewTest.java b/tests/tests/view/src/android/view/cts/ViewTest.java
index 95a365f..1c579ab 100644
--- a/tests/tests/view/src/android/view/cts/ViewTest.java
+++ b/tests/tests/view/src/android/view/cts/ViewTest.java
@@ -16,11 +16,16 @@
 
 package android.view.cts;
 
+import android.content.res.ColorStateList;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.ColorFilter;
+import android.graphics.PorterDuff;
+
 import com.android.cts.stub.R;
 import com.android.internal.view.menu.ContextMenuBuilder;
 import com.google.android.collect.Lists;
 
-import android.app.Activity;
 import android.content.Context;
 import android.content.res.Resources;
 import android.content.res.XmlResourceParser;
@@ -54,7 +59,6 @@
 import android.view.SoundEffectConstants;
 import android.view.TouchDelegate;
 import android.view.View;
-import android.view.MotionEvent.PointerProperties;
 import android.view.View.BaseSavedState;
 import android.view.View.OnClickListener;
 import android.view.View.OnCreateContextMenuListener;
@@ -3338,6 +3342,63 @@
         touchListener.reset();
     }
 
+    public void testBackgroundTint() {
+        View inflatedView = mActivity.findViewById(R.id.background_tint);
+
+        assertEquals("Background tint inflated correctly",
+                Color.WHITE, inflatedView.getBackgroundTintList().getDefaultColor());
+        assertEquals("Background tint mode inflated correctly",
+                PorterDuff.Mode.SRC_OVER, inflatedView.getBackgroundTintMode());
+
+        MockDrawable bg = new MockDrawable();
+        View view = new View(mActivity);
+
+        view.setBackground(bg);
+        assertFalse("No background tint applied by default", bg.hasCalledSetTint());
+
+        view.setBackgroundTintList(ColorStateList.valueOf(Color.WHITE));
+        assertTrue("Background tint applied when setBackgroundTints() called after setBackground()",
+                bg.hasCalledSetTint());
+
+        bg.reset();
+        view.setBackground(null);
+        view.setBackground(bg);
+        assertTrue("Background tint applied when setBackgroundTints() called before setBackground()",
+                bg.hasCalledSetTint());
+    }
+
+    private static class MockDrawable extends Drawable {
+        private boolean mCalledSetTint = false;
+
+        @Override
+        public void draw(Canvas canvas) {}
+
+        @Override
+        public void setAlpha(int alpha) {}
+
+        @Override
+        public void setColorFilter(ColorFilter cf) {}
+
+        @Override
+        public int getOpacity() {
+            return 0;
+        }
+
+        @Override
+        public void setTintList(ColorStateList tint) {
+            super.setTintList(tint);
+            mCalledSetTint = true;
+        }
+
+        public boolean hasCalledSetTint() {
+            return mCalledSetTint;
+        }
+
+        public void reset() {
+            mCalledSetTint = false;
+        }
+    }
+
     private static class MockEditText extends EditText {
         private boolean mCalledCheckInputConnectionProxy = false;
         private boolean mCalledOnCreateInputConnection = false;
@@ -3574,6 +3635,39 @@
             View source, int changeType) {
 
         }
+
+        @Override
+        public boolean onStartNestedScroll(View child, View target, int nestedScrollAxes) {
+            return false;
+        }
+
+        @Override
+        public void onNestedScrollAccepted(View child, View target, int nestedScrollAxes) {
+        }
+
+        @Override
+        public void onStopNestedScroll(View target) {
+        }
+
+        @Override
+        public void onNestedScroll(View target, int dxConsumed, int dyConsumed,
+                                   int dxUnconsumed, int dyUnconsumed) {
+        }
+
+        @Override
+        public void onNestedPreScroll(View target, int dx, int dy, int[] consumed) {
+        }
+
+        @Override
+        public boolean onNestedFling(View target, float velocityX, float velocityY,
+                boolean consumed) {
+            return false;
+        }
+
+        @Override
+        public boolean onNestedPreFling(View target, float velocityX, float velocityY) {
+            return false;
+        }
     }
 
     private final class OnCreateContextMenuListenerImpl implements OnCreateContextMenuListener {
diff --git a/tests/tests/view/src/android/view/cts/WindowTest.java b/tests/tests/view/src/android/view/cts/WindowTest.java
index fb670a9..5593795 100644
--- a/tests/tests/view/src/android/view/cts/WindowTest.java
+++ b/tests/tests/view/src/android/view/cts/WindowTest.java
@@ -997,6 +997,24 @@
         @Override
         public void takeInputQueue(InputQueue.Callback callback) {
         }
+
+        @Override
+        public void setStatusBarColor(int color) {
+        }
+
+        @Override
+        public int getStatusBarColor() {
+            return 0;
+        }
+
+        @Override
+        public void setNavigationBarColor(int color) {
+        }
+
+        @Override
+        public int getNavigationBarColor() {
+            return 0;
+        }
     }
 
     private class MockWindowCallback implements Window.Callback {
diff --git a/tests/tests/view/src/android/view/inputmethod/cts/InputConnectionWrapperTest.java b/tests/tests/view/src/android/view/inputmethod/cts/InputConnectionWrapperTest.java
index 17147cf..8cc6919 100644
--- a/tests/tests/view/src/android/view/inputmethod/cts/InputConnectionWrapperTest.java
+++ b/tests/tests/view/src/android/view/inputmethod/cts/InputConnectionWrapperTest.java
@@ -23,6 +23,7 @@
 import android.view.KeyEvent;
 import android.view.inputmethod.CompletionInfo;
 import android.view.inputmethod.CorrectionInfo;
+import android.view.inputmethod.CursorAnchorInfoRequest;
 import android.view.inputmethod.EditorInfo;
 import android.view.inputmethod.ExtractedText;
 import android.view.inputmethod.ExtractedTextRequest;
@@ -84,6 +85,10 @@
         assertTrue(inputConnection.isGetSelectedTextCalled);
         wrapper.setComposingRegion(0, 3);
         assertTrue(inputConnection.isSetComposingRegionCalled);
+        wrapper.requestCursorAnchorInfo(new CursorAnchorInfoRequest(
+                CursorAnchorInfoRequest.TYPE_CURSOR_ANCHOR_INFO,
+                CursorAnchorInfoRequest.FLAG_CURSOR_ANCHOR_INFO_MONITOR));
+        assertTrue(inputConnection.isRequestCursorAnchorInfoCalled);
     }
 
     private class MockInputConnection implements InputConnection {
@@ -108,6 +113,7 @@
         public boolean isSetComposingTextCalled;
         public boolean isSetComposingRegionCalled;
         public boolean isSetSelectionCalled;
+        public boolean isRequestCursorAnchorInfoCalled;
 
         public boolean beginBatchEdit() {
             isBeginBatchEditCalled = true;
@@ -213,5 +219,10 @@
             isSetSelectionCalled = true;
             return false;
         }
+
+        public int requestCursorAnchorInfo(CursorAnchorInfoRequest request) {
+            isRequestCursorAnchorInfoCalled = true;
+            return CursorAnchorInfoRequest.RESULT_NOT_HANDLED;
+        }
     }
 }
diff --git a/tests/tests/view/src/android/view/inputmethod/cts/InputMethodInfoTest.java b/tests/tests/view/src/android/view/inputmethod/cts/InputMethodInfoTest.java
index 12ce833..1a067b4 100644
--- a/tests/tests/view/src/android/view/inputmethod/cts/InputMethodInfoTest.java
+++ b/tests/tests/view/src/android/view/inputmethod/cts/InputMethodInfoTest.java
@@ -164,6 +164,7 @@
         mInputMethodInfo.writeToParcel(p, 0);
         p.setDataPosition(0);
         final InputMethodInfo imi = InputMethodInfo.CREATOR.createFromParcel(p);
+        p.recycle();
 
         assertEquals(mInputMethodInfo.getPackageName(), imi.getPackageName());
         assertEquals(mInputMethodInfo.getServiceName(), imi.getServiceName());
@@ -178,6 +179,7 @@
         mInputMethodSubtype.writeToParcel(p, 0);
         p.setDataPosition(0);
         final InputMethodSubtype subtype = InputMethodSubtype.CREATOR.createFromParcel(p);
+        p.recycle();
 
         assertEquals(mInputMethodSubtype.containsExtraValueKey(mSubtypeExtraValue_key),
                 subtype.containsExtraValueKey(mSubtypeExtraValue_key));
diff --git a/tests/tests/webkit/AndroidManifest.xml b/tests/tests/webkit/AndroidManifest.xml
index f4424a8..776f695 100644
--- a/tests/tests/webkit/AndroidManifest.xml
+++ b/tests/tests/webkit/AndroidManifest.xml
@@ -28,9 +28,12 @@
         <uses-library android:name="android.test.runner" />
     </application>
 
-    <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
                      android:targetPackage="com.android.cts.stub"
-                     android:label="CTS tests of android.webkit"/>
+                     android:label="CTS tests of android.webkit">
+        <meta-data android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
 
 </manifest>
 
diff --git a/tests/tests/webkit/src/android/webkit/cts/CookieManagerTest.java b/tests/tests/webkit/src/android/webkit/cts/CookieManagerTest.java
index ead235e..6de18ed 100644
--- a/tests/tests/webkit/src/android/webkit/cts/CookieManagerTest.java
+++ b/tests/tests/webkit/src/android/webkit/cts/CookieManagerTest.java
@@ -21,7 +21,11 @@
 import android.webkit.CookieManager;
 import android.webkit.CookieSyncManager;
 import android.webkit.WebView;
+import android.webkit.ValueCallback;
 
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
 
 import java.util.Date;
 import java.util.regex.Matcher;
@@ -32,8 +36,9 @@
 
     private static final int TEST_TIMEOUT = 5000;
 
-    private WebViewOnUiThread mOnUiThread;
+    private WebView mWebView;
     private CookieManager mCookieManager;
+    private WebViewOnUiThread mOnUiThread;
 
     public CookieManagerTest() {
         super("com.android.cts.stub", CookieSyncManagerStubActivity.class);
@@ -42,12 +47,20 @@
     @Override
     protected void setUp() throws Exception {
         super.setUp();
-        WebView webview = getActivity().getWebView();
-        if (webview != null) {
-            mOnUiThread = new WebViewOnUiThread(this, webview);
+        mWebView = getActivity().getWebView();
+        if (mWebView != null) {
+            mOnUiThread = new WebViewOnUiThread(this, mWebView);
 
             mCookieManager = CookieManager.getInstance();
             assertNotNull(mCookieManager);
+
+            // We start with no cookies.
+            mCookieManager.removeAllCookie();
+            assertFalse(mCookieManager.hasCookies());
+
+            // But accepting cookies.
+            mCookieManager.setAcceptCookie(false);
+            assertFalse(mCookieManager.acceptCookie());
         }
     }
 
@@ -68,14 +81,20 @@
         }
     }
 
+    public void testFlush() {
+        if (!NullWebViewUtils.isWebViewAvailable()) {
+            return;
+        }
+        mCookieManager.flush();
+    }
+
     public void testAcceptCookie() throws Exception {
         if (!NullWebViewUtils.isWebViewAvailable()) {
             return;
         }
-        mCookieManager.removeAllCookie();
+
         mCookieManager.setAcceptCookie(false);
         assertFalse(mCookieManager.acceptCookie());
-        assertFalse(mCookieManager.hasCookies());
 
         CtsTestServer server = new CtsTestServer(getActivity(), false);
         String url = server.getCookieUrl("conquest.html");
@@ -119,115 +138,245 @@
         m = pat.matcher(cookie);
         assertTrue(m.matches());
         assertEquals("42", m.group(1)); // value got incremented
-
-        // clean up all cookies
-        mCookieManager.removeAllCookie();
     }
 
-    public void testCookieManager() {
+    public void testSetCookie() {
         if (!NullWebViewUtils.isWebViewAvailable()) {
             return;
         }
-        // enable cookie
-        mCookieManager.setAcceptCookie(true);
-        assertTrue(mCookieManager.acceptCookie());
-
-        // first there should be no cookie stored
-        assertFalse(mCookieManager.hasCookies());
 
         String url = "http://www.example.com";
         String cookie = "name=test";
         mCookieManager.setCookie(url, cookie);
         assertEquals(cookie, mCookieManager.getCookie(url));
+        assertTrue(mCookieManager.hasCookies());
+    }
 
-        // sync cookie from RAM to FLASH, because hasCookies() only counts FLASH cookies
-        CookieSyncManager.getInstance().sync();
+    public void testSetCookieNullCallback() {
+        if (!NullWebViewUtils.isWebViewAvailable()) {
+            return;
+        }
+
+        final String url = "http://www.example.com";
+        final String cookie = "name=test";
+        mCookieManager.setCookie(url, cookie, null);
         new PollingCheck(TEST_TIMEOUT) {
             @Override
             protected boolean check() {
-                return mCookieManager.hasCookies();
+                String c = mCookieManager.getCookie(url);
+                return mCookieManager.getCookie(url).contains(cookie);
+            }
+        }.run();
+    }
+
+    public void testSetCookieCallback() throws Throwable {
+        if (!NullWebViewUtils.isWebViewAvailable()) {
+            return;
+        }
+
+        final Semaphore s = new Semaphore(0);
+        final AtomicBoolean status = new AtomicBoolean();
+        final ValueCallback<Boolean> callback = new ValueCallback<Boolean>() {
+            @Override
+            public void onReceiveValue(Boolean success) {
+                status.set(success);
+                s.release();
+            }
+        };
+    }
+
+    public void testRemoveCookies() throws InterruptedException {
+        if (!NullWebViewUtils.isWebViewAvailable()) {
+            return;
+        }
+
+        final String url = "http://www.example.com";
+        final String sessionCookie = "cookie1=peter";
+        final String longCookie = "cookie2=sue";
+        final String quickCookie = "cookie3=marc";
+
+        mCookieManager.setCookie(url, sessionCookie);
+        mCookieManager.setCookie(url, makeExpiringCookie(longCookie, 600));
+        mCookieManager.setCookie(url, makeExpiringCookieMs(quickCookie, 1500));
+
+        String allCookies = mCookieManager.getCookie(url);
+        assertTrue(allCookies.contains(sessionCookie));
+        assertTrue(allCookies.contains(longCookie));
+        assertTrue(allCookies.contains(quickCookie));
+
+        mCookieManager.removeSessionCookie();
+        allCookies = mCookieManager.getCookie(url);
+        assertFalse(allCookies.contains(sessionCookie));
+        assertTrue(allCookies.contains(longCookie));
+        assertTrue(allCookies.contains(quickCookie));
+
+        Thread.sleep(2000); // wait for quick cookie to expire
+        mCookieManager.removeExpiredCookie();
+        allCookies = mCookieManager.getCookie(url);
+        assertFalse(allCookies.contains(sessionCookie));
+        assertTrue(allCookies.contains(longCookie));
+        assertFalse(allCookies.contains(quickCookie));
+
+        mCookieManager.removeAllCookie();
+        assertNull(mCookieManager.getCookie(url));
+        assertFalse(mCookieManager.hasCookies());
+    }
+
+    public void testRemoveCookiesNullCallback() throws InterruptedException {
+        if (!NullWebViewUtils.isWebViewAvailable()) {
+            return;
+        }
+
+        final String url = "http://www.example.com";
+        final String sessionCookie = "cookie1=peter";
+        final String longCookie = "cookie2=sue";
+        final String quickCookie = "cookie3=marc";
+
+        mCookieManager.setCookie(url, sessionCookie);
+        mCookieManager.setCookie(url, makeExpiringCookie(longCookie, 600));
+        mCookieManager.setCookie(url, makeExpiringCookieMs(quickCookie, 1500));
+
+        String allCookies = mCookieManager.getCookie(url);
+        assertTrue(allCookies.contains(sessionCookie));
+        assertTrue(allCookies.contains(longCookie));
+        assertTrue(allCookies.contains(quickCookie));
+
+        mCookieManager.removeSessionCookies(null);
+        allCookies = mCookieManager.getCookie(url);
+        new PollingCheck(TEST_TIMEOUT) {
+            @Override
+            protected boolean check() {
+                String c = mCookieManager.getCookie(url);
+                return !c.contains(sessionCookie) &&
+                        c.contains(longCookie) &&
+                        c.contains(quickCookie);
             }
         }.run();
 
-        // clean up all cookies
-        mCookieManager.removeAllCookie();
+        mCookieManager.removeAllCookies(null);
         new PollingCheck(TEST_TIMEOUT) {
             @Override
             protected boolean check() {
                 return !mCookieManager.hasCookies();
             }
         }.run();
+        assertNull(mCookieManager.getCookie(url));
     }
 
-    @SuppressWarnings("deprecation")
-    public void testRemoveCookies() throws InterruptedException {
+    public void testRemoveCookiesCallback() throws InterruptedException {
         if (!NullWebViewUtils.isWebViewAvailable()) {
             return;
         }
-        // enable cookie
-        mCookieManager.setAcceptCookie(true);
-        assertTrue(mCookieManager.acceptCookie());
-        assertFalse(mCookieManager.hasCookies());
+
+        final Semaphore s = new Semaphore(0);
+        final AtomicBoolean anyDeleted = new AtomicBoolean();
+        final ValueCallback<Boolean> callback = new ValueCallback<Boolean>() {
+            @Override
+            public void onReceiveValue(Boolean n) {
+                anyDeleted.set(n);
+                s.release();
+            }
+        };
 
         final String url = "http://www.example.com";
-        final String cookie1 = "cookie1=peter";
-        final String cookie2 = "cookie2=sue";
-        final String cookie3 = "cookie3=marc";
+        final String sessionCookie = "cookie1=peter";
+        final String normalCookie = "cookie2=sue";
 
-        mCookieManager.setCookie(url, cookie1); // session cookie
-
-        Date date = new Date();
-        date.setTime(date.getTime() + 1000 * 600);
-        String value2 = cookie2 + "; expires=" + date.toGMTString();
-        mCookieManager.setCookie(url, value2); // expires in 10min
-
-        long expiration = 3000;
-        date = new Date();
-        date.setTime(date.getTime() + expiration);
-        String value3 = cookie3 + "; expires=" + date.toGMTString();
-        mCookieManager.setCookie(url, value3); // expires in 3s
+        // We set one session cookie and one normal cookie.
+        mCookieManager.setCookie(url, sessionCookie);
+        mCookieManager.setCookie(url, makeExpiringCookie(normalCookie, 600));
 
         String allCookies = mCookieManager.getCookie(url);
-        assertTrue(allCookies.contains(cookie1));
-        assertTrue(allCookies.contains(cookie2));
-        assertTrue(allCookies.contains(cookie3));
+        assertTrue(allCookies.contains(sessionCookie));
+        assertTrue(allCookies.contains(normalCookie));
 
-        mCookieManager.removeSessionCookie();
-        new PollingCheck(TEST_TIMEOUT) {
-            @Override
-            protected boolean check() {
-                String c = mCookieManager.getCookie(url);
-                return !c.contains(cookie1) && c.contains(cookie2) && c.contains(cookie3);
-            }
-        }.run();
+        // When we remove session cookies there are some to remove.
+        removeSessionCookiesOnUiThread(callback);
+        assertTrue(s.tryAcquire(TEST_TIMEOUT, TimeUnit.MILLISECONDS));
+        assertTrue(anyDeleted.get());
 
-        Thread.sleep(expiration + 1000); // wait for cookie to expire
-        mCookieManager.removeExpiredCookie();
-        new PollingCheck(TEST_TIMEOUT) {
-            @Override
-            protected boolean check() {
-                String c = mCookieManager.getCookie(url);
-                return !c.contains(cookie1) && c.contains(cookie2) && !c.contains(cookie3);
-            }
-        }.run();
+        // The normal cookie is not removed.
+        assertTrue(mCookieManager.getCookie(url).contains(normalCookie));
 
-        mCookieManager.removeAllCookie();
-        new PollingCheck(TEST_TIMEOUT) {
-            @Override
-            protected boolean check() {
-                return mCookieManager.getCookie(url) == null;
-            }
-        }.run();
+        // When we remove session cookies again there are none to remove.
+        removeSessionCookiesOnUiThread(callback);
+        assertTrue(s.tryAcquire(TEST_TIMEOUT, TimeUnit.MILLISECONDS));
+        assertFalse(anyDeleted.get());
+
+        // When we remove all cookies there are some to remove.
+        removeAllCookiesOnUiThread(callback);
+        assertTrue(s.tryAcquire(TEST_TIMEOUT, TimeUnit.MILLISECONDS));
+        assertTrue(anyDeleted.get());
+
+        // Now we have no cookies.
+        assertFalse(mCookieManager.hasCookies());
+        assertNull(mCookieManager.getCookie(url));
+
+        // When we remove all cookies again there are none to remove.
+        removeAllCookiesOnUiThread(callback);
+        assertTrue(s.tryAcquire(TEST_TIMEOUT, TimeUnit.MILLISECONDS));
+        assertFalse(anyDeleted.get());
     }
 
-    private void waitForCookie(final String url) {
-        new PollingCheck(TEST_TIMEOUT) {
-            @Override
-            protected boolean check() {
-                return mCookieManager.getCookie(url) != null;
-            }
-        }.run();
+    /*
+    TODO: uncomment when acceptThirdPartyCookies implementation lands
+    public void testThirdPartyCookie() throws Throwable {
+        if (!NullWebViewUtils.isWebViewAvailable()) {
+            return;
+        }
+        CtsTestServer server = null;
+        try {
+            // In theory we need two servers to test this, one server ('the first party')
+            // which returns a response with a link to a second server ('the third party')
+            // at different origin. This second server attempts to set a cookie which should
+            // fail if AcceptThirdPartyCookie() is false.
+            // Strictly according to the letter of RFC6454 it should be possible to set this
+            // situation up with two TestServers on different ports (these count as having
+            // different origins) but Chrome is not strict about this and does not check the
+            // port. Instead we cheat making some of the urls come from localhost and some
+            // from 127.0.0.1 which count (both in theory and pratice) as having different
+            // origins.
+            server = new CtsTestServer(getActivity());
+
+            // Turn on Javascript (otherwise <script> aren't fetched spoiling the test).
+            mOnUiThread.getSettings().setJavaScriptEnabled(true);
+
+            // Turn global allow on.
+            mCookieManager.setAcceptCookie(true);
+            assertTrue(mCookieManager.acceptCookie());
+
+            // When third party cookies are disabled...
+            mOnUiThread.setAcceptThirdPartyCookies(false);
+            assertFalse(mOnUiThread.acceptThirdPartyCookies());
+
+            // ...we can't set third party cookies.
+            // First on the third party server we get a url which tries to set a cookie.
+            String cookieUrl = toThirdPartyUrl(
+                    server.getSetCookieUrl("cookie_1.js", "test1", "value1"));
+            // Then we create a url on the first party server which links to the first url.
+            String url = server.getLinkedScriptUrl("/content_1.html", cookieUrl);
+            mOnUiThread.loadUrlAndWaitForCompletion(url);
+            assertNull(mCookieManager.getCookie(cookieUrl));
+
+            // When third party cookies are enabled...
+            mOnUiThread.setAcceptThirdPartyCookies(true);
+            assertTrue(mOnUiThread.acceptThirdPartyCookies());
+
+            // ...we can set third party cookies.
+            cookieUrl = toThirdPartyUrl(
+                    server.getSetCookieUrl("/cookie_2.js", "test2", "value2"));
+            url = server.getLinkedScriptUrl("/content_2.html", cookieUrl);
+            mOnUiThread.loadUrlAndWaitForCompletion(url);
+            waitForCookie(cookieUrl);
+            String cookie = mCookieManager.getCookie(cookieUrl);
+            assertNotNull(cookie);
+            assertTrue(cookie.contains("test2"));
+        } finally {
+            if (server != null) server.shutdown();
+            mOnUiThread.getSettings().setJavaScriptEnabled(false);
+        }
     }
+    */
 
     public void testb3167208() throws Exception {
         if (!NullWebViewUtils.isWebViewAvailable()) {
@@ -241,4 +390,70 @@
         assertNotNull(cookie);
         assertTrue(cookie.contains("foo=bar"));
     }
-}
+
+    private void waitForCookie(final String url) {
+        new PollingCheck(TEST_TIMEOUT) {
+            @Override
+            protected boolean check() {
+                return mCookieManager.getCookie(url) != null;
+            }
+        }.run();
+    }
+
+    @SuppressWarnings("deprecation")
+    private String makeExpiringCookie(String cookie, int secondsTillExpiry) {
+        return makeExpiringCookieMs(cookie, 1000*secondsTillExpiry);
+    }
+
+    @SuppressWarnings("deprecation")
+    private String makeExpiringCookieMs(String cookie, int millisecondsTillExpiry) {
+        Date date = new Date();
+        date.setTime(date.getTime() + millisecondsTillExpiry);
+        return cookie + "; expires=" + date.toGMTString();
+    }
+
+    private void removeAllCookiesOnUiThread(final ValueCallback<Boolean> callback) {
+        runTestOnUiThreadAndCatch(new Runnable() {
+            @Override
+            public void run() {
+                mCookieManager.removeAllCookies(callback);
+            }
+        });
+    }
+
+    private void removeSessionCookiesOnUiThread(final ValueCallback<Boolean> callback) {
+        runTestOnUiThreadAndCatch(new Runnable() {
+            @Override
+            public void run() {
+                mCookieManager.removeSessionCookies(callback);
+            }
+        });
+    }
+
+    private void setCookieOnUiThread(final String url, final String cookie,
+            final ValueCallback<Boolean> callback) {
+        runTestOnUiThreadAndCatch(new Runnable() {
+            @Override
+            public void run() {
+                mCookieManager.setCookie(url, cookie, callback);
+            }
+        });
+    }
+
+    private void runTestOnUiThreadAndCatch(Runnable runnable) {
+        try {
+            runTestOnUiThread(runnable);
+        } catch (Throwable t) {
+            fail("Unexpected error while running on UI thread: " + t.getMessage());
+        }
+    }
+
+    /**
+     * Makes a url look as if it comes from a different host.
+     * @param url the url to fake.
+     * @return the resulting url after faking.
+     */
+    public String toThirdPartyUrl(String url) {
+        return url.replace("localhost", "127.0.0.1");
+    }
+ }
diff --git a/tests/tests/webkit/src/android/webkit/cts/CookieSyncManagerTest.java b/tests/tests/webkit/src/android/webkit/cts/CookieSyncManagerTest.java
deleted file mode 100644
index 95e3add..0000000
--- a/tests/tests/webkit/src/android/webkit/cts/CookieSyncManagerTest.java
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.webkit.cts;
-
-
-import android.content.Context;
-import android.cts.util.PollingCheck;
-import android.test.ActivityInstrumentationTestCase2;
-import android.webkit.CookieManager;
-import android.webkit.CookieSyncManager;
-
-public class CookieSyncManagerTest
-        extends ActivityInstrumentationTestCase2<CookieSyncManagerStubActivity> {
-
-    private final static int COOKIE_MANAGER_TIMEOUT = 5000;
-
-    public CookieSyncManagerTest() {
-        super("com.android.cts.stub", CookieSyncManagerStubActivity.class);
-    }
-
-    public void testCookieSyncManager() throws Exception {
-        if (getActivity().getWebView() == null) {
-            return;
-        }
-        CookieSyncManager csm1 = CookieSyncManager.createInstance(getActivity());
-        assertNotNull(csm1);
-
-        CookieSyncManager csm2 = CookieSyncManager.getInstance();
-        assertNotNull(csm2);
-
-        assertSame(csm1, csm2);
-
-        final CookieManager cookieManager = CookieManager.getInstance();
-
-        // Remove all cookies from the database.
-        cookieManager.removeAllCookie();
-        new PollingCheck(COOKIE_MANAGER_TIMEOUT) {
-            @Override
-            protected boolean check() {
-                return !cookieManager.hasCookies();
-            }
-        }.run();
-
-        cookieManager.setAcceptCookie(true);
-        assertTrue(cookieManager.acceptCookie());
-
-        CtsTestServer server = new CtsTestServer(getActivity(), false);
-        String url = server.getCookieUrl("conquest.html");
-        String cookieValue = "a=b";
-        cookieManager.setCookie(url, cookieValue);
-        assertEquals(cookieValue, cookieManager.getCookie(url));
-
-        // Store the cookie to the database.
-        csm1.sync();
-        new PollingCheck(COOKIE_MANAGER_TIMEOUT) {
-            @Override
-            protected boolean check() {
-                return cookieManager.hasCookies();
-            }
-        }.run();
-
-        // Remove all cookies from the database.
-        cookieManager.removeAllCookie();
-        new PollingCheck(COOKIE_MANAGER_TIMEOUT) {
-            @Override
-            protected boolean check() {
-                return !cookieManager.hasCookies();
-            }
-        }.run();
-    }
-}
diff --git a/tests/tests/webkit/src/android/webkit/cts/TestHtmlConstants.java b/tests/tests/webkit/src/android/webkit/cts/TestHtmlConstants.java
index 11cc1a5..30b8210 100644
--- a/tests/tests/webkit/src/android/webkit/cts/TestHtmlConstants.java
+++ b/tests/tests/webkit/src/android/webkit/cts/TestHtmlConstants.java
@@ -50,6 +50,7 @@
     public static final String HTML_URL1 = "webkit/test_firstPage.html";
     public static final String HTML_URL2 = "webkit/test_secondPage.html";
     public static final String HTML_URL3 = "webkit/test_thirdPage.html";
+    public static final String HTML_URL1_TITLE = "First page";
 
     public static final String BLANK_PAGE_URL = "webkit/test_blankPage.html";
     public static final String ADD_JAVA_SCRIPT_INTERFACE_URL = "webkit/test_jsInterface.html";
@@ -65,6 +66,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 3142298..fca14e2 100644
--- a/tests/tests/webkit/src/android/webkit/cts/WebChromeClientTest.java
+++ b/tests/tests/webkit/src/android/webkit/cts/WebChromeClientTest.java
@@ -126,6 +126,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);
@@ -136,6 +137,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 7872dbe..5907d2f 100644
--- a/tests/tests/webkit/src/android/webkit/cts/WebViewClientTest.java
+++ b/tests/tests/webkit/src/android/webkit/cts/WebViewClientTest.java
@@ -92,11 +92,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 {
         if (!NullWebViewUtils.isWebViewAvailable()) {
             return;
@@ -110,12 +111,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);
@@ -134,13 +137,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();
@@ -336,6 +356,7 @@
         private boolean mOnReceivedHttpAuthRequestCalled;
         private boolean mOnUnhandledKeyEventCalled;
         private boolean mOnScaleChangedCalled;
+        private int mShouldOverrideUrlLoadingCallCount;
         private String mLastShouldOverrideUrl;
 
         public MockWebViewClient() {
@@ -378,6 +399,10 @@
             return mOnScaleChangedCalled;
         }
 
+        public int getShouldOverrideUrlLoadingCallCount() {
+            return mShouldOverrideUrlLoadingCallCount;
+        }
+
         public String getLastShouldOverrideUrl() {
             return mLastShouldOverrideUrl;
         }
@@ -444,6 +469,7 @@
         @Override
         public boolean shouldOverrideUrlLoading(WebView view, String url) {
             mLastShouldOverrideUrl = url;
+            mShouldOverrideUrlLoadingCallCount++;
             return false;
         }
     }
diff --git a/tests/tests/webkit/src/android/webkit/cts/WebViewSslTest.java b/tests/tests/webkit/src/android/webkit/cts/WebViewSslTest.java
new file mode 100644
index 0000000..6798f89
--- /dev/null
+++ b/tests/tests/webkit/src/android/webkit/cts/WebViewSslTest.java
@@ -0,0 +1,966 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.webkit.cts;
+
+import android.cts.util.PollingCheck;
+import android.net.Uri;
+import android.net.http.SslCertificate;
+import android.net.http.SslError;
+import android.os.StrictMode;
+import android.os.StrictMode.ThreadPolicy;
+import android.test.ActivityInstrumentationTestCase2;
+import android.test.UiThreadTest;
+import android.util.Log;
+import android.webkit.ClientCertRequest;
+import android.webkit.SslErrorHandler;
+import android.webkit.ValueCallback;
+import android.webkit.WebSettings;
+import android.webkit.WebView;
+import android.webkit.WebViewClient;
+import android.webkit.cts.WebViewOnUiThread.WaitForLoadedClient;
+
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.security.KeyFactory;
+import java.security.PrivateKey;
+import java.security.Principal;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
+import java.security.spec.PKCS8EncodedKeySpec;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.Callable;
+
+import javax.net.ssl.X509TrustManager;
+
+public class WebViewSslTest extends ActivityInstrumentationTestCase2<WebViewStubActivity> {
+    private static final String LOGTAG = "WebViewSslTest";
+
+    /**
+     * Taken verbatim from AndroidKeyStoreTest.java. Copying the build notes here for reference.
+     * The keys and certificates below are generated with:
+     *
+     * openssl req -new -x509 -days 3650 -extensions v3_ca -keyout cakey.pem -out cacert.pem
+     * openssl req -newkey rsa:1024 -keyout userkey.pem -nodes -days 3650 -out userkey.req
+     * mkdir -p demoCA/newcerts
+     * touch demoCA/index.txt
+     * echo "01" > demoCA/serial
+     * openssl ca -out usercert.pem -in userkey.req -cert cacert.pem -keyfile cakey.pem -days 3650
+     */
+
+    /**
+     * Generated from above and converted with:
+     *
+     * openssl x509 -outform d -in usercert.pem | xxd -i | sed 's/0x/(byte) 0x/g'
+     */
+    private static final byte[] FAKE_RSA_USER_1 = new byte[] {
+            (byte) 0x30, (byte) 0x82, (byte) 0x02, (byte) 0x95, (byte) 0x30, (byte) 0x82,
+            (byte) 0x01, (byte) 0xfe, (byte) 0xa0, (byte) 0x03, (byte) 0x02, (byte) 0x01,
+            (byte) 0x02, (byte) 0x02, (byte) 0x01, (byte) 0x01, (byte) 0x30, (byte) 0x0d,
+            (byte) 0x06, (byte) 0x09, (byte) 0x2a, (byte) 0x86, (byte) 0x48, (byte) 0x86,
+            (byte) 0xf7, (byte) 0x0d, (byte) 0x01, (byte) 0x01, (byte) 0x05, (byte) 0x05,
+            (byte) 0x00, (byte) 0x30, (byte) 0x4f, (byte) 0x31, (byte) 0x0b, (byte) 0x30,
+            (byte) 0x09, (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x04, (byte) 0x06,
+            (byte) 0x13, (byte) 0x02, (byte) 0x55, (byte) 0x53, (byte) 0x31, (byte) 0x0b,
+            (byte) 0x30, (byte) 0x09, (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x04,
+            (byte) 0x08, (byte) 0x13, (byte) 0x02, (byte) 0x43, (byte) 0x41, (byte) 0x31,
+            (byte) 0x16, (byte) 0x30, (byte) 0x14, (byte) 0x06, (byte) 0x03, (byte) 0x55,
+            (byte) 0x04, (byte) 0x07, (byte) 0x13, (byte) 0x0d, (byte) 0x4d, (byte) 0x6f,
+            (byte) 0x75, (byte) 0x6e, (byte) 0x74, (byte) 0x61, (byte) 0x69, (byte) 0x6e,
+            (byte) 0x20, (byte) 0x56, (byte) 0x69, (byte) 0x65, (byte) 0x77, (byte) 0x31,
+            (byte) 0x1b, (byte) 0x30, (byte) 0x19, (byte) 0x06, (byte) 0x03, (byte) 0x55,
+            (byte) 0x04, (byte) 0x0a, (byte) 0x13, (byte) 0x12, (byte) 0x41, (byte) 0x6e,
+            (byte) 0x64, (byte) 0x72, (byte) 0x6f, (byte) 0x69, (byte) 0x64, (byte) 0x20,
+            (byte) 0x54, (byte) 0x65, (byte) 0x73, (byte) 0x74, (byte) 0x20, (byte) 0x43,
+            (byte) 0x61, (byte) 0x73, (byte) 0x65, (byte) 0x73, (byte) 0x30, (byte) 0x1e,
+            (byte) 0x17, (byte) 0x0d, (byte) 0x31, (byte) 0x32, (byte) 0x30, (byte) 0x38,
+            (byte) 0x31, (byte) 0x34, (byte) 0x32, (byte) 0x33, (byte) 0x32, (byte) 0x35,
+            (byte) 0x34, (byte) 0x38, (byte) 0x5a, (byte) 0x17, (byte) 0x0d, (byte) 0x32,
+            (byte) 0x32, (byte) 0x30, (byte) 0x38, (byte) 0x31, (byte) 0x32, (byte) 0x32,
+            (byte) 0x33, (byte) 0x32, (byte) 0x35, (byte) 0x34, (byte) 0x38, (byte) 0x5a,
+            (byte) 0x30, (byte) 0x55, (byte) 0x31, (byte) 0x0b, (byte) 0x30, (byte) 0x09,
+            (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x04, (byte) 0x06, (byte) 0x13,
+            (byte) 0x02, (byte) 0x55, (byte) 0x53, (byte) 0x31, (byte) 0x0b, (byte) 0x30,
+            (byte) 0x09, (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x04, (byte) 0x08,
+            (byte) 0x13, (byte) 0x02, (byte) 0x43, (byte) 0x41, (byte) 0x31, (byte) 0x1b,
+            (byte) 0x30, (byte) 0x19, (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x04,
+            (byte) 0x0a, (byte) 0x13, (byte) 0x12, (byte) 0x41, (byte) 0x6e, (byte) 0x64,
+            (byte) 0x72, (byte) 0x6f, (byte) 0x69, (byte) 0x64, (byte) 0x20, (byte) 0x54,
+            (byte) 0x65, (byte) 0x73, (byte) 0x74, (byte) 0x20, (byte) 0x43, (byte) 0x61,
+            (byte) 0x73, (byte) 0x65, (byte) 0x73, (byte) 0x31, (byte) 0x1c, (byte) 0x30,
+            (byte) 0x1a, (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x04, (byte) 0x03,
+            (byte) 0x13, (byte) 0x13, (byte) 0x73, (byte) 0x65, (byte) 0x72, (byte) 0x76,
+            (byte) 0x65, (byte) 0x72, (byte) 0x31, (byte) 0x2e, (byte) 0x65, (byte) 0x78,
+            (byte) 0x61, (byte) 0x6d, (byte) 0x70, (byte) 0x6c, (byte) 0x65, (byte) 0x2e,
+            (byte) 0x63, (byte) 0x6f, (byte) 0x6d, (byte) 0x30, (byte) 0x81, (byte) 0x9f,
+            (byte) 0x30, (byte) 0x0d, (byte) 0x06, (byte) 0x09, (byte) 0x2a, (byte) 0x86,
+            (byte) 0x48, (byte) 0x86, (byte) 0xf7, (byte) 0x0d, (byte) 0x01, (byte) 0x01,
+            (byte) 0x01, (byte) 0x05, (byte) 0x00, (byte) 0x03, (byte) 0x81, (byte) 0x8d,
+            (byte) 0x00, (byte) 0x30, (byte) 0x81, (byte) 0x89, (byte) 0x02, (byte) 0x81,
+            (byte) 0x81, (byte) 0x00, (byte) 0xce, (byte) 0x29, (byte) 0xeb, (byte) 0xf6,
+            (byte) 0x5b, (byte) 0x25, (byte) 0xdc, (byte) 0xa1, (byte) 0xa6, (byte) 0x2c,
+            (byte) 0x66, (byte) 0xcb, (byte) 0x20, (byte) 0x90, (byte) 0x27, (byte) 0x86,
+            (byte) 0x8a, (byte) 0x44, (byte) 0x71, (byte) 0x50, (byte) 0xda, (byte) 0xd3,
+            (byte) 0x02, (byte) 0x77, (byte) 0x55, (byte) 0xe9, (byte) 0xe8, (byte) 0x08,
+            (byte) 0xf3, (byte) 0x36, (byte) 0x9a, (byte) 0xae, (byte) 0xab, (byte) 0x04,
+            (byte) 0x6d, (byte) 0x00, (byte) 0x99, (byte) 0xbf, (byte) 0x7d, (byte) 0x0f,
+            (byte) 0x67, (byte) 0x8b, (byte) 0x1d, (byte) 0xd4, (byte) 0x2b, (byte) 0x7c,
+            (byte) 0xcb, (byte) 0xcd, (byte) 0x33, (byte) 0xc7, (byte) 0x84, (byte) 0x30,
+            (byte) 0xe2, (byte) 0x45, (byte) 0x21, (byte) 0xb3, (byte) 0x75, (byte) 0xf5,
+            (byte) 0x79, (byte) 0x02, (byte) 0xda, (byte) 0x50, (byte) 0xa3, (byte) 0x8b,
+            (byte) 0xce, (byte) 0xc3, (byte) 0x8e, (byte) 0x0f, (byte) 0x25, (byte) 0xeb,
+            (byte) 0x08, (byte) 0x2c, (byte) 0xdd, (byte) 0x1c, (byte) 0xcf, (byte) 0xff,
+            (byte) 0x3b, (byte) 0xde, (byte) 0xb6, (byte) 0xaa, (byte) 0x2a, (byte) 0xa9,
+            (byte) 0xc4, (byte) 0x8a, (byte) 0x24, (byte) 0x24, (byte) 0xe6, (byte) 0x29,
+            (byte) 0x0d, (byte) 0x98, (byte) 0x4c, (byte) 0x32, (byte) 0xa1, (byte) 0x7b,
+            (byte) 0x23, (byte) 0x2b, (byte) 0x42, (byte) 0x30, (byte) 0xee, (byte) 0x78,
+            (byte) 0x08, (byte) 0x47, (byte) 0xad, (byte) 0xf2, (byte) 0x96, (byte) 0xd5,
+            (byte) 0xf1, (byte) 0x62, (byte) 0x42, (byte) 0x2d, (byte) 0x35, (byte) 0x19,
+            (byte) 0xb4, (byte) 0x3c, (byte) 0xc9, (byte) 0xc3, (byte) 0x5f, (byte) 0x03,
+            (byte) 0x16, (byte) 0x3a, (byte) 0x23, (byte) 0xac, (byte) 0xcb, (byte) 0xce,
+            (byte) 0x9e, (byte) 0x51, (byte) 0x2e, (byte) 0x6d, (byte) 0x02, (byte) 0x03,
+            (byte) 0x01, (byte) 0x00, (byte) 0x01, (byte) 0xa3, (byte) 0x7b, (byte) 0x30,
+            (byte) 0x79, (byte) 0x30, (byte) 0x09, (byte) 0x06, (byte) 0x03, (byte) 0x55,
+            (byte) 0x1d, (byte) 0x13, (byte) 0x04, (byte) 0x02, (byte) 0x30, (byte) 0x00,
+            (byte) 0x30, (byte) 0x2c, (byte) 0x06, (byte) 0x09, (byte) 0x60, (byte) 0x86,
+            (byte) 0x48, (byte) 0x01, (byte) 0x86, (byte) 0xf8, (byte) 0x42, (byte) 0x01,
+            (byte) 0x0d, (byte) 0x04, (byte) 0x1f, (byte) 0x16, (byte) 0x1d, (byte) 0x4f,
+            (byte) 0x70, (byte) 0x65, (byte) 0x6e, (byte) 0x53, (byte) 0x53, (byte) 0x4c,
+            (byte) 0x20, (byte) 0x47, (byte) 0x65, (byte) 0x6e, (byte) 0x65, (byte) 0x72,
+            (byte) 0x61, (byte) 0x74, (byte) 0x65, (byte) 0x64, (byte) 0x20, (byte) 0x43,
+            (byte) 0x65, (byte) 0x72, (byte) 0x74, (byte) 0x69, (byte) 0x66, (byte) 0x69,
+            (byte) 0x63, (byte) 0x61, (byte) 0x74, (byte) 0x65, (byte) 0x30, (byte) 0x1d,
+            (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x1d, (byte) 0x0e, (byte) 0x04,
+            (byte) 0x16, (byte) 0x04, (byte) 0x14, (byte) 0x32, (byte) 0xa1, (byte) 0x1e,
+            (byte) 0x6b, (byte) 0x69, (byte) 0x04, (byte) 0xfe, (byte) 0xb3, (byte) 0xcd,
+            (byte) 0xf8, (byte) 0xbb, (byte) 0x14, (byte) 0xcd, (byte) 0xff, (byte) 0xd4,
+            (byte) 0x16, (byte) 0xc3, (byte) 0xab, (byte) 0x44, (byte) 0x2f, (byte) 0x30,
+            (byte) 0x1f, (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x1d, (byte) 0x23,
+            (byte) 0x04, (byte) 0x18, (byte) 0x30, (byte) 0x16, (byte) 0x80, (byte) 0x14,
+            (byte) 0x33, (byte) 0x05, (byte) 0xee, (byte) 0xfe, (byte) 0x6f, (byte) 0x60,
+            (byte) 0xc7, (byte) 0xf9, (byte) 0xa9, (byte) 0xd2, (byte) 0x73, (byte) 0x5c,
+            (byte) 0x8f, (byte) 0x6d, (byte) 0xa2, (byte) 0x2f, (byte) 0x97, (byte) 0x8e,
+            (byte) 0x5d, (byte) 0x51, (byte) 0x30, (byte) 0x0d, (byte) 0x06, (byte) 0x09,
+            (byte) 0x2a, (byte) 0x86, (byte) 0x48, (byte) 0x86, (byte) 0xf7, (byte) 0x0d,
+            (byte) 0x01, (byte) 0x01, (byte) 0x05, (byte) 0x05, (byte) 0x00, (byte) 0x03,
+            (byte) 0x81, (byte) 0x81, (byte) 0x00, (byte) 0x46, (byte) 0x42, (byte) 0xef,
+            (byte) 0x56, (byte) 0x89, (byte) 0x78, (byte) 0x90, (byte) 0x38, (byte) 0x24,
+            (byte) 0x9f, (byte) 0x8c, (byte) 0x7a, (byte) 0xce, (byte) 0x7a, (byte) 0xa5,
+            (byte) 0xb5, (byte) 0x1e, (byte) 0x74, (byte) 0x96, (byte) 0x34, (byte) 0x49,
+            (byte) 0x8b, (byte) 0xed, (byte) 0x44, (byte) 0xb3, (byte) 0xc9, (byte) 0x05,
+            (byte) 0xd7, (byte) 0x48, (byte) 0x55, (byte) 0x52, (byte) 0x59, (byte) 0x15,
+            (byte) 0x0b, (byte) 0xaa, (byte) 0x16, (byte) 0x86, (byte) 0xd2, (byte) 0x8e,
+            (byte) 0x16, (byte) 0x99, (byte) 0xe8, (byte) 0x5f, (byte) 0x11, (byte) 0x71,
+            (byte) 0x42, (byte) 0x55, (byte) 0xd1, (byte) 0xc4, (byte) 0x6f, (byte) 0x2e,
+            (byte) 0xa9, (byte) 0x64, (byte) 0x6f, (byte) 0xd8, (byte) 0xfd, (byte) 0x43,
+            (byte) 0x13, (byte) 0x24, (byte) 0xaa, (byte) 0x67, (byte) 0xe6, (byte) 0xf5,
+            (byte) 0xca, (byte) 0x80, (byte) 0x5e, (byte) 0x3a, (byte) 0x3e, (byte) 0xcc,
+            (byte) 0x4f, (byte) 0xba, (byte) 0x87, (byte) 0xe6, (byte) 0xae, (byte) 0xbf,
+            (byte) 0x8f, (byte) 0xd5, (byte) 0x28, (byte) 0x38, (byte) 0x58, (byte) 0x30,
+            (byte) 0x24, (byte) 0xf6, (byte) 0x53, (byte) 0x5b, (byte) 0x41, (byte) 0x53,
+            (byte) 0xe6, (byte) 0x45, (byte) 0xbc, (byte) 0xbe, (byte) 0xe6, (byte) 0xbb,
+            (byte) 0x5d, (byte) 0xd8, (byte) 0xa7, (byte) 0xf9, (byte) 0x64, (byte) 0x99,
+            (byte) 0x04, (byte) 0x43, (byte) 0x75, (byte) 0xd7, (byte) 0x2d, (byte) 0x32,
+            (byte) 0x0a, (byte) 0x94, (byte) 0xaf, (byte) 0x06, (byte) 0x34, (byte) 0xae,
+            (byte) 0x46, (byte) 0xbd, (byte) 0xda, (byte) 0x00, (byte) 0x0e, (byte) 0x25,
+            (byte) 0xc2, (byte) 0xf7, (byte) 0xc9, (byte) 0xc3, (byte) 0x65, (byte) 0xd2,
+            (byte) 0x08, (byte) 0x41, (byte) 0x0a, (byte) 0xf3, (byte) 0x72
+    };
+
+    /**
+     * Generated from above and converted with:
+     *
+     * openssl x509 -outform d -in cacert.pem | xxd -i | sed 's/0x/(byte) 0x/g'
+     */
+    private static final byte[] FAKE_RSA_CA_1 = {
+            (byte) 0x30, (byte) 0x82, (byte) 0x02, (byte) 0xce, (byte) 0x30, (byte) 0x82,
+            (byte) 0x02, (byte) 0x37, (byte) 0xa0, (byte) 0x03, (byte) 0x02, (byte) 0x01,
+            (byte) 0x02, (byte) 0x02, (byte) 0x09, (byte) 0x00, (byte) 0xe1, (byte) 0x6a,
+            (byte) 0xa2, (byte) 0xf4, (byte) 0x2e, (byte) 0x55, (byte) 0x48, (byte) 0x0a,
+            (byte) 0x30, (byte) 0x0d, (byte) 0x06, (byte) 0x09, (byte) 0x2a, (byte) 0x86,
+            (byte) 0x48, (byte) 0x86, (byte) 0xf7, (byte) 0x0d, (byte) 0x01, (byte) 0x01,
+            (byte) 0x05, (byte) 0x05, (byte) 0x00, (byte) 0x30, (byte) 0x4f, (byte) 0x31,
+            (byte) 0x0b, (byte) 0x30, (byte) 0x09, (byte) 0x06, (byte) 0x03, (byte) 0x55,
+            (byte) 0x04, (byte) 0x06, (byte) 0x13, (byte) 0x02, (byte) 0x55, (byte) 0x53,
+            (byte) 0x31, (byte) 0x0b, (byte) 0x30, (byte) 0x09, (byte) 0x06, (byte) 0x03,
+            (byte) 0x55, (byte) 0x04, (byte) 0x08, (byte) 0x13, (byte) 0x02, (byte) 0x43,
+            (byte) 0x41, (byte) 0x31, (byte) 0x16, (byte) 0x30, (byte) 0x14, (byte) 0x06,
+            (byte) 0x03, (byte) 0x55, (byte) 0x04, (byte) 0x07, (byte) 0x13, (byte) 0x0d,
+            (byte) 0x4d, (byte) 0x6f, (byte) 0x75, (byte) 0x6e, (byte) 0x74, (byte) 0x61,
+            (byte) 0x69, (byte) 0x6e, (byte) 0x20, (byte) 0x56, (byte) 0x69, (byte) 0x65,
+            (byte) 0x77, (byte) 0x31, (byte) 0x1b, (byte) 0x30, (byte) 0x19, (byte) 0x06,
+            (byte) 0x03, (byte) 0x55, (byte) 0x04, (byte) 0x0a, (byte) 0x13, (byte) 0x12,
+            (byte) 0x41, (byte) 0x6e, (byte) 0x64, (byte) 0x72, (byte) 0x6f, (byte) 0x69,
+            (byte) 0x64, (byte) 0x20, (byte) 0x54, (byte) 0x65, (byte) 0x73, (byte) 0x74,
+            (byte) 0x20, (byte) 0x43, (byte) 0x61, (byte) 0x73, (byte) 0x65, (byte) 0x73,
+            (byte) 0x30, (byte) 0x1e, (byte) 0x17, (byte) 0x0d, (byte) 0x31, (byte) 0x32,
+            (byte) 0x30, (byte) 0x38, (byte) 0x31, (byte) 0x34, (byte) 0x31, (byte) 0x36,
+            (byte) 0x35, (byte) 0x35, (byte) 0x34, (byte) 0x34, (byte) 0x5a, (byte) 0x17,
+            (byte) 0x0d, (byte) 0x32, (byte) 0x32, (byte) 0x30, (byte) 0x38, (byte) 0x31,
+            (byte) 0x32, (byte) 0x31, (byte) 0x36, (byte) 0x35, (byte) 0x35, (byte) 0x34,
+            (byte) 0x34, (byte) 0x5a, (byte) 0x30, (byte) 0x4f, (byte) 0x31, (byte) 0x0b,
+            (byte) 0x30, (byte) 0x09, (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x04,
+            (byte) 0x06, (byte) 0x13, (byte) 0x02, (byte) 0x55, (byte) 0x53, (byte) 0x31,
+            (byte) 0x0b, (byte) 0x30, (byte) 0x09, (byte) 0x06, (byte) 0x03, (byte) 0x55,
+            (byte) 0x04, (byte) 0x08, (byte) 0x13, (byte) 0x02, (byte) 0x43, (byte) 0x41,
+            (byte) 0x31, (byte) 0x16, (byte) 0x30, (byte) 0x14, (byte) 0x06, (byte) 0x03,
+            (byte) 0x55, (byte) 0x04, (byte) 0x07, (byte) 0x13, (byte) 0x0d, (byte) 0x4d,
+            (byte) 0x6f, (byte) 0x75, (byte) 0x6e, (byte) 0x74, (byte) 0x61, (byte) 0x69,
+            (byte) 0x6e, (byte) 0x20, (byte) 0x56, (byte) 0x69, (byte) 0x65, (byte) 0x77,
+            (byte) 0x31, (byte) 0x1b, (byte) 0x30, (byte) 0x19, (byte) 0x06, (byte) 0x03,
+            (byte) 0x55, (byte) 0x04, (byte) 0x0a, (byte) 0x13, (byte) 0x12, (byte) 0x41,
+            (byte) 0x6e, (byte) 0x64, (byte) 0x72, (byte) 0x6f, (byte) 0x69, (byte) 0x64,
+            (byte) 0x20, (byte) 0x54, (byte) 0x65, (byte) 0x73, (byte) 0x74, (byte) 0x20,
+            (byte) 0x43, (byte) 0x61, (byte) 0x73, (byte) 0x65, (byte) 0x73, (byte) 0x30,
+            (byte) 0x81, (byte) 0x9f, (byte) 0x30, (byte) 0x0d, (byte) 0x06, (byte) 0x09,
+            (byte) 0x2a, (byte) 0x86, (byte) 0x48, (byte) 0x86, (byte) 0xf7, (byte) 0x0d,
+            (byte) 0x01, (byte) 0x01, (byte) 0x01, (byte) 0x05, (byte) 0x00, (byte) 0x03,
+            (byte) 0x81, (byte) 0x8d, (byte) 0x00, (byte) 0x30, (byte) 0x81, (byte) 0x89,
+            (byte) 0x02, (byte) 0x81, (byte) 0x81, (byte) 0x00, (byte) 0xa3, (byte) 0x72,
+            (byte) 0xab, (byte) 0xd0, (byte) 0xe4, (byte) 0xad, (byte) 0x2f, (byte) 0xe7,
+            (byte) 0xe2, (byte) 0x79, (byte) 0x07, (byte) 0x36, (byte) 0x3d, (byte) 0x0c,
+            (byte) 0x8d, (byte) 0x42, (byte) 0x9a, (byte) 0x0a, (byte) 0x33, (byte) 0x64,
+            (byte) 0xb3, (byte) 0xcd, (byte) 0xb2, (byte) 0xd7, (byte) 0x3a, (byte) 0x42,
+            (byte) 0x06, (byte) 0x77, (byte) 0x45, (byte) 0x29, (byte) 0xe9, (byte) 0xcb,
+            (byte) 0xb7, (byte) 0x4a, (byte) 0xd6, (byte) 0xee, (byte) 0xad, (byte) 0x01,
+            (byte) 0x91, (byte) 0x9b, (byte) 0x0c, (byte) 0x59, (byte) 0xa1, (byte) 0x03,
+            (byte) 0xfa, (byte) 0xf0, (byte) 0x5a, (byte) 0x7c, (byte) 0x4f, (byte) 0xf7,
+            (byte) 0x8d, (byte) 0x36, (byte) 0x0f, (byte) 0x1f, (byte) 0x45, (byte) 0x7d,
+            (byte) 0x1b, (byte) 0x31, (byte) 0xa1, (byte) 0x35, (byte) 0x0b, (byte) 0x00,
+            (byte) 0xed, (byte) 0x7a, (byte) 0xb6, (byte) 0xc8, (byte) 0x4e, (byte) 0xa9,
+            (byte) 0x86, (byte) 0x4c, (byte) 0x7b, (byte) 0x99, (byte) 0x57, (byte) 0x41,
+            (byte) 0x12, (byte) 0xef, (byte) 0x6b, (byte) 0xbc, (byte) 0x3d, (byte) 0x60,
+            (byte) 0xf2, (byte) 0x99, (byte) 0x1a, (byte) 0xcd, (byte) 0xed, (byte) 0x56,
+            (byte) 0xa4, (byte) 0xe5, (byte) 0x36, (byte) 0x9f, (byte) 0x24, (byte) 0x1f,
+            (byte) 0xdc, (byte) 0x89, (byte) 0x40, (byte) 0xc8, (byte) 0x99, (byte) 0x92,
+            (byte) 0xab, (byte) 0x4a, (byte) 0xb5, (byte) 0x61, (byte) 0x45, (byte) 0x62,
+            (byte) 0xff, (byte) 0xa3, (byte) 0x45, (byte) 0x65, (byte) 0xaf, (byte) 0xf6,
+            (byte) 0x27, (byte) 0x30, (byte) 0x51, (byte) 0x0e, (byte) 0x0e, (byte) 0xeb,
+            (byte) 0x79, (byte) 0x0c, (byte) 0xbe, (byte) 0xb3, (byte) 0x0a, (byte) 0x6f,
+            (byte) 0x29, (byte) 0x06, (byte) 0xdc, (byte) 0x2f, (byte) 0x6b, (byte) 0x51,
+            (byte) 0x02, (byte) 0x03, (byte) 0x01, (byte) 0x00, (byte) 0x01, (byte) 0xa3,
+            (byte) 0x81, (byte) 0xb1, (byte) 0x30, (byte) 0x81, (byte) 0xae, (byte) 0x30,
+            (byte) 0x1d, (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x1d, (byte) 0x0e,
+            (byte) 0x04, (byte) 0x16, (byte) 0x04, (byte) 0x14, (byte) 0x33, (byte) 0x05,
+            (byte) 0xee, (byte) 0xfe, (byte) 0x6f, (byte) 0x60, (byte) 0xc7, (byte) 0xf9,
+            (byte) 0xa9, (byte) 0xd2, (byte) 0x73, (byte) 0x5c, (byte) 0x8f, (byte) 0x6d,
+            (byte) 0xa2, (byte) 0x2f, (byte) 0x97, (byte) 0x8e, (byte) 0x5d, (byte) 0x51,
+            (byte) 0x30, (byte) 0x7f, (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x1d,
+            (byte) 0x23, (byte) 0x04, (byte) 0x78, (byte) 0x30, (byte) 0x76, (byte) 0x80,
+            (byte) 0x14, (byte) 0x33, (byte) 0x05, (byte) 0xee, (byte) 0xfe, (byte) 0x6f,
+            (byte) 0x60, (byte) 0xc7, (byte) 0xf9, (byte) 0xa9, (byte) 0xd2, (byte) 0x73,
+            (byte) 0x5c, (byte) 0x8f, (byte) 0x6d, (byte) 0xa2, (byte) 0x2f, (byte) 0x97,
+            (byte) 0x8e, (byte) 0x5d, (byte) 0x51, (byte) 0xa1, (byte) 0x53, (byte) 0xa4,
+            (byte) 0x51, (byte) 0x30, (byte) 0x4f, (byte) 0x31, (byte) 0x0b, (byte) 0x30,
+            (byte) 0x09, (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x04, (byte) 0x06,
+            (byte) 0x13, (byte) 0x02, (byte) 0x55, (byte) 0x53, (byte) 0x31, (byte) 0x0b,
+            (byte) 0x30, (byte) 0x09, (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x04,
+            (byte) 0x08, (byte) 0x13, (byte) 0x02, (byte) 0x43, (byte) 0x41, (byte) 0x31,
+            (byte) 0x16, (byte) 0x30, (byte) 0x14, (byte) 0x06, (byte) 0x03, (byte) 0x55,
+            (byte) 0x04, (byte) 0x07, (byte) 0x13, (byte) 0x0d, (byte) 0x4d, (byte) 0x6f,
+            (byte) 0x75, (byte) 0x6e, (byte) 0x74, (byte) 0x61, (byte) 0x69, (byte) 0x6e,
+            (byte) 0x20, (byte) 0x56, (byte) 0x69, (byte) 0x65, (byte) 0x77, (byte) 0x31,
+            (byte) 0x1b, (byte) 0x30, (byte) 0x19, (byte) 0x06, (byte) 0x03, (byte) 0x55,
+            (byte) 0x04, (byte) 0x0a, (byte) 0x13, (byte) 0x12, (byte) 0x41, (byte) 0x6e,
+            (byte) 0x64, (byte) 0x72, (byte) 0x6f, (byte) 0x69, (byte) 0x64, (byte) 0x20,
+            (byte) 0x54, (byte) 0x65, (byte) 0x73, (byte) 0x74, (byte) 0x20, (byte) 0x43,
+            (byte) 0x61, (byte) 0x73, (byte) 0x65, (byte) 0x73, (byte) 0x82, (byte) 0x09,
+            (byte) 0x00, (byte) 0xe1, (byte) 0x6a, (byte) 0xa2, (byte) 0xf4, (byte) 0x2e,
+            (byte) 0x55, (byte) 0x48, (byte) 0x0a, (byte) 0x30, (byte) 0x0c, (byte) 0x06,
+            (byte) 0x03, (byte) 0x55, (byte) 0x1d, (byte) 0x13, (byte) 0x04, (byte) 0x05,
+            (byte) 0x30, (byte) 0x03, (byte) 0x01, (byte) 0x01, (byte) 0xff, (byte) 0x30,
+            (byte) 0x0d, (byte) 0x06, (byte) 0x09, (byte) 0x2a, (byte) 0x86, (byte) 0x48,
+            (byte) 0x86, (byte) 0xf7, (byte) 0x0d, (byte) 0x01, (byte) 0x01, (byte) 0x05,
+            (byte) 0x05, (byte) 0x00, (byte) 0x03, (byte) 0x81, (byte) 0x81, (byte) 0x00,
+            (byte) 0x8c, (byte) 0x30, (byte) 0x42, (byte) 0xfa, (byte) 0xeb, (byte) 0x1a,
+            (byte) 0x26, (byte) 0xeb, (byte) 0xda, (byte) 0x56, (byte) 0x32, (byte) 0xf2,
+            (byte) 0x9d, (byte) 0xa5, (byte) 0x24, (byte) 0xd8, (byte) 0x3a, (byte) 0xda,
+            (byte) 0x30, (byte) 0xa6, (byte) 0x8b, (byte) 0x46, (byte) 0xfe, (byte) 0xfe,
+            (byte) 0xdb, (byte) 0xf1, (byte) 0xe6, (byte) 0xe1, (byte) 0x7c, (byte) 0x1b,
+            (byte) 0xe7, (byte) 0x77, (byte) 0x00, (byte) 0xa1, (byte) 0x1c, (byte) 0x19,
+            (byte) 0x17, (byte) 0x73, (byte) 0xb0, (byte) 0xf0, (byte) 0x9d, (byte) 0xf3,
+            (byte) 0x4f, (byte) 0xb6, (byte) 0xbc, (byte) 0xc7, (byte) 0x47, (byte) 0x85,
+            (byte) 0x2a, (byte) 0x4a, (byte) 0xa1, (byte) 0xa5, (byte) 0x58, (byte) 0xf5,
+            (byte) 0xc5, (byte) 0x1a, (byte) 0x51, (byte) 0xb1, (byte) 0x04, (byte) 0x80,
+            (byte) 0xee, (byte) 0x3a, (byte) 0xec, (byte) 0x2f, (byte) 0xe1, (byte) 0xfd,
+            (byte) 0x58, (byte) 0xeb, (byte) 0xed, (byte) 0x82, (byte) 0x9e, (byte) 0x38,
+            (byte) 0xa3, (byte) 0x24, (byte) 0x75, (byte) 0xf7, (byte) 0x3e, (byte) 0xc2,
+            (byte) 0xc5, (byte) 0x27, (byte) 0xeb, (byte) 0x6f, (byte) 0x7b, (byte) 0x50,
+            (byte) 0xda, (byte) 0x43, (byte) 0xdc, (byte) 0x3b, (byte) 0x0b, (byte) 0x6f,
+            (byte) 0x78, (byte) 0x8f, (byte) 0xb0, (byte) 0x66, (byte) 0xe1, (byte) 0x12,
+            (byte) 0x87, (byte) 0x5f, (byte) 0x97, (byte) 0x7b, (byte) 0xca, (byte) 0x14,
+            (byte) 0x79, (byte) 0xf7, (byte) 0xe8, (byte) 0x6c, (byte) 0x72, (byte) 0xdb,
+            (byte) 0x91, (byte) 0x65, (byte) 0x17, (byte) 0x54, (byte) 0xe0, (byte) 0x74,
+            (byte) 0x1d, (byte) 0xac, (byte) 0x47, (byte) 0x04, (byte) 0x12, (byte) 0xe0,
+            (byte) 0xc3, (byte) 0x66, (byte) 0x19, (byte) 0x05, (byte) 0x2e, (byte) 0x7e,
+            (byte) 0xf1, (byte) 0x61
+    };
+
+    /**
+     * Generated from above and converted with:
+     *
+     * openssl pkcs8 -topk8 -outform d -in userkey.pem -nocrypt | xxd -i | sed 's/0x/(byte) 0x/g'
+     */
+    private static final byte[] FAKE_RSA_KEY_1 = new byte[] {
+            (byte) 0x30, (byte) 0x82, (byte) 0x02, (byte) 0x78, (byte) 0x02, (byte) 0x01,
+            (byte) 0x00, (byte) 0x30, (byte) 0x0d, (byte) 0x06, (byte) 0x09, (byte) 0x2a,
+            (byte) 0x86, (byte) 0x48, (byte) 0x86, (byte) 0xf7, (byte) 0x0d, (byte) 0x01,
+            (byte) 0x01, (byte) 0x01, (byte) 0x05, (byte) 0x00, (byte) 0x04, (byte) 0x82,
+            (byte) 0x02, (byte) 0x62, (byte) 0x30, (byte) 0x82, (byte) 0x02, (byte) 0x5e,
+            (byte) 0x02, (byte) 0x01, (byte) 0x00, (byte) 0x02, (byte) 0x81, (byte) 0x81,
+            (byte) 0x00, (byte) 0xce, (byte) 0x29, (byte) 0xeb, (byte) 0xf6, (byte) 0x5b,
+            (byte) 0x25, (byte) 0xdc, (byte) 0xa1, (byte) 0xa6, (byte) 0x2c, (byte) 0x66,
+            (byte) 0xcb, (byte) 0x20, (byte) 0x90, (byte) 0x27, (byte) 0x86, (byte) 0x8a,
+            (byte) 0x44, (byte) 0x71, (byte) 0x50, (byte) 0xda, (byte) 0xd3, (byte) 0x02,
+            (byte) 0x77, (byte) 0x55, (byte) 0xe9, (byte) 0xe8, (byte) 0x08, (byte) 0xf3,
+            (byte) 0x36, (byte) 0x9a, (byte) 0xae, (byte) 0xab, (byte) 0x04, (byte) 0x6d,
+            (byte) 0x00, (byte) 0x99, (byte) 0xbf, (byte) 0x7d, (byte) 0x0f, (byte) 0x67,
+            (byte) 0x8b, (byte) 0x1d, (byte) 0xd4, (byte) 0x2b, (byte) 0x7c, (byte) 0xcb,
+            (byte) 0xcd, (byte) 0x33, (byte) 0xc7, (byte) 0x84, (byte) 0x30, (byte) 0xe2,
+            (byte) 0x45, (byte) 0x21, (byte) 0xb3, (byte) 0x75, (byte) 0xf5, (byte) 0x79,
+            (byte) 0x02, (byte) 0xda, (byte) 0x50, (byte) 0xa3, (byte) 0x8b, (byte) 0xce,
+            (byte) 0xc3, (byte) 0x8e, (byte) 0x0f, (byte) 0x25, (byte) 0xeb, (byte) 0x08,
+            (byte) 0x2c, (byte) 0xdd, (byte) 0x1c, (byte) 0xcf, (byte) 0xff, (byte) 0x3b,
+            (byte) 0xde, (byte) 0xb6, (byte) 0xaa, (byte) 0x2a, (byte) 0xa9, (byte) 0xc4,
+            (byte) 0x8a, (byte) 0x24, (byte) 0x24, (byte) 0xe6, (byte) 0x29, (byte) 0x0d,
+            (byte) 0x98, (byte) 0x4c, (byte) 0x32, (byte) 0xa1, (byte) 0x7b, (byte) 0x23,
+            (byte) 0x2b, (byte) 0x42, (byte) 0x30, (byte) 0xee, (byte) 0x78, (byte) 0x08,
+            (byte) 0x47, (byte) 0xad, (byte) 0xf2, (byte) 0x96, (byte) 0xd5, (byte) 0xf1,
+            (byte) 0x62, (byte) 0x42, (byte) 0x2d, (byte) 0x35, (byte) 0x19, (byte) 0xb4,
+            (byte) 0x3c, (byte) 0xc9, (byte) 0xc3, (byte) 0x5f, (byte) 0x03, (byte) 0x16,
+            (byte) 0x3a, (byte) 0x23, (byte) 0xac, (byte) 0xcb, (byte) 0xce, (byte) 0x9e,
+            (byte) 0x51, (byte) 0x2e, (byte) 0x6d, (byte) 0x02, (byte) 0x03, (byte) 0x01,
+            (byte) 0x00, (byte) 0x01, (byte) 0x02, (byte) 0x81, (byte) 0x80, (byte) 0x16,
+            (byte) 0x59, (byte) 0xc3, (byte) 0x24, (byte) 0x1d, (byte) 0x33, (byte) 0x98,
+            (byte) 0x9c, (byte) 0xc9, (byte) 0xc8, (byte) 0x2c, (byte) 0x88, (byte) 0xbf,
+            (byte) 0x0a, (byte) 0x01, (byte) 0xce, (byte) 0xfb, (byte) 0x34, (byte) 0x7a,
+            (byte) 0x58, (byte) 0x7a, (byte) 0xb0, (byte) 0xbf, (byte) 0xa6, (byte) 0xb2,
+            (byte) 0x60, (byte) 0xbe, (byte) 0x70, (byte) 0x21, (byte) 0xf5, (byte) 0xfc,
+            (byte) 0x85, (byte) 0x0d, (byte) 0x33, (byte) 0x58, (byte) 0xa1, (byte) 0xe5,
+            (byte) 0x09, (byte) 0x36, (byte) 0x84, (byte) 0xb2, (byte) 0x04, (byte) 0x0a,
+            (byte) 0x02, (byte) 0xd3, (byte) 0x88, (byte) 0x1f, (byte) 0x0c, (byte) 0x2b,
+            (byte) 0x1d, (byte) 0xe9, (byte) 0x3d, (byte) 0xe7, (byte) 0x79, (byte) 0xf9,
+            (byte) 0x32, (byte) 0x5c, (byte) 0x8a, (byte) 0x75, (byte) 0x49, (byte) 0x12,
+            (byte) 0xe4, (byte) 0x05, (byte) 0x26, (byte) 0xd4, (byte) 0x2e, (byte) 0x9e,
+            (byte) 0x1f, (byte) 0xcc, (byte) 0x54, (byte) 0xad, (byte) 0x33, (byte) 0x8d,
+            (byte) 0x99, (byte) 0x00, (byte) 0xdc, (byte) 0xf5, (byte) 0xb4, (byte) 0xa2,
+            (byte) 0x2f, (byte) 0xba, (byte) 0xe5, (byte) 0x62, (byte) 0x30, (byte) 0x6d,
+            (byte) 0xe6, (byte) 0x3d, (byte) 0xeb, (byte) 0x24, (byte) 0xc2, (byte) 0xdc,
+            (byte) 0x5f, (byte) 0xb7, (byte) 0x16, (byte) 0x35, (byte) 0xa3, (byte) 0x98,
+            (byte) 0x98, (byte) 0xa8, (byte) 0xef, (byte) 0xe8, (byte) 0xc4, (byte) 0x96,
+            (byte) 0x6d, (byte) 0x38, (byte) 0xab, (byte) 0x26, (byte) 0x6d, (byte) 0x30,
+            (byte) 0xc2, (byte) 0xa0, (byte) 0x44, (byte) 0xe4, (byte) 0xff, (byte) 0x7e,
+            (byte) 0xbe, (byte) 0x7c, (byte) 0x33, (byte) 0xa5, (byte) 0x10, (byte) 0xad,
+            (byte) 0xd7, (byte) 0x1e, (byte) 0x13, (byte) 0x20, (byte) 0xb3, (byte) 0x1f,
+            (byte) 0x41, (byte) 0x02, (byte) 0x41, (byte) 0x00, (byte) 0xf1, (byte) 0x89,
+            (byte) 0x07, (byte) 0x0f, (byte) 0xe8, (byte) 0xcf, (byte) 0xab, (byte) 0x13,
+            (byte) 0x2a, (byte) 0x8f, (byte) 0x88, (byte) 0x80, (byte) 0x11, (byte) 0x9a,
+            (byte) 0x79, (byte) 0xb6, (byte) 0x59, (byte) 0x3a, (byte) 0x50, (byte) 0x6e,
+            (byte) 0x57, (byte) 0x37, (byte) 0xab, (byte) 0x2a, (byte) 0xd2, (byte) 0xaa,
+            (byte) 0xd9, (byte) 0x72, (byte) 0x73, (byte) 0xff, (byte) 0x8b, (byte) 0x47,
+            (byte) 0x76, (byte) 0xdd, (byte) 0xdc, (byte) 0xf5, (byte) 0x97, (byte) 0x44,
+            (byte) 0x3a, (byte) 0x78, (byte) 0xbe, (byte) 0x17, (byte) 0xb4, (byte) 0x22,
+            (byte) 0x6f, (byte) 0xe5, (byte) 0x23, (byte) 0x70, (byte) 0x1d, (byte) 0x10,
+            (byte) 0x5d, (byte) 0xba, (byte) 0x16, (byte) 0x81, (byte) 0xf1, (byte) 0x45,
+            (byte) 0xce, (byte) 0x30, (byte) 0xb4, (byte) 0xab, (byte) 0x80, (byte) 0xe4,
+            (byte) 0x98, (byte) 0x31, (byte) 0x02, (byte) 0x41, (byte) 0x00, (byte) 0xda,
+            (byte) 0x82, (byte) 0x9d, (byte) 0x3f, (byte) 0xca, (byte) 0x2f, (byte) 0xe1,
+            (byte) 0xd4, (byte) 0x86, (byte) 0x77, (byte) 0x48, (byte) 0xa6, (byte) 0xab,
+            (byte) 0xab, (byte) 0x1c, (byte) 0x42, (byte) 0x5c, (byte) 0xd5, (byte) 0xc7,
+            (byte) 0x46, (byte) 0x59, (byte) 0x91, (byte) 0x3f, (byte) 0xfc, (byte) 0xcc,
+            (byte) 0xec, (byte) 0xc2, (byte) 0x40, (byte) 0x12, (byte) 0x2c, (byte) 0x8d,
+            (byte) 0x1f, (byte) 0xa2, (byte) 0x18, (byte) 0x88, (byte) 0xee, (byte) 0x82,
+            (byte) 0x4a, (byte) 0x5a, (byte) 0x5e, (byte) 0x88, (byte) 0x20, (byte) 0xe3,
+            (byte) 0x7b, (byte) 0xe0, (byte) 0xd8, (byte) 0x3a, (byte) 0x52, (byte) 0x9a,
+            (byte) 0x26, (byte) 0x6a, (byte) 0x04, (byte) 0xec, (byte) 0xe8, (byte) 0xb9,
+            (byte) 0x48, (byte) 0x40, (byte) 0xe1, (byte) 0xe1, (byte) 0x83, (byte) 0xa6,
+            (byte) 0x67, (byte) 0xa6, (byte) 0xfd, (byte) 0x02, (byte) 0x41, (byte) 0x00,
+            (byte) 0x89, (byte) 0x72, (byte) 0x3e, (byte) 0xb0, (byte) 0x90, (byte) 0xfd,
+            (byte) 0x4c, (byte) 0x0e, (byte) 0xd6, (byte) 0x13, (byte) 0x63, (byte) 0xcb,
+            (byte) 0xed, (byte) 0x38, (byte) 0x88, (byte) 0xb6, (byte) 0x79, (byte) 0xc4,
+            (byte) 0x33, (byte) 0x6c, (byte) 0xf6, (byte) 0xf8, (byte) 0xd8, (byte) 0xd0,
+            (byte) 0xbf, (byte) 0x9d, (byte) 0x35, (byte) 0xac, (byte) 0x69, (byte) 0xd2,
+            (byte) 0x2b, (byte) 0xc1, (byte) 0xf9, (byte) 0x24, (byte) 0x7b, (byte) 0xce,
+            (byte) 0xcd, (byte) 0xcb, (byte) 0xa7, (byte) 0xb2, (byte) 0x7a, (byte) 0x0a,
+            (byte) 0x27, (byte) 0x19, (byte) 0xc9, (byte) 0xaf, (byte) 0x0d, (byte) 0x21,
+            (byte) 0x89, (byte) 0x88, (byte) 0x7c, (byte) 0xad, (byte) 0x9e, (byte) 0x8d,
+            (byte) 0x47, (byte) 0x6d, (byte) 0x3f, (byte) 0xce, (byte) 0x7b, (byte) 0xa1,
+            (byte) 0x74, (byte) 0xf1, (byte) 0xa0, (byte) 0xa1, (byte) 0x02, (byte) 0x41,
+            (byte) 0x00, (byte) 0xd9, (byte) 0xa8, (byte) 0xf5, (byte) 0xfe, (byte) 0xce,
+            (byte) 0xe6, (byte) 0x77, (byte) 0x6b, (byte) 0xfe, (byte) 0x2d, (byte) 0xe0,
+            (byte) 0x1e, (byte) 0xb6, (byte) 0x2e, (byte) 0x12, (byte) 0x4e, (byte) 0x40,
+            (byte) 0xaf, (byte) 0x6a, (byte) 0x7b, (byte) 0x37, (byte) 0x49, (byte) 0x2a,
+            (byte) 0x96, (byte) 0x25, (byte) 0x83, (byte) 0x49, (byte) 0xd4, (byte) 0x0c,
+            (byte) 0xc6, (byte) 0x78, (byte) 0x25, (byte) 0x24, (byte) 0x90, (byte) 0x90,
+            (byte) 0x06, (byte) 0x15, (byte) 0x9e, (byte) 0xfe, (byte) 0xf9, (byte) 0xdf,
+            (byte) 0x5b, (byte) 0xf3, (byte) 0x7e, (byte) 0x38, (byte) 0x70, (byte) 0xeb,
+            (byte) 0x57, (byte) 0xd0, (byte) 0xd9, (byte) 0xa7, (byte) 0x0e, (byte) 0x14,
+            (byte) 0xf7, (byte) 0x95, (byte) 0x68, (byte) 0xd5, (byte) 0xc8, (byte) 0xab,
+            (byte) 0x9d, (byte) 0x3a, (byte) 0x2b, (byte) 0x51, (byte) 0xf9, (byte) 0x02,
+            (byte) 0x41, (byte) 0x00, (byte) 0x96, (byte) 0xdf, (byte) 0xe9, (byte) 0x67,
+            (byte) 0x6c, (byte) 0xdc, (byte) 0x90, (byte) 0x14, (byte) 0xb4, (byte) 0x1d,
+            (byte) 0x22, (byte) 0x33, (byte) 0x4a, (byte) 0x31, (byte) 0xc1, (byte) 0x9d,
+            (byte) 0x2e, (byte) 0xff, (byte) 0x9a, (byte) 0x2a, (byte) 0x95, (byte) 0x4b,
+            (byte) 0x27, (byte) 0x74, (byte) 0xcb, (byte) 0x21, (byte) 0xc3, (byte) 0xd2,
+            (byte) 0x0b, (byte) 0xb2, (byte) 0x46, (byte) 0x87, (byte) 0xf8, (byte) 0x28,
+            (byte) 0x01, (byte) 0x8b, (byte) 0xd8, (byte) 0xb9, (byte) 0x4b, (byte) 0xcd,
+            (byte) 0x9a, (byte) 0x96, (byte) 0x41, (byte) 0x0e, (byte) 0x36, (byte) 0x6d,
+            (byte) 0x40, (byte) 0x42, (byte) 0xbc, (byte) 0xd9, (byte) 0xd3, (byte) 0x7b,
+            (byte) 0xbc, (byte) 0xa7, (byte) 0x92, (byte) 0x90, (byte) 0xdd, (byte) 0xa1,
+            (byte) 0x9c, (byte) 0xce, (byte) 0xa1, (byte) 0x87, (byte) 0x11, (byte) 0x51
+    };
+
+    private WebView mWebView;
+    private CtsTestServer mWebServer;
+    private WebViewOnUiThread mOnUiThread;
+
+    public WebViewSslTest() {
+        super("com.android.cts.stub", WebViewStubActivity.class);
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        final WebViewStubActivity activity = getActivity();
+        mWebView = activity.getWebView();
+        if (mWebView != null) {
+            new PollingCheck() {
+                @Override
+                    protected boolean check() {
+                        return activity.hasWindowFocus();
+                }
+            }.run();
+            File f = activity.getFileStreamPath("snapshot");
+            if (f.exists()) {
+                f.delete();
+            }
+
+            mOnUiThread = new WebViewOnUiThread(this, mWebView);
+        }
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        if (mOnUiThread != null) {
+            mOnUiThread.cleanUp();
+        }
+        if (mWebServer != null) {
+            stopWebServer();
+        }
+        super.tearDown();
+    }
+
+    private void startWebServer(boolean secure) throws Exception {
+        assertNull(mWebServer);
+        mWebServer = new CtsTestServer(getActivity(), secure);
+    }
+
+    private void stopWebServer() throws Exception {
+        assertNotNull(mWebServer);
+        ThreadPolicy oldPolicy = StrictMode.getThreadPolicy();
+        ThreadPolicy tmpPolicy = new ThreadPolicy.Builder(oldPolicy)
+                .permitNetwork()
+                .build();
+        StrictMode.setThreadPolicy(tmpPolicy);
+        mWebServer.shutdown();
+        mWebServer = null;
+        StrictMode.setThreadPolicy(oldPolicy);
+    }
+
+    @UiThreadTest
+    public void testInsecureSiteClearsCertificate() throws Throwable {
+        if (!NullWebViewUtils.isWebViewAvailable()) {
+            return;
+        }
+        final class MockWebViewClient extends WaitForLoadedClient {
+            public MockWebViewClient() {
+                super(mOnUiThread);
+            }
+            @Override
+            public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
+                handler.proceed();
+            }
+        }
+
+        startWebServer(true);
+        mOnUiThread.setWebViewClient(new MockWebViewClient());
+        mOnUiThread.loadUrlAndWaitForCompletion(
+                mWebServer.getAssetUrl(TestHtmlConstants.HELLO_WORLD_URL));
+        SslCertificate cert = mWebView.getCertificate();
+        assertNotNull(cert);
+        assertEquals("Android", cert.getIssuedTo().getUName());
+
+        stopWebServer();
+
+        startWebServer(false);
+        mOnUiThread.loadUrlAndWaitForCompletion(
+                mWebServer.getAssetUrl(TestHtmlConstants.HELLO_WORLD_URL));
+        assertNull(mWebView.getCertificate());
+    }
+
+    @UiThreadTest
+    public void testSecureSiteSetsCertificate() throws Throwable {
+        if (!NullWebViewUtils.isWebViewAvailable()) {
+            return;
+        }
+        final class MockWebViewClient extends WaitForLoadedClient {
+            public MockWebViewClient() {
+                super(mOnUiThread);
+            }
+            @Override
+            public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
+                handler.proceed();
+            }
+        }
+
+        startWebServer(false);
+        mOnUiThread.loadUrlAndWaitForCompletion(
+                mWebServer.getAssetUrl(TestHtmlConstants.HELLO_WORLD_URL));
+        assertNull(mWebView.getCertificate());
+
+        stopWebServer();
+
+        startWebServer(true);
+        mOnUiThread.setWebViewClient(new MockWebViewClient());
+        mOnUiThread.loadUrlAndWaitForCompletion(
+                mWebServer.getAssetUrl(TestHtmlConstants.HELLO_WORLD_URL));
+        SslCertificate cert = mWebView.getCertificate();
+        assertNotNull(cert);
+        assertEquals("Android", cert.getIssuedTo().getUName());
+    }
+
+    @UiThreadTest
+    public void testClearSslPreferences() throws Throwable {
+        if (!NullWebViewUtils.isWebViewAvailable()) {
+            return;
+        }
+        // Load the first page. We expect a call to
+        // WebViewClient.onReceivedSslError().
+        final SslErrorWebViewClient webViewClient = new SslErrorWebViewClient(mOnUiThread);
+        startWebServer(true);
+        final String url = mWebServer.getAssetUrl(TestHtmlConstants.HELLO_WORLD_URL);
+        mOnUiThread.setWebViewClient(webViewClient);
+        mOnUiThread.clearSslPreferences();
+        mOnUiThread.loadUrlAndWaitForCompletion(url);
+        assertTrue(webViewClient.wasOnReceivedSslErrorCalled());
+
+        // Load the page again. We expect another call to
+        // WebViewClient.onReceivedSslError() since we cleared sslpreferences.
+        mOnUiThread.clearSslPreferences();
+        webViewClient.resetWasOnReceivedSslErrorCalled();
+        mOnUiThread.loadUrlAndWaitForCompletion(url);
+        assertTrue(webViewClient.wasOnReceivedSslErrorCalled());
+        assertEquals(TestHtmlConstants.HELLO_WORLD_TITLE, mOnUiThread.getTitle());
+
+        // Load the page once again, without clearing the sslpreferences.
+        // Make sure we do not get the callback.
+        webViewClient.resetWasOnReceivedSslErrorCalled();
+        mOnUiThread.loadUrlAndWaitForCompletion(url);
+        assertFalse(webViewClient.wasOnReceivedSslErrorCalled());
+        assertEquals(TestHtmlConstants.HELLO_WORLD_TITLE, mOnUiThread.getTitle());
+    }
+
+    public void testOnReceivedSslError() throws Throwable {
+        if (!NullWebViewUtils.isWebViewAvailable()) {
+            return;
+        }
+        final class MockWebViewClient extends WaitForLoadedClient {
+            private String mErrorUrl;
+            private WebView mWebView;
+
+            public MockWebViewClient() {
+                super(mOnUiThread);
+            }
+            @Override
+            public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
+                mWebView = view;
+                mErrorUrl = error.getUrl();
+                handler.proceed();
+            }
+            public String errorUrl() {
+                return mErrorUrl;
+            }
+            public WebView webView() {
+                return mWebView;
+            }
+        }
+
+        startWebServer(true);
+        final String errorUrl = mWebServer.getAssetUrl(TestHtmlConstants.HELLO_WORLD_URL);
+        final MockWebViewClient webViewClient = new MockWebViewClient();
+        mOnUiThread.setWebViewClient(webViewClient);
+        mOnUiThread.clearSslPreferences();
+        mOnUiThread.loadUrlAndWaitForCompletion(errorUrl);
+
+        assertEquals(mWebView, webViewClient.webView());
+        assertEquals(errorUrl, webViewClient.errorUrl());
+    }
+
+    public void testOnReceivedSslErrorProceed() throws Throwable {
+        if (!NullWebViewUtils.isWebViewAvailable()) {
+            return;
+        }
+        final class MockWebViewClient extends WaitForLoadedClient {
+            public MockWebViewClient() {
+                super(mOnUiThread);
+            }
+            @Override
+            public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
+                handler.proceed();
+            }
+        }
+
+        startWebServer(true);
+        final String url = mWebServer.getAssetUrl(TestHtmlConstants.HELLO_WORLD_URL);
+        mOnUiThread.setWebViewClient(new MockWebViewClient());
+        mOnUiThread.loadUrlAndWaitForCompletion(url);
+        assertEquals(TestHtmlConstants.HELLO_WORLD_TITLE, mOnUiThread.getTitle());
+    }
+
+    public void testOnReceivedSslErrorCancel() throws Throwable {
+        if (!NullWebViewUtils.isWebViewAvailable()) {
+            return;
+        }
+        final class MockWebViewClient extends WaitForLoadedClient {
+            public MockWebViewClient() {
+                super(mOnUiThread);
+            }
+            @Override
+            public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
+                handler.cancel();
+            }
+        }
+
+        startWebServer(true);
+        final String url = mWebServer.getAssetUrl(TestHtmlConstants.HELLO_WORLD_URL);
+        mOnUiThread.setWebViewClient(new MockWebViewClient());
+        mOnUiThread.clearSslPreferences();
+        mOnUiThread.loadUrlAndWaitForCompletion(url);
+        assertFalse(TestHtmlConstants.HELLO_WORLD_TITLE.equals(mOnUiThread.getTitle()));
+    }
+
+    public void testSslErrorProceedResponseReusedForSameHost() throws Throwable {
+        if (!NullWebViewUtils.isWebViewAvailable()) {
+            return;
+        }
+        // Load the first page. We expect a call to
+        // WebViewClient.onReceivedSslError().
+        final SslErrorWebViewClient webViewClient = new SslErrorWebViewClient(mOnUiThread);
+        startWebServer(true);
+        final String firstUrl = mWebServer.getAssetUrl(TestHtmlConstants.HTML_URL1);
+        mOnUiThread.setWebViewClient(webViewClient);
+        mOnUiThread.clearSslPreferences();
+        mOnUiThread.loadUrlAndWaitForCompletion(firstUrl);
+        assertTrue(webViewClient.wasOnReceivedSslErrorCalled());
+
+        // Load the second page. We don't expect a call to
+        // WebViewClient.onReceivedSslError(), but the page should load.
+        webViewClient.resetWasOnReceivedSslErrorCalled();
+        final String sameHostUrl = mWebServer.getAssetUrl(TestHtmlConstants.HTML_URL2);
+        mOnUiThread.loadUrlAndWaitForCompletion(sameHostUrl);
+        assertFalse(webViewClient.wasOnReceivedSslErrorCalled());
+        assertEquals("Second page", mOnUiThread.getTitle());
+    }
+
+    public void testSslErrorProceedResponseNotReusedForDifferentHost() throws Throwable {
+        if (!NullWebViewUtils.isWebViewAvailable()) {
+            return;
+        }
+        // Load the first page. We expect a call to
+        // WebViewClient.onReceivedSslError().
+        final SslErrorWebViewClient webViewClient = new SslErrorWebViewClient(mOnUiThread);
+        startWebServer(true);
+        final String firstUrl = mWebServer.getAssetUrl(TestHtmlConstants.HTML_URL1);
+        mOnUiThread.setWebViewClient(webViewClient);
+        mOnUiThread.clearSslPreferences();
+        mOnUiThread.loadUrlAndWaitForCompletion(firstUrl);
+        assertTrue(webViewClient.wasOnReceivedSslErrorCalled());
+
+        // Load the second page. We expect another call to
+        // WebViewClient.onReceivedSslError().
+        webViewClient.resetWasOnReceivedSslErrorCalled();
+        // The test server uses the host "localhost". "127.0.0.1" works as an
+        // alias, but will be considered unique by the WebView.
+        final String differentHostUrl = mWebServer.getAssetUrl(TestHtmlConstants.HTML_URL2).replace(
+                "localhost", "127.0.0.1");
+        mOnUiThread.loadUrlAndWaitForCompletion(differentHostUrl);
+        assertTrue(webViewClient.wasOnReceivedSslErrorCalled());
+        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);
+        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);
+        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 testProceedClientCertRequest() throws Throwable {
+        mWebServer = new CtsTestServer(getActivity(), CtsTestServer.SslMode.NEEDS_CLIENT_AUTH);
+        String url = mWebServer.getAssetUrl(TestHtmlConstants.HELLO_WORLD_URL);
+        final ClientCertWebViewClient webViewClient = new ClientCertWebViewClient(mOnUiThread);
+        mOnUiThread.setWebViewClient(webViewClient);
+        clearClientCertPreferences();
+        mOnUiThread.loadUrlAndWaitForCompletion(url);
+        assertTrue(TestHtmlConstants.HELLO_WORLD_TITLE.equals(mOnUiThread.getTitle()));
+
+        // Test that the user's response for this server is kept in cache. Load a different
+        // page from the same server and make sure we don't receive a client cert request callback.
+        int callCount = webViewClient.getClientCertRequestCount();
+        url = mWebServer.getAssetUrl(TestHtmlConstants.HTML_URL1);
+        mOnUiThread.loadUrlAndWaitForCompletion(url);
+        assertTrue(TestHtmlConstants.HTML_URL1_TITLE.equals(mOnUiThread.getTitle()));
+        assertEquals(callCount, webViewClient.getClientCertRequestCount());
+
+        // Now clear the cache and reload the page. We should receive a new callback.
+        clearClientCertPreferences();
+        mOnUiThread.loadUrlAndWaitForCompletion(url);
+        assertTrue(TestHtmlConstants.HTML_URL1_TITLE.equals(mOnUiThread.getTitle()));
+        assertEquals(callCount + 1, webViewClient.getClientCertRequestCount());
+    }
+
+    public void testIgnoreClientCertRequest() throws Throwable {
+        mWebServer = new CtsTestServer(getActivity(), CtsTestServer.SslMode.NEEDS_CLIENT_AUTH);
+        String url = mWebServer.getAssetUrl(TestHtmlConstants.HELLO_WORLD_URL);
+        final ClientCertWebViewClient webViewClient = new ClientCertWebViewClient(mOnUiThread);
+        mOnUiThread.setWebViewClient(webViewClient);
+        clearClientCertPreferences();
+        // Ignore the request. Load should fail.
+        webViewClient.setAction(ClientCertWebViewClient.IGNORE);
+        mOnUiThread.loadUrlAndWaitForCompletion(url);
+        assertFalse(TestHtmlConstants.HELLO_WORLD_TITLE.equals(mOnUiThread.getTitle()));
+        assertEquals(WebViewClient.ERROR_FAILED_SSL_HANDSHAKE, webViewClient.onReceivedErrorCode());
+
+        // Load a different page from the same domain, ignoring the request. We should get a callback,
+        // and load should fail.
+        int callCount = webViewClient.getClientCertRequestCount();
+        url = mWebServer.getAssetUrl(TestHtmlConstants.HTML_URL1);
+        mOnUiThread.loadUrlAndWaitForCompletion(url);
+        assertFalse(TestHtmlConstants.HTML_URL1_TITLE.equals(mOnUiThread.getTitle()));
+        assertEquals(WebViewClient.ERROR_FAILED_SSL_HANDSHAKE, webViewClient.onReceivedErrorCode());
+        assertEquals(callCount + 1, webViewClient.getClientCertRequestCount());
+
+        // Reload, proceeding the request. Load should succeed.
+        webViewClient.setAction(ClientCertWebViewClient.PROCEED);
+        url = mWebServer.getAssetUrl(TestHtmlConstants.HELLO_WORLD_URL);
+        mOnUiThread.loadUrlAndWaitForCompletion(url);
+        assertTrue(TestHtmlConstants.HELLO_WORLD_TITLE.equals(mOnUiThread.getTitle()));
+    }
+
+    public void testCancelClientCertRequest() throws Throwable {
+        mWebServer = new CtsTestServer(getActivity(), CtsTestServer.SslMode.NEEDS_CLIENT_AUTH);
+        final String url = mWebServer.getAssetUrl(TestHtmlConstants.HELLO_WORLD_URL);
+        final ClientCertWebViewClient webViewClient = new ClientCertWebViewClient(mOnUiThread);
+        mOnUiThread.setWebViewClient(webViewClient);
+        clearClientCertPreferences();
+        // Cancel the request. Load should fail.
+        webViewClient.setAction(ClientCertWebViewClient.CANCEL);
+        mOnUiThread.loadUrlAndWaitForCompletion(url);
+        assertFalse(TestHtmlConstants.HELLO_WORLD_TITLE.equals(mOnUiThread.getTitle()));
+        assertEquals(WebViewClient.ERROR_FAILED_SSL_HANDSHAKE, webViewClient.onReceivedErrorCode());
+
+        // Reload. The request should fail without generating a new callback.
+        int callCount = webViewClient.getClientCertRequestCount();
+        mOnUiThread.loadUrlAndWaitForCompletion(url);
+        assertEquals(callCount, webViewClient.getClientCertRequestCount());
+        assertFalse(TestHtmlConstants.HELLO_WORLD_TITLE.equals(mOnUiThread.getTitle()));
+        assertEquals(WebViewClient.ERROR_FAILED_SSL_HANDSHAKE, webViewClient.onReceivedErrorCode());
+    }
+
+    /**
+     * {@link X509TrustManager} that trusts everybody.
+     */
+    private static class TrustManager implements X509TrustManager {
+        public void checkClientTrusted(X509Certificate[] chain, String authType) {
+            // Trust the CtSTestServer's client...
+        }
+
+        public void checkServerTrusted(X509Certificate[] chain, String authType) {
+            // Trust the CtSTestServer...
+        }
+
+        public X509Certificate[] getAcceptedIssuers() {
+            try {
+                CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
+                return new X509Certificate[] {
+                        (X509Certificate) certFactory.generateCertificate(
+                                new ByteArrayInputStream(FAKE_RSA_CA_1))
+                        };
+            } catch (Exception ex) {
+                Log.e(LOGTAG, "failed creating certificate chain" + ex);
+                return null;
+            }
+        }
+    }
+
+    public void testClientCertIssuersReceivedCorrectly() throws Throwable {
+        mWebServer = new CtsTestServer(getActivity(), CtsTestServer.SslMode.NEEDS_CLIENT_AUTH,
+                new TrustManager());
+        final String url = mWebServer.getAssetUrl(TestHtmlConstants.HELLO_WORLD_URL);
+        final ClientCertWebViewClient webViewClient = new ClientCertWebViewClient(mOnUiThread);
+        mOnUiThread.setWebViewClient(webViewClient);
+        clearClientCertPreferences();
+        mOnUiThread.loadUrlAndWaitForCompletion(url);
+        // Verify that issuers sent by the server are received correctly
+        CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
+        X509Certificate  cert = (X509Certificate) certFactory.generateCertificate(
+                                new ByteArrayInputStream(FAKE_RSA_CA_1));
+        Principal[] principals = webViewClient.getPrincipals();
+        assertEquals(1, principals.length);
+        // TODO: should we issue getIssuerX500Principal instead?
+        assertEquals(cert.getIssuerDN(), principals[0]);
+    }
+
+    private void clearClientCertPreferences() {
+       final AtomicBoolean cleared = new AtomicBoolean(false);
+        mOnUiThread.clearClientCertPreferences(new Runnable() {
+            @Override
+            public void run() {
+                cleared.set(true);
+            }
+        });
+        // Wait until clearclientcertpreferences clears the preferences. Generally this is just a
+        // thread hopping.
+        new PollingCheck(WebViewTest.TEST_TIMEOUT) {
+            @Override
+            protected boolean check() {
+                return cleared.get();
+            }
+        }.run();
+    }
+
+    // Note that this class is not thread-safe.
+    static class SslErrorWebViewClient extends WaitForLoadedClient {
+        private boolean mWasOnReceivedSslErrorCalled;
+        private String mErrorUrl;
+        private int mErrorCode;
+
+        public SslErrorWebViewClient(WebViewOnUiThread onUiThread) {
+            super(onUiThread);
+        }
+        @Override
+        public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
+            mWasOnReceivedSslErrorCalled = true;
+            mErrorUrl = error.getUrl();
+            handler.proceed();
+        }
+        @Override
+        public void onReceivedError(WebView view, int errorCode, String description,
+                String failingUrl) {
+            mErrorCode = errorCode;
+        }
+        public void resetWasOnReceivedSslErrorCalled() {
+            mWasOnReceivedSslErrorCalled = false;
+        }
+        public boolean wasOnReceivedSslErrorCalled() {
+            return mWasOnReceivedSslErrorCalled;
+        }
+        public String errorUrl() {
+            return mErrorUrl;
+        }
+        public int onReceivedErrorCode() {
+            return mErrorCode;
+        }
+    }
+
+    // Modifies the default behavior of SslErrorWebViewClient to accept the request, and provide
+    // certs.
+    static class ClientCertWebViewClient extends SslErrorWebViewClient {
+        // User Actions
+        public static final int PROCEED = 1;
+        public static final int CANCEL = 2;
+        public static final int IGNORE = 3;
+
+        private int mClientCertRequests;
+        private int mAction = PROCEED;
+        private Principal[] mPrincipals;
+
+        public ClientCertWebViewClient(WebViewOnUiThread onUiThread) {
+            super(onUiThread);
+        }
+
+        public int getClientCertRequestCount() {
+            return mClientCertRequests;
+        }
+
+        public Principal[] getPrincipals() {
+            return mPrincipals;
+        }
+
+        public void resetClientCertRequestCount() {
+            mClientCertRequests = 0;
+        }
+
+        public void setAction(int action) {
+            mAction = action;
+        }
+
+        @Override
+        public void onReceivedClientCertRequest(WebView view, ClientCertRequest request) {
+            mClientCertRequests++;
+            mPrincipals = request.getPrincipals();
+            if (mAction == IGNORE) {
+                request.ignore();
+                return;
+            }
+            if (mAction == CANCEL) {
+                request.cancel();
+                return;
+            }
+            if (mAction == PROCEED) {
+                try {
+                    CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
+                    X509Certificate[] certChain =  new X509Certificate[] {
+                            (X509Certificate) certFactory.generateCertificate(
+                                    new ByteArrayInputStream(FAKE_RSA_USER_1)),
+                            (X509Certificate) certFactory.generateCertificate(
+                                    new ByteArrayInputStream(FAKE_RSA_CA_1))
+                    };
+                    KeyFactory keyFactory = KeyFactory.getInstance("RSA");
+                    PrivateKey key = keyFactory.generatePrivate(
+                        new PKCS8EncodedKeySpec(FAKE_RSA_KEY_1));
+                    request.proceed(key, certChain);
+                    return;
+                } catch (Exception e) {
+                    Log.e(LOGTAG,  "Fatal error" + e);
+                }
+            }
+            throw new IllegalStateException("unknown action");
+        }
+    }
+}
diff --git a/tests/tests/webkit/src/android/webkit/cts/WebViewTest.java b/tests/tests/webkit/src/android/webkit/cts/WebViewTest.java
index 71c2f36..c9a7de7 100755
--- a/tests/tests/webkit/src/android/webkit/cts/WebViewTest.java
+++ b/tests/tests/webkit/src/android/webkit/cts/WebViewTest.java
@@ -29,8 +29,6 @@
 import android.graphics.Picture;
 import android.graphics.Rect;
 import android.net.Uri;
-import android.net.http.SslCertificate;
-import android.net.http.SslError;
 import android.os.Bundle;
 import android.os.CancellationSignal;
 import android.os.Handler;
@@ -58,7 +56,6 @@
 import android.webkit.CookieSyncManager;
 import android.webkit.DownloadListener;
 import android.webkit.JavascriptInterface;
-import android.webkit.SslErrorHandler;
 import android.webkit.ValueCallback;
 import android.webkit.WebBackForwardList;
 import android.webkit.WebChromeClient;
@@ -75,24 +72,32 @@
 
 import junit.framework.Assert;
 
+import java.io.ByteArrayInputStream;
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.IOException;
+
 import java.util.Collections;
 import java.util.Date;
+import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicReference;
 import java.util.concurrent.Callable;
+import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.FutureTask;
 import java.util.concurrent.TimeUnit;
 import java.util.HashMap;
+import java.util.Map;
 
 import org.apache.http.Header;
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpEntityEnclosingRequest;
 import org.apache.http.HttpRequest;
+import org.apache.http.util.EncodingUtils;
+import org.apache.http.util.EntityUtils;
 
 public class WebViewTest extends ActivityInstrumentationTestCase2<WebViewStubActivity> {
-    private static final String LOGTAG = "WebViewTest";
+    public static final long TEST_TIMEOUT = 20000L;
     private static final int INITIAL_PROGRESS = 100;
-    private static long TEST_TIMEOUT = 20000L;
     private static final String X_REQUESTED_WITH = "X-Requested-With";
     private static final String PRINTER_TEST_FILE = "print.pdf";
     private static final String PDF_PREAMBLE = "%PDF-1";
@@ -147,7 +152,7 @@
             mOnUiThread.cleanUp();
         }
         if (mWebServer != null) {
-            mWebServer.shutdown();
+            stopWebServer();
         }
         if (mIconDb != null) {
             mIconDb.removeAllIcons();
@@ -399,6 +404,36 @@
     }
 
     @UiThreadTest
+    public void testPostUrlWithNonNetworkUrl() throws Exception {
+        final String nonNetworkUrl = "file:///android_asset/" + TestHtmlConstants.HELLO_WORLD_URL;
+
+        mOnUiThread.postUrlAndWaitForCompletion(nonNetworkUrl, new byte[1]);
+
+        // Test if the nonNetworkUrl is loaded
+        assertEquals(TestHtmlConstants.HELLO_WORLD_TITLE, mWebView.getTitle());
+    }
+
+    @UiThreadTest
+    public void testPostUrlWithNetworkUrl() throws Exception {
+        startWebServer(false);
+        final String networkUrl = mWebServer.getAssetUrl(TestHtmlConstants.HELLO_WORLD_URL);
+        final String postDataString = "username=my_username&password=my_password";
+        final byte[] postData = EncodingUtils.getBytes(postDataString, "BASE64");
+
+        mOnUiThread.postUrlAndWaitForCompletion(networkUrl, postData);
+
+        HttpRequest request = mWebServer.getLastRequest(TestHtmlConstants.HELLO_WORLD_URL);
+        // The last request should be POST
+        assertEquals(request.getRequestLine().getMethod(), "POST");
+
+        // The last request should have a request body
+        assertTrue(request instanceof HttpEntityEnclosingRequest);
+        HttpEntity entity = ((HttpEntityEnclosingRequest) request).getEntity();
+        String entityString = EntityUtils.toString(entity);
+        assertEquals(entityString, postDataString);
+    }
+
+    @UiThreadTest
     public void testLoadUrlDoesNotStripParamsWhenLoadingContentUrls() throws Exception {
         if (!NullWebViewUtils.isWebViewAvailable()) {
             return;
@@ -460,6 +495,28 @@
         assertEquals(requester, header.getValue());
     }
 
+    public void testCanInjectHeaders() throws Exception {
+        final String X_FOO = "X-foo";
+        final String X_FOO_VALUE = "test";
+
+        final String X_REFERER = "Referer";
+        final String X_REFERER_VALUE = "http://www.example.com/";
+        startWebServer(false);
+        String url = mWebServer.getAssetUrl(TestHtmlConstants.HELLO_WORLD_URL);
+        HashMap<String, String> map = new HashMap<String, String>();
+        map.put(X_FOO, X_FOO_VALUE);
+        map.put(X_REFERER, X_REFERER_VALUE);
+        mOnUiThread.loadUrlAndWaitForCompletion(url, map);
+
+        HttpRequest request = mWebServer.getLastRequest(TestHtmlConstants.HELLO_WORLD_URL);
+        for (Map.Entry<String,String> value : map.entrySet()) {
+            String header = value.getKey();
+            Header[] matchingHeaders = request.getHeaders(header);
+            assertEquals("header " + header + " not found", 1, matchingHeaders.length);
+            assertEquals(value.getValue(), matchingHeaders[0].getValue());
+        }
+    }
+
     @SuppressWarnings("deprecation")
     @UiThreadTest
     public void testGetVisibleTitleHeight() throws Exception {
@@ -634,6 +691,30 @@
         String url = mWebServer.getAssetUrl(TestHtmlConstants.ADD_JAVA_SCRIPT_INTERFACE_URL);
         mOnUiThread.loadUrlAndWaitForCompletion(url);
         assertEquals("Original title", obj.waitForResult());
+
+        // Verify that only methods annotated with @JavascriptInterface are exposed
+        // on the JavaScript interface object.
+        mOnUiThread.evaluateJavascript("typeof dummy.provideResult",
+                new ValueCallback<String>() {
+                    @Override
+                    public void onReceiveValue(String result) {
+                        assertEquals("\"function\"", result);
+                    }
+                });
+        mOnUiThread.evaluateJavascript("typeof dummy.wasProvideResultCalled",
+                new ValueCallback<String>() {
+                    @Override
+                    public void onReceiveValue(String result) {
+                        assertEquals("\"undefined\"", result);
+                    }
+                });
+        mOnUiThread.evaluateJavascript("typeof dummy.getClass",
+                new ValueCallback<String>() {
+                    @Override
+                    public void onReceiveValue(String result) {
+                        assertEquals("\"undefined\"", result);
+                    }
+                });
     }
 
     @UiThreadTest
@@ -748,6 +829,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 testJavascriptInterfaceCustomPropertiesClearedOnReload() throws Exception {
         if (!NullWebViewUtils.isWebViewAvailable()) {
             return;
@@ -776,6 +893,66 @@
         jsResult.run();
     }
 
+    public void testJavascriptInterfaceForClientPopup() throws Exception {
+        if (!NullWebViewUtils.isWebViewAvailable()) {
+            return;
+        }
+
+        mOnUiThread.getSettings().setJavaScriptEnabled(true);
+        mOnUiThread.getSettings().setJavaScriptCanOpenWindowsAutomatically(true);
+        mOnUiThread.getSettings().setSupportMultipleWindows(true);
+
+        class DummyJavaScriptInterface {
+            @JavascriptInterface
+            public int test() {
+                return 42;
+            }
+        }
+        final DummyJavaScriptInterface obj = new DummyJavaScriptInterface();
+
+        final WebView childWebView = mOnUiThread.createWebView();
+        WebViewOnUiThread childOnUiThread = new WebViewOnUiThread(this, childWebView);
+        childOnUiThread.getSettings().setJavaScriptEnabled(true);
+        childOnUiThread.addJavascriptInterface(obj, "dummy");
+
+        final boolean[] hadOnCreateWindow = new boolean[1];
+        hadOnCreateWindow[0] = false;
+        mOnUiThread.setWebChromeClient(new WebViewOnUiThread.WaitForProgressClient(mOnUiThread) {
+            @Override
+            public boolean onCreateWindow(
+                WebView view, boolean isDialog, boolean isUserGesture, Message resultMsg) {
+                getActivity().addContentView(childWebView, new ViewGroup.LayoutParams(
+                            ViewGroup.LayoutParams.FILL_PARENT,
+                            ViewGroup.LayoutParams.WRAP_CONTENT));
+                WebView.WebViewTransport transport = (WebView.WebViewTransport) resultMsg.obj;
+                transport.setWebView(childWebView);
+                resultMsg.sendToTarget();
+                hadOnCreateWindow[0] = true;
+                return true;
+            }
+        });
+
+        startWebServer(false);
+        mOnUiThread.loadUrlAndWaitForCompletion(mWebServer.
+                getAssetUrl(TestHtmlConstants.POPUP_URL));
+        new PollingCheck(TEST_TIMEOUT) {
+            @Override
+            protected boolean check() {
+                return hadOnCreateWindow[0];
+            }
+        }.run();
+
+        childOnUiThread.loadUrlAndWaitForCompletion("about:blank");
+        EvaluateJsResultPollingCheck jsResult;
+        jsResult = new EvaluateJsResultPollingCheck("true");
+        childOnUiThread.evaluateJavascript("'dummy' in window", jsResult);
+        jsResult.run();
+        // Verify that the injected object is functional.
+        jsResult = new EvaluateJsResultPollingCheck("42");
+        childOnUiThread.evaluateJavascript("dummy.test()", jsResult);
+        jsResult.run();
+    }
+
     private final class TestPictureListener implements PictureListener {
         public int callCount;
 
@@ -1003,7 +1180,6 @@
         // Verify that the resource request makes it to the server.
         assertTrue(mWebServer.wasResourceRequested(imgUrl));
         assertEquals(historyUrl, mWebView.getUrl());
-        assertEquals(historyUrl, mWebView.getOriginalUrl());
 
         // Check that reported URL is "about:blank" when supplied history URL
         // is null.
@@ -1013,7 +1189,6 @@
                 "text/html", "UTF-8", null);
         assertTrue(mWebServer.wasResourceRequested(imgUrl));
         assertEquals("about:blank", mWebView.getUrl());
-        assertEquals("about:blank", mWebView.getOriginalUrl());
 
         // Test that JavaScript can access content from the same origin as the base URL.
         mWebView.getSettings().setJavaScriptEnabled(true);
@@ -1038,19 +1213,18 @@
         mOnUiThread.loadDataWithBaseURLAndWaitForCompletion("http://www.foo.com",
                 HTML_HEADER + "<title>Hello World%21</title><body>bar</body></html>",
                 "text/html", "UTF-8", null);
-        assertEquals("Hello World%21", mWebView.getTitle());
+        assertEquals("Hello World%21", mOnUiThread.getTitle());
 
         // Check that when a data: base URL is used, we treat the String to load as a data: URL
         // and run load steps such as decoding URL entities (i.e., contrary to the test case
         // above.)
         mOnUiThread.loadDataWithBaseURLAndWaitForCompletion("data:foo",
                 HTML_HEADER + "<title>Hello World%21</title></html>", "text/html", "UTF-8", null);
-        assertEquals("Hello World!", mWebView.getTitle());
+        assertEquals("Hello World!", mOnUiThread.getTitle());
 
         // Check the method is null input safe.
         mOnUiThread.loadDataWithBaseURLAndWaitForCompletion(null, null, null, null, null);
-        assertEquals("about:blank", mWebView.getUrl());
-        assertEquals("about:blank", mWebView.getOriginalUrl());
+        assertEquals("about:blank", mOnUiThread.getUrl());
     }
 
     private static class WaitForFindResultsListener extends FutureTask<Integer>
@@ -1640,19 +1814,6 @@
     }
 
     @UiThreadTest
-    public void testGetFavicon() throws Exception {
-        if (!NullWebViewUtils.isWebViewAvailable()) {
-            return;
-        }
-        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 {
         if (!NullWebViewUtils.isWebViewAvailable()) {
             return;
@@ -1720,7 +1881,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
@@ -1741,7 +1902,6 @@
         assertEquals(url1, copyListAfterRestore.getItemAtIndex(0).getUrl());
         assertEquals(url2, copyListAfterRestore.getItemAtIndex(1).getUrl());
         assertEquals(url3, copyListAfterRestore.getItemAtIndex(2).getUrl());
-        */
     }
 
     public void testSetWebViewClient() throws Throwable {
@@ -1761,227 +1921,6 @@
         webViewClient.waitForScaleChanged();
     }
 
-    @UiThreadTest
-    public void testInsecureSiteClearsCertificate() throws Throwable {
-        if (!NullWebViewUtils.isWebViewAvailable()) {
-            return;
-        }
-        final class MockWebViewClient extends WaitForLoadedClient {
-            public MockWebViewClient() {
-                super(mOnUiThread);
-            }
-            @Override
-            public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
-                handler.proceed();
-            }
-        }
-
-        startWebServer(true);
-        mOnUiThread.setWebViewClient(new MockWebViewClient());
-        mOnUiThread.loadUrlAndWaitForCompletion(
-                mWebServer.getAssetUrl(TestHtmlConstants.HELLO_WORLD_URL));
-        SslCertificate cert = mWebView.getCertificate();
-        assertNotNull(cert);
-        assertEquals("Android", cert.getIssuedTo().getUName());
-
-        stopWebServer();
-
-        startWebServer(false);
-        mOnUiThread.loadUrlAndWaitForCompletion(
-                mWebServer.getAssetUrl(TestHtmlConstants.HELLO_WORLD_URL));
-        assertNull(mWebView.getCertificate());
-    }
-
-    @UiThreadTest
-    public void testSecureSiteSetsCertificate() throws Throwable {
-        if (!NullWebViewUtils.isWebViewAvailable()) {
-            return;
-        }
-        final class MockWebViewClient extends WaitForLoadedClient {
-            public MockWebViewClient() {
-                super(mOnUiThread);
-            }
-            @Override
-            public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
-                handler.proceed();
-            }
-        }
-
-        startWebServer(false);
-        mOnUiThread.loadUrlAndWaitForCompletion(
-                mWebServer.getAssetUrl(TestHtmlConstants.HELLO_WORLD_URL));
-        assertNull(mWebView.getCertificate());
-
-        stopWebServer();
-
-        startWebServer(true);
-        mOnUiThread.setWebViewClient(new MockWebViewClient());
-        mOnUiThread.loadUrlAndWaitForCompletion(
-                mWebServer.getAssetUrl(TestHtmlConstants.HELLO_WORLD_URL));
-        SslCertificate cert = mWebView.getCertificate();
-        assertNotNull(cert);
-        assertEquals("Android", cert.getIssuedTo().getUName());
-    }
-
-    @UiThreadTest
-    public void testClearSslPreferences() throws Throwable {
-        if (!NullWebViewUtils.isWebViewAvailable()) {
-            return;
-        }
-        // Load the first page. We expect a call to
-        // WebViewClient.onReceivedSslError().
-        final SslErrorWebViewClient webViewClient = new SslErrorWebViewClient();
-        startWebServer(true);
-        final String url = mWebServer.getAssetUrl(TestHtmlConstants.HELLO_WORLD_URL);
-        mOnUiThread.setWebViewClient(webViewClient);
-        mOnUiThread.clearSslPreferences();
-        mOnUiThread.loadUrlAndWaitForCompletion(url);
-        assertTrue(webViewClient.wasOnReceivedSslErrorCalled());
-
-        // Load the page again. We expect another call to
-        // WebViewClient.onReceivedSslError() since we cleared sslpreferences.
-        mOnUiThread.clearSslPreferences();
-        webViewClient.resetWasOnReceivedSslErrorCalled();
-        mOnUiThread.loadUrlAndWaitForCompletion(url);
-        assertTrue(webViewClient.wasOnReceivedSslErrorCalled());
-        assertEquals(TestHtmlConstants.HELLO_WORLD_TITLE, mOnUiThread.getTitle());
-
-        // Load the page once again, without clearing the sslpreferences.
-        // Make sure we do not get the callback.
-        webViewClient.resetWasOnReceivedSslErrorCalled();
-        mOnUiThread.loadUrlAndWaitForCompletion(url);
-        assertFalse(webViewClient.wasOnReceivedSslErrorCalled());
-        assertEquals(TestHtmlConstants.HELLO_WORLD_TITLE, mOnUiThread.getTitle());
-    }
-
-    public void testOnReceivedSslError() throws Throwable {
-        if (!NullWebViewUtils.isWebViewAvailable()) {
-            return;
-        }
-        final class MockWebViewClient extends WaitForLoadedClient {
-            private String mErrorUrl;
-            private WebView mWebView;
-
-            public MockWebViewClient() {
-                super(mOnUiThread);
-            }
-            @Override
-            public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
-                mWebView = view;
-                mErrorUrl = error.getUrl();
-                handler.proceed();
-            }
-            public String errorUrl() {
-                return mErrorUrl;
-            }
-            public WebView webView() {
-                return mWebView;
-            }
-        }
-
-        startWebServer(true);
-        final String errorUrl = mWebServer.getAssetUrl(TestHtmlConstants.HELLO_WORLD_URL);
-        final MockWebViewClient webViewClient = new MockWebViewClient();
-        mOnUiThread.setWebViewClient(webViewClient);
-        mOnUiThread.clearSslPreferences();
-        mOnUiThread.loadUrlAndWaitForCompletion(errorUrl);
-
-        assertEquals(mWebView, webViewClient.webView());
-        assertEquals(errorUrl, webViewClient.errorUrl());
-    }
-
-    public void testOnReceivedSslErrorProceed() throws Throwable {
-        if (!NullWebViewUtils.isWebViewAvailable()) {
-            return;
-        }
-        final class MockWebViewClient extends WaitForLoadedClient {
-            public MockWebViewClient() {
-                super(mOnUiThread);
-            }
-            @Override
-            public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
-                handler.proceed();
-            }
-        }
-
-        startWebServer(true);
-        final String url = mWebServer.getAssetUrl(TestHtmlConstants.HELLO_WORLD_URL);
-        mOnUiThread.setWebViewClient(new MockWebViewClient());
-        mOnUiThread.loadUrlAndWaitForCompletion(url);
-        assertEquals(TestHtmlConstants.HELLO_WORLD_TITLE, mOnUiThread.getTitle());
-    }
-
-    public void testOnReceivedSslErrorCancel() throws Throwable {
-        if (!NullWebViewUtils.isWebViewAvailable()) {
-            return;
-        }
-        final class MockWebViewClient extends WaitForLoadedClient {
-            public MockWebViewClient() {
-                super(mOnUiThread);
-            }
-            @Override
-            public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
-                handler.cancel();
-            }
-        }
-
-        startWebServer(true);
-        final String url = mWebServer.getAssetUrl(TestHtmlConstants.HELLO_WORLD_URL);
-        mOnUiThread.setWebViewClient(new MockWebViewClient());
-        mOnUiThread.clearSslPreferences();
-        mOnUiThread.loadUrlAndWaitForCompletion(url);
-        assertFalse(TestHtmlConstants.HELLO_WORLD_TITLE.equals(mOnUiThread.getTitle()));
-    }
-
-    public void testSslErrorProceedResponseReusedForSameHost() throws Throwable {
-        if (!NullWebViewUtils.isWebViewAvailable()) {
-            return;
-        }
-        // Load the first page. We expect a call to
-        // WebViewClient.onReceivedSslError().
-        final SslErrorWebViewClient webViewClient = new SslErrorWebViewClient();
-        startWebServer(true);
-        final String firstUrl = mWebServer.getAssetUrl(TestHtmlConstants.HTML_URL1);
-        mOnUiThread.setWebViewClient(webViewClient);
-        mOnUiThread.clearSslPreferences();
-        mOnUiThread.loadUrlAndWaitForCompletion(firstUrl);
-        assertTrue(webViewClient.wasOnReceivedSslErrorCalled());
-
-        // Load the second page. We don't expect a call to
-        // WebViewClient.onReceivedSslError(), but the page should load.
-        webViewClient.resetWasOnReceivedSslErrorCalled();
-        final String sameHostUrl = mWebServer.getAssetUrl(TestHtmlConstants.HTML_URL2);
-        mOnUiThread.loadUrlAndWaitForCompletion(sameHostUrl);
-        assertFalse(webViewClient.wasOnReceivedSslErrorCalled());
-        assertEquals("Second page", mOnUiThread.getTitle());
-    }
-
-    public void testSslErrorProceedResponseNotReusedForDifferentHost() throws Throwable {
-        if (!NullWebViewUtils.isWebViewAvailable()) {
-            return;
-        }
-        // Load the first page. We expect a call to
-        // WebViewClient.onReceivedSslError().
-        final SslErrorWebViewClient webViewClient = new SslErrorWebViewClient();
-        startWebServer(true);
-        final String firstUrl = mWebServer.getAssetUrl(TestHtmlConstants.HTML_URL1);
-        mOnUiThread.setWebViewClient(webViewClient);
-        mOnUiThread.clearSslPreferences();
-        mOnUiThread.loadUrlAndWaitForCompletion(firstUrl);
-        assertTrue(webViewClient.wasOnReceivedSslErrorCalled());
-
-        // Load the second page. We expect another call to
-        // WebViewClient.onReceivedSslError().
-        webViewClient.resetWasOnReceivedSslErrorCalled();
-        // The test server uses the host "localhost". "127.0.0.1" works as an
-        // alias, but will be considered unique by the WebView.
-        final String differentHostUrl = mWebServer.getAssetUrl(TestHtmlConstants.HTML_URL2).replace(
-                "localhost", "127.0.0.1");
-        mOnUiThread.loadUrlAndWaitForCompletion(differentHostUrl);
-        assertTrue(webViewClient.wasOnReceivedSslErrorCalled());
-        assertEquals("Second page", mOnUiThread.getTitle());
-    }
-
     public void testRequestChildRectangleOnScreen() throws Throwable {
         if (!NullWebViewUtils.isWebViewAvailable()) {
             return;
@@ -2012,12 +1951,13 @@
         if (!NullWebViewUtils.isWebViewAvailable()) {
             return;
         }
+
+        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,
@@ -2026,7 +1966,7 @@
                 this.mimeType = mimetype;
                 this.contentLength = contentLength;
                 this.contentDisposition = contentDisposition;
-                this.called = true;
+                resultLatch.countDown();
             }
         }
 
@@ -2043,27 +1983,17 @@
         // the WebView will load the new URL.
         mOnUiThread.setDownloadListener(listener);
         mOnUiThread.getSettings().setJavaScriptEnabled(true);
-
-        // See b/13675265 for discussion on why the setTimeout is necessary.
-        // Works around a Blink bug.
         mOnUiThread.loadDataAndWaitForCompletion(
-                "<html><body onload=\"setTimeout(" +
-                "function() { window.location = \'" + url + "\'; }, 100);\">" +
-                "</body></html>", "text/html", null);
+                "<html><body onload=\"window.location = \'" + url + "\'\"></body></html>",
+                "text/html", null);
         // 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
@@ -2353,11 +2283,6 @@
         });
     }
 
-    @UiThreadTest
-    public void testInternals() {
-        // Do not test these APIs. They are implementation details.
-    }
-
     private static class HrefCheckHandler extends Handler {
         private boolean mHadRecieved;
 
@@ -2452,51 +2377,6 @@
         return true;
     }
 
-    // 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
@@ -2522,31 +2402,6 @@
         }
     }
 
-    // Note that this class is not thread-safe.
-    final class SslErrorWebViewClient extends WaitForLoadedClient {
-        private boolean mWasOnReceivedSslErrorCalled;
-        private String mErrorUrl;
-
-        public SslErrorWebViewClient() {
-            super(mOnUiThread);
-        }
-        @Override
-        public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
-            mWasOnReceivedSslErrorCalled = true;
-            mErrorUrl = error.getUrl();
-            handler.proceed();
-        }
-        public void resetWasOnReceivedSslErrorCalled() {
-            mWasOnReceivedSslErrorCalled = false;
-        }
-        public boolean wasOnReceivedSslErrorCalled() {
-            return mWasOnReceivedSslErrorCalled;
-        }
-        public String errorUrl() {
-            return mErrorUrl;
-        }
-    }
-
     private void pollingCheckForCanZoomIn() {
         new PollingCheck(TEST_TIMEOUT) {
             @Override
diff --git a/tests/tests/widget/AndroidManifest.xml b/tests/tests/widget/AndroidManifest.xml
index e69a7d5..3c7fe5f 100644
--- a/tests/tests/widget/AndroidManifest.xml
+++ b/tests/tests/widget/AndroidManifest.xml
@@ -23,9 +23,12 @@
         <uses-library android:name="android.test.runner" />
     </application>
 
-    <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
                      android:targetPackage="com.android.cts.stub"
-                     android:label="CTS tests of android.widget"/>
+                     android:label="CTS tests of android.widget">
+        <meta-data android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
 
 </manifest>
 
diff --git a/tests/tests/widget/src/android/widget/cts/AbsSeekBarTest.java b/tests/tests/widget/src/android/widget/cts/AbsSeekBarTest.java
index 4881a24..58230e5 100644
--- a/tests/tests/widget/src/android/widget/cts/AbsSeekBarTest.java
+++ b/tests/tests/widget/src/android/widget/cts/AbsSeekBarTest.java
@@ -16,6 +16,11 @@
 
 package android.widget.cts;
 
+import android.content.res.ColorStateList;
+import android.graphics.Color;
+import android.graphics.PorterDuff;
+import android.test.UiThreadTest;
+
 import com.android.cts.stub.R;
 
 
@@ -29,7 +34,6 @@
 import android.test.ActivityInstrumentationTestCase2;
 import android.util.AttributeSet;
 import android.view.KeyEvent;
-import android.view.MotionEvent;
 import android.widget.AbsSeekBar;
 import android.widget.SeekBar;
 
@@ -147,7 +151,7 @@
         // AbsSeekBar is an abstract class, use its subclass: SeekBar to do this test.
         runTestOnUiThread(new Runnable() {
             public void run() {
-                mActivity.setContentView(R.layout.seekbar);
+                mActivity.setContentView(R.layout.seekbar_layout);
             }
         });
         getInstrumentation().waitForIdleSync();
@@ -207,6 +211,34 @@
         assertEquals(keyProgressIncrement + 1, myAbsSeekBar.getKeyProgressIncrement());
     }
 
+    @UiThreadTest
+    public void testThumbTint() {
+        mActivity.setContentView(R.layout.seekbar_layout);
+
+        SeekBar inflatedView = (SeekBar) mActivity.findViewById(R.id.thumb_tint);
+
+        assertEquals("Thumb tint inflated correctly",
+                Color.WHITE, inflatedView.getThumbTintList().getDefaultColor());
+        assertEquals("Thumb tint mode inflated correctly",
+                PorterDuff.Mode.SRC_OVER, inflatedView.getThumbTintMode());
+
+        MockDrawable thumb = new MockDrawable();
+        SeekBar view = new SeekBar(mActivity);
+
+        view.setThumb(thumb);
+        assertFalse("No thumb tint applied by default", thumb.hasCalledSetTint());
+
+        view.setThumbTintList(ColorStateList.valueOf(Color.WHITE));
+        assertTrue("Thumb tint applied when setThumbTintList() called after setThumb()",
+                thumb.hasCalledSetTint());
+
+        thumb.reset();
+        view.setThumb(null);
+        view.setThumb(thumb);
+        assertTrue("Thumb tint applied when setThumbTintList() called before setThumb()",
+                thumb.hasCalledSetTint());
+    }
+
     public void testFoo() {
         // Do not test these APIs. They are callbacks which:
         // 1. The callback machanism has been tested in super class
@@ -240,6 +272,7 @@
     private static class MockDrawable extends Drawable {
         private int mAlpha;
         private boolean mCalledDraw = false;
+        private boolean mCalledSetTint = false;
 
         @Override
         public void draw(Canvas canvas) {
@@ -252,6 +285,7 @@
 
         public void reset() {
             mCalledDraw = false;
+            mCalledSetTint = false;
         }
 
         @Override
@@ -269,7 +303,16 @@
         }
 
         @Override
-        public void setColorFilter(ColorFilter cf) {
+        public void setColorFilter(ColorFilter cf) { }
+
+        @Override
+        public void setTintList(ColorStateList tint) {
+            super.setTintList(tint);
+            mCalledSetTint = true;
+        }
+
+        public boolean hasCalledSetTint() {
+            return mCalledSetTint;
         }
     }
 }
diff --git a/tests/tests/widget/src/android/widget/cts/CompoundButtonTest.java b/tests/tests/widget/src/android/widget/cts/CompoundButtonTest.java
index 252e237..6674ef6 100644
--- a/tests/tests/widget/src/android/widget/cts/CompoundButtonTest.java
+++ b/tests/tests/widget/src/android/widget/cts/CompoundButtonTest.java
@@ -16,6 +16,12 @@
 
 package android.widget.cts;
 
+import android.content.res.ColorStateList;
+import android.graphics.Color;
+import android.graphics.ColorFilter;
+import android.graphics.PorterDuff;
+import android.view.LayoutInflater;
+import android.widget.ToggleButton;
 import com.android.cts.stub.R;
 
 
@@ -321,6 +327,65 @@
         assertTrue(compoundButton.verifyDrawable(drawable));
     }
 
+    public void testButtonTint() {
+        LayoutInflater inflater = LayoutInflater.from(mContext);
+        View layout = inflater.inflate(R.layout.togglebutton_layout, null);
+        CompoundButton inflatedView = (CompoundButton) layout.findViewById(R.id.button_tint);
+
+        assertEquals("Button tint inflated correctly",
+                Color.WHITE, inflatedView.getButtonTintList().getDefaultColor());
+        assertEquals("Button tint mode inflated correctly",
+                PorterDuff.Mode.SRC_OVER, inflatedView.getButtonTintMode());
+
+        MockDrawable button = new MockDrawable();
+        CompoundButton view = new ToggleButton(mContext);
+
+        view.setButtonDrawable(button);
+        assertFalse("No button tint applied by default", button.hasCalledSetTint());
+
+        view.setButtonTintList(ColorStateList.valueOf(Color.WHITE));
+        assertTrue("Button tint applied when setButtonTintList() called after setButton()",
+                button.hasCalledSetTint());
+
+        button.reset();
+        view.setButtonDrawable(null);
+        view.setButtonDrawable(button);
+        assertTrue("Button tint applied when setButtonTintList() called before setButton()",
+                button.hasCalledSetTint());
+    }
+
+    private static class MockDrawable extends Drawable {
+        private boolean mCalledSetTint = false;
+
+        @Override
+        public void draw(Canvas canvas) {}
+
+        @Override
+        public void setAlpha(int alpha) {}
+
+        @Override
+        public void setColorFilter(ColorFilter cf) {}
+
+        @Override
+        public void setTintList(ColorStateList tint) {
+            super.setTintList(tint);
+            mCalledSetTint = true;
+        }
+
+        @Override
+        public int getOpacity() {
+            return 0;
+        }
+
+        public boolean hasCalledSetTint() {
+            return mCalledSetTint;
+        }
+
+        public void reset() {
+            mCalledSetTint = false;
+        }
+    }
+
     private final class MockCompoundButton extends CompoundButton {
         public MockCompoundButton(Context context) {
             super(context);
diff --git a/tests/tests/widget/src/android/widget/cts/FrameLayoutTest.java b/tests/tests/widget/src/android/widget/cts/FrameLayoutTest.java
index 6564ce0..b1a0d44 100644
--- a/tests/tests/widget/src/android/widget/cts/FrameLayoutTest.java
+++ b/tests/tests/widget/src/android/widget/cts/FrameLayoutTest.java
@@ -16,6 +16,12 @@
 
 package android.widget.cts;
 
+import android.content.res.ColorStateList;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.ColorFilter;
+import android.graphics.PorterDuff;
+
 import com.android.cts.stub.R;
 
 import org.xmlpull.v1.XmlPullParser;
@@ -268,6 +274,31 @@
         assertTrue(myFrameLayout.verifyDrawable(null));
     }
 
+    public void testForegroundTint() {
+        FrameLayout inflatedView = (FrameLayout) mActivity.findViewById(R.id.foreground_tint);
+
+        assertEquals("Foreground tint inflated correctly",
+                Color.WHITE, inflatedView.getForegroundTintList().getDefaultColor());
+        assertEquals("Foreground tint mode inflated correctly",
+                PorterDuff.Mode.SRC_OVER, inflatedView.getForegroundTintMode());
+
+        MockDrawable foreground = new MockDrawable();
+        FrameLayout view = new FrameLayout(mActivity);
+
+        view.setForeground(foreground);
+        assertFalse("No foreground tint applied by default", foreground.hasCalledSetTint());
+
+        view.setForegroundTintList(ColorStateList.valueOf(Color.WHITE));
+        assertTrue("Foreground tint applied when setForegroundTintList() called after setForeground()",
+                foreground.hasCalledSetTint());
+
+        foreground.reset();
+        view.setForeground(null);
+        view.setForeground(foreground);
+        assertTrue("Foreground tint applied when setForegroundTintList() called before setForeground()",
+                foreground.hasCalledSetTint());
+    }
+
     private static void assertCenterAligned(View container, Drawable drawable) {
         Rect rect = drawable.getBounds();
         int leftDelta = rect.left - container.getLeft();
@@ -285,6 +316,38 @@
         return Xml.asAttributeSet(parser);
     }
 
+    private static class MockDrawable extends Drawable {
+        private boolean mCalledSetTint = false;
+
+        @Override
+        public void draw(Canvas canvas) {}
+
+        @Override
+        public void setAlpha(int alpha) {}
+
+        @Override
+        public void setColorFilter(ColorFilter cf) {}
+
+        @Override
+        public void setTintList(ColorStateList tint) {
+            super.setTintList(tint);
+            mCalledSetTint = true;
+        }
+
+        @Override
+        public int getOpacity() {
+            return 0;
+        }
+
+        public boolean hasCalledSetTint() {
+            return mCalledSetTint;
+        }
+
+        public void reset() {
+            mCalledSetTint = false;
+        }
+    }
+
     private static class MyFrameLayout extends FrameLayout {
         public MyFrameLayout(Context context) {
             super(context);
diff --git a/tests/tests/widget/src/android/widget/cts/ImageViewTest.java b/tests/tests/widget/src/android/widget/cts/ImageViewTest.java
index a213f05..84276f9 100644
--- a/tests/tests/widget/src/android/widget/cts/ImageViewTest.java
+++ b/tests/tests/widget/src/android/widget/cts/ImageViewTest.java
@@ -22,6 +22,9 @@
 import java.io.InputStream;
 import java.io.OutputStream;
 
+import android.content.res.ColorStateList;
+import android.graphics.Color;
+
 import org.xmlpull.v1.XmlPullParser;
 
 import android.app.Activity;
@@ -437,6 +440,31 @@
         assertTrue(mockImageView.verifyDrawable(bgdrawable));
     }
 
+    public void testImageTint() {
+        ImageView inflatedView = (ImageView) mActivity.findViewById(R.id.image_tint);
+
+        assertEquals("Image tint inflated correctly",
+                Color.WHITE, inflatedView.getImageTintList().getDefaultColor());
+        assertEquals("Image tint mode inflated correctly",
+                PorterDuff.Mode.SRC_OVER, inflatedView.getImageTintMode());
+
+        MockDrawable image = new MockDrawable();
+        ImageView view = new ImageView(mActivity);
+
+        view.setImageDrawable(image);
+        assertFalse("No image tint applied by default", image.hasCalledSetTint());
+
+        view.setImageTintList(ColorStateList.valueOf(Color.WHITE));
+        assertTrue("Image tint applied when setImageTintList() called after set()",
+                image.hasCalledSetTint());
+
+        image.reset();
+        view.setImageDrawable(null);
+        view.setImageDrawable(image);
+        assertTrue("Image tint applied when setImageTintList() called before set()",
+                image.hasCalledSetTint());
+    }
+
     private static class MockImageView extends ImageView {
         private boolean mOnSizeChangedCalled = false;
 
@@ -500,6 +528,7 @@
     private class MockDrawable extends Drawable {
         private ColorFilter mColorFilter;
         private boolean mDrawCalled = false;
+        private boolean mCalledSetTint = false;
         private int mAlpha;
 
         public boolean hasDrawCalled() {
@@ -537,5 +566,19 @@
         public boolean isStateful() {
             return true;
         }
+
+        @Override
+        public void setTintList(ColorStateList tint) {
+            super.setTintList(tint);
+            mCalledSetTint = true;
+        }
+
+        public boolean hasCalledSetTint() {
+            return mCalledSetTint;
+        }
+
+        public void reset() {
+            mCalledSetTint = false;
+        }
     }
 }
diff --git a/tests/tests/widget/src/android/widget/cts/ProgressBarTest.java b/tests/tests/widget/src/android/widget/cts/ProgressBarTest.java
index 8a0b9a0..80681d0 100644
--- a/tests/tests/widget/src/android/widget/cts/ProgressBarTest.java
+++ b/tests/tests/widget/src/android/widget/cts/ProgressBarTest.java
@@ -16,6 +16,11 @@
 
 package android.widget.cts;
 
+import android.content.res.ColorStateList;
+import android.graphics.Color;
+import android.graphics.PorterDuff;
+import android.view.LayoutInflater;
+
 import com.android.cts.stub.R;
 
 
@@ -314,8 +319,81 @@
         // Do not test, it's controlled by View. Implementation details
     }
 
+    public void testProgressTint() {
+        LayoutInflater inflater = LayoutInflater.from(mContext);
+        View layout = inflater.inflate(R.layout.progressbar_layout, null);
+        ProgressBar inflatedView = (ProgressBar) layout.findViewById(R.id.progress_tint);
+
+        assertEquals("Progress tint inflated correctly",
+                Color.WHITE, inflatedView.getProgressTintList().getDefaultColor());
+        assertEquals("Progress tint mode inflated correctly",
+                PorterDuff.Mode.SRC_OVER, inflatedView.getProgressTintMode());
+
+        assertEquals("Progress background tint inflated correctly",
+                Color.WHITE, inflatedView.getProgressBackgroundTintList().getDefaultColor());
+        assertEquals("Progress background tint mode inflated correctly",
+                PorterDuff.Mode.SRC_OVER, inflatedView.getProgressBackgroundTintMode());
+
+        assertEquals("Secondary progress tint inflated correctly",
+                Color.WHITE, inflatedView.getSecondaryProgressTintList().getDefaultColor());
+        assertEquals("Secondary progress tint mode inflated correctly",
+                PorterDuff.Mode.SRC_OVER, inflatedView.getSecondaryProgressTintMode());
+
+        MockDrawable progress = new MockDrawable();
+        ProgressBar view = new ProgressBar(mContext);
+
+        view.setProgressDrawable(progress);
+        assertFalse("No progress tint applied by default", progress.hasCalledSetTint());
+
+        view.setProgressBackgroundTintList(ColorStateList.valueOf(Color.WHITE));
+        assertFalse("Progress background tint not applied when layer missing",
+                progress.hasCalledSetTint());
+
+        view.setSecondaryProgressTintList(ColorStateList.valueOf(Color.WHITE));
+        assertFalse("Secondary progress tint not applied when layer missing",
+                progress.hasCalledSetTint());
+
+        view.setProgressTintList(ColorStateList.valueOf(Color.WHITE));
+        assertTrue("Progress tint applied when setProgressTintList() called after setProgress()",
+                progress.hasCalledSetTint());
+
+        progress.reset();
+        view.setProgressDrawable(null);
+        view.setProgressDrawable(progress);
+        assertTrue("Progress tint applied when setProgressTintList() called before setProgress()",
+                progress.hasCalledSetTint());
+    }
+
+    public void testIndeterminateTint() {
+        LayoutInflater inflater = LayoutInflater.from(mContext);
+        View layout = inflater.inflate(R.layout.progressbar_layout, null);
+        ProgressBar inflatedView = (ProgressBar) layout.findViewById(R.id.indeterminate_tint);
+
+        assertEquals("Indeterminate tint inflated correctly",
+                Color.WHITE, inflatedView.getIndeterminateTintList().getDefaultColor());
+        assertEquals("Indeterminate tint mode inflated correctly",
+                PorterDuff.Mode.SRC_OVER, inflatedView.getIndeterminateTintMode());
+
+        MockDrawable indeterminate = new MockDrawable();
+        ProgressBar view = new ProgressBar(mContext);
+
+        view.setIndeterminateDrawable(indeterminate);
+        assertFalse("No indeterminate tint applied by default", indeterminate.hasCalledSetTint());
+
+        view.setIndeterminateTintList(ColorStateList.valueOf(Color.WHITE));
+        assertTrue("Indeterminate tint applied when setIndeterminateTintList() called after "
+                + "setIndeterminate()", indeterminate.hasCalledSetTint());
+
+        indeterminate.reset();
+        view.setIndeterminateDrawable(null);
+        view.setIndeterminateDrawable(indeterminate);
+        assertTrue("Indeterminate tint applied when setIndeterminateTintList() called before "
+                + "setIndeterminate()", indeterminate.hasCalledSetTint());
+    }
+
     private class MockDrawable extends Drawable {
         private boolean mCalledDraw = false;
+        private boolean mCalledSetTint = false;
 
         @Override
         public void draw(Canvas canvas) {
@@ -335,12 +413,23 @@
         public void setColorFilter(ColorFilter cf) {
         }
 
+        @Override
+        public void setTintList(ColorStateList tint) {
+            super.setTintList(tint);
+            mCalledSetTint = true;
+        }
+
+        public boolean hasCalledSetTint() {
+            return mCalledSetTint;
+        }
+
         public boolean hasCalledDraw() {
             return mCalledDraw;
         }
 
         public void reset() {
             mCalledDraw = false;
+            mCalledSetTint = false;
         }
 
     }
diff --git a/tools/cts-holo-generation/Android.mk b/tools/cts-holo-generation/Android.mk
index 5f12ef0..2affc5e 100644
--- a/tools/cts-holo-generation/Android.mk
+++ b/tools/cts-holo-generation/Android.mk
@@ -23,7 +23,7 @@
 LOCAL_DEX_PREOPT := false
 LOCAL_PROGUARD_ENABLED := disabled
 
-LOCAL_JAVA_LIBRARIES := android.test.runner
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
diff --git a/tools/cts-holo-generation/AndroidManifest.xml b/tools/cts-holo-generation/AndroidManifest.xml
index d7de891..ad4ae22 100644
--- a/tools/cts-holo-generation/AndroidManifest.xml
+++ b/tools/cts-holo-generation/AndroidManifest.xml
@@ -7,7 +7,7 @@
     <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
 
     <instrumentation
-        android:name="android.test.InstrumentationTestRunner"
+        android:name="android.support.test.runner.AndroidJUnitRunner"
         android:targetPackage="com.android.cts.holo_capture" />
 
     <application>
diff --git a/tools/cts-java-scanner-doclet/src/com/android/cts/javascannerdoclet/CtsJavaScannerDoclet.java b/tools/cts-java-scanner-doclet/src/com/android/cts/javascannerdoclet/CtsJavaScannerDoclet.java
index 40a3d05..f54ba00 100644
--- a/tools/cts-java-scanner-doclet/src/com/android/cts/javascannerdoclet/CtsJavaScannerDoclet.java
+++ b/tools/cts-java-scanner-doclet/src/com/android/cts/javascannerdoclet/CtsJavaScannerDoclet.java
@@ -61,6 +61,8 @@
  */
 public class CtsJavaScannerDoclet extends Doclet {
 
+    private static final String JUNIT4_TEST_ANNOTATION = "org.junit.Test";
+
     static final String JUNIT_TEST_CASE_CLASS_NAME = "junit.framework.testcase";
 
     public static boolean start(RootDoc root) {
@@ -72,31 +74,61 @@
         PrintWriter writer = new PrintWriter(System.out);
 
         for (ClassDoc clazz : classes) {
-            if (clazz.isAbstract() || !isValidJUnitTestCase(clazz)) {
+            if (clazz.isAbstract()) {
                 continue;
             }
+
+            final boolean isJUnit3 = isJUnit3TestCase(clazz);
+            if (!isJUnit3 && !isJUnit4TestClass(clazz)) {
+                continue;
+            }
+
             writer.append("suite:").println(clazz.containingPackage().name());
             writer.append("case:").println(clazz.name());
             for (; clazz != null; clazz = clazz.superclass()) {
                 for (MethodDoc method : clazz.methods()) {
-                    if (!method.name().startsWith("test")) {
-                        continue;
-                    }
                     int timeout = -1;
-                    AnnotationDesc[] annotations = method.annotations();
-                    for (AnnotationDesc annot : annotations) {
-                        AnnotationTypeDoc atype = annot.annotationType();
-                        if (atype.toString().equals("com.android.cts.util.TimeoutReq")) {
-                            ElementValuePair[] cpairs = annot.elementValues();
-                            for (ElementValuePair pair: cpairs) {
-                                AnnotationTypeElementDoc elem = pair.element();
-                                AnnotationValue value = pair.value();
-                                if (elem.name().equals("minutes")) {
-                                    timeout = ((Integer)value.value());
+                    if (isJUnit3) {
+                        if (!method.name().startsWith("test")) {
+                            continue;
+                        }
+
+                        AnnotationDesc[] annotations = method.annotations();
+                        for (AnnotationDesc annot : annotations) {
+                            String atype = annot.annotationType().toString();
+                            if (atype.equals("android.cts.util.TimeoutReq")) {
+                                ElementValuePair[] cpairs = annot.elementValues();
+                                for (ElementValuePair pair : cpairs) {
+                                    AnnotationTypeElementDoc elem = pair.element();
+                                    AnnotationValue value = pair.value();
+                                    if (elem.name().equals("minutes")) {
+                                        timeout = ((Integer) value.value());
+                                    }
                                 }
                             }
                         }
+                    } else {
+                        /* JUnit4 */
+                        boolean isTest = false;
+
+                        for (AnnotationDesc annot : method.annotations()) {
+                            if (annot.annotationType().toString().equals(JUNIT4_TEST_ANNOTATION)) {
+                                isTest = true;
+
+                                for (ElementValuePair pair : annot.elementValues()) {
+                                    if (pair.element().name().equals("timeout")) {
+                                        /* JUnit4 timeouts are in milliseconds. */
+                                        timeout = (int) (((Long) pair.value().value()) / 60000L);
+                                    }
+                                }
+                            }
+                        }
+
+                        if (!isTest) {
+                            continue;
+                        }
                     }
+
                     writer.append("test:");
                     if (timeout >= 0) {
                         writer.append(method.name()).println(":" + timeout);
@@ -111,7 +143,7 @@
         return true;
     }
 
-    private static boolean isValidJUnitTestCase(ClassDoc clazz) {
+    private static boolean isJUnit3TestCase(ClassDoc clazz) {
         while((clazz = clazz.superclass()) != null) {
             if (JUNIT_TEST_CASE_CLASS_NAME.equals(clazz.qualifiedName().toLowerCase())) {
                 return true;
@@ -119,4 +151,15 @@
         }
         return false;
     }
+
+    private static boolean isJUnit4TestClass(ClassDoc clazz) {
+        for (MethodDoc method : clazz.methods()) {
+            for (AnnotationDesc annot : method.annotations()) {
+                if (annot.annotationType().toString().equals(JUNIT4_TEST_ANNOTATION)) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
 }
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..06951b9 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,6 @@
         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(sourceDir.toString());
         return join(sourcePath, ":");
     }
@@ -86,6 +84,7 @@
     private String getClassPath() {
         List<String> classPath = new ArrayList<String>();
         classPath.add("./prebuilts/misc/common/tradefed/tradefed-prebuilt.jar");
+        classPath.add("./prebuilts/misc/common/ub-uiautomator/ub-uiautomator.jar");
         return join(classPath, ":");
     }
 
diff --git a/tools/cts-reference-app-lib/Android.mk b/tools/cts-reference-app-lib/Android.mk
index 8341970..fae85b4 100644
--- a/tools/cts-reference-app-lib/Android.mk
+++ b/tools/cts-reference-app-lib/Android.mk
@@ -24,7 +24,7 @@
 # and when built explicitly put it in the data partition
 LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
 
-LOCAL_JAVA_LIBRARIES := android.test.runner
+LOCAL_SDK_VERSION := current
 
 LOCAL_MODULE := android.cts.refapp
 
diff --git a/tools/cts-xml-generator/src/com/android/cts/xmlgenerator/CtsXmlGenerator.java b/tools/cts-xml-generator/src/com/android/cts/xmlgenerator/CtsXmlGenerator.java
index c5b253a..1cf5a7e 100644
--- a/tools/cts-xml-generator/src/com/android/cts/xmlgenerator/CtsXmlGenerator.java
+++ b/tools/cts-xml-generator/src/com/android/cts/xmlgenerator/CtsXmlGenerator.java
@@ -24,7 +24,9 @@
 
 import java.io.File;
 import java.util.Arrays;
+import java.util.HashMap;
 import java.util.HashSet;
+import java.util.Map;
 import java.util.Set;
 
 import javax.xml.parsers.DocumentBuilderFactory;
@@ -36,7 +38,7 @@
         System.err.println("Arguments: " + Arrays.asList(args));
         System.err.println("Usage: cts-xml-generator -p PACKAGE_NAME -n NAME [-t TEST_TYPE]"
                 + " [-j JAR_PATH] [-i INSTRUMENTATION] [-m MANIFEST_FILE] [-e EXPECTATION_FILE]"
-                + " [-o OUTPUT_FILE]");
+                + " [-o OUTPUT_FILE] [-a APP_NAME_SPACE] [-x ADDITIONAL_ATTRIBUTE_KEY->VALUE]");
         System.exit(1);
     }
 
@@ -51,6 +53,7 @@
         String jarPath = null;
         String appNameSpace = null;
         String targetNameSpace = null;
+        Map<String, String> additionalAttributes = new HashMap<String, String>();
 
         for (int i = 0; i < args.length; i++) {
             if ("-p".equals(args[i])) {
@@ -74,6 +77,20 @@
                 appNameSpace =  getArg(args, ++i, "Missing value for app name space");
             } else if ("-r".equals(args[i])) {
                 targetNameSpace =  getArg(args, ++i, "Missing value for target name space");
+            } else if ("-x".equals(args[i])) {
+                String value = getArg(args, ++i, "Missing value for additional attribute");
+                String[] tokens = value.split("->");
+                if (tokens.length != 2) {
+                    System.err.println(
+                            "For specifying additional attributes; use the format KEY->VALUE");
+                    usage(args);
+                }
+                if (additionalAttributes.containsKey(tokens[0])) {
+                    System.err.println(String.format(
+                            "Additional attribute %s has already been specified", tokens[0]));
+                    usage(args);
+                }
+                additionalAttributes.put(tokens[0], tokens[1]);
             } else {
                 System.err.println("Unsupported flag: " + args[i]);
                 usage(args);
@@ -103,7 +120,8 @@
 
         ExpectationStore store = ExpectationStore.parse(expectationFiles, ModeId.DEVICE);
         XmlGenerator generator = new XmlGenerator(store, appNameSpace, appPackageName,
-                name, runner, instrumentation, targetNameSpace, jarPath, testType, outputPath);
+                name, runner, instrumentation, targetNameSpace, jarPath, testType, outputPath,
+                additionalAttributes);
         generator.writePackageXml();
     }
 
diff --git a/tools/cts-xml-generator/src/com/android/cts/xmlgenerator/XmlGenerator.java b/tools/cts-xml-generator/src/com/android/cts/xmlgenerator/XmlGenerator.java
index 7b1996d..b1a006e 100644
--- a/tools/cts-xml-generator/src/com/android/cts/xmlgenerator/XmlGenerator.java
+++ b/tools/cts-xml-generator/src/com/android/cts/xmlgenerator/XmlGenerator.java
@@ -27,6 +27,7 @@
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
+import java.util.Map;
 import java.util.List;
 
 /**
@@ -72,9 +73,12 @@
     /** ExpectationStore to filter out known failures. */
     private final ExpectationStore mExpectations;
 
+    private final Map<String, String> mAdditionalAttributes;
+
     XmlGenerator(ExpectationStore expectations, String appNameSpace, String appPackageName,
             String name, String runner, String targetBinaryName, String targetNameSpace,
-            String jarPath, String testType, String outputPath) {
+            String jarPath, String testType, String outputPath,
+            Map<String, String> additionalAttributes) {
         mAppNamespace = appNameSpace;
         mAppPackageName = appPackageName;
         mName = name;
@@ -85,6 +89,7 @@
         mTestType = testType;
         mOutputPath = outputPath;
         mExpectations = expectations;
+        mAdditionalAttributes = additionalAttributes;
     }
 
     public void writePackageXml() throws IOException {
@@ -133,6 +138,10 @@
             writer.append(" jarPath=\"").append(mJarPath).append("\"");
         }
 
+        for (Map.Entry<String, String> entry : mAdditionalAttributes.entrySet()) {
+            writer.append(String.format(" %s=\"%s\"", entry.getKey(), entry.getValue()));
+        }
+
         writer.println(" version=\"1.0\">");
 
         TestListParser parser = new TestListParser();
diff --git a/tools/device-setup/TestDeviceSetup/Android.mk b/tools/device-setup/TestDeviceSetup/Android.mk
index ba1998c..44e66bb 100644
--- a/tools/device-setup/TestDeviceSetup/Android.mk
+++ b/tools/device-setup/TestDeviceSetup/Android.mk
@@ -25,8 +25,6 @@
 LOCAL_DEX_PREOPT := false
 LOCAL_PROGUARD_ENABLED := disabled
 
-LOCAL_JAVA_LIBRARIES := android.test.runner
-
 LOCAL_SDK_VERSION := current
 
 LOCAL_PACKAGE_NAME := TestDeviceSetup
diff --git a/tools/junit/Android.mk b/tools/junit/Android.mk
new file mode 100644
index 0000000..b581149
--- /dev/null
+++ b/tools/junit/Android.mk
@@ -0,0 +1,23 @@
+# 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_SRC_FILES :=  $(call all-java-files-under, src)
+LOCAL_MODULE := cts-junit
+LOCAL_MODULE_TAGS := optional
+LOCAL_STATIC_JAVA_LIBRARIES := junit4-target
+LOCAL_DEX_PREOPT := false
+include $(BUILD_JAVA_LIBRARY)
diff --git a/tools/junit/src/com/android/cts/junit/SingleJUnitTestRunListener.java b/tools/junit/src/com/android/cts/junit/SingleJUnitTestRunListener.java
new file mode 100644
index 0000000..06e9bc4
--- /dev/null
+++ b/tools/junit/src/com/android/cts/junit/SingleJUnitTestRunListener.java
@@ -0,0 +1,67 @@
+/*
+ * 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.junit;
+
+import org.junit.runner.Description;
+import org.junit.runner.Result;
+import org.junit.runner.notification.Failure;
+import org.junit.runner.notification.RunListener;
+
+public class SingleJUnitTestRunListener extends RunListener {
+    private static class Prefixes {
+        @SuppressWarnings("unused")
+        private static final String INFORMATIONAL_MARKER = "[----------]";
+        private static final String START_TEST_RUN_MARKER = "[==========] Running";
+        private static final String TEST_RUN_MARKER = "[==========]";
+        private static final String START_TEST_MARKER = "[ RUN      ]";
+        private static final String OK_TEST_MARKER = "[       OK ]";
+        private static final String FAILED_TEST_MARKER = "[  FAILED  ]";
+    }
+
+    @Override
+    public void testRunStarted(Description description) throws Exception {
+    }
+
+    @Override
+    public void testRunFinished(Result result) throws Exception {
+        String status = result.wasSuccessful() ? Prefixes.OK_TEST_MARKER
+                : Prefixes.FAILED_TEST_MARKER;
+        System.out.println(status);
+    }
+
+    @Override
+    public void testStarted(Description description) throws Exception {
+        System.out.println(String.format("%s %s.%s", Prefixes.START_TEST_MARKER,
+                description.getClassName(), description.getMethodName()));
+    }
+
+    @Override
+    public void testFinished(Description description) throws Exception {
+    }
+
+    @Override
+    public void testFailure(Failure failure) throws Exception {
+    }
+
+    @Override
+    public void testAssumptionFailure(Failure failure) {
+    }
+
+    @Override
+    public void testIgnored(Description description) throws Exception {
+    }
+}
diff --git a/tools/junit/src/com/android/cts/junit/SingleJUnitTestRunner.java b/tools/junit/src/com/android/cts/junit/SingleJUnitTestRunner.java
new file mode 100644
index 0000000..2eb9f3a
--- /dev/null
+++ b/tools/junit/src/com/android/cts/junit/SingleJUnitTestRunner.java
@@ -0,0 +1,48 @@
+/*
+ * 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.junit;
+
+import org.junit.runner.JUnitCore;
+import org.junit.runner.Request;
+import org.junit.runner.Result;
+
+/**
+ * Test runner to run a single JUnit test. It will output either [PASSED] or [FAILED] at the end.
+ */
+public class SingleJUnitTestRunner {
+    private static String mUsage = "Usage: java -cp <classpath> SingleJUnitTestRunner" +
+            " class#testmethod";
+    private static final String PASSED_TEST_MARKER = "[ PASSED ]";
+    private static final String FAILED_TEST_MARKER = "[ FAILED ]";
+
+    public static void main(String... args) throws ClassNotFoundException {
+        if (args.length != 1) {
+            throw new IllegalArgumentException(mUsage);
+        }
+        String[] classAndMethod = args[0].split("#");
+        if (classAndMethod.length != 2) {
+            throw new IllegalArgumentException(mUsage);
+        }
+        Request request = Request.method(Class.forName(classAndMethod[0]),
+                classAndMethod[1]);
+        JUnitCore jUnitCore = new JUnitCore();
+        Result result = jUnitCore.run(request);
+        String status = result.wasSuccessful() ? PASSED_TEST_MARKER : FAILED_TEST_MARKER;
+        System.out.println(String.format("%s %s.%s", status,
+                classAndMethod[0], classAndMethod[1]));
+    }
+}
diff --git a/tools/tradefed-host/.classpath b/tools/tradefed-host/.classpath
index 0b1866d..b82e340 100644
--- a/tools/tradefed-host/.classpath
+++ b/tools/tradefed-host/.classpath
@@ -2,10 +2,11 @@
 <classpath>
 	<classpathentry kind="src" path="src"/>
 	<classpathentry kind="src" path="res"/>
+	<classpathentry kind="src" path="commonutil-src"/>
 	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
 	<classpathentry exported="true" kind="var" path="CTS_SRC_ROOT/out/host/common/obj/JAVA_LIBRARIES/ctsdeviceinfolib_intermediates/javalib.jar"/>
 	<classpathentry exported="true" kind="var" path="CTS_SRC_ROOT/out/host/common/obj/JAVA_LIBRARIES/hosttestlib_intermediates/javalib.jar"/>
 	<classpathentry kind="var" path="CTS_SRC_ROOT/out/host/common/obj/JAVA_LIBRARIES/ddmlib-prebuilt_intermediates/ddmlib-prebuilt.jar" sourcepath="/SDK_SRC_ROOT"/>
-	<classpathentry kind="var" path="CTS_SRC_ROOT/out/host/common/obj/JAVA_LIBRARIES/tradefed-prebuilt_intermediates/tradefed-prebuilt.jar" sourcepath="/TRADEFED_ROOT/tools/tradefederation/src"/>
+        <classpathentry kind="var" path="CTS_SRC_ROOT/out/host/common/obj/JAVA_LIBRARIES/tradefed-prebuilt_intermediates/tradefed-prebuilt.jar" sourcepath="/TRADEFED_ROOT/tools/tradefederation/src"/>
 	<classpathentry kind="output" path="bin"/>
 </classpath>
diff --git a/tools/tradefed-host/.project b/tools/tradefed-host/.project
index 990c63e..ffd16af 100644
--- a/tools/tradefed-host/.project
+++ b/tools/tradefed-host/.project
@@ -14,4 +14,11 @@
 	<natures>
 		<nature>org.eclipse.jdt.core.javanature</nature>
 	</natures>
+	<linkedResources>
+		<link>
+			<name>commonutil-src</name>
+			<type>2</type>
+			<locationURI>CTS_SRC_ROOT/cts/libs/commonutil/src</locationURI>
+		</link>
+	</linkedResources>
 </projectDescription>
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/res/config/cts.xml b/tools/tradefed-host/res/config/cts.xml
index 158f49d..416b400 100644
--- a/tools/tradefed-host/res/config/cts.xml
+++ b/tools/tradefed-host/res/config/cts.xml
@@ -22,6 +22,7 @@
     <test class="com.android.cts.tradefed.testtype.CtsTest" />
     <logger class="com.android.tradefed.log.FileLogger" />
     <result_reporter class="com.android.cts.tradefed.result.CtsXmlResultReporter" />
+    <result_reporter class="com.android.cts.tradefed.result.CtsTestLogReporter" />
     <result_reporter class="com.android.cts.tradefed.result.IssueReporter" />
 
 </configuration>
diff --git a/tools/tradefed-host/src/com/android/cts/tradefed/build/CtsBuildProvider.java b/tools/tradefed-host/src/com/android/cts/tradefed/build/CtsBuildProvider.java
index e924f48..ccdb0eb 100644
--- a/tools/tradefed-host/src/com/android/cts/tradefed/build/CtsBuildProvider.java
+++ b/tools/tradefed-host/src/com/android/cts/tradefed/build/CtsBuildProvider.java
@@ -31,7 +31,7 @@
     @Option(name="cts-install-path", description="the path to the cts installation to use")
     private String mCtsRootDirPath = System.getProperty("CTS_ROOT");
 
-    public static final String CTS_BUILD_VERSION = "4.4W_r1";
+    public static final String CTS_BUILD_VERSION = "4.4_r3";
 
     /**
      * {@inheritDoc}
diff --git a/tools/tradefed-host/src/com/android/cts/tradefed/result/CtsTestLogReporter.java b/tools/tradefed-host/src/com/android/cts/tradefed/result/CtsTestLogReporter.java
new file mode 100644
index 0000000..bbdcb05
--- /dev/null
+++ b/tools/tradefed-host/src/com/android/cts/tradefed/result/CtsTestLogReporter.java
@@ -0,0 +1,137 @@
+/*
+ * 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.result;
+
+import com.android.cts.tradefed.device.DeviceInfoCollector;
+import com.android.ddmlib.Log;
+import com.android.ddmlib.Log.LogLevel;
+import com.android.ddmlib.testrunner.TestIdentifier;
+import com.android.tradefed.build.IBuildInfo;
+import com.android.tradefed.config.Option;
+import com.android.tradefed.config.OptionCopier;
+import com.android.tradefed.log.LogUtil.CLog;
+import com.android.tradefed.result.IShardableListener;
+import com.android.tradefed.result.StubTestInvocationListener;
+
+import java.util.Map;
+
+/**
+ * Dumps tests in progress to stdout
+ */
+public class CtsTestLogReporter extends StubTestInvocationListener implements IShardableListener {
+
+    @Option(name = "quiet-output", description = "Mute display of test results.")
+    private boolean mQuietOutput = false;
+
+    protected IBuildInfo mBuildInfo;
+    private String mDeviceSerial;
+    private TestResults mResults = new TestResults();
+    private TestPackageResult mCurrentPkgResult = null;
+    private boolean mIsDeviceInfoRun = false;
+
+    @Override
+    public void invocationStarted(IBuildInfo buildInfo) {
+        mDeviceSerial = buildInfo.getDeviceSerial() == null ? "unknown_device" : buildInfo.getDeviceSerial();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void testRunStarted(String name, int numTests) {
+        if (mCurrentPkgResult != null && !name.equals(mCurrentPkgResult.getAppPackageName())) {
+            // display results from previous run
+            logCompleteRun(mCurrentPkgResult);
+        }
+        mIsDeviceInfoRun = name.equals(DeviceInfoCollector.APP_PACKAGE_NAME);
+        if (mIsDeviceInfoRun) {
+            logResult("Collecting device info");
+        } else  {
+            if (mCurrentPkgResult == null || !name.equals(mCurrentPkgResult.getAppPackageName())) {
+                logResult("-----------------------------------------");
+                logResult("Test package %s started", name);
+                logResult("-----------------------------------------");
+            }
+            mCurrentPkgResult = mResults.getOrCreatePackage(name);
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void testStarted(TestIdentifier test) {
+        mCurrentPkgResult.insertTest(test);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void testFailed(TestFailure status, TestIdentifier test, String trace) {
+        mCurrentPkgResult.reportTestFailure(test, CtsTestStatus.FAIL, trace);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void testEnded(TestIdentifier test, Map<String, String> testMetrics) {
+        mCurrentPkgResult.reportTestEnded(test);
+        Test result = mCurrentPkgResult.findTest(test);
+        String stack = result.getStackTrace() == null ? "" : "\n" + result.getStackTrace();
+        logResult("%s#%s %s %s", test.getClassName(), test.getTestName(), result.getResult(),
+                stack);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void invocationEnded(long elapsedTime) {
+        // display the results of the last completed run
+        if (mCurrentPkgResult != null) {
+            logCompleteRun(mCurrentPkgResult);
+        }
+    }
+
+    private void logResult(String format, Object... args) {
+        if (mQuietOutput) {
+            CLog.i(format, args);
+        } else {
+            Log.logAndDisplay(LogLevel.INFO, mDeviceSerial, String.format(format, args));
+        }
+    }
+
+    private void logCompleteRun(TestPackageResult pkgResult) {
+        if (pkgResult.getAppPackageName().equals(DeviceInfoCollector.APP_PACKAGE_NAME)) {
+            logResult("Device info collection complete");
+            return;
+        }
+        logResult("%s package complete: Passed %d, Failed %d, Not Executed %d",
+                pkgResult.getAppPackageName(), pkgResult.countTests(CtsTestStatus.PASS),
+                pkgResult.countTests(CtsTestStatus.FAIL),
+                pkgResult.countTests(CtsTestStatus.NOT_EXECUTED));
+    }
+
+    @Override
+    public IShardableListener clone() {
+        CtsTestLogReporter clone = new CtsTestLogReporter();
+        OptionCopier.copyOptionsNoThrow(this, clone);
+        return clone;
+    }
+}
diff --git a/tools/tradefed-host/src/com/android/cts/tradefed/result/CtsXmlResultReporter.java b/tools/tradefed-host/src/com/android/cts/tradefed/result/CtsXmlResultReporter.java
index 70310ca..956f1b1 100644
--- a/tools/tradefed-host/src/com/android/cts/tradefed/result/CtsXmlResultReporter.java
+++ b/tools/tradefed-host/src/com/android/cts/tradefed/result/CtsXmlResultReporter.java
@@ -26,12 +26,15 @@
 import com.android.tradefed.build.IBuildInfo;
 import com.android.tradefed.build.IFolderBuildInfo;
 import com.android.tradefed.config.Option;
+import com.android.tradefed.config.Option.Importance;
 import com.android.tradefed.log.LogUtil.CLog;
 import com.android.tradefed.result.ITestInvocationListener;
+import com.android.tradefed.result.ITestSummaryListener;
 import com.android.tradefed.result.InputStreamSource;
 import com.android.tradefed.result.LogDataType;
 import com.android.tradefed.result.LogFileSaver;
 import com.android.tradefed.result.TestSummary;
+import com.android.tradefed.util.AbiFormatter;
 import com.android.tradefed.util.FileUtil;
 import com.android.tradefed.util.StreamUtil;
 
@@ -43,6 +46,7 @@
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
+import java.util.List;
 import java.util.Map;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
@@ -54,7 +58,7 @@
  * <p/>
  * Outputs xml in format governed by the cts_result.xsd
  */
-public class CtsXmlResultReporter implements ITestInvocationListener {
+public class CtsXmlResultReporter implements ITestInvocationListener, ITestSummaryListener {
     private static final String LOG_TAG = "CtsXmlResultReporter";
 
     static final String TEST_RESULT_FILE_NAME = "testResult.xml";
@@ -69,6 +73,9 @@
     static final String PLAN_ATTR = "testPlan";
     static final String STARTTIME_ATTR = "starttime";
 
+    @Option(name = "quiet-output", description = "Mute display of test results.")
+    private boolean mQuietOutput = false;
+
     private static final String REPORT_DIR_NAME = "output-file-path";
     @Option(name=REPORT_DIR_NAME, description="root file system path to directory to store xml " +
             "test results and associated logs. If not specified, results will be stored at " +
@@ -83,9 +90,6 @@
     @Option(name = CtsTest.CONTINUE_OPTION, description = "the test result session to continue.")
     private Integer mContinueSessionId = null;
 
-    @Option(name = "quiet-output", description = "Mute display of test results.")
-    private boolean mQuietOutput = false;
-
     @Option(name = "result-server", description = "Server to publish test results.")
     private String mResultServer;
 
@@ -98,9 +102,15 @@
     private ResultReporter mReporter;
     private File mLogDir;
     private String mSuiteName;
+    private String mReferenceUrl;
 
     private static final Pattern mCtsLogPattern = Pattern.compile("(.*)\\+\\+\\+\\+(.*)");
 
+    @Option(name = AbiFormatter.FORCE_ABI_STRING,
+            description = AbiFormatter.FORCE_ABI_DESCRIPTION,
+            importance = Importance.IF_UNSET)
+    private String mForceAbi = null;
+
     public void setReportDir(File reportDir) {
         mReportDir = reportDir;
     }
@@ -220,27 +230,11 @@
         return new LogFileSaver(mLogDir);
     }
 
-    /**
-     * {@inheritDoc}
-     */
+
     @Override
     public void testRunStarted(String name, int numTests) {
-        if (mCurrentPkgResult != null && !name.equals(mCurrentPkgResult.getAppPackageName())) {
-            // display results from previous run
-            logCompleteRun(mCurrentPkgResult);
-        }
+        mCurrentPkgResult = mResults.getOrCreatePackage(name);
         mIsDeviceInfoRun = name.equals(DeviceInfoCollector.APP_PACKAGE_NAME);
-        if (mIsDeviceInfoRun) {
-            logResult("Collecting device info");
-        } else  {
-            if (mCurrentPkgResult == null || !name.equals(mCurrentPkgResult.getAppPackageName())) {
-                logResult("-----------------------------------------");
-                logResult("Test package %s started", name);
-                logResult("-----------------------------------------");
-            }
-            mCurrentPkgResult = mResults.getOrCreatePackage(name);
-        }
-
     }
 
     /**
@@ -266,10 +260,6 @@
     public void testEnded(TestIdentifier test, Map<String, String> testMetrics) {
         collectCtsResults(test, testMetrics);
         mCurrentPkgResult.reportTestEnded(test);
-        Test result = mCurrentPkgResult.findTest(test);
-        String stack = result.getStackTrace() == null ? "" : "\n" + result.getStackTrace();
-        logResult("%s#%s %s %s", test.getClassName(), test.getTestName(), result.getResult(),
-                stack);
     }
 
     /**
@@ -314,10 +304,6 @@
      */
     @Override
     public void invocationEnded(long elapsedTime) {
-        // display the results of the last completed run
-        if (mCurrentPkgResult != null) {
-            logCompleteRun(mCurrentPkgResult);
-        }
         if (mReportDir == null || mStartTime == null) {
             // invocationStarted must have failed, abort
             CLog.w("Unable to create XML report");
@@ -330,7 +316,7 @@
         zipResults(mReportDir);
 
         try {
-            mReporter.reportResult(reportFile);
+            mReporter.reportResult(reportFile, mReferenceUrl);
         } catch (IOException e) {
             CLog.e(e);
         }
@@ -344,17 +330,6 @@
         }
     }
 
-    private void logCompleteRun(TestPackageResult pkgResult) {
-        if (pkgResult.getAppPackageName().equals(DeviceInfoCollector.APP_PACKAGE_NAME)) {
-            logResult("Device info collection complete");
-            return;
-        }
-        logResult("%s package complete: Passed %d, Failed %d, Not Executed %d",
-                pkgResult.getAppPackageName(), pkgResult.countTests(CtsTestStatus.PASS),
-                pkgResult.countTests(CtsTestStatus.FAIL),
-                pkgResult.countTests(CtsTestStatus.NOT_EXECUTED));
-    }
-
     /**
      * Creates a report file and populates it with the report data from the completed tests.
      */
@@ -382,7 +357,7 @@
         } catch (IOException e) {
             Log.e(LOG_TAG, "Failed to generate report data");
         } finally {
-            StreamUtil.closeStream(stream);
+            StreamUtil.close(stream);
         }
     }
 
@@ -402,7 +377,9 @@
         serializer.attribute(ns, "endtime", endTime);
         serializer.attribute(ns, "version", CTS_RESULT_FILE_VERSION);
         serializer.attribute(ns, "suite", mSuiteName);
-
+        if (mForceAbi != null) {
+            serializer.attribute(ns, "abi", mForceAbi);
+        }
         mResults.serialize(serializer);
         // TODO: not sure why, but the serializer doesn't like this statement
         //serializer.endTag(ns, RESULT_TAG);
@@ -498,4 +475,17 @@
     public TestSummary getSummary() {
         return null;
     }
+
+    /**
+     * {@inheritDoc}
+     */
+     @Override
+     public void putSummary(List<TestSummary> summaries) {
+         // By convention, only store the first summary that we see as the summary URL.
+         if (summaries.isEmpty()) {
+             return;
+         }
+
+         mReferenceUrl = summaries.get(0).getSummary().getString();
+     }
 }
diff --git a/tools/tradefed-host/src/com/android/cts/tradefed/result/ResultReporter.java b/tools/tradefed-host/src/com/android/cts/tradefed/result/ResultReporter.java
index 199cbc5..39969d3 100644
--- a/tools/tradefed-host/src/com/android/cts/tradefed/result/ResultReporter.java
+++ b/tools/tradefed-host/src/com/android/cts/tradefed/result/ResultReporter.java
@@ -21,6 +21,8 @@
 import java.io.IOException;
 import java.io.InputStream;
 
+import javax.annotation.Nullable;
+
 /**
  * Class that sends a HTTP POST multipart/form-data request containing
  * the test result XML.
@@ -37,7 +39,7 @@
         mSuiteName = suiteName;
     }
 
-    public void reportResult(File reportFile) throws IOException {
+    public void reportResult(File reportFile, @Nullable String referenceUrl) throws IOException {
         if (isEmpty(mServerUrl)) {
             return;
         }
@@ -45,16 +47,19 @@
         InputStream input = new FileInputStream(reportFile);
         try {
             byte[] data = IssueReporter.getBytes(input, RESULT_XML_BYTES);
-            new MultipartForm(mServerUrl)
+            MultipartForm multipartForm = new MultipartForm(mServerUrl)
                     .addFormValue("suite", mSuiteName)
-                    .addFormFile("resultXml", "testResult.xml.gz", data)
-                    .submit();
+                    .addFormFile("resultXml", "testResult.xml.gz", data);
+            if (!isEmpty(referenceUrl)) {
+                multipartForm.addFormValue("referenceUrl", referenceUrl);
+            }
+            multipartForm.submit();
         } finally {
             input.close();
         }
     }
 
-    private boolean isEmpty(String value) {
+    private static boolean isEmpty(String value) {
         return value == null || value.trim().isEmpty();
     }
 }
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 48f4773..de868d1 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
@@ -26,7 +26,9 @@
 import com.android.tradefed.build.IBuildInfo;
 import com.android.tradefed.config.ConfigurationException;
 import com.android.tradefed.config.Option;
+import com.android.tradefed.config.OptionSetter;
 import com.android.tradefed.config.Option.Importance;
+import com.android.tradefed.config.OptionCopier;
 import com.android.tradefed.device.DeviceNotAvailableException;
 import com.android.tradefed.device.ITestDevice;
 import com.android.tradefed.device.TestDeviceOptions;
@@ -40,6 +42,7 @@
 import com.android.tradefed.testtype.IRemoteTest;
 import com.android.tradefed.testtype.IResumableTest;
 import com.android.tradefed.testtype.IShardableTest;
+import com.android.tradefed.util.AbiFormatter;
 import com.android.tradefed.util.RunUtil;
 import com.android.tradefed.util.xml.AbstractXmlParser.ParseException;
 
@@ -62,6 +65,7 @@
 import java.util.Queue;
 import java.util.Set;
 
+
 /**
  * A {@link Test} for running CTS tests.
  * <p/>
@@ -157,6 +161,11 @@
             "performant.")
     private boolean mLogcatOnFailures = false;
 
+    @Option(name = AbiFormatter.FORCE_ABI_STRING,
+            description = AbiFormatter.FORCE_ABI_DESCRIPTION,
+            importance = Importance.IF_UNSET)
+    private String mForceAbi = null;
+
     @Option(name = "logcat-on-failure-size", description =
             "The max number of logcat data in bytes to capture when --logcat-on-failure is on. " +
             "Should be an amount that can comfortably fit in memory.")
@@ -167,8 +176,8 @@
     /** data structure for a {@link IRemoteTest} and its known tests */
     class TestPackage {
         private final IRemoteTest mTestForPackage;
-        private final Collection<TestIdentifier> mKnownTests;
         private final ITestPackageDef mPackageDef;
+        private final Collection<TestIdentifier> mKnownTests;
 
         TestPackage(ITestPackageDef packageDef, IRemoteTest testForPackage,
                 Collection<TestIdentifier> knownTests) {
@@ -438,11 +447,35 @@
                 TestPackage knownTests = mRemainingTestPkgs.get(0);
 
                 IRemoteTest test = knownTests.getTestForPackage();
+
+                if (mForceAbi != null) {
+                    OptionSetter optionSetter = null;
+                    boolean hasField = false;
+                    try {
+                        optionSetter = new OptionSetter(test);
+                        if (optionSetter.getTypeForOption(AbiFormatter.FORCE_ABI_STRING)
+                                .equals("string")) {
+                            hasField = true;
+                        }
+                    } catch (ConfigurationException e) {
+                        // ignore if there are tests not taking force-abi option
+                        // for example native test do not need this option.
+                    }
+                    if (hasField) {
+                        try{
+                            optionSetter.setOptionValue(AbiFormatter.FORCE_ABI_STRING, mForceAbi);
+                        } catch (ConfigurationException e) {
+                            CLog.e(e);
+                            throw new RuntimeException(e);
+                        }
+                    }
+                }
+
                 if (test instanceof IDeviceTest) {
-                    ((IDeviceTest)test).setDevice(getDevice());
+                    ((IDeviceTest) test).setDevice(getDevice());
                 }
                 if (test instanceof IBuildReceiver) {
-                    ((IBuildReceiver)test).setBuild(mBuildInfo);
+                    ((IBuildReceiver) test).setBuild(mBuildInfo);
                 }
 
                 forwardPackageDetails(knownTests.getPackageDef(), listener);
@@ -467,6 +500,12 @@
 
             uninstallPrequisiteApks(uninstallPackages);
 
+        } catch (RuntimeException e) {
+            CLog.e(e);
+            throw e;
+        } catch (Error e) {
+            CLog.e(e);
+            throw e;
         } finally {
             filter.reportUnexecutedTests();
         }
@@ -693,7 +732,15 @@
         for (String apkName : prerequisiteApks) {
             try {
                 File apkFile = mCtsBuild.getTestApp(apkName);
-                String errorCode = getDevice().installPackage(apkFile, true);
+                String errorCode = null;
+                String[] options = {};
+                if (mForceAbi != null) {
+                    String abi = AbiFormatter.getDefaultAbi(getDevice(), mForceAbi);
+                    if (abi != null) {
+                        options = new String[]{String.format("--abi %s ", abi)};
+                    }
+                }
+                errorCode = getDevice().installPackage(apkFile, true, options);
                 if (errorCode != null) {
                     CLog.e("Failed to install %s. Reason: %s", apkName, errorCode);
                 }
@@ -736,6 +783,8 @@
         // don't create more shards than the number of tests we have!
         for (int i = 0; i < mShards && i < allTests.size(); i++) {
             CtsTest shard = new CtsTest();
+            OptionCopier.copyOptionsNoThrow(this, shard);
+            shard.mShards = 0;
             shard.mRemainingTestPkgs = new LinkedList<TestPackage>();
             shardQueue.add(shard);
         }
diff --git a/tools/tradefed-host/src/com/android/cts/tradefed/testtype/ITestPackageDef.java b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/ITestPackageDef.java
index 68d6743..0c3d9bc 100644
--- a/tools/tradefed-host/src/com/android/cts/tradefed/testtype/ITestPackageDef.java
+++ b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/ITestPackageDef.java
@@ -39,10 +39,6 @@
      * Creates a runnable {@link IRemoteTest} from info stored in this definition.
      *
      * @param testCaseDir {@link File} representing directory of test case data
-     * @param className the test class to restrict this run to or <code>null</code> to run all tests
-     *            in package
-     * @param methodName the optional test method to restrict this run to, or <code>null</code> to
-     *            run all tests in class/package
      * @return a {@link IRemoteTest} with all necessary data populated to run the test or
      *         <code>null</code> if test could not be created
      */
diff --git a/tools/tradefed-host/src/com/android/cts/tradefed/testtype/InstrumentationApkTest.java b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/InstrumentationApkTest.java
index 77189cd..35b4998 100644
--- a/tools/tradefed-host/src/com/android/cts/tradefed/testtype/InstrumentationApkTest.java
+++ b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/InstrumentationApkTest.java
@@ -18,20 +18,24 @@
 import com.android.cts.tradefed.build.CtsBuildHelper;
 import com.android.ddmlib.Log;
 import com.android.tradefed.build.IBuildInfo;
+import com.android.tradefed.config.Option;
+import com.android.tradefed.config.Option.Importance;
 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.InstrumentationTest;
+import com.android.tradefed.util.AbiFormatter;
 
+import junit.framework.Assert;
+
+import java.io.File;
 import java.io.FileNotFoundException;
 import java.util.ArrayList;
 import java.util.Collection;
 
-import junit.framework.Assert;
-
 /**
- * A {@link InstrumentationTest} that will install CTS apks 
+ * A {@link InstrumentationTest} that will install CTS apks
  * before test execution, and uninstall on execution completion.
  */
 public class InstrumentationApkTest extends InstrumentationTest implements IBuildReceiver {
@@ -44,6 +48,11 @@
 
     private CtsBuildHelper mCtsBuild = null;
 
+    @Option(name = AbiFormatter.FORCE_ABI_STRING,
+            description = AbiFormatter.FORCE_ABI_DESCRIPTION,
+            importance = Importance.IF_UNSET)
+    private String mForceAbi = null;
+
     /**
      * {@inheritDoc}
      */
@@ -84,11 +93,19 @@
             Log.d(LOG_TAG, String.format("Installing %s on %s", apkFileName,
                     mTestDevice.getSerialNumber()));
             try {
-                String installCode = mTestDevice.installPackage(mCtsBuild.getTestApp(apkFileName),
-                        true);
-                if (installCode != null) {
+                File apkFile = mCtsBuild.getTestApp(apkFileName);
+                String errorCode = null;
+                String[] options = {};
+                if (mForceAbi != null) {
+                    String abi = AbiFormatter.getDefaultAbi(mTestDevice, mForceAbi);
+                    if (abi != null) {
+                        options = new String[]{String.format("--abi %s ", abi)};
+                    }
+                }
+                errorCode = mTestDevice.installPackage(apkFile, true, options);
+                if (errorCode != null) {
                     Log.e(LOG_TAG, String.format("Failed to install %s on %s. Reason: %s",
-                          apkFileName, mTestDevice.getSerialNumber(), installCode));
+                          apkFileName, mTestDevice.getSerialNumber(), errorCode));
                 }
             } catch (FileNotFoundException e) {
                 Log.e(LOG_TAG, String.format("Could not find file %s", apkFileName));
@@ -97,8 +114,8 @@
         super.run(listener);
         for (String packageName : mUninstallPackages) {
             Log.d(LOG_TAG, String.format("Uninstalling %s on %s", packageName,
-                    getDevice().getSerialNumber()));
-            getDevice().uninstallPackage(packageName);
+                    mTestDevice.getSerialNumber()));
+            mTestDevice.uninstallPackage(packageName);
         }
     }
 }
diff --git a/tools/tradefed-host/src/com/android/cts/tradefed/testtype/JUnitDeviceTest.java b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/JUnitDeviceTest.java
new file mode 100644
index 0000000..4adcb0b
--- /dev/null
+++ b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/JUnitDeviceTest.java
@@ -0,0 +1,222 @@
+/*
+ * 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.ddmlib.testrunner.TestIdentifier;
+import com.android.tradefed.build.IBuildInfo;
+import com.android.tradefed.config.Option;
+import com.android.tradefed.config.Option.Importance;
+import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.log.LogUtil.CLog;
+import com.android.tradefed.result.ITestInvocationListener;
+import com.android.tradefed.testtype.IBuildReceiver;
+import com.android.tradefed.testtype.IDeviceTest;
+import com.android.tradefed.testtype.IRemoteTest;
+import com.android.tradefed.util.AbiFormatter;
+import com.android.tradefed.util.ArrayUtil;
+
+import junit.framework.Assert;
+import junit.framework.Test;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * {@link Test} for running CTS JUnit tests on the device.
+ */
+public class JUnitDeviceTest implements IDeviceTest, IRemoteTest, IBuildReceiver {
+
+    private static final String TMP_DIR = "/data/local/tmp/";
+
+    @Option(name = AbiFormatter.FORCE_ABI_STRING, description = AbiFormatter.FORCE_ABI_DESCRIPTION,
+            importance = Importance.IF_UNSET)
+    private String mForceAbi = null;
+
+    @Option(name = "junit-device-runtime",
+            description = "The name of the runtime to use on the device",
+            importance = Importance.ALWAYS)
+    private String mRuntimePath = "dalvikvm|#ABI#|";
+
+    @Option(name = "junit-device-tmpdir", description = "Device path where to store the test jars."
+            , importance = Importance.IF_UNSET)
+    private String mDeviceTestTmpPath = TMP_DIR;
+
+
+
+    // default to no timeout
+    private long mMaxTimeToOutputResponse = 0;
+
+    private ITestDevice mDevice;
+    private String mRunName;
+    private Collection<TestIdentifier> mTests;
+    private CtsBuildHelper mCtsBuild = null;
+
+    private List<String> mJarPaths = new ArrayList<String>();
+
+    private String mRuntimeArgs;
+
+
+
+    private static final String JUNIT_JAR = "cts-junit.jar";
+
+    private Set<String> mTestJars = new HashSet<String>(Arrays.asList(JUNIT_JAR));
+
+    @Override
+    public ITestDevice getDevice() {
+        return mDevice;
+    }
+
+    @Override
+    public void setDevice(ITestDevice device) {
+        mDevice = device;
+    }
+
+    public void addTestJarFileName(String jarFileName) {
+        mTestJars.add(jarFileName);
+    }
+
+    public void setRunName(String runName) {
+        mRunName = runName;
+    }
+
+    public void setTests(Collection<TestIdentifier> tests) {
+        mTests = tests;
+    }
+
+    public Collection<TestIdentifier> getTests() {
+        return mTests;
+    }
+
+    @Override
+    public void run(ITestInvocationListener listener) throws DeviceNotAvailableException {
+        addTestJarFileName(JUNIT_JAR);
+        checkFields();
+        long startTime = System.currentTimeMillis();
+        listener.testRunStarted(mRunName, mTests.size());
+        try {
+            installJars();
+            String jarPath = ArrayUtil.join(":", mJarPaths);
+            for (TestIdentifier testId : mTests) {
+                SingleJUnitTestResultParser resultParser = new SingleJUnitTestResultParser(
+                        testId, listener);
+                String cmdLine = String.format("ANDROID_DATA=%s %s -cp %s %s " +
+                        "com.android.cts.junit.SingleJUnitTestRunner %s#%s",
+                        mDeviceTestTmpPath, mRuntimePath, jarPath, mRuntimeArgs,
+                        testId.getClassName(), testId.getTestName());
+                String cmd = AbiFormatter.formatCmdForAbi(cmdLine, mForceAbi);
+                CLog.d("Running %s", cmd);
+                listener.testStarted(testId);
+                mDevice.executeShellCommand(cmd, resultParser, mMaxTimeToOutputResponse,
+                        TimeUnit.MILLISECONDS, 0);
+            }
+        } finally {
+            listener.testRunEnded(System.currentTimeMillis() - startTime,
+                    Collections.<String, String> emptyMap());
+            // Remove jar files from device
+            removeJars();
+        }
+    }
+
+    /**
+     * Installs the jar files on the device under test.
+     *
+     * @throws DeviceNotAvailableException
+     */
+    protected void installJars() throws DeviceNotAvailableException {
+        for (String f : mTestJars) {
+            CLog.d("Installing %s on %s", f, getDevice().getSerialNumber());
+            File jarFile;
+            try {
+                String fullJarPath = String.format("%s%s", mDeviceTestTmpPath, f);
+                jarFile = mCtsBuild.getTestApp(f);
+                boolean result = getDevice().pushFile(jarFile, fullJarPath);
+                Assert.assertTrue(String.format("Failed to push file to %s", fullJarPath), result);
+                mJarPaths.add(fullJarPath);
+            } catch (FileNotFoundException e) {
+                Assert.fail(String.format("Could not find file %s", f));
+            }
+        }
+    }
+
+    /**
+     * Cleans up the jar files from the device under test.
+     *
+     * @throws DeviceNotAvailableException
+     */
+    protected void removeJars() throws DeviceNotAvailableException {
+        for (String f : mTestJars) {
+            String fullJarPath = String.format("%s%s", mDeviceTestTmpPath, f);
+            CLog.d("Uninstalling %s on %s", fullJarPath, getDevice().getSerialNumber());
+            getDevice().executeShellCommand(String.format("rm %s", fullJarPath));
+        }
+    }
+
+    @Override
+    public void setBuild(IBuildInfo buildInfo) {
+        mCtsBuild = CtsBuildHelper.createBuildHelper(buildInfo);
+    }
+
+    /**
+     * Checks that all mandatory member fields has been set.
+     */
+    protected void checkFields() {
+        if (mRunName == null) {
+            throw new IllegalArgumentException("run name has not been set");
+        }
+        if (mDevice == null) {
+            throw new IllegalArgumentException("Device has not been set");
+        }
+        if (mTestJars.isEmpty()) {
+            throw new IllegalArgumentException("No test jar has been set");
+        }
+        if (mTests == null) {
+            throw new IllegalArgumentException("tests has not been set");
+        }
+        if (mCtsBuild == null) {
+            throw new IllegalArgumentException("build has not been set");
+        }
+        for (String f : mTestJars) {
+            try {
+
+                mCtsBuild.getTestApp(f);
+            } catch (FileNotFoundException e) {
+                throw new IllegalArgumentException(String.format(
+                        "Could not find jar %s in CTS build %s", f,
+                        mCtsBuild.getRootDir().getAbsolutePath()));
+            }
+        }
+    }
+
+    /**
+     * Add runtime arguments to run the tests with.
+     *
+     * @param mRunTimeArgs
+     */
+    public void addRunTimeArgs(String mRunTimeArgs) {
+        mRuntimeArgs = mRunTimeArgs;
+    }
+}
diff --git a/tools/tradefed-host/src/com/android/cts/tradefed/testtype/JarHostTest.java b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/JarHostTest.java
index 1c59b69..22a016a 100644
--- a/tools/tradefed-host/src/com/android/cts/tradefed/testtype/JarHostTest.java
+++ b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/JarHostTest.java
@@ -19,9 +19,11 @@
 import com.android.ddmlib.Log;
 import com.android.ddmlib.testrunner.TestIdentifier;
 import com.android.tradefed.build.IBuildInfo;
+import com.android.tradefed.config.ConfigurationException;
 import com.android.tradefed.device.DeviceNotAvailableException;
 import com.android.tradefed.device.ITestDevice;
 import com.android.tradefed.result.ITestInvocationListener;
+
 import com.android.tradefed.testtype.DeviceTestResult.RuntimeDeviceNotAvailableException;
 import com.android.tradefed.testtype.IBuildReceiver;
 import com.android.tradefed.testtype.IDeviceTest;
@@ -188,6 +190,14 @@
     }
 
     /**
+     * setOptions sets options to the tests invoked from this test.
+     * It is used to passing options from JarHostTest to the tests started by JarHostTest.
+     * The default implementation does nothing.
+     */
+    protected void setOptions(Test junitTest) throws ConfigurationException {
+    }
+
+    /**
      * Run test with timeout support.
      */
     private void runTest(TestIdentifier testId, final Test junitTest, final TestResult junitResult) {
@@ -203,6 +213,11 @@
         if (junitTest instanceof IBuildReceiver) {
             ((IBuildReceiver)junitTest).setBuild(mBuildInfo);
         }
+        try {
+            setOptions(junitTest);
+        } catch (ConfigurationException e) {
+            Log.e(LOG_TAG, e.toString());
+        }
         TestRunnable testRunnable = new TestRunnable(junitTest, junitResult);
 
         CommandStatus status = RunUtil.getDefault().runTimed(mTimeoutMs, testRunnable, true);
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/SingleJUnitTestResultParser.java b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/SingleJUnitTestResultParser.java
new file mode 100644
index 0000000..117a974
--- /dev/null
+++ b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/SingleJUnitTestResultParser.java
@@ -0,0 +1,96 @@
+/*
+ * 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.MultiLineReceiver;
+import com.android.ddmlib.testrunner.ITestRunListener;
+import com.android.ddmlib.testrunner.TestIdentifier;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Map;
+
+/**
+ * Parses the test results from {@link com.android.cts.junit.SingleJUnitTestRunner}
+ */
+public class SingleJUnitTestResultParser extends MultiLineReceiver {
+
+    private static final String PASSED_TEST_MARKER = "[ PASSED ]";
+    private static final String FAILED_TEST_MARKER = "[ FAILED ]";
+    private final TestIdentifier mTestId;
+    private final Collection<ITestRunListener> mTestListeners;
+    private StringBuilder mStackTrace = new StringBuilder();
+
+    public SingleJUnitTestResultParser(TestIdentifier testId, Collection<ITestRunListener> listeners) {
+        mTestId = testId;
+        mTestListeners = new ArrayList<ITestRunListener>(listeners);
+    }
+
+    public SingleJUnitTestResultParser(TestIdentifier testId, ITestRunListener listener) {
+        mTestId = testId;
+        mTestListeners = new ArrayList<ITestRunListener>(1);
+        mTestListeners.add(listener);
+    }
+
+    @Override
+    public boolean isCancelled() {
+        // TODO Auto-generated method stub
+        return false;
+    }
+
+    @Override
+    public void processNewLines(String[] lines) {
+        for (String line : lines) {
+            parse(line);
+        }
+    }
+
+    /**
+     * Parses a given string.
+     * @param line
+     */
+    private void parse(String line) {
+        if (line.startsWith(PASSED_TEST_MARKER)) {
+            doTestEnded(true);
+        } else if (line.startsWith(FAILED_TEST_MARKER)) {
+            doTestEnded(false);
+        } else {
+            // Store everything in case there is a failure.
+            mStackTrace.append("\n");
+            mStackTrace.append(line);
+        }
+    }
+
+    /**
+     * Handle cases when test ends.
+     * @param testPassed whether or not the test passed.
+     */
+    private void doTestEnded(boolean testPassed) {
+        // If test failed.
+        if (!testPassed) {
+            for (ITestRunListener listener : mTestListeners) {
+                listener.testFailed(ITestRunListener.TestFailure.FAILURE, mTestId,
+                        mStackTrace.toString());
+            }
+        }
+        Map<String, String> emptyMap = Collections.emptyMap();
+        for (ITestRunListener listener : mTestListeners) {
+            listener.testEnded(mTestId, emptyMap);
+        }
+        mStackTrace = new StringBuilder();
+    }
+}
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 f1a0485..4d1b3e2 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.
@@ -38,7 +39,7 @@
     private final Set<TestIdentifier> mIncludedTests;
 
     private String mIncludedClass = null;
-    private String mIncludedMethod = null;
+    private Pattern mIncludedMethod = null;
 
     /**
      * Creates a {@link TestFilter}
@@ -124,7 +125,9 @@
      */
     public void setTestInclusion(String className, String method) {
         mIncludedClass = className;
-        mIncludedMethod = method;
+        if (method != null) {
+            mIncludedMethod = Pattern.compile(method);
+        }
     }
 
     /**
@@ -140,7 +143,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..c477585 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,12 +47,15 @@
     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";
+    public static final String JUNIT_DEVICE_TEST = "jUnitDeviceTest";
 
     private static final String SIGNATURE_TEST_METHOD = "testSignature";
     private static final String SIGNATURE_TEST_CLASS = "android.tests.sigtest.SignatureTest";
@@ -61,9 +64,9 @@
     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 String mRunTimeArgs = null;
     private boolean mIsSignatureTest = false;
     private String mTestPackageName = null;
     private String mDigest = null;
@@ -97,6 +100,14 @@
         return mUri;
     }
 
+    void setRunTimeArgs(String runTimeArgs) {
+        mRunTimeArgs = runTimeArgs;
+    }
+
+    String getRunTimeArgs() {
+        return mRunTimeArgs;
+    }
+
     void setAppNameSpace(String appNameSpace) {
         mAppNameSpace = appNameSpace;
     }
@@ -230,6 +241,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);
@@ -257,6 +271,15 @@
             instrTest.addInstallApk(String.format("%s.apk", mName), mAppNameSpace);
             mDigest = generateDigest(testCaseDir, String.format("%s.apk", mName));
             return instrTest;
+        } else if (JUNIT_DEVICE_TEST.equals(mTestType)){
+            CLog.d("Creating JUnit device test %s", mName);
+            JUnitDeviceTest jUnitDeviceTest = new JUnitDeviceTest();
+            jUnitDeviceTest.setRunName(getUri());
+            jUnitDeviceTest.addTestJarFileName(mJarPath);
+            jUnitDeviceTest.addRunTimeArgs(mRunTimeArgs);
+            jUnitDeviceTest.setTests(mTests);
+            mDigest = generateDigest(testCaseDir, mJarPath);
+            return jUnitDeviceTest;
         } else {
             CLog.d("Creating instrumentation test for %s", mName);
             InstrumentationApkTest instrTest = new InstrumentationApkTest();
@@ -270,6 +293,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.
      *
@@ -285,9 +320,9 @@
         instrTest.setTestPackageName(mTestPackageName);
         instrTest.setClassName(mClassName);
         instrTest.setMethodName(mMethodName);
-        instrTest.setTestsToRun(mTests,
-                !mExcludedTestFilter.hasExclusion()
-                /* only force batch mode if no tests are excluded */);
+        instrTest.setTestsToRun(mTests, false
+            /* force batch mode off to always run using testFile */);
+        instrTest.setReRunUsingTestFile(true);
         // mName means 'apk file name' for instrumentation tests
         instrTest.addInstallApk(String.format("%s.apk", mName), mAppNameSpace);
         mDigest = generateDigest(testCaseDir, String.format("%s.apk", mName));
diff --git a/tools/tradefed-host/src/com/android/cts/tradefed/testtype/TestPackageXmlParser.java b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/TestPackageXmlParser.java
index bd67f78..5d10dfc 100644
--- a/tools/tradefed-host/src/com/android/cts/tradefed/testtype/TestPackageXmlParser.java
+++ b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/TestPackageXmlParser.java
@@ -75,6 +75,7 @@
                 final String javaPackageFilter = attributes.getValue("javaPackageFilter");
                 final String targetBinaryName = attributes.getValue("targetBinaryName");
                 final String targetNameSpace = attributes.getValue("targetNameSpace");
+                final String runTimeArgs = attributes.getValue("runtimeArgs");
 
                 mPackageDef = new TestPackageDef();
                 mPackageDef.setUri(entryUriValue);
@@ -84,6 +85,7 @@
                 mPackageDef.setTestType(getTestType(attributes));
                 mPackageDef.setJarPath(jarPath);
                 mPackageDef.setIsSignatureCheck(parseBoolean(signatureCheck));
+                mPackageDef.setRunTimeArgs(runTimeArgs);
                 if (!"".equals(javaPackageFilter)) {
                     mPackageDef.setTestPackageName(javaPackageFilter);
                 }
diff --git a/tools/tradefed-host/src/com/android/cts/tradefed/testtype/VMHostTest.java b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/VMHostTest.java
index 0ebdeea..9b4c86d 100644
--- a/tools/tradefed-host/src/com/android/cts/tradefed/testtype/VMHostTest.java
+++ b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/VMHostTest.java
@@ -16,12 +16,19 @@
 package com.android.cts.tradefed.testtype;
 
 import com.android.cts.tradefed.build.CtsBuildHelper;
+import com.android.tradefed.config.ConfigurationException;
+import com.android.tradefed.config.Option;
+import com.android.tradefed.config.Option.Importance;
+import com.android.tradefed.config.OptionSetter;
 import com.android.tradefed.device.DeviceNotAvailableException;
 import com.android.tradefed.device.ITestDevice;
 import com.android.tradefed.log.LogUtil.CLog;
 import com.android.tradefed.result.ITestInvocationListener;
+import com.android.tradefed.util.AbiFormatter;
 import com.android.tradefed.util.FileUtil;
 
+import junit.framework.Test;
+
 import java.io.File;
 import java.io.IOException;
 import java.util.zip.ZipFile;
@@ -35,6 +42,12 @@
     private static final String VM_TEST_TEMP_DIR = "/data/local/tmp/vm-tests";
     private static final String EMULATOR_TEMP_DIR = "/data/local/tmp";
 
+    @Option(name = AbiFormatter.FORCE_ABI_STRING,
+            description = AbiFormatter.FORCE_ABI_DESCRIPTION,
+            importance = Importance.IF_UNSET)
+    private String mForceAbi = null;
+
+
     /**
      * {@inheritDoc}
      */
@@ -95,6 +108,14 @@
         return true;
     }
 
+    @Override
+    protected void setOptions(Test junitTest) throws ConfigurationException{
+        if (mForceAbi != null) {
+            OptionSetter optionSetter = new OptionSetter(junitTest);
+            optionSetter.setOptionValue(AbiFormatter.FORCE_ABI_STRING, mForceAbi);
+        }
+    }
+
     /**
      * Removes temporary file directory from device
      *
diff --git a/tools/tradefed-host/src/com/android/cts/tradefed/testtype/WrappedGTest.java b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/WrappedGTest.java
index 74c15f6..78cd6f77 100644
--- a/tools/tradefed-host/src/com/android/cts/tradefed/testtype/WrappedGTest.java
+++ b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/WrappedGTest.java
@@ -19,6 +19,8 @@
 import com.android.cts.tradefed.build.CtsBuildHelper;
 import com.android.ddmlib.testrunner.ITestRunListener;
 import com.android.tradefed.build.IBuildInfo;
+import com.android.tradefed.config.Option;
+import com.android.tradefed.config.Option.Importance;
 import com.android.tradefed.device.DeviceNotAvailableException;
 import com.android.tradefed.device.ITestDevice;
 import com.android.tradefed.log.LogUtil.CLog;
@@ -26,6 +28,7 @@
 import com.android.tradefed.testtype.IBuildReceiver;
 import com.android.tradefed.testtype.IDeviceTest;
 import com.android.tradefed.testtype.IRemoteTest;
+import com.android.tradefed.util.AbiFormatter;
 
 import java.io.File;
 import java.io.FileNotFoundException;
@@ -45,6 +48,10 @@
     private final String mName;
     private final String mUri;
 
+    @Option(name = AbiFormatter.FORCE_ABI_STRING,
+            description = AbiFormatter.FORCE_ABI_DESCRIPTION,
+            importance = Importance.IF_UNSET)
+    private String mForceAbi = null;
 
     public WrappedGTest(String appNameSpace, String uri, String name, String runner) {
         mAppNameSpace = appNameSpace;
@@ -100,7 +107,14 @@
         WrappedGTestResultParser resultParser = new WrappedGTestResultParser(mUri, listener);
         resultParser.setFakePackagePrefix(mUri + ".");
         try {
-            String command = String.format("am instrument -w %s/.%s", mAppNameSpace, mRunner);
+            String options = "";
+            if (mForceAbi != null) {
+                String abi = AbiFormatter.getDefaultAbi(getDevice(), mForceAbi);
+                if (abi != null) {
+                    options = String.format("--abi %s ", abi);
+                }
+            }
+            String command = String.format("am instrument -w %s%s/.%s", options, mAppNameSpace, mRunner);
             mDevice.executeShellCommand(command, resultParser, mMaxTestTimeMs, 0);
         } catch (DeviceNotAvailableException e) {
             resultParser.flush();
diff --git a/tools/tradefed-host/tests/src/com/android/cts/tradefed/result/CtsXmlResultReporterTest.java b/tools/tradefed-host/tests/src/com/android/cts/tradefed/result/CtsXmlResultReporterTest.java
index 5776568..26cf39c 100644
--- a/tools/tradefed-host/tests/src/com/android/cts/tradefed/result/CtsXmlResultReporterTest.java
+++ b/tools/tradefed-host/tests/src/com/android/cts/tradefed/result/CtsXmlResultReporterTest.java
@@ -21,6 +21,7 @@
 import com.android.ddmlib.testrunner.TestIdentifier;
 import com.android.tradefed.build.IFolderBuildInfo;
 import com.android.tradefed.log.LogUtil.CLog;
+import com.android.tradefed.result.TestSummary;
 import com.android.tradefed.result.XmlResultReporter;
 import com.android.tradefed.util.FileUtil;
 
@@ -32,7 +33,10 @@
 import java.io.File;
 import java.io.IOException;
 import java.io.OutputStream;
+import java.util.Arrays;
+import java.util.ArrayList;
 import java.util.Collections;
+import java.util.List;
 import java.util.Map;
 
 /**
@@ -40,6 +44,8 @@
  */
 public class CtsXmlResultReporterTest extends TestCase {
 
+    private static final List<TestSummary> SUMMARY_LIST =
+            new ArrayList<>(Arrays.asList(new TestSummary("TEST_SUMMARY_URL")));
     private CtsXmlResultReporter mResultReporter;
     private ByteArrayOutputStream mOutputStream;
     private File mReportDir;
@@ -116,6 +122,7 @@
         mResultReporter.testEnded(testId, emptyMap);
         mResultReporter.testRunEnded(3000, emptyMap);
         mResultReporter.invocationEnded(1);
+        mResultReporter.putSummary(SUMMARY_LIST);
         String output =  getOutput();
         CLog.d("Actual output: %s", output);
         System.out.println(output);
diff --git a/tools/utils/CollectAllTests.java b/tools/utils/CollectAllTests.java
index fe13e00..367fb93 100644
--- a/tools/utils/CollectAllTests.java
+++ b/tools/utils/CollectAllTests.java
@@ -14,27 +14,23 @@
  * limitations under the License.
  */
 
+import org.junit.runner.RunWith;
 import org.w3c.dom.Document;
 import org.w3c.dom.Element;
 import org.w3c.dom.Node;
 import org.w3c.dom.NodeList;
 
-import vogar.Expectation;
 import vogar.ExpectationStore;
-import vogar.ModeId;
 
 import java.io.BufferedReader;
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.FileReader;
-import java.io.FilenameFilter;
 import java.io.IOException;
 import java.lang.annotation.Annotation;
-import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 import java.lang.reflect.Modifier;
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.Enumeration;
 import java.util.HashSet;
 import java.util.Iterator;
@@ -47,11 +43,7 @@
 import javax.xml.parsers.DocumentBuilderFactory;
 import javax.xml.parsers.ParserConfigurationException;
 
-import junit.framework.Test;
 import junit.framework.TestCase;
-import junit.framework.TestResult;
-import junit.textui.ResultPrinter;
-import junit.textui.TestRunner;
 
 public class CollectAllTests extends DescriptionGenerator {
 
@@ -203,18 +195,19 @@
                 Class<?> klass = Class.forName(className,
                                                false,
                                                CollectAllTests.class.getClassLoader());
-                if (!TestCase.class.isAssignableFrom(klass)) {
+                final int modifiers = klass.getModifiers();
+                if (Modifier.isAbstract(modifiers) || !Modifier.isPublic(modifiers)) {
                     continue;
                 }
-                if (Modifier.isAbstract(klass.getModifiers())) {
+
+                final boolean isJunit4Class = isJunit4Class(klass);
+                if (!isJunit4Class && !isJunit3Test(klass)) {
                     continue;
                 }
-                if (!Modifier.isPublic(klass.getModifiers())) {
-                    continue;
-                }
+
                 try {
                     klass.getConstructor(new Class<?>[] { String.class } );
-                    addToTests(expectations, testCases, klass.asSubclass(TestCase.class));
+                    addToTests(expectations, testCases, klass);
                     continue;
                 } catch (NoSuchMethodException e) {
                 } catch (SecurityException e) {
@@ -222,9 +215,10 @@
                             + className);
                     e.printStackTrace();
                 }
+
                 try {
                     klass.getConstructor(new Class<?>[0]);
-                    addToTests(expectations, testCases, klass.asSubclass(TestCase.class));
+                    addToTests(expectations, testCases, klass);
                     continue;
                 } catch (NoSuchMethodException e) {
                 } catch (SecurityException e) {
@@ -276,7 +270,7 @@
                 BufferedReader reader = new BufferedReader(new FileReader(makeFileName));
                 String line;
 
-                while ((line =reader.readLine())!=null) {
+                while ((line = reader.readLine())!=null) {
                     if (line.startsWith(TEST_TYPE)) {
                         if (line.indexOf(ATTRIBUTE_VM_HOST_TEST) >= 0) {
                             type = VM_HOST_TEST;
@@ -314,32 +308,22 @@
         }
     }
 
-    private static String getKnownFailure(final Class<? extends TestCase> testClass,
+    private static String getKnownFailure(final Class<?> testClass,
             final String testName) {
         return getAnnotation(testClass, testName, KNOWN_FAILURE);
     }
 
-    private static boolean isKnownFailure(final Class<? extends TestCase> testClass,
+    private static boolean isKnownFailure(final Class<?> testClass,
             final String testName) {
         return getAnnotation(testClass, testName, KNOWN_FAILURE) != null;
     }
 
-    private static boolean isBrokenTest(final Class<? extends TestCase> testClass,
-            final String testName)  {
-        return getAnnotation(testClass, testName, BROKEN_TEST) != null;
-    }
-
-    private static boolean isSuppressed(final Class<? extends TestCase> testClass,
+    private static boolean isSuppressed(final Class<?> testClass,
             final String testName)  {
         return getAnnotation(testClass, testName, SUPPRESSED_TEST) != null;
     }
 
-    private static boolean hasSideEffects(final Class<? extends TestCase> testClass,
-            final String testName) {
-        return getAnnotation(testClass, testName, SIDE_EFFECT) != null;
-    }
-
-    private static String getAnnotation(final Class<? extends TestCase> testClass,
+    private static String getAnnotation(final Class<?> testClass,
             final String testName, final String annotationName) {
         try {
             Method testMethod = testClass.getMethod(testName, (Class[])null);
@@ -373,38 +357,42 @@
 
     private static void addToTests(ExpectationStore[] expectations,
                                    Map<String,TestClass> testCases,
-                                   Class<? extends TestCase> test) {
-        Class testClass = test;
+                                   Class<?> testClass) {
         Set<String> testNames = new HashSet<String>();
-        while (TestCase.class.isAssignableFrom(testClass)) {
-            Method[] testMethods = testClass.getDeclaredMethods();
-            for (Method testMethod : testMethods) {
-                String testName = testMethod.getName();
-                if (testNames.contains(testName)) {
-                    continue;
-                }
-                if (!testName.startsWith("test")) {
-                    continue;
-                }
-                if (testMethod.getParameterTypes().length != 0) {
-                    continue;
-                }
-                if (!testMethod.getReturnType().equals(Void.TYPE)) {
-                    continue;
-                }
-                if (!Modifier.isPublic(testMethod.getModifiers())) {
-                    continue;
-                }
-                testNames.add(testName);
-                addToTests(expectations, testCases, test, testName);
+
+        boolean isJunit3Test = isJunit3Test(testClass);
+
+        Method[] testMethods = testClass.getMethods();
+        for (Method testMethod : testMethods) {
+            String testName = testMethod.getName();
+            if (testNames.contains(testName)) {
+                continue;
             }
-            testClass = testClass.getSuperclass();
+
+            /* Make sure the method has the right signature. */
+            if (!Modifier.isPublic(testMethod.getModifiers())) {
+                continue;
+            }
+            if (!testMethod.getReturnType().equals(Void.TYPE)) {
+                continue;
+            }
+            if (testMethod.getParameterTypes().length != 0) {
+                continue;
+            }
+
+            if ((isJunit3Test && !testName.startsWith("test"))
+                    || (!isJunit3Test && !isJunit4TestMethod(testMethod))) {
+                continue;
+            }
+
+            testNames.add(testName);
+            addToTests(expectations, testCases, testClass, testName);
         }
     }
 
     private static void addToTests(ExpectationStore[] expectations,
                                    Map<String,TestClass> testCases,
-                                   Class<? extends TestCase> test,
+                                   Class<?> test,
                                    String testName) {
 
         String testClassName = test.getName();
@@ -413,15 +401,9 @@
         if (isKnownFailure(test, testName)) {
             System.out.println("ignoring known failure: " + test + "#" + testName);
             return;
-        } else if (isBrokenTest(test, testName)) {
-            System.out.println("ignoring broken test: " + test + "#" + testName);
-            return;
         } else if (isSuppressed(test, testName)) {
             System.out.println("ignoring suppressed test: " + test + "#" + testName);
             return;
-        } else if (hasSideEffects(test, testName)) {
-            System.out.println("ignoring test with side effects: " + test + "#" + testName);
-            return;
         } else if (VogarUtils.isVogarKnownFailure(expectations,
                                                   testClassName,
                                                   testName)) {
@@ -430,7 +412,7 @@
             return;
         }
 
-        TestClass testClass = null;
+        TestClass testClass;
         if (testCases.containsKey(testClassName)) {
             testClass = testCases.get(testClassName);
         } else {
@@ -441,6 +423,40 @@
         testClass.mCases.add(new TestMethod(testName, "", "", knownFailure, false, false));
     }
 
+    private static boolean isJunit3Test(Class<?> klass) {
+        return TestCase.class.isAssignableFrom(klass);
+    }
+
+    private static boolean isJunit4Class(Class<?> klass) {
+        for (Annotation a : klass.getAnnotations()) {
+            if (RunWith.class.isAssignableFrom(a.annotationType())) {
+                // @RunWith is currently not supported for CTS tests because tradefed cannot handle
+                // a single test spawning other tests with different names.
+                System.out.println("Skipping test class " + klass.getName()
+                        + ": JUnit4 @RunWith is not supported");
+                return false;
+            }
+        }
+
+        for (Method m : klass.getMethods()) {
+            if (isJunit4TestMethod(m)) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    private static boolean isJunit4TestMethod(Method method) {
+        for (Annotation a : method.getAnnotations()) {
+            if (org.junit.Test.class.isAssignableFrom(a.annotationType())) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
     /**
      * Determines if a given string is a valid java package name
      * @param javaPackageName
diff --git a/tools/utils/DescriptionGenerator.java b/tools/utils/DescriptionGenerator.java
index 0731b49..607d2e5 100644
--- a/tools/utils/DescriptionGenerator.java
+++ b/tools/utils/DescriptionGenerator.java
@@ -66,8 +66,6 @@
 public class DescriptionGenerator extends Doclet {
     static final String HOST_CONTROLLER = "dalvik.annotation.HostController";
     static final String KNOWN_FAILURE = "dalvik.annotation.KnownFailure";
-    static final String BROKEN_TEST = "dalvik.annotation.BrokenTest";
-    static final String SIDE_EFFECT = "dalvik.annotation.SideEffect";
     static final String SUPPRESSED_TEST = "android.test.suitebuilder.annotation.Suppress";
     static final String CTS_EXPECTATION_DIR = "cts/tests/expectations";
 
@@ -545,8 +543,6 @@
                         controller = getAnnotationDescription(cAnnot);
                     } else if (atype.toString().equals(KNOWN_FAILURE)) {
                         knownFailure = getAnnotationDescription(cAnnot);
-                    } else if (atype.toString().equals(BROKEN_TEST)) {
-                        isBroken = true;
                     } else if (atype.toString().equals(SUPPRESSED_TEST)) {
                         isSuppressed = true;
                     }
diff --git a/tools/utils/buildCts.py b/tools/utils/buildCts.py
index 8e4ae49..0f74a81 100755
--- a/tools/utils/buildCts.py
+++ b/tools/utils/buildCts.py
@@ -188,47 +188,14 @@
           'cts.AlertDialogTest#testAlertDialogCancelable',
           'cts.ExpandableListActivityTest#testCallback',],
       'android.hardware' : [
-          'cts.SensorIntegrationTests#testAccelerometerDoesNotStopGyroscope',
-          'cts.SensorIntegrationTests#testsAccelerometerDoesnNotStopMagnetometer',
-          'cts.SensorIntegrationTests#testAndroidTestCaseSetupProperly',
-          'cts.SensorIntegrationTests#testBatchAndFlush',
-          'cts.SensorIntegrationTests#testGyroscopeDoesNotStopAccelerometer',
-          'cts.SensorIntegrationTests#testGyroscopeDoesNotStopMagnetometer',
-          'cts.SensorIntegrationTests#testMagnetometerDoesNotStopAccelerometer',
-          'cts.SensorIntegrationTests#testMagnetometerDoesNotStopGyroscope',
-          'cts.SensorMagneticFieldTest#testBatchingStoppingOtherClients',
-          'cts.SensorMagneticFieldTest#testBatchingStoppingOtherClientsBatching',
-          'cts.SensorMagneticFieldTest#testFrequencyAccuracy',
-          'cts.SensorMagneticFieldTest#testOneClientSeveralThreads',
-          'cts.SensorMagneticFieldTest#testOneClientSeveralThreadsBatching',
-          'cts.SensorMagneticFieldTest#testStandardDeviationWhileStatic',
-          'cts.SensorMagneticFieldTest#testStoppingOtherClients',
-          'cts.SensorMagneticFieldTest#testStoppingOtherClientsBatching',
-          'cts.SensorAccelerometerTest#testBatchingStoppingOtherClients',
-          'cts.SensorAccelerometerTest#testBatchingStoppingOtherClientsBatching',
-          'cts.SensorAccelerometerTest#testFrequencyAccuracy',
-          'cts.SensorAccelerometerTest#testOneClientSeveralThreads',
-          'cts.SensorAccelerometerTest#testOneClientSeveralThreadsBatching',
-          'cts.SensorGyroscopeTest#testBatchingStoppingOtherClients',
-          'cts.SensorGyroscopeTest#testBatchingStoppingOtherClientsBatching',
-          'cts.SensorGyroscopeTest#testFrequencyAccuracy',
-          'cts.SensorGyroscopeTest#testOneClientSeveralThreads',
-          'cts.SensorGyroscopeTest#testOneClientSeveralThreadsBatching',
-          'cts.SensorGyroscopeTest#testStandardDeviationWhilStatic',
-          'cts.SensorGyroscopeTest#testStoppingOtherClients',
-          'cts.SensorGyroscopeTest#testStoppingOtherClientsBatching',
-          'cts.SensorAccelerometerTest#testStandardDeviationWhileStatic',
-          'cts.SensorAccelerometerTest#testStoppingOtherClients',
-          'cts.SensorAccelerometerTest#testStoppingOtherClientsBatching',
           'camera2.cts.CameraDeviceTest#testCameraDeviceRepeatingRequest',
           'camera2.cts.ImageReaderTest#testImageReaderFromCameraJpeg',
-          'cts.CameraGLTest#testCameraToSurfaceTextureMetadata',
           'cts.CameraTest#testImmediateZoom',
           'cts.CameraTest#testPreviewCallback',
           'cts.CameraTest#testSmoothZoom',
           'cts.CameraTest#testVideoSnapshot',
+          'cts.CameraGLTest#testCameraToSurfaceTextureMetadata',
           'cts.CameraGLTest#testSetPreviewTextureBothCallbacks',
-          'cts.CameraGLTest#testSetPreviewTexturePreviewCallback',
           'cts.CameraGLTest#testSetPreviewTexturePreviewCallback',],
       'android.media' : [
           'cts.DecoderTest#testCodecResetsH264WithSurface',
@@ -237,15 +204,15 @@
           'cts.NativeMediaTest#test480pPlay',],
       'android.net' : [
           'cts.ConnectivityManagerTest#testStartUsingNetworkFeature_enableHipri',
-          'wifi.cts.ScanResultTest#testScanResultTimeStamp',
           'cts.DnsTest#testDnsWorks',
-          'cts.TrafficStatsTest#testTrafficStatsForLocalhost',
-          'wifi.cts.ScanResultTest#testAndroidTestCaseSetupProperly',
-          'wifi.cts.NsdManagerTest#testAndroidTestCaseSetupProperly',
           'cts.SSLCertificateSocketFactoryTest#testCreateSocket',
           'cts.SSLCertificateSocketFactoryTest#test_createSocket_bind',
           'cts.SSLCertificateSocketFactoryTest#test_createSocket_simple',
-          'cts.SSLCertificateSocketFactoryTest#test_createSocket_wrapping',],
+          'cts.SSLCertificateSocketFactoryTest#test_createSocket_wrapping',
+          'cts.TrafficStatsTest#testTrafficStatsForLocalhost',
+          'wifi.cts.NsdManagerTest#testAndroidTestCaseSetupProperly',
+          'wifi.cts.ScanResultTest#testAndroidTestCaseSetupProperly',
+          'wifi.cts.ScanResultTest#testScanResultTimeStamp',],
       'android.security' : [
           'cts.BannedFilesTest#testNoSu',
           'cts.BannedFilesTest#testNoSuInPath',
@@ -260,9 +227,7 @@
           'cts.WebViewClientTest#testOnReceivedHttpAuthRequest',
           'cts.WebViewClientTest#testOnScaleChanged',
           'cts.WebViewClientTest#testOnUnhandledKeyEvent',
-          'cts.WebViewTest#testSetInitialScale',],
-      'android.widget' : [
-          'cts.GridViewTest#testSetNumColumns',],}
+          'cts.WebViewTest#testSetInitialScale',]}
 
 def LogGenerateDescription(name):
   print 'Generating test description for package %s' % name
diff --git a/tools/utils/monsoon.py b/tools/utils/monsoon.py
new file mode 100755
index 0000000..f3d63c5
--- /dev/null
+++ b/tools/utils/monsoon.py
@@ -0,0 +1,422 @@
+#!/usr/bin/python2.6
+
+# 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.
+
+"""Interface for a USB-connected Monsoon power meter
+(http://msoon.com/LabEquipment/PowerMonitor/).
+This file requires gflags, which requires setuptools.
+To install setuptools: sudo apt-get install python-setuptools
+To install gflags, see http://code.google.com/p/python-gflags/
+To install pyserial, see http://pyserial.sourceforge.net/
+
+Example usages:
+  Set the voltage of the device 7536 to 4.0V
+  python2.6 monsoon.py --voltage=4.0 --serialno 7536
+
+  Get 5000hz data from device number 7536, with unlimited number of samples
+  python2.6 monsoon.py --samples -1 --hz 5000 --serialno 7536
+
+  Get 200Hz data for 5 seconds (1000 events) from default device
+  python2.6 monsoon.py --samples 100 --hz 200
+
+  Get unlimited 200Hz data from device attached at /dev/ttyACM0
+  python2.6 monsoon.py --samples -1 --hz 200 --device /dev/ttyACM0
+"""
+
+import fcntl
+import os
+import select
+import signal
+import stat
+import struct
+import sys
+import time
+import collections
+
+import gflags as flags  # http://code.google.com/p/python-gflags/
+
+import serial           # http://pyserial.sourceforge.net/
+
+FLAGS = flags.FLAGS
+
+class Monsoon:
+  """
+  Provides a simple class to use the power meter, e.g.
+  mon = monsoon.Monsoon()
+  mon.SetVoltage(3.7)
+  mon.StartDataCollection()
+  mydata = []
+  while len(mydata) < 1000:
+    mydata.extend(mon.CollectData())
+  mon.StopDataCollection()
+  """
+
+  def __init__(self, device=None, serialno=None, wait=1):
+    """
+    Establish a connection to a Monsoon.
+    By default, opens the first available port, waiting if none are ready.
+    A particular port can be specified with "device", or a particular Monsoon
+    can be specified with "serialno" (using the number printed on its back).
+    With wait=0, IOError is thrown if a device is not immediately available.
+    """
+
+    self._coarse_ref = self._fine_ref = self._coarse_zero = self._fine_zero = 0
+    self._coarse_scale = self._fine_scale = 0
+    self._last_seq = 0
+    self.start_voltage = 0
+
+    if device:
+      self.ser = serial.Serial(device, timeout=1)
+      return
+
+    while True:  # try all /dev/ttyACM* until we find one we can use
+      for dev in os.listdir("/dev"):
+        if not dev.startswith("ttyACM"): continue
+        tmpname = "/tmp/monsoon.%s.%s" % (os.uname()[0], dev)
+        self._tempfile = open(tmpname, "w")
+        try:
+          os.chmod(tmpname, 0666)
+        except OSError:
+          pass
+        try:  # use a lockfile to ensure exclusive access
+          fcntl.lockf(self._tempfile, fcntl.LOCK_EX | fcntl.LOCK_NB)
+        except IOError as e:
+          print >>sys.stderr, "device %s is in use" % dev
+          continue
+
+        try:  # try to open the device
+          self.ser = serial.Serial("/dev/%s" % dev, timeout=1)
+          self.StopDataCollection()  # just in case
+          self._FlushInput()  # discard stale input
+          status = self.GetStatus()
+        except Exception as e:
+          print >>sys.stderr, "error opening device %s: %s" % (dev, e)
+          continue
+
+        if not status:
+          print >>sys.stderr, "no response from device %s" % dev
+        elif serialno and status["serialNumber"] != serialno:
+          print >>sys.stderr, ("Note: another device serial #%d seen on %s" %
+                               (status["serialNumber"], dev))
+        else:
+          self.start_voltage = status["voltage1"]
+          return
+
+      self._tempfile = None
+      if not wait: raise IOError("No device found")
+      print >>sys.stderr, "waiting for device..."
+      time.sleep(1)
+
+
+  def GetStatus(self):
+    """ Requests and waits for status.  Returns status dictionary. """
+
+    # status packet format
+    STATUS_FORMAT = ">BBBhhhHhhhHBBBxBbHBHHHHBbbHHBBBbbbbbbbbbBH"
+    STATUS_FIELDS = [
+        "packetType", "firmwareVersion", "protocolVersion",
+        "mainFineCurrent", "usbFineCurrent", "auxFineCurrent", "voltage1",
+        "mainCoarseCurrent", "usbCoarseCurrent", "auxCoarseCurrent", "voltage2",
+        "outputVoltageSetting", "temperature", "status", "leds",
+        "mainFineResistor", "serialNumber", "sampleRate",
+        "dacCalLow", "dacCalHigh",
+        "powerUpCurrentLimit", "runTimeCurrentLimit", "powerUpTime",
+        "usbFineResistor", "auxFineResistor",
+        "initialUsbVoltage", "initialAuxVoltage",
+        "hardwareRevision", "temperatureLimit", "usbPassthroughMode",
+        "mainCoarseResistor", "usbCoarseResistor", "auxCoarseResistor",
+        "defMainFineResistor", "defUsbFineResistor", "defAuxFineResistor",
+        "defMainCoarseResistor", "defUsbCoarseResistor", "defAuxCoarseResistor",
+        "eventCode", "eventData", ]
+
+    self._SendStruct("BBB", 0x01, 0x00, 0x00)
+    while True:  # Keep reading, discarding non-status packets
+      bytes = self._ReadPacket()
+      if not bytes: return None
+      if len(bytes) != struct.calcsize(STATUS_FORMAT) or bytes[0] != "\x10":
+        print >>sys.stderr, "wanted status, dropped type=0x%02x, len=%d" % (
+                ord(bytes[0]), len(bytes))
+        continue
+
+      status = dict(zip(STATUS_FIELDS, struct.unpack(STATUS_FORMAT, bytes)))
+      assert status["packetType"] == 0x10
+      for k in status.keys():
+        if k.endswith("VoltageSetting"):
+          status[k] = 2.0 + status[k] * 0.01
+        elif k.endswith("FineCurrent"):
+          pass # needs calibration data
+        elif k.endswith("CoarseCurrent"):
+          pass # needs calibration data
+        elif k.startswith("voltage") or k.endswith("Voltage"):
+          status[k] = status[k] * 0.000125
+        elif k.endswith("Resistor"):
+          status[k] = 0.05 + status[k] * 0.0001
+          if k.startswith("aux") or k.startswith("defAux"): status[k] += 0.05
+        elif k.endswith("CurrentLimit"):
+          status[k] = 8 * (1023 - status[k]) / 1023.0
+      return status
+
+  def RampVoltage(self, start, end):
+    v = start
+    if v < 3.0: v = 3.0       # protocol doesn't support lower than this
+    while (v < end):
+      self.SetVoltage(v)
+      v += .1
+      time.sleep(.1)
+    self.SetVoltage(end)
+
+  def SetVoltage(self, v):
+    """ Set the output voltage, 0 to disable. """
+    if v == 0:
+      self._SendStruct("BBB", 0x01, 0x01, 0x00)
+    else:
+      self._SendStruct("BBB", 0x01, 0x01, int((v - 2.0) * 100))
+
+
+  def SetMaxCurrent(self, i):
+    """Set the max output current."""
+    assert i >= 0 and i <= 8
+
+    val = 1023 - int((i/8)*1023)
+    self._SendStruct("BBB", 0x01, 0x0a, val & 0xff)
+    self._SendStruct("BBB", 0x01, 0x0b, val >> 8)
+
+  def SetUsbPassthrough(self, val):
+    """ Set the USB passthrough mode: 0 = off, 1 = on,  2 = auto. """
+    self._SendStruct("BBB", 0x01, 0x10, val)
+
+
+  def StartDataCollection(self):
+    """ Tell the device to start collecting and sending measurement data. """
+    self._SendStruct("BBB", 0x01, 0x1b, 0x01) # Mystery command
+    self._SendStruct("BBBBBBB", 0x02, 0xff, 0xff, 0xff, 0xff, 0x03, 0xe8)
+
+
+  def StopDataCollection(self):
+    """ Tell the device to stop collecting measurement data. """
+    self._SendStruct("BB", 0x03, 0x00) # stop
+
+
+  def CollectData(self):
+    """ Return some current samples.  Call StartDataCollection() first. """
+    while True:  # loop until we get data or a timeout
+      bytes = self._ReadPacket()
+      if not bytes: return None
+      if len(bytes) < 4 + 8 + 1 or bytes[0] < "\x20" or bytes[0] > "\x2F":
+        print >>sys.stderr, "wanted data, dropped type=0x%02x, len=%d" % (
+            ord(bytes[0]), len(bytes))
+        continue
+
+      seq, type, x, y = struct.unpack("BBBB", bytes[:4])
+      data = [struct.unpack(">hhhh", bytes[x:x+8])
+              for x in range(4, len(bytes) - 8, 8)]
+
+      if self._last_seq and seq & 0xF != (self._last_seq + 1) & 0xF:
+        print >>sys.stderr, "data sequence skipped, lost packet?"
+      self._last_seq = seq
+
+      if type == 0:
+        if not self._coarse_scale or not self._fine_scale:
+          print >>sys.stderr, "waiting for calibration, dropped data packet"
+          continue
+
+        out = []
+        for main, usb, aux, voltage in data:
+          if main & 1:
+            out.append(((main & ~1) - self._coarse_zero) * self._coarse_scale)
+          else:
+            out.append((main - self._fine_zero) * self._fine_scale)
+        return out
+
+      elif type == 1:
+        self._fine_zero = data[0][0]
+        self._coarse_zero = data[1][0]
+        # print >>sys.stderr, "zero calibration: fine 0x%04x, coarse 0x%04x" % (
+        #     self._fine_zero, self._coarse_zero)
+
+      elif type == 2:
+        self._fine_ref = data[0][0]
+        self._coarse_ref = data[1][0]
+        # print >>sys.stderr, "ref calibration: fine 0x%04x, coarse 0x%04x" % (
+        #     self._fine_ref, self._coarse_ref)
+
+      else:
+        print >>sys.stderr, "discarding data packet type=0x%02x" % type
+        continue
+
+      if self._coarse_ref != self._coarse_zero:
+        self._coarse_scale = 2.88 / (self._coarse_ref - self._coarse_zero)
+      if self._fine_ref != self._fine_zero:
+        self._fine_scale = 0.0332 / (self._fine_ref - self._fine_zero)
+
+
+  def _SendStruct(self, fmt, *args):
+    """ Pack a struct (without length or checksum) and send it. """
+    data = struct.pack(fmt, *args)
+    data_len = len(data) + 1
+    checksum = (data_len + sum(struct.unpack("B" * len(data), data))) % 256
+    out = struct.pack("B", data_len) + data + struct.pack("B", checksum)
+    self.ser.write(out)
+
+
+  def _ReadPacket(self):
+    """ Read a single data record as a string (without length or checksum). """
+    len_char = self.ser.read(1)
+    if not len_char:
+      print >>sys.stderr, "timeout reading from serial port"
+      return None
+
+    data_len = struct.unpack("B", len_char)
+    data_len = ord(len_char)
+    if not data_len: return ""
+
+    result = self.ser.read(data_len)
+    if len(result) != data_len: return None
+    body = result[:-1]
+    checksum = (data_len + sum(struct.unpack("B" * len(body), body))) % 256
+    if result[-1] != struct.pack("B", checksum):
+      print >>sys.stderr, "invalid checksum from serial port"
+      return None
+    return result[:-1]
+
+  def _FlushInput(self):
+    """ Flush all read data until no more available. """
+    self.ser.flush()
+    flushed = 0
+    while True:
+      ready_r, ready_w, ready_x = select.select([self.ser], [], [self.ser], 0)
+      if len(ready_x) > 0:
+        print >>sys.stderr, "exception from serial port"
+        return None
+      elif len(ready_r) > 0:
+        flushed += 1
+        self.ser.read(1)  # This may cause underlying buffering.
+        self.ser.flush()  # Flush the underlying buffer too.
+      else:
+        break
+    if flushed > 0:
+      print >>sys.stderr, "dropped >%d bytes" % flushed
+
+def main(argv):
+  """ Simple command-line interface for Monsoon."""
+  useful_flags = ["voltage", "status", "usbpassthrough", "samples", "current"]
+  if not [f for f in useful_flags if FLAGS.get(f, None) is not None]:
+    print __doc__.strip()
+    print FLAGS.MainModuleHelp()
+    return
+
+  if FLAGS.avg and FLAGS.avg < 0:
+    print "--avg must be greater than 0"
+    return
+
+  mon = Monsoon(device=FLAGS.device, serialno=FLAGS.serialno)
+
+  if FLAGS.voltage is not None:
+    if FLAGS.ramp is not None:
+      mon.RampVoltage(mon.start_voltage, FLAGS.voltage)
+    else:
+      mon.SetVoltage(FLAGS.voltage)
+
+  if FLAGS.current is not None:
+    mon.SetMaxCurrent(FLAGS.current)
+
+  if FLAGS.status:
+    items = sorted(mon.GetStatus().items())
+    print "\n".join(["%s: %s" % item for item in items])
+
+  if FLAGS.usbpassthrough:
+    if FLAGS.usbpassthrough == 'off':
+      mon.SetUsbPassthrough(0)
+    elif FLAGS.usbpassthrough == 'on':
+      mon.SetUsbPassthrough(1)
+    elif FLAGS.usbpassthrough == 'auto':
+      mon.SetUsbPassthrough(2)
+    else:
+      sys.exit('bad passthrough flag: %s' % FLAGS.usbpassthrough)
+
+  if FLAGS.samples:
+    # Make sure state is normal
+    mon.StopDataCollection()
+    status = mon.GetStatus()
+    native_hz = status["sampleRate"] * 1000
+
+    # Collect and average samples as specified
+    mon.StartDataCollection()
+
+    # In case FLAGS.hz doesn't divide native_hz exactly, use this invariant:
+    # 'offset' = (consumed samples) * FLAGS.hz - (emitted samples) * native_hz
+    # This is the error accumulator in a variation of Bresenham's algorithm.
+    emitted = offset = 0
+    collected = []
+    history_deque = collections.deque() # past n samples for rolling average
+
+    try:
+      last_flush = time.time()
+      while emitted < FLAGS.samples or FLAGS.samples == -1:
+        # The number of raw samples to consume before emitting the next output
+        need = (native_hz - offset + FLAGS.hz - 1) / FLAGS.hz
+        if need > len(collected):     # still need more input samples
+          samples = mon.CollectData()
+          if not samples: break
+          collected.extend(samples)
+        else:
+          # Have enough data, generate output samples.
+          # Adjust for consuming 'need' input samples.
+          offset += need * FLAGS.hz
+          while offset >= native_hz:  # maybe multiple, if FLAGS.hz > native_hz
+            this_sample = sum(collected[:need]) / need
+
+            if FLAGS.timestamp: print int(time.time()),
+
+            if FLAGS.avg:
+              history_deque.appendleft(this_sample)
+              if len(history_deque) > FLAGS.avg: history_deque.pop()
+              print "%f %f" % (this_sample,
+                               sum(history_deque) / len(history_deque))
+            else:
+              print "%f" % this_sample
+            sys.stdout.flush()
+
+            offset -= native_hz
+            emitted += 1              # adjust for emitting 1 output sample
+          collected = collected[need:]
+          now = time.time()
+          if now - last_flush >= 0.99:  # flush every second
+            sys.stdout.flush()
+            last_flush = now
+    except KeyboardInterrupt:
+      print >>sys.stderr, "interrupted"
+
+    mon.StopDataCollection()
+
+
+if __name__ == '__main__':
+  # Define flags here to avoid conflicts with people who use us as a library
+  flags.DEFINE_boolean("status", None, "Print power meter status")
+  flags.DEFINE_integer("avg", None,
+                       "Also report average over last n data points")
+  flags.DEFINE_float("voltage", None, "Set output voltage (0 for off)")
+  flags.DEFINE_float("current", None, "Set max output current")
+  flags.DEFINE_string("usbpassthrough", None, "USB control (on, off, auto)")
+  flags.DEFINE_integer("samples", None, "Collect and print this many samples")
+  flags.DEFINE_integer("hz", 5000, "Print this many samples/sec")
+  flags.DEFINE_string("device", None,
+                      "Path to the device in /dev/... (ex:/dev/ttyACM1)")
+  flags.DEFINE_integer("serialno", None, "Look for a device with this serial number")
+  flags.DEFINE_boolean("timestamp", None,
+                       "Also print integer (seconds) timestamp on each line")
+  flags.DEFINE_boolean("ramp", True, "Gradually increase voltage")
+
+  main(FLAGS(sys.argv))
diff --git a/tools/vm-tests-tf/etc/starttests b/tools/vm-tests-tf/etc/starttests
new file mode 100755
index 0000000..be8fad4
--- /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 -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..6f5226c 100644
--- a/tools/vm-tests-tf/src/util/build/BuildDalvikSuite.java
+++ b/tools/vm-tests-tf/src/util/build/BuildDalvikSuite.java
@@ -212,17 +212,21 @@
         "package " + pName + ";\n" +
         "import java.io.IOException;\n" +
         "import com.android.tradefed.testtype.DeviceTestCase;\n" +
+        "import com.android.tradefed.config.Option;\n" +
+        "import com.android.tradefed.config.Option.Importance;\n" +
+        "import com.android.tradefed.util.AbiFormatter;\n" +
         "\n" +
         "public class " + sourceName + " extends DeviceTestCase {\n";
     }
 
     private String getShellExecJavaLine(String classpath, String mainclass) {
-      String cmd = String.format("ANDROID_DATA=%s dalvikvm -Xint:portable -Xmx512M -Xss32K " +
+      String cmd = String.format("ANDROID_DATA=%s dalvikvm|#ABI#| -Xmx512M -Xss32K " +
               "-Djava.io.tmpdir=%s -classpath %s %s", TARGET_JAR_ROOT_PATH, TARGET_JAR_ROOT_PATH,
               classpath, mainclass);
-      return "String res = getDevice().executeShellCommand(\""+ cmd + "\");\n" +
-             "// A sucessful adb shell command returns an empty string.\n" +
-             "assertEquals(\"" + cmd + "\", \"\", res);";
+      return "String cmd = AbiFormatter.formatCmdForAbi(\"" + cmd + "\", mForceAbi);\n" +
+          "String res = getDevice().executeShellCommand(cmd);\n" +
+          "// A sucessful adb shell command returns an empty string.\n" +
+          "assertEquals(cmd, \"\", res);";
     }
 
     private String getWarningMessage() {
@@ -260,6 +264,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);
@@ -278,6 +283,10 @@
 
             openCTSHostFileFor(pName, classOnlyName);
 
+            curJunitFileData += "@Option(name = AbiFormatter.FORCE_ABI_STRING,\n" +
+                "description = AbiFormatter.FORCE_ABI_DESCRIPTION,\n" +
+                "importance = Importance.IF_UNSET)\nprivate String mForceAbi = null;\n\n";
+
             List<String> methods = entry.getValue();
             Collections.sort(methods, new Comparator<String>() {
                 public int compare(String s1, String s2) {
@@ -336,6 +345,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 +435,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);