Merge "Remove unimplemented and unused service declaration"
diff --git a/apps/CameraITS/tests/scene1/test_yuv_plus_raw.py b/apps/CameraITS/tests/scene1/test_yuv_plus_raw.py
index a5ceaba..10e100d 100644
--- a/apps/CameraITS/tests/scene1/test_yuv_plus_raw.py
+++ b/apps/CameraITS/tests/scene1/test_yuv_plus_raw.py
@@ -51,11 +51,11 @@
         tile = its.image.get_image_patch(img, 0.45, 0.45, 0.1, 0.1)
         rgb0 = its.image.compute_image_means(tile)
 
-        # Raw shots are 1/2 x 1/2 smaller after conversion to RGB, so scale the
-        # tile appropriately.
+        # Raw shots are 1/2 x 1/2 smaller after conversion to RGB, but tile
+        # cropping is relative.
         img = its.image.convert_capture_to_rgb_image(cap_raw, props=props)
         its.image.write_image(img, "%s_raw.jpg" % (NAME), True)
-        tile = its.image.get_image_patch(img, 0.475, 0.475, 0.05, 0.05)
+        tile = its.image.get_image_patch(img, 0.45, 0.45, 0.1, 0.1)
         rgb1 = its.image.compute_image_means(tile)
 
         rms_diff = math.sqrt(
diff --git a/apps/CameraITS/tests/scene1/test_yuv_plus_raw10.py b/apps/CameraITS/tests/scene1/test_yuv_plus_raw10.py
index f281089..be5f701 100644
--- a/apps/CameraITS/tests/scene1/test_yuv_plus_raw10.py
+++ b/apps/CameraITS/tests/scene1/test_yuv_plus_raw10.py
@@ -51,11 +51,11 @@
         tile = its.image.get_image_patch(img, 0.45, 0.45, 0.1, 0.1)
         rgb0 = its.image.compute_image_means(tile)
 
-        # Raw shots are 1/2 x 1/2 smaller after conversion to RGB, so scale the
-        # tile appropriately.
+        # Raw shots are 1/2 x 1/2 smaller after conversion to RGB, but tile
+        # cropping is relative.
         img = its.image.convert_capture_to_rgb_image(cap_raw, props=props)
         its.image.write_image(img, "%s_raw.jpg" % (NAME), True)
-        tile = its.image.get_image_patch(img, 0.475, 0.475, 0.05, 0.05)
+        tile = its.image.get_image_patch(img, 0.45, 0.45, 0.1, 0.1)
         rgb1 = its.image.compute_image_means(tile)
 
         rms_diff = math.sqrt(
diff --git a/apps/CameraITS/tests/scene1/test_yuv_plus_raw12.py b/apps/CameraITS/tests/scene1/test_yuv_plus_raw12.py
index 5b6051a..8070785 100644
--- a/apps/CameraITS/tests/scene1/test_yuv_plus_raw12.py
+++ b/apps/CameraITS/tests/scene1/test_yuv_plus_raw12.py
@@ -51,11 +51,11 @@
         tile = its.image.get_image_patch(img, 0.45, 0.45, 0.1, 0.1)
         rgb0 = its.image.compute_image_means(tile)
 
-        # Raw shots are 1/2 x 1/2 smaller after conversion to RGB, so scale the
-        # tile appropriately.
+        # Raw shots are 1/2 x 1/2 smaller after conversion to RGB, but tile
+        # cropping is relative.
         img = its.image.convert_capture_to_rgb_image(cap_raw, props=props)
         its.image.write_image(img, "%s_raw.jpg" % (NAME), True)
-        tile = its.image.get_image_patch(img, 0.475, 0.475, 0.05, 0.05)
+        tile = its.image.get_image_patch(img, 0.45, 0.45, 0.1, 0.1)
         rgb1 = its.image.compute_image_means(tile)
 
         rms_diff = math.sqrt(
diff --git a/apps/CameraITS/tests/scene2/test_faces.py b/apps/CameraITS/tests/scene2/test_faces.py
index 8a3a403..4e30fc1 100644
--- a/apps/CameraITS/tests/scene2/test_faces.py
+++ b/apps/CameraITS/tests/scene2/test_faces.py
@@ -13,6 +13,7 @@
 # limitations under the License.
 
 import its.image
+import its.caps
 import its.device
 import its.objects
 import os.path
@@ -32,13 +33,14 @@
         fd_modes = props['android.statistics.info.availableFaceDetectModes']
         a = props['android.sensor.info.activeArraySize']
         aw, ah = a['right'] - a['left'], a['bottom'] - a['top']
-        gain, exp, _, _, focus = cam.do_3a(get_results=True)
-        print 'iso = %d' % gain
-        print 'exp = %.2fms' % (exp*1.0E-6)
-        if focus == 0.0:
-            print 'fd = infinity'
-        else:
-            print 'fd = %.2fcm' % (1.0E2/focus)
+        if its.caps.read_3a(props):
+            gain, exp, _, _, focus = cam.do_3a(get_results=True)
+            print 'iso = %d' % gain
+            print 'exp = %.2fms' % (exp*1.0E-6)
+            if focus == 0.0:
+                print 'fd = infinity'
+            else:
+                print 'fd = %.2fcm' % (1.0E2/focus)
         for fd_mode in fd_modes:
             assert(FD_MODE_OFF <= fd_mode <= FD_MODE_FULL)
             req = its.objects.auto_capture_request()
diff --git a/apps/CameraITS/tests/scene5/test_lens_shading_and_color_uniformity.py b/apps/CameraITS/tests/scene5/test_lens_shading_and_color_uniformity.py
index 1307680..f8a97e4 100644
--- a/apps/CameraITS/tests/scene5/test_lens_shading_and_color_uniformity.py
+++ b/apps/CameraITS/tests/scene5/test_lens_shading_and_color_uniformity.py
@@ -52,16 +52,16 @@
 
     with its.device.ItsSession() as cam:
         props = cam.get_camera_properties()
-        its.caps.skip_unless(its.caps.read_3a(props))
-        # Converge 3A and get the estimates.
-        sens, exp, gains, xform, focus = cam.do_3a(get_results=True,
-                                                   do_af=False,
-                                                   lock_ae=True,
-                                                   lock_awb=True)
-        print "AE sensitivity %d, exposure %dms" % (sens, exp / 1000000.0)
-        print "AWB gains", gains
-        print "AWB transform", xform
-        print "AF distance", focus
+        if its.caps.read_3a(props):
+            # Converge 3A and get the estimates.
+            sens, exp, gains, xform, focus = cam.do_3a(get_results=True,
+                                                       do_af=False,
+                                                       lock_ae=True,
+                                                       lock_awb=True)
+            print "AE sensitivity %d, exposure %dms" % (sens, exp / 1000000.0)
+            print "AWB gains", gains
+            print "AWB transform", xform
+            print "AF distance", focus
         req = its.objects.auto_capture_request()
         img_size = its.objects.get_available_output_sizes("yuv", props)
         w = img_size[0][0]
diff --git a/common/host-side/tradefed/res/report/compatibility_failures.xsl b/common/host-side/tradefed/res/report/compatibility_failures.xsl
new file mode 100644
index 0000000..be65b91
--- /dev/null
+++ b/common/host-side/tradefed/res/report/compatibility_failures.xsl
@@ -0,0 +1,270 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<!DOCTYPE xsl:stylesheet [ <!ENTITY nbsp "&#160;"> ]>
+<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
+
+    <xsl:output method="html" version="1.0" encoding="UTF-8" indent="yes"/>
+
+    <xsl:template match="/">
+
+        <html>
+            <head>
+                <title>Test Report</title>
+                <style type="text/css">
+                    @import "compatibility_result.css";
+                </style>
+            </head>
+            <body>
+                <div>
+                    <table class="title">
+                        <tr>
+                            <td align="left"><img src="logo.png"/></td>
+                        </tr>
+                    </table>
+                </div>
+
+                <div>
+                    <table class="summary">
+                        <tr>
+                            <th colspan="2">Summary</th>
+                        </tr>
+                        <tr>
+                            <td class="rowtitle">Suite / Plan</td>
+                            <td>
+                                <xsl:value-of select="Result/@suite_name"/> / <xsl:value-of select="Result/@suite_plan"/>
+                            </td>
+                        </tr>
+                        <tr>
+                            <td class="rowtitle">Suite / Build</td>
+                            <td>
+                                <xsl:value-of select="Result/@suite_version"/> / <xsl:value-of select="Result/@suite_build_number"/>
+                            </td>
+                        </tr>
+                        <tr>
+                            <td class="rowtitle">Host Info</td>
+                            <td>
+                                Result/@start
+                                <xsl:value-of select="Result/@host_name"/>
+                                (<xsl:value-of select="Result/@os_name"/> - <xsl:value-of select="Result/@os_version"/>)
+                            </td>
+                        </tr>
+                        <tr>
+                            <td class="rowtitle">Start time / End Time</td>
+                            <td>
+                                <xsl:value-of select="Result/@start_display"/> /
+                                <xsl:value-of select="Result/@end_display"/>
+                            </td>
+                        </tr>
+                        <tr>
+                            <td class="rowtitle">Tests Passed</td>
+                            <td>
+                                <xsl:value-of select="Result/Summary/@pass"/>
+                            </td>
+                        </tr>
+                        <tr>
+                            <td class="rowtitle">Tests Failed</td>
+                            <td>
+                                <xsl:value-of select="Result/Summary/@failed"/>
+                            </td>
+                        </tr>
+                        <tr>
+                            <td class="rowtitle">Tests Not Executed</td>
+                            <td>
+                                <xsl:value-of select="Result/Summary/@not_executed"/>
+                            </td>
+                        </tr>
+                        <tr>
+                            <td class="rowtitle">Modules Done</td>
+                            <td>
+                                <xsl:value-of select="Result/Summary/@modules_done"/>
+                            </td>
+                        </tr>
+                        <tr>
+                            <td class="rowtitle">Modules Total</td>
+                            <td>
+                                <xsl:value-of select="Result/Summary/@modules_total"/>
+                            </td>
+                        </tr>
+                        <tr>
+                            <td class="rowtitle">Fingerprint</td>
+                            <td>
+                                <xsl:value-of select="Result/Build/@build_fingerprint"/>
+                            </td>
+                        </tr>
+                        <tr>
+                            <td class="rowtitle">Security Patch</td>
+                            <td>
+                                <xsl:value-of select="Result/Build/@build_version_security_patch"/>
+                            </td>
+                        </tr>
+                        <tr>
+                            <td class="rowtitle">Release (SDK)</td>
+                            <td>
+                                <xsl:value-of select="Result/Build/@build_version_release"/> (<xsl:value-of select="Result/Build/@build_version_sdk"/>)
+                            </td>
+                        </tr>
+                        <tr>
+                            <td class="rowtitle">ABIs</td>
+                            <td>
+                                <xsl:value-of select="Result/Build/@build_abis"/>
+                            </td>
+                        </tr>
+                    </table>
+                </div>
+
+                <!-- High level summary of test execution -->
+                <br/>
+                <div>
+                    <table class="testsummary">
+                        <tr>
+                            <th>Module</th>
+                            <th>Passed</th>
+                            <th>Failed</th>
+                            <th>Not Executed</th>
+                            <th>Total Tests</th>
+                        </tr>
+                        <xsl:for-each select="Result/Module">
+                            <tr>
+                                <td>
+                                    <xsl:if test="count(TestCase/Test[@result = 'fail']) &gt; 0">
+                                        <xsl:variable name="href"><xsl:value-of select="@name"/> - <xsl:value-of select="@abi"/></xsl:variable>
+                                        <a href="#{$href}"><xsl:value-of select="@name"/> - <xsl:value-of select="@abi"/></a>
+                                    </xsl:if>
+                                    <xsl:if test="count(TestCase/Test[@result = 'fail']) &lt; 1">
+                                        <xsl:value-of select="@name"/> - <xsl:value-of select="@abi"/>
+                                    </xsl:if>
+                                </td>
+                                <td>
+                                    <xsl:value-of select="@pass"/>
+                                </td>
+                                <td>
+                                    <xsl:value-of select="count(TestCase/Test[@result = 'fail'])"/>
+                                </td>
+                                <td>
+                                    <xsl:value-of select="count(TestCase/Test[@result = 'not_executed'])"/>
+                                </td>
+                                <td>
+                                    <xsl:value-of select="count(TestCase/Test[@result = 'fail']) + @pass + count(TestCase/Test[@result = 'not_executed']) "/>
+                                </td>
+                            </tr>
+                        </xsl:for-each> <!-- end Module -->
+                    </table>
+                </div>
+
+                <br/>
+                <xsl:call-template name="detailedTestReport">
+                    <xsl:with-param name="resultFilter" select="'fail'" />
+                </xsl:call-template>
+
+            </body>
+        </html>
+    </xsl:template>
+
+    <xsl:template name="detailedTestReport">
+        <xsl:param name="resultFilter" />
+        <div>
+            <xsl:for-each select="Result/Module">
+                <xsl:if test="$resultFilter=''
+                        or count(TestCase/Test[@result=$resultFilter]) &gt; 0">
+
+                    <table class="testdetails">
+                        <tr>
+                            <td class="module" colspan="3">
+                                <xsl:variable name="href"><xsl:value-of select="@name"/> - <xsl:value-of select="@abi"/></xsl:variable>
+                                <a name="{$href}"><xsl:value-of select="@name"/> - <xsl:value-of select="@abi"/></a>
+                            </td>
+                        </tr>
+
+                        <tr>
+                            <th width="30%">Test</th>
+                            <th width="5%">Result</th>
+                            <th>Details</th>
+                        </tr>
+
+                        <xsl:for-each select="TestCase">
+                            <xsl:variable name="TestCase" select="."/>
+                            <!-- test -->
+                            <xsl:for-each select="Test">
+                                <xsl:if test="$resultFilter='' or @result=$resultFilter">
+                                    <tr>
+                                        <td class="testname"> <xsl:value-of select="$TestCase/@name"/>#<xsl:value-of select="@name"/></td>
+
+                                        <!-- test results -->
+                                        <xsl:if test="@result='pass'">
+                                            <td class="pass">
+                                                <div style="text-align: center; margin-left:auto; margin-right:auto;">
+                                                    <xsl:value-of select="@result"/>
+                                                </div>
+                                            </td>
+                                            <td class="failuredetails"/>
+                                        </xsl:if>
+
+                                        <xsl:if test="@result='fail'">
+                                            <td class="failed">
+                                                <div style="text-align: center; margin-left:auto; margin-right:auto;">
+                                                    <xsl:value-of select="@result"/>
+                                                </div>
+                                            </td>
+                                            <td class="failuredetails">
+                                                <div class="details">
+                                                    <xsl:value-of select="Failure/@message"/>
+                                                </div>
+                                            </td>
+                                        </xsl:if>
+
+                                        <xsl:if test="@result='not_executed'">
+                                            <td class="not_executed">
+                                                <div style="text-align: center; margin-left:auto; margin-right:auto;">
+                                                    <xsl:value-of select="@result"/>
+                                                </div>
+                                            </td>
+                                            <td class="failuredetails"></td>
+                                        </xsl:if>
+                                    </tr> <!-- finished with a row -->
+                                </xsl:if>
+                            </xsl:for-each> <!-- end test -->
+                        </xsl:for-each>
+                    </table>
+                </xsl:if>
+            </xsl:for-each> <!-- end test Module -->
+        </div>
+    </xsl:template>
+
+    <!-- Take a delimited string and insert line breaks after a some number of elements. -->
+    <xsl:template name="formatDelimitedString">
+        <xsl:param name="string" />
+        <xsl:param name="numTokensPerRow" select="10" />
+        <xsl:param name="tokenIndex" select="1" />
+        <xsl:if test="$string">
+            <!-- Requires the last element to also have a delimiter after it. -->
+            <xsl:variable name="token" select="substring-before($string, ';')" />
+            <xsl:value-of select="$token" />
+            <xsl:text>&#160;</xsl:text>
+
+            <xsl:if test="$tokenIndex mod $numTokensPerRow = 0">
+                <br />
+            </xsl:if>
+
+            <xsl:call-template name="formatDelimitedString">
+                <xsl:with-param name="string" select="substring-after($string, ';')" />
+                <xsl:with-param name="numTokensPerRow" select="$numTokensPerRow" />
+                <xsl:with-param name="tokenIndex" select="$tokenIndex + 1" />
+            </xsl:call-template>
+        </xsl:if>
+    </xsl:template>
+
+</xsl:stylesheet>
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 16bf486..fea9ce8 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
@@ -232,6 +232,13 @@
     }
 
     /**
+     * @return a {@link File} in the resultDir for counting expected test runs
+     */
+    public File getTestRunsFile() throws FileNotFoundException {
+        return new File(getResultDir(), "test_runs.txt");
+    }
+
+    /**
      * @return a {@link String} to use for directory suffixes created from the given time.
      */
     public static String getDirSuffix(long millis) {
diff --git a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/result/ConsoleReporter.java b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/result/ConsoleReporter.java
index cea6953..c4a1385 100644
--- a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/result/ConsoleReporter.java
+++ b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/result/ConsoleReporter.java
@@ -64,26 +64,17 @@
      */
     @Override
     public void testRunStarted(String id, int numTests) {
-        if (mModuleId == null || !mModuleId.equals(id)) {
-            mModuleId = id;
-            mTotalTestsInModule = numTests;
-            // Reset counters
-            mCurrentTestNum = 0;
-            mPassedTests = 0;
-            mFailedTests = 0;
-            mNotExecutedTests = 0;
-            mTestFailed = false;
-            logMessage("Starting %s with %d test%s",
-                    id, mTotalTestsInModule, (mTotalTestsInModule > 1) ? "s" : "");
-        } else {
-            if (mNotExecutedTests == 0) {
-                mTotalTestsInModule += numTests;
-            } else {
-                mTotalTestsInModule += Math.max(0, numTests - mNotExecutedTests);
-            }
-            logMessage("Continuing %s with %d test%s",
-                    id, mTotalTestsInModule, (mTotalTestsInModule > 1) ? "s" : "");
-        }
+        boolean isRepeatModule = (mModuleId != null && mModuleId.equals(id));
+        mModuleId = id;
+        mTotalTestsInModule = numTests;
+        // Reset counters
+        mCurrentTestNum = 0;
+        mPassedTests = 0;
+        mFailedTests = 0;
+        mNotExecutedTests = 0;
+        mTestFailed = false;
+        logMessage("%s %s with %d test%s", (isRepeatModule) ? "Continuing" : "Starting", id,
+                mTotalTestsInModule, (mTotalTestsInModule > 1) ? "s" : "");
     }
 
     /**
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 ec41baf..dc31b4e 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
@@ -17,6 +17,7 @@
 
 import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
 import com.android.compatibility.common.tradefed.result.InvocationFailureHandler;
+import com.android.compatibility.common.tradefed.result.TestRunHandler;
 import com.android.compatibility.common.tradefed.testtype.CompatibilityTest;
 import com.android.compatibility.common.tradefed.testtype.CompatibilityTest.RetryType;
 import com.android.compatibility.common.util.ICaseResult;
@@ -247,27 +248,30 @@
      */
     @Override
     public void testRunStarted(String id, int numTests) {
-        if (mCurrentModuleResult != null && mCurrentModuleResult.getId().equals(id)) {
-            // In case we get another test run of a known module, update the complete
-            // status to false to indicate it is not complete.
-            if (mCurrentModuleResult.isDone()) {
-                // modules run with HostTest treat each test class as a separate module.
-                // TODO(aaronholden): remove this case when JarHostTest is no longer calls
-                // testRunStarted for each test class.
-                mTotalTestsInModule += numTests;
-            } else {
-                // treat new tests as not executed tests from current module
-                mTotalTestsInModule +=
-                        Math.max(0, numTests - mCurrentModuleResult.getNotExecuted());
-            }
+        if (mCurrentModuleResult != null && mCurrentModuleResult.getId().equals(id)
+                && mCurrentModuleResult.isDone()) {
+            // Modules run with JarHostTest treat each test class as a separate module,
+            // resulting in additional unexpected test runs.
+            // This case exists only for N
+            mTotalTestsInModule += numTests;
         } else {
+            // Handle non-JarHostTest case
             mCurrentModuleResult = mResult.getOrCreateModule(id);
-            mTotalTestsInModule = numTests;
+            mModuleWasDone = mCurrentModuleResult.isDone();
+            if (!mModuleWasDone) {
+                // we only want to update testRun variables if the IModuleResult is not yet done
+                // otherwise leave testRun variables alone so isDone evaluates to true.
+                if (mCurrentModuleResult.getExpectedTestRuns() == 0) {
+                    mCurrentModuleResult.setExpectedTestRuns(TestRunHandler.getTestRuns(
+                            mBuildHelper, mCurrentModuleResult.getId()));
+                }
+                mCurrentModuleResult.addTestRun();
+            }
             // Reset counters
+            mTotalTestsInModule = numTests;
             mCurrentTestNum = 0;
         }
-        mModuleWasDone = mCurrentModuleResult.isDone();
-        mCurrentModuleResult.setDone(false);
+        mCurrentModuleResult.inProgress(true);
     }
 
     /**
@@ -351,17 +355,30 @@
      */
     @Override
     public void testRunEnded(long elapsedTime, Map<String, String> metrics) {
+        mCurrentModuleResult.inProgress(false);
         mCurrentModuleResult.addRuntime(elapsedTime);
-        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);
+        if (!mModuleWasDone) {
+            // Not executed count now represents an upper-bound for a fix to b/33211104.
+            // Only setNotExecuted this number if the module has already been completely executed.
+            int testCountDiff = Math.max(mTotalTestsInModule - mCurrentTestNum, 0);
+            if (isShardResultReporter()) {
+                // reset value, which is added to total count for master shard upon merge
+                mCurrentModuleResult.setNotExecuted(testCountDiff);
+            } else {
+                // increment value for master shard
+                mCurrentModuleResult.setNotExecuted(mCurrentModuleResult.getNotExecuted()
+                        + testCountDiff);
+            }
+            if (mCanMarkDone) {
+                // Only mark module done if status of the invocation allows it (mCanMarkDone) and
+                // if module has not already been marked done.
+                mCurrentModuleResult.setDone(mCurrentTestNum >= mTotalTestsInModule);
+            }
         }
-        mCurrentModuleResult.setNotExecuted(Math.max(mTotalTestsInModule - mCurrentTestNum, 0));
         if (isShardResultReporter()) {
             // Forward module results to the master.
             mMasterResultReporter.mergeModuleResult(mCurrentModuleResult);
+            mCurrentModuleResult.resetTestRuns();
         }
     }
 
@@ -473,7 +490,13 @@
                     mBuildHelper.getSuiteBuild(), mResult, mResultDir, startTime,
                     elapsedTime + startTime, mReferenceUrl, getLogUrl(),
                     mBuildHelper.getCommandLineArgs());
-            info("Test Result: %s", resultFile.getCanonicalPath());
+            // Create failure report after zip file so extra data is not uploaded
+            File failureReport = ResultHandler.createFailureReport(resultFile);
+            if (failureReport.exists()) {
+                info("Test Result: %s", failureReport.getCanonicalPath());
+            } else {
+                info("Test Result: %s", resultFile.getCanonicalPath());
+            }
             File zippedResults = zipResults(mResultDir);
             debug("Full Result: %s", zippedResults.getCanonicalPath());
 
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 6b9b5e4..9dbbcbb 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
@@ -65,14 +65,6 @@
         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",
             importance=Importance.IF_UNSET)
     private String mSubPlanName = null;
@@ -192,49 +184,7 @@
             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)) {
                 TestFilter moduleInclude =
                             new TestFilter(module.getAbi(), module.getName(), null /*test*/);
diff --git a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/result/TestRunHandler.java b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/result/TestRunHandler.java
new file mode 100644
index 0000000..4473eb8
--- /dev/null
+++ b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/result/TestRunHandler.java
@@ -0,0 +1,97 @@
+/*
+ * 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.tradefed.result;
+
+import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
+import com.android.tradefed.log.LogUtil.CLog;
+import com.android.tradefed.util.FileUtil;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
+
+/**
+ * A helper class for setting and checking the number of expected test runs.
+ */
+public class TestRunHandler {
+
+    private static final String MAP_DELIMITER = "->";
+
+    /**
+     * Determine the number of expected test runs for the module
+     *
+     * @param buildHelper the {@link CompatibilityBuildHelper} from which to retrieve invocation
+     * failure file
+     * @return the number of expected test runs, or 1 if module is not found
+     */
+    public static int getTestRuns(final CompatibilityBuildHelper buildHelper, String id) {
+        try {
+            File f = buildHelper.getTestRunsFile();
+            if (!f.exists() || f.length() == 0) {
+                return 1; // test runs file doesn't exist, expect one test run by default
+            }
+            String mapString = FileUtil.readStringFromFile(f);
+            Map<String, Integer> map = stringToMap(mapString);
+            Integer testRuns = map.get(id);
+            return (testRuns == null) ? 1 : testRuns;
+        } catch (IOException e) {
+            CLog.e("Could not read test run file for session %s",
+                buildHelper.getDirSuffix(buildHelper.getStartTime()));
+            CLog.e(e);
+            return 1;
+        }
+    }
+
+    /**
+     * Write the number of expected test runs to the result's test run file.
+     *
+     * @param buildHelper the {@link CompatibilityBuildHelper} used to write the
+     * test run file
+     * @param testRuns a mapping of module names to number of test runs expected
+     */
+    public static void setTestRuns(final CompatibilityBuildHelper buildHelper,
+            Map<String, Integer> testRuns) {
+        try {
+            File f = buildHelper.getTestRunsFile();
+            if (!f.exists()) {
+                f.createNewFile();
+            }
+            FileUtil.writeToFile(mapToString(testRuns), f);
+        } catch (IOException e) {
+            CLog.e("Exception while writing test runs file.");
+            CLog.e(e);
+        }
+    }
+
+    private static String mapToString(Map<String, Integer> map) {
+        StringBuilder sb = new StringBuilder("");
+        for (Map.Entry<String, Integer> entry : map.entrySet()) {
+            sb.append(String.format("%s%s%d\n", entry.getKey(), MAP_DELIMITER, entry.getValue()));
+        }
+        return sb.toString();
+    }
+
+    private static Map<String, Integer> stringToMap(String str) {
+        Map<String, Integer> map = new HashMap<>();
+        for (String entry : str.split("\n")) {
+            String[] parts = entry.split(MAP_DELIMITER);
+            map.put(parts[0], Integer.parseInt(parts[1]));
+        }
+        return map;
+    }
+}
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 50ee9d0..f1e2b38 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
@@ -41,6 +41,7 @@
 import com.android.tradefed.result.ITestInvocationListener;
 import com.android.tradefed.result.InputStreamSource;
 import com.android.tradefed.result.LogDataType;
+import com.android.tradefed.result.ResultForwarder;
 import com.android.tradefed.suite.checker.ISystemStatusChecker;
 import com.android.tradefed.suite.checker.ISystemStatusCheckerReceiver;
 import com.android.tradefed.testtype.Abi;
@@ -69,6 +70,7 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
+import java.util.Collections;
 import java.util.HashSet;
 import java.util.LinkedHashSet;
 import java.util.LinkedList;
@@ -353,6 +355,11 @@
                 // Get the tests to run in this shard
                 modules = mModuleRepo.getModules(getDevice().getSerialNumber(), mShardIndex);
             }
