Merge "DO NOT MERGE Add comment for false-negative test on emulator" into nougat-cts-dev
diff --git a/apps/CtsVerifier/AndroidManifest.xml b/apps/CtsVerifier/AndroidManifest.xml
index 3411f47..7815c33 100644
--- a/apps/CtsVerifier/AndroidManifest.xml
+++ b/apps/CtsVerifier/AndroidManifest.xml
@@ -18,7 +18,7 @@
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
       package="com.android.cts.verifier"
       android:versionCode="5"
-      android:versionName="7.0_r3">
+      android:versionName="7.0_r5">
 
     <uses-sdk android:minSdkVersion="19" android:targetSdkVersion="24"/>
 
@@ -1965,7 +1965,7 @@
             </intent-filter>
             <meta-data android:name="test_category" android:value="@string/test_category_other" />
             <meta-data android:name="test_excluded_features"
-                       android:value="android.hardware.type.television:android.software.leanback:android.hardware.type.watch" />
+                       android:value="android.hardware.type.television:android.software.leanback:android.hardware.type.watch:android.hardware.type.automotive" />
         </activity>
 
         <activity android:name=".tv.MockTvInputSetupActivity">
diff --git a/apps/CtsVerifier/res/layout/audio_frequency_unprocessed_activity.xml b/apps/CtsVerifier/res/layout/audio_frequency_unprocessed_activity.xml
index abc8033..142ffff 100644
--- a/apps/CtsVerifier/res/layout/audio_frequency_unprocessed_activity.xml
+++ b/apps/CtsVerifier/res/layout/audio_frequency_unprocessed_activity.xml
@@ -1,18 +1,11 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2015 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
+<!-- Copyright (C) 2015 The Android Open Source Project Licensed under the
+    Apache License, Version 2.0 (the "License"); you may not use this file except
+    in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+    Unless required by applicable law or agreed to in writing, software distributed
+    under the License is distributed on an "AS IS" BASIS, WITHOUT 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"
@@ -24,54 +17,285 @@
     <ScrollView
         android:layout_width="match_parent"
         android:layout_height="match_parent"
-        android:id="@+id/scrollView"
-    >
+        android:id="@+id/scrollView">
 
         <LinearLayout
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
-            android:orientation="vertical"
-        >
+            android:orientation="vertical">
 
-        <TextView
+            <TextView
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
                 android:scrollbars="vertical"
                 android:gravity="bottom"
-                android:id="@+id/audio_frequency_unprocessed_defined"/>
-
-                <ProgressBar
-                    android:layout_width="wrap_content"
-                    android:layout_height="wrap_content"
-                    android:layout_weight="1"
-                    android:id="@+id/audio_frequency_unprocessed_progress_bar" />
+                android:id="@+id/audio_frequency_unprocessed_defined" />
 
             <LinearLayout
-                android:layout_width="wrap_content"
+                android:layout_width="match_parent"
                 android:layout_height="wrap_content"
                 android:orientation="vertical"
-                android:id="@+id/audio_frequency_unprocessed_layout_test1"
+                android:id="@+id/unprocessed_layout_test_tone"
             >
 
-                <TextView
-                    android:layout_width="wrap_content"
-                    android:layout_height="wrap_content"
-                    android:text="@string/audio_frequency_unprocessed_instructions2"
-                    android:id="@+id/audio_frequency_unprocessed_instructions2" />
-
-                <Button
-                    android:layout_width="wrap_content"
-                    android:layout_height="wrap_content"
-                    android:text="@string/audio_frequency_unprocessed_test1_btn"
-                    android:id="@+id/audio_frequency_unprocessed_test1_btn" />
+                <View
+                    android:layout_width="match_parent"
+                    android:layout_height="1dp"
+                    android:background="?android:colorAccent" />
 
                 <TextView
                     android:layout_width="wrap_content"
                     android:layout_height="wrap_content"
-                    android:text="@string/audio_frequency_unprocessed_results_text"
-                    android:id="@+id/audio_frequency_unprocessed_results1_text" />
+                    android:scrollbars="vertical"
+                    android:gravity="bottom"
+                    android:text="@string/unprocessed_test_tone_instructions"
+                    android:id="@+id/unprocessed_test_tone_instructions" />
+
+                <LinearLayout
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content"
+                    android:orientation="horizontal">
+
+                    <LinearLayout
+                        android:layout_width="wrap_content"
+                        android:layout_height="wrap_content"
+                        android:orientation="vertical"
+                        android:layout_weight="2">
+
+                        <Button
+                            android:layout_width="wrap_content"
+                            android:layout_height="wrap_content"
+                            android:soundEffectsEnabled="false"
+                            android:text="@string/unprocessed_test_tone_btn"
+                            android:id="@+id/unprocessed_test_tone_btn" />
+                        <ProgressBar
+                            android:layout_width="wrap_content"
+                            android:layout_height="wrap_content"
+                            android:layout_weight="1"
+                            android:id="@+id/unprocessed_test_tone_progress_bar" />
+
+                    </LinearLayout>
+
+                    <View
+                        android:layout_width="1dp"
+                        android:layout_height="match_parent"
+                        android:background="?android:colorAccent" />
+
+                    <LinearLayout
+                        android:layout_width="0dp"
+                        android:layout_height="wrap_content"
+                        android:orientation="vertical"
+                        android:layout_weight="1">
+                        <Button
+                            android:layout_width="wrap_content"
+                            android:layout_height="wrap_content"
+                            android:soundEffectsEnabled="false"
+                            android:text="@string/unprocessed_play"
+                            android:id="@+id/unprocessed_play_tone_btn" />
+                    </LinearLayout>
+                </LinearLayout>
+                <TextView
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content"
+                    android:text="@string/unprocessed_test_tone_result"
+                    android:id="@+id/unprocessed_test_tone_result" />
             </LinearLayout>
 
+            <LinearLayout
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:orientation="vertical"
+                android:id="@+id/unprocessed_layout_test_noise">
+
+                <View
+                    android:layout_width="match_parent"
+                    android:layout_height="1dp"
+                    android:background="?android:colorAccent" />
+
+                <TextView
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:scrollbars="vertical"
+                    android:gravity="bottom"
+                    android:text="@string/unprocessed_test_noise_instructions"
+                    android:id="@+id/unprocessed_test_noise_instructions" />
+
+                <LinearLayout
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content"
+                    android:orientation="horizontal">
+
+                    <LinearLayout
+                        android:layout_width="wrap_content"
+                        android:layout_height="wrap_content"
+                        android:orientation="vertical"
+                        android:layout_weight="2">
+
+                        <Button
+                            android:layout_width="wrap_content"
+                            android:layout_height="wrap_content"
+                            android:soundEffectsEnabled="false"
+                            android:text="@string/unprocessed_test_noise_btn"
+                            android:id="@+id/unprocessed_test_noise_btn" />
+                        <ProgressBar
+                            android:layout_width="wrap_content"
+                            android:layout_height="wrap_content"
+                            android:layout_weight="1"
+                            android:id="@+id/unprocessed_test_noise_progress_bar" />
+                    </LinearLayout>
+
+                    <View
+                        android:layout_width="1dp"
+                        android:layout_height="match_parent"
+                        android:background="?android:colorAccent" />
+
+                    <LinearLayout
+                        android:layout_width="0dp"
+                        android:layout_height="wrap_content"
+                        android:orientation="vertical"
+                        android:layout_weight="1">
+                        <Button
+                            android:layout_width="wrap_content"
+                            android:layout_height="wrap_content"
+                            android:soundEffectsEnabled="false"
+                            android:text="@string/unprocessed_play"
+                            android:id="@+id/unprocessed_play_noise_btn" />
+                    </LinearLayout>
+                </LinearLayout>
+                <TextView
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content"
+                    android:text="@string/unprocessed_test_noise_result"
+                    android:id="@+id/unprocessed_test_noise_result" />
+            </LinearLayout>
+
+        <LinearLayout
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:orientation="vertical"
+                android:id="@+id/unprocessed_layout_test_usb_background">
+
+                <View
+                    android:layout_width="match_parent"
+                    android:layout_height="1dp"
+                    android:background="?android:colorAccent" />
+
+                <TextView
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:scrollbars="vertical"
+                    android:gravity="bottom"
+                    android:text="@string/unprocessed_test_usb_background_instructions"
+                    android:id="@+id/unprocessed_test_usb_background_instructions" />
+
+                <LinearLayout
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content"
+                    android:orientation="horizontal">
+
+                    <LinearLayout
+                        android:layout_width="wrap_content"
+                        android:layout_height="wrap_content"
+                        android:orientation="vertical"
+                        android:layout_weight="2">
+
+                        <Button
+                            android:layout_width="wrap_content"
+                            android:layout_height="wrap_content"
+                            android:soundEffectsEnabled="false"
+                            android:text="@string/unprocessed_test_usb_background_btn"
+                            android:id="@+id/unprocessed_test_usb_background_btn" />
+                        <ProgressBar
+                            android:layout_width="wrap_content"
+                            android:layout_height="wrap_content"
+                            android:layout_weight="1"
+                            android:id="@+id/unprocessed_test_usb_background_progress_bar" />
+                    </LinearLayout>
+                </LinearLayout>
+                <TextView
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content"
+                    android:text="@string/unprocessed_test_usb_background_result"
+                    android:id="@+id/unprocessed_test_usb_background_result" />
+            </LinearLayout>
+
+            <LinearLayout
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:orientation="vertical"
+                android:id="@+id/unprocessed_layout_test_usb_noise">
+
+                <View
+                    android:layout_width="match_parent"
+                    android:layout_height="1dp"
+                    android:background="?android:colorAccent" />
+
+                <TextView
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:scrollbars="vertical"
+                    android:gravity="bottom"
+                    android:text="@string/unprocessed_test_usb_noise_instructions"
+                    android:id="@+id/unprocessed_test_usb_noise_instructions" />
+
+                <LinearLayout
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content"
+                    android:orientation="horizontal">
+
+                    <LinearLayout
+                        android:layout_width="wrap_content"
+                        android:layout_height="wrap_content"
+                        android:orientation="vertical"
+                        android:layout_weight="2">
+
+                        <Button
+                            android:layout_width="wrap_content"
+                            android:layout_height="wrap_content"
+                            android:soundEffectsEnabled="false"
+                            android:text="@string/unprocessed_test_usb_noise_btn"
+                            android:id="@+id/unprocessed_test_usb_noise_btn" />
+                        <ProgressBar
+                            android:layout_width="wrap_content"
+                            android:layout_height="wrap_content"
+                            android:layout_weight="1"
+                            android:id="@+id/unprocessed_test_usb_noise_progress_bar" />
+                    </LinearLayout>
+
+                    <View
+                        android:layout_width="1dp"
+                        android:layout_height="match_parent"
+                        android:background="?android:colorAccent" />
+
+                    <LinearLayout
+                        android:layout_width="0dp"
+                        android:layout_height="wrap_content"
+                        android:orientation="vertical"
+                        android:layout_weight="1">
+                        <Button
+                            android:layout_width="wrap_content"
+                            android:layout_height="wrap_content"
+                            android:soundEffectsEnabled="false"
+                            android:text="@string/unprocessed_play"
+                            android:id="@+id/unprocessed_play_usb_noise_btn" />
+                    </LinearLayout>
+                </LinearLayout>
+                <TextView
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content"
+                    android:text="@string/unprocessed_test_usb_noise_result"
+                    android:id="@+id/unprocessed_test_usb_noise_result" />
+            </LinearLayout>
+             <View
+                android:layout_width="match_parent"
+                android:layout_height="1dp"
+                android:background="?android:colorAccent" />
+
+            <TextView
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content"
+                    android:text="@string/unprocessed_test_global_result"
+                    android:id="@+id/unprocessed_test_global_result" />
 
             <include layout="@layout/pass_fail_buttons" />
         </LinearLayout>
diff --git a/apps/CtsVerifier/res/raw/onekhztone.mp3 b/apps/CtsVerifier/res/raw/onekhztone.mp3
new file mode 100644
index 0000000..c0e6660
--- /dev/null
+++ b/apps/CtsVerifier/res/raw/onekhztone.mp3
Binary files differ
diff --git a/apps/CtsVerifier/res/values/strings.xml b/apps/CtsVerifier/res/values/strings.xml
old mode 100644
new mode 100755
index c197a25..6cb4778
--- a/apps/CtsVerifier/res/values/strings.xml
+++ b/apps/CtsVerifier/res/values/strings.xml
@@ -342,7 +342,7 @@
     <string name="ble_low">Low</string>
     <string name="ble_medium">Medium</string>
     <string name="ble_high">High</string>
-    <string name="ble_scanner_power_level_instruction">Count: Ultra low &lt; low &lt; medium &lt; high\nRssi: Ultra low &lt; low &lt; medium &lt; high\nDistance to see count freezing: Ultra low &lt; low &lt; medium &lt; high\nA common error is ultra low, low and medium behave similarly, with similar rssi, freeze at similar distance.\n\n All power level receive a different mac address. After 15 mins, a green text "Get a new Mac address" will show up.</string>
+    <string name="ble_scanner_power_level_instruction">Count: Ultra low &lt; low &lt; medium &lt; high\nRssi: Ultra low &lt; low &lt; medium &lt; high\nDistance to see count freezing: Ultra low &lt; low &lt; medium &lt; high\nA common error is ultra low, low and medium behave similarly, with similar rssi, freeze at similar distance.\n\n All power level receive a mac address.</string>
     <string name="ble_scanner_scan_filter_name">BLE Hardware Scan Filter</string>
     <string name="ble_scanner_scan_filter_info">Lock the screen of scanner, and connect to monsoon. It will not wake up when advertiser is advertising unscannable, and scanner is scanning with filter.</string>
     <string name="ble_scanner_scan_filter_instruction">Scan filter is to scan data with service UUID = 0x6666 only. If you scan without scan filter, data with service UUID = 0x5555 and 0x6666 will show up on screen.\nFor monsoon test:\n\tClick scan with filter, lock the screen, connect to monsoon. It will not wake up when advertiser is advertising unscannable data packets, but will show a peak in power usage when advertiser is advertising scannable data.\nFor logcat test:\n\tClick scan with filter, logcat the scanner. No data will be received by GattService when advertiser is advertising unscannable data.</string>
@@ -2252,10 +2252,6 @@
         This was expected.\n
         Mark this test as passed.\n
     </string>
-    <string name="device_owner_vpn_connection_canceled">
-        Cannot establish a VPN connection.\n
-        Connection canceled by user.\n
-    </string>
     <string name="device_owner_vpn_test">Check VPN</string>
     <string name="device_owner_vpn_info_default">Vpn test message</string>
 
@@ -2269,7 +2265,8 @@
         - You cannot edit, add or remove any existing VPNs.\n
         - Trying to perform any of the above actions will trigger a support message.\n\n
         2. Press Check VPN to check programmatic Vpn test.\n
-        - Check Vpn setup\n\n
+        - Check Vpn setup is not allowed\n
+        - If prompted to allow a VPN connection, press OK.\n\n
         Use the Back button to return to this page.
     </string>
     <string name="device_owner_user_vpn_restriction_set">Set VPN restriction</string>
@@ -2469,7 +2466,7 @@
     <string name="password_quality_complex">Complex</string>
     <string name="set_permitted_accessibility_services">Set permitted accessibility services</string>
     <string name="permitted_accessibility_services_set_step">
-        Disallow \'Dummy Accessibility service\' from permitted accessibility services by turning on
+        Check that \'Dummy Accessibility service\' is not enabled in Settings and disallow \'Dummy Accessibility service\' from permitted accessibility services by turning on
         the switch below.
     </string>
     <string name="set_permitted_accessibility_services_action">
@@ -2480,7 +2477,7 @@
     </string>
     <string name="set_permitted_input_methods">Set permitted input methods</string>
     <string name="permitted_input_methods_set_step">
-        Disallow \'Dummy Input method\' from permitted input methods by turning on the switch below.
+        Check that \'Dummy Input method\' is not enabled in Settings and disallow \'Dummy Input method\' from permitted input methods by turning on the switch below.
     </string>
     <string name="set_permitted_input_methods_action">
         Enabling \'Dummy Input Method\' in the list of accessibility services
@@ -2741,7 +2738,7 @@
     <string name="audio_general_headset_yes">Yes</string>
     <string name="audio_general_deficiency_found">WARNING: Some results show potential deficiencies on the system.
     Please consider addressing them for a future release.</string>
-    <string name="audio_general_test_passed">Test Successful</string>
+    <string name="audio_general_test_passed">Test Result: Successful</string>
     <string name="audio_general_test_failed">Test Result: Not Optimal</string>
     <string name="audio_general_default_false_string">false</string>
     <string name="audio_general_default_true_string">true</string>
@@ -2835,17 +2832,39 @@
      <!-- Audio Frequency Unprocessed Test -->
     <string name="audio_frequency_unprocessed_test">Audio Frequency Unprocessed Test</string>
     <string name="audio_frequency_unprocessed_info">
-   This test requires an external broadband noise source (or click/impulse).
-   Please be prepared to activate the noise source when asked to.
-   The system will measure frequency response of the built in microphone using the UNPROCESSED
-   audio source.
+    This test requires an external USB reference microphone, external speakers and a Sound Pressure Level meter.
+    You can play the test signals from the device under test or from a secondary device.
+    Follow the instructions on the screen to measure the frequency response for the built in microphone
+    using UNPROCESSED audio source.
+    If the Audio Frequency Unprocessed feature is defined in this system, success in all tests is mandatory to pass.
+    If the feature is not defined, measurements are still needed, but success in all of them is not mandatory to pass.
        </string>
-    <string name="audio_frequency_unprocessed_defined">Audio Frequency Unprocessed feature is defined. Test is mandatory</string>
-    <string name="audio_frequency_unprocessed_not_defined">Audio Frequency Unprocessed feature is NOT defined. Test is optional</string>
-     <string name="audio_frequency_unprocessed_instructions2">
-          Once you press the [TEST] button, you have 5 seconds to play a broadband sound (click or white noise).
-    </string>
-    <string name="audio_frequency_unprocessed_test1_btn">Test 1</string>
-    <string name="audio_frequency_unprocessed_results_text">Results...</string>
+    <string name="audio_frequency_unprocessed_defined">Audio Frequency Unprocessed feature is defined. Success in all tests is mandatory to pass</string>
+    <string name="audio_frequency_unprocessed_not_defined">Audio Frequency Unprocessed feature is NOT defined. Success in all test is NOT mandatory to pass</string>
+
+    <string name="unprocessed_play">Play</string>
+    <string name="unprocessed_stop">Stop</string>
+
+    <string name="unprocessed_test_tone_instructions">TEST TONE: Press [PLAY] to play tone at 1 Khz. Measure sound SPL to be 94 dB
+    right next to microphone under test. Press [TEST]</string>
+    <string name="unprocessed_test_tone_btn">Test</string>
+    <string name="unprocessed_test_tone_result">Results...</string>
+
+    <string name="unprocessed_test_noise_instructions">TEST NOISE: Position speakers 40 cms from device under test.
+    Press [PLAY] to play broadband white noise. Press [TEST]</string>
+    <string name="unprocessed_test_noise_btn">Test</string>
+    <string name="unprocessed_test_noise_result">Results...</string>
+
+    <string name="unprocessed_test_usb_background_instructions">TEST USB BACKGROUND: Connect USB microphone and position it right next to microphone under test.
+    No source of noise should be active during this test. Press [TEST]</string>
+    <string name="unprocessed_test_usb_background_btn">Test</string>
+    <string name="unprocessed_test_usb_background_result">Results...</string>
+
+    <string name="unprocessed_test_usb_noise_instructions">TEST USB NOISE: Connect USB microphone and position it right next to microphone under test. 
+    Position speakers 40 cms from device under test. Press [PLAY] to play broadband white noise. Press [TEST]</string>
+    <string name="unprocessed_test_usb_noise_btn">Test</string>
+    <string name="unprocessed_test_usb_noise_result">Results...</string>
+
+    <string name="unprocessed_test_global_result">Global Results...</string>
 
 </resources>
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioFrequencyUnprocessedActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioFrequencyUnprocessedActivity.java
index 56275c5..009dd58 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioFrequencyUnprocessedActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioFrequencyUnprocessedActivity.java
@@ -22,11 +22,11 @@
 import com.android.compatibility.common.util.ReportLog;
 import com.android.compatibility.common.util.ResultType;
 import com.android.compatibility.common.util.ResultUnit;
+
 import android.content.Context;
 import android.content.BroadcastReceiver;
 import android.content.Intent;
 import android.content.IntentFilter;
-
 import android.media.AudioDeviceCallback;
 import android.media.AudioDeviceInfo;
 import android.media.AudioFormat;
@@ -34,17 +34,13 @@
 import android.media.AudioTrack;
 import android.media.AudioRecord;
 import android.media.MediaRecorder;
-
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.Message;
 import android.os.SystemClock;
-
 import android.util.Log;
-
 import android.view.View;
 import android.view.View.OnClickListener;
-
 import android.widget.Button;
 import android.widget.TextView;
 import android.widget.SeekBar;
@@ -59,25 +55,63 @@
     private static final String TAG = "AudioFrequencyUnprocessedActivity";
 
     private static final int TEST_STARTED = 900;
-    private static final int TEST_ENDED = 901;
-    private static final int TEST_MESSAGE = 902;
-    private static final int TEST1_MESSAGE = 903;
-    private static final int TEST1_ENDED = 904;
-    private static final double MIN_ENERGY_BAND_1 = -50.0;          //dB Full Scale
-    private static final double MAX_ENERGY_BAND_1_BASE = -60.0;     //dB Full Scale
-    private static final double MIN_FRACTION_POINTS_IN_BAND = 0.3;
+    private static final int TEST_MESSAGE = 903;
+    private static final int TEST_ENDED = 904;
+    private static final int TEST_ENDED_ERROR = 905;
+    private static final double MIN_FRACTION_POINTS_IN_BAND = 0.5;
+
+    private static final double TONE_RMS_EXPECTED = -36.0;
+    private static final double TONE_RMS_MAX_ERROR = 3.0;
+
     private static final double MAX_VAL = Math.pow(2, 15);
     private static final double CLIP_LEVEL = (MAX_VAL-10) / MAX_VAL;
 
+    private static final int SOURCE_TONE = 0;
+    private static final int SOURCE_NOISE = 1;
+
+    private static final int TEST_NONE = -1;
+    private static final int TEST_TONE = 0;
+    private static final int TEST_NOISE = 1;
+    private static final int TEST_USB_BACKGROUND = 2;
+    private static final int TEST_USB_NOISE = 3;
+    private static final int TEST_COUNT = 4;
+    private int mCurrentTest = TEST_NONE;
+    private boolean mTestsDone[] = new boolean[TEST_COUNT];
+
+    private static final int TEST_DURATION_DEFAULT = 2000;
+    private static final int TEST_DURATION_TONE = TEST_DURATION_DEFAULT;
+    private static final int TEST_DURATION_NOISE = TEST_DURATION_DEFAULT;
+    private static final int TEST_DURATION_USB_BACKGROUND = TEST_DURATION_DEFAULT;
+    private static final int TEST_DURATION_USB_NOISE = TEST_DURATION_DEFAULT;
+
     final OnBtnClickListener mBtnClickListener = new OnBtnClickListener();
     Context mContext;
 
-    Button mTest1Button;                //execute test 1
-    Button mTest2Button;                 //user to start test
-    String mUsbDevicesInfo;             //usb device info for report
-    LinearLayout mLayoutTest1;
-    TextView mTest1Result;
-    ProgressBar mProgressBar;
+    LinearLayout mLayoutTestTone;
+    Button mButtonTestTone;
+    ProgressBar mProgressTone;
+    TextView mResultTestTone;
+    Button mButtonPlayTone;
+
+    LinearLayout mLayoutTestNoise;
+    Button mButtonTestNoise;
+    ProgressBar mProgressNoise;
+    TextView mResultTestNoise;
+    Button mButtonPlayNoise;
+
+    LinearLayout mLayoutTestUsbBackground;
+    Button mButtonTestUsbBackground;
+    ProgressBar mProgressUsbBackground;
+    TextView mResultTestUsbBackground;
+
+    LinearLayout mLayoutTestUsbNoise;
+    Button mButtonTestUsbNoise;
+    ProgressBar mProgressUsbNoise;
+    TextView mResultTestUsbNoise;
+    Button mButtonPlayUsbNoise;
+
+    TextView mGlobalResultText;
+    TextView mTextViewUnprocessedStatus;
 
     private boolean mIsRecording = false;
     private final Object mRecordingLock = new Object();
@@ -95,6 +129,8 @@
 
     PipeShort mPipe = new PipeShort(65536);
 
+    SoundPlayerObject mSPlayer;
+
     private boolean mSupportsUnprocessed = false;
 
     private DspBufferComplex mC;
@@ -102,25 +138,27 @@
 
     private DspWindow mWindow;
     private DspFftServer mFftServer;
-    private VectorAverage mFreqAverageMain = new VectorAverage();
-    private VectorAverage mFreqAverageBuiltIn = new VectorAverage();
+    private VectorAverage mFreqAverageTone = new VectorAverage();
+    private VectorAverage mFreqAverageNoise = new VectorAverage();
+    private VectorAverage mFreqAverageUsbBackground = new VectorAverage();
+    private VectorAverage mFreqAverageUsbNoise = new VectorAverage();
 
-    int mBands = 4;
-    AudioBandSpecs[] bandSpecsArray = new AudioBandSpecs[mBands];
-    private TextView mTextViewUnprocessedStatus;
+    //RMS for tone:
+    private double mRMS;
+    private double mRMSMax;
 
-    private class OnBtnClickListener implements OnClickListener {
-        @Override
-        public void onClick(View v) {
-            switch (v.getId()) {
-            case R.id.audio_frequency_unprocessed_test1_btn:
-                setMaxLevel();
-                testMaxLevel();
-                startTest1();
-                break;
-            }
-        }
-    }
+    private double mRMSTone;
+    private double mRMSMaxTone;
+
+    int mBands = 3;
+    int mBandsTone = 3;
+    int mBandsBack = 3;
+    AudioBandSpecs[] mBandSpecsMic = new AudioBandSpecs[mBands];
+    AudioBandSpecs[] mBandSpecsTone = new AudioBandSpecs[mBandsTone];
+    AudioBandSpecs[] mBandSpecsBack = new AudioBandSpecs[mBandsBack];
+    private Results mResultsMic;
+    private Results mResultsTone;
+    private Results mResultsBack;
 
     @Override
     protected void onCreate(Bundle savedInstanceState) {
@@ -139,13 +177,51 @@
                     getResources().getText(R.string.audio_frequency_unprocessed_not_defined));
         }
 
-        mTest1Button = (Button)findViewById(R.id.audio_frequency_unprocessed_test1_btn);
-        mTest1Button.setOnClickListener(mBtnClickListener);
-        mTest1Result = (TextView)findViewById(R.id.audio_frequency_unprocessed_results1_text);
-        mLayoutTest1 = (LinearLayout) findViewById(R.id.audio_frequency_unprocessed_layout_test1);
-        mProgressBar = (ProgressBar)findViewById(R.id.audio_frequency_unprocessed_progress_bar);
-        showWait(false);
-        enableLayout(mLayoutTest1, true);
+        mSPlayer = new SoundPlayerObject();
+        playerSetSource(SOURCE_TONE);
+
+        // Test tone
+        mLayoutTestTone = (LinearLayout) findViewById(R.id.unprocessed_layout_test_tone);
+        mButtonTestTone = (Button) findViewById(R.id.unprocessed_test_tone_btn);
+        mButtonTestTone.setOnClickListener(mBtnClickListener);
+        mProgressTone = (ProgressBar) findViewById(R.id.unprocessed_test_tone_progress_bar);
+        mResultTestTone = (TextView) findViewById(R.id.unprocessed_test_tone_result);
+        mButtonPlayTone = (Button) findViewById(R.id.unprocessed_play_tone_btn);
+        mButtonPlayTone.setOnClickListener(mBtnClickListener);
+        showWait(mProgressTone, false);
+
+        //Test Noise
+        mLayoutTestNoise = (LinearLayout) findViewById(R.id.unprocessed_layout_test_noise);
+        mButtonTestNoise = (Button) findViewById(R.id.unprocessed_test_noise_btn);
+        mButtonTestNoise.setOnClickListener(mBtnClickListener);
+        mProgressNoise = (ProgressBar) findViewById(R.id.unprocessed_test_noise_progress_bar);
+        mResultTestNoise = (TextView) findViewById(R.id.unprocessed_test_noise_result);
+        mButtonPlayNoise = (Button) findViewById(R.id.unprocessed_play_noise_btn);
+        mButtonPlayNoise.setOnClickListener(mBtnClickListener);
+        showWait(mProgressNoise, false);
+
+        //USB Background
+        mLayoutTestUsbBackground = (LinearLayout)
+                findViewById(R.id.unprocessed_layout_test_usb_background);
+        mButtonTestUsbBackground = (Button) findViewById(R.id.unprocessed_test_usb_background_btn);
+        mButtonTestUsbBackground.setOnClickListener(mBtnClickListener);
+        mProgressUsbBackground = (ProgressBar)
+                findViewById(R.id.unprocessed_test_usb_background_progress_bar);
+        mResultTestUsbBackground = (TextView)
+                findViewById(R.id.unprocessed_test_usb_background_result);
+        showWait(mProgressUsbBackground, false);
+
+        mLayoutTestUsbNoise = (LinearLayout) findViewById(R.id.unprocessed_layout_test_usb_noise);
+        mButtonTestUsbNoise = (Button) findViewById(R.id.unprocessed_test_usb_noise_btn);
+        mButtonTestUsbNoise.setOnClickListener(mBtnClickListener);
+        mProgressUsbNoise = (ProgressBar)findViewById(R.id.unprocessed_test_usb_noise_progress_bar);
+        mResultTestUsbNoise = (TextView) findViewById(R.id.unprocessed_test_usb_noise_result);
+        mButtonPlayUsbNoise = (Button) findViewById(R.id.unprocessed_play_usb_noise_btn);
+        mButtonPlayUsbNoise.setOnClickListener(mBtnClickListener);
+        showWait(mProgressUsbNoise, false);
+
+        setButtonPlayStatus(-1);
+        mGlobalResultText = (TextView) findViewById(R.id.unprocessed_test_global_result);
 
         //Init FFT stuff
         mAudioShortArray2 = new short[mBlockSizeSamples*2];
@@ -162,29 +238,141 @@
         setInfoResources(R.string.audio_frequency_unprocessed_test,
                 R.string.audio_frequency_unprocessed_info, -1);
 
-        //Init bands for BuiltIn/Reference test
-        bandSpecsArray[0] = new AudioBandSpecs(
-                2, 500,         /* frequency start,stop */
-                30.0, -50,     /* start top,bottom value */
-                30.0, -4.0       /* stop top,bottom value */);
+        //Init bands for Mic test
+        mBandSpecsMic[0] = new AudioBandSpecs(
+                5, 100,          /* frequency start,stop */
+                20.0, -20.0,     /* start top,bottom value */
+                20.0, -20.0      /* stop top,bottom value */);
 
-        bandSpecsArray[1] = new AudioBandSpecs(
-                500,4000,       /* frequency start,stop */
-                4.0, -4.0,      /* start top,bottom value */
-                4.0, -4.0        /* stop top,bottom value */);
+        mBandSpecsMic[1] = new AudioBandSpecs(
+                100, 7000,       /* frequency start,stop */
+                10.0, -10.0,     /* start top,bottom value */
+                10.0, -10.0      /* stop top,bottom value */);
 
-        bandSpecsArray[2] = new AudioBandSpecs(
-                4000, 12000,    /* frequency start,stop */
-                4.0, -4.0,      /* start top,bottom value */
-                5.0, -5.0       /* stop top,bottom value */);
+        mBandSpecsMic[2] = new AudioBandSpecs(
+                7000, 20000,     /* frequency start,stop */
+                30.0, -30.0,     /* start top,bottom value */
+                30.0, -30.0      /* stop top,bottom value */);
 
-        bandSpecsArray[3] = new AudioBandSpecs(
-                12000, 20000,   /* frequency start,stop */
-                5.0, -5.0,      /* start top,bottom value */
-                5.0, -30.0      /* stop top,bottom value */);
+        //Init bands for Tone test
+        mBandSpecsTone[0] = new AudioBandSpecs(
+                5, 900,          /* frequency start,stop */
+                -10.0, -100.0,     /* start top,bottom value */
+                -10.0, -100.0      /* stop top,bottom value */);
+
+        mBandSpecsTone[1] = new AudioBandSpecs(
+                900, 1100,       /* frequency start,stop */
+                10.0, -50.0,     /* start top,bottom value */
+                10.0, -10.0      /* stop top,bottom value */);
+
+        mBandSpecsTone[2] = new AudioBandSpecs(
+                1100, 20000,     /* frequency start,stop */
+                -30.0, -120.0,     /* start top,bottom value */
+                -30.0, -120.0      /* stop top,bottom value */);
+
+      //Init bands for Background test
+        mBandSpecsBack[0] = new AudioBandSpecs(
+                5, 100,          /* frequency start,stop */
+                10.0, -120.0,     /* start top,bottom value */
+                -10.0, -120.0      /* stop top,bottom value */);
+
+        mBandSpecsBack[1] = new AudioBandSpecs(
+                100, 7000,       /* frequency start,stop */
+                -10.0, -120.0,     /* start top,bottom value */
+                -50.0, -120.0      /* stop top,bottom value */);
+
+        mBandSpecsBack[2] = new AudioBandSpecs(
+                7000, 20000,     /* frequency start,stop */
+                -50.0, -120.0,     /* start top,bottom value */
+                -50.0, -120.0      /* stop top,bottom value */);
 
         mSupportsUnprocessed = supportsUnprocessed();
         Log.v(TAG, "Supports unprocessed: " + mSupportsUnprocessed);