+            // Update BuildInfo in each shard to store the original command-line arguments from
+            // the session to be retried. These arguments will be serialized in the report later.
+            if (mRetrySessionId != null) {
+                loadRetryCommandLineArgs(mRetrySessionId);
+            }
 
             listener = new FailureListener(listener, getDevice(), mBugReportOnFailure,
                     mLogcatOnFailure, mScreenshotOnFailure, mRebootOnFailure, mMaxLogcatBytes);
@@ -445,11 +452,17 @@
                 if (checkers != null && !checkers.isEmpty()) {
                     runPreModuleCheck(module.getName(), checkers, mDevice, listener);
                 }
+                // Workaround to b/34202787: Add result forwarder that ensures module is reported
+                // with 0 tests if test runner doesn't report anything in this case.
+                // Necessary for solution to b/33289177, in which completed modules may sometimes
+                // not be marked done until retried with 0 tests.
+                ModuleResultForwarder moduleListener = new ModuleResultForwarder(listener);
                 try {
                     if (module.getTest() instanceof IBuildReceiver) {
                         ((IBuildReceiver)module.getTest()).setBuild(mBuildHelper.getBuildInfo());
                     }
-                    module.run(listener);
+                    module.run(moduleListener);
+                    moduleListener.finish(module.getId());
                 } catch (DeviceUnresponsiveException due) {
                     // being able to catch a DeviceUnresponsiveException here implies that recovery
                     // was successful, and test execution should proceed to next module
@@ -604,6 +617,30 @@
     }
 
     /**
+     * Sets the retry command-line args to be stored in the BuildInfo and serialized into the
+     * report upon completion of the invocation.
+     */
+    void loadRetryCommandLineArgs(Integer sessionId) {
+        IInvocationResult result = null;
+        try {
+            result = ResultHandler.findResult(mBuildHelper.getResultsDir(), sessionId);
+        } catch (FileNotFoundException e) {
+            // We should never reach this point, because this method should only be called
+            // after setupFilters(), so result exists if we've gotten this far
+            throw new RuntimeException(e);
+        }
+        if (result == null) {
+            // Again, this should never happen
+            throw new IllegalArgumentException(String.format(
+                    "Could not find session with id %d", sessionId));
+        }
+        String retryCommandLineArgs = result.getCommandLineArgs();
+        if (retryCommandLineArgs != null) {
+            mBuildHelper.setRetryCommandLineArgs(retryCommandLineArgs);
+        }
+    }
+
+    /**
      * Sets the include/exclude filters up based on if a module name was given or whether this is a
      * retry run.
      */
@@ -638,8 +675,6 @@
 
             String retryCommandLineArgs = result.getCommandLineArgs();
             if (retryCommandLineArgs != null) {
-                // Copy the original command into the build helper so it can be serialized later
-                mBuildHelper.setRetryCommandLineArgs(retryCommandLineArgs);
                 try {
                     // parse the command-line string from the result file and set options
                     ArgsOptionParser parser = new ArgsOptionParser(this);
@@ -773,4 +808,32 @@
     public void setCollectTestsOnly(boolean collectTestsOnly) {
         mCollectTestsOnly = collectTestsOnly;
     }
+
+    private class ModuleResultForwarder extends ResultForwarder {
+
+        private boolean mTestRunStarted = false;
+        private ITestInvocationListener mListener;
+
+        public ModuleResultForwarder(ITestInvocationListener listener) {
+            super(listener);
+            mListener = listener;
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        @Override
+        public void testRunStarted(String name, int numTests) {
+            mListener.testRunStarted(name, numTests);
+            mTestRunStarted = true;
+        }
+
+        public void finish(String moduleId) {
+            if (!mTestRunStarted) {
+                mListener.testRunStarted(moduleId, 0);
+                mListener.testRunEnded(0, Collections.emptyMap());
+            }
+        }
+    }
+
 }
diff --git a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/ModuleRepo.java b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/ModuleRepo.java
index 8b72acf..33627b2 100644
--- a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/ModuleRepo.java
+++ b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/ModuleRepo.java
@@ -16,6 +16,8 @@
 package com.android.compatibility.common.tradefed.testtype;
 
 import com.android.compatibility.common.tradefed.util.LinearPartition;
+import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
+import com.android.compatibility.common.tradefed.result.TestRunHandler;
 import com.android.compatibility.common.util.TestFilter;
 import com.android.tradefed.build.IBuildInfo;
 import com.android.tradefed.config.ConfigurationException;
@@ -204,6 +206,7 @@
             throw new IllegalArgumentException(
                     String.format("No config files found in %s", testsDir.getAbsolutePath()));
         }
+        Map<String, Integer> shardedTestCounts = new HashMap<>();
         for (File configFile : configFiles) {
             final String name = configFile.getName().replace(CONFIG_EXT, "");
             final String[] pathArg = new String[] { configFile.getAbsolutePath() };
@@ -256,6 +259,9 @@
                     if (mTotalShards > 1) {
                          shardedTests = splitShardableTests(tests, buildInfo);
                     }
+                    if (shardedTests.size() > 1) {
+                        shardedTestCounts.put(id, shardedTests.size());
+                    }
                     for (IRemoteTest test : shardedTests) {
                         addModuleDef(name, abi, test, pathArg);
                     }
@@ -265,6 +271,7 @@
                         configFile.getName()), e);
             }
         }
+        TestRunHandler.setTestRuns(new CompatibilityBuildHelper(buildInfo), shardedTestCounts);
     }
 
     private List<IRemoteTest> splitShardableTests(List<IRemoteTest> tests, IBuildInfo buildInfo) {
diff --git a/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/presubmit/PresubmitSetupValidation.java b/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/presubmit/PresubmitSetupValidation.java
index 3342ba0..4eb48fc 100644
--- a/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/presubmit/PresubmitSetupValidation.java
+++ b/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/presubmit/PresubmitSetupValidation.java
@@ -44,4 +44,14 @@
                     + "CTS unit tests configuration.", e.getMessage()));
         }
     }
+
+    /**
+     * Test to ensure that Zip dependency on the Apache Commons Compress coming from TradeFed is
+     * properly setup. This dependency is required for some utilities of TradeFed to work.
+     */
+    public void testDependencyCommonsCompress() throws Exception {
+        ClassLoader loader = ClassLoader.getSystemClassLoader();
+        // This will throw an exception if dependency isn't met.
+        loader.loadClass("org.apache.commons.compress.archivers.zip.ZipFile");
+    }
 }
diff --git a/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/result/ConsoleReporterTest.java b/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/result/ConsoleReporterTest.java
index 2db94fa..272b94d 100644
--- a/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/result/ConsoleReporterTest.java
+++ b/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/result/ConsoleReporterTest.java
@@ -90,15 +90,6 @@
         assertEquals(0, mReporter.getPassedTests());
         assertEquals(0, mReporter.getCurrentTestNum());
         assertEquals(3, mReporter.getTotalTestsInModule());
-
-        runTests();
-        // Same id, should not reset test counters, but aggregate total tests
-        mReporter.testRunStarted(ID2, 5);
-        assertEquals(ID2, mReporter.getModuleId());
-        assertEquals(2, mReporter.getFailedTests());
-        assertEquals(1, mReporter.getPassedTests());
-        assertEquals(3, mReporter.getCurrentTestNum());
-        assertEquals(8, mReporter.getTotalTestsInModule());
     }
 
     /** Run 4 test, but one is ignored */
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 09885ed..5af79dc 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
@@ -296,7 +296,7 @@
         // Set up IInvocationResult with existing results from previous session
         IInvocationResult invocationResult = mReporter.getResult();
         IModuleResult moduleResult = invocationResult.getOrCreateModule(ID);
-        moduleResult.setDone(false);
+        moduleResult.initializeDone(false);
         ICaseResult caseResult = moduleResult.getOrCreateResult(CLASS);
         ITestResult testResult1 = caseResult.getOrCreateResult(METHOD_1);
         testResult1.setResultStatus(TestStatus.PASS);
diff --git a/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/testtype/ModuleRepoTest.java b/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/testtype/ModuleRepoTest.java
index f341823..9793d8b 100644
--- a/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/testtype/ModuleRepoTest.java
+++ b/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/testtype/ModuleRepoTest.java
@@ -16,6 +16,8 @@
 
 package com.android.compatibility.common.tradefed.testtype;
 
+import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
+import com.android.compatibility.common.tradefed.build.CompatibilityBuildProvider;
 import com.android.compatibility.common.tradefed.testtype.ModuleRepo.ConfigFilter;
 import com.android.tradefed.build.IBuildInfo;
 import com.android.tradefed.device.DeviceNotAvailableException;
@@ -40,6 +42,7 @@
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.HashMap;
 import java.util.HashSet;
 import java.util.LinkedHashSet;
 import java.util.List;
@@ -75,6 +78,9 @@
     private static final Set<String> EXCLUDES = new HashSet<>();
     private static final Set<String> FILES = new HashSet<>();
     private static final String FILENAME = "%s.config";
+    private static final String ROOT_DIR_ATTR = "ROOT_DIR";
+    private static final String SUITE_NAME_ATTR = "SUITE_NAME";
+    private static final String START_TIME_MS_ATTR = "START_TIME_MS";
     private static final String ABI_32 = "armeabi-v7a";
     private static final String ABI_64 = "arm64-v8a";
     private static final String MODULE_NAME_A = "FooModuleA";
@@ -116,6 +122,7 @@
     }
     private ModuleRepo mRepo;
     private File mTestsDir;
+    private File mRootDir;
     private IBuildInfo mMockBuildInfo;
 
     @Override
@@ -123,6 +130,19 @@
         mTestsDir = setUpConfigs();
         mRepo = new ModuleRepo();
         mMockBuildInfo = EasyMock.createMock(IBuildInfo.class);