+
+        mResultsMic =  new Results("mic_response", mBands);
+        mResultsTone = new Results("tone_response", mBandsTone);
+        mResultsBack = new Results("background_response", mBandsBack);
+    }
+
+    private void playerToggleButton(int buttonId, int sourceId) {
+        if (playerIsPlaying()) {
+            playerStopAll();
+        } else {
+            playerSetSource(sourceId);
+            playerTransport(true);
+            setButtonPlayStatus(buttonId);
+        }
+    }
+
+    private class OnBtnClickListener implements OnClickListener {
+        @Override
+        public void onClick(View v) {
+            int id = v.getId();
+            switch (id) {
+            case R.id.unprocessed_test_tone_btn:
+                startTest(TEST_TONE);
+                break;
+            case R.id.unprocessed_play_tone_btn:
+                playerToggleButton(id, SOURCE_TONE);
+                break;
+            case R.id.unprocessed_test_noise_btn:
+                startTest(TEST_NOISE);
+                break;
+            case R.id.unprocessed_play_noise_btn:
+                playerToggleButton(id, SOURCE_NOISE);
+                break;
+            case R.id.unprocessed_test_usb_background_btn:
+                startTest(TEST_USB_BACKGROUND);
+                break;
+            case R.id.unprocessed_test_usb_noise_btn:
+                startTest(TEST_USB_NOISE);
+                break;
+            case R.id.unprocessed_play_usb_noise_btn:
+                playerToggleButton(id, SOURCE_NOISE);
+                break;
+            }
+        }
+    }
+
+    private void setButtonPlayStatus(int playResId) {
+        String play = getResources().getText(R.string.unprocessed_play).toString();
+        String stop = getResources().getText(R.string.unprocessed_stop).toString();
+
+        mButtonPlayTone.setText(playResId == R.id.unprocessed_play_tone_btn ? stop : play);
+        mButtonPlayNoise.setText(playResId == R.id.unprocessed_play_noise_btn ? stop : play);
+        mButtonPlayUsbNoise.setText(playResId ==
+                R.id.unprocessed_play_usb_noise_btn ? stop : play);
+    }
+
+    private void playerSetSource(int sourceIndex) {
+        switch (sourceIndex) {
+            case SOURCE_TONE:
+                mSPlayer.setSoundWithResId(getApplicationContext(), R.raw.onekhztone);
+                break;
+            default:
+            case SOURCE_NOISE:
+                mSPlayer.setSoundWithResId(getApplicationContext(),
+                        R.raw.stereo_mono_white_noise_48);
+                break;
+        }
+    }
+
+    private void playerTransport(boolean play) {
+        if (!mSPlayer.isAlive()) {
+            mSPlayer.start();
+        }
+        mSPlayer.play(play);
+    }
+
+    private boolean playerIsPlaying() {
+       return mSPlayer.isPlaying();
+    }
+
+    private void playerStopAll() {
+        if (mSPlayer.isAlive() && mSPlayer.isPlaying()) {
+            mSPlayer.play(false);
+            setButtonPlayStatus(-1);
+        }
     }
 
     /**
@@ -197,14 +385,66 @@
         }
     }
 
-    /**
-     * show active progress bar
-     */
-    private void showWait(boolean show) {
+    private void showWait(ProgressBar pb, boolean show) {
         if (show) {
-            mProgressBar.setVisibility(View.VISIBLE);
+            pb.setVisibility(View.VISIBLE);
         } else {
-            mProgressBar.setVisibility(View.INVISIBLE);
+            pb.setVisibility(View.INVISIBLE);
+        }
+    }
+
+    private String getTestString(int testId) {
+        String name = "undefined";
+        switch(testId) {
+            case TEST_TONE:
+                name = "BuiltIn_tone";
+                break;
+            case TEST_NOISE:
+                name = "BuiltIn_noise";
+                break;
+            case TEST_USB_BACKGROUND:
+                name = "USB_background";
+                break;
+            case TEST_USB_NOISE:
+                name = "USB_noise";
+                break;
+        }
+        return name;
+    }
+
+    private void showWait(int testId, boolean show) {
+        switch(testId) {
+            case TEST_TONE:
+                showWait(mProgressTone, show);
+                break;
+            case TEST_NOISE:
+                showWait(mProgressNoise, show);
+                break;
+            case TEST_USB_BACKGROUND:
+                showWait(mProgressUsbBackground, show);
+                break;
+            case TEST_USB_NOISE:
+                showWait(mProgressUsbNoise, show);
+                break;
+        }
+    }
+
+    private void showMessage(int testId, String msg) {
+        if (msg != null && msg.length() > 0) {
+            switch(testId) {
+                case TEST_TONE:
+                    mResultTestTone.setText(msg);
+                    break;
+                case TEST_NOISE:
+                    mResultTestNoise.setText(msg);
+                    break;
+                case TEST_USB_BACKGROUND:
+                    mResultTestUsbBackground.setText(msg);
+                    break;
+                case TEST_USB_NOISE:
+                    mResultTestUsbNoise.setText(msg);
+                    break;
+            }
         }
     }
 
@@ -224,47 +464,320 @@
         return unprocessedSupport;
     }
 
-    /**
-     *  Start the loopback audio test
-     */
-    private void startTest1() {
+    private void computeAllResults() {
+        StringBuilder sb = new StringBuilder();
+
+        boolean allDone = true;
+
+        for (int i=0; i<TEST_COUNT; i++) {
+            allDone = allDone & mTestsDone[i];
+            sb.append(String.format("%s : %s\n", getTestString(i),
+                    mTestsDone[i] ? "DONE" :" NOT DONE"));
+        }
+
+        if (allDone) {
+            sb.append(computeResults());
+        } else {
+            sb.append("Please execute all tests for results\n");
+        }
+        mGlobalResultText.setText(sb.toString());
+    }
+
+    private void processSpectrum(Results results, AudioBandSpecs[] bandsSpecs, int anchorBand) {
+        int points = results.mValuesLog.length;
+        int bandCount = bandsSpecs.length;
+        int currentBand = 0;
+        for (int i = 0; i < points; i++) {
+            double freq = (double)mSamplingRate * i / (double)mBlockSizeSamples;
+            if (freq > bandsSpecs[currentBand].mFreqStop) {
+                currentBand++;
+                if (currentBand >= bandCount)
+                    break;
+            }
+
+            if (freq >= bandsSpecs[currentBand].mFreqStart) {
+                results.mAverageEnergyPerBand[currentBand] += results.mValuesLog[i];
+                results.mPointsPerBand[currentBand]++;
+            }
+        }
+
+        for (int b = 0; b < bandCount; b++) {
+            if (results.mPointsPerBand[b] > 0) {
+                results.mAverageEnergyPerBand[b] =
+                        results.mAverageEnergyPerBand[b] / results.mPointsPerBand[b];
+            }
+        }
+
+        //set offset relative to band anchor band level
+        for (int b = 0; b < bandCount; b++) {
+            if (anchorBand > -1 && anchorBand < bandCount) {
+                bandsSpecs[b].setOffset(results.mAverageEnergyPerBand[anchorBand]);
+            } else {
+                bandsSpecs[b].setOffset(0);
+            }
+        }
+
+        //test points in band.
+        currentBand = 0;
+        for (int i = 0; i < points; i++) {
+            double freq = (double) mSamplingRate * i / (double) mBlockSizeSamples;
+            if (freq >  bandsSpecs[currentBand].mFreqStop) {
+                currentBand++;
+                if (currentBand >= bandCount)
+                    break;
+            }
+
+            if (freq >= bandsSpecs[currentBand].mFreqStart) {
+                double value = results.mValuesLog[i];
+                if (bandsSpecs[currentBand].isInBounds(freq, value)) {
+                    results.mInBoundPointsPerBand[currentBand]++;
+                }
+            }
+        }
+    }
+
+    private String computeResults() {
+        StringBuilder sb = new StringBuilder();
+
+        int points = mFreqAverageNoise.getSize();
+        //mFreqAverageNoise size is determined by the latest data writen to it.
+        //Currently, this data is always mBlockSizeSamples/2.
+        if (points < 1) {
+            return "error: not enough points";
+        }
+
+        double[] tone = new double[points];
+        double[] noise = new double[points];
+        double[] reference = new double[points];
+        double[] background = new double[points];
+
+        mFreqAverageTone.getData(tone, false);
+        mFreqAverageNoise.getData(noise, false);
+        mFreqAverageUsbNoise.getData(reference, false);
+        mFreqAverageUsbBackground.getData(background, false);
+
+        //Convert to dB
+        double[] toneDb = new double[points];
+        double[] noiseDb = new double[points];
+        double[] referenceDb = new double[points];
+        double[] backgroundDb = new double[points];
+
+        double[] compensatedNoiseDb = new double[points];
+
+        for (int i = 0; i < points; i++) {
+            toneDb[i] = 20 * Math.log10(tone[i]);
+            noiseDb[i] = 20 * Math.log10(noise[i]);
+            referenceDb[i] = 20 * Math.log10(reference[i]);
+            backgroundDb[i] = 20 * Math.log10(background[i]);
+
+            compensatedNoiseDb[i] = noiseDb[i] - referenceDb[i];
+        }
+
+        mResultsMic.reset();
+        mResultsTone.reset();
+        mResultsBack.reset();
+
+        mResultsMic.mValuesLog = compensatedNoiseDb;
+        mResultsTone.mValuesLog = toneDb;
+        mResultsBack.mValuesLog = backgroundDb;
+
+        processSpectrum(mResultsMic, mBandSpecsMic, 1);
+        processSpectrum(mResultsTone, mBandSpecsTone, 1);
+        processSpectrum(mResultsBack, mBandSpecsBack, -1); //no reference for offset
+
+        //Tone test
+        boolean toneTestSuccess = true;
+        {
+            //rms level should be -36 dbfs +/- 3 db?
+            double rmsMaxDb =  20 * Math.log10(mRMSMaxTone);
+            sb.append(String.format("RMS level of tone: %.2f dBFS\n", rmsMaxDb));
+            sb.append(String.format("Target RMS level: %.2f dBFS +/- %.2f dB\n",
+                    TONE_RMS_EXPECTED,
+                    TONE_RMS_MAX_ERROR));
+            if (Math.abs(rmsMaxDb - TONE_RMS_EXPECTED) > TONE_RMS_MAX_ERROR) {
+                toneTestSuccess = false;
+                sb.append("RMS level test FAILED\n");
+            } else {
+                sb.append(" RMS level test SUCCESSFUL\n");
+            }
+            //check the spectrum is really a tone around 1 khz
+        }
+
+        sb.append("\n");
+        sb.append(mResultsTone.toString());
+        if (mResultsTone.testAll()) {
+            sb.append(" 1 Khz Tone Frequency Response Test SUCCESSFUL\n");
+        } else {
+            sb.append(" 1 Khz Tone Frequency Response Test FAILED\n");
+        }
+        sb.append("\n");
+
+        sb.append("\n");
+        sb.append(mResultsBack.toString());
+        if (mResultsBack.testAll()) {
+            sb.append(" Background environment Test SUCCESSFUL\n");
+        } else {
+            sb.append(" Background environment Test FAILED\n");
+        }
+
+        sb.append("\n");
+        sb.append(mResultsMic.toString());
+        if (mResultsMic.testAll()) {
+            sb.append(" Frequency Response Test SUCCESSFUL\n");
+        } else {
+            sb.append(" Frequency Response Test FAILED\n");
+        }
+        sb.append("\n");
+
+        recordTestResults(mResultsTone);
+        recordTestResults(mResultsMic);
+
+        boolean allTestsPassed = false;
+        if (mResultsMic.testAll() && mResultsTone.testAll() && toneTestSuccess &&
+                mResultsBack.testAll()) {
+            allTestsPassed = true;
+            String strSuccess = getResources().getString(R.string.audio_general_test_passed);
+            sb.append(strSuccess);
+        } else {
+            String strFailed = getResources().getString(R.string.audio_general_test_failed);
+            sb.append(strFailed);
+        }
+
+        sb.append("\n");
+        if (mSupportsUnprocessed) { //test is mandatory
+            sb.append(getResources().getText(
+                    R.string.audio_frequency_unprocessed_defined).toString());
+            if (allTestsPassed) {
+                getPassButton().setEnabled(true);
+            } else {
+                getPassButton().setEnabled(false);
+            }
+        } else {
+            //test optional
+            sb.append(getResources().getText(
+                    R.string.audio_frequency_unprocessed_not_defined).toString());
+            getPassButton().setEnabled(true);
+        }
+        return sb.toString();
+    }
+
+    Thread mTestThread;
+    private void startTest(int testId) {
         if (mTestThread != null && !mTestThread.isAlive()) {
             mTestThread = null; //kill it.
         }
 
         if (mTestThread == null) {
+            mRMS = 0;
+            mRMSMax = 0;
             Log.v(TAG,"Executing test Thread");
-            mTestThread = new Thread(mTest1Runnable);
+            switch(testId) {
+                case TEST_TONE:
+                    mTestThread = new Thread(new TestRunnable(TEST_TONE) {
+                        public void run() {
+                            super.run();
+                            if (!mUsbMicConnected) {
+                                sendMessage(mTestId, TEST_MESSAGE,
+                                        "Testing Built in Microphone: Tone");
+                                mRMSTone = 0;
+                                mRMSMaxTone = 0;
+                                mFreqAverageTone.reset();
+                                mFreqAverageTone.setCaptureType(VectorAverage.CAPTURE_TYPE_MAX);
+                                record(TEST_DURATION_TONE);
+                                sendMessage(mTestId, TEST_ENDED, "Testing Completed");
+                                mTestsDone[mTestId] = true;
+                            } else {
+                                sendMessage(mTestId, TEST_ENDED_ERROR,
+                                        "Please Unplug USB Microphone");
+                                mTestsDone[mTestId] = false;
+                            }
+                        }
+                    });
+                break;
+                case TEST_NOISE:
+                    mTestThread = new Thread(new TestRunnable(TEST_NOISE) {
+                        public void run() {
+                            super.run();
+                            if (!mUsbMicConnected) {
+                                sendMessage(mTestId, TEST_MESSAGE,
+                                        "Testing Built in Microphone: Noise");
+                                mFreqAverageNoise.reset();
+                                mFreqAverageNoise.setCaptureType(VectorAverage.CAPTURE_TYPE_MAX);
+                                record(TEST_DURATION_NOISE);
+                                sendMessage(mTestId, TEST_ENDED, "Testing Completed");
+                                mTestsDone[mTestId] = true;
+                            } else {
+                                sendMessage(mTestId, TEST_ENDED_ERROR,
+                                        "Please Unplug USB Microphone");
+                                mTestsDone[mTestId] = false;
+                            }
+                        }
+                    });
+                break;
+                case TEST_USB_BACKGROUND:
+                    playerStopAll();
+                    mTestThread = new Thread(new TestRunnable(TEST_USB_BACKGROUND) {
+                        public void run() {
+                            super.run();
+                            if (mUsbMicConnected) {
+                                sendMessage(mTestId, TEST_MESSAGE,
+                                        "Testing USB Microphone: background");
+                                mFreqAverageUsbBackground.reset();
+                                mFreqAverageUsbBackground.setCaptureType(
+                                        VectorAverage.CAPTURE_TYPE_AVERAGE);
+                                record(TEST_DURATION_USB_BACKGROUND);
+                                sendMessage(mTestId, TEST_ENDED, "Testing Completed");
+                                mTestsDone[mTestId] = true;
+                            } else {
+                                sendMessage(mTestId, TEST_ENDED_ERROR,
+                                        "USB Microphone not detected.");
+                                mTestsDone[mTestId] = false;
+                            }
+                        }
+                    });
+                break;
+                case TEST_USB_NOISE:
+                    mTestThread = new Thread(new TestRunnable(TEST_USB_NOISE) {
+                        public void run() {
+                            super.run();
+                            if (mUsbMicConnected) {
+                                sendMessage(mTestId, TEST_MESSAGE, "Testing USB Microphone: Noise");
+                                mFreqAverageUsbNoise.reset();
+                                mFreqAverageUsbNoise.setCaptureType(VectorAverage.CAPTURE_TYPE_MAX);
+                                record(TEST_DURATION_USB_NOISE);
+                                sendMessage(mTestId, TEST_ENDED, "Testing Completed");
+                                mTestsDone[mTestId] = true;
+                            } else {
+                                sendMessage(mTestId, TEST_ENDED_ERROR,
+                                        "USB Microphone not detected.");
+                                mTestsDone[mTestId] = false;
+                            }
+                        }
+                    });
+                break;
+            }
             mTestThread.start();
         } else {
             Log.v(TAG,"test Thread already running.");
         }
     }
-
-    Thread mTestThread;
-    Runnable mTest1Runnable = new Runnable() {
-        public void run() {
-            Message msg = Message.obtain();
-            msg.what = TEST_STARTED;
-            mMessageHandler.sendMessage(msg);
-
-            sendMessage("Testing Built in Microphone");
-            mFreqAverageBuiltIn.reset();
-            mFreqAverageBuiltIn.setCaptureType(VectorAverage.CAPTURE_TYPE_MAX);
-            play();
-
-            sendMessage("Testing Completed");
-
-            Message msg2 = Message.obtain();
-            msg2.what = TEST1_ENDED;
-            mMessageHandler.sendMessage(msg2);
+    public class TestRunnable implements Runnable {
+        public int mTestId;
+        public boolean mUsbMicConnected;
+        TestRunnable(int testId) {
+            Log.v(TAG,"New TestRunnable");
+            mTestId = testId;
         }
-
-        private void play() {
+        public void run() {
+            mCurrentTest = mTestId;
+            sendMessage(mTestId, TEST_STARTED,"");
+            mUsbMicConnected =
+                    UsbMicrophoneTester.getIsMicrophoneConnected(getApplicationContext());
+        };
+        public void record(int durationMs) {
             startRecording();
-
             try {
-                Thread.sleep(5000);
+                Thread.sleep(durationMs);
             } catch (InterruptedException e) {
                 e.printStackTrace();
                 //restore interrupted status
@@ -272,41 +785,40 @@
             }
             stopRecording();
         }
-
-        private void sendMessage(String str) {
+        public void sendMessage(int testId, int msgType, String str) {
             Message msg = Message.obtain();
-            msg.what = TEST1_MESSAGE;
+            msg.what = msgType;
             msg.obj = str;
+            msg.arg1 = testId;
             mMessageHandler.sendMessage(msg);
         }
-    };
+    }
 
     private Handler mMessageHandler = new Handler() {
         public void handleMessage(Message msg) {
             super.handleMessage(msg);
+            int testId = msg.arg1; //testId
+            String str = (String) msg.obj;
             switch (msg.what) {
             case TEST_STARTED:
-                showWait(true);
-                getPassButton().setEnabled(false);
+                showWait(testId, true);
+//                getPassButton().setEnabled(false);
+                break;
+            case TEST_MESSAGE:
+                    showMessage(testId, str);
                 break;
             case TEST_ENDED:
-                showWait(false);
-//                computeTest2Results();
+                showWait(testId, false);
+                playerStopAll();
+                showMessage(testId, str);
+                appendResultsToScreen(testId, "test finished");
+                computeAllResults();
                 break;
-            case TEST1_MESSAGE: {
-                    String str = (String)msg.obj;
-                    if (str != null) {
-                        mTest1Result.setText(str);
-                    }
-                }
-                break;
-            case TEST1_ENDED:
-                showWait(false);
-                computeTest1Results();
-                break;
-            case TEST_MESSAGE: {
-                }
-                break;
+            case TEST_ENDED_ERROR:
+                showWait(testId, false);
+                playerStopAll();
+                showMessage(testId, str);
+                computeAllResults();
             default:
                 Log.e(TAG, String.format("Unknown message: %d", msg.what));
             }
@@ -314,21 +826,32 @@
     };
 
     private class Results {
+        private int mBandCount;
         private String mLabel;
         public double[] mValuesLog;
-        int[] mPointsPerBand = new int[mBands];
-        double[] mAverageEnergyPerBand = new double[mBands];
-        int[] mInBoundPointsPerBand = new int[mBands];
-        public Results(String label) {
+        int[] mPointsPerBand; // = new int[mBands];
+        double[] mAverageEnergyPerBand;// = new double[mBands];
+        int[] mInBoundPointsPerBand;// = new int[mBands];
+        public Results(String label, int bandCount) {
             mLabel = label;
+            mBandCount = bandCount;
+            mPointsPerBand = new int[mBandCount];
+            mAverageEnergyPerBand = new double[mBandCount];
+            mInBoundPointsPerBand = new int[mBandCount];
+        }
+        public void reset() {
+            for (int i = 0; i < mBandCount; i++) {
+                mPointsPerBand[i] = 0;
+                mAverageEnergyPerBand[i] = 0;
+                mInBoundPointsPerBand[i] = 0;
+            }
         }
 
         //append results
         public String toString() {
             StringBuilder sb = new StringBuilder();
             sb.append(String.format("Channel %s\n", mLabel));
-            sb.append("Level in Band 1 : " + (testLevel() ? "OK" :"Not Optimal") + "\n");
-            for (int b = 0; b < mBands; b++) {
+            for (int b = 0; b < mBandCount; b++) {
                 double percent = 0;
                 if (mPointsPerBand[b] > 0) {
                     percent = 100.0 * (double) mInBoundPointsPerBand[b] / mPointsPerBand[b];
@@ -344,15 +867,8 @@
             return sb.toString();
         }
 
-        public boolean testLevel() {
-            if (mAverageEnergyPerBand[1] >= MIN_ENERGY_BAND_1) {
-                return true;
-            }
-            return false;
-        }
-
         public boolean testInBand(int b) {
-            if (b >= 0 && b < mBands && mPointsPerBand[b] > 0) {
+            if (b >= 0 && b < mBandCount && mPointsPerBand[b] > 0) {
                 if ((double) mInBoundPointsPerBand[b] / mPointsPerBand[b] >
                     MIN_FRACTION_POINTS_IN_BAND) {
                         return true;
@@ -362,10 +878,7 @@
         }
 
         public boolean testAll() {
-            if (!testLevel()) {
-                return false;
-            }
-            for (int b = 0; b < mBands; b++) {
+            for (int b = 0; b < mBandCount; b++) {
                 if (!testInBand(b)) {
                     return false;
                 }
@@ -375,98 +888,13 @@
     }
 
 
-    /**
-     * compute test1 results
-     */
-    private void computeTest1Results() {
-        Results resultsBuiltIn = new Results("BuiltIn");
-        if (computeResultsForVector(mFreqAverageBuiltIn, resultsBuiltIn, bandSpecsArray)) {
-            appendResultsToScreen(resultsBuiltIn.toString(), mTest1Result);
-            recordTestResults(resultsBuiltIn);
-        }
-        if (mSupportsUnprocessed) { //test is mandatory
-            appendResultsToScreen(getResources().getText(
-                    R.string.audio_frequency_unprocessed_defined).toString(), mTest1Result);
-            if (resultsBuiltIn.testAll()) {
-                //tst passed
-                getPassButton().setEnabled(true);
-                String strSuccess = getResources().getString(R.string.audio_general_test_passed);
-                appendResultsToScreen(strSuccess, mTest1Result);
-            } else {
-                getPassButton().setEnabled(false);
-                String strFailed = getResources().getString(R.string.audio_general_test_failed);
-                appendResultsToScreen(strFailed, mTest1Result);
-            }
-        } else {
-            //test optional
-            appendResultsToScreen(getResources().getText(
-                    R.string.audio_frequency_unprocessed_not_defined).toString(), mTest1Result);
-            getPassButton().setEnabled(true);
-        }
-    }
-
-    private boolean computeResultsForVector(VectorAverage freqAverage, Results results,
-            AudioBandSpecs[] bandSpecs) {
-
-        int points = freqAverage.getSize();
-        if (points > 0) {
-            //compute vector in db
-            double[] values = new double[points];
-            freqAverage.getData(values, false);
-            results.mValuesLog = new double[points];
-            for (int i = 0; i < points; i++) {
-                results.mValuesLog[i] = 20 * Math.log10(values[i]);
-            }
-
-            int currentBand = 0;
-            for (int i = 0; i < points; i++) {
-                double freq = (double)mSamplingRate * i / (double)mBlockSizeSamples;
-                if (freq > bandSpecs[currentBand].mFreqStop) {
-                    currentBand++;
-                    if (currentBand >= mBands)
-                        break;
-                }
-
-                if (freq >= bandSpecs[currentBand].mFreqStart) {
-                    results.mAverageEnergyPerBand[currentBand] += results.mValuesLog[i];
-                    results.mPointsPerBand[currentBand]++;
-                }
-            }
-
-            for (int b = 0; b < mBands; b++) {
-                if (results.mPointsPerBand[b] > 0) {
-                    results.mAverageEnergyPerBand[b] =
-                            results.mAverageEnergyPerBand[b] / results.mPointsPerBand[b];
-                }
-            }
-
-            //set offset relative to band 1 level
-            for (int b = 0; b < mBands; b++) {
-                bandSpecs[b].setOffset(results.mAverageEnergyPerBand[1]);
-            }
-
-            //test points in band.
-            currentBand = 0;
-            for (int i = 0; i < points; i++) {
-                double freq = (double)mSamplingRate * i / (double)mBlockSizeSamples;
-                if (freq >  bandSpecs[currentBand].mFreqStop) {
-                    currentBand++;
-                    if (currentBand >= mBands)
-                        break;
-                }
-
-                if (freq >= bandSpecs[currentBand].mFreqStart) {
-                    double value = results.mValuesLog[i];
-                    if (bandSpecs[currentBand].isInBounds(freq, value)) {
-                        results.mInBoundPointsPerBand[currentBand]++;
-                    }
-                }
-            }
-            return true;
-        } else {
-            return false;
-        }
-    }
+//    /**
+//     * compute test results
+//     */
+//    private void computeTestResults(int testId) {
+//        String testName = getTestString(testId);
+//        appendResultsToScreen(testId, "test finished");
+//    }
 
     //append results
     private void appendResultsToScreen(String str, TextView text) {
@@ -474,6 +902,24 @@
         text.setText(currentText + "\n" + str);
     }
 
+    private void appendResultsToScreen(int testId, String str) {
+        switch(testId) {
+            case TEST_TONE:
+                appendResultsToScreen(str, mResultTestTone);
+                showToneRMS();
+                break;
+            case TEST_NOISE:
+                appendResultsToScreen(str, mResultTestNoise);
+                break;
+            case TEST_USB_BACKGROUND:
+                appendResultsToScreen(str, mResultTestUsbBackground);
+                break;
+            case TEST_USB_NOISE:
+                appendResultsToScreen(str, mResultTestUsbNoise);
+                break;
+        }
+    }
+
     /**
      * Store test results in log
      */
@@ -615,6 +1061,12 @@
         mRecorder.setPositionNotificationPeriod(mBlockSizeSamples / 2);
         return true;
     }
+    private void showToneRMS() {
+        String str = String.format("RMS: %.3f dBFS. Max RMS: %.3f dBFS",
+                20 * Math.log10(mRMSTone),
+                20 * Math.log10(mRMSMaxTone));
+        showMessage(TEST_TONE, str);
+    }
 
     // ---------------------------------------------------------
     // Implementation of AudioRecord.OnPeriodicNotificationListener
@@ -627,9 +1079,10 @@
 
             //compute stuff.
             int clipcount = 0;
-            double sum = 0;
+//            double sum = 0;
             double maxabs = 0;
             int i;
+            double rmsTempSum = 0;
 
             for (i = 0; i < samplesNeeded; i++) {
                 double value = mAudioShortArray2[i] / MAX_VAL;
@@ -643,10 +1096,18 @@
                     clipcount++;
                 }
 
-                sum += value * value;
+                rmsTempSum += value * value;
                 //fft stuff
                 mData.mData[i] = value;
             }
+            double rms = Math.sqrt(rmsTempSum / samplesNeeded);
+
+            double alpha = 0.9;
+            double total_rms = rms * alpha + mRMS *(1-alpha);
+            mRMS = total_rms;
+            if (mRMS > mRMSMax) {
+                mRMSMax = mRMS;
+            }
 
             //for the current frame, compute FFT and send to the viewer.
 
@@ -660,8 +1121,25 @@
                 halfMagnitude[i] = Math.sqrt(mC.mReal[i] * mC.mReal[i] + mC.mImag[i] * mC.mImag[i]);
             }
 
-            mFreqAverageMain.setData(halfMagnitude, false); //average all of them!
-            mFreqAverageBuiltIn.setData(halfMagnitude, false);
+            switch(mCurrentTest) {
+                case TEST_TONE: {
+                    mFreqAverageTone.setData(halfMagnitude, false);
+                    //Update realtime info on screen
+                    mRMSTone = mRMS;
+                    mRMSMaxTone = mRMSMax;
+                   showToneRMS();
+                }
+                    break;
+                case TEST_NOISE:
+                    mFreqAverageNoise.setData(halfMagnitude, false);
+                    break;
+                case TEST_USB_BACKGROUND:
+                    mFreqAverageUsbBackground.setData(halfMagnitude, false);
+                    break;
+                case TEST_USB_NOISE:
+                    mFreqAverageUsbNoise.setData(halfMagnitude, false);
+                    break;
+            }
         }
     }
 
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleScannerPowerLevelActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleScannerPowerLevelActivity.java
old mode 100644
new mode 100755
index bf3484e..c0097ce
--- a/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleScannerPowerLevelActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleScannerPowerLevelActivity.java
@@ -30,7 +30,6 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.os.Bundle;
-import android.os.CountDownTimer;
 import android.util.Log;
 import android.widget.TextView;
 import android.widget.Toast;
@@ -46,10 +45,6 @@
     private Map<Integer, Integer> mCount;
     private int[] mPowerLevel;
 
-    private TextView mTimerText;
-    private CountDownTimer mTimer;
-    private static final long REFRESH_MAC_TIME = 930000; // 15.5 min
-
     private static final int[] POWER_DBM = {-21, -15, -7, 1, 9};
 
     @Override
@@ -59,23 +54,6 @@
         setPassFailButtonClickListeners();
         setInfoResources(R.string.ble_power_level_name,
                          R.string.ble_power_level_info, -1);
-        getPassButton().setEnabled(false);
-
-        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!");
-            }
-        };
 
         mRssiText = new HashMap<Integer, TextView>();
         mCountText = new HashMap<Integer, TextView>();
@@ -168,7 +146,6 @@
                         for (int i : mPowerLevel) {
                             mCount.put(i, 0);
                         }
-                        mTimer.start();
                     }
                     Integer t = mCount.get(powerLevelBit) + 1;
                     mCount.put(powerLevelBit, t);
@@ -188,10 +165,6 @@
                 case BleScannerService.BLE_PRIVACY_NEW_MAC_RECEIVE:
                      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;
             }
         }
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodFlowTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodFlowTestActivity.java
index 6aacea8..9af0840 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodFlowTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodFlowTestActivity.java
@@ -399,7 +399,6 @@
         adapter.add(mCredSettingsVisibleTest);
         adapter.add(mAppSettingsVisibleTest);
         adapter.add(mLocationSettingsVisibleTest);
-        adapter.add(mWiFiDataUsageSettingsVisibleTest);
         adapter.add(mCellularDataUsageSettingsVisibleTest);
         adapter.add(mPrintSettingsVisibleTest);
 
@@ -420,6 +419,10 @@
         adapter.add(mParentProfilePassword);
         adapter.add(mPolicyTransparencyTest);
 
+        if (getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI)) {
+            adapter.add(mWiFiDataUsageSettingsVisibleTest);
+        }
+
         if (canResolveIntent(new Intent(Settings.ACTION_APPLICATION_SETTINGS))) {
             adapter.add(mDisallowAppsControlTest);
         }
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/CommandReceiverActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/CommandReceiverActivity.java
index 1349694..cf8ec89 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/CommandReceiverActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/CommandReceiverActivity.java
@@ -97,8 +97,10 @@
                     }
                 } break;
                 case COMMAND_DISALLOW_KEYGUARD_UNREDACTED_NOTIFICATIONS: {
-                    mDpm.setKeyguardDisabledFeatures(mAdmin,
-                            DevicePolicyManager.KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS);
+                    boolean enforced = intent.getBooleanExtra(EXTRA_ENFORCED, false);
+                    mDpm.setKeyguardDisabledFeatures(mAdmin, enforced
+                            ? DevicePolicyManager.KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS
+                            : 0);
                 } break;
                 case COMMAND_SET_AUTO_TIME_REQUIRED: {
                     mDpm.setAutoTimeRequired(mAdmin,
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DisallowAppsControlActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DisallowAppsControlActivity.java
index 985cbf6..3cabbd3 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DisallowAppsControlActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DisallowAppsControlActivity.java
@@ -67,11 +67,8 @@
     @Override
     public void finish() {
         // Pass and fail buttons are known to call finish() when clicked, and this is when we want to
-        // clear the password.
-        final ComponentName adminComponent = getAdminComponent();
-        if (mDpm.isAdminActive(adminComponent)) {
-            allowAppsControl();
-        }
+        // clear the restriction.
+        allowAppsControl();
         super.finish();
     }
 
@@ -120,9 +117,4 @@
         setupCheckDisabledForceStopTest(adapter);
         setupCheckDisabledAppStorageButtonsTest(adapter);
     }
-
-    @Override
-    protected void clearRemainingState(final DialogTestListItem test) {
-        allowAppsControl();
-    }
 }
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/NfcTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/NfcTestActivity.java
index 2f7619c..e767f7f 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/NfcTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/NfcTestActivity.java
@@ -22,6 +22,7 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
+import android.content.pm.PackageManager;
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
 import android.graphics.Color;
@@ -52,6 +53,7 @@
     private static final String NFC_BEAM_ACTIVITY = "com.android.nfc.BeamShareActivity";
     private static final String SAMPLE_IMAGE_FILENAME = "image_to_share.jpg";
     private static final String SAMPLE_IMAGE_CONTENT = "sample image";
+    private static final String SAMPLE_TEXT = "sample text";
     private static final int MARGIN = 80;
     private static final int TEXT_SIZE = 200;
 
@@ -76,11 +78,28 @@
                     UserManager.DISALLOW_OUTGOING_BEAM);
         }
 
-        final Uri uri = createUriForImage(SAMPLE_IMAGE_FILENAME, SAMPLE_IMAGE_CONTENT);
-        Uri[] uris = new Uri[] { uri };
-
         mNfcAdapter = NfcAdapter.getDefaultAdapter(this);