+        // Flesh out the result directory structure so ModuleRepo can write to the test runs file
+        mRootDir = FileUtil.createTempDir("root");
+        File subRootDir = new File(mRootDir, String.format("android-suite"));
+        File resultsDir = new File(subRootDir, "results");
+        File resultDir = new File(resultsDir, CompatibilityBuildHelper.getDirSuffix(0));
+        resultDir.mkdirs();
+
+        Map<String, String> mockBuildInfoMap = new HashMap<String, String>();
+        mockBuildInfoMap.put(ROOT_DIR_ATTR, mRootDir.getAbsolutePath());
+        mockBuildInfoMap.put(SUITE_NAME_ATTR, "suite");
+        mockBuildInfoMap.put(START_TIME_MS_ATTR, Long.toString(0));
+        EasyMock.expect(mMockBuildInfo.getBuildAttributes()).andReturn(mockBuildInfoMap).anyTimes();
+        EasyMock.replay(mMockBuildInfo);
     }
 
     private File setUpConfigs() throws IOException {
@@ -154,6 +174,12 @@
     public void tearDown() throws Exception {
         FileUtil.recursiveDelete(mTestsDir);
         mRepo.resetModuleRepo();
+        tearDownConfigs(mTestsDir);
+        tearDownConfigs(mRootDir);
+    }
+
+    private void tearDownConfigs(File testsDir) {
+        FileUtil.recursiveDelete(testsDir);
     }
 
     public void testInitialization() throws Exception {
diff --git a/common/util/src/com/android/compatibility/common/util/IModuleResult.java b/common/util/src/com/android/compatibility/common/util/IModuleResult.java
index 7c6279c..06d66c0 100644
--- a/common/util/src/com/android/compatibility/common/util/IModuleResult.java
+++ b/common/util/src/com/android/compatibility/common/util/IModuleResult.java
@@ -32,17 +32,91 @@
 
     long getRuntime();
 
-    boolean isDone();
-
-    void setDone(boolean done);
-
-    boolean isPassed();
-
+    /**
+     * Get the estimate of not-executed tests for this module. This estimate is a maximum
+     * not-executed count, assuming all test runs have been started.
+     * @return estimate of not-executed tests
+     */
     int getNotExecuted();
 
+    /**
+     * Set the estimate of not-executed tests for this module. This estimate is a maximum
+     * not-executed count, assuming all test runs have been started.
+     * @param estimate of not-executed tests
+     */
     void setNotExecuted(int numTests);
 
     /**
+     * Whether all expected tests have been executed and all expected test runs have been seen
+     * and completed.
+     *
+     * @return the comprehensive completeness status of the module
+     */
+    boolean isDone();
+
+    /**
+     * Whether all expected tests have been executed for the test runs seen so far.
+     *
+     * @return the completeness status of the module so far
+     */
+    boolean isDoneSoFar();
+
+    /**
+     * Explicitly sets the "done" status for this module. To be used when constructing this
+     * instance from an XML report. The done status for an {@link IModuleResult} can be changed
+     * indiscriminately by method setDone(boolean) immediately after a call to initializeDone,
+     * whereas the status may only be switched to false immediately after a call to setDone.
+     *
+     * @param done the initial completeness status of the module
+     */
+    void initializeDone(boolean done);
+
+    /**
+     * Sets the "done" status for this module. To be used after each test run for the module.
+     * After setDone is used once, subsequent calls to setDone will AND the given value with the
+     * existing done status value. Thus a module with "done" already set to false cannot be marked
+     * done unless re-initialized (see initializeDone).
+     *
+     * @param done the completeness status of the module for a test run
+     */
+    void setDone(boolean done);
+
+    /**
+     * Sets the "in-progress" status for this module. Useful for tracking completion of the module
+     * in the case that a test run begins but never ends.
+     *
+     * @param inProgress whether the module is currently in progress
+     */
+    void inProgress(boolean inProgress);
+
+    /**
+     * @return the number of expected test runs for this module in this invocation
+     */
+    int getExpectedTestRuns();
+
+    /**
+     * @param the number of expected test runs for this module in this invocation
+     */
+    void setExpectedTestRuns(int numRuns);
+
+    /**
+     * @return the number of test runs seen for this module in this invocation
+     */
+    int getTestRuns();
+
+    /**
+     * Adds to the count of test runs seen for this module in this invocation
+     */
+    void addTestRun();
+
+    /**
+     * Reset the count of test runs seen for this module in this invocation. Should be performed
+     * after merging the module into another module, so that future merges do not double-count the
+     * same test runs.
+     */
+    void resetTestRuns();
+
+    /**
      * Gets a {@link ICaseResult} for the given testcase, creating it if it doesn't exist.
      *
      * @param caseName the name of the testcase eg &lt;package-name&gt;&lt;class-name&gt;
diff --git a/common/util/src/com/android/compatibility/common/util/ModuleResult.java b/common/util/src/com/android/compatibility/common/util/ModuleResult.java
index 0c43e93..60038cf 100644
--- a/common/util/src/com/android/compatibility/common/util/ModuleResult.java
+++ b/common/util/src/com/android/compatibility/common/util/ModuleResult.java
@@ -28,7 +28,13 @@
 
     private String mId;
     private long mRuntime = 0;
+
+    /* Variables related to completion of the module */
     private boolean mDone = false;
+    private boolean mHaveSetDone = false;
+    private boolean mInProgress = false;
+    private int mExpectedTestRuns = 0;
+    private int mActualTestRuns = 0;
     private int mNotExecuted = 0;
 
     private Map<String, ICaseResult> mResults = new HashMap<>();
@@ -46,7 +52,27 @@
      */
     @Override
     public boolean isDone() {
-        return mDone;
+        return mDone && !mInProgress && (mActualTestRuns >= mExpectedTestRuns);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public boolean isDoneSoFar() {
+        return mDone && !mInProgress;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void initializeDone(boolean done) {
+        mDone = done;
+        mHaveSetDone = false;
+        if (mDone) {
+            mNotExecuted = 0;
+        }
     }
 
     /**
@@ -54,15 +80,63 @@
      */
     @Override
     public void setDone(boolean done) {
-        mDone = done;
+        if (mHaveSetDone) {
+            mDone &= done; // If we've already set done for this instance, AND the received value
+        } else {
+            mDone = done; // If done has only been initialized, overwrite the existing value
+        }
+        mHaveSetDone = true;
+        if (mDone) {
+            mNotExecuted = 0;
+        }
     }
 
     /**
      * {@inheritDoc}
      */
     @Override
-    public boolean isPassed() {
-        return mDone && countResults(TestStatus.FAIL) == 0;
+    public void inProgress(boolean inProgress) {
+        mInProgress = inProgress;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public int getExpectedTestRuns() {
+        return mExpectedTestRuns;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void setExpectedTestRuns(int numRuns) {
+        mExpectedTestRuns = numRuns;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public int getTestRuns() {
+        return mActualTestRuns;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void addTestRun() {
+        mActualTestRuns++;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void resetTestRuns() {
+        mActualTestRuns = 0;
     }
 
     /**
@@ -184,7 +258,12 @@
         }
 
         this.mRuntime += otherModuleResult.getRuntime();
-        this.mDone = otherModuleResult.isDone();
+        this.mNotExecuted += otherModuleResult.getNotExecuted();
+        this.setDone(otherModuleResult.isDoneSoFar());
+        this.mActualTestRuns += otherModuleResult.getTestRuns();
+        // expected test runs are the same across shards, except for shards that do not run this
+        // module at least once (for which the value is not yet set).
+        this.mExpectedTestRuns = otherModuleResult.getExpectedTestRuns();
         for (ICaseResult otherCaseResult : otherModuleResult.getResults()) {
             ICaseResult caseResult = getOrCreateResult(otherCaseResult.getName());
             caseResult.mergeFrom(otherCaseResult);
diff --git a/common/util/src/com/android/compatibility/common/util/ResultHandler.java b/common/util/src/com/android/compatibility/common/util/ResultHandler.java
index e30efdb..2c63ae4 100644
--- a/common/util/src/com/android/compatibility/common/util/ResultHandler.java
+++ b/common/util/src/com/android/compatibility/common/util/ResultHandler.java
@@ -29,6 +29,7 @@
 import java.io.FileOutputStream;
 import java.io.FileReader;
 import java.io.IOException;
+import java.io.InputStream;
 import java.io.OutputStream;
 import java.net.InetAddress;
 import java.net.UnknownHostException;
@@ -42,7 +43,11 @@
 import java.util.List;
 import java.util.Map.Entry;
 import java.util.Set;
-
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.stream.StreamResult;
+import javax.xml.transform.stream.StreamSource;
 /**
  * Handles conversion of results to/from files.
  */
@@ -53,6 +58,8 @@
     private static final String NS = null;
     private static final String RESULT_FILE_VERSION = "5.0";
     public static final String TEST_RESULT_FILE_NAME = "test_result.xml";
+    private static final String FAILURE_REPORT_NAME = "test_result_failures.html";
+    private static final String FAILURE_XSL_FILE_NAME = "compatibility_failures.xsl";
 
     public static final String[] RESULT_RESOURCES = {
         "compatibility_result.css",
@@ -83,7 +90,7 @@
     private static final String LOG_URL_ATTR = "log_url";
     private static final String MESSAGE_ATTR = "message";
     private static final String MODULE_TAG = "Module";
-    private static final String MODULES_EXECUTED_ATTR = "modules_done";
+    private static final String MODULES_DONE_ATTR = "modules_done";
     private static final String MODULES_TOTAL_ATTR = "modules_total";
     private static final String NAME_ATTR = "name";
     private static final String NOT_EXECUTED_ATTR = "not_executed";
@@ -182,9 +189,9 @@
                     String moduleId = AbiUtils.createId(abi, name);
                     boolean done = Boolean.parseBoolean(parser.getAttributeValue(NS, DONE_ATTR));
                     IModuleResult module = invocation.getOrCreateModule(moduleId);
-                    module.setDone(done);
-                    int notExecuted =
-                            Integer.parseInt(parser.getAttributeValue(NS, NOT_EXECUTED_ATTR));
+                    module.initializeDone(done);
+                    int notExecuted = Integer.parseInt(
+                            parser.getAttributeValue(NS, NOT_EXECUTED_ATTR));
                     module.setNotExecuted(notExecuted);
                     long runtime = Long.parseLong(parser.getAttributeValue(NS, RUNTIME_ATTR));
                     module.addRuntime(runtime);
@@ -244,7 +251,7 @@
                         && !checksumReporter.containsModuleResult(
                                 module, invocation.getBuildFingerprint());
                     if (checksumMismatch) {
-                        module.setDone(false);
+                        module.initializeDone(false);
                     }
                 }
                 parser.require(XmlPullParser.END_TAG, NS, RESULT_TAG);
@@ -354,7 +361,7 @@
         serializer.attribute(NS, PASS_ATTR, Integer.toString(passed));
         serializer.attribute(NS, FAILED_ATTR, Integer.toString(failed));
         serializer.attribute(NS, NOT_EXECUTED_ATTR, Integer.toString(notExecuted));
-        serializer.attribute(NS, MODULES_EXECUTED_ATTR,
+        serializer.attribute(NS, MODULES_DONE_ATTR,
                 Integer.toString(result.getModuleCompleteCount()));
         serializer.attribute(NS, MODULES_TOTAL_ATTR,
                 Integer.toString(result.getModules().size()));
@@ -368,6 +375,8 @@
             serializer.attribute(NS, RUNTIME_ATTR, String.valueOf(module.getRuntime()));
             serializer.attribute(NS, DONE_ATTR, Boolean.toString(module.isDone()));
             serializer.attribute(NS, NOT_EXECUTED_ATTR, Integer.toString(module.getNotExecuted()));
+            serializer.attribute(NS, PASS_ATTR,
+                    Integer.toString(module.countResults(TestStatus.PASS)));
             for (ICaseResult cr : module.getResults()) {
                 serializer.startTag(NS, CASE_TAG);
                 serializer.attribute(NS, NAME_ATTR, cr.getName());
@@ -427,6 +436,22 @@
         return resultFile;
     }
 
+    /**
+     * Generate html report listing an failed tests
+     */
+    public static File createFailureReport(File inputXml) {
+        File failureReport = new File(inputXml.getParentFile(), FAILURE_REPORT_NAME);
+        try (InputStream xslStream = ResultHandler.class.getResourceAsStream(
+                String.format("/report/%s", FAILURE_XSL_FILE_NAME));
+             OutputStream outputStream = new FileOutputStream(failureReport)) {
+
+            Transformer transformer = TransformerFactory.newInstance().newTransformer(
+                    new StreamSource(xslStream));
+            transformer.transform(new StreamSource(inputXml), new StreamResult(outputStream));
+        } catch (IOException | TransformerException ignored) { }
+        return failureReport;
+    }
+
     private static void createChecksum(File resultDir, IInvocationResult invocationResult) {
         RetryChecksumStatus retryStatus = invocationResult.getRetryChecksumStatus();
         switch (retryStatus) {
diff --git a/hostsidetests/devicepolicy/app/AccountCheck/Auth/src/com/android/cts/devicepolicy/accountcheck/AccountCheckTest.java b/hostsidetests/devicepolicy/app/AccountCheck/Auth/src/com/android/cts/devicepolicy/accountcheck/AccountCheckTest.java
index 73c18af..62c0b0b 100644
--- a/hostsidetests/devicepolicy/app/AccountCheck/Auth/src/com/android/cts/devicepolicy/accountcheck/AccountCheckTest.java
+++ b/hostsidetests/devicepolicy/app/AccountCheck/Auth/src/com/android/cts/devicepolicy/accountcheck/AccountCheckTest.java
@@ -68,21 +68,21 @@
     }
 
     /**
-     * Add an incompatible account, type A.
+     * Add an incompatible account, type A, no features.
      */
     public void testAddIncompatibleA() throws Exception {
         addAccount();
     }
 
     /**
-     * Add an incompatible account, type B.
+     * Add an incompatible account, type B.  Disallow feature only.
      */
     public void testAddIncompatibleB() throws Exception {
         addAccount(ACCOUNT_FEATURE_DISALLOWED);
     }
 
     /**
-     * Add an incompatible account, type C.
+     * Add an incompatible account, type C.  Has the disallow feature.
      */
     public void testAddIncompatibleC() throws Exception {
         addAccount(ACCOUNT_FEATURE_ALLOWED, ACCOUNT_FEATURE_DISALLOWED);
diff --git a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/RequiredStrongAuthTimeoutTest.java b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/RequiredStrongAuthTimeoutTest.java
new file mode 100644
index 0000000..c236093
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/RequiredStrongAuthTimeoutTest.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.deviceandprofileowner;
+
+import android.content.ComponentName;
+
+import java.util.concurrent.TimeUnit;
+
+public class RequiredStrongAuthTimeoutTest extends BaseDeviceAdminTest {
+
+    private static final long DEFAULT_STRONG_AUTH_TIMEOUT_MS = TimeUnit.HOURS.toMillis(72);
+    private static final long MINIMUM_STRONG_AUTH_TIMEOUT_MS = TimeUnit.HOURS.toMillis(1);
+    private static final long ONE_MINUTE = TimeUnit.MINUTES.toMillis(1);
+    private static final long MIN_PLUS_ONE_MINUTE = MINIMUM_STRONG_AUTH_TIMEOUT_MS + ONE_MINUTE;
+    private static final long MAX_MINUS_ONE_MINUTE = DEFAULT_STRONG_AUTH_TIMEOUT_MS - ONE_MINUTE;
+
+    private static final ComponentName ADMIN = ADMIN_RECEIVER_COMPONENT;
+
+    public void testSetRequiredStrongAuthTimeout() throws Exception {
+        // aggregation should be the default if unset by any admin
+        assertEquals(mDevicePolicyManager.getRequiredStrongAuthTimeout(null),
+                DEFAULT_STRONG_AUTH_TIMEOUT_MS);
+
+        // admin not participating by default
+        assertEquals(mDevicePolicyManager.getRequiredStrongAuthTimeout(ADMIN), 0);
+
+        //clamping from the top
+        mDevicePolicyManager.setRequiredStrongAuthTimeout(ADMIN,
+                DEFAULT_STRONG_AUTH_TIMEOUT_MS + ONE_MINUTE);
+        assertEquals(mDevicePolicyManager.getRequiredStrongAuthTimeout(ADMIN),
+                DEFAULT_STRONG_AUTH_TIMEOUT_MS);
+        assertEquals(mDevicePolicyManager.getRequiredStrongAuthTimeout(null),
+                DEFAULT_STRONG_AUTH_TIMEOUT_MS);
+
+        // 0 means the admin is not participating, so default should be returned
+        mDevicePolicyManager.setRequiredStrongAuthTimeout(ADMIN, 0);
+        assertEquals(mDevicePolicyManager.getRequiredStrongAuthTimeout(ADMIN), 0);
+        assertEquals(mDevicePolicyManager.getRequiredStrongAuthTimeout(null),
+                DEFAULT_STRONG_AUTH_TIMEOUT_MS);
+
+        // clamping from the bottom
+        mDevicePolicyManager.setRequiredStrongAuthTimeout(ADMIN,
+                MINIMUM_STRONG_AUTH_TIMEOUT_MS - ONE_MINUTE);
+        assertEquals(mDevicePolicyManager.getRequiredStrongAuthTimeout(ADMIN),
+                MINIMUM_STRONG_AUTH_TIMEOUT_MS);
+        assertEquals(mDevicePolicyManager.getRequiredStrongAuthTimeout(null),
+                MINIMUM_STRONG_AUTH_TIMEOUT_MS);
+
+        // values within range
+        mDevicePolicyManager.setRequiredStrongAuthTimeout(ADMIN, MIN_PLUS_ONE_MINUTE);
+        assertEquals(mDevicePolicyManager.getRequiredStrongAuthTimeout(ADMIN), MIN_PLUS_ONE_MINUTE);
+        assertEquals(mDevicePolicyManager.getRequiredStrongAuthTimeout(null), MIN_PLUS_ONE_MINUTE);
+
+        mDevicePolicyManager.setRequiredStrongAuthTimeout(ADMIN, MAX_MINUS_ONE_MINUTE);
+        assertEquals(mDevicePolicyManager.getRequiredStrongAuthTimeout(ADMIN),
+                MAX_MINUS_ONE_MINUTE);
+        assertEquals(mDevicePolicyManager.getRequiredStrongAuthTimeout(null), MAX_MINUS_ONE_MINUTE);
+
+        // reset to default
+        mDevicePolicyManager.setRequiredStrongAuthTimeout(ADMIN, 0);
+        assertEquals(mDevicePolicyManager.getRequiredStrongAuthTimeout(ADMIN), 0);
+        assertEquals(mDevicePolicyManager.getRequiredStrongAuthTimeout(null),
+                DEFAULT_STRONG_AUTH_TIMEOUT_MS);
+
+        // negative value
+        try {
+            mDevicePolicyManager.setRequiredStrongAuthTimeout(ADMIN, -ONE_MINUTE);
+            fail("Didn't throw IllegalArgumentException");
+        } catch (IllegalArgumentException iae) {
+        }
+    }
+}
diff --git a/hostsidetests/devicepolicy/app/SimpleApp/src/com/android/cts/launcherapps/simpleapp/SimpleActivity.java b/hostsidetests/devicepolicy/app/SimpleApp/src/com/android/cts/launcherapps/simpleapp/SimpleActivity.java
index 628a2cd..918094c 100644
--- a/hostsidetests/devicepolicy/app/SimpleApp/src/com/android/cts/launcherapps/simpleapp/SimpleActivity.java
+++ b/hostsidetests/devicepolicy/app/SimpleApp/src/com/android/cts/launcherapps/simpleapp/SimpleActivity.java
@@ -20,6 +20,7 @@
 import android.content.Intent;
 import android.os.Bundle;
 import android.util.Log;
+import android.view.WindowManager;
 
 import java.lang.Override;
 
@@ -35,6 +36,7 @@
     @Override
     public void onCreate(Bundle icicle) {
         super.onCreate(icicle);
+        getWindow().addFlags(WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD);
         Log.i(TAG, "Created for user " + android.os.Process.myUserHandle());
     }
 
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/AccountCheckHostSideTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/AccountCheckHostSideTest.java
index 6541146..a90a3d5 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/AccountCheckHostSideTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/AccountCheckHostSideTest.java
@@ -179,53 +179,54 @@
             assertTestOnlyNotInstallable();
             assertNonTestOnlyNotInstallable();
 
-            // Incompatible, type B.
-            removeAllAccounts();
-            runTest("testAddIncompatibleB");
-
-            assertTestOnlyNotInstallable();
-            assertNonTestOnlyNotInstallable();
-
-            // Incompatible, type C.
-            removeAllAccounts();
-            runTest("testAddIncompatibleC");
-
-            assertTestOnlyNotInstallable();
-            assertNonTestOnlyNotInstallable();
-
-            // Compatible.
-            removeAllAccounts();
-            runTest("testAddCompatible");
-
-            assertTestOnlyInstallable(); // Now test-only owner can be accepted.
-            assertNonTestOnlyNotInstallable();
-
-            // 2 compatible accounts.
-            removeAllAccounts();
-            runTest("testAddCompatible");
-            runTest("testAddCompatible");
-
-            assertTestOnlyInstallable(); // Now test-only owner can be accepted.
-
-            assertNonTestOnlyNotInstallable();
-
-            // 2 compatible accounts + 1 incompatible.
-            removeAllAccounts();
-            runTest("testAddIncompatibleA");
-            runTest("testAddCompatible");
-            runTest("testAddCompatible");
-
-            assertTestOnlyNotInstallable();
-            assertNonTestOnlyNotInstallable();
-
-            // 2 compatible accounts + 1 incompatible, different order.
-            removeAllAccounts();
-            runTest("testAddCompatible");
-            runTest("testAddCompatible");
-            runTest("testAddIncompatibleB");
-
-            assertTestOnlyNotInstallable();
-            assertNonTestOnlyNotInstallable();
+            // The following tests use non-public strings, so disabled until they go public.
+//            // Incompatible, type B.
+//            removeAllAccounts();
+//            runTest("testAddIncompatibleB");
+//
+//            assertTestOnlyNotInstallable();
+//            assertNonTestOnlyNotInstallable();
+//
+//            // Incompatible, type C.
+//            removeAllAccounts();
+//            runTest("testAddIncompatibleC");
+//
+//            assertTestOnlyNotInstallable();
+//            assertNonTestOnlyNotInstallable();
+//
+//            // Compatible.
+//            removeAllAccounts();
+//            runTest("testAddCompatible");
+//
+//            assertTestOnlyInstallable(); // Now test-only owner can be accepted.
+//            assertNonTestOnlyNotInstallable();
+//
+//            // 2 compatible accounts.
+//            removeAllAccounts();
+//            runTest("testAddCompatible");
+//            runTest("testAddCompatible");
+//
+//            assertTestOnlyInstallable(); // Now test-only owner can be accepted.
+//
+//            assertNonTestOnlyNotInstallable();
+//
+//            // 2 compatible accounts + 1 incompatible.
+//            removeAllAccounts();
+//            runTest("testAddIncompatibleA");
+//            runTest("testAddCompatible");
+//            runTest("testAddCompatible");
+//
+//            assertTestOnlyNotInstallable();
+//            assertNonTestOnlyNotInstallable();
+//
+//            // 2 compatible accounts + 1 incompatible, different order.
+//            removeAllAccounts();
+//            runTest("testAddCompatible");
+//            runTest("testAddCompatible");
+//            runTest("testAddIncompatibleB");
+//
+//            assertTestOnlyNotInstallable();
+//            assertNonTestOnlyNotInstallable();
         } catch (Throwable th) {
             CLog.w("Tests failed; current accounts are:");
             CLog.w(getDevice().executeShellCommand("dumpsys account"));
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAndProfileOwnerTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAndProfileOwnerTest.java
index c2ee3f7..c29b294 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAndProfileOwnerTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAndProfileOwnerTest.java
@@ -628,6 +628,13 @@
 
     }
 
+    public void testRequiredStrongAuthTimeout() throws Exception {
+        if (!mHasFeature) {
+            return;
+        }
+        executeDeviceTestClass(".RequiredStrongAuthTimeoutTest");
+    }
+
     protected void executeDeviceTestClass(String className) throws Exception {
         runDeviceTestsAsUser(DEVICE_ADMIN_PKG, className, mUserId);
     }
diff --git a/hostsidetests/os/src/android/os/cts/StaticSharedLibsHostTests.java b/hostsidetests/os/src/android/os/cts/StaticSharedLibsHostTests.java
index 185363c..7452d22 100644
--- a/hostsidetests/os/src/android/os/cts/StaticSharedLibsHostTests.java
+++ b/hostsidetests/os/src/android/os/cts/StaticSharedLibsHostTests.java
@@ -39,6 +39,12 @@
     private static final String STATIC_LIB_PROVIDER4_APK = "CtsStaticSharedLibProviderApp4.apk";
     private static final String STATIC_LIB_PROVIDER4_PKG = "android.os.lib.provider";
 
+    private static final String STATIC_LIB_PROVIDER5_APK = "CtsStaticSharedLibProviderApp5.apk";
+    private static final String STATIC_LIB_PROVIDER5_PKG = "android.os.lib.provider";
+
+    private static final String STATIC_LIB_PROVIDER6_APK = "CtsStaticSharedLibProviderApp6.apk";
+    private static final String STATIC_LIB_PROVIDER6_PKG = "android.os.lib.provider";
+
     private static final String STATIC_LIB_CONSUMER1_APK = "CtsStaticSharedLibConsumerApp1.apk";
     private static final String STATIC_LIB_CONSUMER1_PKG = "android.os.lib.consumer1";
 
@@ -224,6 +230,22 @@
         }
     }
 
+    public void testLibraryAndPackageNameCanMatch() throws Exception {
+        getDevice().uninstallPackage(STATIC_LIB_PROVIDER5_PKG);
+        getDevice().uninstallPackage(STATIC_LIB_PROVIDER6_PKG);
+        try {
+            // Install a library with same name as package should work.
+            assertNull(getDevice().installPackage(mBuildHelper.getTestFile(
+                    STATIC_LIB_PROVIDER5_APK), false, false));
+            // Install a library with same name as package should work.
+            assertNull(getDevice().installPackage(mBuildHelper.getTestFile(
+                    STATIC_LIB_PROVIDER6_APK), true, false));
+        } finally {
+            getDevice().uninstallPackage(STATIC_LIB_PROVIDER5_PKG);
+            getDevice().uninstallPackage(STATIC_LIB_PROVIDER6_PKG);
+        }
+    }
+
     public void testGetSharedLibraries() throws Exception {
         getDevice().uninstallPackage(STATIC_LIB_CONSUMER1_PKG);
         getDevice().uninstallPackage(STATIC_LIB_CONSUMER2_PKG);
diff --git a/hostsidetests/os/test-apps/StaticSharedLibProviderApp5/Android.mk b/hostsidetests/os/test-apps/StaticSharedLibProviderApp5/Android.mk
new file mode 100644
index 0000000..a9df0f8
--- /dev/null
+++ b/hostsidetests/os/test-apps/StaticSharedLibProviderApp5/Android.mk
@@ -0,0 +1,33 @@
+#
+# Copyright (C) 2017 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_SDK_VERSION := current
+
+LOCAL_PACKAGE_NAME := CtsStaticSharedLibProviderApp5
+
+LOCAL_COMPATIBILITY_SUITE := cts
+
+LOCAL_AAPT_FLAGS := --shared-lib
+
+LOCAL_EXPORT_PACKAGE_RESOURCES := true
+
+include $(BUILD_CTS_SUPPORT_PACKAGE)
diff --git a/hostsidetests/os/test-apps/StaticSharedLibProviderApp5/AndroidManifest.xml b/hostsidetests/os/test-apps/StaticSharedLibProviderApp5/AndroidManifest.xml
new file mode 100755
index 0000000..404a0fa
--- /dev/null
+++ b/hostsidetests/os/test-apps/StaticSharedLibProviderApp5/AndroidManifest.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+        package="android.os.lib.provider"
+        android:versionCode="1"
+        android:versionName="1.0">
+    <application android:hasCode="false">
+        <static-library android:name="android.os.lib.provider_2" android:version="1"/>
+    </application>
+</manifest>
diff --git a/hostsidetests/os/test-apps/StaticSharedLibProviderApp6/Android.mk b/hostsidetests/os/test-apps/StaticSharedLibProviderApp6/Android.mk
new file mode 100644
index 0000000..3c06105
--- /dev/null
+++ b/hostsidetests/os/test-apps/StaticSharedLibProviderApp6/Android.mk
@@ -0,0 +1,33 @@
+#
+# Copyright (C) 2017 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_SDK_VERSION := current
+
+LOCAL_PACKAGE_NAME := CtsStaticSharedLibProviderApp6
+
+LOCAL_COMPATIBILITY_SUITE := cts
+
+LOCAL_AAPT_FLAGS := --shared-lib
+
+LOCAL_EXPORT_PACKAGE_RESOURCES := true
+
+include $(BUILD_CTS_SUPPORT_PACKAGE)
diff --git a/hostsidetests/os/test-apps/StaticSharedLibProviderApp6/AndroidManifest.xml b/hostsidetests/os/test-apps/StaticSharedLibProviderApp6/AndroidManifest.xml
new file mode 100755
index 0000000..7619998f
--- /dev/null
+++ b/hostsidetests/os/test-apps/StaticSharedLibProviderApp6/AndroidManifest.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+        package="android.os.lib.provider"
+        android:versionCode="2"
+        android:versionName="2.0">
+    <application android:hasCode="false">
+        <static-library android:name="android.os.lib.provider_2" android:version="2"/>
+    </application>
+</manifest>
diff --git a/hostsidetests/theme/assets/hdpi.zip b/hostsidetests/theme/assets/hdpi.zip
new file mode 100644
index 0000000..6cc2d50
--- /dev/null
+++ b/hostsidetests/theme/assets/hdpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/mdpi.zip b/hostsidetests/theme/assets/mdpi.zip
new file mode 100644
index 0000000..a350ff6
--- /dev/null
+++ b/hostsidetests/theme/assets/mdpi.zip
Binary files differ
diff --git a/tests/app/src/android/app/cts/ActivityManagerTest.java b/tests/app/src/android/app/cts/ActivityManagerTest.java
index 07dc354..910af71 100644
--- a/tests/app/src/android/app/cts/ActivityManagerTest.java
+++ b/tests/app/src/android/app/cts/ActivityManagerTest.java
@@ -781,7 +781,7 @@
         } finally {
             conn.stopMonitoring();
 
-            cmd = "appops set " + SIMPLE_PACKAGE_NAME + " RUN_IN_BACKGROUND normal";
+            cmd = "appops set " + SIMPLE_PACKAGE_NAME + " RUN_IN_BACKGROUND allow";
             result = SystemUtil.runShellCommand(getInstrumentation(), cmd);
 
             am.removeOnUidImportanceListener(uidGoneListener);
diff --git a/tests/app/src/android/app/cts/SystemFeaturesTest.java b/tests/app/src/android/app/cts/SystemFeaturesTest.java
index 979d18a..f03c549 100644
--- a/tests/app/src/android/app/cts/SystemFeaturesTest.java
+++ b/tests/app/src/android/app/cts/SystemFeaturesTest.java
@@ -474,6 +474,10 @@
         if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE) &&
                 !mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEVISION) &&
                 !mPackageManager.hasSystemFeature(PackageManager.FEATURE_WATCH)) {
+            // 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.
             assertAvailable(PackageManager.FEATURE_USB_ACCESSORY);
         }
     }
diff --git a/tests/tests/assist/common/src/android/assist/common/Utils.java b/tests/tests/assist/common/src/android/assist/common/Utils.java
old mode 100644
new mode 100755
index 54416b4..3f98cb3
--- a/tests/tests/assist/common/src/android/assist/common/Utils.java
+++ b/tests/tests/assist/common/src/android/assist/common/Utils.java
@@ -64,6 +64,7 @@
     /** Flag Secure Test intent constants */
     public static final String FLAG_SECURE_HASRESUMED = ACTION_PREFIX + "flag_secure_hasResumed";
     public static final String APP_3P_HASRESUMED = ACTION_PREFIX + "app_3p_hasResumed";
+    public static final String APP_3P_HASDRAWED = ACTION_PREFIX + "app_3p_hasDrawed";
     public static final String TEST_ACTIVITY_LOADED = ACTION_PREFIX + "test_activity_hasResumed";
 
     /** Two second timeout for getting back assist context */
diff --git a/tests/tests/assist/src/android/assist/cts/AssistStructureTest.java b/tests/tests/assist/src/android/assist/cts/AssistStructureTest.java
old mode 100644
new mode 100755
index d0a00c9..a34d09b
--- a/tests/tests/assist/src/android/assist/cts/AssistStructureTest.java
+++ b/tests/tests/assist/src/android/assist/cts/AssistStructureTest.java
@@ -39,6 +39,7 @@
 
     private BroadcastReceiver mReceiver;
     private CountDownLatch mHasResumedLatch = new CountDownLatch(1);
+    private CountDownLatch mHasDrawedLatch = new CountDownLatch(1);
     private CountDownLatch mReadyLatch = new CountDownLatch(1);
 
     public AssistStructureTest() {
@@ -68,6 +69,7 @@
         mReceiver = new AssistStructureTestBroadcastReceiver();
         IntentFilter filter = new IntentFilter();
         filter.addAction(Utils.APP_3P_HASRESUMED);
+        filter.addAction(Utils.APP_3P_HASDRAWED);
         filter.addAction(Utils.ASSIST_RECEIVER_REGISTERED);
         mContext.registerReceiver(mReceiver, filter);
     }
@@ -77,6 +79,15 @@
         if (!mHasResumedLatch.await(Utils.ACTIVITY_ONRESUME_TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
             fail("Activity failed to resume in " + Utils.ACTIVITY_ONRESUME_TIMEOUT_MS + "msec");
         }
+
+    }
+
+    private void waitForOnDraw() throws Exception {
+        Log.i(TAG, "waiting for onDraw() before continuing");
+        if (!mHasDrawedLatch.await(Utils.ACTIVITY_ONRESUME_TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
+            fail("Activity failed to draw in " + Utils.ACTIVITY_ONRESUME_TIMEOUT_MS + "msec");
+        }
+
     }
 
     public void testAssistStructure() throws Throwable {
@@ -88,6 +99,7 @@
         mTestActivity.startTest(TEST_CASE_TYPE);
         waitForAssistantToBeReady(mReadyLatch);
         waitForOnResume();
+        waitForOnDraw();
         startSession();
         waitForContext();
         getInstrumentation().waitForIdleSync();
@@ -112,6 +124,10 @@
                 if (mReadyLatch != null) {
                     mReadyLatch.countDown();
                 }
+            }else if (action.equals(Utils.APP_3P_HASDRAWED)) {
+                if (mHasDrawedLatch != null) {
+                    mHasDrawedLatch.countDown();
+                }
             }
         }
     }
diff --git a/tests/tests/assist/testapp/src/android/voiceinteraction/testapp/TestApp.java b/tests/tests/assist/testapp/src/android/voiceinteraction/testapp/TestApp.java
old mode 100644
new mode 100755
index e0f83cc..7ef9e8c
--- a/tests/tests/assist/testapp/src/android/voiceinteraction/testapp/TestApp.java
+++ b/tests/tests/assist/testapp/src/android/voiceinteraction/testapp/TestApp.java
@@ -71,4 +71,10 @@
             }
         });
     }
+
+    public void onEnterAnimationComplete() {
+        Log.i(TAG, "TestApp onEnterAnimationComplete ");
+        sendBroadcast(new Intent(Utils.APP_3P_HASDRAWED));
+    }
+
 }
\ No newline at end of file
diff --git a/tests/tests/display/src/android/display/cts/DisplayTest.java b/tests/tests/display/src/android/display/cts/DisplayTest.java
index 4e24981..5992add 100644
--- a/tests/tests/display/src/android/display/cts/DisplayTest.java
+++ b/tests/tests/display/src/android/display/cts/DisplayTest.java
@@ -173,6 +173,7 @@
         assertFalse(cap.getDesiredMaxLuminance() < -1.0f);
         assertFalse(cap.getDesiredMinLuminance() < -1.0f);
         assertFalse(cap.getDesiredMaxAverageLuminance() < -1.0f);
+        assertFalse(display.isHdr());
     }
 
     /**
@@ -205,6 +206,8 @@
         assertTrue(0 < display.getRefreshRate());
 
         assertTrue(display.getName().contains(OVERLAY_DISPLAY_NAME_PREFIX));
+
+        assertFalse(display.isWideColorGamut());
     }
 
     /**
diff --git a/tests/tests/graphics/src/android/graphics/cts/CanvasTest.java b/tests/tests/graphics/src/android/graphics/cts/CanvasTest.java
index a4b57bd..e98063d 100644
--- a/tests/tests/graphics/src/android/graphics/cts/CanvasTest.java
+++ b/tests/tests/graphics/src/android/graphics/cts/CanvasTest.java
@@ -774,6 +774,24 @@
     }
 
     @Test
+    public void testRestoreToCountExceptionBehavior() {
+        int restoreTo = mCanvas.save();
+        mCanvas.save();
+        int beforeCount = mCanvas.getSaveCount();
+
+        boolean exceptionObserved = false;
+        try {
+            mCanvas.restoreToCount(restoreTo - 1);
+        } catch (IllegalArgumentException e) {
+            exceptionObserved = true;
+        }
+
+        // restore to count threw, AND did no restoring
+        assertTrue(exceptionObserved);
+        assertEquals(beforeCount, mCanvas.getSaveCount());
+    }
+
+    @Test
     public void testRestoreToCount() {
         final Matrix m1 = new Matrix();
         m1.setValues(values1);
diff --git a/tests/tests/hardware/Android.mk b/tests/tests/hardware/Android.mk
index 723a3f8..6e14198 100644
--- a/tests/tests/hardware/Android.mk
+++ b/tests/tests/hardware/Android.mk
@@ -1,4 +1,4 @@
-# Copyright (C) 2008 The Android Open Source Project
+# Copyright (C) 2017 The Android Open Source Project
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
@@ -14,29 +14,34 @@
 
 LOCAL_PATH:= $(call my-dir)
 
-# CtsHardwareTestCases package
-
 include $(CLEAR_VARS)
 
+# don't include this package in any target
 LOCAL_MODULE_TAGS := tests
-
+# and when built explicitly put it in the data partition
 LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
 
 # Tag this module as a cts test artifact
 LOCAL_COMPATIBILITY_SUITE := cts
 
-LOCAL_STATIC_JAVA_LIBRARIES := \
-    compatibility-device-util \
-    ctstestrunner \
-    mockito-target-minus-junit4 \
-    android-ex-camera2
-
-LOCAL_SRC_FILES := $(call all-java-files-under, src) $(call all-renderscript-files-under, src)
-
-LOCAL_PACKAGE_NAME := CtsHardwareTestCases
-
-LOCAL_SDK_VERSION := current
+LOCAL_MULTILIB := both
 
 LOCAL_JAVA_LIBRARIES := android.test.runner
 
+LOCAL_STATIC_JAVA_LIBRARIES := \
+    android-support-test \
+    compatibility-device-util \
+    ctstestrunner \
+    mockito-target-minus-junit4 \
+    platform-test-annotations \
+    ub-uiautomator
+
+LOCAL_JNI_SHARED_LIBRARIES := libctshardware_jni libnativehelper_compat_libc++
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_PACKAGE_NAME := CtsHardwareTestCases
+
 include $(BUILD_CTS_PACKAGE)
+
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/tests/tests/hardware/jni/Android.mk b/tests/tests/hardware/jni/Android.mk
new file mode 100644
index 0000000..0cd95e7
--- /dev/null
+++ b/tests/tests/hardware/jni/Android.mk
@@ -0,0 +1,37 @@
+# Copyright (C) 2017 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := libctshardware_jni
+
+LOCAL_CFLAGS += -Werror
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_SRC_FILES := \
+		CtsHardwareJniOnLoad.cpp \
+		android_hardware_cts_HardwareBufferTest.cpp
+
+LOCAL_C_INCLUDES := $(JNI_H_INCLUDE)
+
+LOCAL_SHARED_LIBRARIES := libandroid libnativehelper_compat_libc++ liblog
+
+LOCAL_CXX_STL := libc++_static
+
+LOCAL_CLANG := true
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/tests/tests/hardware/jni/CtsHardwareJniOnLoad.cpp b/tests/tests/hardware/jni/CtsHardwareJniOnLoad.cpp
new file mode 100644
index 0000000..2f607c9
--- /dev/null
+++ b/tests/tests/hardware/jni/CtsHardwareJniOnLoad.cpp
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <jni.h>
+#include <stdio.h>
+
+extern int register_android_hardware_cts_HardwareBufferTest(JNIEnv*);
+
+jint JNI_OnLoad(JavaVM* vm, void* /*reserved*/) {
+    JNIEnv* env = nullptr;
+    if (vm->GetEnv((void**)&env, JNI_VERSION_1_4) != JNI_OK)
+        return JNI_ERR;
+    if (register_android_hardware_cts_HardwareBufferTest(env))
+        return JNI_ERR;
+    return JNI_VERSION_1_4;
+}
diff --git a/tests/tests/hardware/jni/android_hardware_cts_HardwareBufferTest.cpp b/tests/tests/hardware/jni/android_hardware_cts_HardwareBufferTest.cpp
new file mode 100644
index 0000000..59c1637
--- /dev/null
+++ b/tests/tests/hardware/jni/android_hardware_cts_HardwareBufferTest.cpp
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <android/hardware_buffer.h>
+
+#include <jni.h>
+
+#include <android/hardware_buffer_jni.h>
+#include <utils/Errors.h>
+
+#define LOG_TAG "HardwareBufferTest"
+
+static jobject android_hardware_HardwareBuffer_nativeCreateHardwareBuffer(JNIEnv* env, jclass,
+        jint width, jint height, jint format, jint layers, jlong usage) {
+    AHardwareBuffer* buffer = NULL;
+    AHardwareBuffer_Desc desc;
+
+    desc.width = width;
+    desc.height = height;
+    desc.layers = layers;
+    desc.usage0 = usage;
+    desc.usage1 = 0;
+    desc.format = format;
+    int res = AHardwareBuffer_allocate(&desc, &buffer);
+    if (res == android::NO_ERROR) {
+        return AHardwareBuffer_toHardwareBuffer(env, buffer);
+    } else {
+        return 0;
+    }
+}
+
+static void android_hardware_HardwareBuffer_nativeReleaseHardwareBuffer(JNIEnv* env, jclass,
+        jobject hardwareBufferObj) {
+    AHardwareBuffer* buffer = AHardwareBuffer_fromHardwareBuffer(env, hardwareBufferObj);
+    AHardwareBuffer_release(buffer);
+}
+
+static JNINativeMethod gMethods[] = {
+    { "nativeCreateHardwareBuffer", "(IIIIJ)Landroid/hardware/HardwareBuffer;",
+            (void *) android_hardware_HardwareBuffer_nativeCreateHardwareBuffer },
+    { "nativeReleaseHardwareBuffer", "(Landroid/hardware/HardwareBuffer;)V",
+           (void *) android_hardware_HardwareBuffer_nativeReleaseHardwareBuffer },
+};
+
+int register_android_hardware_cts_HardwareBufferTest(JNIEnv* env)
+{
+    jclass clazz = env->FindClass("android/hardware/cts/HardwareBufferTest");
+    return env->RegisterNatives(clazz, gMethods,
+            sizeof(gMethods) / sizeof(JNINativeMethod));
+}
diff --git a/tests/tests/hardware/src/android/hardware/cts/HardwareBufferTest.java b/tests/tests/hardware/src/android/hardware/cts/HardwareBufferTest.java
new file mode 100644
index 0000000..6d1f0d1
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/cts/HardwareBufferTest.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.cts;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import android.graphics.PixelFormat;
+import android.hardware.HardwareBuffer;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Test {@link HardwareBuffer}.
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class HardwareBufferTest {
+    private static native HardwareBuffer nativeCreateHardwareBuffer(int width, int height,
+            int format, int layers, long usage);
+    private static native void nativeReleaseHardwareBuffer(HardwareBuffer hardwareBufferObj);
+
+    static {
+        System.loadLibrary("ctshardware_jni");
+    }
+
+    @Test
+    public void testCreate() {
+        HardwareBuffer buffer = HardwareBuffer.create(2, 4, HardwareBuffer.RGBA_8888, 1,
+                HardwareBuffer.USAGE0_CPU_READ);
+        assertTrue(buffer != null);
+        assertEquals(2, buffer.getWidth());
+        assertEquals(4, buffer.getHeight());
+        assertEquals(HardwareBuffer.RGBA_8888, buffer.getFormat());
+        assertEquals(1, buffer.getLayers());
+        assertEquals(HardwareBuffer.USAGE0_CPU_READ, buffer.getUsage());
+
+        buffer = HardwareBuffer.create(2, 4, HardwareBuffer.RGBX_8888, 1,
+                HardwareBuffer.USAGE0_CPU_READ);
+        assertEquals(HardwareBuffer.RGBX_8888, buffer.getFormat());
+        buffer = HardwareBuffer.create(2, 4, HardwareBuffer.RGB_888, 1,
+                HardwareBuffer.USAGE0_CPU_READ);
+        assertEquals(HardwareBuffer.RGB_888, buffer.getFormat());
+        buffer = HardwareBuffer.create(2, 4, HardwareBuffer.RGB_565, 1,
+                HardwareBuffer.USAGE0_CPU_READ);
+        assertEquals(HardwareBuffer.RGB_565, buffer.getFormat());
+    }
+
+    @Test
+    public void testCreateFailsWithInvalidArguments() {
+        HardwareBuffer buffer = null;
+        assertEquals(null, buffer);
+        try {
+            buffer = HardwareBuffer.create(0, 4, HardwareBuffer.RGB_888, 1,
+                    HardwareBuffer.USAGE0_CPU_READ);
+        } catch (IllegalArgumentException e) {}
+        assertEquals(null, buffer);
+        try {
+            buffer = HardwareBuffer.create(2, 0, HardwareBuffer.RGB_888, 1,
+                    HardwareBuffer.USAGE0_CPU_READ);
+        } catch (IllegalArgumentException e) {}
+        assertEquals(null, buffer);
+        try {
+            buffer = HardwareBuffer.create(2, 4, 0, 1,
+                    HardwareBuffer.USAGE0_CPU_READ);
+        } catch (IllegalArgumentException e) {}
+        assertEquals(null, buffer);
+        try {
+            buffer = HardwareBuffer.create(2, 4, HardwareBuffer.RGB_888, -1,
+                    HardwareBuffer.USAGE0_CPU_READ);
+        } catch (IllegalArgumentException e) {}
+        assertEquals(null, buffer);
+    }
+
+    @Test
+    public void testCreateFromNativeObject() {
+        HardwareBuffer buffer = nativeCreateHardwareBuffer(2, 4, HardwareBuffer.RGBA_8888, 1,
+                    HardwareBuffer.USAGE0_CPU_READ);
+        assertTrue(buffer != null);
+        assertEquals(2, buffer.getWidth());
+        assertEquals(4, buffer.getHeight());
+        assertEquals(HardwareBuffer.RGBA_8888, buffer.getFormat());
+        assertEquals(1, buffer.getLayers());
+        assertEquals(HardwareBuffer.USAGE0_CPU_READ, buffer.getUsage());
+        nativeReleaseHardwareBuffer(buffer);
+    }
+}
diff --git a/tests/tests/nativehardware/Android.mk b/tests/tests/nativehardware/Android.mk
new file mode 100644
index 0000000..c19511e
--- /dev/null
+++ b/tests/tests/nativehardware/Android.mk
@@ -0,0 +1,45 @@
+# Copyright (C) 2017 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Build the unit tests.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE:= CtsNativeHardwareTestCases
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA)/nativetest
+LOCAL_MULTILIB := both
+LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
+LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
+
+#LOCAL_C_INCLUDES := \
+#    $(call include-path-for, wilhelm) \
+#    $(call include-path-for, wilhelm-ut)
+
+LOCAL_SRC_FILES := \
+    src/AHardwareBufferTest.cpp
+
+LOCAL_SHARED_LIBRARIES := \
+  libandroid \
+  libutils \
+  liblog \
+
+LOCAL_STATIC_LIBRARIES := \
+  libgtest \
+
+LOCAL_CTS_TEST_PACKAGE := android.nativehardware
+# Tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts
+
+include $(BUILD_CTS_EXECUTABLE)
diff --git a/tests/tests/nativehardware/AndroidTest.xml b/tests/tests/nativehardware/AndroidTest.xml
new file mode 100644
index 0000000..8fec974
--- /dev/null
+++ b/tests/tests/nativehardware/AndroidTest.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<configuration description="Config for CTS Native Hardware test cases">
+    <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
+        <option name="cleanup" value="true" />
+        <option name="push" value="CtsNativeHardwareTestCases->/data/local/tmp/CtsNativeHardwareTestCases" />
+        <option name="append-bitness" value="true" />
+    </target_preparer>
+    <test class="com.android.tradefed.testtype.GTest" >
+        <option name="native-test-device-path" value="/data/local/tmp" />
+        <option name="module-name" value="CtsNativeHardwareTestCases" />
+        <option name="runtime-hint" value="1s" />
+    </test>
+</configuration>
diff --git a/tests/tests/nativehardware/src/AHardwareBufferTest.cpp b/tests/tests/nativehardware/src/AHardwareBufferTest.cpp
new file mode 100644
index 0000000..37a51e4
--- /dev/null
+++ b/tests/tests/nativehardware/src/AHardwareBufferTest.cpp
@@ -0,0 +1,328 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "AHardwareBuffer_test"
+//#define LOG_NDEBUG 0
+
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include <android/hardware_buffer.h>
+#include <ui/GraphicBuffer.h>
+#include <utils/Errors.h>
+
+#include <gtest/gtest.h>
+
+using namespace android;
+
+// Helper routines for checking that allocation was successful; depend on
+// internal implementation details.
+static inline GraphicBuffer* AHardwareBuffer_to_GraphicBuffer(
+        AHardwareBuffer* buffer) {
+    return reinterpret_cast<GraphicBuffer*>(buffer);
+}
+
+static inline AHardwareBuffer* GraphicBuffer_to_AHardwareBuffer(
+        GraphicBuffer* buffer) {
+    return reinterpret_cast<AHardwareBuffer*>(buffer);
+}
+
+// This function is temporarily necessary to convert between bit versions.
+static inline uint32_t convertGralloc1ToGralloc0UsageBits(uint64_t usage0,
+        uint64_t usage1) {
+    uint32_t bits = 0;
+    if (usage0 & AHARDWAREBUFFER_USAGE0_CPU_READ)
+        bits |= GRALLOC_USAGE_SW_READ_RARELY;
+    if (usage0 & AHARDWAREBUFFER_USAGE0_CPU_READ_OFTEN)
+        bits |= GRALLOC_USAGE_SW_READ_OFTEN;
+    if (usage0 & AHARDWAREBUFFER_USAGE0_CPU_WRITE)
+        bits |= GRALLOC_USAGE_SW_WRITE_RARELY;
+    if (usage0 & AHARDWAREBUFFER_USAGE0_CPU_WRITE_OFTEN)
+        bits |= GRALLOC_USAGE_SW_WRITE_OFTEN;
+    if (usage0 & AHARDWAREBUFFER_USAGE0_GPU_SAMPLED_IMAGE)
+        bits |= GRALLOC_USAGE_HW_TEXTURE;
+    if (usage0 & AHARDWAREBUFFER_USAGE0_GPU_COLOR_OUTPUT)
+        bits |= GRALLOC_USAGE_HW_RENDER;
+    // Not sure what this should be.
+    if (usage0 & AHARDWAREBUFFER_USAGE0_GPU_STORAGE_IMAGE) bits |= 0;
+    // Not sure what this should be.
+    if (usage0 & AHARDWAREBUFFER_USAGE0_GPU_CUBEMAP) bits |= 0;
+    // Not yet supported in gralloc1.
+    // if (usage0 & AHARDWAREBUFFER_USAGE0_GPU_DATA_BUFFER) bits |= 0;
+    if (usage0 & AHARDWAREBUFFER_USAGE0_VIDEO_ENCODE)
+        bits |= GRALLOC_USAGE_HW_VIDEO_ENCODER;
+    if (usage0 & AHARDWAREBUFFER_USAGE0_PROTECTED_CONTENT)
+        bits |= GRALLOC_USAGE_PROTECTED;
+
+    (void)usage1;
+
+    return bits;
+}
+
+static uint32_t convertFromGraphicBufferFormat(uint32_t format) {
+    switch (format) {
+        case PIXEL_FORMAT_RGBA_8888:
+            return AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM;
+        case PIXEL_FORMAT_RGBA_FP16:
+            return AHARDWAREBUFFER_FORMAT_R16G16B16A16_SFLOAT;
+        case PIXEL_FORMAT_RGBX_8888:
+            return AHARDWAREBUFFER_FORMAT_R8G8B8X8_UNORM;
+        case PIXEL_FORMAT_RGB_565:
+            return AHARDWAREBUFFER_FORMAT_R5G6B5_UNORM;
+        case PIXEL_FORMAT_RGB_888:
+            return AHARDWAREBUFFER_FORMAT_R8G8B8_UNORM;
+        default:
+            return 0;
+    }
+}
+
+static ::testing::AssertionResult BuildFailureMessage(uint32_t expected,
+        uint32_t actual, const char* type) {
+    return ::testing::AssertionFailure() << "Buffer " << type << " do not match"
+            << ": " << actual << " != " << expected;
+}
+
+static ::testing::AssertionResult CheckAHardwareBufferMatchesDesc(
+        AHardwareBuffer* abuffer, const AHardwareBuffer_Desc& desc) {
+    GraphicBuffer* buffer = AHardwareBuffer_to_GraphicBuffer(abuffer);
+    if (static_cast<uint32_t>(buffer->width) != desc.width)
+        return BuildFailureMessage(desc.width,
+                static_cast<uint32_t>(buffer->width), "widths");
+    if (static_cast<uint32_t>(buffer->height) != desc.height)
+        return BuildFailureMessage(desc.height,
+                static_cast<uint32_t>(buffer->height), "heights");
+    if (static_cast<uint32_t>(buffer->layerCount) != desc.layers)
+        return BuildFailureMessage(desc.layers,
+                static_cast<uint32_t>(buffer->layerCount), "layers");
+    if (static_cast<uint32_t>(buffer->usage) !=
+            convertGralloc1ToGralloc0UsageBits(desc.usage0, desc.usage1))
+        return BuildFailureMessage(
+                convertGralloc1ToGralloc0UsageBits(desc.usage0, desc.usage1),
+                static_cast<uint32_t>(buffer->usage), "usages");
+    if (convertFromGraphicBufferFormat(buffer->getPixelFormat()) != desc.format)
+        return BuildFailureMessage(desc.format,
+                static_cast<uint32_t>(buffer->format), "formats");
+    return ::testing::AssertionSuccess();
+}
+
+// Test that passing in NULL values to allocate works as expected.
+TEST(AHardwareBufferTest, AHardwareBuffer_allocate_FailsWithNullInput) {
+    AHardwareBuffer* buffer;
+    AHardwareBuffer_Desc desc;
+
+    memset(&desc, 0, sizeof(AHardwareBuffer_Desc));
+
+    int res = AHardwareBuffer_allocate(&desc, NULL);
+    EXPECT_EQ(BAD_VALUE, res);
+    res = AHardwareBuffer_allocate(NULL, &buffer);
+    EXPECT_EQ(BAD_VALUE, res);
+    res = AHardwareBuffer_allocate(NULL, NULL);
+    EXPECT_EQ(BAD_VALUE, res);
+}
+
+// Test that allocate can create an AHardwareBuffer correctly.
+TEST(AHardwareBufferTest, AHardwareBuffer_allocate_Succeeds) {
+    AHardwareBuffer* buffer = NULL;
+    AHardwareBuffer_Desc desc;
+
+    desc.width = 2;
+    desc.height = 4;
+    desc.layers = 1;
+    desc.usage0 = AHARDWAREBUFFER_USAGE0_GPU_SAMPLED_IMAGE;
+    desc.usage1 = 0;
+    desc.format = AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM;
+    int res = AHardwareBuffer_allocate(&desc, &buffer);
+    EXPECT_EQ(NO_ERROR, res);
+    EXPECT_TRUE(CheckAHardwareBufferMatchesDesc(buffer, desc));
+    AHardwareBuffer_release(buffer);
+    buffer = NULL;
+
+    desc.width = 4;
+    desc.height = 12;
+    desc.layers = 1;
+    desc.usage0 = AHARDWAREBUFFER_USAGE0_GPU_SAMPLED_IMAGE;
+    desc.usage1 = 0;
+    desc.format = AHARDWAREBUFFER_FORMAT_R5G6B5_UNORM;
+    res = AHardwareBuffer_allocate(&desc, &buffer);
+    EXPECT_EQ(NO_ERROR, res);
+    GraphicBuffer* gb = AHardwareBuffer_to_GraphicBuffer(buffer);
+    EXPECT_TRUE(CheckAHardwareBufferMatchesDesc(buffer, desc));
+    AHardwareBuffer_release(buffer);
+}
+
+TEST(AHardwareBufferTest, AHardwareBuffer_describe_Succeeds) {
+    AHardwareBuffer* buffer = NULL;
+    AHardwareBuffer_Desc desc;
+
+    desc.width = 2;
+    desc.height = 4;
+    desc.layers = 1;
+    desc.usage0 = AHARDWAREBUFFER_USAGE0_GPU_SAMPLED_IMAGE;
+    desc.usage1 = 0;
+    desc.format = AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM;
+    int res = AHardwareBuffer_allocate(&desc, &buffer);
+    EXPECT_EQ(NO_ERROR, res);
+
+    AHardwareBuffer_Desc expected_desc;
+    memset(&expected_desc, 0, sizeof(AHardwareBuffer_Desc));
+    AHardwareBuffer_describe(NULL, &desc);
+    EXPECT_EQ(0U, expected_desc.width);
+    AHardwareBuffer_describe(buffer, NULL);
+    EXPECT_EQ(0U, expected_desc.width);
+    AHardwareBuffer_describe(buffer, &desc);
+    EXPECT_TRUE(CheckAHardwareBufferMatchesDesc(buffer, desc));
+
+    AHardwareBuffer_release(buffer);
+}
+
+struct ClientData {
+    const char* path;
+    AHardwareBuffer* buffer;
+    ClientData(const char* path_in, AHardwareBuffer* buffer_in)
+            : path(path_in), buffer(buffer_in) {}
+};
+
+static void* clientFunction(void* data) {
+    ClientData* pdata = reinterpret_cast<ClientData*>(data);
+
+    int fd = socket(PF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0);
+    if (fd < 0) {
+        GTEST_LOG_(ERROR) << "Client socket call failed: " << strerror(errno);
+        return reinterpret_cast<void*>(-1);
+    }
+
+    struct sockaddr_un addr;
+    memset(&addr, 0, sizeof(addr));
+    addr.sun_family = AF_UNIX;
+    strcpy(addr.sun_path, pdata->path);
+
+    if (connect(fd, reinterpret_cast<struct sockaddr*>(&addr),
+            sizeof(addr)) < 0) {
+        GTEST_LOG_(ERROR) << "Client connect call failed: " << strerror(errno);
+        return reinterpret_cast<void*>(-1);
+    }
+
+    int err = AHardwareBuffer_sendHandleToUnixSocket(pdata->buffer, fd);
+    EXPECT_EQ(NO_ERROR, err);
+    close(fd);
+    return 0;
+}
+
+TEST(AHardwareBufferTest, AHardwareBuffer_SendAndRecv_Succeeds) {
+    AHardwareBuffer* buffer = NULL;
+    AHardwareBuffer_Desc desc;
+
+    desc.width = 2;
+    desc.height = 4;
+    desc.layers = 1;
+    desc.usage0 = AHARDWAREBUFFER_USAGE0_GPU_SAMPLED_IMAGE;
+    desc.usage1 = 0;
+    desc.format = AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM;
+
+    // Test that an invalid buffer fails.
+    int err = AHardwareBuffer_sendHandleToUnixSocket(NULL, 0);
+    EXPECT_EQ(BAD_VALUE, err);
+    err = 0;
+    err = AHardwareBuffer_sendHandleToUnixSocket(buffer, 0);
+    EXPECT_EQ(BAD_VALUE, err);
+
+    // Allocate the buffer.
+    err = AHardwareBuffer_allocate(&desc, &buffer);
+    EXPECT_EQ(NO_ERROR, err);
+
+    int fd = socket(AF_UNIX, SOCK_STREAM, 0);
+    ASSERT_LT(0, fd);
+
+    std::string tempFile = "/data/local/tmp/ahardwarebuffer_test_XXXXXX";
+    int tempFd = mkstemp(&tempFile[0]);
+    unlink(&tempFile[0]);
+
+    struct sockaddr_un addr;
+    memset(&addr, 0, sizeof(addr));
+    addr.sun_family = AF_UNIX;
+    strcpy(addr.sun_path, &tempFile[0]);
+
+    // Bind the server and listen on the socket.
+    ASSERT_NE(-1, bind(fd, reinterpret_cast<struct sockaddr*>(&addr),
+          sizeof(addr))) << strerror(errno);
+    ASSERT_EQ(0, listen(fd, 1)) << strerror(errno);
+
+    // Launch a client that will send the buffer back.
+    ClientData data(&tempFile[0], buffer);
+    pthread_t thread;
+    ASSERT_EQ(0, pthread_create(&thread, NULL, clientFunction, &data));
+
+    // Wait for the client to send the buffer.
+    int acceptFd = accept(fd, NULL, NULL);
+    ASSERT_LT(0, acceptFd) << strerror(errno);
+
+    // Receive the buffer.
+    err = AHardwareBuffer_recvHandleFromUnixSocket(acceptFd, NULL);
+    EXPECT_EQ(BAD_VALUE, err);
+
+    AHardwareBuffer* received = NULL;
+    err = AHardwareBuffer_recvHandleFromUnixSocket(acceptFd, &received);
+    EXPECT_EQ(NO_ERROR, err);
+    ASSERT_TRUE(received != NULL);
+    EXPECT_TRUE(CheckAHardwareBufferMatchesDesc(received, desc));
+
+    void* ret_val;
+    ASSERT_EQ(0, pthread_join(thread, &ret_val));
+    ASSERT_EQ(NULL, ret_val);
+    close(acceptFd);
+    close(fd);
+
+    AHardwareBuffer_release(buffer);
+    AHardwareBuffer_release(received);
+}
+
+TEST(AHardwareBufferTest, AHardwareBuffer_Lock_and_Unlock_Succeed) {
+    AHardwareBuffer* buffer = NULL;
+    AHardwareBuffer_Desc desc;
+
+    desc.width = 2;
+    desc.height = 4;
+    desc.layers = 1;
+    desc.usage0 = AHARDWAREBUFFER_USAGE0_GPU_SAMPLED_IMAGE |
+            AHARDWAREBUFFER_USAGE0_CPU_READ;
+    desc.usage1 = 0;
+    desc.format = AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM;
+
+    // Test that an invalid buffer fails.
+    int err = AHardwareBuffer_lock(NULL, 0, -1, NULL, NULL);
+    EXPECT_EQ(BAD_VALUE, err);
+    err = 0;
+
+    // Allocate the buffer.
+    err = AHardwareBuffer_allocate(&desc, &buffer);
+    EXPECT_EQ(NO_ERROR, err);
+    void* bufferData = NULL;
+    err = AHardwareBuffer_lock(buffer, AHARDWAREBUFFER_USAGE0_CPU_READ, -1,
+          NULL, &bufferData);
+    EXPECT_EQ(NO_ERROR, err);
+    EXPECT_TRUE(bufferData != NULL);
+    int32_t fence = -1;
+    err = AHardwareBuffer_unlock(buffer, &fence);
+
+    AHardwareBuffer_release(buffer);
+}
+
+int main(int argc, char **argv) {
+    testing::InitGoogleTest(&argc, argv);
+    return RUN_ALL_TESTS();
+}
diff --git a/tests/tests/permission/src/android/permission/cts/TelephonyManagerPermissionTest.java b/tests/tests/permission/src/android/permission/cts/TelephonyManagerPermissionTest.java
index e2112aa..54c2371 100644
--- a/tests/tests/permission/src/android/permission/cts/TelephonyManagerPermissionTest.java
+++ b/tests/tests/permission/src/android/permission/cts/TelephonyManagerPermissionTest.java
@@ -31,6 +31,7 @@
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import java.util.Collections;
 
 /**
  * Test the non-location-related functionality of TelephonyManager.
@@ -270,6 +271,48 @@
         }
     }
 
+    /**
+     * Verify that TelephonyManager.setAllowedCarriers requires Permission.
+     * <p>
+     * Requires Permission:
+     * {@link android.Manifest.permission#MODIFY_PHONE_STATE}.
+     */
+    @Test
+    public void testSetAllowedCarriers() {
+        if (!mHasTelephony
+                || !getContext().getPackageManager().hasSystemFeature(
+                        PackageManager.FEATURE_TELEPHONY_CARRIERLOCK)) {
+            return;
+        }
+        try {
+            mTelephonyManager.setAllowedCarriers(0, Collections.emptyList());
+            fail("Able to set allowed carriers");
+        } catch (SecurityException e) {
+            // expected
+        }
+    }
+
+    /**
+     * Verify that TelephonyManager.getAllowedCarriers requires Permission.
+     * <p>
+     * Requires Permission:
+     * {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE}.
+     */
+    @Test
+    public void testGetAllowedCarriers() {
+        if (!mHasTelephony
+                || !getContext().getPackageManager().hasSystemFeature(
+                        PackageManager.FEATURE_TELEPHONY_CARRIERLOCK)) {
+            return;
+        }
+        try {
+            mTelephonyManager.getAllowedCarriers(0);
+            fail("Able to get allowed carriers");
+        } catch (SecurityException e) {
+            // expected
+        }
+    }
+
     private static Context getContext() {
         return InstrumentationRegistry.getContext();
     }
diff --git a/tests/tests/security/res/raw/bug_33251605.bmp b/tests/tests/security/res/raw/bug_33251605.bmp
new file mode 100644
index 0000000..0060ff4
--- /dev/null
+++ b/tests/tests/security/res/raw/bug_33251605.bmp
Binary files differ
diff --git a/tests/tests/security/src/android/security/cts/BigRleTest.java b/tests/tests/security/src/android/security/cts/BigRleTest.java
new file mode 100644
index 0000000..f3c2302
--- /dev/null
+++ b/tests/tests/security/src/android/security/cts/BigRleTest.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES 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.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.test.AndroidTestCase;
+
+import java.io.InputStream;
+
+import android.security.cts.R;
+
+public class BigRleTest extends AndroidTestCase {
+    /**
+     * Verifies that the device does not run OOM decoding a particular RLE encoded BMP.
+     *
+     * This image reports that its encoded length is over 4 gigs. Prior to fixing issue 33251605,
+     * we attempted to allocate space for all the encoded data at once, resulting in OOM.
+     */
+    public void test_android_bug_33251605() {
+        InputStream exploitImage = mContext.getResources().openRawResource(R.raw.bug_33251605);
+        Bitmap bitmap = BitmapFactory.decodeStream(exploitImage);
+    }
+}
diff --git a/tests/tests/text/src/android/text/cts/FontManagerTest.java b/tests/tests/text/src/android/text/cts/FontManagerTest.java
new file mode 100644
index 0000000..69ea522
--- /dev/null
+++ b/tests/tests/text/src/android/text/cts/FontManagerTest.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.text.cts;
+
+import android.content.Context;
+import android.os.ParcelFileDescriptor;
+import android.system.ErrnoException;
+import android.system.Os;
+import android.system.OsConstants;
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.text.FontConfig;
+import android.text.FontManager;
+
+import java.io.FileDescriptor;
+import java.util.List;
+
+/**
+ * Tests {@link FontManager}.
+ */
+public class FontManagerTest extends AndroidTestCase {
+
+    private FontManager mFontManager;
+
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+        mFontManager = (FontManager) getContext().getSystemService(Context.FONT_SERVICE);
+    }
+
+    @SmallTest
+    public void testGetSystemFontsData() {
+        FontConfig config = mFontManager.getSystemFonts();
+
+        assertNotNull(config);
+        assertTrue("There should at least be one font family", config.getFamilies().size() > 0);
+        for (int i = 0; i < config.getFamilies().size(); ++i) {
+            FontConfig.Family family = config.getFamilies().get(i);
+            assertTrue("Each font family should have at least one font",
+                    family.getFonts().size() > 0);
+            for (int j = 0; j < family.getFonts().size(); ++j) {
+                FontConfig.Font font = family.getFonts().get(j);
+                assertNotNull("FontManager should provide a FileDescriptor for each system font",
+                        font.getFd());
+            }
+        }
+    }
+
+    @SmallTest
+    public void testFileDescriptorsAreReadOnly() throws Exception {
+        FontConfig fc = mFontManager.getSystemFonts();
+
+        List<FontConfig.Family> families = fc.getFamilies();
+        for (int i = 0; i < families.size(); ++i) {
+            List<FontConfig.Font> fonts = families.get(i).getFonts();
+            for (int j = 0; j < fonts.size(); ++j) {
+                ParcelFileDescriptor pfd = fonts.get(j).getFd();
+                assertNotNull(pfd);
+                FileDescriptor fd = pfd.getFileDescriptor();
+                long size = Os.lseek(fd, 0, OsConstants.SEEK_END);
+                // Read only mapping should success.
+                long addr = Os.mmap(0, size, OsConstants.PROT_READ, OsConstants.MAP_SHARED, fd, 0);
+                Os.munmap(addr, size);
+
+                // Mapping with PROT_WRITE should fail with EPERM.
+                try {
+                    Os.mmap(0, size, OsConstants.PROT_READ | OsConstants.PROT_WRITE,
+                            OsConstants.MAP_SHARED, fd, 0);
+                    fail();
+                } catch (ErrnoException e) {
+                    // EPERM should be raised.
+                }
+            }
+        }
+    }
+}
diff --git a/tests/tests/view/src/android/view/cts/FocusFinderTest.java b/tests/tests/view/src/android/view/cts/FocusFinderTest.java
index 954a84a..5cd2fec 100644
--- a/tests/tests/view/src/android/view/cts/FocusFinderTest.java
+++ b/tests/tests/view/src/android/view/cts/FocusFinderTest.java
@@ -235,27 +235,20 @@
         assertTrue(nextFocus == mBottomRight || nextFocus == mBottomLeft);
     }
 