-        mNfcAdapter.setBeamPushUris(uris, this);
+
+        final Intent shareIntent = new Intent(Intent.ACTION_SEND);
+
+        // Sending a large amount of data requires hand-over to bluetooth, so determine here
+        // if supported by the device. If bluetooth is not supported, a simple text message
+        // will be transferred instead.
+        final boolean hasBluetooth =
+                getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH);
+
+        if (hasBluetooth) {
+            final Uri uri = createUriForImage(SAMPLE_IMAGE_FILENAME, SAMPLE_IMAGE_CONTENT);
+            Uri[] uris = new Uri[]{uri};
+
+            mNfcAdapter.setBeamPushUris(uris, this);
+
+            shareIntent.putExtra(Intent.EXTRA_STREAM, uri);
+            shareIntent.setType("image/jpg");
+            shareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+        } else {
+            shareIntent.putExtra(Intent.EXTRA_TEXT, SAMPLE_TEXT);
+        }
 
         findViewById(R.id.manual_beam_button).setOnClickListener(new OnClickListener() {
             @Override
@@ -91,10 +110,6 @@
         findViewById(R.id.intent_share_button).setOnClickListener(new OnClickListener() {
             @Override
             public void onClick(View view) {
-                Intent shareIntent = new Intent(Intent.ACTION_SEND);
-                shareIntent.putExtra(Intent.EXTRA_STREAM, uri);
-                shareIntent.setType("image/jpg");
-                shareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
                 // Specify the package name of NfcBeamActivity so that the tester don't need to
                 // select the activity manually.
                 shareIntent.setClassName(NFC_BEAM_PACKAGE, NFC_BEAM_ACTIVITY);
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/PolicyTransparencyTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/PolicyTransparencyTestActivity.java
index 9261284..5eaf862 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/PolicyTransparencyTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/PolicyTransparencyTestActivity.java
@@ -266,7 +266,8 @@
         return mTestId;
     }
 
-    public class DummyAccessibilityService extends AccessibilityService {
+    public static class DummyAccessibilityService extends AccessibilityService {
+
         @Override
         public void onAccessibilityEvent(AccessibilityEvent event) {
             // Do nothing
@@ -278,7 +279,7 @@
         }
     }
 
-    public class DummyInputMethod extends InputMethodService {
+    public static class DummyInputMethod extends InputMethodService {
         @Override
         public boolean onEvaluateFullscreenMode() {
             return false;
@@ -306,4 +307,4 @@
             this.command = command;
         }
     }
-}
\ No newline at end of file
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/PolicyTransparencyTestListActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/PolicyTransparencyTestListActivity.java
index 88f4f18..c5b4a93 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/PolicyTransparencyTestListActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/PolicyTransparencyTestListActivity.java
@@ -18,6 +18,7 @@
 
 import android.app.admin.DevicePolicyManager;
 import android.content.Intent;
+import android.content.pm.PackageManager;
 import android.database.DataSetObserver;
 import android.os.Bundle;
 import android.provider.Settings;
@@ -121,7 +122,8 @@
     private void addTestsToAdapter(final ArrayTestListAdapter adapter) {
         for (String restriction : UserRestrictions.getUserRestrictions()) {
             final Intent intent = UserRestrictions.getUserRestrictionTestIntent(this, restriction);
-            if (!mIsDeviceOwner && !UserRestrictions.isValidForPO(restriction)) {
+            if (!UserRestrictions.isRestrictionValid(this, restriction) ||
+                    (!mIsDeviceOwner && !UserRestrictions.isValidForPO(restriction))) {
                 continue;
             }
             final String title = UserRestrictions.getRestrictionLabel(this, restriction);
@@ -133,7 +135,8 @@
         for (Pair<Intent, Integer> policy : POLICIES) {
             final Intent intent = policy.first;
             String test = intent.getStringExtra(PolicyTransparencyTestActivity.EXTRA_TEST);
-            if (!mIsDeviceOwner && !ALSO_VALID_FOR_PO.contains(test)) {
+            if (!isPolicyValid(test) ||
+                    (!mIsDeviceOwner && !ALSO_VALID_FOR_PO.contains(test))) {
                 continue;
             }
             final String title = getString(policy.second);
@@ -144,6 +147,16 @@
         }
     }
 
+    private boolean isPolicyValid(String test) {
+        final PackageManager pm = getPackageManager();
+        switch (test) {
+            case PolicyTransparencyTestActivity.TEST_CHECK_PERMITTED_INPUT_METHOD:
+                return pm.hasSystemFeature(PackageManager.FEATURE_INPUT_METHODS);
+            default:
+                return true;
+        }
+    }
+
     private void setSupportMsgButtonClickListeners() {
         findViewById(R.id.short_msg_button).setOnClickListener(this);
         findViewById(R.id.long_msg_button).setOnClickListener(this);
@@ -178,4 +191,4 @@
                 : CommandReceiverActivity.COMMAND_PROFILE_OWNER_CLEAR_POLICIES);
         startActivity(intent);
     }
-}
\ No newline at end of file
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/UserRestrictions.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/UserRestrictions.java
index 06d0381..935a1b8 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/UserRestrictions.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/UserRestrictions.java
@@ -18,6 +18,7 @@
 
 import android.content.Context;
 import android.content.Intent;
+import android.content.pm.PackageManager;
 import android.os.UserManager;
 import android.provider.Settings;
 import android.util.ArrayMap;
@@ -174,6 +175,43 @@
                         item.intentAction);
     }
 
+    public static boolean isRestrictionValid(Context context, String restriction) {
+        final PackageManager pm = context.getPackageManager();
+        switch (restriction) {
+            case UserManager.DISALLOW_ADD_USER:
+            case UserManager.DISALLOW_REMOVE_USER:
+                return UserManager.supportsMultipleUsers();
+            case UserManager.DISALLOW_ADJUST_VOLUME:
+                return pm.hasSystemFeature(PackageManager.FEATURE_AUDIO_OUTPUT);
+            case UserManager.DISALLOW_CONFIG_CELL_BROADCASTS:
+                // Get com.android.internal.R.bool.config_cellBroadcastAppLinks
+                final int resId = context.getResources().getIdentifier(
+                        "config_cellBroadcastAppLinks", "bool", "android");
+                boolean isCellBroadcastAppLinkEnabled = context.getResources().getBoolean(resId);
+                try {
+                    if (isCellBroadcastAppLinkEnabled) {
+                        if (pm.getApplicationEnabledSetting("com.android.cellbroadcastreceiver")
+                                == PackageManager.COMPONENT_ENABLED_STATE_DISABLED) {
+                            isCellBroadcastAppLinkEnabled = false;  // CMAS app disabled
+                        }
+                    }
+                } catch (IllegalArgumentException ignored) {
+                    isCellBroadcastAppLinkEnabled = false;  // CMAS app not installed
+                }
+                return isCellBroadcastAppLinkEnabled;
+            case UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS:
+                return pm.hasSystemFeature(PackageManager.FEATURE_TELEPHONY);
+            case UserManager.DISALLOW_CONFIG_WIFI:
+                return pm.hasSystemFeature(PackageManager.FEATURE_WIFI);
+            case UserManager.DISALLOW_OUTGOING_BEAM:
+                return pm.hasSystemFeature(PackageManager.FEATURE_NFC);
+            case UserManager.DISALLOW_SHARE_LOCATION:
+                return pm.hasSystemFeature(PackageManager.FEATURE_LOCATION);
+            default:
+                return true;
+        }
+    }
+
     private static class UserRestrictionItem {
         final int label;
         final int userAction;
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/VpnTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/VpnTestActivity.java
index 49c0c20..b7c10bb 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/VpnTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/VpnTestActivity.java
@@ -56,7 +56,7 @@
     private DevicePolicyManager mDevicePolicyManager;
     private UserManager mUserManager;
     private static final String TAG = "DeviceOwnerPositiveTestActivity";
-    private static final int REQUEST_VPN_CODE = 1;
+    private static final int REQUEST_VPN_CODE = 54321;
 
     @Override
     protected void onCreate(Bundle savedInstanceState) {
@@ -81,12 +81,13 @@
 
     @Override
     protected void onActivityResult(int requestCode, int result, Intent data) {
-        if (requestCode == REQUEST_VPN_CODE && result == RESULT_OK) {
+        if (requestCode == REQUEST_VPN_CODE) {
+            // We don't care about the result - ideally it should automatically cancel, but if
+            // some custom component doesn't do that, try to establish the connection anyway
+            // and see what happens.
             establishVpn();
         } else {
-            // vpn connection canceled by user
-            Log.w(TAG, "Test failed, canceled by user");
-            populateInfo(R.string.device_owner_vpn_connection_canceled);
+            Log.w(TAG, "Unexpected request code: " + requestCode);
         }
     }
 
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/DynamicSensorDiscoveryTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/DynamicSensorDiscoveryTestActivity.java
index dd9c752..df3a67c 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/DynamicSensorDiscoveryTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/DynamicSensorDiscoveryTestActivity.java
@@ -88,7 +88,7 @@
         showUserMessage(String.format("Please connect an external sensor to device in %d seconds.",
                     CONNECTION_TIMEOUT_SEC));
 
-        Assert.assertTrue("Cannot detect sensor connection.", mCallback.waitForConnection());
+        Assert.assertTrue("Cannot detect sensor connection.", mCallback.waitForConnection(null));
         mSensorConnected = true;
         mSensorId = mCallback.getSensorId();
         return "OnConnect: Success";
@@ -143,7 +143,8 @@
 
         showUserMessage(String.format("Please connect the same sensor that was previously " +
                     "connected in %d seconds", CONNECTION_TIMEOUT_SEC));
-        Assert.assertTrue("Cannot detect sensor reconnection.", mCallback.waitForConnection());
+        Assert.assertTrue("Cannot detect sensor reconnection.",
+                mCallback.waitForConnection(mSensorId));
 
         Integer sensorId = mCallback.getSensorId();
         boolean match = mSensorId != null && sensorId != null &&
@@ -155,16 +156,19 @@
     private class Callback extends SensorManager.DynamicSensorCallback {
 
         private Sensor mSensor = null;
+        private Integer mExpectSensorId = null;
         private CountDownLatch mConnectLatch;
         private CountDownLatch mDisconnectLatch;
 
         @Override
         public void onDynamicSensorConnected(Sensor sensor) {
-            mSensor = sensor;
-            Log.d(TAG, "Sensor Connected: " + mSensor);
+            Log.d(TAG, "Sensor Connected: " + sensor);
 
-            if (mConnectLatch != null) {
-                mConnectLatch.countDown();
+            if (mExpectSensorId == null || mExpectSensorId == sensor.getId()) {
+                mSensor = sensor;
+                if (mConnectLatch != null) {
+                    mConnectLatch.countDown();
+                }
             }
         }
 
@@ -178,8 +182,9 @@
             }
         }
 
-        public boolean waitForConnection() {
+        public boolean waitForConnection(Integer sensorId) {
             boolean ret;
+            mExpectSensorId = sensorId;
             mConnectLatch = new CountDownLatch(1);
             try {
                 ret = mConnectLatch.await(CONNECTION_TIMEOUT_SEC, TimeUnit.SECONDS);
diff --git a/common/device-side/device-info/src/com/android/compatibility/common/deviceinfo/GlesStubActivity.java b/common/device-side/device-info/src/com/android/compatibility/common/deviceinfo/GlesStubActivity.java
index ef95b66..9e0d3c5 100644
--- a/common/device-side/device-info/src/com/android/compatibility/common/deviceinfo/GlesStubActivity.java
+++ b/common/device-side/device-info/src/com/android/compatibility/common/deviceinfo/GlesStubActivity.java
@@ -204,7 +204,13 @@
 
         static protected int[] getIntValues(int fieldId, int count) throws IllegalAccessException{
             int[] resultInts = new int[count];
-            GLES20.glGetIntegerv(fieldId, resultInts, 0);
+            // The JNI wrapper layer has a piece of code that defines
+            // the expected array length. It defaults to 1 and looks
+            // like it's missing GLES3 variables. So, we won't be
+            // querying if the array has zero lenght.
+            if (count > 0) {
+                GLES20.glGetIntegerv(fieldId, resultInts, 0);
+            }
             return resultInts;
         }
     }
@@ -318,7 +324,7 @@
         }
     }
 
-	// NOTE: Changes to the types of the variables will carry over to
+    // NOTE: Changes to the types of the variables will carry over to
     // GraphicsDeviceInfo proto via GraphicsDeviceInfo. See
     // go/edi-userguide for details.
     static ImplementationVariable[] GLES2_IMPLEMENTATION_VARIABLES = {
diff --git a/common/host-side/tradefed/res/report/compatibility_result.xsl b/common/host-side/tradefed/res/report/compatibility_result.xsl
index b0e40b1..b86107d 100644
--- a/common/host-side/tradefed/res/report/compatibility_result.xsl
+++ b/common/host-side/tradefed/res/report/compatibility_result.xsl
@@ -180,7 +180,7 @@
     <xsl:template name="filteredResultTestReport">
         <xsl:param name="header" />
         <xsl:param name="resultFilter" />
-        <xsl:variable name="numMatching" select="count(Result/Module/Test[@result=$resultFilter])" />
+        <xsl:variable name="numMatching" select="count(Result/Module/TestCase/Test[@result=$resultFilter])" />
         <xsl:if test="$numMatching &gt; 0">
             <h2 align="center"><xsl:value-of select="$header" /> (<xsl:value-of select="$numMatching"/>)</h2>
             <xsl:call-template name="detailedTestReport">
@@ -194,7 +194,7 @@
         <div>
             <xsl:for-each select="Result/Module">
                 <xsl:if test="$resultFilter=''
-                        or count(Test[@result=$resultFilter]) &gt; 0">
+                        or count(TestCase/Test[@result=$resultFilter]) &gt; 0">
 
                     <table class="testdetails">
                         <tr>
@@ -214,7 +214,7 @@
                             <xsl:variable name="TestCase" select="."/>
                             <!-- test -->
                             <xsl:for-each select="Test">
-                                <xsl:if test="$resultFilter='' or $resultFilter=@result">
+                                <xsl:if test="$resultFilter='' or @result=$resultFilter">
                                     <tr>
                                         <td class="testname"> <xsl:value-of select="$TestCase/@name"/>#<xsl:value-of select="@name"/></td>
 
diff --git a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/build/CompatibilityBuildHelper.java b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/build/CompatibilityBuildHelper.java
index ed9150e..10fc053 100644
--- a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/build/CompatibilityBuildHelper.java
+++ b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/build/CompatibilityBuildHelper.java
@@ -121,6 +121,10 @@
         }
     }
 
+    public String getRecentCommandLineArgs() {
+        return mBuildInfo.getBuildAttributes().get(COMMAND_LINE_ARGS);
+    }
+
     public String getSuiteBuild() {
         return mBuildInfo.getBuildAttributes().get(SUITE_BUILD);
     }
diff --git a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/result/ResultReporter.java b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/result/ResultReporter.java
index 6a9a9ab..05f7ff9 100644
--- a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/result/ResultReporter.java
+++ b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/result/ResultReporter.java
@@ -18,6 +18,7 @@
 import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
 import com.android.compatibility.common.tradefed.result.InvocationFailureHandler;
 import com.android.compatibility.common.tradefed.testtype.CompatibilityTest;
+import com.android.compatibility.common.tradefed.testtype.CompatibilityTest.RetryType;
 import com.android.compatibility.common.util.ICaseResult;
 import com.android.compatibility.common.util.IInvocationResult;
 import com.android.compatibility.common.util.IModuleResult;
@@ -91,6 +92,13 @@
             importance = Importance.IF_UNSET)
     private Integer mRetrySessionId = null;
 
+    @Option(name = CompatibilityTest.RETRY_TYPE_OPTION,
+            description = "used with " + CompatibilityTest.RETRY_OPTION
+            + ", retry tests of a certain status. Possible values include \"failed\" and "
+            + "\"not_executed\".",
+            importance = Importance.IF_UNSET)
+    private RetryType mRetryType = null;
+
     @Option(name = "result-server", description = "Server to publish test results.")
     private String mResultServer;
 
@@ -125,6 +133,13 @@
     private int mCurrentTestNum;
     private int mTotalTestsInModule;
 
+
+    // Whether modules can be marked done for this invocation. Initialized in invocationStarted()
+    // Visible for unit testing
+    protected boolean mCanMarkDone;
+    // Whether the current module has previously been marked done
+    private boolean mModuleWasDone;
+
     // Nullable. If null, "this" is considered the master and must handle
     // result aggregation and reporting. When not null, it should forward events
     // to the master.
@@ -157,6 +172,7 @@
             if (mDeviceSerial == null && buildInfo.getDeviceSerial() != null) {
                 mDeviceSerial = buildInfo.getDeviceSerial();
             }
+            mCanMarkDone = canMarkDone(mBuildHelper.getRecentCommandLineArgs());
         }
 
         if (isShardResultReporter()) {
@@ -253,13 +269,14 @@
                 mTotalTestsInModule +=
                         Math.max(0, numTests - mCurrentModuleResult.getNotExecuted());
             }
-            mCurrentModuleResult.setDone(false);
         } else {
             mCurrentModuleResult = mResult.getOrCreateModule(id);
             mTotalTestsInModule = numTests;
             // Reset counters
             mCurrentTestNum = 0;
         }
+        mModuleWasDone = mCurrentModuleResult.isDone();
+        mCurrentModuleResult.setDone(false);
     }
 
     /**
@@ -344,8 +361,12 @@
     @Override
     public void testRunEnded(long elapsedTime, Map<String, String> metrics) {
         mCurrentModuleResult.addRuntime(elapsedTime);
-        // Expect them to be equal, but greater than to be safe.
-        mCurrentModuleResult.setDone(mCurrentTestNum >= mTotalTestsInModule);
+        if (mCanMarkDone || mModuleWasDone) {
+            // Only mark module done if status of the invocation allows it (mCanMarkDone) or module
+            // was previously marked done (mModuleWasDone) and all expected tests are collected.
+            // Expect mCurrentTestNum = mTotalTestsInModule, but use >= to be safe
+            mCurrentModuleResult.setDone(mCurrentTestNum >= mTotalTestsInModule);
+        }
         mCurrentModuleResult.setNotExecuted(Math.max(mTotalTestsInModule - mCurrentTestNum, 0));
         if (isShardResultReporter()) {
             // Forward module results to the master.
@@ -408,7 +429,6 @@
 
         // NOTE: Everything after this line only applies to the master ResultReporter.
 
-
         synchronized(this) {
             // The master ResultReporter tracks the progress of all invocations across
             // shard ResultReporters. Writing results should not proceed until all
@@ -460,16 +480,16 @@
 
         long startTime = mResult.getStartTime();
         try {
+            // Zip the full test results directory.
+            copyDynamicConfigFiles(mBuildHelper.getDynamicConfigFiles(), mResultDir);
+            copyFormattingFiles(mResultDir);
+
             File resultFile = ResultHandler.writeResults(mBuildHelper.getSuiteName(),
                     mBuildHelper.getSuiteVersion(), mBuildHelper.getSuitePlan(),
                     mBuildHelper.getSuiteBuild(), mResult, mResultDir, startTime,
                     elapsedTime + startTime, mReferenceUrl, getLogUrl(),
                     mBuildHelper.getCommandLineArgs());
             info("Test Result: %s", resultFile.getCanonicalPath());
-
-            // Zip the full test results directory.
-            copyDynamicConfigFiles(mBuildHelper.getDynamicConfigFiles(), mResultDir);
-            copyFormattingFiles(mResultDir);
             File zippedResults = zipResults(mResultDir);
             info("Full Result: %s", zippedResults.getCanonicalPath());
 
@@ -614,6 +634,28 @@
     }
 
     /**
+     * Returns whether it is safe to mark modules as "done", given the invocation command-line
+     * arguments. Returns true unless this is a retry and specific filtering techniques are applied
+     * on the command-line, such as:
+     *   --retry-type failed
+     *   --include-filter
+     *   --exclude-filter
+     *   -t/--test
+     *   --subplan
+     */
+    private boolean canMarkDone(String args) {
+        if (mRetrySessionId == null) {
+            return true; // always allow modules to be marked done if not retry
+        }
+        return !(RetryType.FAILED.equals(mRetryType)
+                || args.contains(CompatibilityTest.INCLUDE_FILTER_OPTION)
+                || args.contains(CompatibilityTest.EXCLUDE_FILTER_OPTION)
+                || args.contains(CompatibilityTest.SUBPLAN_OPTION)
+                || args.matches(String.format(".* (-%s|--%s) .*",
+                CompatibilityTest.TEST_OPTION_SHORT_NAME, CompatibilityTest.TEST_OPTION)));
+    }
+
+    /**
      * Copy the xml formatting files stored in this jar to the results directory
      *
      * @param resultsDir
diff --git a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/result/SubPlanCreator.java b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/result/SubPlanCreator.java
index ff28263..6b9b5e4 100644
--- a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/result/SubPlanCreator.java
+++ b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/result/SubPlanCreator.java
@@ -57,12 +57,20 @@
     public static final String FAILED = "failed";
     public static final String NOT_EXECUTED = "not_executed";
     // static mapping of result types to TestStatuses
-    private static final Map<String, TestStatus> mStatusMap;
+    private static final Map<String, TestStatus> STATUS_MAP;
     static {
         Map<String, TestStatus> statusMap = new HashMap<String, TestStatus>();
         statusMap.put(PASSED, TestStatus.PASS);
         statusMap.put(FAILED, TestStatus.FAIL);
-        mStatusMap = Collections.unmodifiableMap(statusMap);
+        STATUS_MAP = Collections.unmodifiableMap(statusMap);
+    }
+
+    // TODO(aaronholden): remove this temporary workaround for b/33090757
+    private static final Set<String> MULTITEST_MODULES;
+    static {
+        Set<String> multiTestModuleSet = new HashSet<String>();
+        multiTestModuleSet.add("CtsDeqpTestCases");
+        MULTITEST_MODULES = Collections.unmodifiableSet(multiTestModuleSet);
     }
 
     @Option (name = "name", shortName = 'n', description = "the name of the subplan to create",
@@ -183,10 +191,51 @@
         if (mModuleName != null) {
             subPlan.addIncludeFilter(new TestFilter(mAbiName, mModuleName, mTestName).toString());
         }
+        Set<TestStatus> statusesToRun = getStatusesToRun();
 
         for (IModuleResult module : mResult.getModules()) {
+
+            // TODO(aaronholden): remove this special case from SubPlanCreator, and filter
+            // individual tests only when the module should run. Tracked by b/33211104
+            if (MULTITEST_MODULES.contains(module.getName())) {
+                // cannot check module.isDone() since this value is not accurate for modules
+                // with multiple test configs. If we should run not-executed tests, include module
+                // and exclude tests with status not in mResultTypes.
+                TestFilter moduleFilter =
+                            new TestFilter(module.getAbi(), module.getName(), null /*test*/);
+                if (mResultTypes.contains(NOT_EXECUTED)) {
+                    subPlan.addIncludeFilter(moduleFilter.toString());
+                    for (ICaseResult caseResult : module.getResults()) {
+                        for (ITestResult testResult : caseResult.getResults()) {
+                            if (!statusesToRun.contains(testResult.getResultStatus())) {
+                                TestFilter testExclude = new TestFilter(module.getAbi(),
+                                        module.getName(), testResult.getFullName());
+                                subPlan.addExcludeFilter(testExclude.toString());
+                            }
+                        }
+                    }
+                } else {
+                    // not running not-executed tests, only include executed tests
+                    if (shouldRunModule(module)) {
+                        // at least some executed tests will be included
+                        for (ICaseResult caseResult : module.getResults()) {
+                            for (ITestResult testResult : caseResult.getResults()) {
+                                if (statusesToRun.contains(testResult.getResultStatus())) {
+                                    TestFilter testInclude = new TestFilter(module.getAbi(),
+                                            module.getName(), testResult.getFullName());
+                                    subPlan.addIncludeFilter(testInclude.toString());
+                                }
+                            }
+                        }
+                    } else {
+                        // no executed tests will be included, so exclude entire module
+                        subPlan.addExcludeFilter(moduleFilter.toString());
+                    }
+                }
+                continue;
+            }
+
             if (shouldRunModule(module)) {
-                Set<TestStatus> statusesToRun = getStatusesToRun();
                 TestFilter moduleInclude =
                             new TestFilter(module.getAbi(), module.getName(), null /*test*/);
                 if (shouldRunEntireModule(module)) {
@@ -280,7 +329,7 @@
         for (String resultType : mResultTypes) {
             // no test status exists for not-executed tests
             if (resultType != NOT_EXECUTED) {
-                statusesToRun.add(mStatusMap.get(resultType));
+                statusesToRun.add(STATUS_MAP.get(resultType));
             }
         }
         return statusesToRun;
diff --git a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/targetprep/PreconditionPreparer.java b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/targetprep/PreconditionPreparer.java
index fc25e03..27ef658 100644
--- a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/targetprep/PreconditionPreparer.java
+++ b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/targetprep/PreconditionPreparer.java
@@ -19,14 +19,20 @@
 import com.android.compatibility.common.tradefed.testtype.CompatibilityTest;
 import com.android.ddmlib.Log;
 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.device.DeviceNotAvailableException;
 import com.android.tradefed.device.ITestDevice;
 import com.android.tradefed.log.LogUtil;
+import com.android.tradefed.log.LogUtil.CLog;
 import com.android.tradefed.targetprep.BuildError;
 import com.android.tradefed.targetprep.ITargetPreparer;
 import com.android.tradefed.targetprep.TargetSetupError;
 
+import java.util.ArrayList;
+import java.util.List;
+
 /**
  * An {@link ITargetPreparer} that performs checks and/or tasks to ensure the
  * the device is ready to run the test suite.
@@ -38,16 +44,38 @@
             description = "Whether preconditions should be skipped")
     private boolean mSkipPreconditions = false;
 
+    @Option(name = CompatibilityTest.PRECONDITION_ARG_OPTION,
+            description = "the arguments to pass to a precondition. The expected format is"
+                    + "\"<arg-name>:<arg-value>\"")
+    private List<String> mPreconditionArgs = new ArrayList<>();
+
     protected final String LOG_TAG = getClass().getSimpleName();
 
     @Override
     public void setUp(ITestDevice device, IBuildInfo buildInfo) throws TargetSetupError,
             BuildError, DeviceNotAvailableException {
         if (!mSkipPreconditions) {
+            for (String preconditionArg : mPreconditionArgs) {
+                String[] parts = preconditionArg.split(":");
+                String argName = parts[0];
+                // If arg-value is not supplied, set to "true"
+                String argValue = (parts.length > 1) ? parts[1] : Boolean.toString(true);
+                setOption(argName, argValue);
+            }
             run(device, buildInfo);
         }
     }
 
+    private void setOption(String option, String value) {
+        try {
+            OptionSetter setter = new OptionSetter(this);
+            setter.setOptionValue(option, value);
+        } catch (ConfigurationException e) {
+            CLog.i("Value %s for option %s not applicable for class %s", value, option,
+                    this.getClass().getName());
+        }
+    }
+
     public abstract void run(ITestDevice device, IBuildInfo buildInfo)
             throws TargetSetupError, BuildError, DeviceNotAvailableException;
 
diff --git a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/CompatibilityTest.java b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/CompatibilityTest.java
index bc7a29b..891a0ae 100644
--- a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/CompatibilityTest.java
+++ b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/CompatibilityTest.java
@@ -85,9 +85,11 @@
     public static final String INCLUDE_FILTER_OPTION = "include-filter";
     public static final String EXCLUDE_FILTER_OPTION = "exclude-filter";
     private static final String PLAN_OPTION = "plan";
-    private static final String SUBPLAN_OPTION = "subplan";
+    public static final String SUBPLAN_OPTION = "subplan";
     public static final String MODULE_OPTION = "module";
     public static final String TEST_OPTION = "test";
+    public static final String PRECONDITION_ARG_OPTION = "precondition-arg";
+    public static final char TEST_OPTION_SHORT_NAME = 't';
     private static final String MODULE_ARG_OPTION = "module-arg";
     private static final String TEST_ARG_OPTION = "test-arg";
     public static final String RETRY_OPTION = "retry";
@@ -135,11 +137,17 @@
     private String mModuleName = null;
 
     @Option(name = TEST_OPTION,
-            shortName = 't',
+            shortName = TEST_OPTION_SHORT_NAME,
             description = "the test run.",
             importance = Importance.IF_UNSET)
     private String mTestName = null;
 
+    @Option(name = PRECONDITION_ARG_OPTION,
+            description = "the arguments to pass to a precondition. The expected format is"
+                    + "\"<arg-name>:<arg-value>\"",
+            importance = Importance.ALWAYS)
+    private List<String> mPreconditionArgs = new ArrayList<>();
+
     @Option(name = MODULE_ARG_OPTION,
             description = "the arguments to pass to a module. The expected format is"
                     + "\"<module-name>:<arg-name>:<arg-value>\"",
@@ -376,7 +384,7 @@
                 module.setBuild(mBuildHelper.getBuildInfo());
                 module.setDevice(mDevice);
                 module.setPreparerWhitelist(mPreparerWhitelist);
-                isPrepared &= (module.prepare(mSkipPreconditions));
+                isPrepared &= (module.prepare(mSkipPreconditions, mPreconditionArgs));
             }
             mModuleRepo.setPrepared(isPrepared);
 
diff --git a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/IModuleDef.java b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/IModuleDef.java
index 14427ca..c7acdcd 100644
--- a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/IModuleDef.java
+++ b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/IModuleDef.java
@@ -23,6 +23,7 @@
 import com.android.tradefed.testtype.IRuntimeHintProvider;
 
 import java.io.File;
+import java.util.List;
 import java.util.Set;
 
 /**
@@ -66,8 +67,12 @@
 
     /**
      * Runs the module's precondition checks and setup tasks.
+     * @param skipPrep whether preparation should be skipped
+     * @param preconditionArgs arguments to set on precondition preparers for the module, taking
+     * format arg-name:arg-value. If "arg-value" is unset, the value will default to "true".
      * @return whether preparation succeeded.
      */
-    boolean prepare(boolean skipPrep) throws DeviceNotAvailableException;
+    boolean prepare(boolean skipPrep, List<String> preconditionArgs)
+            throws DeviceNotAvailableException;
 
 }
diff --git a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/ModuleDef.java b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/ModuleDef.java
index 2c3f96b..5ed6870 100644
--- a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/ModuleDef.java
+++ b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/ModuleDef.java
@@ -268,7 +268,8 @@
      * {@inheritDoc}
      */
     @Override
-    public boolean prepare(boolean skipPrep) throws DeviceNotAvailableException {
+    public boolean prepare(boolean skipPrep, List<String> preconditionArgs)
+            throws DeviceNotAvailableException {
         for (ITargetPreparer preparer : mPreconditions) {
             CLog.d("Preparer: %s", preparer.getClass().getSimpleName());
             if (preparer instanceof IAbiReceiver) {
@@ -276,6 +277,9 @@
             }
             setOption(preparer, CompatibilityTest.SKIP_PRECONDITIONS_OPTION,
                     Boolean.toString(skipPrep));
+            for (String preconditionArg : preconditionArgs) {
+                setOption(preparer, CompatibilityTest.PRECONDITION_ARG_OPTION, preconditionArg);
+            }
             try {
                 preparer.setUp(mDevice, mBuild);
             } catch (BuildError e) {
diff --git a/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/UnitTests.java b/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/UnitTests.java
index a7a31f6..b8b7858 100644
--- a/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/UnitTests.java
+++ b/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/UnitTests.java
@@ -17,6 +17,7 @@
 
 import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelperTest;
 import com.android.compatibility.common.tradefed.command.CompatibilityConsoleTest;
+import com.android.compatibility.common.tradefed.result.ChecksumReporterTest;
 import com.android.compatibility.common.tradefed.result.ConsoleReporterTest;
 import com.android.compatibility.common.tradefed.result.ResultReporterTest;
 import com.android.compatibility.common.tradefed.result.SubPlanCreatorTest;
@@ -45,6 +46,7 @@
         addTestSuite(CompatibilityConsoleTest.class);
         addTestSuite(CompatibilityTestTest.class);
         addTestSuite(ConsoleReporterTest.class);
+        addTestSuite(ChecksumReporterTest.class);
         addTestSuite(ResultReporterTest.class);
         addTestSuite(CompatibilityTestTest.class);
         addTestSuite(OptionHelperTest.class);
diff --git a/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/result/ChecksumReporterTest.java b/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/result/ChecksumReporterTest.java
new file mode 100644
index 0000000..0646385
--- /dev/null
+++ b/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/result/ChecksumReporterTest.java
@@ -0,0 +1,184 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.compatibility.common.tradefed.result;
+
+import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
+import com.android.compatibility.common.tradefed.build.CompatibilityBuildProvider;
+import com.android.compatibility.common.tradefed.result.ResultReporter;
+import com.android.compatibility.common.util.ChecksumReporter;
+import com.android.compatibility.common.util.ChecksumReporter.ChecksumValidationException;
+import com.android.compatibility.common.util.ICaseResult;
+import com.android.compatibility.common.util.IInvocationResult;
+import com.android.compatibility.common.util.IModuleResult;
+import com.android.compatibility.common.util.ITestResult;
+import com.android.compatibility.common.util.ReportLog;
+import com.android.compatibility.common.util.TestStatus;
+import com.android.tradefed.build.BuildInfo;
+import com.android.tradefed.build.IBuildInfo;
+import com.android.tradefed.config.OptionSetter;
+import com.android.tradefed.util.FileUtil;
+
+import junit.framework.TestCase;
+
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+
+public class ChecksumReporterTest extends TestCase {
+
+    private static final String ROOT_PROPERTY = "TESTS_ROOT";
+    private static final String ROOT_DIR_NAME = "root";
+    private static final String SUITE_NAME = "TESTS";
+    private static final String BUILD_NUMBER = "2";
+    private static final String SUITE_PLAN = "cts";
+    private static final String BASE_DIR_NAME = "android-tests";
+    private static final String TESTCASES = "testcases";
+    private static final String DYNAMIC_CONFIG_URL = "";
+    private static final long START_TIME = 123456L;
+
+    private ChecksumReporter mReporter;
+    private File mRoot = null;
+    private IBuildInfo mBuildInfo;
+    private CompatibilityBuildHelper mBuildHelper;
+    private ReportLog mReportLog = null;
+    private IInvocationResult mInvocationResult;
+    private IModuleResult mModuleResult;
+    private ITestResult mFailedTest;
+
+    @Override
+    public void setUp() throws Exception {
+        mReporter = new ChecksumReporter(100, .001, (short)1);
+        mRoot = FileUtil.createTempDir(ROOT_DIR_NAME);
+        File baseDir = new File(mRoot, BASE_DIR_NAME);
+        baseDir.mkdirs();
+        File testDir = new File(baseDir, TESTCASES);
+        testDir.mkdirs();
+        System.setProperty(ROOT_PROPERTY, mRoot.getAbsolutePath());
+
+        ResultReporter resultReporter = new ResultReporter();
+        OptionSetter setter = new OptionSetter(resultReporter);
+        mBuildInfo = new BuildInfo(BUILD_NUMBER, "", "");
+        mBuildHelper = new CompatibilityBuildHelper(mBuildInfo);
+        mBuildHelper.init(SUITE_PLAN, DYNAMIC_CONFIG_URL, START_TIME);
+        resultReporter.invocationStarted(mBuildInfo);
+        mInvocationResult = resultReporter.getResult();
+        mModuleResult = mInvocationResult.getOrCreateModule("Module-1");
+        mModuleResult.setDone(true);
+        mModuleResult.setNotExecuted(0);
+        ICaseResult caseResult = mModuleResult.getOrCreateResult("Case-1");
+        ITestResult test1 = caseResult.getOrCreateResult("Test1");
+        test1.passed(mReportLog);
+        mFailedTest = caseResult.getOrCreateResult("Test2");
+        mFailedTest.failed("stack-trace - error happened");
+
+        IModuleResult moduleResult2 = mInvocationResult.getOrCreateModule("Module-2");
+        ICaseResult caseResult2 = moduleResult2.getOrCreateResult("Case-2");
+        mModuleResult.setDone(false);
+        mModuleResult.setNotExecuted(1);
+        ITestResult test3 = caseResult2.getOrCreateResult("Test3");
+        test3.passed(mReportLog);
+
+    }
+
+    @Override
+    public void tearDown() throws Exception {
+        mReporter = null;
+    }
+
+    public void testStoreAndRetrieveTestResults() {
+        mReporter.addInvocation(mInvocationResult);
+        VerifyInvocationResults(mInvocationResult, mReporter);
+    }
+
+    /***
+     * By definition this test is flaky since the checksum has a false positive probability of .1%
+     */
+    public void testInvalidChecksums() {
+        mReporter.addInvocation(mInvocationResult);
+        IModuleResult module = mInvocationResult.getModules().get(1);
+        module.setDone(!module.isDone());
+        String fingerprint = mInvocationResult.getBuildFingerprint();
+        assertFalse("Checksum should contain module: " + module.getName(),
+                mReporter.containsModuleResult(module, fingerprint));
+
+        mFailedTest.setResultStatus(TestStatus.PASS);
+        assertFalse("Checksum should not contain test: " + mFailedTest.getName(),
+                mReporter.containsTestResult(mFailedTest, mModuleResult, fingerprint));
+        assertFalse("Module checksum should verify number of tests",
+                mReporter.containsModuleResult(mModuleResult, fingerprint));
+    }
+
+    public void testFileSerialization()
+            throws IOException, ClassNotFoundException, ChecksumValidationException {
+        mReporter.addInvocation(mInvocationResult);
+
+        File file1 = new File(mRoot, "file1.txt");
+        try (FileWriter fileWriter = new FileWriter(file1, false)) {
+            fileWriter.append("This is a test file");
+        }
+
+        mReporter.addDirectory(mRoot);
+        mReporter.saveToFile(mRoot);
+
+        ChecksumReporter storedChecksum = ChecksumReporter.load(mRoot);
+        VerifyInvocationResults(mInvocationResult, storedChecksum);
+        assertTrue("Serializing checksum maintains file hash",
+                storedChecksum.containsFile(file1, mRoot.getName()));
+    }
+
+    public void testFileCRCOperations() throws IOException {
+        File subDirectory = new File(mRoot, "child");
+        subDirectory.mkdir();
+        File file1 = new File(mRoot, "file1.txt");
+        try (FileWriter fileWriter = new FileWriter(file1, false)) {
+            fileWriter.append("This is a test file");
+        }
+
+        File file2 = new File(subDirectory, "file2.txt");
+        try (FileWriter fileWriter = new FileWriter(file2, false)) {
+            fileWriter.append("This is another test file with a different crc");
+        }
+
+        mReporter.addDirectory(mRoot);
+        String folderName = mRoot.getName();
+        assertTrue(mReporter.containsFile(file1, folderName));
+        assertTrue(mReporter.containsFile(file2, folderName + "/child"));
+        assertFalse("Should not contain non-existent file",
+                mReporter.containsFile(new File(mRoot, "fake.txt"), folderName));
+
+        File file3 = new File(mRoot, "file3.txt");
+        try (FileWriter fileWriter = new FileWriter(file3, false)) {
+            fileWriter.append("This is a test file added after crc calculated");
+        }
+        assertFalse("Should not contain file created after crc calculated",
+                mReporter.containsFile(file3, folderName));
+
+    }
+
+    private void VerifyInvocationResults(IInvocationResult invocation, ChecksumReporter reporter) {
+        for (IModuleResult module : invocation.getModules()) {
+            String buildFingerprint = invocation.getBuildFingerprint();
+            assertTrue("Checksum should contain module: " + module.getName(),
+                    reporter.containsModuleResult(module, buildFingerprint));
+            for (ICaseResult caseResult : module.getResults()) {
+                for (ITestResult result : caseResult.getResults()) {
+                    assertTrue("Checksum should contain test: " + result.getName(),
+                            reporter.containsTestResult(result, module, buildFingerprint));
+                }
+            }
+        }
+    }
+}
diff --git a/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/result/ResultReporterTest.java b/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/result/ResultReporterTest.java
index 0df6ad0..9e76e54 100644
--- a/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/result/ResultReporterTest.java
+++ b/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/result/ResultReporterTest.java
@@ -273,6 +273,84 @@
                 finalTestResult2.getResultStatus());
     }
 
+    public void testRetryCanSetDone() throws Exception {
+        mReporter.invocationStarted(mBuildInfo);
+        // Set mCanMarkDone directly (otherwise we must build result directory, write XML, and
+        // perform actual retry)
+        mReporter.mCanMarkDone = true;
+        // Set up IInvocationResult with existing results from previous session
+        IInvocationResult invocationResult = mReporter.getResult();
+        IModuleResult moduleResult = invocationResult.getOrCreateModule(ID);
+        moduleResult.setDone(false);
+        ICaseResult caseResult = moduleResult.getOrCreateResult(CLASS);
+        ITestResult testResult1 = caseResult.getOrCreateResult(METHOD_1);
+        testResult1.setResultStatus(TestStatus.PASS);
+        testResult1.setRetry(true);
+        ITestResult testResult2 = caseResult.getOrCreateResult(METHOD_2);
+        testResult2.setResultStatus(TestStatus.FAIL);
+        testResult2.setStackTrace(STACK_TRACE);
+        testResult2.setRetry(true);
+
+        // Assume no additional filtering is applied to retry, and all tests for the module have
+        // been collected. Thus, module "done" value should switch.
+        mReporter.testRunStarted(ID, 1);
+
+        TestIdentifier test2 = new TestIdentifier(CLASS, METHOD_2);
+        mReporter.testStarted(test2);
+        mReporter.testEnded(test2, new HashMap<String, String>());
+
+        mReporter.testRunEnded(10, new HashMap<String, String>());
+        mReporter.invocationEnded(10);
+
+        // Verification that results have been overwritten.
+        IInvocationResult result = mReporter.getResult();
+        assertEquals("Expected 2 pass", 2, result.countResults(TestStatus.PASS));
+        assertEquals("Expected 0 failures", 0, result.countResults(TestStatus.FAIL));
+        List<IModuleResult> modules = result.getModules();
+        assertEquals("Expected 1 module", 1, modules.size());
+        IModuleResult module = modules.get(0);
+        assertTrue("Module should be marked done", module.isDone());
+    }
+
+    public void testRetryCannotSetDone() throws Exception {
+        mReporter.invocationStarted(mBuildInfo);
+        // Set mCanMarkDone directly (otherwise we must build result directory, write XML, and
+        // perform actual retry)
+        mReporter.mCanMarkDone = false;
+        // Set up IInvocationResult with existing results from previous session
+        IInvocationResult invocationResult = mReporter.getResult();
+        IModuleResult moduleResult = invocationResult.getOrCreateModule(ID);
+        moduleResult.setDone(false);
+        ICaseResult caseResult = moduleResult.getOrCreateResult(CLASS);
+        ITestResult testResult1 = caseResult.getOrCreateResult(METHOD_1);
+        testResult1.setResultStatus(TestStatus.PASS);
+        testResult1.setRetry(true);
+        ITestResult testResult2 = caseResult.getOrCreateResult(METHOD_2);
+        testResult2.setResultStatus(TestStatus.FAIL);
+        testResult2.setStackTrace(STACK_TRACE);
+        testResult2.setRetry(true);
+
+        // Since using retry-type failed option, we only run previously failed test
+        // and don't run any non-executed tests, so module "done" value should not switch.
+        mReporter.testRunStarted(ID, 1);
+
+        TestIdentifier test2 = new TestIdentifier(CLASS, METHOD_2);
+        mReporter.testStarted(test2);
+        mReporter.testEnded(test2, new HashMap<String, String>());
+
+        mReporter.testRunEnded(10, new HashMap<String, String>());
+        mReporter.invocationEnded(10);
+
+        // Verification that results have been overwritten.
+        IInvocationResult result = mReporter.getResult();
+        assertEquals("Expected 2 pass", 2, result.countResults(TestStatus.PASS));
+        assertEquals("Expected 0 failures", 0, result.countResults(TestStatus.FAIL));
+        List<IModuleResult> modules = result.getModules();
+        assertEquals("Expected 1 module", 1, modules.size());
+        IModuleResult module = modules.get(0);
+        assertFalse("Module should not be marked done", module.isDone());
+    }
+
     public void testResultReporting_moduleNotDone() throws Exception {
         mReporter.invocationStarted(mBuildInfo);
         mReporter.testRunStarted(ID, 2);
diff --git a/common/host-side/util/src/com/android/compatibility/common/util/ChecksumReporter.java b/common/host-side/util/src/com/android/compatibility/common/util/ChecksumReporter.java
new file mode 100644
index 0000000..faac61f
--- /dev/null
+++ b/common/host-side/util/src/com/android/compatibility/common/util/ChecksumReporter.java
@@ -0,0 +1,396 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.compatibility.common.util;
+
+import com.android.annotations.Nullable;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Joiner;
+import com.google.common.base.Strings;
+import com.google.common.hash.BloomFilter;
+import com.google.common.hash.Funnels;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.ObjectInput;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutput;
+import java.io.ObjectOutputStream;
+import java.io.OutputStream;
+import java.io.Serializable;
+import java.security.DigestException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.Arrays;
+import java.util.HashMap;
+
+/***
+ * Calculate and store checksum values for files and test results
+ */
+public final class ChecksumReporter implements Serializable {
+
+    public static final String NAME = "checksum.data";
+    public static final String PREV_NAME = "checksum.previous.data";
+
+    private static final double DEFAULT_FPP = 0.05;
+    private static final String SEPARATOR = "/";
+    private static final String ID_SEPARATOR = "@";
+    private static final String NAME_SEPARATOR = ".";
+
+    private static final short CURRENT_VERSION = 1;
+    // Serialized format Id (ie magic number) used to identify serialized data.
+    static final short SERIALIZED_FORMAT_CODE = 650;
+
+    private final BloomFilter<CharSequence> mResultChecksum;
+    private final HashMap<String, byte[]> mFileChecksum;
+    private final short mVersion;
+
+    /***
+     * Calculate checksum of test results and files in result directory and write to disk
+     * @param dir test results directory
+     * @param result test results
+     * @return true if successful, false if unable to calculate or store the checksum
+     */
+    public static boolean tryCreateChecksum(File dir, IInvocationResult result) {
+        try {
+            int totalCount = countTestResults(result);
+            ChecksumReporter checksumReporter =
+                    new ChecksumReporter(totalCount, DEFAULT_FPP, CURRENT_VERSION);
+            checksumReporter.addInvocation(result);
+            checksumReporter.addDirectory(dir);
+            checksumReporter.saveToFile(dir);
+        } catch (Exception e) {
+            return false;
+        }
+        return true;
+    }
+
+    /***
+     * Create Checksum Reporter from data saved on disk
+     * @param directory
+     * @return
+     * @throws ChecksumValidationException
+     */
+    public static ChecksumReporter load(File directory) throws ChecksumValidationException {
+        ChecksumReporter reporter = new ChecksumReporter(directory);
+        if (reporter.getCapacity() > 1.1) {
+            throw new ChecksumValidationException("Capacity exceeded.");
+        }
+        return reporter;
+    }
+
+    /***
+     * Deserialize checksum from file
+     * @param directory the parent directory containing the checksum file
+     * @throws ChecksumValidationException
+     */
+    public ChecksumReporter(File directory) throws ChecksumValidationException {
+        File file = new File(directory, ChecksumReporter.NAME);
+        try (FileInputStream fileStream = new FileInputStream(file);
+            InputStream outputStream = new BufferedInputStream(fileStream);
+            ObjectInput objectInput = new ObjectInputStream(outputStream)) {
+            short magicNumber = objectInput.readShort();
+            switch (magicNumber) {
+                case SERIALIZED_FORMAT_CODE:
+                   mVersion = objectInput.readShort();
+                    mResultChecksum = (BloomFilter<CharSequence>) objectInput.readObject();
+                    mFileChecksum = (HashMap<String, byte[]>) objectInput.readObject();
+                    break;
+                default:
+                    throw new ChecksumValidationException("Unknown format of serialized data.");
+            }
+        } catch (Exception e) {
+            throw new ChecksumValidationException("Unable to load checksum from file", e);
+        }
+        if (mVersion > CURRENT_VERSION) {
+            throw new ChecksumValidationException(
+                    "File contains a newer version of ChecksumReporter");
+        }
+    }
+
+    /***
+     * Create new instance of ChecksumReporter
+     * @param testCount the number of test results that will be stored
+     * @param fpp the false positive percentage for result lookup misses
+     */
+    public ChecksumReporter(int testCount, double fpp, short version) {
+        mResultChecksum = BloomFilter.create(Funnels.unencodedCharsFunnel(),
+                testCount, fpp);
+        mFileChecksum = new HashMap<>();
+        mVersion = version;
+    }
+
+    /***
+     * Add each test result from each module and test case
+     */
+    public void addInvocation(IInvocationResult invocationResult) {
+        for (IModuleResult module : invocationResult.getModules()) {
+            String buildFingerprint = invocationResult.getBuildFingerprint();
+            addModuleResult(module, buildFingerprint);
+            for (ICaseResult caseResult : module.getResults()) {
+                for (ITestResult testResult : caseResult.getResults()) {
+                    addTestResult(testResult, module, buildFingerprint);
+                }
+            }
+        }
+    }
+
+    /***
+     * Calculate CRC of file and store the result
+     * @param file crc calculated on this file
+     * @param path part of the key to identify the files crc
+     */
+    public void addFile(File file, String path) {
+        byte[] crc;
+        try {
+            crc = calculateFileChecksum(file);
+        } catch (ChecksumValidationException e) {
+            crc = new byte[0];
+        }
+        String key = path + SEPARATOR + file.getName();
+        mFileChecksum.put(key, crc);
+    }
+
+    @VisibleForTesting
+    public boolean containsFile(File file, String path) {
+        String key = path + SEPARATOR + file.getName();
+        if (mFileChecksum.containsKey(key))
+        {
+            try {
+                byte[] crc = calculateFileChecksum(file);
+                return Arrays.equals(mFileChecksum.get(key), crc);
+            } catch (ChecksumValidationException e) {
+                return false;
+            }
+        }
+        return false;
+    }
+
+    /***
+     * Adds all child files recursively through all sub directories
+     * @param directory target that is deeply searched for files
+     */
+    public void addDirectory(File directory) {
+        addDirectory(directory, directory.getName());
+    }
+
+    /***
+     * @param path the relative path to the current directory from the base directory
+     */
+    private void addDirectory(File directory, String path) {
+        for(String childName : directory.list()) {
+            File child = new File(directory, childName);
+            if (child.isDirectory()) {
+                addDirectory(child, path + SEPARATOR + child.getName());
+            } else {
+                addFile(child, path);
+            }
+        }
+    }
+
+    /***
+     * Calculate checksum of test result and store the value
+     * @param testResult the target of the checksum
+     * @param moduleResult the module that contains the test result
+     * @param buildFingerprint the fingerprint the test execution is running against
+     */
+    public void addTestResult(
+        ITestResult testResult, IModuleResult moduleResult, String buildFingerprint) {
+
+        String signature = generateTestResultSignature(testResult, moduleResult, buildFingerprint);
+        mResultChecksum.put(signature);
+    }
+
+    @VisibleForTesting
+    public boolean containsTestResult(
+            ITestResult testResult, IModuleResult moduleResult, String buildFingerprint) {
+
+        String signature = generateTestResultSignature(testResult, moduleResult, buildFingerprint);
+        return mResultChecksum.mightContain(signature);
+    }
+
+    /***
+     * Calculate checksm of module result and store value
+     * @param moduleResult  the target of the checksum
+     * @param buildFingerprint the fingerprint the test execution is running against
+     */
+    public void addModuleResult(IModuleResult moduleResult, String buildFingerprint) {
+        mResultChecksum.put(
+                generateModuleResultSignature(moduleResult, buildFingerprint));
+        mResultChecksum.put(
+                generateModuleSummarySignature(moduleResult, buildFingerprint));
+    }
+
+    @VisibleForTesting
+    public Boolean containsModuleResult(IModuleResult moduleResult, String buildFingerprint) {
+        return mResultChecksum.mightContain(
+                generateModuleResultSignature(moduleResult, buildFingerprint));
+    }
+
+    /***
+     * Write the checksum data to disk.
+     * Overwrites existing file
+     * @param directory
+     * @throws IOException
+     */
+    public void saveToFile(File directory) throws IOException {
+        File file = new File(directory, NAME);
+
+        try (FileOutputStream fileStream = new FileOutputStream(file, false);
+             OutputStream outputStream = new BufferedOutputStream(fileStream);
+             ObjectOutput objectOutput = new ObjectOutputStream(outputStream)) {
+            objectOutput.writeShort(SERIALIZED_FORMAT_CODE);
+            objectOutput.writeShort(mVersion);
+            objectOutput.writeObject(mResultChecksum);
+            objectOutput.writeObject(mFileChecksum);
+        }
+    }
+
+    @VisibleForTesting
+    double getCapacity() {
+        // If default FPP changes:
+        // increment the CURRENT_VERSION and set the denominator based on this.mVersion
+        return mResultChecksum.expectedFpp() / DEFAULT_FPP;
+    }
+
+    static String generateTestResultSignature(ITestResult testResult, IModuleResult module,
+            String buildFingerprint) {
+        StringBuilder sb = new StringBuilder();
+        String stacktrace = testResult.getStackTrace();
+
+        stacktrace = stacktrace == null ? "" : stacktrace.trim();
+        // Line endings for stacktraces are somewhat unpredictable and there is no need to
+        // actually read the result they are all removed for consistency.
+        stacktrace = stacktrace.replaceAll("\\r?\\n|\\r", "");
+        sb.append(buildFingerprint).append(SEPARATOR)
+                .append(module.getId()).append(SEPARATOR)
+                .append(testResult.getFullName()).append(SEPARATOR)
+                .append(testResult.getResultStatus().getValue()).append(SEPARATOR)
+                .append(stacktrace).append(SEPARATOR);
+        return sb.toString();
+    }
+
+    static String generateTestResultSignature(
+            String packageName, String suiteName, String caseName, String testName, String abi,
+            String status,
+            String stacktrace,
+            String buildFingerprint) {
+
+        String testId = buildTestId(suiteName, caseName, testName, abi);
+        StringBuilder sb = new StringBuilder();
+
+        stacktrace = stacktrace == null ? "" : stacktrace.trim();
+        // Line endings for stacktraces are somewhat unpredictable and there is no need to
+        // actually read the result they are all removed for consistency.
+        stacktrace = stacktrace.replaceAll("\\r?\\n|\\r", "");
+        sb.append(buildFingerprint)
+                .append(SEPARATOR)
+                .append(packageName)
+                .append(SEPARATOR)
+                .append(testId)
+                .append(SEPARATOR)
+                .append(status)
+                .append(SEPARATOR)
+                .append(stacktrace)
+                .append(SEPARATOR);
+        return sb.toString();
+    }
+
+    private static String buildTestId(
+            String suiteName, String caseName, String testName, @Nullable String abi) {
+        String name = Joiner.on(NAME_SEPARATOR).skipNulls().join(
+                Strings.emptyToNull(suiteName),
+                Strings.emptyToNull(caseName),
+                Strings.emptyToNull(testName));
+        return Joiner.on(ID_SEPARATOR).skipNulls().join(
+                Strings.emptyToNull(name),
+                Strings.emptyToNull(abi));
+    }
+
+
+    private static String generateModuleResultSignature(IModuleResult module,
+            String buildFingerprint) {
+        StringBuilder sb = new StringBuilder();
+        sb.append(buildFingerprint).append(SEPARATOR)
+                .append(module.getId()).append(SEPARATOR)
+                .append(module.isDone()).append(SEPARATOR)
+                .append(module.getNotExecuted()).append(SEPARATOR)
+                .append(module.countResults(TestStatus.FAIL));
+        return sb.toString();
+    }
+
+    private static String generateModuleSummarySignature(IModuleResult module,
+            String buildFingerprint) {
+        StringBuilder sb = new StringBuilder();
+        sb.append(buildFingerprint).append(SEPARATOR)
+                .append(module.getId()).append(SEPARATOR)
+                .append(module.countResults(TestStatus.FAIL));
+        return sb.toString();
+    }
+
+    static byte[] calculateFileChecksum(File file) throws ChecksumValidationException {
+
+        try (FileInputStream fis = new FileInputStream(file);
+             InputStream inputStream = new BufferedInputStream(fis)) {
+            MessageDigest hashSum = MessageDigest.getInstance("SHA-256");
+            int cnt;
+            int bufferSize = 8192;
+            byte [] buffer = new byte[bufferSize];
+            while ((cnt = inputStream.read(buffer)) != -1) {
+                hashSum.update(buffer, 0, cnt);
+            }
+
+            byte[] partialHash = new byte[32];
+            hashSum.digest(partialHash, 0, 32);
+            return partialHash;
+        } catch (NoSuchAlgorithmException e) {
+            throw new ChecksumValidationException("Unable to hash file.", e);
+        } catch (IOException e) {
+            throw new ChecksumValidationException("Unable to hash file.", e);
+        } catch (DigestException e) {
+            throw new ChecksumValidationException("Unable to hash file.", e);
+        }
+    }
+
+
+    private static int countTestResults(IInvocationResult invocation) {
+        int count = 0;
+        for (IModuleResult module : invocation.getModules()) {
+            // Two entries per module (result & summary)
+            count += 2;
+            for (ICaseResult caseResult : module.getResults()) {
+                count += caseResult.getResults().size();
+            }
+        }
+        return count;
+    }
+
+    public static class ChecksumValidationException extends Exception {
+        public ChecksumValidationException(String detailMessage) {
+            super(detailMessage);
+        }
+
+        public ChecksumValidationException(String detailMessage, Throwable throwable) {
+            super(detailMessage, throwable);
+        }
+    }
+}
diff --git a/common/util/src/com/android/compatibility/common/util/ResultHandler.java b/common/host-side/util/src/com/android/compatibility/common/util/ResultHandler.java
similarity index 79%
rename from common/util/src/com/android/compatibility/common/util/ResultHandler.java
rename to common/host-side/util/src/com/android/compatibility/common/util/ResultHandler.java
index 435f515..d04ed8e 100644
--- a/common/util/src/com/android/compatibility/common/util/ResultHandler.java
+++ b/common/host-side/util/src/com/android/compatibility/common/util/ResultHandler.java
@@ -15,6 +15,10 @@
  */
 package com.android.compatibility.common.util;
 
+import com.android.compatibility.common.util.ChecksumReporter.ChecksumValidationException;
+
+import com.google.common.base.Strings;
+
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
 import org.xmlpull.v1.XmlPullParserFactory;
@@ -28,6 +32,9 @@
 import java.io.OutputStream;
 import java.net.InetAddress;
 import java.net.UnknownHostException;
+import java.nio.file.FileSystems;
+import java.nio.file.Files;
+import java.nio.file.Path;
 import java.text.SimpleDateFormat;
 import java.util.ArrayList;
 import java.util.Comparator;
@@ -98,12 +105,17 @@
      * @param resultsDir
      */
     public static List<IInvocationResult> getResults(File resultsDir) {
+        return getResults(resultsDir, false);
+    }
+
+    /**
+     * @param resultsDir
+     * @param useChecksum
+     */
+    public static List<IInvocationResult> getResults(
+            File resultsDir, Boolean useChecksum) {
         List<IInvocationResult> results = new ArrayList<>();
-        File[] files = resultsDir.listFiles();
-        if (files == null || files.length == 0) {
-            // No results, just return the empty list
-            return results;
-        }
+        List<File> files = getResultDirectories(resultsDir);;
         for (File resultDir : files) {
             if (!resultDir.isDirectory()) {
                 continue;
@@ -113,7 +125,20 @@
                 if (!resultFile.exists()) {
                     continue;
                 }
+                Boolean invocationUseChecksum = useChecksum;
                 IInvocationResult invocation = new InvocationResult();
+                invocation.setRetryDirectory(resultDir);
+                ChecksumReporter checksumReporter = null;
+                if (invocationUseChecksum) {
+                    try {
+                        checksumReporter = ChecksumReporter.load(resultDir);
+                        invocation.setRetryChecksumStatus(RetryChecksumStatus.RetryWithChecksum);
+                    } catch (ChecksumValidationException e) {
+                        // Unable to read checksum form previous execution
+                        invocation.setRetryChecksumStatus(RetryChecksumStatus.RetryWithoutChecksum);
+                        invocationUseChecksum = false;
+                    }
+                }
                 XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
                 XmlPullParser parser = factory.newPullParser();
                 parser.setInput(new FileReader(resultFile));
@@ -155,7 +180,7 @@
                     int notExecuted =
                             Integer.parseInt(parser.getAttributeValue(NS, NOT_EXECUTED_ATTR));
                     module.setNotExecuted(notExecuted);
-                    int runtime = Integer.parseInt(parser.getAttributeValue(NS, RUNTIME_ATTR));
+                    long runtime = Long.parseLong(parser.getAttributeValue(NS, RUNTIME_ATTR));
                     module.addRuntime(runtime);
                     while (parser.nextTag() == XmlPullParser.START_TAG) {
                         parser.require(XmlPullParser.START_TAG, NS, CASE_TAG);
@@ -194,10 +219,22 @@
                                 }
                             }
                             parser.require(XmlPullParser.END_TAG, NS, TEST_TAG);
+                            Boolean checksumMismatch = invocationUseChecksum
+                                    && !checksumReporter.containsTestResult(
+                                    test, module, invocation.getBuildFingerprint());
+                            if (checksumMismatch) {
+                                test.removeResult();
+                            }
                         }
                         parser.require(XmlPullParser.END_TAG, NS, CASE_TAG);
                     }
                     parser.require(XmlPullParser.END_TAG, NS, MODULE_TAG);
+                    Boolean checksumMismatch = invocationUseChecksum
+                            && !checksumReporter.containsModuleResult(
+                            module, invocation.getBuildFingerprint());
+                    if (checksumMismatch) {
+                        module.setDone(false);
+                    }
                 }
                 parser.require(XmlPullParser.END_TAG, NS, RESULT_TAG);
                 results.add(invocation);
@@ -296,6 +333,10 @@
         serializer.startTag(NS, BUILD_TAG);
         for (Entry<String, String> entry : result.getInvocationInfo().entrySet()) {
             serializer.attribute(NS, entry.getKey(), entry.getValue());
+            if (Strings.isNullOrEmpty(result.getBuildFingerprint()) &&
+                    entry.getKey().equals(BUILD_FINGERPRINT)) {
+                result.setBuildFingerprint(entry.getValue());
+            }
         }
         serializer.endTag(NS, BUILD_TAG);
 
@@ -370,20 +411,59 @@
             serializer.endTag(NS, MODULE_TAG);
         }
         serializer.endDocument();
+        createChecksum(resultDir, result);
         return resultFile;
     }
 
+    private static void createChecksum(File resultDir, IInvocationResult invocationResult) {
+        RetryChecksumStatus retryStatus = invocationResult.getRetryChecksumStatus();
+        switch (retryStatus) {
+            case NotRetry: case RetryWithChecksum:
+                // Do not disrupt the process if there is a problem generating checksum.
+                ChecksumReporter.tryCreateChecksum(resultDir, invocationResult);
+                break;
+            case RetryWithoutChecksum:
+                // If the previous run has an invalid checksum file,
+                // copy it into current results folder for future troubleshooting
+                File retryDirectory = invocationResult.getRetryDirectory();
+                Path retryChecksum = FileSystems.getDefault().getPath(
+                        retryDirectory.getAbsolutePath(), ChecksumReporter.NAME);
+                if (!retryChecksum.toFile().exists()) {
+                    // if no checksum file, check for a copy from a previous retry
+                    retryChecksum = FileSystems.getDefault().getPath(
+                            retryDirectory.getAbsolutePath(), ChecksumReporter.PREV_NAME);
+                }
+
+                if (retryChecksum.toFile().exists()) {
+                    File checksumCopy = new File(resultDir, ChecksumReporter.PREV_NAME);
+                    try (FileOutputStream stream = new FileOutputStream(checksumCopy)) {
+                        Files.copy(retryChecksum, stream);
+                    } catch (IOException e) {
+                        // Do not disrupt the process if there is a problem copying checksum
+                    }
+                }
+        }
+    }
+
     /**
      * Find the IInvocationResult for the given sessionId.
      */
     public static IInvocationResult findResult(File resultsDir, Integer sessionId)
             throws FileNotFoundException {
+        return findResult(resultsDir, sessionId, true);
+    }
+
+    /**
+     * Find the IInvocationResult for the given sessionId.
+     */
+    private static IInvocationResult findResult(
+            File resultsDir, Integer sessionId, Boolean useChecksum) throws FileNotFoundException {
         if (sessionId < 0) {
             throw new IllegalArgumentException(
                 String.format("Invalid session id [%d] ", sessionId));
         }
 
-        List<IInvocationResult> results = getResults(resultsDir);
+        List<IInvocationResult> results = getResults(resultsDir, useChecksum);
         if (results == null || sessionId >= results.size()) {
             throw new RuntimeException(String.format("Could not find session [%d]", sessionId));
         }
@@ -391,6 +471,33 @@
     }
 
     /**
+     * Get a list of child directories that contain test invocation results
+     * @param resultsDir the root test result directory
+     * @return
+     */
+    public static List<File> getResultDirectories(File resultsDir) {
+        List<File> directoryList = new ArrayList<>();
+        File[] files = resultsDir.listFiles();
+        if (files == null || files.length == 0) {
+            // No results, just return the empty list
+            return directoryList;
+        }
+        for (File resultDir : files) {
+            if (!resultDir.isDirectory()) {
+                continue;
+            }
+            // Only include if it contain results file
+            File resultFile = new File(resultDir, TEST_RESULT_FILE_NAME);
+            if (!resultFile.exists()) {
+                continue;
+            }
+            directoryList.add(resultDir);
+        }
+        Collections.sort(directoryList, (d1, d2) -> d1.getName().compareTo(d2.getName()));
+        return directoryList;
+    }
+
+    /**
      * Return the given time as a {@link String} suitable for displaying.
      * <p/>
      * Example: Fri Aug 20 15:13:03 PDT 2010
diff --git a/common/host-side/util/tests/Android.mk b/common/host-side/util/tests/Android.mk
index a0e9a3b..4a78835 100644
--- a/common/host-side/util/tests/Android.mk
+++ b/common/host-side/util/tests/Android.mk
@@ -18,7 +18,7 @@
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
-LOCAL_JAVA_LIBRARIES := compatibility-host-util junit json-prebuilt
+LOCAL_JAVA_LIBRARIES := compatibility-host-util junit json-prebuilt tradefed-prebuilt
 
 LOCAL_MODULE := compatibility-host-util-tests
 
diff --git a/common/host-side/util/tests/src/com/android/compatibility/common/util/HostUnitTests.java b/common/host-side/util/tests/src/com/android/compatibility/common/util/HostUnitTests.java
index 4b47bf3..169cfdb 100644
--- a/common/host-side/util/tests/src/com/android/compatibility/common/util/HostUnitTests.java
+++ b/common/host-side/util/tests/src/com/android/compatibility/common/util/HostUnitTests.java
@@ -28,6 +28,7 @@
     public HostUnitTests() {
         super();
         addTestSuite(DynamicConfigHandlerTest.class);
+        addTestSuite(ResultHandlerTest.class);
     }
 
     public static Test suite() {
diff --git a/common/util/tests/src/com/android/compatibility/common/util/ResultHandlerTest.java b/common/host-side/util/tests/src/com/android/compatibility/common/util/ResultHandlerTest.java
similarity index 100%
rename from common/util/tests/src/com/android/compatibility/common/util/ResultHandlerTest.java
rename to common/host-side/util/tests/src/com/android/compatibility/common/util/ResultHandlerTest.java
diff --git a/common/util/src/com/android/compatibility/common/util/IInvocationResult.java b/common/util/src/com/android/compatibility/common/util/IInvocationResult.java
index 739dd48..2b75371 100644
--- a/common/util/src/com/android/compatibility/common/util/IInvocationResult.java
+++ b/common/util/src/com/android/compatibility/common/util/IInvocationResult.java
@@ -114,4 +114,24 @@
      * Return the number of completed test modules for this invocation.
      */
     int getModuleCompleteCount();
+
+    /**
+     * Return status of checksum from previous session
+     */
+    RetryChecksumStatus getRetryChecksumStatus();
+
+    /**
+     * Set status of checksum from previous session
+     */
+    void setRetryChecksumStatus(RetryChecksumStatus retryStatus);
+
+    /**
+     * Return the directory of the previous sessions results
+     */
+    File getRetryDirectory();
+
+    /**
+     * Set the directory of the previous sessions results
+     */
+    void setRetryDirectory(File resultDir);
 }