-    // Tests for finding new groups don't look at geometrical properties of the views. For them,
+    // Tests for finding new cluster don't look at geometrical properties of the views. For them,
     // only tab order is important, which is mTopLeft, mTopRight, mBottomLeft. mBottomRight isn't
     // used.
-    private void verifyNextGroup(
-            int groupType, View currentGroup, int direction, View expectedNextGroup) {
-        View actualNextGroup = mFocusFinder.findNextKeyboardNavigationGroup(
-                groupType, mLayout, currentGroup, direction);
-        assertEquals(expectedNextGroup, actualNextGroup);
+    private void verifyNextCluster(View currentCluster, int direction, View expectedNextCluster) {
+        View actualNextCluster = mFocusFinder.findNextKeyboardNavigationCluster(
+                mLayout, currentCluster, direction);
+        assertEquals(expectedNextCluster, actualNextCluster);
     }
 
     @Test
-    public void testNoGroups() {
-        // No views are marked as groups, so next group is always null.
-        verifyNextGroup(
-                View.KEYBOARD_NAVIGATION_GROUP_CLUSTER, mTopRight, View.FOCUS_FORWARD, null);
-        verifyNextGroup(
-                View.KEYBOARD_NAVIGATION_GROUP_CLUSTER, mTopRight, View.FOCUS_BACKWARD, null);
-        verifyNextGroup(
-                View.KEYBOARD_NAVIGATION_GROUP_SECTION, mTopRight, View.FOCUS_FORWARD, null);
-        verifyNextGroup(
-                View.KEYBOARD_NAVIGATION_GROUP_SECTION, mTopRight, View.FOCUS_BACKWARD, null);
+    public void testNoClusters() {
+        // No views are marked as clusters, so next cluster is always null.
+        verifyNextCluster(mTopRight, View.FOCUS_FORWARD, null);
+        verifyNextCluster(mTopRight, View.FOCUS_BACKWARD, null);
     }
 
     @Test
@@ -265,59 +258,16 @@
         mTopRight.setKeyboardNavigationCluster(true);
         mBottomLeft.setKeyboardNavigationCluster(true);
 
-        verifyNextGroup(
-                View.KEYBOARD_NAVIGATION_GROUP_CLUSTER, null, View.FOCUS_FORWARD, mTopLeft);
-        verifyNextGroup(
-                View.KEYBOARD_NAVIGATION_GROUP_CLUSTER, mTopLeft, View.FOCUS_FORWARD, mTopRight);
-        verifyNextGroup(
-                View.KEYBOARD_NAVIGATION_GROUP_CLUSTER, mTopRight, View.FOCUS_FORWARD, mBottomLeft);
-        verifyNextGroup(
-                View.KEYBOARD_NAVIGATION_GROUP_CLUSTER, mBottomLeft, View.FOCUS_FORWARD, mLayout);
-        verifyNextGroup(
-                View.KEYBOARD_NAVIGATION_GROUP_CLUSTER, mBottomRight, View.FOCUS_FORWARD, mLayout);
+        verifyNextCluster(null, View.FOCUS_FORWARD, mTopLeft);
+        verifyNextCluster(mTopLeft, View.FOCUS_FORWARD, mTopRight);
+        verifyNextCluster(mTopRight, View.FOCUS_FORWARD, mBottomLeft);
+        verifyNextCluster(mBottomLeft, View.FOCUS_FORWARD, mLayout);
+        verifyNextCluster(mBottomRight, View.FOCUS_FORWARD, mLayout);
 
-        verifyNextGroup(
-                View.KEYBOARD_NAVIGATION_GROUP_CLUSTER, null, View.FOCUS_BACKWARD, mBottomLeft);
-        verifyNextGroup(
-                View.KEYBOARD_NAVIGATION_GROUP_CLUSTER, mTopLeft, View.FOCUS_BACKWARD, mLayout);
-        verifyNextGroup(
-                View.KEYBOARD_NAVIGATION_GROUP_CLUSTER, mTopRight, View.FOCUS_BACKWARD, mTopLeft);
-        verifyNextGroup(
-                View.KEYBOARD_NAVIGATION_GROUP_CLUSTER, mBottomLeft, View.FOCUS_BACKWARD,
-                mTopRight);
-        verifyNextGroup(
-                View.KEYBOARD_NAVIGATION_GROUP_CLUSTER, mBottomRight, View.FOCUS_BACKWARD, mLayout);
-    }
-
-    @Test
-    public void testFindNextSection() {
-        // Section navigation from all possible starting points in all directions.
-        mTopLeft.setKeyboardNavigationSection(true);
-        mTopRight.setKeyboardNavigationSection(true);
-        mBottomLeft.setKeyboardNavigationSection(true);
-
-        verifyNextGroup(
-                View.KEYBOARD_NAVIGATION_GROUP_SECTION, null, View.FOCUS_FORWARD, mTopLeft);
-        verifyNextGroup(
-                View.KEYBOARD_NAVIGATION_GROUP_SECTION, mTopLeft, View.FOCUS_FORWARD, mTopRight);
-        verifyNextGroup(
-                View.KEYBOARD_NAVIGATION_GROUP_SECTION, mTopRight, View.FOCUS_FORWARD, mBottomLeft);
-        verifyNextGroup(
-                View.KEYBOARD_NAVIGATION_GROUP_SECTION, mBottomLeft, View.FOCUS_FORWARD, mTopLeft);
-        verifyNextGroup(
-                View.KEYBOARD_NAVIGATION_GROUP_SECTION, mBottomRight, View.FOCUS_FORWARD, mTopLeft);
-
-        verifyNextGroup(
-                View.KEYBOARD_NAVIGATION_GROUP_SECTION, null, View.FOCUS_BACKWARD, mBottomLeft);
-        verifyNextGroup(
-                View.KEYBOARD_NAVIGATION_GROUP_SECTION, mTopLeft, View.FOCUS_BACKWARD, mBottomLeft);
-        verifyNextGroup(
-                View.KEYBOARD_NAVIGATION_GROUP_SECTION, mTopRight, View.FOCUS_BACKWARD, mTopLeft);
-        verifyNextGroup(
-                View.KEYBOARD_NAVIGATION_GROUP_SECTION, mBottomLeft, View.FOCUS_BACKWARD,
-                mTopRight);
-        verifyNextGroup(
-                View.KEYBOARD_NAVIGATION_GROUP_SECTION, mBottomRight, View.FOCUS_BACKWARD,
-                mBottomLeft);
+        verifyNextCluster(null, View.FOCUS_BACKWARD, mBottomLeft);
+        verifyNextCluster(mTopLeft, View.FOCUS_BACKWARD, mLayout);
+        verifyNextCluster(mTopRight, View.FOCUS_BACKWARD, mTopLeft);
+        verifyNextCluster(mBottomLeft, View.FOCUS_BACKWARD, mTopRight);
+        verifyNextCluster(mBottomRight, View.FOCUS_BACKWARD, mLayout);
     }
 }
diff --git a/tests/tests/view/src/android/view/cts/MockView.java b/tests/tests/view/src/android/view/cts/MockView.java
index 7938d64..022609a 100644
--- a/tests/tests/view/src/android/view/cts/MockView.java
+++ b/tests/tests/view/src/android/view/cts/MockView.java
@@ -71,6 +71,7 @@
     private boolean mCalledOnKeyPreIme = false;
     private boolean mCalledOnResolvePointerIcon = false;
     private boolean mCalledOnVisibilityAggregated = false;
+    private boolean mCalledRequestFocus = false;
 
     private int mOldWidth = -1;
     private int mOldHeight = -1;
@@ -626,10 +627,20 @@
         mLastAggregatedVisibility = isVisible;
     }
 
+    @Override
+    public boolean requestFocus(int direction, Rect previouslyFocusedRect) {
+        mCalledRequestFocus = true;
+        return super.requestFocus(direction, previouslyFocusedRect);
+    }
+
     public boolean hasCalledOnVisibilityAggregated() {
         return mCalledOnVisibilityAggregated;
     }
 
+    public boolean hasCalledRequestFocus() {
+        return mCalledRequestFocus;
+    }
+
     public boolean getLastAggregatedVisibility() {
         return mLastAggregatedVisibility;
     }