diff --git a/common/util/src/com/android/compatibility/common/util/ITestResult.java b/common/util/src/com/android/compatibility/common/util/ITestResult.java
index c35b997..24ee1aa 100644
--- a/common/util/src/com/android/compatibility/common/util/ITestResult.java
+++ b/common/util/src/com/android/compatibility/common/util/ITestResult.java
@@ -142,4 +142,8 @@
      */
     boolean isRetry();
 
+    /**
+     * Clear the existing result and default to 'failed'
+     */
+    void removeResult();
 }
diff --git a/common/util/src/com/android/compatibility/common/util/InvocationResult.java b/common/util/src/com/android/compatibility/common/util/InvocationResult.java
index f74c61d..83f1dac 100644
--- a/common/util/src/com/android/compatibility/common/util/InvocationResult.java
+++ b/common/util/src/com/android/compatibility/common/util/InvocationResult.java
@@ -15,6 +15,7 @@
  */
 package com.android.compatibility.common.util;
 
+import java.io.File;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashMap;
@@ -37,7 +38,8 @@
     private String mTestPlan;
     private String mCommandLineArgs;
     private int mNotExecuted = 0;
-
+    private RetryChecksumStatus mRetryChecksumStatus = RetryChecksumStatus.NotRetry;
+    private File mRetryDirectory = null;
     /**
      * {@inheritDoc}
      */