@@ -674,7 +685,7 @@
         mCalledOnKeyPreIme = false;
         mCalledOnResolvePointerIcon = false;
         mCalledOnVisibilityAggregated = false;
-        mCalledOnVisibilityAggregated = false;
+        mCalledRequestFocus = false;
 
         mOldWidth = -1;
         mOldHeight = -1;
diff --git a/tests/tests/view/src/android/view/cts/ViewGroupTest.java b/tests/tests/view/src/android/view/cts/ViewGroupTest.java
index 868a398..5b8d17f 100644
--- a/tests/tests/view/src/android/view/cts/ViewGroupTest.java
+++ b/tests/tests/view/src/android/view/cts/ViewGroupTest.java
@@ -140,73 +140,37 @@
         mMockViewGroup.setFocusable(true);
         mMockViewGroup.addFocusables(list, 0);
         assertEquals(2, list.size());
-
-        // Group is a cluster.
-        list.clear();
-        mMockViewGroup.setKeyboardNavigationCluster(true);
-        mMockViewGroup.addFocusables(list, View.FOCUS_FORWARD);
-        assertEquals(0, list.size());
-        mMockViewGroup.addFocusables(list, View.FOCUS_DOWN);
-        assertEquals(2, list.size());
-        list.clear();
-        mTextView.requestFocus();
-        mMockViewGroup.addFocusables(list, View.FOCUS_FORWARD);
-        assertEquals(2, list.size());
     }
 
     @UiThreadTest
     @Test