@@ -201,4 +203,36 @@
         }
         return completeModules;
     }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public RetryChecksumStatus getRetryChecksumStatus() {
+        return mRetryChecksumStatus;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void setRetryChecksumStatus(RetryChecksumStatus retryStatus) {
+        mRetryChecksumStatus = retryStatus;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public File getRetryDirectory() {
+        return mRetryDirectory;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void setRetryDirectory(File resultDir) {
+        mRetryDirectory = resultDir;
+    }
 }
diff --git a/common/util/src/com/android/compatibility/common/util/RetryChecksumStatus.java b/common/util/src/com/android/compatibility/common/util/RetryChecksumStatus.java
new file mode 100644
index 0000000..a86ab37
--- /dev/null
+++ b/common/util/src/com/android/compatibility/common/util/RetryChecksumStatus.java
@@ -0,0 +1,8 @@
+package com.android.compatibility.common.util;
+
+
+public enum RetryChecksumStatus {
+    NotRetry,
+    RetryWithChecksum,
+    RetryWithoutChecksum
+}
diff --git a/common/util/src/com/android/compatibility/common/util/TestResult.java b/common/util/src/com/android/compatibility/common/util/TestResult.java
index ba378d0..36b6e5a 100644
--- a/common/util/src/com/android/compatibility/common/util/TestResult.java
+++ b/common/util/src/com/android/compatibility/common/util/TestResult.java
@@ -242,6 +242,15 @@
      * {@inheritDoc}
      */
     @Override
+    public void removeResult() {
+        setResultStatus(TestStatus.FAIL);
+        setStackTrace("");
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
     public int compareTo(ITestResult another) {
         return getName().compareTo(another.getName());
     }
diff --git a/common/util/tests/src/com/android/compatibility/common/util/UnitTests.java b/common/util/tests/src/com/android/compatibility/common/util/UnitTests.java
index 967b8a8..e6c6a87 100644
--- a/common/util/tests/src/com/android/compatibility/common/util/UnitTests.java
+++ b/common/util/tests/src/com/android/compatibility/common/util/UnitTests.java
@@ -37,7 +37,6 @@
         addTestSuite(StatTest.class);
         addTestSuite(TestFilterTest.class);
         addTestSuite(TestResultTest.class);
-        addTestSuite(ResultHandlerTest.class);
     }
 
     public static Test suite() {
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/AdoptableHostTest.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/AdoptableHostTest.java
index 1032c62..b77eef2 100644
--- a/hostsidetests/appsecurity/src/android/appsecurity/cts/AdoptableHostTest.java
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/AdoptableHostTest.java
@@ -293,8 +293,18 @@
     }
 
     private String getAdoptionDisk() throws Exception {
-        final String disks = getDevice().executeShellCommand("sm list-disks adoptable");
-        if (disks == null || disks.length() == 0) {
+        // In the case where we run multiple test we cleanup the state of the device. This
+        // results in the execution of sm forget all which causes the MountService to "reset"
+        // all its knowledge about available drives. This can cause the adoptable drive to
+        // become temporarily unavailable.
+        int attempt = 0;
+        String disks = getDevice().executeShellCommand("sm list-disks adoptable");
+        while ((disks == null || disks.isEmpty()) && attempt++ < 15) {
+            Thread.sleep(1000);
+            disks = getDevice().executeShellCommand("sm list-disks adoptable");
+        }
+
+        if (disks == null || disks.isEmpty()) {
             throw new AssertionError("Devices that claim to support adoptable storage must have "
                     + "adoptable media inserted during CTS to verify correct behavior");
         }
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/DirectBootHostTest.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/DirectBootHostTest.java
index fd1bc4d..a4686aa 100644
--- a/hostsidetests/appsecurity/src/android/appsecurity/cts/DirectBootHostTest.java
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/DirectBootHostTest.java
@@ -129,6 +129,7 @@
 
     public void doDirectBootTest(String mode) throws Exception {
         int[] users = {};
+        boolean doTest = true;
         try {
             users = createUsersForTest();
 
@@ -150,17 +151,22 @@
 
             // Reboot system into known state with keys ejected
             if (MODE_EMULATED.equals(mode)) {
-                getDevice().executeShellCommand("sm set-emulate-fbe true");
+                final String res = getDevice().executeShellCommand("sm set-emulate-fbe true");
+                if (res != null && res.contains("Emulation not supported")) {
+                    doTest = false;
+                }
                 getDevice().waitForDeviceOnline();
             } else {
                 getDevice().rebootUntilOnline();
             }
             waitForBootCompleted();
 
-            if (MODE_NONE.equals(mode)) {
-                runDeviceTests(PKG, CLASS, "testVerifyUnlockedAndDismiss", users);
-            } else {
-                runDeviceTests(PKG, CLASS, "testVerifyLockedAndDismiss", users);
+            if (doTest) {
+                if (MODE_NONE.equals(mode)) {
+                    runDeviceTests(PKG, CLASS, "testVerifyUnlockedAndDismiss", users);
+                } else {
+                    runDeviceTests(PKG, CLASS, "testVerifyLockedAndDismiss", users);
+                }
             }
 
         } finally {
diff --git a/hostsidetests/appsecurity/test-apps/UsePermissionApp23/src/com/android/cts/usepermission/BasePermissionsTest.java b/hostsidetests/appsecurity/test-apps/UsePermissionApp23/src/com/android/cts/usepermission/BasePermissionsTest.java
index 250a198..d0453fb 100644
--- a/hostsidetests/appsecurity/test-apps/UsePermissionApp23/src/com/android/cts/usepermission/BasePermissionsTest.java
+++ b/hostsidetests/appsecurity/test-apps/UsePermissionApp23/src/com/android/cts/usepermission/BasePermissionsTest.java
@@ -262,7 +262,7 @@
         Assert.assertNotNull("Permissions label should be present", permLabelView);
 
         AccessibilityNodeInfo permItemView = findCollectionItem(permLabelView);
-        Assert.assertNotNull("Permissions item should be present", permLabelView);
+        Assert.assertNotNull("Permissions item should be present", permItemView);
 
         click(permItemView);
 
diff --git a/hostsidetests/devicepolicy/app/DeviceAdmin/src/com.android.cts.deviceadmin/ClearDeviceAdminWithNoProtectionTest.java b/hostsidetests/devicepolicy/app/DeviceAdmin/src/com.android.cts.deviceadmin/ClearDeviceAdminWithNoProtectionTest.java
index 3bfa905..7dd303f 100644
--- a/hostsidetests/devicepolicy/app/DeviceAdmin/src/com.android.cts.deviceadmin/ClearDeviceAdminWithNoProtectionTest.java
+++ b/hostsidetests/devicepolicy/app/DeviceAdmin/src/com.android.cts.deviceadmin/ClearDeviceAdminWithNoProtectionTest.java
@@ -33,7 +33,7 @@
 
         if (dpm.isAdminActive(cn)) {
             dpm.removeActiveAdmin(cn);
-            for (int i = 0; i < 1000 && dpm.isAdminActive(cn); i++) {
+            for (int i = 0; i < 6000 && dpm.isAdminActive(cn); i++) {
                 Thread.sleep(10);
             }
         }
diff --git a/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/SecurityLoggingTest.java b/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/SecurityLoggingTest.java
index 8e42279..71aa205 100644
--- a/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/SecurityLoggingTest.java
+++ b/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/SecurityLoggingTest.java
@@ -16,9 +16,12 @@
 package com.android.cts.deviceowner;
 
 import android.app.admin.SecurityLog.SecurityEvent;
-import android.os.UserHandle;
+import android.os.Parcel;
+import android.os.SystemClock;
 
+import java.util.Arrays;
 import java.util.List;
+import java.util.concurrent.TimeUnit;
 
 public class SecurityLoggingTest extends BaseDeviceOwnerTest {
 
@@ -62,11 +65,72 @@
     }
 
     /**
-     * Test: Test enabling and disabling of security logging.
+     * Test: retrieving security logs. This test has should be called when security logging is
+     * enabled and directly after reboot with device owner installed so that security logging
+     * actually takes place and fetching the logs isn't subject to rate limiting.
      */
-    public void testEnablingAndDisablingSecurityLogging() {
+    public void testGetSecurityLogs() {
+        List<SecurityEvent> events = mDevicePolicyManager.retrieveSecurityLogs(getWho());
+
+        // There must be at least some events, e.g. PackageManager logs all process launches.
+        assertTrue("Unable to get events", events != null && events.size() > 0);
+
+        // We don't know much about the events, so just call public API methods and do a simple
+        // sanity check of timestamps.
+
+        // Check that timestamps are between system start and current time.
+        long systemStartedNanos = TimeUnit.MILLISECONDS.toNanos(System.currentTimeMillis())
+                - SystemClock.elapsedRealtimeNanos();
+        long nowNanos = TimeUnit.MILLISECONDS.toNanos(System.currentTimeMillis() + 1);
+
+        for (int i = 0; i < events.size(); i++) {
+            SecurityEvent event = events.get(i);
+            long currentTimestampNanos = event.getTimeNanos();
+            assertTrue("Logged event predates boot", currentTimestampNanos >= systemStartedNanos);
+            assertTrue("Last logged event is in future", currentTimestampNanos <= nowNanos);
+
+            // Test parcelling: flatten to a parcel.
+            Parcel p = Parcel.obtain();
+            event.writeToParcel(p, 0);
+            p.setDataPosition(0);
+
+            // Restore from parcel and check contents.
+            SecurityEvent restored = SecurityEvent.CREATOR.createFromParcel(p);
+            p.recycle();
+
+            // For some events data is encapsulated into Object array.
+            if (event.getData() instanceof Object[]) {
+                assertTrue("Parcelling changed the array returned by getData",
+                        Arrays.equals((Object[]) event.getData(), (Object[]) restored.getData()));
+            } else {
+                assertEquals("Parcelling changed the result of getData",
+                        event.getData(), restored.getData());
+            }
+            assertEquals("Parcelling changed the result of getTag",
+                    event.getTag(), restored.getTag());
+            assertEquals("Parcelling changed the result of getTimeNanos",
+                    event.getTimeNanos(), restored.getTimeNanos());
+            assertEquals("Parcelling changed the result of describeContents",
+                    event.describeContents(), restored.describeContents());
+        }
+    }
+
+    /**
+     * Test: Test enabling security logging. This test should be executed after installing a device
+     * owner so that we check that logging is not enabled by default. This test has a side effect:
+     * security logging is enabled after its execution.
+     */
+    public void testEnablingSecurityLogging() {
+        assertFalse(mDevicePolicyManager.isSecurityLoggingEnabled(getWho()));
         mDevicePolicyManager.setSecurityLoggingEnabled(getWho(), true);
         assertTrue(mDevicePolicyManager.isSecurityLoggingEnabled(getWho()));
+    }
+
+    /**
+     * Test: Test disabling security logging. This test has a side effect: security logging is
+     * disabled after its execution.
+     */
+    public void testDisablingSecurityLogging() {
         mDevicePolicyManager.setSecurityLoggingEnabled(getWho(), false);
         assertFalse(mDevicePolicyManager.isSecurityLoggingEnabled(getWho()));
     }
diff --git a/hostsidetests/devicepolicy/app/ManagedProfile/AndroidManifest.xml b/hostsidetests/devicepolicy/app/ManagedProfile/AndroidManifest.xml
index a2c7219..2a992a8 100644
--- a/hostsidetests/devicepolicy/app/ManagedProfile/AndroidManifest.xml
+++ b/hostsidetests/devicepolicy/app/ManagedProfile/AndroidManifest.xml
@@ -19,6 +19,7 @@
 
     <uses-sdk android:minSdkVersion="20"/>
     <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
+    <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
     <uses-permission android:name="android.permission.AUTHENTICATE_ACCOUNTS" />
     <uses-permission android:name="android.permission.BLUETOOTH" />
     <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
diff --git a/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/WifiTest.java b/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/WifiTest.java
index 685ad82..124ebe8 100644
--- a/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/WifiTest.java
+++ b/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/WifiTest.java
@@ -23,6 +23,7 @@
 import android.os.SystemClock;
 import android.test.AndroidTestCase;
 
+import java.util.List;
 import java.util.concurrent.TimeUnit;
 
 import static com.android.compatibility.common.util.WifiConfigCreator.ACTION_CREATE_WIFI_CONFIG;
@@ -50,10 +51,27 @@
     // Shared WifiManager instance.
     private WifiManager mWifiManager;
 
+    // Original setting of WifiManager.isWifiEnabled() before setup.
+    private boolean mWifiEnabled;
+
     @Override
     public void setUp() throws Exception {
         super.setUp();
         mWifiManager = (WifiManager) getContext().getSystemService(Context.WIFI_SERVICE);
+        mWifiEnabled = mWifiManager.isWifiEnabled();
+        if (!mWifiEnabled) {
+            mWifiManager.setWifiEnabled(true);
+            awaitWifiEnabledState(true);
+        }
+    }
+
+    @Override
+    public void tearDown() throws Exception {
+        if (!mWifiEnabled) {
+            mWifiManager.setWifiEnabled(false);
+            awaitWifiEnabledState(false);
+        }
+        super.tearDown();
     }
 
     /**
@@ -92,7 +110,6 @@
             intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
             getContext().startActivity(intent);
         }
-
         assertTrue(awaitNetworkState(NETWORK_SSID, /* exists */ false));
     }
 
@@ -123,6 +140,23 @@
     }
 
     /**
+     * Block until {@link WifiManager#isWifiEnabled()} returns {@param enabled}. Wait for up to
+     * {@link WifiTest#UPDATE_TIMEOUT_MS} milliseconds, in increments of
+     * {@link WifiTest#UPDATE_INTERVAL_MS}.
+     */
+    private void awaitWifiEnabledState(boolean enabled) throws RuntimeException {
+        for (int probes = 0; probes * UPDATE_INTERVAL_MS <= UPDATE_TIMEOUT_MS; probes++) {
+            if (probes != 0) {
+                SystemClock.sleep(UPDATE_INTERVAL_MS);
+            }
+            if (mWifiManager.isWifiEnabled() == enabled) {
+                return;
+            }
+        }
+        throw new RuntimeException("Waited too long for wifi enabled state = " + enabled);
+    }
+
+    /**
      * Internal method to find an existing {@link WifiConfiguration} with the given SSID.
      *
      * @return A {@link WifiConfiguration} matching the specification, or {@code null} if no such
@@ -132,9 +166,12 @@
         if (!ssid.startsWith("\"")) {
             ssid = '"' + ssid + '"';
         }
-        for (WifiConfiguration config : mWifiManager.getConfiguredNetworks()) {
-            if (ssid.equals(config.SSID)) {
-                return config;
+        final List<WifiConfiguration> configs = mWifiManager.getConfiguredNetworks();
+        if (configs != null) {
+            for (WifiConfiguration config : configs) {
+                if (ssid.equals(config.SSID)) {
+                    return config;
+                }
             }
         }
         return null;
diff --git a/hostsidetests/devicepolicy/app/PackageInstaller/src/com/android/cts/packageinstaller/ManualPackageInstallTest.java b/hostsidetests/devicepolicy/app/PackageInstaller/src/com/android/cts/packageinstaller/ManualPackageInstallTest.java
index 1bb797f..98a47f2 100644
--- a/hostsidetests/devicepolicy/app/PackageInstaller/src/com/android/cts/packageinstaller/ManualPackageInstallTest.java
+++ b/hostsidetests/devicepolicy/app/PackageInstaller/src/com/android/cts/packageinstaller/ManualPackageInstallTest.java
@@ -40,8 +40,7 @@
             .pkg("com.android.settings");
     private static final BySelector INSTALL_BUTTON_SELECTOR = By
             .clazz(android.widget.Button.class.getName())
-            .res("com.android.packageinstaller:id/ok_button")
-            .pkg("com.google.android.packageinstaller");
+            .res("com.android.packageinstaller:id/ok_button");
 
     public void testManualInstallSucceeded() throws Exception {
         assertInstallPackage();
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/BaseDevicePolicyTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/BaseDevicePolicyTest.java
index 76e4c73..d5aec5c 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/BaseDevicePolicyTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/BaseDevicePolicyTest.java
@@ -248,6 +248,12 @@
         return !runResult.hasFailedTests() && runResult.getNumTestsInState(TestStatus.PASSED) > 0;
     }
 
+    /** Reboots the device and block until the boot complete flag is set. */
+    protected void rebootAndWaitUntilReady() throws DeviceNotAvailableException {
+        getDevice().rebootUntilOnline();
+        assertTrue("Device failed to boot", getDevice().waitForBootComplete(60000));
+    }
+
     /** Returns true if the system supports the split between system and primary user. */
     protected boolean hasUserSplit() throws DeviceNotAvailableException {
         return getBooleanSystemProperty("ro.fw.system_user_split", false);
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAndProfileOwnerTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAndProfileOwnerTest.java
index 70f5218..9cf6fc8 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAndProfileOwnerTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAndProfileOwnerTest.java
@@ -474,7 +474,7 @@
             String command = "rm " + TEST_APP_LOCATION + apk.getName();
             getDevice().executeShellCommand(command);
             getDevice().uninstallPackage(TEST_APP_PKG);
-            getDevice().uninstallPackage(PACKAGE_INSTALLER_APK);
+            getDevice().uninstallPackage(PACKAGE_INSTALLER_PKG);
             if (unknownSourceSetting != null) {
                 putSettings(SECURE_SETTING_CATEGORY, UNKNOWN_SOURCES_SETTING, unknownSourceSetting,
                         mUserId);
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerTest.java
index 4748b35..c373f3f 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerTest.java
@@ -17,6 +17,7 @@
 package com.android.cts.devicepolicy;
 
 import java.util.ArrayList;
+import java.util.concurrent.TimeUnit;
 
 /**
  * Set of tests for Device Owner use cases.
@@ -62,6 +63,7 @@
                     DEVICE_OWNER_PKG + "/" + ADMIN_RECEIVER_TEST_CLASS, mPrimaryUserId,
                     /*expectFailure*/ false)) {
                 removeAdmin(DEVICE_OWNER_PKG + "/" + ADMIN_RECEIVER_TEST_CLASS, mPrimaryUserId);
+                getDevice().uninstallPackage(DEVICE_OWNER_PKG);
                 fail("Failed to set device owner");
             }
         }
@@ -101,7 +103,7 @@
     }
 
     public void testWifi() throws Exception {
-        if (hasDeviceFeature("android.hardware.wifi")) {
+        if (!hasDeviceFeature("android.hardware.wifi")) {
             return;
         }
         executeDeviceOwnerTest("WifiTest");
@@ -310,7 +312,18 @@
         }
         executeDeviceTestMethod(".SecurityLoggingTest",
                 "testRetrievingSecurityLogsNotPossibleImmediatelyAfterPreviousSuccessfulRetrieval");
-        executeDeviceTestMethod(".SecurityLoggingTest", "testEnablingAndDisablingSecurityLogging");
+        try {
+            executeDeviceTestMethod(".SecurityLoggingTest", "testEnablingSecurityLogging");
+            rebootAndWaitUntilReady();
+            // Sleep for ~1 minute so that SecurityLogMonitor fetches the security events. This is
+            // an implementation detail we shouldn't rely on but there is no other way to do it
+            // currently.
+            Thread.sleep(TimeUnit.SECONDS.toMillis(70));
+            executeDeviceTestMethod(".SecurityLoggingTest", "testGetSecurityLogs");
+        } finally {
+            // Always attempt to disable security logging to bring the device to initial state.
+            executeDeviceTestMethod(".SecurityLoggingTest", "testDisablingSecurityLogging");
+        }
     }
 
     public void testLockTask() throws Exception {
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedDeviceOwnerTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedDeviceOwnerTest.java
index 42ff41a..a164428 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedDeviceOwnerTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedDeviceOwnerTest.java
@@ -36,9 +36,13 @@
             mUserId = mPrimaryUserId;
 
             installAppAsUser(DEVICE_ADMIN_APK, mUserId);
-            assertTrue("Failed to set device owner", setDeviceOwner(
+            if (!setDeviceOwner(
                     DEVICE_ADMIN_PKG + "/" + ADMIN_RECEIVER_TEST_CLASS, mUserId,
-                    /*expectFailure*/ false));
+                    /*expectFailure*/ false)) {
+                removeAdmin(DEVICE_ADMIN_PKG + "/" + ADMIN_RECEIVER_TEST_CLASS, mUserId);
+                getDevice().uninstallPackage(DEVICE_ADMIN_PKG);
+                fail("Failed to set device owner");
+            }
         }
     }
 
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedProfileOwnerTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedProfileOwnerTest.java
index f65a245..e1d50bd 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedProfileOwnerTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedProfileOwnerTest.java
@@ -30,7 +30,13 @@
             mUserId = mPrimaryUserId;
 
             installAppAsUser(DEVICE_ADMIN_APK, mUserId);
-            setProfileOwnerOrFail(DEVICE_ADMIN_PKG + "/" + ADMIN_RECEIVER_TEST_CLASS, mUserId);
+            if (!setProfileOwner(
+                    DEVICE_ADMIN_PKG + "/" + ADMIN_RECEIVER_TEST_CLASS, mUserId,
+                    /*expectFailure*/ false)) {
+                removeAdmin(DEVICE_ADMIN_PKG + "/" + ADMIN_RECEIVER_TEST_CLASS, mUserId);
+                getDevice().uninstallPackage(DEVICE_ADMIN_PKG);
+                fail("Failed to set profile owner");
+            }
         }
     }
 
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ProfileOwnerTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ProfileOwnerTest.java
index 4105012..aedabc2 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ProfileOwnerTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ProfileOwnerTest.java
@@ -36,9 +36,13 @@
 
         if (mHasFeature) {
             installAppAsUser(PROFILE_OWNER_APK, mUserId);
-            assertTrue("Failed to set profile owner",
-                    setProfileOwner(PROFILE_OWNER_PKG + "/" + ADMIN_RECEIVER_TEST_CLASS, mUserId,
-                             /* expectFailure */ false));
+            if (!setProfileOwner(
+                    PROFILE_OWNER_PKG + "/" + ADMIN_RECEIVER_TEST_CLASS, mUserId,
+                    /* expectFailure */ false)) {
+                removeAdmin(PROFILE_OWNER_PKG + "/" + ADMIN_RECEIVER_TEST_CLASS, mUserId);
+                getDevice().uninstallPackage(PROFILE_OWNER_PKG);
+                fail("Failed to set profile owner");
+            }
         }
     }
 
@@ -53,13 +57,6 @@
         super.tearDown();
     }
 
-    public void testWifi() throws Exception {
-        if (hasDeviceFeature("android.hardware.wifi")) {
-            return;
-        }
-        executeProfileOwnerTest("WifiTest");
-    }
-
     private void executeProfileOwnerTest(String testClassName) throws Exception {
         if (!mHasFeature) {
             return;
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ProfileOwnerTestApi23.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ProfileOwnerTestApi23.java
index 4c1477e..33aa1d2 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ProfileOwnerTestApi23.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ProfileOwnerTestApi23.java
@@ -34,7 +34,13 @@
             mUserId = USER_OWNER;
 
             installAppAsUser(DEVICE_ADMIN_APK, mUserId);
-            setProfileOwnerOrFail(DEVICE_ADMIN_PKG + "/" + ADMIN_RECEIVER_TEST_CLASS, mUserId);
+            if (!setProfileOwner(
+                    DEVICE_ADMIN_PKG + "/" + ADMIN_RECEIVER_TEST_CLASS, mUserId,
+                    /*expectFailure*/ false)) {
+                removeAdmin(DEVICE_ADMIN_PKG + "/" + ADMIN_RECEIVER_TEST_CLASS, mUserId);
+                getDevice().uninstallPackage(DEVICE_ADMIN_PKG);
+                fail("Failed to set profile owner");
+            }
         }
     }
 
@@ -43,6 +49,7 @@
         if (mHasFeature) {
             assertTrue("Failed to remove profile owner.",
                     removeAdmin(DEVICE_ADMIN_PKG + "/" + ADMIN_RECEIVER_TEST_CLASS, mUserId));
+            getDevice().uninstallPackage(DEVICE_ADMIN_PKG);
         }
         super.tearDown();
     }
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/UserRestrictionsTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/UserRestrictionsTest.java
index 41e7864..bc61a81 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/UserRestrictionsTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/UserRestrictionsTest.java
@@ -44,13 +44,15 @@
                 assertTrue("Failed to clear owner",
                         removeAdmin(DEVICE_ADMIN_PKG + "/" + ADMIN_RECEIVER_TEST_CLASS,
                                 mDeviceOwnerUserId));
+                assertTrue("Some user restrictions are still set",
+                        runTests("userrestrictions.CheckNoOwnerRestrictionsTest",
+                                mDeviceOwnerUserId));
             }
-            assertTrue("Some user restrictions are still set",
-                    runTests("userrestrictions.CheckNoOwnerRestrictionsTest", mDeviceOwnerUserId));
 
             // DO/PO might have set DISALLOW_REMOVE_USER, so it needs to be done after removing
             // them.
             removeTestUsers();
+            getDevice().uninstallPackage(DEVICE_ADMIN_PKG);
         }
         super.tearDown();
     }
diff --git a/hostsidetests/security/Android.mk b/hostsidetests/security/Android.mk
index cc04431..418ce38 100644
--- a/hostsidetests/security/Android.mk
+++ b/hostsidetests/security/Android.mk
@@ -18,6 +18,8 @@
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
+LOCAL_JAVA_RESOURCE_DIRS := res
+
 LOCAL_MODULE_TAGS := optional
 
 # tag this module as a cts test artifact
diff --git a/hostsidetests/security/res/add-debug.apk b/hostsidetests/security/res/add-debug.apk
new file mode 100644
index 0000000..dff9cb6
--- /dev/null
+++ b/hostsidetests/security/res/add-debug.apk
Binary files differ
diff --git a/hostsidetests/security/res/crash_mod.apk b/hostsidetests/security/res/crash_mod.apk
new file mode 100644
index 0000000..4991294
--- /dev/null
+++ b/hostsidetests/security/res/crash_mod.apk
Binary files differ
diff --git a/hostsidetests/security/res/poc b/hostsidetests/security/res/poc
new file mode 100644
index 0000000..8b6f012
--- /dev/null
+++ b/hostsidetests/security/res/poc
Binary files differ
diff --git a/hostsidetests/security/res/test-case b/hostsidetests/security/res/test-case
new file mode 100644
index 0000000..bac4af1
--- /dev/null
+++ b/hostsidetests/security/res/test-case
Binary files differ
diff --git a/hostsidetests/security/src/android/security/cts/AdbUtils.java b/hostsidetests/security/src/android/security/cts/AdbUtils.java
new file mode 100644
index 0000000..a3018fa
--- /dev/null
+++ b/hostsidetests/security/src/android/security/cts/AdbUtils.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.security.cts;
+
+import com.android.tradefed.device.CollectingOutputReceiver;
+import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.testtype.DeviceTestCase;
+
+import android.platform.test.annotations.RootPermissionTest;
+
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.Scanner;
+
+public class AdbUtils {
+
+    /** Runs a commandline on the specified device
+     *
+     * @param command the command to be ran
+     * @param device device for the command to be ran on
+     * @return the console output from running the command
+     */
+    public static String runCommandLine(String command, ITestDevice device) throws Exception
+    {
+        return device.executeShellCommand(command);
+    }
+
+    /**
+     * Pushes and runs a binary to the selected device
+     *
+     * @param pathToPoc a string path to poc from the /res folder
+     * @param device device to be ran on
+     * @return the console output from the binary
+     */
+    public static String runPoc(String pathToPoc, ITestDevice device) throws Exception {
+        String fullResourceName = pathToPoc;
+        File pocFile = File.createTempFile("poc", "");
+        try {
+            pocFile = extractResource(fullResourceName, pocFile);
+            device.pushFile(pocFile, "/data/local/tmp/poc");
+            device.executeShellCommand("chmod +x /data/local/tmp/poc");
+            return device.executeShellCommand("/data/local/tmp/poc");
+        } finally {
+            pocFile.delete();
+        }
+    }
+
+    /**
+     * Pushes and installs an apk to the selected device
+     *
+     * @param pathToApk a string path to apk from the /res folder
+     * @param device device to be ran on
+     * @return the output from attempting to install the apk
+     */
+    public static String installApk(String pathToApk, ITestDevice device) throws Exception {
+
+        String fullResourceName = pathToApk;
+        File apkFile = File.createTempFile("apkFile", ".apk");
+        try {
+            apkFile = extractResource(fullResourceName, apkFile);
+            return device.installPackage(apkFile, true);
+        } finally {
+            apkFile.delete();
+        }
+    }
+
+   /**
+     * Extracts the binary data from a resource and writes it to a temp file
+     */
+    private static File extractResource(String fullResourceName, File file) throws Exception {
+        try (InputStream in = AdbUtils.class.getResourceAsStream(fullResourceName);
+            OutputStream out = new BufferedOutputStream(new FileOutputStream(file))) {
+            if (in == null) {
+                throw new IllegalArgumentException("Resource not found: " + fullResourceName);
+            }
+            byte[] buf = new byte[65536];
+            int chunkSize;
+            while ((chunkSize = in.read(buf)) != -1) {
+                out.write(buf, 0, chunkSize);
+            }
+            return file;
+        }
+
+    }
+}
diff --git a/hostsidetests/security/src/android/security/cts/SecurityTestCase.java b/hostsidetests/security/src/android/security/cts/SecurityTestCase.java
new file mode 100644
index 0000000..b6599c1
--- /dev/null
+++ b/hostsidetests/security/src/android/security/cts/SecurityTestCase.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.security.cts;
+
+import com.android.tradefed.device.CollectingOutputReceiver;
+import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.testtype.DeviceTestCase;
+
+import android.platform.test.annotations.RootPermissionTest;
+
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.Scanner;
+
+public class SecurityTestCase extends DeviceTestCase {
+
+    private long kernelStartTime;
+
+    /**
+     * Waits for device to be online, marks the most recent boottime of the device
+     */
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+
+        kernelStartTime = System.currentTimeMillis()/1000 -
+            Integer.parseInt(getDevice().executeShellCommand("cut -f1 -d. /proc/uptime").trim());
+        //TODO:(badash@): Watch for other things to track.
+        //     Specifically time when app framework starts
+    }
+
+    /**
+     * Takes a device and runs a root command.  There is a more robust version implemented by
+     * NativeDevice, but due to some other changes it isnt trivially acessible, but I can get
+     * that implementation fairly easy if we think it is a better idea.
+     */
+    public void enableAdbRoot(ITestDevice mDevice) throws DeviceNotAvailableException {
+        boolean isUserDebug =
+            "userdebug".equals(mDevice.executeShellCommand("getprop ro.build.type").trim());
+        if (!isUserDebug) {
+            //TODO(badash@): This would Noop once cl: ag/1594311 is in
+            return;
+        }
+        mDevice.executeAdbCommand("root");
+    }
+
+    /**
+     * Makes sure the phone is online, and the ensure the current boottime is within 2 seconds
+     * (due to rounding) of the previous boottime to check if The phone has crashed.
+     */
+    @Override
+    public void tearDown() throws Exception {
+        getDevice().waitForDeviceOnline(60 * 1000);
+        assertTrue("Phone has had a hard reset",
+            (System.currentTimeMillis()/1000 -
+                Integer.parseInt(getDevice().executeShellCommand("cut -f1 -d. /proc/uptime").trim())
+                    - kernelStartTime < 2));
+        //TODO(badash@): add ability to catch runtime restart
+        getDevice().executeAdbCommand("unroot");
+    }
+}
diff --git a/hostsidetests/services/activitymanager/src/android/server/cts/ActivityManagerActivityVisiblityTests.java b/hostsidetests/services/activitymanager/src/android/server/cts/ActivityManagerActivityVisiblityTests.java
index 6f527ed..534e2ea 100644
--- a/hostsidetests/services/activitymanager/src/android/server/cts/ActivityManagerActivityVisiblityTests.java
+++ b/hostsidetests/services/activitymanager/src/android/server/cts/ActivityManagerActivityVisiblityTests.java
@@ -38,6 +38,11 @@
         executeShellCommand(AM_START_HOME_ACTIVITY_COMMAND);
         mAmWmState.waitForHomeActivityVisible(mDevice);
 
+        /* TODO: Find a proper way to wait until launcher activity
+         * becomes fully visible. It appears that both VisibleBehindActivity
+         * and home activity are visible at some point before the former
+         * disappears.*/
+        Thread.sleep(3000);
         mAmWmState.computeState(mDevice, null);
         mAmWmState.assertContainsStack(
                 "Must contain fullscreen stack.", FULLSCREEN_WORKSPACE_STACK_ID);