-    public void testAddKeyboardNavigationGroups() {
+    public void testAddKeyboardNavigationClusters() {
         View v1 = new MockView(mContext);
         View v2 = new MockView(mContext);
         mMockViewGroup.addView(v1);
         mMockViewGroup.addView(v2);
 
-        // No groups.
+        // No clusters.
         ArrayList<View> list = new ArrayList<>();
-        mMockViewGroup.addKeyboardNavigationGroups(View.KEYBOARD_NAVIGATION_GROUP_CLUSTER, list, 0);
-        assertEquals(0, list.size());
-        mMockViewGroup.addKeyboardNavigationGroups(View.KEYBOARD_NAVIGATION_GROUP_SECTION, list, 0);
+        mMockViewGroup.addKeyboardNavigationClusters(list, 0);
         assertEquals(0, list.size());
 
-        // Children are a section and a cluster.
+        // A cluster and a non-cluster child.
         v1.setKeyboardNavigationCluster(true);
-        v2.setKeyboardNavigationSection(true);
-        mMockViewGroup.addKeyboardNavigationGroups(View.KEYBOARD_NAVIGATION_GROUP_CLUSTER, list, 0);
+        mMockViewGroup.addKeyboardNavigationClusters(list, 0);
         assertEquals(1, list.size());
         assertEquals(v1, list.get(0));
         list.clear();
-        mMockViewGroup.addKeyboardNavigationGroups(View.KEYBOARD_NAVIGATION_GROUP_SECTION, list, 0);
-        assertEquals(1, list.size());
-        assertEquals(v2, list.get(0));
-        list.clear();
-
-        // Nested groups. Should ignore children.
-        mMockViewGroup.setKeyboardNavigationCluster(true);
-        mMockViewGroup.addKeyboardNavigationGroups(View.KEYBOARD_NAVIGATION_GROUP_CLUSTER, list, 0);
-        assertEquals(1, list.size());
-        assertEquals(mMockViewGroup, list.get(0));
-        list.clear();
-        mMockViewGroup.setKeyboardNavigationCluster(false);
-        mMockViewGroup.setKeyboardNavigationSection(true);
-        mMockViewGroup.addKeyboardNavigationGroups(View.KEYBOARD_NAVIGATION_GROUP_SECTION, list, 0);
-        assertEquals(1, list.size());
-        assertEquals(mMockViewGroup, list.get(0));
-        list.clear();
-        mMockViewGroup.setKeyboardNavigationSection(false);
 
         // Blocking descendants from getting focus also blocks group search.
         mMockViewGroup.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
-        mMockViewGroup.addKeyboardNavigationGroups(View.KEYBOARD_NAVIGATION_GROUP_CLUSTER, list, 0);
-        assertEquals(0, list.size());
-        mMockViewGroup.addKeyboardNavigationGroups(View.KEYBOARD_NAVIGATION_GROUP_SECTION, list, 0);
+        mMockViewGroup.addKeyboardNavigationClusters(list, 0);
         assertEquals(0, list.size());
         mMockViewGroup.setDescendantFocusability(ViewGroup.FOCUS_BEFORE_DESCENDANTS);
 
         // Testing the results ordering.
-        v2.setKeyboardNavigationSection(false);
         v2.setKeyboardNavigationCluster(true);
-        mMockViewGroup.addKeyboardNavigationGroups(View.KEYBOARD_NAVIGATION_GROUP_CLUSTER, list, 0);
+        mMockViewGroup.addKeyboardNavigationClusters(list, 0);
         assertEquals(2, list.size());
         assertEquals(v1, list.get(0));
         assertEquals(v2, list.get(1));
@@ -216,22 +180,14 @@
         ViewGroup parent = new MockViewGroup(mContext);
         parent.addView(mMockViewGroup);
         mMockViewGroup.removeView(v2);
-        v1.setKeyboardNavigationCluster(false);
-        v1.setKeyboardNavigationSection(true);
-        parent.addKeyboardNavigationGroups(View.KEYBOARD_NAVIGATION_GROUP_SECTION, list, 0);
+        parent.addKeyboardNavigationClusters(list, 0);
         assertEquals(1, list.size());
         assertEquals(v1, list.get(0));
         list.clear();
 
-        // Searching for sections doesn't enter clusters.
-        mMockViewGroup.setKeyboardNavigationCluster(true);
-        parent.addKeyboardNavigationGroups(View.KEYBOARD_NAVIGATION_GROUP_SECTION, list, 0);
-        assertEquals(0, list.size());
-        mMockViewGroup.setKeyboardNavigationCluster(false);
-
         // Invisible children get ignored.
         mMockViewGroup.setVisibility(View.GONE);
-        parent.addKeyboardNavigationGroups(View.KEYBOARD_NAVIGATION_GROUP_SECTION, list, 0);
+        parent.addKeyboardNavigationClusters(list, 0);
         assertEquals(0, list.size());
     }
 
@@ -773,22 +729,6 @@
 
     @UiThreadTest
     @Test
-    public void testFindFocusWithCluster() {
-        // v1 is a cluster, so it doesn't accept focus from outside if it.
-        View v1 = new MockView(mContext);
-        v1.setFocusable(true);
-        v1.setKeyboardNavigationCluster(true);
-        View v2 = new MockView(mContext);
-        v2.setFocusable(true);
-        mMockViewGroup.addView(v1);
-        mMockViewGroup.addView(v2);
-
-        mMockViewGroup.requestFocus();
-        assertSame(v2, mMockViewGroup.findFocus());
-    }
-
-    @UiThreadTest
-    @Test
     public void testFitSystemWindows() {
         Rect rect = new Rect(1, 1, 100, 100);
         assertFalse(mMockViewGroup.fitSystemWindows(rect));
@@ -849,28 +789,6 @@
 
     @UiThreadTest
     @Test
-    public void testFocusSearchWithCluster() {
-        // focusSearch for FORWARD treats cluster like a root.
-        View view = new View(mContext);
-        view.setFocusable(true);
-        View auntView = new View(mContext);
-        auntView.setFocusable(true);
-        MockViewGroup viewParent = new MockViewGroup(mContext);
-        viewParent.addView(view);
-        mMockViewGroup.addView(viewParent);
-        mMockViewGroup.addView(auntView);
-        mMockViewGroup.returnActualFocusSearchResult = true;
-        viewParent.returnActualFocusSearchResult = true;
-        mMockViewGroup.setIsRootNamespace(true);
-        view.requestFocus();
-
-        assertSame(auntView, view.focusSearch(View.FOCUS_FORWARD));
-        viewParent.setKeyboardNavigationCluster(true);
-        assertSame(view, view.focusSearch(View.FOCUS_FORWARD));
-    }
-
-    @UiThreadTest
-    @Test
     public void testGatherTransparentRegion() {
         Region region = new Region();
         mMockTextView.setAnimation(new AlphaAnimation(mContext, null));
@@ -1526,21 +1444,94 @@
         assertTrue(mMockViewGroup.isOnRequestFocusInDescendantsCalled);
     }
 
+    private void setupRestoreDefaultFocus() {
+        // Mark 2 children as focusable and add to the parent, then mark the second one as focused
+        // by default.
+        mMockViewGroup = new MockViewGroup(mContext);
+        mMockTextView = new MockTextView(mContext);
+        mMockTextView.setFocusable(true);
+        mTextView = new TextView(mContext);
+        mTextView.setFocusable(true);
+        mMockViewGroup.addView(mMockTextView);
+        mMockViewGroup.addView(mTextView);
+        mTextView.setFocusedByDefault(true);
+    }
+
     @UiThreadTest
     @Test
-    public void testRequestFocusWithCluster() {
-        // requestFocus skips nested clusters.
-        View v1 = new View(mContext);
-        v1.setFocusable(true);
-        View v2 = new View(mContext);
-        v2.setFocusable(true);
-        mMockViewGroup.addView(v1);
-        mMockViewGroup.addView(v2);
+    public void testRestoreDefaultFocus() {
+        // Invoking restoreDefaultFocus with various conditions that affect the outcome.
+        setupRestoreDefaultFocus();
+        mTextView.setFocusedByDefault(false);
+        mMockViewGroup.restoreDefaultFocus(View.FOCUS_FORWARD);
+        assertSame(mMockTextView, mMockViewGroup.findFocus());
 
+        setupRestoreDefaultFocus();
+        mTextView.setKeyboardNavigationCluster(true);
+        mMockViewGroup.restoreDefaultFocus(View.FOCUS_FORWARD);
+        assertSame(mMockTextView, mMockViewGroup.findFocus());
+
+        setupRestoreDefaultFocus();
+        mMockViewGroup.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
+        mMockViewGroup.restoreDefaultFocus(View.FOCUS_FORWARD);
         assertSame(null, mMockViewGroup.findFocus());
-        v1.setKeyboardNavigationCluster(true);
-        assertTrue(mMockViewGroup.requestFocus());
-        assertSame(v2, mMockViewGroup.findFocus());
+
+        setupRestoreDefaultFocus();
+        mTextView.setVisibility(View.INVISIBLE);
+        mMockViewGroup.restoreDefaultFocus(View.FOCUS_FORWARD);
+        assertSame(mMockTextView, mMockViewGroup.findFocus());
+
+        setupRestoreDefaultFocus();
+        mMockViewGroup.restoreDefaultFocus(View.FOCUS_FORWARD);
+        assertSame(mTextView, mMockViewGroup.findFocus());
+    }
+
+    @UiThreadTest
+    @Test
+    public void testDefaultFocusViewRemoved() {
+        // Removing default-focus view from its parent in various ways.
+        setupRestoreDefaultFocus();
+        mMockViewGroup.removeView(mTextView);
+        mMockViewGroup.restoreDefaultFocus(View.FOCUS_FORWARD);
+        assertSame(mMockTextView, mMockViewGroup.findFocus());
+
+        setupRestoreDefaultFocus();
+        mMockViewGroup.removeViews(1, 1);
+        mMockViewGroup.restoreDefaultFocus(View.FOCUS_FORWARD);
+        assertSame(mMockTextView, mMockViewGroup.findFocus());
+
+        setupRestoreDefaultFocus();
+        mMockViewGroup.removeAllViewsInLayout();
+        mMockViewGroup.restoreDefaultFocus(View.FOCUS_FORWARD);
+        assertSame(null, mMockViewGroup.findFocus());
+
+        setupRestoreDefaultFocus();
+        mMockViewGroup.detachViewFromParent(mTextView);
+        mMockViewGroup.attachViewToParent(mTextView, 1, null);
+        mMockViewGroup.restoreDefaultFocus(View.FOCUS_FORWARD);
+        assertSame(mTextView, mMockViewGroup.findFocus());
+
+        setupRestoreDefaultFocus();
+        mMockViewGroup.detachViewFromParent(mTextView);
+        mMockViewGroup.removeDetachedView(mTextView, false);
+        mMockViewGroup.restoreDefaultFocus(View.FOCUS_FORWARD);
+        assertSame(mMockTextView, mMockViewGroup.findFocus());
+    }
+
+    @UiThreadTest
+    @Test
+    public void testAddViewWithDefaultFocus() {
+        // Adding a view that has default focus propagates the default focus chain to the root.
+        mMockViewGroup = new MockViewGroup(mContext);
+        mMockTextView = new MockTextView(mContext);
+        mMockTextView.setFocusable(true);
+        mTextView = new TextView(mContext);
+        mTextView.setFocusable(true);
+        mTextView.setFocusedByDefault(true);
+        mMockViewGroup.addView(mMockTextView);
+        mMockViewGroup.addView(mTextView);
+        mMockViewGroup.restoreDefaultFocus(View.FOCUS_FORWARD);
+        assertTrue(mTextView.isFocused());
     }
 
     @UiThreadTest
diff --git a/tests/tests/view/src/android/view/cts/ViewTest.java b/tests/tests/view/src/android/view/cts/ViewTest.java
index 6512f1b..472d660 100644
--- a/tests/tests/view/src/android/view/cts/ViewTest.java
+++ b/tests/tests/view/src/android/view/cts/ViewTest.java
@@ -1204,45 +1204,24 @@
     }
 
     @Test
-    public void testAddKeyboardNavigationGroups() {
+    public void testAddKeyboardNavigationClusters() {
         View view = new View(mActivity);
         ArrayList<View> viewList = new ArrayList<>();
 
-        // View is not a keyboard navigation group
+        // View is not a keyboard navigation cluster
         assertFalse(view.isKeyboardNavigationCluster());
-        assertFalse(view.isKeyboardNavigationSection());
-        assertEquals(0, viewList.size());
-
-        view.addKeyboardNavigationGroups(View.KEYBOARD_NAVIGATION_GROUP_CLUSTER, viewList, 0);
-        assertEquals(0, viewList.size());
-
-        view.addKeyboardNavigationGroups(View.KEYBOARD_NAVIGATION_GROUP_SECTION, viewList, 0);
+        view.addKeyboardNavigationClusters(viewList, 0);
         assertEquals(0, viewList.size());
 
         // View is a cluster
         view.setKeyboardNavigationCluster(true);
-        view.addKeyboardNavigationGroups(View.KEYBOARD_NAVIGATION_GROUP_SECTION, viewList, 0);
-        assertEquals(0, viewList.size());
-
-        view.addKeyboardNavigationGroups(View.KEYBOARD_NAVIGATION_GROUP_CLUSTER, viewList, 0);
-        assertEquals(1, viewList.size());
-        assertEquals(view, viewList.get(0));
-
-        viewList.clear();
-        view.setKeyboardNavigationCluster(false);
-
-        // View is a section
-        view.setKeyboardNavigationSection(true);
-        view.addKeyboardNavigationGroups(View.KEYBOARD_NAVIGATION_GROUP_CLUSTER, viewList, 0);
-        assertEquals(0, viewList.size());
-
-        view.addKeyboardNavigationGroups(View.KEYBOARD_NAVIGATION_GROUP_SECTION, viewList, 0);
+        view.addKeyboardNavigationClusters(viewList, 0);
         assertEquals(1, viewList.size());
         assertEquals(view, viewList.get(0));
     }
 
     @Test
-    public void testKeyboardNavigationGroupSearch() {
+    public void testKeyboardNavigationClusterSearch() {
         mMockParent.setIsRootNamespace(true);
         View v1 = new MockView(mActivity);
         View v2 = new MockView(mActivity);
@@ -1252,55 +1231,20 @@
         // Searching for clusters.
         v1.setKeyboardNavigationCluster(true);
         v2.setKeyboardNavigationCluster(true);
-        assertEquals(v2, mMockParent.keyboardNavigationGroupSearch(
-                View.KEYBOARD_NAVIGATION_GROUP_CLUSTER, v1, View.FOCUS_FORWARD));
-        assertEquals(v1, mMockParent.keyboardNavigationGroupSearch(
-                View.KEYBOARD_NAVIGATION_GROUP_CLUSTER, null, View.FOCUS_FORWARD));
-        assertEquals(v2, mMockParent.keyboardNavigationGroupSearch(
-                View.KEYBOARD_NAVIGATION_GROUP_CLUSTER, null, View.FOCUS_BACKWARD));
-        assertEquals(v2, v1.keyboardNavigationGroupSearch(
-                View.KEYBOARD_NAVIGATION_GROUP_CLUSTER, null, View.FOCUS_FORWARD));
-        assertEquals(mMockParent, v1.keyboardNavigationGroupSearch(
-                View.KEYBOARD_NAVIGATION_GROUP_CLUSTER, null, View.FOCUS_BACKWARD));
-        assertEquals(mMockParent, v2.keyboardNavigationGroupSearch(
-                View.KEYBOARD_NAVIGATION_GROUP_CLUSTER, null, View.FOCUS_FORWARD));
-        assertEquals(v1, v2.keyboardNavigationGroupSearch(
-                View.KEYBOARD_NAVIGATION_GROUP_CLUSTER, null, View.FOCUS_BACKWARD));
-        v1.setKeyboardNavigationCluster(false);
-        v2.setKeyboardNavigationCluster(false);
+        assertEquals(v2, mMockParent.keyboardNavigationClusterSearch(v1, View.FOCUS_FORWARD));
+        assertEquals(v1, mMockParent.keyboardNavigationClusterSearch(null, View.FOCUS_FORWARD));
+        assertEquals(v2, mMockParent.keyboardNavigationClusterSearch(null, View.FOCUS_BACKWARD));
+        assertEquals(v2, v1.keyboardNavigationClusterSearch(null, View.FOCUS_FORWARD));
+        assertEquals(mMockParent, v1.keyboardNavigationClusterSearch(null, View.FOCUS_BACKWARD));
+        assertEquals(mMockParent, v2.keyboardNavigationClusterSearch(null, View.FOCUS_FORWARD));
+        assertEquals(v1, v2.keyboardNavigationClusterSearch(null, View.FOCUS_BACKWARD));
 
-        // Searching for sections.
-        v1.setKeyboardNavigationSection(true);
-        v2.setKeyboardNavigationSection(true);
-        assertEquals(v2, mMockParent.keyboardNavigationGroupSearch(
-                View.KEYBOARD_NAVIGATION_GROUP_SECTION, v1, View.FOCUS_FORWARD));
-        assertEquals(v1, mMockParent.keyboardNavigationGroupSearch(
-                View.KEYBOARD_NAVIGATION_GROUP_SECTION, null, View.FOCUS_FORWARD));
-        assertEquals(v2, mMockParent.keyboardNavigationGroupSearch(
-                View.KEYBOARD_NAVIGATION_GROUP_SECTION, null, View.FOCUS_BACKWARD));
-        assertEquals(v2, v1.keyboardNavigationGroupSearch(
-                View.KEYBOARD_NAVIGATION_GROUP_SECTION, null, View.FOCUS_FORWARD));
-        assertEquals(v2, v1.keyboardNavigationGroupSearch(
-                View.KEYBOARD_NAVIGATION_GROUP_SECTION, null, View.FOCUS_BACKWARD));
-        assertEquals(v1, v2.keyboardNavigationGroupSearch(
-                View.KEYBOARD_NAVIGATION_GROUP_SECTION, null, View.FOCUS_FORWARD));
-        assertEquals(v1, v2.keyboardNavigationGroupSearch(
-                View.KEYBOARD_NAVIGATION_GROUP_SECTION, null, View.FOCUS_BACKWARD));
-
-        // Sections in 3-level hierarchy.
+        // Clusters in 3-level hierarchy.
         ViewGroup root = new MockViewParent(mActivity);
         root.setIsRootNamespace(true);
-        View auntSection = new MockView(mActivity);
-        auntSection.setKeyboardNavigationSection(true);
-        root.addView(auntSection);
         mMockParent.setIsRootNamespace(false);
         root.addView(mMockParent);
-
-        assertEquals(auntSection, v2.keyboardNavigationGroupSearch(
-                View.KEYBOARD_NAVIGATION_GROUP_SECTION, null, View.FOCUS_FORWARD));
-        mMockParent.setKeyboardNavigationCluster(true);
-        assertEquals(v1, v2.keyboardNavigationGroupSearch(
-                View.KEYBOARD_NAVIGATION_GROUP_SECTION, null, View.FOCUS_FORWARD));
+        assertEquals(root, v2.keyboardNavigationClusterSearch(null, View.FOCUS_FORWARD));
     }
 
     @Test
@@ -2924,6 +2868,14 @@
         assertTrue(view.hasCalledOnFocusChanged());
     }
 
+    @UiThreadTest
+    @Test
+    public void testRestoreDefaultFocus() {
+        MockView view = new MockView(mActivity);
+        view.restoreDefaultFocus(0);
+        assertTrue(view.hasCalledRequestFocus());
+    }
+
     @Test
     public void testDrawableState() {
         MockView view = new MockView(mActivity);
diff --git a/tests/tests/widget/src/android/widget/cts/TextViewTest.java b/tests/tests/widget/src/android/widget/cts/TextViewTest.java
index e102ec5..21438d5 100644
--- a/tests/tests/widget/src/android/widget/cts/TextViewTest.java
+++ b/tests/tests/widget/src/android/widget/cts/TextViewTest.java
@@ -6287,6 +6287,31 @@
         verify(spanDetails.mClickableSpan, never()).onClick(mTextView);
     }
 
+    @UiThreadTest
+    @Test
+    public void testOnInitializeA11yNodeInfo_populatesHintTextProperly() {
+        final TextView textView = new TextView(mActivity);
+        textView.setText("", BufferType.EDITABLE);
+        final String hintText = "Hint text";
+        textView.setHint(hintText);
+        AccessibilityNodeInfo info = AccessibilityNodeInfo.obtain();
+        textView.onInitializeAccessibilityNodeInfo(info);
+        assertTrue("Hint text flag set incorrectly for accessibility", info.isShowingHintText());
+        assertTrue("Hint text not showing as accessibility text",
+                TextUtils.equals(hintText, info.getText()));
+        assertTrue("Hint text not provided to accessibility",
+                TextUtils.equals(hintText, info.getHintText()));
+
+        final String nonHintText = "Something else";
+        textView.setText(nonHintText, BufferType.EDITABLE);
+        textView.onInitializeAccessibilityNodeInfo(info);
+        assertFalse("Hint text flag set incorrectly for accessibility", info.isShowingHintText());
+        assertTrue("Text not provided to accessibility",
+                TextUtils.equals(nonHintText, info.getText()));
+        assertTrue("Hint text not provided to accessibility",
+                TextUtils.equals(hintText, info.getHintText()));
+    }
+
     @Test
     public void testAutoSizeCallers_setCompoundDrawables() throws Throwable {
         final TextView autoSizeTextView = prepareAndRetrieveAutoSizeTestData(
@@ -6592,41 +6617,140 @@
         DisplayMetrics metrics = mActivity.getResources().getDisplayMetrics();
         TextView autoSizeTextViewXY = (TextView) mActivity.findViewById(R.id.textview_autosize_xy);
 
-        // The size has been set to 50dp in the layout but this being an AUTO_SIZE_TYPE_XY TextView,
-        // the size is considered max size thus the value returned by getSize() in this case should
-        // be lower than the one set (given that there is not much available space and the font size
-        // is very high). In theory the values could be equal for a different TextView
+        // The size has been set to 50dp in the layout but this being an AUTO_SIZE_TEXT_TYPE_XY
+        // TextView, the size is considered max size thus the value returned by getSize() in this
+        // case should be lower than the one set (given that there is not much available space and
+        // the font size is very high). In theory the values could be equal for a different TextView
         // configuration.
         final float sizeSetInPixels = TypedValue.applyDimension(
                 TypedValue.COMPLEX_UNIT_DIP, 50f, metrics);
         assertTrue(autoSizeTextViewXY.getTextSize() < sizeSetInPixels);
     }
 
-    @UiThreadTest
     @Test
-    public void testOnInitializeA11yNodeInfo_populatesHintTextProperly() {
+    public void testAutoSizeXY_getSetAutoSizeTextXY_defaults() {
         final TextView textView = new TextView(mActivity);
-        textView.setText("", BufferType.EDITABLE);
-        final String hintText = "Hint text";
-        textView.setHint(hintText);
-        AccessibilityNodeInfo info = AccessibilityNodeInfo.obtain();
-        textView.onInitializeAccessibilityNodeInfo(info);
-        assertTrue("Hint text flag set incorrectly for accessibility", info.isShowingHintText());
-        assertTrue("Hint text not showing as accessibility text",
-                TextUtils.equals(hintText, info.getText()));
-        assertTrue("Hint text not provided to accessibility",
-                TextUtils.equals(hintText, info.getHintText()));
+        assertEquals(TextView.AUTO_SIZE_TEXT_TYPE_NONE, textView.getAutoSizeTextType());
+        // Min/Max/Granularity values for auto-sizing are 0 because they are not used.
+        assertEquals(0, textView.getAutoSizeMinTextSize());
+        assertEquals(0, textView.getAutoSizeMaxTextSize());
+        assertEquals(0, textView.getAutoSizeStepGranularity());
 
-        final String nonHintText = "Something else";
-        textView.setText(nonHintText, BufferType.EDITABLE);
-        textView.onInitializeAccessibilityNodeInfo(info);
-        assertFalse("Hint text flag set incorrectly for accessibility", info.isShowingHintText());
-        assertTrue("Text not provided to accessibility",
-                TextUtils.equals(nonHintText, info.getText()));
-        assertTrue("Hint text not provided to accessibility",
-                TextUtils.equals(hintText, info.getHintText()));
+        textView.setAutoSizeTextType(TextView.AUTO_SIZE_TEXT_TYPE_XY);
+        assertEquals(TextView.AUTO_SIZE_TEXT_TYPE_XY, textView.getAutoSizeTextType());
+        // Min/Max default values for auto-sizing XY have been loaded.
+        final int minSize = textView.getAutoSizeMinTextSize();
+        assertNotEquals(0, minSize);
+        final int maxSize = textView.getAutoSizeMaxTextSize();
+        assertNotEquals(0, maxSize);
+        assertTrue(minSize < maxSize);
+        final int stepGranularity = textView.getAutoSizeStepGranularity();
+        assertNotEquals(0, stepGranularity);
+
+        textView.setAutoSizeTextType(TextView.AUTO_SIZE_TEXT_TYPE_NONE);
+        assertEquals(TextView.AUTO_SIZE_TEXT_TYPE_NONE, textView.getAutoSizeTextType());
+        // Min/Max values for auto-sizing XY have been cleared.
+        assertEquals(0, textView.getAutoSizeMinTextSize());
+        assertEquals(0, textView.getAutoSizeMaxTextSize());
+        assertEquals(0, textView.getAutoSizeStepGranularity());
     }
 
+    @Test
+    public void testAutoSizeXY_getSetAutoSizeStepGranularity() {
+        final TextView textView = new TextView(mActivity);
+        assertEquals(TextView.AUTO_SIZE_TEXT_TYPE_NONE, textView.getAutoSizeTextType());
+        final int initialValue = 0;
+        assertEquals(initialValue, textView.getAutoSizeStepGranularity());
+
+        textView.setAutoSizeTextType(TextView.AUTO_SIZE_TEXT_TYPE_XY);
+        assertEquals(TextView.AUTO_SIZE_TEXT_TYPE_XY, textView.getAutoSizeTextType());
+        final int defaultValue = 1; // 1px.
+        // If the auto-size type is AUTO_SIZE_TEXT_TYPE_XY then it means textView went through the
+        // auto-size setup and given that 0 is an invalid value it changed it to the default.
+        assertEquals(defaultValue, textView.getAutoSizeStepGranularity());
+
+        final int newValue = 33;
+        textView.setAutoSizeStepGranularity(TypedValue.COMPLEX_UNIT_PX, newValue);
+        assertEquals(newValue, textView.getAutoSizeStepGranularity());
+    }
+
+    @Test
+    public void testAutoSizeXY_getSetAutoSizeMinTextSize() {
+        final TextView textView = new TextView(mActivity);
+        textView.setAutoSizeTextType(TextView.AUTO_SIZE_TEXT_TYPE_XY);
+        assertEquals(TextView.AUTO_SIZE_TEXT_TYPE_XY, textView.getAutoSizeTextType());
+        final int minSize = textView.getAutoSizeMinTextSize();
+        assertNotEquals(0, minSize);
+        final int maxSize = textView.getAutoSizeMaxTextSize();
+        assertNotEquals(0, maxSize);
+
+        // This is just a test check to verify the next assertions. If this fails it is a problem
+        // of this test setup (we need at least 2 units).
+        assertTrue((maxSize - minSize) > 1);
+        final int newMinSize = maxSize - 1;
+        textView.setAutoSizeMinTextSize(TypedValue.COMPLEX_UNIT_PX, newMinSize);
+        assertEquals(newMinSize, textView.getAutoSizeMinTextSize());
+        // Max size has not changed.
+        assertEquals(maxSize, textView.getAutoSizeMaxTextSize());
+
+        // Prevent validation error (max <= min).
+        textView.setAutoSizeMaxTextSize(TypedValue.COMPLEX_UNIT_SP, newMinSize + 10);
+        textView.setAutoSizeMinTextSize(TypedValue.COMPLEX_UNIT_SP, newMinSize);
+        // It does not matter which unit has been used to set the min size, the getter always
+        // returns it in pixels.
+        assertEquals((int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, newMinSize,
+                mActivity.getResources().getDisplayMetrics()), textView.getAutoSizeMinTextSize());
+    }
+
+    @Test(expected = IllegalStateException.class)
+    public void testAutoSizeXY_throwsException_whenMaxLessThanMin() {
+        final TextView textView = new TextView(mActivity);
+        textView.setAutoSizeTextType(TextView.AUTO_SIZE_TEXT_TYPE_XY);
+        textView.setAutoSizeMinTextSize(TypedValue.COMPLEX_UNIT_PX, 10);
+        // Should throw IllegalStateException here (because min > max).
+        textView.setAutoSizeMaxTextSize(TypedValue.COMPLEX_UNIT_PX, 9);
+    }
+
+    @Test
+    public void testAutoSizeXY_getSetAutoSizeMaxTextSize() {
+        final TextView textView = new TextView(mActivity);
+        textView.setAutoSizeTextType(TextView.AUTO_SIZE_TEXT_TYPE_XY);
+        assertEquals(TextView.AUTO_SIZE_TEXT_TYPE_XY, textView.getAutoSizeTextType());
+        final int minSize = textView.getAutoSizeMinTextSize();
+        assertNotEquals(0, minSize);
+        final int maxSize = textView.getAutoSizeMaxTextSize();
+        assertNotEquals(0, maxSize);
+
+        final int newMaxSize = maxSize + 11;
+        textView.setAutoSizeMaxTextSize(TypedValue.COMPLEX_UNIT_PX, newMaxSize);
+        assertEquals(newMaxSize, textView.getAutoSizeMaxTextSize());
+        // Min size has not changed.
+        assertEquals(minSize, textView.getAutoSizeMinTextSize());
+
+        textView.setAutoSizeMaxTextSize(TypedValue.COMPLEX_UNIT_SP, newMaxSize);
+        // It does not matter which unit has been used to set the max size, the getter always
+        // returns it in pixels.
+        assertEquals((int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, newMaxSize,
+                mActivity.getResources().getDisplayMetrics()), textView.getAutoSizeMaxTextSize());
+    }
+
+    @Test
+    public void testAutoSizeXY_autoSizeCalledWhenTypeChanged() throws Throwable {
+        mTextView = findTextView(R.id.textview_text);
+        // Make sure we pick an already inflated non auto-sized text view.
+        assertEquals(TextView.AUTO_SIZE_TEXT_TYPE_NONE, mTextView.getAutoSizeTextType());
+        // Set the text size to a very low value in order to prepare for auto-size.
+        final int customTextSize = 3;
+        mActivityRule.runOnUiThread(() ->
+                mTextView.setTextSize(TypedValue.COMPLEX_UNIT_PX, customTextSize));
+        mInstrumentation.waitForIdleSync();
+        assertEquals(customTextSize, mTextView.getTextSize(), 0f);
+        mActivityRule.runOnUiThread(() ->
+                mTextView.setAutoSizeTextType(TextView.AUTO_SIZE_TEXT_TYPE_XY));
+        mInstrumentation.waitForIdleSync();
+        // The size of the text should have changed.
+        assertNotEquals(customTextSize, mTextView.getTextSize(), 0f);
+    }
 
     /**
      * Some TextView attributes require non-fixed width and/or layout height. This function removes