@@ -123,7 +128,9 @@
 
     public void testTranslucentActivityOverDockedStack() throws Exception {
         launchActivityInDockStack(DOCKED_ACTIVITY_NAME);
+        mAmWmState.computeState(mDevice, new String[] {DOCKED_ACTIVITY_NAME});
         launchActivityInStack(TEST_ACTIVITY_NAME, FULLSCREEN_WORKSPACE_STACK_ID);
+        mAmWmState.computeState(mDevice, new String[] {DOCKED_ACTIVITY_NAME, TEST_ACTIVITY_NAME});
         launchActivityInStack(TRANSLUCENT_ACTIVITY_NAME, DOCKED_STACK_ID);
         mAmWmState.computeState(mDevice, new String[] {TEST_ACTIVITY_NAME, DOCKED_ACTIVITY_NAME,
                 TRANSLUCENT_ACTIVITY_NAME}, false /* compareTaskAndStackBounds */);
diff --git a/hostsidetests/services/activitymanager/src/android/server/cts/ActivityManagerDockedStackTests.java b/hostsidetests/services/activitymanager/src/android/server/cts/ActivityManagerDockedStackTests.java
index 8268cb5..ae22720 100644
--- a/hostsidetests/services/activitymanager/src/android/server/cts/ActivityManagerDockedStackTests.java
+++ b/hostsidetests/services/activitymanager/src/android/server/cts/ActivityManagerDockedStackTests.java
@@ -59,8 +59,9 @@
 
     public void testLaunchToSide() throws Exception {
         launchActivityInDockStack(LAUNCH_TO_SIDE_ACTIVITY_NAME);
-        launchActivityToSide(LAUNCH_TO_SIDE_ACTIVITY_NAME);
         mAmWmState.computeState(mDevice, new String[] {LAUNCH_TO_SIDE_ACTIVITY_NAME});
+        launchActivityToSide(LAUNCH_TO_SIDE_ACTIVITY_NAME);
+        mAmWmState.computeState(mDevice, new String[] {TEST_ACTIVITY_NAME});
 
         mAmWmState.assertContainsStack(
                 "Must contain fullscreen stack.", FULLSCREEN_WORKSPACE_STACK_ID);
@@ -71,6 +72,7 @@
         launchActivityInDockStack(LAUNCH_TO_SIDE_ACTIVITY_NAME);
         final String[] waitForFirstVisible = new String[] {TEST_ACTIVITY_NAME};
         final String[] waitForSecondVisible = new String[] {NO_RELAUNCH_ACTIVITY_NAME};
+        mAmWmState.computeState(mDevice, new String[] {LAUNCH_TO_SIDE_ACTIVITY_NAME});
 
         // Launch activity to side.
         launchActivityToSide(LAUNCH_TO_SIDE_ACTIVITY_NAME);
@@ -103,7 +105,10 @@
 
     public void testLaunchToSideMultiple() throws Exception {
         launchActivityInDockStack(LAUNCH_TO_SIDE_ACTIVITY_NAME);
-        final String[] waitForActivitiesVisible = new String[] {TEST_ACTIVITY_NAME};
+        mAmWmState.computeState(mDevice, new String[] {LAUNCH_TO_SIDE_ACTIVITY_NAME});
+
+        final String[] waitForActivitiesVisible =
+            new String[] {TEST_ACTIVITY_NAME, LAUNCH_TO_SIDE_ACTIVITY_NAME};
 
         // Launch activity to side.
         launchActivityToSide(LAUNCH_TO_SIDE_ACTIVITY_NAME);
@@ -142,11 +147,16 @@
     private void launchTargetToSide(String targetActivityName,
                                     boolean taskCountMustIncrement) throws Exception {
         launchActivityInDockStack(LAUNCH_TO_SIDE_ACTIVITY_NAME);
-        final String[] waitForActivitiesVisible = new String[] {targetActivityName};
+        mAmWmState.computeState(mDevice, new String[] {LAUNCH_TO_SIDE_ACTIVITY_NAME});
+
+        final String[] waitForActivitiesVisible =
+            new String[] {targetActivityName, LAUNCH_TO_SIDE_ACTIVITY_NAME};
 
         // Launch activity to side with data.
         launchActivityToSide(LAUNCH_TO_SIDE_ACTIVITY_NAME, true, false, targetActivityName);
         mAmWmState.computeState(mDevice, waitForActivitiesVisible);
+        mAmWmState.assertContainsStack(
+                "Must contain fullscreen stack.", FULLSCREEN_WORKSPACE_STACK_ID);
         int taskNumberInitial = mAmWmState.getAmState().getStackById(FULLSCREEN_WORKSPACE_STACK_ID)
                 .getTasks().size();
         mAmWmState.assertNotNull("Launched to side activity must be in fullscreen stack.",
@@ -192,7 +202,9 @@
 
     public void testLaunchToSideMultipleWithFlag() throws Exception {
         launchActivityInDockStack(LAUNCH_TO_SIDE_ACTIVITY_NAME);
-        final String[] waitForActivitiesVisible = new String[] {TEST_ACTIVITY_NAME};
+        mAmWmState.computeState(mDevice, new String[] {LAUNCH_TO_SIDE_ACTIVITY_NAME});
+        final String[] waitForActivitiesVisible =
+            new String[] {LAUNCH_TO_SIDE_ACTIVITY_NAME, TEST_ACTIVITY_NAME};
 
         // Launch activity to side.
         launchActivityToSide(LAUNCH_TO_SIDE_ACTIVITY_NAME);
@@ -219,15 +231,17 @@
 
     public void testRotationWhenDocked() throws Exception {
         launchActivityInDockStack(LAUNCH_TO_SIDE_ACTIVITY_NAME);
+        mAmWmState.computeState(mDevice, new String[] {LAUNCH_TO_SIDE_ACTIVITY_NAME});
         launchActivityToSide(LAUNCH_TO_SIDE_ACTIVITY_NAME);
-        final String[] waitForActivitiesVisible = new String[] {LAUNCH_TO_SIDE_ACTIVITY_NAME};
-        mAmWmState.computeState(mDevice, waitForActivitiesVisible);
+        mAmWmState.computeState(mDevice, new String[] {TEST_ACTIVITY_NAME});
         mAmWmState.assertContainsStack(
                 "Must contain fullscreen stack.", FULLSCREEN_WORKSPACE_STACK_ID);
         mAmWmState.assertContainsStack("Must contain docked stack.", DOCKED_STACK_ID);
 
         // Rotate device single steps (90°) 0-1-2-3.
         // Each time we compute the state we implicitly assert valid bounds.
+        String[] waitForActivitiesVisible =
+            new String[] {LAUNCH_TO_SIDE_ACTIVITY_NAME, TEST_ACTIVITY_NAME};
         setDeviceRotation(0);
         mAmWmState.computeState(mDevice, waitForActivitiesVisible);
         setDeviceRotation(1);
@@ -252,14 +266,16 @@
 
     public void testRotationWhenDockedWhileLocked() throws Exception {
         launchActivityInDockStack(LAUNCH_TO_SIDE_ACTIVITY_NAME);
+        mAmWmState.computeState(mDevice, new String[] {LAUNCH_TO_SIDE_ACTIVITY_NAME});
         launchActivityToSide(LAUNCH_TO_SIDE_ACTIVITY_NAME);
-        final String[] waitForActivitiesVisible = new String[] {LAUNCH_TO_SIDE_ACTIVITY_NAME};
-        mAmWmState.computeState(mDevice, waitForActivitiesVisible);
+        mAmWmState.computeState(mDevice, new String[] {TEST_ACTIVITY_NAME});
         mAmWmState.assertSanity();
         mAmWmState.assertContainsStack(
                 "Must contain fullscreen stack.", FULLSCREEN_WORKSPACE_STACK_ID);
         mAmWmState.assertContainsStack("Must contain docked stack.", DOCKED_STACK_ID);
 
+        String[] waitForActivitiesVisible =
+            new String[] {LAUNCH_TO_SIDE_ACTIVITY_NAME, TEST_ACTIVITY_NAME};
         lockDevice();
         setDeviceRotation(0);
         unlockDevice();
@@ -283,7 +299,9 @@
 
     public void testResizeDockedStack() throws Exception {
         launchActivityInDockStack(DOCKED_ACTIVITY_NAME);
+        mAmWmState.computeState(mDevice, new String[] {DOCKED_ACTIVITY_NAME});
         launchActivityInStack(TEST_ACTIVITY_NAME, FULLSCREEN_WORKSPACE_STACK_ID);
+        mAmWmState.computeState(mDevice, new String[] {TEST_ACTIVITY_NAME});
         resizeDockedStack(STACK_SIZE, STACK_SIZE, TASK_SIZE, TASK_SIZE);
         mAmWmState.computeState(mDevice, new String[] {TEST_ACTIVITY_NAME, DOCKED_ACTIVITY_NAME},
                 false /* compareTaskAndStackBounds */);
@@ -298,12 +316,14 @@
     }
 
     public void testActivityLifeCycleOnResizeDockedStack() throws Exception {
+        final String[] waitTestActivityName = new String[] {TEST_ACTIVITY_NAME};
         executeShellCommand(getAmStartCmd(TEST_ACTIVITY_NAME));
-        mAmWmState.computeState(mDevice, new String[] {TEST_ACTIVITY_NAME});
+        mAmWmState.computeState(mDevice, waitTestActivityName);
         final Rectangle fullScreenBounds =
                 mAmWmState.getWmState().getStack(FULLSCREEN_WORKSPACE_STACK_ID).getBounds();
 
         moveActivityToDockStack(TEST_ACTIVITY_NAME);
+        mAmWmState.computeState(mDevice, waitTestActivityName);
         launchActivityInStack(NO_RELAUNCH_ACTIVITY_NAME, FULLSCREEN_WORKSPACE_STACK_ID);
 
         mAmWmState.computeState(mDevice,
@@ -315,15 +335,15 @@
 
         Rectangle newBounds = computeNewDockBounds(fullScreenBounds, initialDockBounds, true);
         resizeDockedStack(newBounds.width, newBounds.height, newBounds.width, newBounds.height);
+        mAmWmState.computeState(mDevice,
+                new String[]{TEST_ACTIVITY_NAME, NO_RELAUNCH_ACTIVITY_NAME});
 
         // We resize twice to make sure we cross an orientation change threshold for both
         // activities.
         newBounds = computeNewDockBounds(fullScreenBounds, initialDockBounds, false);
         resizeDockedStack(newBounds.width, newBounds.height, newBounds.width, newBounds.height);
-
         mAmWmState.computeState(mDevice,
                 new String[]{TEST_ACTIVITY_NAME, NO_RELAUNCH_ACTIVITY_NAME});
-
         assertActivityLifecycle(TEST_ACTIVITY_NAME, true);
         assertActivityLifecycle(NO_RELAUNCH_ACTIVITY_NAME, false);
     }
diff --git a/hostsidetests/sustainedperf/src/android/SustainedPerformance/cts/SustainedPerformanceHostTest.java b/hostsidetests/sustainedperf/src/android/SustainedPerformance/cts/SustainedPerformanceHostTest.java
index 7b3a30c..683aded 100644
--- a/hostsidetests/sustainedperf/src/android/SustainedPerformance/cts/SustainedPerformanceHostTest.java
+++ b/hostsidetests/sustainedperf/src/android/SustainedPerformance/cts/SustainedPerformanceHostTest.java
@@ -157,7 +157,6 @@
     }
 
     private void setUpEnvironment() throws Exception {
-        device.disconnectFromWifi();
         dhryMin = Double.MAX_VALUE;
         dhryMax = Double.MIN_VALUE;
         Thread.sleep(600000);
@@ -240,15 +239,21 @@
         device.executeShellCommand("settings put global airplane_mode_on 0");
         device.executeShellCommand("am broadcast -a android.intent.action.AIRPLANE_MODE --ez state false");
 
-        double perfdegradapp = (appResultsWithMode.get(1) - appResultsWithoutMode.get(1))*100/appResultsWithMode.get(1);
-        double perfdegraddhry = (dhrystoneResultsWithoutMode.get(0) - dhrystoneResultsWithMode.get(0))*100/dhrystoneResultsWithoutMode.get(0);
+        double resDhry = dhrystoneResultsWithMode.get(2);
+        double resApp = appResultsWithMode.get(2);
+
+        /* Report if performance is below 5% margin for both dhrystone and shader */
+        if ((resDhry > 5) || (resApp > 5)) {
+            Log.w("SustainedPerformanceHostTests",
+                  "Sustainable mode results, Dhrystone: " + resDhry + " App: " + resApp);
+        }
 
         /*
-         * Checks if the performance in the mode is consistent with
-         * 5% error margin.
+         * Error if the performance in the mode is not consistent with
+         * 5% error margin for shader and 10% error margin for dhrystone.
          */
         assertFalse("Results in the mode are not sustainable",
-                (dhrystoneResultsWithMode.get(2) > 10) ||
-                (appResultsWithMode.get(2)) > 5);
+                    (resDhry > 15) ||
+                    (resApp > 5));
     }
 }
diff --git a/hostsidetests/theme/assets/22/hdpi.zip b/hostsidetests/theme/assets/22/hdpi.zip
index 0fa67b7..2b23d67 100644
--- a/hostsidetests/theme/assets/22/hdpi.zip
+++ b/hostsidetests/theme/assets/22/hdpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/22/xhdpi.zip b/hostsidetests/theme/assets/22/xhdpi.zip
index de6e2e1..fbde98a 100644
--- a/hostsidetests/theme/assets/22/xhdpi.zip
+++ b/hostsidetests/theme/assets/22/xhdpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/22/xxhdpi.zip b/hostsidetests/theme/assets/22/xxhdpi.zip
index 9f0d778..ed7036e 100644
--- a/hostsidetests/theme/assets/22/xxhdpi.zip
+++ b/hostsidetests/theme/assets/22/xxhdpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/24/hdpi.zip b/hostsidetests/theme/assets/24/hdpi.zip
new file mode 100644
index 0000000..d9dc466
--- /dev/null
+++ b/hostsidetests/theme/assets/24/hdpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/24/tvdpi.zip b/hostsidetests/theme/assets/24/tvdpi.zip
new file mode 100644
index 0000000..b9ef65a
--- /dev/null
+++ b/hostsidetests/theme/assets/24/tvdpi.zip
Binary files differ
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityGlobalActionsTest.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityGlobalActionsTest.java
index b4f839e..bbda333 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityGlobalActionsTest.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityGlobalActionsTest.java
@@ -104,8 +104,11 @@
         waitForIdle();
 
         // Clean up.
+        // The GLOBAL_ACTION_HOME is needed in the case where additional
+        // notifications showing up, we need to clear them as well for the upcoming
+        // new tests to work properly.
         getInstrumentation().getUiAutomation().performGlobalAction(
-                AccessibilityService.GLOBAL_ACTION_BACK);
+                AccessibilityService.GLOBAL_ACTION_HOME);
 
         // Sleep a bit so the UI is settled.
         waitForIdle();
diff --git a/tests/admin/src/android/admin/cts/DevicePolicyManagerTest.java b/tests/admin/src/android/admin/cts/DevicePolicyManagerTest.java
index 543a385..deb1854 100644
--- a/tests/admin/src/android/admin/cts/DevicePolicyManagerTest.java
+++ b/tests/admin/src/android/admin/cts/DevicePolicyManagerTest.java
@@ -671,11 +671,13 @@
     }
 
     /**
-     * Test that managed provisioning is pre-installed if and only if the device declares the
-     * device admin feature.
+     * Test that managed provisioning is pre-installed if the device declares the device admin
+     * feature.
      */
     public void testManagedProvisioningPreInstalled() throws Exception {
-        assertEquals(mDeviceAdmin, isPackageInstalledOnSystemImage(MANAGED_PROVISIONING_PKG));
+        if (mDeviceAdmin) {
+            assertTrue(isPackageInstalledOnSystemImage(MANAGED_PROVISIONING_PKG));
+        }
     }
 
     private void assertDeviceOwnerMessage(String message) {
diff --git a/tests/app/src/android/app/cts/SystemFeaturesTest.java b/tests/app/src/android/app/cts/SystemFeaturesTest.java
index cad4922..38443b9 100644
--- a/tests/app/src/android/app/cts/SystemFeaturesTest.java
+++ b/tests/app/src/android/app/cts/SystemFeaturesTest.java
@@ -457,6 +457,10 @@
     }
 
     public void testUsbAccessory() {
+        // USB accessory mode is only a requirement for devices with USB ports supporting
+        // peripheral mode. As there is no public API to distinguish a device with only host
+        // mode support from having both peripheral and host support, the test may have
+        // false negatives.
         if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE) &&
                 !mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEVISION) &&
                 !mPackageManager.hasSystemFeature(PackageManager.FEATURE_WATCH)) {
@@ -464,23 +468,18 @@
         }
     }
 
-    public void testWifiFeature() throws Exception {
+  public void testWifiFeature() throws Exception {
         if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_WIFI)) {
             // no WiFi, skip the test
             return;
         }
         boolean enabled = mWifiManager.isWifiEnabled();
         try {
-            // WifiManager is hard-coded to return true,
-            // the case without WiFi is already handled,
-            // so this case MUST have WiFi.
-            if (mWifiManager.setWifiEnabled(true)) {
-                assertAvailable(PackageManager.FEATURE_WIFI);
-            }
+            // assert wifimanager can toggle wifi from current sate
+            assertTrue(mWifiManager.setWifiEnabled(!enabled));
+
         } finally {
-            if (!enabled) {
-                mWifiManager.setWifiEnabled(false);
-            }
+            mWifiManager.setWifiEnabled(enabled);
         }
     }
 
diff --git a/tests/backup/src/android/backup/cts/BackupQuotaTest.java b/tests/backup/src/android/backup/cts/BackupQuotaTest.java
index 6122ad0..01c368a 100644
--- a/tests/backup/src/android/backup/cts/BackupQuotaTest.java
+++ b/tests/backup/src/android/backup/cts/BackupQuotaTest.java
@@ -147,10 +147,6 @@
                 out.append(str);
             }
             return out.toString();
-        } finally {
-            if (br != null) {
-                closeQuietly(br);
-            }
         }
     }
 
@@ -158,7 +154,7 @@
                                                                String command) throws Exception {
         final ParcelFileDescriptor pfd =
                 instrumentation.getUiAutomation().executeShellCommand(command);
-        return new FileInputStream(pfd.getFileDescriptor());
+        return new ParcelFileDescriptor.AutoCloseInputStream(pfd);
     }
 
     private static void closeQuietly(AutoCloseable closeable) {
diff --git a/tests/camera/libctscamera2jni/native-camera-jni.cpp b/tests/camera/libctscamera2jni/native-camera-jni.cpp
index db687bf..5b75bf2 100644
--- a/tests/camera/libctscamera2jni/native-camera-jni.cpp
+++ b/tests/camera/libctscamera2jni/native-camera-jni.cpp
@@ -1079,7 +1079,7 @@
 
         // Check get unknown value fails
         uint32_t badTag = (uint32_t) ACAMERA_VENDOR_START - 1;
-        ret = ACameraMetadata_getConstEntry(chars, ACAMERA_VENDOR_START, &entry);
+        ret = ACameraMetadata_getConstEntry(chars, badTag, &entry);
         if (ret == ACAMERA_OK) {
             LOG_ERROR(errorString, "Error: get unknown tag should fail!");
             goto cleanup;
diff --git a/tests/camera/src/android/hardware/camera2/cts/AllocationTest.java b/tests/camera/src/android/hardware/camera2/cts/AllocationTest.java
index 2969753..bbc4c75 100644
--- a/tests/camera/src/android/hardware/camera2/cts/AllocationTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/AllocationTest.java
@@ -494,6 +494,10 @@
                             });
 
                             stopCapture();
+                            if (VERBOSE) Log.v(TAG, "Cleanup Renderscript cache");
+                            scriptGraph.close();
+                            RenderScriptSingleton.clearContext();
+                            RenderScriptSingleton.setContext(getContext());
                         }
                     }
                 });
diff --git a/tests/camera/src/android/hardware/camera2/cts/CameraManagerTest.java b/tests/camera/src/android/hardware/camera2/cts/CameraManagerTest.java
index 67c08fe..11071fc 100644
--- a/tests/camera/src/android/hardware/camera2/cts/CameraManagerTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/CameraManagerTest.java
@@ -149,11 +149,13 @@
             assertNotNull("Can't get lens facing info", lensFacing);
             if (lensFacing == CameraCharacteristics.LENS_FACING_FRONT) {
                 assertTrue("System doesn't have front camera feature",
-                        mPackageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA_FRONT) ||
-                        mPackageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA_EXTERNAL));
+                        mPackageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA_FRONT));
             } else if (lensFacing == CameraCharacteristics.LENS_FACING_BACK) {
                 assertTrue("System doesn't have back camera feature",
                         mPackageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA));
+            } else if (lensFacing == CameraCharacteristics.LENS_FACING_EXTERNAL) {
+                assertTrue("System doesn't have external camera feature",
+                        mPackageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA_EXTERNAL));
             } else {
                 fail("Unknown camera lens facing " + lensFacing.toString());
             }
@@ -166,10 +168,11 @@
         assertTrue("Missing system feature: FEATURE_CAMERA_ANY",
                ids.length == 0
             || mPackageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA_ANY));
-        assertTrue("Missing system feature: FEATURE_CAMERA or FEATURE_CAMERA_FRONT",
+        assertTrue("Missing system feature: FEATURE_CAMERA, FEATURE_CAMERA_FRONT or FEATURE_CAMERA_EXTERNAL",
                ids.length == 0
             || mPackageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA)
-            || mPackageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA_FRONT));
+            || mPackageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA_FRONT)
+            || mPackageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA_EXTERNAL));
     }
 
     // Test: that properties can be queried from each device, without exceptions.
diff --git a/tests/camera/src/android/hardware/camera2/cts/helpers/StaticMetadata.java b/tests/camera/src/android/hardware/camera2/cts/helpers/StaticMetadata.java
index df89297..07e810f 100644
--- a/tests/camera/src/android/hardware/camera2/cts/helpers/StaticMetadata.java
+++ b/tests/camera/src/android/hardware/camera2/cts/helpers/StaticMetadata.java
@@ -714,7 +714,7 @@
      */
     public int[] getAvailableToneMapModesChecked() {
         Key<int[]> key = CameraCharacteristics.TONEMAP_AVAILABLE_TONE_MAP_MODES;
-        int[] modes = getValueFromKeyNonNull(key);
+        int[] modes = mCharacteristics.get(key);
 
         if (modes == null) {
             return new int[0];
diff --git a/tests/leanbackjank/app/res/values/strings.xml b/tests/leanbackjank/app/res/values/strings.xml
index 17edf95..c90bbbc 100644
--- a/tests/leanbackjank/app/res/values/strings.xml
+++ b/tests/leanbackjank/app/res/values/strings.xml
@@ -20,8 +20,6 @@
     <string name="browse_title"><![CDATA[Leanback launcher jank test application]]></string>
     <string name="error">Error</string>
     <string name="ok">OK</string>
-    <string name="grid_item_template">Item %1$d</string>
-    <string name="settings">Settings</string>
 
     <!-- Error messages -->
     <string name="error_fragment_message">An error occurred</string>
diff --git a/tests/leanbackjank/app/src/android/leanbackjank/app/presenter/CardPresenter.java b/tests/leanbackjank/app/src/android/leanbackjank/app/presenter/CardPresenter.java
index f237438..bedbc5d 100644
--- a/tests/leanbackjank/app/src/android/leanbackjank/app/presenter/CardPresenter.java
+++ b/tests/leanbackjank/app/src/android/leanbackjank/app/presenter/CardPresenter.java
@@ -30,8 +30,8 @@
  * It contains an Image CardView
  */
 public class CardPresenter extends Presenter {
-    private static int CARD_WIDTH = 313;
-    private static int CARD_HEIGHT = 176;
+    private static int CARD_WIDTH = 560;
+    private static int CARD_HEIGHT = 320;
     private static int sSelectedBackgroundColor;
     private static int sDefaultBackgroundColor;
     private Drawable mDefaultCardImage;
diff --git a/tests/leanbackjank/app/src/android/leanbackjank/app/presenter/GridItemPresenter.java b/tests/leanbackjank/app/src/android/leanbackjank/app/presenter/GridItemPresenter.java
deleted file mode 100644
index 1ab7e8d..0000000
--- a/tests/leanbackjank/app/src/android/leanbackjank/app/presenter/GridItemPresenter.java
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES 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.leanbackjank.app.presenter;
-
-import android.graphics.Color;
-import android.support.v17.leanback.widget.Presenter;
-import android.view.Gravity;
-import android.view.ViewGroup;
-import android.widget.TextView;
-
-import android.leanbackjank.app.R;
-import android.leanbackjank.app.ui.MainFragment;
-
-public class GridItemPresenter extends Presenter {
-    private static int GRID_ITEM_WIDTH = 200;
-    private static int GRID_ITEM_HEIGHT = 200;
-
-    private MainFragment mainFragment;
-
-    public GridItemPresenter(MainFragment mainFragment) {
-        this.mainFragment = mainFragment;
-    }
-
-    @Override
-    public ViewHolder onCreateViewHolder(ViewGroup parent) {
-        TextView view = new TextView(parent.getContext());
-        view.setLayoutParams(new ViewGroup.LayoutParams(GRID_ITEM_WIDTH, GRID_ITEM_HEIGHT));
-        view.setFocusable(true);
-        view.setFocusableInTouchMode(true);
-        view.setBackgroundColor(
-            mainFragment.getResources().getColor(R.color.default_background, null));
-        view.setTextColor(Color.WHITE);
-        view.setGravity(Gravity.CENTER);
-        return new ViewHolder(view);
-    }
-
-    @Override
-    public void onBindViewHolder(ViewHolder viewHolder, Object item) {
-        ((TextView) viewHolder.view).setText((String) item);
-    }
-
-    @Override
-    public void onUnbindViewHolder(ViewHolder viewHolder) {
-    }
-}
diff --git a/tests/leanbackjank/app/src/android/leanbackjank/app/ui/MainFragment.java b/tests/leanbackjank/app/src/android/leanbackjank/app/ui/MainFragment.java
index 78032c2..399681c 100644
--- a/tests/leanbackjank/app/src/android/leanbackjank/app/ui/MainFragment.java
+++ b/tests/leanbackjank/app/src/android/leanbackjank/app/ui/MainFragment.java
@@ -21,7 +21,6 @@
 import android.leanbackjank.app.data.VideoProvider;
 import android.leanbackjank.app.model.Movie;
 import android.leanbackjank.app.presenter.CardPresenter;
-import android.leanbackjank.app.presenter.GridItemPresenter;
 import android.leanbackjank.app.presenter.IconHeaderItemPresenter;
 import android.os.Bundle;
 import android.os.Handler;
@@ -184,15 +183,6 @@
             mRowsAdapter.add(new ListRow(header, listRowAdapter));
         }
 
-        HeaderItem gridHeader = new HeaderItem(i, getString(R.string.settings));
-
-        GridItemPresenter gridPresenter = new GridItemPresenter(this);
-        ArrayObjectAdapter gridRowAdapter = new ArrayObjectAdapter(gridPresenter);
-        for (int j = 0; j < 10; j++) {
-            gridRowAdapter.add(getString(R.string.grid_item_template, j));
-        }
-        mRowsAdapter.add(new ListRow(gridHeader, gridRowAdapter));
-
         setAdapter(mRowsAdapter);
     }
 
diff --git a/tests/tests/app.usage/src/android/app/usage/cts/NetworkUsageStatsTest.java b/tests/tests/app.usage/src/android/app/usage/cts/NetworkUsageStatsTest.java
index bd96d76..2c3c2ad 100644
--- a/tests/tests/app.usage/src/android/app/usage/cts/NetworkUsageStatsTest.java
+++ b/tests/tests/app.usage/src/android/app/usage/cts/NetworkUsageStatsTest.java
@@ -26,6 +26,9 @@
 import android.net.NetworkCapabilities;
 import android.net.NetworkInfo;
 import android.net.NetworkRequest;
+import android.net.TrafficStats;
+import android.os.Handler;
+import android.os.HandlerThread;
 import android.os.ParcelFileDescriptor;
 import android.os.Process;
 import android.os.RemoteException;
@@ -53,6 +56,12 @@
     private static final long MINUTE = 1000 * 60;
     private static final int TIMEOUT_MILLIS = 15000;
 
+    private static final String CHECK_CONNECTIVITY_URL = "http://www.265.com/";
+    private static final String CHECK_CALLBACK_URL = CHECK_CONNECTIVITY_URL;
+
+    private static final int NETWORK_TAG = 0xf00d;
+    private static final long THRESHOLD_BYTES = 2 * 1024 * 1024;  // 2 MB
+
     private interface NetworkInterfaceToTest {
         int getNetworkType();
         int getTransportType();
@@ -119,7 +128,7 @@
     private String mWriteSettingsMode;
     private String mUsageStatsMode;
 
-    private void exerciseRemoteHost(Network network) throws Exception {
+    private void exerciseRemoteHost(Network network, URL url) throws Exception {
         NetworkInfo networkInfo = mCm.getNetworkInfo(network);
         if (networkInfo == null) {
             Log.w(LOG_TAG, "Network info is null");
@@ -131,8 +140,8 @@
         String originalKeepAlive = System.getProperty("http.keepAlive");
         System.setProperty("http.keepAlive", "false");
         try {
-            urlc = (HttpURLConnection) network.openConnection(new URL(
-                    "http://www.265.com/"));
+            TrafficStats.setThreadStatsTag(NETWORK_TAG);
+            urlc = (HttpURLConnection) network.openConnection(url);
             urlc.setConnectTimeout(TIMEOUT_MILLIS);
             urlc.setUseCaches(false);
             urlc.connect();
@@ -147,6 +156,7 @@
         } catch (Exception e) {
             Log.i(LOG_TAG, "Badness during exercising remote server: " + e);
         } finally {
+            TrafficStats.clearThreadStatsTag();
             if (in != null) {
                 try {
                     in.close();
@@ -229,10 +239,12 @@
 
     private class NetworkCallback extends ConnectivityManager.NetworkCallback {
         private long mTolerance;
+        private URL mUrl;
         public boolean success;
 
-        NetworkCallback(long tolerance) {
+        NetworkCallback(long tolerance, URL url) {
             mTolerance = tolerance;
+            mUrl = url;
             success = false;
         }
 
@@ -240,7 +252,7 @@
         public void onAvailable(Network network) {
             try {
                 mStartTime = System.currentTimeMillis() - mTolerance;
-                exerciseRemoteHost(network);
+                exerciseRemoteHost(network, mUrl);
                 mEndTime = System.currentTimeMillis() + mTolerance;
                 success = true;
                 synchronized(NetworkUsageStatsTest.this) {
@@ -260,7 +272,7 @@
         if (!hasFeature) {
             return false;
         }
-        NetworkCallback callback = new NetworkCallback(tolerance);
+        NetworkCallback callback = new NetworkCallback(tolerance, new URL(CHECK_CONNECTIVITY_URL));
         mCm.requestNetwork(new NetworkRequest.Builder()
                 .addTransportType(sNetworkInterfacesToTest[networkTypeIndex].getTransportType())
                 .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
@@ -520,10 +532,99 @@
         }
     }
 
+    public void testTagDetails() throws Exception {
+        for (int i = 0; i < sNetworkInterfacesToTest.length; ++i) {
+            // Relatively large tolerance to accommodate for history bucket size.
+            if (!shouldTestThisNetworkType(i, MINUTE * 120)) {
+                continue;
+            }
+            setAppOpsMode(AppOpsManager.OPSTR_GET_USAGE_STATS, "allow");
+            NetworkStats result = null;
+            try {
+                result = mNsm.queryDetailsForUidTag(
+                        sNetworkInterfacesToTest[i].getNetworkType(), getSubscriberId(i),
+                        mStartTime, mEndTime, Process.myUid(), NETWORK_TAG);
+                assertTrue(result != null);
+                NetworkStats.Bucket bucket = new NetworkStats.Bucket();
+                long totalTxPackets = 0;
+                long totalRxPackets = 0;
+                long totalTxBytes = 0;
+                long totalRxBytes = 0;
+                while (result.hasNextBucket()) {
+                    assertTrue(result.getNextBucket(bucket));
+                    assertTimestamps(bucket);
+                    assertEquals(bucket.getState(), NetworkStats.Bucket.STATE_ALL);
+                    assertEquals(bucket.getUid(), Process.myUid());
+                    if (bucket.getTag() == NETWORK_TAG) {
+                        totalTxPackets += bucket.getTxPackets();
+                        totalRxPackets += bucket.getRxPackets();
+                        totalTxBytes += bucket.getTxBytes();
+                        totalRxBytes += bucket.getRxBytes();
+                    }
+                }
+                assertTrue("No Rx bytes tagged with 0x" + Integer.toHexString(NETWORK_TAG)
+                        + " for uid " + Process.myUid(), totalRxBytes > 0);
+                assertTrue("No Rx packets tagged with " + Integer.toHexString(NETWORK_TAG)
+                        + " for uid " + Process.myUid(), totalRxPackets > 0);
+                assertTrue("No Tx bytes tagged with 0x" + Integer.toHexString(NETWORK_TAG)
+                        + " for uid " + Process.myUid(), totalTxBytes > 0);
+                assertTrue("No Tx packets tagged with 0x" + Integer.toHexString(NETWORK_TAG)
+                        + " for uid " + Process.myUid(), totalTxPackets > 0);
+            } catch (SecurityException e) {
+                fail("testUidDetails fails with exception: " + e.toString());
+            } finally {
+                if (result != null) {
+                    result.close();
+                }
+            }
+            setAppOpsMode(AppOpsManager.OPSTR_GET_USAGE_STATS, "deny");
+            try {
+                result = mNsm.queryDetailsForUidTag(
+                        sNetworkInterfacesToTest[i].getNetworkType(), getSubscriberId(i),
+                        mStartTime, mEndTime, Process.myUid(), NETWORK_TAG);
+                fail("negative testUidDetails fails: no exception thrown.");
+            } catch (SecurityException e) {
+                // expected outcome
+            }
+        }
+    }
+
+    public void testCallback() throws Exception {
+        for (int i = 0; i < sNetworkInterfacesToTest.length; ++i) {
+            // Relatively large tolerance to accommodate for history bucket size.
+            if (!shouldTestThisNetworkType(i, MINUTE/2)) {
+                continue;
+            }
+            setAppOpsMode(AppOpsManager.OPSTR_GET_USAGE_STATS, "allow");
+
+            TestUsageCallback usageCallback = new TestUsageCallback();
+            HandlerThread thread = new HandlerThread("callback-thread");
+            thread.start();
+            Handler handler = new Handler(thread.getLooper());
+            mNsm.registerUsageCallback(sNetworkInterfacesToTest[i].getNetworkType(),
+                    getSubscriberId(i), THRESHOLD_BYTES, usageCallback, handler);
+
+            // TODO: Force traffic and check whether the callback is invoked.
+            // Right now the test only covers whether the callback can be registered, but not
+            // whether it is invoked upon data usage since we don't have a scalable way of
+            // storing files of >2MB in CTS.
+
+            mNsm.unregisterUsageCallback(usageCallback);
+        }
+    }
+
     private void assertTimestamps(final NetworkStats.Bucket bucket) {
         assertTrue("Start timestamp " + bucket.getStartTimeStamp() + " is less than " +
                 mStartTime, bucket.getStartTimeStamp() >= mStartTime);
         assertTrue("End timestamp " + bucket.getEndTimeStamp() + " is greater than " +
                 mEndTime, bucket.getEndTimeStamp() <= mEndTime);
     }
+
+    private static class TestUsageCallback extends NetworkStatsManager.UsageCallback {
+        @Override
+        public void onThresholdReached(int networkType, String subscriberId) {
+            Log.v(LOG_TAG, "Called onThresholdReached for networkType=" + networkType
+                    + " subscriberId=" + subscriberId);
+        }
+    }
 }
diff --git a/tests/tests/drm/src/android/drm/cts/DrmInfoRequestTest.java b/tests/tests/drm/src/android/drm/cts/DrmInfoRequestTest.java
index 02894d4..cedc2d5 100644
--- a/tests/tests/drm/src/android/drm/cts/DrmInfoRequestTest.java
+++ b/tests/tests/drm/src/android/drm/cts/DrmInfoRequestTest.java
@@ -31,6 +31,8 @@
 
     public static void testInvalidInfoTypes() throws Exception {
         checkInvalidInfoType(DrmInfoRequest.TYPE_REGISTRATION_INFO - 1);
+        checkInvalidInfoType(
+                DrmInfoRequest.TYPE_RIGHTS_ACQUISITION_PROGRESS_INFO + 1);
     }
 
     public static void testValidInfoTypes() throws Exception {
diff --git a/tests/tests/drm/src/android/drm/cts/DrmInfoStatusTest.java b/tests/tests/drm/src/android/drm/cts/DrmInfoStatusTest.java
index cecd558..103bd6e 100644
--- a/tests/tests/drm/src/android/drm/cts/DrmInfoStatusTest.java
+++ b/tests/tests/drm/src/android/drm/cts/DrmInfoStatusTest.java
@@ -30,6 +30,7 @@
             DrmInfoRequest.TYPE_REGISTRATION_INFO;
 
     public static void testInvalidStatusCodes() throws Exception {
+        checkInvalidStatusCode(DrmInfoStatus.STATUS_ERROR + 1);
         checkInvalidStatusCode(DrmInfoStatus.STATUS_OK - 1);
     }
 
diff --git a/tests/tests/drm/src/android/drm/cts/DrmInfoTest.java b/tests/tests/drm/src/android/drm/cts/DrmInfoTest.java
index 7a498a6..656df5b 100644
--- a/tests/tests/drm/src/android/drm/cts/DrmInfoTest.java
+++ b/tests/tests/drm/src/android/drm/cts/DrmInfoTest.java
@@ -33,6 +33,8 @@
 
     public static void testInvalidInfoTypes() throws Exception {
         checkInvalidInfoType(DrmInfoRequest.TYPE_REGISTRATION_INFO - 1);
+        checkInvalidInfoType(
+                DrmInfoRequest.TYPE_RIGHTS_ACQUISITION_PROGRESS_INFO + 1);
     }
 
     public static void testValidInfoTypes() throws Exception {
diff --git a/tests/tests/graphics/src/android/opengl/cts/OpenGlEsVersionTest.java b/tests/tests/graphics/src/android/opengl/cts/OpenGlEsVersionTest.java
index 77c6da1..5a76816 100644
--- a/tests/tests/graphics/src/android/opengl/cts/OpenGlEsVersionTest.java
+++ b/tests/tests/graphics/src/android/opengl/cts/OpenGlEsVersionTest.java
@@ -25,6 +25,7 @@
 import android.test.ActivityInstrumentationTestCase2;
 import android.util.Log;
 
+import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
 import javax.microedition.khronos.egl.EGL10;
@@ -61,20 +62,16 @@
         int detectedMajorVersion = getDetectedMajorVersion();
         int reportedVersion = getVersionFromActivityManager(mActivity);
 
-        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);
+        assertGlVersionString(1, 1);
         if (detectedMajorVersion == 2) {
             restartActivityWithClientVersion(2);
-            assertGlVersionString(2);
+            assertGlVersionString(2, getMinorVersion(reportedVersion));
         } else if (detectedMajorVersion == 3) {
             restartActivityWithClientVersion(3);
-            assertGlVersionString(3);
+            assertGlVersionString(3, getMinorVersion(reportedVersion));
         }
     }
 
@@ -123,14 +120,11 @@
         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());
+        boolean hasAepExtension = hasExtension(extensions, "GL_ANDROID_extension_pack_es31a");
+        assertEquals("System feature FEATURE_OPENGLES_EXTENSION_PACK is "
+            + (hasAepFeature ? "" : "not ") + "available, but extension GL_ANDROID_extension_pack_es31a is "
+            + (hasAepExtension ? "" : "not ") + "in the OpenGL ES extension list.",
+            hasAepFeature, hasAepExtension);
     }
 
     public void testOpenGlEsVersionForVrHighPerformance() throws InterruptedException {
@@ -288,15 +282,21 @@
     }
 
     /**
-     * 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.
+     * Check that the version string has the form "OpenGL ES(-CM)? (\d+)\.(\d+)", where the two
+     * numbers match the major and minor parameters.
      */
-    private void assertGlVersionString(int majorVersion) throws InterruptedException {
-        String versionString = "" + majorVersion;
-        String message = "OpenGL version string '" + mActivity.getVersionString()
-                + "' is not " + majorVersion + ".0+.";
-        assertTrue(message, Pattern.matches(".*OpenGL.*ES.*" + versionString + "\\.\\d.*",
-                mActivity.getVersionString()));
+    private void assertGlVersionString(int major, int minor) throws InterruptedException {
+        Matcher matcher = Pattern.compile("OpenGL ES(?:-CM)? (\\d+)\\.(\\d+).*")
+                                 .matcher(mActivity.getVersionString());
+        assertTrue("OpenGL ES version string is not of the required form "
+            + "'OpenGL ES(-CM)? (\\d+)\\.(\\d+).*'",
+            matcher.matches());
+        int stringMajor = Integer.parseInt(matcher.group(1));
+        int stringMinor = Integer.parseInt(matcher.group(2));
+        assertEquals("GL_VERSION string doesn't match ActivityManager major version (check ro.opengles.version property)",
+            major, stringMajor);
+        assertEquals("GL_VERSION string doesn't match ActivityManager minor version (check ro.opengles.version property)",
+            minor, stringMinor);
     }
 
     /** Restart {@link GLSurfaceViewCtsActivity} with a specific client version. */
diff --git a/tests/tests/graphics2/src/android/graphics2/cts/TextureViewCameraActivity.java b/tests/tests/graphics2/src/android/graphics2/cts/TextureViewCameraActivity.java
index 01776e5..647fc30 100644
--- a/tests/tests/graphics2/src/android/graphics2/cts/TextureViewCameraActivity.java
+++ b/tests/tests/graphics2/src/android/graphics2/cts/TextureViewCameraActivity.java
@@ -55,13 +55,7 @@
         Assert.assertTrue(mTextureView.isOpaque());
         mWidth = width;
         mHeight = height;
-        PackageManager packageManager = getPackageManager();
-        boolean hasRearCamera = packageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA);
-        boolean hasFrontCamera =
-                packageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA_FRONT);
-        if (hasRearCamera) {
-            mCamera = Camera.open();
-        } else if (hasFrontCamera) {
+        if (Camera.getNumberOfCameras() > 0) {
             mCamera = Camera.open(0);
         } else {
             // no camera, and no frame update, so just complete here.
@@ -84,8 +78,10 @@
     }
 
     public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
-        mCamera.stopPreview();
-        mCamera.release();
+        if (mCamera != null) {
+            mCamera.stopPreview();
+            mCamera.release();
+        }
         return true;
     }
 
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/SensorCtsHelper.java b/tests/tests/hardware/src/android/hardware/cts/helpers/SensorCtsHelper.java
index 9cb3710..92c0ede 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/SensorCtsHelper.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/SensorCtsHelper.java
@@ -141,6 +141,14 @@
     }
 
     /**
+     * If value lies outside the boundary limit, then return the nearer bound value.
+     * Otherwise, return the value unchanged.
+     */
+    public static <TValue extends Number> double clamp(TValue val, TValue min, TValue max) {
+        return Math.min(max.doubleValue(), Math.max(min.doubleValue(), val.doubleValue()));
+    }
+
+    /**
      * @return The magnitude (norm) represented by the given array of values.
      */
     public static double getMagnitude(float[] values) {
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 2f4777b..4639b59 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
@@ -75,7 +75,10 @@
                 sensor.getName(),
                 sensor.getMinDelay(),
                 sensor.getMaxDelay()));
+
+        double minDelayUs = sensor.getMinDelay();
         double maxDelayUs = sensor.getMaxDelay();
+
         if (maxDelayUs <= 0) {
             // This sensor didn't report its maxDelay.
             // It might be only capable of producing its max rate.
@@ -83,22 +86,32 @@
             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();
+            maxDelayUs = minDelayUs;
         }
 
         if (environment.isSensorSamplingRateOverloaded()) {
-            maxDelayUs = sensor.getMinDelay();
+            maxDelayUs = minDelayUs;
         }
 
         // Convert the rateUs parameter into a delay in microseconds and rate in Hz.
         double delayUs = environment.getRequestedSamplingPeriodUs();
 
-        // 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);
+        double lowerExpectedHz = 0;
+        double upperExpectedHz = 0;
+
+        // If sensor reports its maxDelay, then
+        // lowerExpectedHz = upperExpectedHz and minRate <= expectedHz <= maxRate.
+        // If sensor do not report its maxDelay, then
+        // lowerExpectedHz is as per request (obviously <= maxRate) & upperExpectedHz = maxRate.
+        if (sensor.getMaxDelay() > 0) {
+            delayUs = SensorCtsHelper.clamp(delayUs, minDelayUs, maxDelayUs);
+            double expectedHz = SensorCtsHelper.getFrequency(delayUs, TimeUnit.MICROSECONDS);
+            lowerExpectedHz = upperExpectedHz = expectedHz;
+        } else {
+            delayUs = Math.max(minDelayUs, delayUs);
+            lowerExpectedHz = SensorCtsHelper.getFrequency(delayUs, TimeUnit.MICROSECONDS);
+            upperExpectedHz = SensorCtsHelper.getFrequency(minDelayUs, TimeUnit.MICROSECONDS);
+        }
 
         // Set the pass thresholds based on default multipliers.
         double lowerThresholdHz = lowerExpectedHz * DEFAULT_LOWER_THRESHOLD / 100;
diff --git a/tests/tests/location/src/android/location/cts/GeocoderTest.java b/tests/tests/location/src/android/location/cts/GeocoderTest.java
index d96b73f..62d9d25 100644
--- a/tests/tests/location/src/android/location/cts/GeocoderTest.java
+++ b/tests/tests/location/src/android/location/cts/GeocoderTest.java
@@ -17,6 +17,9 @@
 package android.location.cts;
 
 
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
 import android.location.Geocoder;
 import android.test.AndroidTestCase;
 
@@ -43,7 +46,21 @@
 
     public void testIsPresent() {
         Geocoder geocoder = new Geocoder(getContext());
-        assertTrue(geocoder.isPresent());
+        if (isServiceMissing()) {
+            assertFalse(geocoder.isPresent());
+        } else {
+            assertTrue(geocoder.isPresent());
+        }
+    }
+
+    private boolean isServiceMissing() {
+        Context context = getContext();
+        PackageManager pm = context.getPackageManager();
+
+        final Intent intent = new Intent("com.android.location.service.GeocodeProvider");
+        final int flags = PackageManager.MATCH_DIRECT_BOOT_AWARE
+               | PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
+        return pm.queryIntentServices(intent, flags).isEmpty();
     }
 
     public void testGetFromLocation() throws IOException, InterruptedException {
diff --git a/tests/tests/location/src/android/location/cts/GnssMeasurementWhenNoLocationTest.java b/tests/tests/location/src/android/location/cts/GnssMeasurementWhenNoLocationTest.java
index 0f4775e..32c54c9 100644
--- a/tests/tests/location/src/android/location/cts/GnssMeasurementWhenNoLocationTest.java
+++ b/tests/tests/location/src/android/location/cts/GnssMeasurementWhenNoLocationTest.java
@@ -135,7 +135,13 @@
         List<GnssMeasurementsEvent> events = mMeasurementListener.getEvents();
         Log.i(TAG, "Number of GPS measurement events received = " + events.size());
 
-        // Ensure that after getting a few (at least 2) measurements, that we still don't have
+        if (events.isEmpty()) {
+            SoftAssert.failOrWarning(isMeasurementTestStrict(), "No measurement events received",
+                    false);
+            return;  // All of the following checks rely on there being measurements
+        }
+
+        // Ensure that after getting a few (at least 2) measurement events, that we still don't have
         // location (i.e. that we got measurements before location.)  Fail, if strict, warn, if not.
         SoftAssert.failOrWarning(isMeasurementTestStrict(),
                 "Location was received before " + events.size() +
@@ -149,9 +155,8 @@
             return; // allow a (passing) return, if not strict, otherwise continue
         }
 
-        // If device is not indoors, verify
-        // 1) that we receive GPS measurements before being able to calculate the position solution
-        // 2) that mandatory fields of GnssMeasurement are in expected ranges.
+        // If device has received measurements also verify
+        // that mandatory fields of GnssMeasurement are in expected ranges.
         GnssMeasurementsEvent firstEvent = events.get(0);
         Collection<GnssMeasurement> gpsMeasurements = firstEvent.getMeasurements();
         int satelliteCount = gpsMeasurements.size();
diff --git a/tests/tests/location/src/android/location/cts/GnssNavigationMessageTest.java b/tests/tests/location/src/android/location/cts/GnssNavigationMessageTest.java
index 6988a3f..d69be8b 100644
--- a/tests/tests/location/src/android/location/cts/GnssNavigationMessageTest.java
+++ b/tests/tests/location/src/android/location/cts/GnssNavigationMessageTest.java
@@ -79,13 +79,19 @@
         mTestLocationManager
                 .registerGnssNavigationMessageCallback(mTestGnssNavigationMessageListener);
 
-        mTestGnssNavigationMessageListener.await();
+        boolean success = mTestGnssNavigationMessageListener.await();
+
         if (!mTestGnssNavigationMessageListener.verifyState()) {
             return;
         }
+        SoftAssert.failOrWarning(isMeasurementTestStrict(),
+            "Time elapsed without getting enough navigation messages."
+                + " Possibly, the test has been run deep indoors."
+                + " Consider retrying test outdoors.",
+            success);
+
 
         List<GnssNavigationMessage> events = mTestGnssNavigationMessageListener.getEvents();
-        assertTrue("No Gps Navigation Message received.", !events.isEmpty());
 
         // Verify mandatory GnssNavigationMessage field values.
         TestMeasurementUtil.verifyGnssNavMessageMandatoryField(mTestLocationManager, events);
diff --git a/tests/tests/location/src/android/location/cts/SoftAssert.java b/tests/tests/location/src/android/location/cts/SoftAssert.java
index 3913149..4349f07 100644
--- a/tests/tests/location/src/android/location/cts/SoftAssert.java
+++ b/tests/tests/location/src/android/location/cts/SoftAssert.java
@@ -156,7 +156,9 @@
         if (testIsStrict) {
             Assert.assertTrue(message, condition);
         } else {
-            failAsWarning("", message);
+            if (!condition) {
+                failAsWarning("", message);
+            }
         }
     }
 
diff --git a/tests/tests/location/src/android/location/cts/TestGnssMeasurementListener.java b/tests/tests/location/src/android/location/cts/TestGnssMeasurementListener.java
index 1809bfb..4d95a52 100644
--- a/tests/tests/location/src/android/location/cts/TestGnssMeasurementListener.java
+++ b/tests/tests/location/src/android/location/cts/TestGnssMeasurementListener.java
@@ -58,7 +58,9 @@
         // Only count measurement events with more than one actual Measurement in them
         // (not just clock)
         if (event.getMeasurements().size() > 0) {
-            mMeasurementsEvents.add(event);
+            synchronized(mMeasurementsEvents) {
+                mMeasurementsEvents.add(event);
+            }
             mCountDownLatch.countDown();
         }
     }
@@ -120,11 +122,15 @@
     }
 
     /**
-     * Get list of GPS Measurements Events.
+     * Get the current list of GPS Measurements Events.
      *
-     * @return mMeasurementsEvents list of GPS Measurements Events
+     * @return the current list of GPS Measurements Events
      */
     public List<GnssMeasurementsEvent> getEvents() {
-        return mMeasurementsEvents;
+        synchronized(mMeasurementsEvents) {
+            List<GnssMeasurementsEvent> clone = new ArrayList<>();
+            clone.addAll(mMeasurementsEvents);
+            return clone;
+        }
     }
 }
diff --git a/tests/tests/location/src/android/location/cts/TestGnssNavigationMessageListener.java b/tests/tests/location/src/android/location/cts/TestGnssNavigationMessageListener.java
index 068c4cb..a2f12de 100644
--- a/tests/tests/location/src/android/location/cts/TestGnssNavigationMessageListener.java
+++ b/tests/tests/location/src/android/location/cts/TestGnssNavigationMessageListener.java
@@ -21,8 +21,8 @@
 import android.location.GnssNavigationMessage;
 import android.util.Log;
 
-import java.util.ArrayList;
 import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 
@@ -45,7 +45,7 @@
         mTag = tag;
         mCountDownLatch = new CountDownLatch(1);
         mEventsToCollect = eventsToCollect;
-        mEvents = new ArrayList<>(eventsToCollect);
+        mEvents = new CopyOnWriteArrayList<GnssNavigationMessage>();
     }
 
     @Override
diff --git a/tests/tests/location/src/android/location/cts/TestMeasurementUtil.java b/tests/tests/location/src/android/location/cts/TestMeasurementUtil.java
index ec0ceae..662a1fd 100644
--- a/tests/tests/location/src/android/location/cts/TestMeasurementUtil.java
+++ b/tests/tests/location/src/android/location/cts/TestMeasurementUtil.java
@@ -28,8 +28,11 @@
 
 import junit.framework.Assert;
 
+import java.util.Arrays;
+import java.util.HashSet;
 import java.util.List;
 import java.util.concurrent.TimeUnit;
+import java.util.Set;
 
 /**
  * Helper class for GnssMeasurement Tests.
@@ -54,6 +57,21 @@
 
     private static final int YEAR_2016 = 2016;
 
+    // The valid Gnss navigation message type as listed in
+    // android/hardware/libhardware/include/hardware/gps.h
+    public static final Set<Integer> GNSS_NAVIGATION_MESSAGE_TYPE =
+        new HashSet<Integer>(Arrays.asList(
+            GnssNavigationMessage.TYPE_GPS_L1CA,
+            GnssNavigationMessage.TYPE_GPS_L2CNAV,
+            GnssNavigationMessage.TYPE_GPS_L5CNAV,
+            GnssNavigationMessage.TYPE_GPS_CNAV2,
+            GnssNavigationMessage.TYPE_GLO_L1CA,
+            GnssNavigationMessage.TYPE_BDS_D1,
+            GnssNavigationMessage.TYPE_BDS_D2,
+            GnssNavigationMessage.TYPE_GAL_I,
+            GnssNavigationMessage.TYPE_GAL_F
+        ));
+
     /**
      * Check if test can be run on the current device.
      *
@@ -65,15 +83,12 @@
                                                     String testTag,
                                                     int minHardwareYear,
                                                     boolean isCtsVerifier) {
-       // TODO(sumitk): Enable this check once api 24 for N is avaiable.
-       /*
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
             Log.i(TAG, "This test is designed to work on N or newer. " +
                     "Test is being skipped because the platform version is being run in " +
                     Build.VERSION.SDK_INT);
             return false;
         }
-        */
 
         // If device does not have a GPS, skip the test.
         PackageManager pm = testLocationManager.getContext().getPackageManager();
@@ -674,9 +689,9 @@
         SoftAssert softAssert = new SoftAssert(TAG);
         for (GnssNavigationMessage message : events) {
             int type = message.getType();
-            softAssert.assertTrue("Gnss Navigation Message Type:expected [0x0101 - 0x0104]," +
-                            " actual = " + type,
-                    type >= 0x0101 && type <= 0x0104);
+            softAssert.assertTrue("Gnss Navigation Message Type:expected [" +
+                getGnssNavMessageTypes() + "] actual = " + type,
+                    GNSS_NAVIGATION_MESSAGE_TYPE.contains(type));
 
             int gnssYearOfHardware = testLocationManager.getLocationManager().getGnssYearOfHardware();
             if (gnssYearOfHardware >= YEAR_2016) {
@@ -701,4 +716,14 @@
         }
         softAssert.assertAll();
     }
+
+    private static String getGnssNavMessageTypes() {
+        StringBuilder typesStr = new StringBuilder();
+        for (int type : GNSS_NAVIGATION_MESSAGE_TYPE) {
+            typesStr.append(String.format("0x%04X", type));
+            typesStr.append(", ");
+        }
+
+        return typesStr.length() > 2 ? typesStr.substring(0, typesStr.length() - 2) : "";
+    }
 }
diff --git a/tests/tests/media/src/android/media/cts/AdaptivePlaybackTest.java b/tests/tests/media/src/android/media/cts/AdaptivePlaybackTest.java
index 23fa502..4b5b1d7 100644
--- a/tests/tests/media/src/android/media/cts/AdaptivePlaybackTest.java
+++ b/tests/tests/media/src/android/media/cts/AdaptivePlaybackTest.java
@@ -1092,7 +1092,9 @@
             boolean sawOutputEos = false;
             int deadDecoderCounter = 0;
             ArrayList<String> frames = new ArrayList<String>();
-            while ((waitForEos && !sawOutputEos) || frameIx < frameEndIx) {
+            String buf = null;
+            // After all input buffers are queued, dequeue as many output buffers as possible.
+            while ((waitForEos && !sawOutputEos) || frameIx < frameEndIx || buf != null) {
                 if (frameIx < frameEndIx) {
                     if (queueInputBuffer(
                             media,
@@ -1103,7 +1105,7 @@
                     }
                 }
 
-                String buf = dequeueAndReleaseOutputBuffer(info);
+                buf = dequeueAndReleaseOutputBuffer(info);
                 if (buf != null) {
                     // Some decoders output a 0-sized buffer at the end. Disregard those.
                     if (info.size > 0) {
diff --git a/tests/tests/media/src/android/media/cts/ClearKeySystemTest.java b/tests/tests/media/src/android/media/cts/ClearKeySystemTest.java
index 7d5742e..69f3ee8 100644
--- a/tests/tests/media/src/android/media/cts/ClearKeySystemTest.java
+++ b/tests/tests/media/src/android/media/cts/ClearKeySystemTest.java
@@ -344,24 +344,25 @@
             return;
         }
 
-        IConnectionStatus wifiStatus = new WifiStatus(mContext);
-        if (!wifiStatus.isEnabled()) {
-            throw new Error("Wifi is not enabled, please enable Wifi to run tests.");
+        IConnectionStatus connectionStatus = new ConnectionStatus(mContext);
+        if (!connectionStatus.isAvailable()) {
+            throw new Error("Network is not available, reason: " +
+                    connectionStatus.getNotConnectedReason());
         }
-        // If Wifi is not connected, recheck the status a few times.
+
+        // If device is not online, recheck the status a few times.
         int retries = 0;
-        while (!wifiStatus.isConnected()) {
+        while (!connectionStatus.isConnected()) {
             if (retries++ >= CONNECTION_RETRIES) {
-                wifiStatus.printConnectionInfo();
-                throw new Error("Wifi is not connected, reason: " +
-                        wifiStatus.getNotConnectedReason());
+                throw new Error("Device is not online, reason: " +
+                        connectionStatus.getNotConnectedReason());
             }
             try {
                 Thread.sleep(100);
             } catch (InterruptedException e) {
             }
         }
-        wifiStatus.testConnection(videoUrl);
+        connectionStatus.testConnection(videoUrl);
 
         mSessionId = openSession(drm);
         mMediaCodecPlayer = new MediaCodecClearKeyPlayer(
diff --git a/tests/tests/media/src/android/media/cts/WifiStatus.java b/tests/tests/media/src/android/media/cts/ConnectionStatus.java
similarity index 81%
rename from tests/tests/media/src/android/media/cts/WifiStatus.java
rename to tests/tests/media/src/android/media/cts/ConnectionStatus.java
index c2577f1..407e553 100644
--- a/tests/tests/media/src/android/media/cts/WifiStatus.java
+++ b/tests/tests/media/src/android/media/cts/ConnectionStatus.java
@@ -20,8 +20,6 @@
 import android.net.ConnectivityManager;
 import android.net.NetworkInfo;
 import android.net.Uri;
-import android.net.wifi.WifiInfo;
-import android.net.wifi.WifiManager;
 import android.util.Log;
 
 import java.io.BufferedReader;
@@ -32,19 +30,17 @@
 
 /**
  * A class that implements IConnectionStatus interface
- * to report and test Wifi connection.
+ * to report and test connection status.
  */
-public class WifiStatus implements IConnectionStatus {
+public class ConnectionStatus implements IConnectionStatus {
 
-    private static final String TAG = "WifiStatus";
+    private static final String TAG = "ConnectionStatus";
 
     private ConnectivityManager mConnectivityManager;
-    private WifiManager mWifiManager;
 
-    public WifiStatus(Context context) {
+    public ConnectionStatus(Context context) {
         mConnectivityManager =
                 (ConnectivityManager)context.getSystemService(Context.CONNECTIVITY_SERVICE);
-        mWifiManager = (WifiManager)context.getSystemService(Context.WIFI_SERVICE);
     }
 
     public String getNotConnectedReason() {
@@ -52,7 +48,7 @@
         if (networkInfo != null) {
             return networkInfo.getReason();
         } else {
-            return "Cannot get network info";
+            return "Network info is not available.";
         }
     }
 
@@ -66,22 +62,6 @@
         return (networkInfo != null) && networkInfo.isConnected();
     }
 
-    public boolean isEnabled() {
-        return mWifiManager.isWifiEnabled();
-    }
-
-    public void printConnectionInfo() {
-        WifiInfo wifiInfo = mWifiManager.getConnectionInfo();
-        if (wifiInfo == null) {
-          throw new Error("Fail to get Wifi connection info");
-        }
-
-        Log.d(TAG, "ssid=" + wifiInfo.getSSID());
-        Log.d(TAG, "frequency=" + wifiInfo.getFrequency() + " " + WifiInfo.FREQUENCY_UNITS);
-        Log.d(TAG, "rssi=" + wifiInfo.getRssi() + " dBm");
-        Log.d(TAG, "link speed=" + wifiInfo.getLinkSpeed() + " " + WifiInfo.LINK_SPEED_UNITS);
-    }
-
     /**
      * Print lines.
      *
diff --git a/tests/tests/media/src/android/media/cts/IConnectionStatus.java b/tests/tests/media/src/android/media/cts/IConnectionStatus.java
index 39e2781..17d5ff7 100644
--- a/tests/tests/media/src/android/media/cts/IConnectionStatus.java
+++ b/tests/tests/media/src/android/media/cts/IConnectionStatus.java
@@ -29,10 +29,6 @@
 
     public boolean isConnected();
 
-    public boolean isEnabled();
-
-    public void printConnectionInfo();
-
     public void testConnection(Uri uri);
 }
 
diff --git a/tests/tests/media/src/android/media/cts/MediaRecorderTest.java b/tests/tests/media/src/android/media/cts/MediaRecorderTest.java
index dffb653..32d9eaa 100644
--- a/tests/tests/media/src/android/media/cts/MediaRecorderTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaRecorderTest.java
@@ -424,17 +424,6 @@
         return 1;
     }
 
-    public void testRecordAudioFromAudioSourceUnprocessed() throws Exception {
-        if (!hasMicrophone()) {
-            return; // skip
-        }
-        mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.UNPROCESSED);
-        mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.DEFAULT);
-        mMediaRecorder.setOutputFile(OUTPUT_PATH);
-        mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.DEFAULT);
-        recordMedia(MAX_FILE_SIZE, mOutFile);
-    }
-
     public void testGetAudioSourceMax() throws Exception {
         final int max = MediaRecorder.getAudioSourceMax();
         assertTrue(MediaRecorder.AudioSource.DEFAULT <= max);
diff --git a/tests/tests/mediastress/preconditions/src/android/mediastress/cts/preconditions/MediaPreparer.java b/tests/tests/mediastress/preconditions/src/android/mediastress/cts/preconditions/MediaPreparer.java
index a9aabcf..f5d633b 100644
--- a/tests/tests/mediastress/preconditions/src/android/mediastress/cts/preconditions/MediaPreparer.java
+++ b/tests/tests/mediastress/preconditions/src/android/mediastress/cts/preconditions/MediaPreparer.java
@@ -314,8 +314,8 @@
             BuildError, DeviceNotAvailableException {
 
         if (mSkipMediaDownload) {
-            // skip this precondition
-            return;
+            logInfo("Skipping media preparation");
+            return; // skip this precondition
         }
 
         setMountPoint(device);
diff --git a/tests/tests/networksecurityconfig/networksecurityconfig-basic-domain/res/xml/network_security_config.xml b/tests/tests/networksecurityconfig/networksecurityconfig-basic-domain/res/xml/network_security_config.xml
index 6d8565c..d36462b 100644
--- a/tests/tests/networksecurityconfig/networksecurityconfig-basic-domain/res/xml/network_security_config.xml
+++ b/tests/tests/networksecurityconfig/networksecurityconfig-basic-domain/res/xml/network_security_config.xml
@@ -6,6 +6,7 @@
   </base-config>
   <domain-config>
     <domain>android.com</domain>
+    <domain>www.android.com</domain>
     <trust-anchors>
       <certificates src="system" />
       <certificates src="user" />
diff --git a/tests/tests/os/src/android/os/cts/SecurityPatchTest.java b/tests/tests/os/src/android/os/cts/SecurityPatchTest.java
index 4608f22..4531aa6 100644
--- a/tests/tests/os/src/android/os/cts/SecurityPatchTest.java
+++ b/tests/tests/os/src/android/os/cts/SecurityPatchTest.java
@@ -32,7 +32,7 @@
     private static final String SECURITY_PATCH_DATE_ERROR =
             "ro.build.version.security_patch should be \"%d-%02d\" or later. Found \"%s\"";
     private static final int SECURITY_PATCH_YEAR = 2016;
-    private static final int SECURITY_PATCH_MONTH = 8;
+    private static final int SECURITY_PATCH_MONTH = 12;
 
     private boolean mSkipTests = false;
 
diff --git a/tests/tests/provider/src/android/provider/cts/MediaStore_Video_MediaTest.java b/tests/tests/provider/src/android/provider/cts/MediaStore_Video_MediaTest.java
index 5ec765c..485b39c 100644
--- a/tests/tests/provider/src/android/provider/cts/MediaStore_Video_MediaTest.java
+++ b/tests/tests/provider/src/android/provider/cts/MediaStore_Video_MediaTest.java
@@ -228,6 +228,9 @@
 
     private Uri insertVideo(Context context) throws IOException {
         File file = new File(Environment.getExternalStorageDirectory(), "testVideo.3gp");
+        // clean up any potential left over entries from a previous aborted run
+        cleanExternalMediaFile(file.getAbsolutePath());
+
         new FileCopyHelper(context).copyToExternalStorage(R.raw.testvideo, file);
 
         ContentValues values = new ContentValues();
diff --git a/tests/tests/security/Android.mk b/tests/tests/security/Android.mk
index 1f7b601..f2b4470 100644
--- a/tests/tests/security/Android.mk
+++ b/tests/tests/security/Android.mk
@@ -55,7 +55,8 @@
 		libstagefright_foundation
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)\
-                   src/android/security/cts/activity/ISecureRandomService.aidl
+                   src/android/security/cts/activity/ISecureRandomService.aidl\
+                   aidl/android/security/cts/IIsolatedService.aidl
 
 LOCAL_PACKAGE_NAME := CtsSecurityTestCases
 
diff --git a/tests/tests/security/AndroidManifest.xml b/tests/tests/security/AndroidManifest.xml
index 7a67b08..7468d68 100644
--- a/tests/tests/security/AndroidManifest.xml
+++ b/tests/tests/security/AndroidManifest.xml
@@ -35,6 +35,10 @@
                  android:isolatedProcess="true"
                  android:exported="true"/>
 
+        <service android:name="android.security.cts.IsolatedService"
+                 android:process=":Isolated"
+                 android:isolatedProcess="true"/>
+
         <service android:name="android.security.cts.activity.SecureRandomService"
                  android:process=":secureRandom"/>
     </application>
diff --git a/tests/tests/security/aidl/android/security/cts/IIsolatedService.aidl b/tests/tests/security/aidl/android/security/cts/IIsolatedService.aidl
new file mode 100644
index 0000000..16db0fe
--- /dev/null
+++ b/tests/tests/security/aidl/android/security/cts/IIsolatedService.aidl
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.security.cts;
+
+interface IIsolatedService {
+    String[] getCachedSystemServices();
+    IBinder getSystemService(String serviceName);
+}
diff --git a/tests/tests/security/res/raw/bug_27855419.mp4 b/tests/tests/security/res/raw/bug_27855419.mp4
new file mode 100644
index 0000000..51bfa74
--- /dev/null
+++ b/tests/tests/security/res/raw/bug_27855419.mp4
Binary files differ
diff --git a/tests/tests/security/src/android/security/cts/IsolatedProcessTest.java b/tests/tests/security/src/android/security/cts/IsolatedProcessTest.java
new file mode 100644
index 0000000..ac264d0
--- /dev/null
+++ b/tests/tests/security/src/android/security/cts/IsolatedProcessTest.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.security.cts;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.security.cts.IIsolatedService;
+import android.security.cts.IsolatedService;
+import android.test.AndroidTestCase;
+import android.util.Log;
+import com.android.internal.util.ArrayUtils;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import junit.framework.Assert;
+
+public class IsolatedProcessTest extends AndroidTestCase {
+    static final String TAG = IsolatedProcessTest.class.getSimpleName();
+
+    private static final long BIND_SERVICE_TIMEOUT = 5000;
+
+    // No service other than these should be visible to an isolated process
+    private static final String[] SERVICES_ALLOWED_TO_ISOLATED_PROCESS = {
+            "package",
+            Context.ACTIVITY_SERVICE
+    };
+    // Arbitrary set of services to test accessibility from an isolated process
+    private static final String[] RESTRICTED_SERVICES_TO_TEST = {
+            Context.ALARM_SERVICE,
+            Context.WINDOW_SERVICE,
+            Context.POWER_SERVICE
+    };
+
+    private CountDownLatch mLatch;
+    private IIsolatedService mService;
+    private final ServiceConnection mServiceConnection = new ServiceConnection() {
+
+        @Override
+        public void onServiceDisconnected(ComponentName name) {
+            Log.e(TAG, "Isolated service " + name + " died abruptly");
+        }
+
+        @Override
+        public void onServiceConnected(ComponentName name, IBinder service) {
+            mService = IIsolatedService.Stub.asInterface(service);
+            mLatch.countDown();
+        }
+    };
+
+    @Override
+    public void setUp() throws InterruptedException {
+        mLatch = new CountDownLatch(1);
+        Intent serviceIntent = new Intent(mContext, IsolatedService.class);
+        mContext.bindService(serviceIntent, mServiceConnection, Context.BIND_AUTO_CREATE);
+        Assert.assertTrue("Timed out while waiting to bind to isolated service",
+                mLatch.await(BIND_SERVICE_TIMEOUT, TimeUnit.MILLISECONDS));
+    }
+
+    public void testGetCachedServicesFromIsolatedService() throws RemoteException {
+        String[] cachedServices = mService.getCachedSystemServices();
+        for (String serviceName : cachedServices) {
+            Assert.assertTrue(serviceName + " should not be accessbible from an isolated process",
+                    ArrayUtils.contains(SERVICES_ALLOWED_TO_ISOLATED_PROCESS, serviceName));
+        }
+    }
+
+    public void testGetServiceFromIsolatedService() throws RemoteException {
+        for (String serviceName : RESTRICTED_SERVICES_TO_TEST) {
+            IBinder service = mService.getSystemService(serviceName);
+            Assert.assertNull(serviceName + " should not be accessible from an isolated process",
+                    service);
+        }
+    }
+
+    @Override
+    public void tearDown() {
+        mContext.unbindService(mServiceConnection);
+    }
+
+}
diff --git a/tests/tests/security/src/android/security/cts/IsolatedService.java b/tests/tests/security/src/android/security/cts/IsolatedService.java
new file mode 100644
index 0000000..28d2a65
--- /dev/null
+++ b/tests/tests/security/src/android/security/cts/IsolatedService.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.security.cts;
+
+import android.app.Service;
+import android.content.Intent;
+import android.os.IBinder;
+import android.util.Log;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Map;
+
+public class IsolatedService extends Service {
+
+    private static final String TAG = IsolatedService.class.getSimpleName();
+    private static final String SERVICE_MANAGER_CLASS_NAME = "android.os.ServiceManager";
+    private static final String SERVICE_MANAGER_INTERNAL_CACHE_NAME = "sCache";
+    private static final String GET_SERVICE_METHOD_NAME = "getService";
+
+    private String[] getServicesCachedInServiceManager() {
+        ArrayList<String> cachedServices = new ArrayList<String>();
+        try {
+            Class<?> serviceManager = Class.forName(SERVICE_MANAGER_CLASS_NAME);
+            Field cacheField = serviceManager.getDeclaredField(SERVICE_MANAGER_INTERNAL_CACHE_NAME);
+            cacheField.setAccessible(true);
+            HashMap<String, IBinder> sCache = (HashMap<String, IBinder>) cacheField.get(null);
+            for (Map.Entry<String, IBinder> serviceEntry : sCache.entrySet()) {
+                if (serviceEntry.getValue() != null) {
+                    cachedServices.add(serviceEntry.getKey());
+                }
+            }
+        } catch (ClassCastException | ReflectiveOperationException exc) {
+            Log.w(TAG, "Unable to retrieve service manager cache via reflection ", exc);
+        }
+        return cachedServices.toArray(new String[cachedServices.size()]);
+    }
+
+    private IBinder getServiceFromServiceManager(String serviceName) {
+        try {
+            Class<?> serviceManager = Class.forName(SERVICE_MANAGER_CLASS_NAME);
+            Method getServiceMethod =
+                    serviceManager.getDeclaredMethod(GET_SERVICE_METHOD_NAME, String.class);
+            IBinder service = (IBinder) getServiceMethod.invoke(null, serviceName);
+            return service;
+        } catch (ClassCastException | ReflectiveOperationException exc) {
+            Log.w(TAG, "Unable to call ServiceManager.getService() ", exc);
+        }
+        return null;
+    }
+
+    private final IIsolatedService.Stub mBinder = new IIsolatedService.Stub() {
+
+        public String[] getCachedSystemServices() {
+            return getServicesCachedInServiceManager();
+        }
+
+        public IBinder getSystemService(String serviceName) {
+            return getServiceFromServiceManager(serviceName);
+        }
+    };
+
+    @Override
+    public IBinder onBind(Intent intent) {
+        return mBinder;
+    }
+}
diff --git a/tests/tests/security/src/android/security/cts/StagefrightTest.java b/tests/tests/security/src/android/security/cts/StagefrightTest.java
index f939eab..8205c22 100644
--- a/tests/tests/security/src/android/security/cts/StagefrightTest.java
+++ b/tests/tests/security/src/android/security/cts/StagefrightTest.java
@@ -162,6 +162,10 @@
         doStagefrightTestMediaPlayer(R.raw.bug_14388161);
     }
 
+    public void testStagefright_bug_27855419_CVE_2016_2463() throws Exception {
+        doStagefrightTest(R.raw.bug_27855419);
+    }
+
     private void doStagefrightTest(final int rid) throws Exception {
         doStagefrightTestMediaPlayer(rid);
         doStagefrightTestMediaCodec(rid);
diff --git a/tests/tests/telecom/src/android/telecom/cts/BaseTelecomTestWithMockServices.java b/tests/tests/telecom/src/android/telecom/cts/BaseTelecomTestWithMockServices.java
index add19d2..3f3a5a2 100644
--- a/tests/tests/telecom/src/android/telecom/cts/BaseTelecomTestWithMockServices.java
+++ b/tests/tests/telecom/src/android/telecom/cts/BaseTelecomTestWithMockServices.java
@@ -73,7 +73,7 @@
             .addSupportedUriScheme(PhoneAccount.SCHEME_VOICEMAIL)
             .build();
 
-    private static int sCounter = 9999;
+    private static int sCounter = 5549999;
 
     Context mContext;
     TelecomManager mTelecomManager;
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/ActivityTestBase.java b/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/ActivityTestBase.java
index 458f69b..ae915db 100644
--- a/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/ActivityTestBase.java
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/ActivityTestBase.java
@@ -125,7 +125,6 @@
     }
 
     public Bitmap takeScreenshot(Point testOffset) {
-        getInstrumentation().waitForIdleSync();
         Bitmap source = getInstrumentation().getUiAutomation().takeScreenshot();
         return Bitmap.createBitmap(source, testOffset.x, testOffset.y, TEST_WIDTH, TEST_HEIGHT);
     }
diff --git a/tests/tests/view/src/android/view/cts/FrameMetricsListenerTest.java b/tests/tests/view/src/android/view/cts/FrameMetricsListenerTest.java
index 651bfbc..dac3a1922 100644
--- a/tests/tests/view/src/android/view/cts/FrameMetricsListenerTest.java
+++ b/tests/tests/view/src/android/view/cts/FrameMetricsListenerTest.java
@@ -27,6 +27,7 @@
 import android.os.SystemClock;
 import android.test.ActivityInstrumentationTestCase2;
 import android.util.Log;
+import android.view.cts.util.ViewTestUtils;
 import android.view.FrameMetrics;
 import android.view.View;
 import android.view.Window;
@@ -88,16 +89,8 @@
             }
         });
 
-        mInstrumentation.waitForIdleSync();
+        scrollView.postInvalidate();
 
-        runTestOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                scrollView.fling(-100);
-            }
-        });
-
-        mInstrumentation.waitForIdleSync();
         new PollingCheck() {
             @Override
             protected boolean check() {
@@ -109,21 +102,17 @@
             @Override
             public void run() {
                 mActivity.getWindow().removeOnFrameMetricsAvailableListener(listener);
-                data.clear();
             }
         });
 
-        mInstrumentation.waitForIdleSync();
+        data.clear();
 
-        runTestOnUiThread(new Runnable() {
-            @Override
-            public void run() {
-                scrollView.fling(100);
-                assertEquals(0, data.size());
-            }
-        });
-
-        mInstrumentation.waitForIdleSync();
+        // Produce 5 frames and assert no metric listeners were invoked
+        for (int i = 0; i < 5; i++) {
+            ViewTestUtils.runOnMainAndDrawSync(mInstrumentation, mActivity,
+                                               () -> { scrollView.invalidate(); });
+        }
+        assertEquals(0, data.size());
     }
 
     public void testMultipleListeners() throws Throwable {
@@ -245,5 +234,3 @@
         });
     }
 }
-
-
diff --git a/tests/tests/view/src/android/view/inputmethod/cts/InputMethodInfoTest.java b/tests/tests/view/src/android/view/inputmethod/cts/InputMethodInfoTest.java
old mode 100644
new mode 100755
index 3e071b6..b896229
--- a/tests/tests/view/src/android/view/inputmethod/cts/InputMethodInfoTest.java
+++ b/tests/tests/view/src/android/view/inputmethod/cts/InputMethodInfoTest.java
@@ -25,7 +25,10 @@
 import android.content.pm.ServiceInfo;
 import android.content.res.Resources;
 import android.os.Parcel;
+import android.os.ParcelFileDescriptor;
+import android.support.test.InstrumentationRegistry;
 import android.test.AndroidTestCase;
+import android.text.TextUtils;
 import android.util.Printer;
 import android.view.inputmethod.InputMethod;
 import android.view.inputmethod.InputMethodInfo;
@@ -34,7 +37,10 @@
 
 import org.xmlpull.v1.XmlPullParserException;
 
+import java.io.BufferedReader;
 import java.io.IOException;
+import java.io.InputStreamReader;
+import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
@@ -243,6 +249,11 @@
             return;
         }
 
+        if (!TextUtils.equals("native", getFbeMode())) {
+            // Skip the test unless the device is in native FBE mode.
+            return;
+        }
+
         final InputMethodManager imm = mContext.getSystemService(InputMethodManager.class);
         final List<InputMethodInfo> imis = imm.getInputMethodList();
         boolean hasEncryptionAwareInputMethod = false;
@@ -268,4 +279,22 @@
         public void println(String x) {
         }
     }
+
+    private String getFbeMode() {
+        try (ParcelFileDescriptor.AutoCloseInputStream in =
+                     new ParcelFileDescriptor.AutoCloseInputStream(
+                             InstrumentationRegistry
+                            .getInstrumentation()
+                            .getUiAutomation()
+                            .executeShellCommand("sm get-fbe-mode"))) {
+            try (BufferedReader br =
+                         new BufferedReader(new InputStreamReader(in, StandardCharsets.UTF_8))) {
+                // Assume that the output of "sm get-fbe-mode" is always one-line.
+                final String line = br.readLine();
+                return line != null ? line.trim() : "";
+            }
+        } catch (IOException e) {
+            return "";
+        }
+    }
 }
diff --git a/tests/tests/webkit/src/android/webkit/cts/WebViewTest.java b/tests/tests/webkit/src/android/webkit/cts/WebViewTest.java
index e3730a6..9151783 100755
--- a/tests/tests/webkit/src/android/webkit/cts/WebViewTest.java
+++ b/tests/tests/webkit/src/android/webkit/cts/WebViewTest.java
@@ -115,6 +115,14 @@
      * assumed that no scrolling will happen.
      */
     private static final long MIN_SCROLL_WAIT_MS = 1000;
+
+    /**
+     * This is the minimum number of milliseconds to wait for findAll to
+     * find all the matches. If matches are not found, the Listener would
+     * call findAll again until it times out.
+     */
+    private static final long MIN_FIND_WAIT_MS = 3000;
+
     /**
      * Once scrolling has started, this is the interval that scrolling
      * is checked to see if there is a change. If no scrolling change
@@ -1456,18 +1464,34 @@
 
     private static class WaitForFindResultsListener extends FutureTask<Integer>
             implements WebView.FindListener {
-        public WaitForFindResultsListener() {
+        private final WebViewOnUiThread mWebViewOnUiThread;
+        private final int mMatchesWanted;
+        private final String mStringWanted;
+        private final boolean mRetry;
+
+        public WaitForFindResultsListener(
+                WebViewOnUiThread wv, String wanted, int matches, boolean retry) {
             super(new Runnable() {
                 @Override
                 public void run() { }
             }, null);
+            mWebViewOnUiThread = wv;
+            mMatchesWanted = matches;
+            mStringWanted = wanted;
+            mRetry = retry;
         }
 
         @Override
         public void onFindResultReceived(int activeMatchOrdinal, int numberOfMatches,
                 boolean isDoneCounting) {
             if (isDoneCounting) {
-                set(numberOfMatches);
+                //If mRetry set to true and matches aren't equal, call findAll again
+                if (mRetry && numberOfMatches != mMatchesWanted) {
+                    mWebViewOnUiThread.findAll(mStringWanted);
+                }
+                else {
+                    set(numberOfMatches);
+                }
             }
         }
     }
@@ -1488,15 +1512,10 @@
         mOnUiThread.loadDataAndWaitForCompletion("<html><body>" + p
                 + "</body></html>", "text/html", null);
 
-        WaitForFindResultsListener l = new WaitForFindResultsListener();
-        int previousScrollY = mOnUiThread.getScrollY();
-        mOnUiThread.pageDown(true);
-        // Wait for content fully loaded.
-        waitForScrollingComplete(previousScrollY);
+        WaitForFindResultsListener l = new WaitForFindResultsListener(mOnUiThread, "find", 2, true);
         mOnUiThread.setFindListener(l);
         mOnUiThread.findAll("find");
-
-        assertEquals(2, l.get().intValue());
+        assertEquals(2, l.get(MIN_FIND_WAIT_MS, TimeUnit.MILLISECONDS).intValue());
     }
 
     public void testFindNext() throws Throwable {
@@ -1513,10 +1532,13 @@
                 "Find all instances of a word on the page and highlight them.</p>";
 
         mOnUiThread.loadDataAndWaitForCompletion("<html><body>" + p + p + "</body></html>", "text/html", null);
+        WaitForFindResultsListener l = new WaitForFindResultsListener(mOnUiThread, "all", 2, true);
+        mOnUiThread.setFindListener(l);
 
-        // highlight all the strings found
+        // highlight all the strings found and wait for all the matches to be found
         mOnUiThread.findAll("all");
-        getInstrumentation().waitForIdleSync();
+        l.get(MIN_FIND_WAIT_MS, TimeUnit.MILLISECONDS);
+        mOnUiThread.setFindListener(null);
 
         int previousScrollY = mOnUiThread.getScrollY();
 
diff --git a/tests/tests/widget/src/android/widget/cts/TextViewFadingEdgeTest.java b/tests/tests/widget/src/android/widget/cts/TextViewFadingEdgeTest.java
index e627c2b..4d976eb 100644
--- a/tests/tests/widget/src/android/widget/cts/TextViewFadingEdgeTest.java
+++ b/tests/tests/widget/src/android/widget/cts/TextViewFadingEdgeTest.java
@@ -18,6 +18,7 @@
 
 import android.cts.util.PollingCheck;
 import android.test.ActivityInstrumentationTestCase2;
+import android.util.TypedValue;
 import android.view.ViewGroup;
 import android.widget.FrameLayout;
 
@@ -38,8 +39,10 @@
     private static final String LONG_LTR_STRING = "start start1 middle middle1 end end1";
     private static final String SHORT_RTL_STRING = "ت";
     private static final String SHORT_LTR_STRING = "s";
-    public static final int ANY_PADDING = 15;
-    public static final int ANY_FADE_LENGTH = 60;
+    public static final int ANY_PADDING = 20;
+    public static final int ANY_FADE_LENGTH = 20;
+    public static final int TEXT_SIZE = 20;
+    public static final int WIDTH = 100;
 
     private static final TestCase[] TEST_DATA = {
             // no fade - fading disabled
@@ -144,10 +147,10 @@
             int gravity, int textAlignment, boolean scrollToMiddle) {
         final MockTextView textView = new MockTextView(getActivity());
         textView.setSingleLine(true);
-        textView.setTextSize(30);
+        textView.setTextSize(TypedValue.COMPLEX_UNIT_PX, TEXT_SIZE);
         textView.setPadding(ANY_PADDING, ANY_PADDING, ANY_PADDING, ANY_PADDING);
         textView.setFadingEdgeLength(ANY_FADE_LENGTH);
-        textView.setLayoutParams(new ViewGroup.LayoutParams(300,
+        textView.setLayoutParams(new ViewGroup.LayoutParams(WIDTH,
                 ViewGroup.LayoutParams.WRAP_CONTENT));
         textView.setHorizontalFadingEdgeEnabled(horizontalFadingEnabled);
         textView.setText(text);
diff --git a/tools/cts-media/get_achievable_rates.py b/tools/cts-media/get_achievable_rates.py
index 9dde743..369a982 100755
--- a/tools/cts-media/get_achievable_rates.py
+++ b/tools/cts-media/get_achievable_rates.py
@@ -14,36 +14,9 @@
 # limitations under the License.
 #
 
-import argparse, math, re, sys
+import argparse, json, math, re, sys, zipfile
 import xml.etree.ElementTree as ET
 from collections import defaultdict, namedtuple
-import itertools
-
-
-def createLookup(values, key):
-  """Creates a lookup table for a collection of values based on keys.
-
-  Arguments:
-    values: a collection of arbitrary values. Must be iterable.
-    key: a function of one argument that returns the key for a value.
-
-  Returns:
-    A dict mapping keys (as generated by the key argument) to lists of
-    values. All values in the lists have the same key, and are in the order
-    they appeared in the collection.
-  """
-  lookup = defaultdict(list)
-  for v in values:
-    lookup[key(v)].append(v)
-  return lookup
-
-
-def _intify(value):
-  """Returns a value converted to int if possible, else the original value."""
-  try:
-    return int(value)
-  except ValueError:
-    return value
 
 
 class Size(namedtuple('Size', ['width', 'height'])):
@@ -51,43 +24,50 @@
   def __str__(self):
     return '%dx%d' % (self.width, self.height)
 
+def nicekey(v):
+  """Returns a nicer sort key for sorting strings.
 
-class _VideoResultBase(object):
-  """Helper methods for results. Not for use by applications.
+  This sorts using lower case, with numbers in numerical order first."""
+  key = []
+  num = False
+  for p in re.split('(\d+)', v.lower()):
+    if num:
+      key.append(('0', int(p)))
+    elif p:
+      key.append((p, 0))
+    num = not num
+  return key + [(v, 0)]
 
-  Attributes:
-    codec: The name of the codec (string) or None
-    size: Size representing the video size or None
-    mime: The mime-type of the codec (string) or None
-    rates: The measured achievable frame rates
-    is_decoder: True iff codec is a decoder.
-  """
+def nice(v):
+  """Returns a nicer representation for objects in debug messages.
 
-  def __init__(self, is_decoder):
-    self.codec = None
-    self.mime = None
-    self.size = None
-    self._rates_from_failure = []
-    self._rates_from_message = []
-    self.is_decoder = is_decoder
+  Dictionaries are sorted, size is WxH, unicode removed, and floats have 1 digit precision."""
+  if isinstance(v, dict):
+    return 'dict(' + ', '.join(k + '=' + nice(v) for k, v in sorted(v.items(), key=lambda i: nicekey(i[0]))) + ')'
+  if isinstance(v, str):
+    return repr(v)
+  if isinstance(v, int):
+    return str(v)
+  if isinstance(v, Size):
+    return repr(str(v))
+  if isinstance(v, float):
+    return '%.1f' % v
+  if isinstance(v, type(u'')):
+    return repr(str(v))
+  raise ValueError(v)
 
-  def _inited(self):
-    """Returns true iff codec, mime and size was set."""
-    return None not in (self.codec, self.mime, self.size)
-
-  def __len__(self):
-    # don't report any result if codec name, mime type and size is unclear
-    if not self._inited():
-      return 0
-    return len(self.rates)
-
-  @property
-  def rates(self):
-    return self._rates_from_failure or self._rates_from_message
+class ResultParser:
+  @staticmethod
+  def _intify(value):
+    """Returns a value converted to int if possible, else the original value."""
+    try:
+      return int(value)
+    except ValueError:
+      return value
 
   def _parseDict(self, value):
     """Parses a MediaFormat from its string representation sans brackets."""
-    return dict((k, _intify(v))
+    return dict((k, self._intify(v))
                 for k, v in re.findall(r'([^ =]+)=([^ [=]+(?:|\[[^\]]+\]))(?:, |$)', value))
 
   def _cleanFormat(self, format):
@@ -110,293 +90,270 @@
       if key.endswith('Format'):
         self._cleanFormat(value)
     else:
-      value = _intify(value)
+      value = self._intify(value)
     return key, value
 
-  def _parseValuesFromBracket(self, line):
-    """Returns the values enclosed in brackets without the brackets.
 
-    Parses a line matching the pattern "<tag>: [<values>]" and returns <values>.
+def perc(data, p, fn=round):
+  """Returns a percentile value from a sorted array.
 
-    Raises:
-      ValueError: if the line does not match the pattern.
-    """
-    try:
-      return re.match(r'^[^:]+: *\[(?P<values>.*)\]\.$', line).group('values')
-    except AttributeError:
-      raise ValueError('line does not match "tag: [value]": %s' % line)
-
-  def _parseRawData(self, line):
-    """Parses the raw data line for video performance tests.
-
-    Yields:
-      Dict objects corresponding to parsed results, mapping string keys to
-      int, string or dict values.
-    """
-    try:
-      values = self._parseValuesFromBracket(line)
-      result = {}
-      for m in re.finditer(self.MESSAGE_PATTERN + r'(?P<sep>,? +|$)', values):
-        key, value = self._parsePartialResult(m)
-        result[key] = value
-        if m.group('sep') != ' ':
-          yield result
-          result = {}
-    except ValueError:
-      print >> sys.stderr, 'could not parse line %s' % repr(line)
-
-  def _tryParseMeasuredFrameRate(self, line):
-    """Parses a line starting with 'Measured frame rate:'."""
-    if line.startswith('Measured frame rate: '):
-      try:
-        values = self._parseValuesFromBracket(line)
-        values = re.split(r' *, *', values)
-        self._rates_from_failure = list(map(float, values))
-      except ValueError:
-        print >> sys.stderr, 'could not parse line %s' % repr(line)
-
-  def parse(self, test):
-    """Parses the ValueArray and FailedScene lines of a test result.
-
-    Arguments:
-      test: An ElementTree <Test> element.
-    """
-    failure = test.find('FailedScene')
-    if failure is not None:
-      trace = failure.find('StackTrace')
-      if trace is not None:
-        for line in re.split(r'[\r\n]+', trace.text):
-          self._parseFailureLine(line)
-    details = test.find('Details')
-    if details is not None:
-      for array in details.iter('ValueArray'):
-        message = array.get('message')
-        self._parseMessage(message, array)
-
-  def _parseFailureLine(self, line):
-    raise NotImplementedError
-
-  def _parseMessage(self, message, array):
-    raise NotImplementedError
-
-  def getData(self):
-    """Gets the parsed test result data.
-
-    Yields:
-       Result objects containing at least codec, size, mime and rates attributes."""
-    yield self
+  Arguments:
+    data: sorted data
+    p:    percentile value (0-100)
+    fn:   method used for rounding the percentile to an integer index in data
+  """
+  return data[int(fn((len(data) - 1) * p / 100))]
 
 
-class VideoEncoderDecoderTestResult(_VideoResultBase):
-  """Represents a result from a VideoEncoderDecoderTest performance case."""
+def genXml(data, A=None):
+  yield '<?xml version="1.0" encoding="utf-8" ?>'
+  yield '<!-- Copyright 2016 The Android Open Source Project'
+  yield ''
+  yield '     Licensed under the Apache License, Version 2.0 (the "License");'
+  yield '     you may not use this file except in compliance with the License.'
+  yield '     You may obtain a copy of the License at'
+  yield ''
+  yield '          http://www.apache.org/licenses/LICENSE-2.0'
+  yield ''
+  yield '     Unless required by applicable law or agreed to in writing, software'
+  yield '     distributed under the License is distributed on an "AS IS" BASIS,'
+  yield '     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.'
+  yield '     See the License for the specific language governing permissions and'
+  yield '     limitations under the License.'
+  yield '-->'
+  yield ''
+  yield '<MediaCodecs>'
+  last_section = None
+  from collections import namedtuple
+  Comp = namedtuple('Comp', 'is_decoder google mime name')
+  Result = namedtuple('Result', 'mn mx p95 med geo p5')
+  for comp_, cdata in sorted(data.items()):
+    comp = Comp(*comp_)
+    section = 'Decoders' if comp.is_decoder else 'Encoders'
+    if section != last_section:
+      if last_section:
+        yield '    </%s>' % last_section
+      yield '    <%s>' % section
+      last_section = section
+    yield '        <MediaCodec name="%s" type="%s" update="true">' % (comp.name, comp.mime)
+    for size, sdata in sorted(cdata.items()):
+      data = sorted(sdata)
+      N = len(data)
+      mn, mx = data[0], data[-1]
 
-  def __init__(self, unused_m):
-    super(VideoEncoderDecoderTestResult, self).__init__(is_decoder=False)
+      if N < 20 and not A.ignore:
+        raise ValueError("need at least 20 data points for %s size %s; have %s" %
+                         (comp.name, size, N))
 
-  # If a VideoEncoderDecoderTest succeeds, it provides the results in the
-  # message of a ValueArray. If fails, it provides the results in the failure
-  # using raw data. (For now it also includes some data in the ValueArrays even
-  # if it fails, which we ignore.)
+      TO = 2.2     # tolerance with margin
+      T = TO / 1.1 # tolerance without margin
 
-  def _parseFailureLine(self, line):
-    """Handles parsing a line from the failure log."""
-    self._tryParseMeasuredFrameRate(line)
-    if line.startswith('Raw data: '):
-      for result in self._parseRawData(line):
-        fmt = result['EncOutputFormat']
-        self.size = Size(fmt['width'], fmt['height'])
-        self.codec = result['codec']
-        self.mime = fmt['mime']
+      Final = namedtuple('Final', 'comment c2 var qual')
+      lastFinal = None
+      for RG in (10, 15, 20, 25, 30, 40, 50):
+        P = 50./RG
+        quality = 0
+        p95, med, p5 = perc(data, P, math.floor), perc(data, 50, round), perc(data, 100 - P, math.ceil)
+        geo = math.sqrt(p5 * p95)
+        comment = ''
+        pub_lo, pub_hi = min(int(p95 * T), round(geo)), max(math.ceil(p5 / T), round(geo))
+        if pub_lo > med:
+          if pub_lo > med * 1.1:
+            quality += 0.5
+            comment += ' SLOW'
+          pub_lo = int(med)
+        if N < 2 * RG:
+          comment += ' N=%d' % N
+          quality += 2
+        RGVAR = False
+        if p5 / p95 > T ** 3:
+          quality += 3
+          RGVAR = True
+          if pub_hi > pub_lo * TO:
+            quality += 1
+            if RG == 10:
+              # find best pub_lo and pub_hi
+              for i in range(N / 2):
+                pub_lo_, pub_hi_ = min(int(data[N / 2 - i - 1] * T), round(geo), int(med)), max(math.ceil(data[N / 2 + i] / T), round(geo))
+                if pub_hi_ > pub_lo_ * TO:
+                  # ???
+                  pub_lo = min(pub_lo, math.ceil(pub_hi_ / TO))
+                  break
+                pub_lo, pub_hi = pub_lo_, pub_hi_
+        if mn < pub_lo / T or mx > pub_hi * T or pub_lo <= pub_hi / T:
+          quality += 1
+          comment += ' FLAKY('
+          if round(mn, 1) < pub_lo / T:
+            comment += 'mn=%.1f < ' % mn
+          comment += 'RANGE'
+          if round(mx, 1) > pub_hi * T:
+            comment += ' < mx=%.1f' % mx
+          comment += ')'
+        if False:
+          comment += ' DATA(mn=%1.f p%d=%1.f accept=%1.f-%1.f p50=%1.f p%d=%1.f mx=%1.f)' % (
+            mn, 100-P, p95, pub_lo / T, pub_hi * T, med, P, p5, mx)
+        var = math.sqrt(p5/p95)
+        if p95 < geo / T or p5 > geo * T:
+          if RGVAR:
+            comment += ' RG.VARIANCE:%.1f' % ((p5/p95) ** (1./3))
+          else:
+            comment += ' variance:%.1f' % var
+        comment = comment.replace('RANGE', '%d - %d' % (math.ceil(pub_lo / T), int(pub_hi * T)))
+        c2 = ''
+        if N >= 2 * RG:
+          c2 += ' N=%d' % N
+        if var <= T or p5 / p95 > T ** 3:
+          c2 += ' v%d%%=%.1f' % (round(100 - 2 * P), var)
+        if A and A.dbg:
+          c2 += ' E=%s' % (str(quality))
+        if c2:
+          c2 = ' <!--%s -->' % c2
 
-  def _parseMessage(self, message, array):
-    """Handles parsing a message from ValueArrays."""
-    if message.startswith('codec='):
-      result = dict(self._parsePartialResult(m)
-                  for m in re.finditer(self.MESSAGE_PATTERN + '(?: |$)', message))
-      if 'EncInputFormat' in result:
-        self.codec = result['codec']
-        fmt = result['EncInputFormat']
-        self.size = Size(fmt['width'], fmt['height'])
-        self.mime = result['EncOutputFormat']['mime']
-        self._rates_from_message.append(1000000./result['min'])
+        if comment:
+          comment = '            <!-- measured %d%%:%d-%d med:%d%s -->' % (round(100 - 2 * P), int(p95), math.ceil(p5), int(round(med)), comment)
+        if A and A.dbg: yield '<!-- --> %s%s' % (comment, c2)
+        c2 = '            <Limit name="measured-frame-rate-%s" range="%d-%d" />%s' % (size, pub_lo, pub_hi, c2)
+        final = Final(comment, c2, var, quality)
+        if lastFinal and final.var > lastFinal.var * math.sqrt(1.3):
+          if A and A.dbg: yield '<!-- RANGE JUMP -->'
+          break
+        elif not lastFinal or quality <= lastFinal.qual:
+          lastFinal = final
+        if N < 2 * RG or quality >= 4:
+          break
+      comment, c2, var, quality = lastFinal
+
+      if comment:
+        yield comment
+      yield c2
+    yield '        </MediaCodec>'
+  if last_section:
+    yield '    </%s>' % last_section
+  yield '</MediaCodecs>'
 
 
-class VideoDecoderPerfTestResult(_VideoResultBase):
-  """Represents a result from a VideoDecoderPerfTest performance case."""
-
-  # If a VideoDecoderPerfTest succeeds, it provides the results in the message
-  # of a ValueArray. If fails, it provides the results in the failure only
-  # using raw data.
-
-  def __init__(self, unused_m):
-    super(VideoDecoderPerfTestResult, self).__init__(is_decoder=True)
-
-  def _parseFailureLine(self, line):
-    """Handles parsing a line from the failure log."""
-    self._tryParseMeasuredFrameRate(line)
-    # if the test failed, we can only get the codec/size/mime from the raw data.
-    if line.startswith('Raw data: '):
-      for result in self._parseRawData(line):
-        fmt = result['DecOutputFormat']
-        self.size = Size(fmt['width'], fmt['height'])
-        self.codec = result['codec']
-        self.mime = result['mime']
-
-  def _parseMessage(self, message, array):
-    """Handles parsing a message from ValueArrays."""
-    if message.startswith('codec='):
-      result = dict(self._parsePartialResult(m)
-                  for m in re.finditer(self.MESSAGE_PATTERN + '(?: |$)', message))
-      if result.get('decodeto') == 'surface':
-        self.codec = result['codec']
-        fmt = result['DecOutputFormat']
-        self.size = Size(fmt['width'], fmt['height'])
-        self.mime = result['mime']
-        self._rates_from_message.append(1000000. / result['min'])
-
-
-class Results(object):
-  """Container that keeps all test results."""
+class Data:
   def __init__(self):
-      self._results = [] # namedtuples
-      self._device = None
+    self.data = set()
+    self.kind = {}
+    self.devices = set()
+    self.parser = ResultParser()
 
-  VIDEO_ENCODER_DECODER_TEST_REGEX = re.compile(
-      'test(.*)(\d{4})x(\d{4})(Goog|Other)$')
+  def summarize(self, A=None):
+    devs = sorted(self.devices)
+    #           device  > (not encoder,goog,mime,codec)  >  size > fps
+    xmlInfo = defaultdict(lambda: defaultdict(lambda: defaultdict(list)))
 
-  VIDEO_DECODER_PERF_TEST_REGEX = re.compile(
-      'test(VP[89]|H26[34]|MPEG4|HEVC)(\d+)x(\d+)(.*)$')
+    for mime, encoder, goog in sorted(set(self.kind.values())):
+      for dev, build, codec, size, num, std, avg, p0, p5, p10, p20, p30, p40, p50, p60, p70, p80, p90, p95, p100 in self.data:
+        if self.kind[codec] != (mime, encoder, goog):
+          continue
 
-  TestCaseSpec = namedtuple('TestCaseSpec', 'package path class_ regex result_class')
+        if p95 > 2: # ignore measurements at or below 2fps
+          xmlInfo[dev][(not encoder, goog, mime, codec)][size].append(p95)
+        else:
+          print >> sys.stderr, "warning: p95 value is suspiciously low: %s" % (
+            nice(dict(config=dict(dev=dev, codec=codec, size=str(size), N=num),
+                 data=dict(std=std, avg=avg, p0=p0, p5=p5, p10=p10, p20=p20, p30=p30, p40=p40,
+                           p50=p50, p60=p60, p70=p70, p80=p80, p90=p90, p95=p95, p100=p100))))
+    for dev, ddata in xmlInfo.items():
+      outFile = '{}.media_codecs_performance.xml'.format(dev)
+      print >> sys.stderr, "generating", outFile
+      with open(outFile, "wt") as out:
+        for l in genXml(ddata, A=A):
+          out.write(l + '\n')
+          print l
+      print >> sys.stderr, "generated", outFile
 
-  def _getTestCases(self):
-    return [
-      self.TestCaseSpec(package='CtsDeviceVideoPerf',
-                   path='TestSuite/TestSuite/TestSuite/TestSuite/TestCase',
-                   class_='VideoEncoderDecoderTest',
-                   regex=self.VIDEO_ENCODER_DECODER_TEST_REGEX,
-                   result_class=VideoEncoderDecoderTestResult),
-      self.TestCaseSpec(package='CtsMediaTestCases',
-                   path='TestSuite/TestSuite/TestSuite/TestCase',
-                   class_='VideoDecoderPerfTest',
-                   regex=self.VIDEO_DECODER_PERF_TEST_REGEX,
-                   result_class=VideoDecoderPerfTestResult)
-    ]
+  def parse_fmt(self, fmt):
+    return self.parser._parseDict(fmt)
 
-  def _verifyDeviceInfo(self, device):
-    assert self._device in (None, device), "expected %s device" % self._device
-    self._device = device
+  def parse_perf(self, a, device, build):
+    def rateFn(i):
+      if i is None:
+        return i
+      elif i == 0:
+        return 1e6
+      return 1000. / i
 
-  def importXml(self, xml):
-    self._verifyDeviceInfo(xml.find('DeviceInfo/BuildInfo').get('buildName'))
+    points = ('avg', 'min', 'p5', 'p10', 'p20', 'p30', 'p40', 'p50', 'p60', 'p70', 'p80', 'p90', 'p95', 'max')
+    a = dict(a)
+    codec = a['codec_name'] + ''
+    mime = a['mime_type']
+    size = Size(a['width'], a['height'])
+    if 'decode_to' in a:
+      fmt = self.parse_fmt(a['output_format'])
+      ofmt = self.parse_fmt(a['input_format'])
+    else:
+      fmt = self.parse_fmt(a['input_format'])
+      ofmt = self.parse_fmt(a['output_format'])
+    size = Size(max(fmt['width'], ofmt['width']), max(fmt['height'], ofmt['height']))
 
-    packages = createLookup(self._getTestCases(), lambda tc: tc.package)
-
-    for pkg in xml.iter('TestPackage'):
-      tests_in_package = packages.get(pkg.get('name'))
-      if not tests_in_package:
-        continue
-      paths = createLookup(tests_in_package, lambda tc: tc.path)
-      for path, tests_in_path in paths.items():
-        classes = createLookup(tests_in_path, lambda tc: tc.class_)
-        for tc in pkg.iterfind(path):
-          tests_in_class = classes.get(tc.get('name'))
-          if not tests_in_class:
-            continue
-          for test in tc.iter('Test'):
-            for tc in tests_in_class:
-              m = tc.regex.match(test.get('name'))
-              if m:
-                result = tc.result_class(m)
-                result.parse(test)
-                self._results.append(result)
-
-  def importFile(self, path):
-    print >> sys.stderr, 'Importing "%s"...' % path
     try:
-      return self.importXml(ET.parse(path))
-    except ET.ParseError:
-      raise ValueError('not a valid XML file')
+      prefix = 'time_avg_stats_'
+      if prefix + 'stdev' in a and a[prefix + 'avg']:
+        stdev = (a[prefix + 'stdev'] * 1e3 / a[prefix + 'avg'] ** 2)
+        data = ((device, build, codec, size, a[prefix  + 'num'], stdev) +
+                tuple(rateFn(a.get(prefix + i)) for i in points))
+        self.data.add(data)
+        self.kind[codec] = (mime, 'decode_to' not in a, codec.lower().startswith('omx.google.'))
+        self.devices.add(data[0])
+    except (KeyError, ZeroDivisionError):
+      print >> sys.stderr, a
+      raise
 
-  def getData(self):
-    for result in self._results:
-      for data in result.getData():
-        yield data
-
-  def dumpXml(self, results):
-    yield '<?xml version="1.0" encoding="utf-8" ?>'
-    yield '<!-- Copyright 2015 The Android Open Source Project'
-    yield ''
-    yield '     Licensed under the Apache License, Version 2.0 (the "License");'
-    yield '     you may not use this file except in compliance with the License.'
-    yield '     You may obtain a copy of the License at'
-    yield ''
-    yield '          http://www.apache.org/licenses/LICENSE-2.0'
-    yield ''
-    yield '     Unless required by applicable law or agreed to in writing, software'
-    yield '     distributed under the License is distributed on an "AS IS" BASIS,'
-    yield '     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.'
-    yield '     See the License for the specific language governing permissions and'
-    yield '     limitations under the License.'
-    yield '-->'
-    yield ''
-    yield '<MediaCodecs>'
-    last_section = None
-    Comp = namedtuple('Comp', 'is_decoder google mime name')
-    by_comp = createLookup(results,
-                           lambda e: Comp(is_decoder=e.is_decoder, google='.google.' in e.codec, mime=e.mime, name=e.codec))
-    for comp in sorted(by_comp):
-      section = 'Decoders' if comp.is_decoder else 'Encoders'
-      if section != last_section:
-        if last_section:
-          yield '    </%s>' % last_section
-        yield '    <%s>' % section
-        last_section = section
-      yield '        <MediaCodec name="%s" type="%s" update="true">' % (comp.name, comp.mime)
-      by_size = createLookup(by_comp[comp], lambda e: e.size)
-      for size in sorted(by_size):
-        values = list(itertools.chain(*(e.rates for e in by_size[size])))
-        min_, max_ = min(values), max(values)
-        med_ = int(math.sqrt(min_ * max_))
-        yield '            <Limit name="measured-frame-rate-%s" range="%d-%d" />' % (size, med_, med_)
-      yield '        </MediaCodec>'
-    if last_section:
-      yield '    </%s>' % last_section
-    yield '</MediaCodecs>'
-
-
-class Main(object):
-  """Executor of this utility."""
-
-  def __init__(self):
-    self._result = Results()
-
-    self._parser = argparse.ArgumentParser('get_achievable_framerates')
-    self._parser.add_argument('result_xml', nargs='+')
-
-  def _parseArgs(self):
-    self._args = self._parser.parse_args()
-
-  def _importXml(self, xml):
-    self._result.importFile(xml)
-
-  def _report(self):
-    for line in self._result.dumpXml(r for r in self._result.getData() if r):
-      print line
-
-  def run(self):
-    self._parseArgs()
-    try:
-      for xml in self._args.result_xml:
+  def parse_json(self, json, device, build):
+    for test, results in json:
+      if test in ("video_encoder_performance", "video_decoder_performance"):
         try:
-          self._importXml(xml)
-        except (ValueError, IOError, AssertionError) as e:
-          print >> sys.stderr, e
-          raise KeyboardInterrupt
-      self._report()
-    except KeyboardInterrupt:
-      print >> sys.stderr, 'Interrupted.'
+          if isinstance(results, list) and len(results[0]) and len(results[0][0]) == 2 and len(results[0][0][0]):
+            for result in results:
+              self.parse_perf(result, device, build)
+          else:
+            self.parse_perf(results, device, build)
+        except KeyboardInterrupt:
+          raise
 
-if __name__ == '__main__':
-  Main().run()
+  def parse_result(self, result):
+    device, build = '', ''
+    if not result.endswith('.zip'):
+      print >> sys.stderr, "cannot parse %s" % result
+      return
 
+    try:
+      with zipfile.ZipFile(result) as zip:
+        resultInfo, testInfos = None, []
+        for info in zip.infolist():
+          if re.search(r'/GenericDeviceInfo.deviceinfo.json$', info.filename):
+            resultInfo = info
+          elif re.search(r'/Cts(Media|Video)TestCases\.reportlog\.json$', info.filename):
+            testInfos.append(info)
+        if resultInfo:
+          try:
+            jsonFile = zip.open(resultInfo)
+            jsonData = json.load(jsonFile, encoding='utf-8')
+            device, build = jsonData['build_device'], jsonData['build_id']
+          except ValueError:
+            print >> sys.stderr, "could not parse %s" % resultInfo.filename
+        for info in testInfos:
+          jsonFile = zip.open(info)
+          try:
+            jsonData = json.load(jsonFile, encoding='utf-8', object_pairs_hook=lambda items: items)
+          except ValueError:
+            print >> sys.stderr, "cannot parse JSON in %s" % info.filename
+          self.parse_json(jsonData, device, build)
+
+    except zipfile.BadZipfile:
+      raise ValueError('bad zipfile')
+
+
+P = argparse.ArgumentParser("gar_v2")
+P.add_argument("--dbg", "-v", action='store_true', help="dump debug info into xml")
+P.add_argument("--ignore", "-I", action='store_true', help="ignore minimum sample count")
+P.add_argument("result_zip", nargs="*")
+A = P.parse_args()
+
+D = Data()
+for res in A.result_zip:
+  D.parse_result(res)
+D.summarize(A=A)
diff --git a/tools/cts-tradefed/Android.mk b/tools/cts-tradefed/Android.mk
index 32babe6..fbf63d7 100644
--- a/tools/cts-tradefed/Android.mk
+++ b/tools/cts-tradefed/Android.mk
@@ -25,7 +25,7 @@
 LOCAL_SUITE_TARGET_ARCH := $(TARGET_ARCH)
 LOCAL_SUITE_NAME := CTS
 LOCAL_SUITE_FULLNAME := "Compatibility Test Suite"
-LOCAL_SUITE_VERSION := 7.0
+LOCAL_SUITE_VERSION := 7.0_r5
 
 LOCAL_MODULE := cts-tradefed