Merge "Improve wallpaper CTS reliability"
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 f8a97e4..065f854 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
@@ -118,7 +118,7 @@
         dist_max = math.sqrt(pow(w, 2)+pow(h, 2))/2
         for spb_ct in SPB_CT_LIST:
             # list sample block center location
-            num_sample = (1-spb_ct*2)/spb_r/2 + 1
+            num_sample = int(numpy.asscalar((1-spb_ct*2)/spb_r/2 + 1))
             ct_cord_x = numpy.concatenate(
                         (numpy.arange(spb_ct, 1-spb_ct+spb_r, spb_r*2),
                          spb_ct*numpy.ones((num_sample-1)),
diff --git a/apps/CtsVerifier/AndroidManifest.xml b/apps/CtsVerifier/AndroidManifest.xml
index ee92391..ef49b68 100644
--- a/apps/CtsVerifier/AndroidManifest.xml
+++ b/apps/CtsVerifier/AndroidManifest.xml
@@ -126,6 +126,8 @@
             <meta-data android:name="test_category" android:value="@string/test_category_device_admin" />
             <meta-data android:name="test_required_features"
                     android:value="android.software.device_admin" />
+            <meta-data android:name="test_excluded_features"
+                    android:value="android.hardware.type.watch" />
         </activity>
 
         <activity android:name=".admin.ScreenLockTestActivity"
@@ -2968,6 +2970,7 @@
         <activity android:name=".managedprovisioning.CompHelperActivity">
             <intent-filter>
                 <action android:name="com.android.cts.verifier.managedprovisioning.COMP_SET_ALWAYS_ON_VPN" />
+                <action android:name="com.android.cts.verifier.managedprovisioning.COMP_SET_MAXIMUM_PASSWORD_ATTEMPTS" />
                 <category android:name="android.intent.category.DEFAULT"></category>
             </intent-filter>
         </activity>
diff --git a/apps/CtsVerifier/res/values/strings.xml b/apps/CtsVerifier/res/values/strings.xml
index 024f775..928d9afd 100755
--- a/apps/CtsVerifier/res/values/strings.xml
+++ b/apps/CtsVerifier/res/values/strings.xml
@@ -2924,6 +2924,18 @@
         7) Press the Reset button.
     </string>
     <string name="enterprise_privacy_set_default_apps">Set Default Apps</string>
+    <string name="enterprise_privacy_default_ime">Default keyboard</string>
+    <string name="enterprise_privacy_default_ime_info">
+        Please do the following:\n
+        1) Press the Open Settings button.\n
+        2) In the screen that opens, verify that you are not told the default keyboard has been set.\n
+        3) Use the Back button to return to this page.\n
+        4) Press the Set Keyboard button to set the default keyboard.\n
+        5) Repeat steps (1) through (3), verifying that in step (2), you are told now that the default keyboard has been set to CTS Verifier.\n
+        6) Press the Finish button to clear the default keyboard.
+    </string>
+    <string name="enterprise_privacy_set_keyboard">Set Keyboard</string>
+    <string name="enterprise_privacy_finish">Finish</string>
     <string name="enterprise_privacy_always_on_vpn">Always-on VPN</string>
     <string name="enterprise_privacy_always_on_vpn_info">
         Please do the following:\n
@@ -2935,7 +2947,6 @@
         6) Press the Finish button to clear the always-on VPN.
     </string>
     <string name="enterprise_privacy_set_always_on_vpn">Set VPN</string>
-    <string name="enterprise_privacy_finish">Finish</string>
     <string name="enterprise_privacy_comp_always_on_vpn">Always-on VPN (managed profile)</string>
     <string name="enterprise_privacy_comp_always_on_vpn_info">
         Please do the following:\n
@@ -2960,6 +2971,28 @@
     </string>
     <string name="enterprise_privacy_set_proxy">Set Proxy</string>
     <string name="enterprise_privacy_clear_proxy">Clear Proxy</string>
+    <string name="enterprise_privacy_failed_password_wipe">Wipe on authentication failure</string>
+    <string name="enterprise_privacy_failed_password_wipe_info">
+        Please do the following:\n
+        1) Press the Open Settings button.\n
+        2) In the screen that opens, verify that you are not told all device data will be deleted if you mistype your password too many times.\n
+        3) Use the Back button to return to this page.\n
+        4) Press the Set Limit button to set the maximum number of password attempts.\n
+        5) Repeat steps (1) through (3), verifying that in step (2), you are told now that all device data will be deleted if you mistype your password 100 times.\n
+        6) Press the Finish button to clear the maximum number of password attempts.
+    </string>
+    <string name="enterprise_privacy_set_limit">Set Limit</string>
+    <string name="enterprise_privacy_comp_failed_password_wipe">Wipe on authentication failure (managed profile)</string>
+    <string name="enterprise_privacy_comp_failed_password_wipe_info">
+        Please do the following:\n
+        1) Press the Start button to create a work profile.\n
+        2) Press the Open Settings button.\n
+        3) In the screen that opens, verify that you are not told work profile data will be deleted if you mistype your password too many times.\n
+        4) Use the Back button to return to this page.\n
+        5) Press the Set Limit button to set the maximum number of password attempts.\n
+        6) Repeat steps (2) through (4), verifying that in step (3), you are told now that work profile device data will be deleted if you mistype your password 100 times.\n
+        7) Press the Finish button to remove the work profile.
+    </string>
     <string name="enterprise_privacy_quick_settings">Quick settings disclosure</string>
     <string name="enterprise_privacy_quick_settings_info">
         Please do the following:\n
diff --git a/apps/CtsVerifier/res/xml/device_admin_comp_profile.xml b/apps/CtsVerifier/res/xml/device_admin_comp_profile.xml
index 3e6b332..784258d 100644
--- a/apps/CtsVerifier/res/xml/device_admin_comp_profile.xml
+++ b/apps/CtsVerifier/res/xml/device_admin_comp_profile.xml
@@ -15,5 +15,10 @@
 -->
 
 <!-- BEGIN_INCLUDE(meta_data) -->
-<device-admin xmlns:android="http://schemas.android.com/apk/res/android"/>
+<device-admin xmlns:android="http://schemas.android.com/apk/res/android">
+    <uses-policies>
+        <wipe-data />
+        <watch-login />
+    </uses-policies>
+</device-admin>
 <!-- END_INCLUDE(meta_data) -->
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/CommandReceiverActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/CommandReceiverActivity.java
index cc1043d..baec2a3 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/CommandReceiverActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/CommandReceiverActivity.java
@@ -33,6 +33,7 @@
 import android.os.UserManager;
 import android.provider.ContactsContract;
 import android.provider.MediaStore;
+import android.provider.Settings;
 import android.util.Log;
 
 import com.android.cts.verifier.R;
@@ -91,6 +92,12 @@
     public static final String COMMAND_CLEAR_ALWAYS_ON_VPN = "clear-always-on-vpn";
     public static final String COMMAND_SET_GLOBAL_HTTP_PROXY = "set-global-http-proxy";
     public static final String COMMAND_CLEAR_GLOBAL_HTTP_PROXY = "clear-global-http-proxy";
+    public static final String COMMAND_SET_MAXIMUM_PASSWORD_ATTEMPTS =
+            "set-maximum-password-attempts";
+    public static final String COMMAND_CLEAR_MAXIMUM_PASSWORD_ATTEMPTS =
+            "clear-maximum-password-attempts";
+    public static final String COMMAND_SET_DEFAULT_IME = "set-default-ime";
+    public static final String COMMAND_CLEAR_DEFAULT_IME = "clear-default-ime";
 
     public static final String EXTRA_USER_RESTRICTION =
             "com.android.cts.verifier.managedprovisioning.extra.USER_RESTRICTION";
@@ -378,6 +385,31 @@
                     }
                     mDpm.setRecommendedGlobalProxy(mAdmin, null);
                 }
+                case COMMAND_SET_MAXIMUM_PASSWORD_ATTEMPTS: {
+                    if (!mDpm.isDeviceOwnerApp(getPackageName())) {
+                        return;
+                    }
+                    mDpm.setMaximumFailedPasswordsForWipe(mAdmin, 100);
+                } break;
+                case COMMAND_CLEAR_MAXIMUM_PASSWORD_ATTEMPTS: {
+                    if (!mDpm.isDeviceOwnerApp(getPackageName())) {
+                        return;
+                    }
+                    mDpm.setMaximumFailedPasswordsForWipe(mAdmin, 0);
+                } break;
+                case COMMAND_SET_DEFAULT_IME: {
+                    if (!mDpm.isDeviceOwnerApp(getPackageName())) {
+                        return;
+                    }
+                    mDpm.setSecureSetting(mAdmin, Settings.Secure.DEFAULT_INPUT_METHOD,
+                            getPackageName());
+                } break;
+                case COMMAND_CLEAR_DEFAULT_IME: {
+                    if (!mDpm.isDeviceOwnerApp(getPackageName())) {
+                        return;
+                    }
+                    mDpm.setSecureSetting(mAdmin, Settings.Secure.DEFAULT_INPUT_METHOD, null);
+                }
             }
         } catch (Exception e) {
             Log.e(TAG, "Failed to execute command: " + intent, e);
@@ -442,6 +474,8 @@
         mDpm.clearPackagePersistentPreferredActivities(mAdmin, getPackageName());
         mDpm.setAlwaysOnVpnPackage(mAdmin, null, false);
         mDpm.setRecommendedGlobalProxy(mAdmin, null);
+        mDpm.setMaximumFailedPasswordsForWipe(mAdmin, 0);
+        mDpm.setSecureSetting(mAdmin, Settings.Secure.DEFAULT_INPUT_METHOD, null);
 
         uninstallHelperPackage();
         removeManagedProfile();
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/CompDeviceAdminTestReceiver.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/CompDeviceAdminTestReceiver.java
index 4f8b78a..bd43e8e 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/CompDeviceAdminTestReceiver.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/CompDeviceAdminTestReceiver.java
@@ -45,6 +45,7 @@
             // user to send us commands.
             final IntentFilter filter = new IntentFilter();
             filter.addAction(CompHelperActivity.ACTION_SET_ALWAYS_ON_VPN);
+            filter.addAction(CompHelperActivity.ACTION_SET_MAXIMUM_PASSWORD_ATTEMPTS);
             dpm.addCrossProfileIntentFilter(getWho(context), filter,
                     DevicePolicyManager.FLAG_MANAGED_CAN_ACCESS_PARENT);
         }
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/CompHelperActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/CompHelperActivity.java
index 3da0547..8d965a7 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/CompHelperActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/CompHelperActivity.java
@@ -36,22 +36,27 @@
     // Set always-on VPN.
     public static final String ACTION_SET_ALWAYS_ON_VPN
             = "com.android.cts.verifier.managedprovisioning.COMP_SET_ALWAYS_ON_VPN";
+    // Set the number of login failures after which the managed profile is wiped.
+    public static final String ACTION_SET_MAXIMUM_PASSWORD_ATTEMPTS
+            = "com.android.cts.verifier.managedprovisioning.COMP_SET_MAXIMUM_PASSWORD_ATTEMPTS";
 
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
 
+        final ComponentName admin = CompDeviceAdminTestReceiver.getReceiverComponentName();
         final DevicePolicyManager dpm = (DevicePolicyManager) getSystemService(
                 Context.DEVICE_POLICY_SERVICE);
 
         final String action = getIntent().getAction();
         if (ACTION_SET_ALWAYS_ON_VPN.equals(action)) {
             try {
-                dpm.setAlwaysOnVpnPackage(CompDeviceAdminTestReceiver.getReceiverComponentName(),
-                        getPackageName(), false /* lockdownEnabled */);
+                dpm.setAlwaysOnVpnPackage(admin, getPackageName(), false /* lockdownEnabled */);
             } catch (Exception e) {
                 Log.e(TAG, "Unable to set always-on VPN", e);
             }
+        } else if (ACTION_SET_MAXIMUM_PASSWORD_ATTEMPTS.equals(action)) {
+            dpm.setMaximumFailedPasswordsForWipe(admin, 100);
         }
         finish();
     }
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/EnterprisePrivacyTestListActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/EnterprisePrivacyTestListActivity.java
index 54a7ec7..165bcf7 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/EnterprisePrivacyTestListActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/EnterprisePrivacyTestListActivity.java
@@ -54,12 +54,18 @@
             = "ENTERPRISE_PRIVACY_CAMERA_ACCESS";
     private static final String ENTERPRISE_PRIVACY_DEFAULT_APPS
             = "ENTERPRISE_PRIVACY_DEFAULT_APPS";
+    private static final String ENTERPRISE_PRIVACY_DEFAULT_IME
+            = "ENTERPRISE_PRIVACY_DEFAULT_IME";
     private static final String ENTERPRISE_PRIVACY_ALWAYS_ON_VPN
             = "ENTERPRISE_PRIVACY_ALWAYS_ON_VPN";
     private static final String ENTERPRISE_PRIVACY_COMP_ALWAYS_ON_VPN
             = "ENTERPRISE_PRIVACY_COMP_ALWAYS_ON_VPN";
     private static final String ENTERPRISE_PRIVACY_GLOBAL_HTTP_PROXY
             = "ENTERPRISE_PRIVACY_GLOBAL_HTTP_PROXY";
+    private static final String ENTERPRISE_PRIVACY_FAILED_PASSWORD_WIPE
+            = "ENTERPRISE_PRIVACY_FAILED_PASSWORD_WIPE";
+    private static final String ENTERPRISE_PRIVACY_COMP_FAILED_PASSWORD_WIPE
+            = "ENTERPRISE_PRIVACY_COMP_FAILED_PASSWORD_WIPE";
     private static final String ENTERPRISE_PRIVACY_QUICK_SETTINGS
             = "ENTERPRISE_PRIVACY_QUICK_SETTINGS";
     private static final String ENTERPRISE_PRIVACY_KEYGUARD = "ENTERPRISE_PRIVACY_KEYGUARD";
@@ -68,14 +74,6 @@
     public static final String EXTRA_TEST_ID =
             "com.android.cts.verifier.managedprovisioning.extra.TEST_ID";
 
-    private void setManagedProfileActivityEnabled(boolean enabled) {
-        getPackageManager().setComponentEnabledSetting(
-                new ComponentName(this, CompHelperActivity.class),
-                enabled ? PackageManager.COMPONENT_ENABLED_STATE_DEFAULT :
-                        PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
-                PackageManager.DONT_KILL_APP);
-    }
-
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
@@ -90,7 +88,10 @@
             }
         });
         setTestListAdapter(adapter);
-        setManagedProfileActivityEnabled(false);
+        getPackageManager().setComponentEnabledSetting(
+                new ComponentName(this, CompHelperActivity.class),
+                PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
+                PackageManager.DONT_KILL_APP);
     }
 
     private Intent buildCommandIntent(String command) {
@@ -181,6 +182,18 @@
                         new ButtonInfo(R.string.enterprise_privacy_set_default_apps,
                                 buildCommandIntent(CommandReceiverActivity
                                         .COMMAND_ADD_PERSISTENT_PREFERRED_ACTIVITIES))}));
+        adapter.add(createInteractiveTestItem(this, ENTERPRISE_PRIVACY_DEFAULT_IME,
+                R.string.enterprise_privacy_default_ime,
+                R.string.enterprise_privacy_default_ime_info,
+                new ButtonInfo[] {
+                        new ButtonInfo(R.string.enterprise_privacy_open_settings,
+                                new Intent(Settings.ACTION_ENTERPRISE_PRIVACY_SETTINGS)),
+                        new ButtonInfo(R.string.enterprise_privacy_set_keyboard,
+                                buildCommandIntent(CommandReceiverActivity
+                                        .COMMAND_SET_DEFAULT_IME)),
+                        new ButtonInfo(R.string.enterprise_privacy_finish,
+                                buildCommandIntent(CommandReceiverActivity
+                                        .COMMAND_CLEAR_DEFAULT_IME))}));
         adapter.add(createInteractiveTestItem(this, ENTERPRISE_PRIVACY_ALWAYS_ON_VPN,
                 R.string.enterprise_privacy_always_on_vpn,
                 R.string.enterprise_privacy_always_on_vpn_info,
@@ -219,6 +232,32 @@
                         new ButtonInfo(R.string.enterprise_privacy_clear_proxy,
                                 buildCommandIntent(CommandReceiverActivity
                                         .COMMAND_CLEAR_GLOBAL_HTTP_PROXY))}));
+        adapter.add(createInteractiveTestItem(this, ENTERPRISE_PRIVACY_FAILED_PASSWORD_WIPE,
+                R.string.enterprise_privacy_failed_password_wipe,
+                R.string.enterprise_privacy_failed_password_wipe_info,
+                new ButtonInfo[] {
+                        new ButtonInfo(R.string.enterprise_privacy_open_settings,
+                                new Intent(Settings.ACTION_ENTERPRISE_PRIVACY_SETTINGS)),
+                        new ButtonInfo(R.string.enterprise_privacy_set_limit,
+                                buildCommandIntent(CommandReceiverActivity
+                                        .COMMAND_SET_MAXIMUM_PASSWORD_ATTEMPTS)),
+                        new ButtonInfo(R.string.enterprise_privacy_finish,
+                                buildCommandIntent(CommandReceiverActivity
+                                        .COMMAND_CLEAR_MAXIMUM_PASSWORD_ATTEMPTS))}));
+        adapter.add(createInteractiveTestItem(this, ENTERPRISE_PRIVACY_COMP_FAILED_PASSWORD_WIPE,
+                R.string.enterprise_privacy_comp_failed_password_wipe,
+                R.string.enterprise_privacy_comp_failed_password_wipe_info,
+                new ButtonInfo[] {
+                        new ButtonInfo(R.string.enterprise_privacy_start,
+                                buildCommandIntent(
+                                        CommandReceiverActivity.COMMAND_CREATE_MANAGED_PROFILE)),
+                        new ButtonInfo(R.string.enterprise_privacy_open_settings,
+                                new Intent(Settings.ACTION_ENTERPRISE_PRIVACY_SETTINGS)),
+                        new ButtonInfo(R.string.enterprise_privacy_set_limit, new Intent(
+                                CompHelperActivity.ACTION_SET_MAXIMUM_PASSWORD_ATTEMPTS)),
+                        new ButtonInfo(R.string.enterprise_privacy_finish,
+                                buildCommandIntent(
+                                        CommandReceiverActivity.COMMAND_REMOVE_MANAGED_PROFILE))}));
         adapter.add(createInteractiveTestItem(this, ENTERPRISE_PRIVACY_QUICK_SETTINGS,
                 R.string.enterprise_privacy_quick_settings,
                 R.string.enterprise_privacy_quick_settings_info,
@@ -274,6 +313,5 @@
                         PolicyTransparencyTestListActivity.MODE_DEVICE_OWNER);
         startActivity(intent);
         startActivity(buildCommandIntent(CommandReceiverActivity.COMMAND_REMOVE_MANAGED_PROFILE));
-        setManagedProfileActivityEnabled(false);
     }
 }
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 aba4833..335abf4 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
@@ -66,6 +66,8 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
 
 /**
  * Collect test results for an entire invocation and output test results to disk.
@@ -111,6 +113,7 @@
     private String mReferenceUrl;
     private ILogSaver mLogSaver;
     private int invocationEndedCount = 0;
+    private CountDownLatch mFinalized = null;
 
     private IInvocationResult mResult = new InvocationResult();
     private IModuleResult mCurrentModuleResult;
@@ -145,6 +148,7 @@
      */
     public ResultReporter() {
         this(null);
+        mFinalized = new CountDownLatch(1);
     }
 
     /**
@@ -452,6 +456,7 @@
                 return;
             }
             finalizeResults(elapsedTime);
+            mFinalized.countDown();
         }
     }
 
@@ -808,4 +813,12 @@
     public IInvocationResult getResult() {
         return mResult;
     }
+
+    /**
+     * Returns true if the reporter is finalized before the end of the timeout. False otherwise.
+     */
+    @VisibleForTesting
+    public boolean waitForFinalized(long timeout, TimeUnit unit) throws InterruptedException {
+        return mFinalized.await(timeout, unit);
+    }
 }
diff --git a/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/presubmit/IntegrationTest.java b/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/presubmit/IntegrationTest.java
index 2036289..c8a8905 100644
--- a/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/presubmit/IntegrationTest.java
+++ b/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/presubmit/IntegrationTest.java
@@ -16,6 +16,7 @@
 package com.android.compatibility.common.tradefed.presubmit;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
 
 import com.android.compatibility.SuiteInfo;
 import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
@@ -55,6 +56,7 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.concurrent.TimeUnit;
 
 /**
  * Integration tests between {@link CompatibilityTest} and {@link ResultReporter} to ensure proper
@@ -481,7 +483,9 @@
         for (ShardThread thread : threads) {
             thread.join(1000);
         }
-
+        // Allow some time for ResultReport to finalize the results coming from the threads.
+        boolean finalized = mReporter.waitForFinalized(2000, TimeUnit.MILLISECONDS);
+        assertTrue(finalized);
         EasyMock.verify(mMockDevice, mMockBuildInfo);
         // Check aggregated results to make sure it's consistent.
         IInvocationResult result = mReporter.getResult();
@@ -530,7 +534,9 @@
         for (ShardThread thread : threads) {
             thread.join(1000);
         }
-
+        // Allow some time for ResultReport to finalize the results coming from the threads.
+        boolean finalized = mReporter.waitForFinalized(2000, TimeUnit.MILLISECONDS);
+        assertTrue(finalized);
         EasyMock.verify(mMockDevice, mMockBuildInfo);
         // Check aggregated results to make sure it's consistent.
         IInvocationResult result = mReporter.getResult();
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/InstantCookieHostTest.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/InstantCookieHostTest.java
index 034da99..e5a483c 100644
--- a/hostsidetests/appsecurity/src/android/appsecurity/cts/InstantCookieHostTest.java
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/InstantCookieHostTest.java
@@ -89,7 +89,7 @@
 
     private String installPackage(String apk, boolean replace, boolean instant) throws Exception {
         return getDevice().installPackage(mBuildHelper.getTestFile(apk), replace,
-                instant ? "--ephemeral" : "");
+                instant ? "--instant" : "--full");
     }
 
     private String uninstallPackage(String packageName) throws DeviceNotAvailableException {
diff --git a/hostsidetests/appsecurity/test-apps/EncryptionApp/src/com/android/cts/encryptionapp/EncryptionAppTest.java b/hostsidetests/appsecurity/test-apps/EncryptionApp/src/com/android/cts/encryptionapp/EncryptionAppTest.java
index 63b2f21..15197a0 100644
--- a/hostsidetests/appsecurity/test-apps/EncryptionApp/src/com/android/cts/encryptionapp/EncryptionAppTest.java
+++ b/hostsidetests/appsecurity/test-apps/EncryptionApp/src/com/android/cts/encryptionapp/EncryptionAppTest.java
@@ -19,7 +19,6 @@
 import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE;
 import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
 
-import android.app.admin.DevicePolicyManager;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.Context;
@@ -32,8 +31,6 @@
 import android.os.UserManager;
 import android.provider.Settings;
 import android.support.test.uiautomator.UiDevice;
-import android.support.test.uiautomator.UiObject;
-import android.support.test.uiautomator.UiSelector;
 import android.test.InstrumentationTestCase;
 import android.text.format.DateUtils;
 import android.util.Log;
@@ -43,9 +40,6 @@
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 
-// for another encryption ui setting package
-import android.content.pm.ResolveInfo;
-
 public class EncryptionAppTest extends InstrumentationTestCase {
     private static final String TAG = "EncryptionAppTest";
 
@@ -65,9 +59,6 @@
     private UiDevice mDevice;
     private AwareActivity mActivity;
 
-    // for another encryption ui setting package
-    private String mEncryptionSettingPackage = "com.android.settings";
-
     @Override
     public void setUp() throws Exception {
         super.setUp();
@@ -76,13 +67,6 @@
         mDe = mCe.createDeviceProtectedStorageContext();
         mPm = mCe.getPackageManager();
 
-        final Intent intent = new Intent(DevicePolicyManager.ACTION_SET_NEW_PASSWORD);
-        intent.addCategory(Intent.CATEGORY_DEFAULT);
-        ResolveInfo rInfo = mPm.resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY);
-        if (rInfo != null) {
-            mEncryptionSettingPackage = rInfo.activityInfo.packageName;
-        }
-
         mDevice = UiDevice.getInstance(getInstrumentation());
         assertNotNull(mDevice);
     }
@@ -108,44 +92,7 @@
         mDevice.waitForIdle();
 
         // Set a PIN for this user
-        final Intent intent = new Intent(DevicePolicyManager.ACTION_SET_NEW_PASSWORD);
-        intent.addCategory(Intent.CATEGORY_DEFAULT);
-        mActivity.startActivity(intent);
-        mDevice.waitForIdle();
-
-        // Pick PIN from the option list
-        UiObject view = new UiObject(new UiSelector()
-                .resourceId(mEncryptionSettingPackage + ":id/lock_pin"));
-        assertTrue("lock_pin", view.waitForExists(TIMEOUT));
-        view.click();
-        mDevice.waitForIdle();
-
-        // Ignore any interstitial options
-        view = new UiObject(new UiSelector()
-                .resourceId(mEncryptionSettingPackage + ":id/encrypt_dont_require_password"));
-        if (view.waitForExists(TIMEOUT)) {
-            view.click();
-            mDevice.waitForIdle();
-        }
-
-        // Yes, we really want to
-        view = new UiObject(new UiSelector()
-                .resourceId(mEncryptionSettingPackage + ":id/next_button"));
-        if (view.waitForExists(TIMEOUT)) {
-            view.click();
-            mDevice.waitForIdle();
-        }
-
-        // Set our PIN
-        view = new UiObject(new UiSelector()
-                .resourceId(mEncryptionSettingPackage + ":id/password_entry"));
-        assertTrue("password_entry", view.waitForExists(TIMEOUT));
-
-        // Enter it twice to confirm
-        enterTestPin();
-        enterTestPin();
-
-        mDevice.pressBack();
+        mDevice.executeShellCommand("locksettings set-pin 12345");
     }
 
     public void testTearDown() throws Exception {
@@ -157,48 +104,7 @@
         mDevice.waitForIdle();
 
         // Clear PIN for this user
-        final Intent intent = new Intent(DevicePolicyManager.ACTION_SET_NEW_PASSWORD);
-        intent.addCategory(Intent.CATEGORY_DEFAULT);
-        mActivity.startActivity(intent);
-        mDevice.waitForIdle();
-
-        // Enter current PIN
-        UiObject view = new UiObject(new UiSelector()
-                .resourceId(mEncryptionSettingPackage + ":id/password_entry"));
-        if (!view.waitForExists(TIMEOUT)) {
-            // Odd, maybe there is a crash dialog showing; try dismissing it
-            mDevice.pressBack();
-            mDevice.waitForIdle();
-
-            assertTrue("password_entry", view.waitForExists(TIMEOUT));
-        }
-
-        enterTestPin();
-
-        // Set back to "none"
-        view = new UiObject(new UiSelector()
-                .resourceId(mEncryptionSettingPackage + ":id/lock_none"));
-        assertTrue("lock_none", view.waitForExists(TIMEOUT));
-        view.click();
-        mDevice.waitForIdle();
-
-        // Yes, we really want "none" if prompted again
-        view = new UiObject(new UiSelector()
-                .resourceId("com.android.settings:id/lock_none"));
-        if (view.waitForExists(TIMEOUT)) {
-            view.click();
-            mDevice.waitForIdle();
-        }
-
-        // Yes, we really want to
-        view = new UiObject(new UiSelector()
-                .resourceId("android:id/button1"));
-        if (view.waitForExists(TIMEOUT)) {
-            view.click();
-            mDevice.waitForIdle();
-        }
-
-        mDevice.pressBack();
+        mDevice.executeShellCommand("locksettings clear --old 12345");
     }
 
     public void doBootCountBefore() throws Exception {
diff --git a/hostsidetests/compilation/assets/primary.prof b/hostsidetests/compilation/assets/primary.prof
index cef941e..71c158d 100644
--- a/hostsidetests/compilation/assets/primary.prof
+++ b/hostsidetests/compilation/assets/primary.prof
Binary files differ
diff --git a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/api23/Android.mk b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/api23/Android.mk
index 5a6794a..516e32e 100644
--- a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/api23/Android.mk
+++ b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/api23/Android.mk
@@ -24,6 +24,8 @@
 
 LOCAL_SRC_FILES := $(call all-java-files-under, ../src)
 
+LOCAL_JAVA_LIBRARIES = conscrypt
+
 LOCAL_STATIC_JAVA_LIBRARIES = android-support-v4 compatibility-device-util ctstestrunner ub-uiautomator
 
 LOCAL_SDK_VERSION := test_current
diff --git a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/api25/Android.mk b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/api25/Android.mk
index cf8a05d..962c5f5 100644
--- a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/api25/Android.mk
+++ b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/api25/Android.mk
@@ -24,6 +24,8 @@
 
 LOCAL_SRC_FILES := $(call all-java-files-under, ../src)
 
+LOCAL_JAVA_LIBRARIES = conscrypt
+
 LOCAL_STATIC_JAVA_LIBRARIES = android-support-v4 compatibility-device-util ctstestrunner ub-uiautomator
 
 LOCAL_SDK_VERSION := test_current
diff --git a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/latest/Android.mk b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/latest/Android.mk
index 5e083f7..1167a08 100644
--- a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/latest/Android.mk
+++ b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/latest/Android.mk
@@ -24,6 +24,8 @@
 
 LOCAL_SRC_FILES := $(call all-java-files-under, ../src)
 
+LOCAL_JAVA_LIBRARIES = conscrypt
+
 LOCAL_STATIC_JAVA_LIBRARIES = android-support-v4 compatibility-device-util ctstestrunner ub-uiautomator
 
 LOCAL_SDK_VERSION := test_current
diff --git a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/DelegatedCertInstallerTest.java b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/DelegatedCertInstallerTest.java
index cebdb68..3a6e65a 100644
--- a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/DelegatedCertInstallerTest.java
+++ b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/DelegatedCertInstallerTest.java
@@ -24,9 +24,18 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.os.Build;
+import android.os.Process;
+import android.os.UserHandle;
 import android.security.KeyChainException;
 import android.test.MoreAsserts;
 
+import com.android.org.conscrypt.TrustedCertificateStore;
+
+import java.io.ByteArrayInputStream;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
+import java.util.List;
 import java.util.concurrent.Semaphore;
 import java.util.concurrent.TimeUnit;
 
@@ -170,14 +179,18 @@
         super.tearDown();
     }
 
-    public void testCaCertsOperations() throws InterruptedException {
-        byte[] cert = TEST_CA.getBytes();
+    public void testCaCertsOperations() throws InterruptedException, CertificateException {
+        final byte[] cert = TEST_CA.getBytes();
+        final Certificate caCert = CertificateFactory.getInstance("X.509")
+                .generateCertificate(new ByteArrayInputStream(cert));
+        final TrustedCertificateStore store = new TrustedCertificateStore();
 
         mDpm.setCertInstallerPackage(ADMIN_RECEIVER_COMPONENT, CERT_INSTALLER_PACKAGE);
         assertEquals(CERT_INSTALLER_PACKAGE,
                 mDpm.getCertInstallerPackage(ADMIN_RECEIVER_COMPONENT));
 
         // Exercise installCaCert()
+        assertNull(store.getCertificateAlias(caCert));
         installCaCert(cert);
         assertResult("installCaCert", true);
         assertTrue("Certificate is not installed properly", mDpm.hasCaCertInstalled(
@@ -187,12 +200,21 @@
         verifyCaCert(cert);
         assertResult("getInstalledCaCerts()", true);
 
+        // Verify that the CA cert was marked as installed by the Device Owner / Profile Owner.
+        final String alias = store.getCertificateAlias(caCert);
+        assertNotNull(alias);
+        verifyOwnerInstalledStatus(alias, true);
+
         // Exercise uninstallCaCert()
         removeCaCert(cert);
         assertResult("uninstallCaCert()", true);
         assertFalse("Certificate is not removed properly", mDpm.hasCaCertInstalled(
                 ADMIN_RECEIVER_COMPONENT, cert));
 
+        // Verify that the CA cert is no longer reported as installed by the Device Owner / Profile
+        // Owner.
+        verifyOwnerInstalledStatus(alias, false);
+
         // Clear delegated cert installer.
         // Tests after this are expected to fail.
         mDpm.setCertInstallerPackage(ADMIN_RECEIVER_COMPONENT, null);
@@ -288,6 +310,13 @@
         mContext.sendBroadcast(intent);
     }
 
+    private void verifyOwnerInstalledStatus(String alias, boolean expectOwnerInstalled) {
+        final List<String> ownerInstalledCerts =
+                mDpm.getOwnerInstalledCaCerts(Process.myUserHandle());
+        assertNotNull(ownerInstalledCerts);
+        assertEquals(expectOwnerInstalled, ownerInstalledCerts.contains(alias));
+    }
+
     private void assertResult(String testName, Boolean expectSuccess) throws InterruptedException {
         assertTrue("Cert installer did not respond in time.",
                 mAvailableResultSemaphore.tryAcquire(10, TimeUnit.SECONDS));
diff --git a/hostsidetests/devicepolicy/app/DeviceOwner/Android.mk b/hostsidetests/devicepolicy/app/DeviceOwner/Android.mk
index a2d7a60..4e9abd7 100644
--- a/hostsidetests/devicepolicy/app/DeviceOwner/Android.mk
+++ b/hostsidetests/devicepolicy/app/DeviceOwner/Android.mk
@@ -24,7 +24,7 @@
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
-LOCAL_JAVA_LIBRARIES := android.test.runner cts-junit
+LOCAL_JAVA_LIBRARIES := android.test.runner conscrypt cts-junit
 
 LOCAL_STATIC_JAVA_LIBRARIES := \
     ctstestrunner \
diff --git a/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/AdminActionBookkeepingTest.java b/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/AdminActionBookkeepingTest.java
index a5e2768..ff51e9e 100644
--- a/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/AdminActionBookkeepingTest.java
+++ b/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/AdminActionBookkeepingTest.java
@@ -20,17 +20,50 @@
 import android.content.ContentResolver;
 import android.content.Context;
 import android.os.Process;
-import android.os.UserHandle;
 import android.provider.Settings;
 
+import com.android.org.conscrypt.TrustedCertificateStore;
+
+import java.io.ByteArrayInputStream;
 import java.lang.reflect.Method;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateFactory;
+import java.util.List;
 
 public class AdminActionBookkeepingTest extends BaseDeviceOwnerTest {
+    /*
+     * The CA cert below is the content of cacert.pem as generated by:
+     *
+     * openssl req -new -x509 -days 3650 -extensions v3_ca -keyout cakey.pem -out cacert.pem
+     */
+    private static final String TEST_CA =
+            "-----BEGIN CERTIFICATE-----\n" +
+            "MIIDXTCCAkWgAwIBAgIJAK9Tl/F9V8kSMA0GCSqGSIb3DQEBCwUAMEUxCzAJBgNV\n" +
+            "BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX\n" +
+            "aWRnaXRzIFB0eSBMdGQwHhcNMTUwMzA2MTczMjExWhcNMjUwMzAzMTczMjExWjBF\n" +
+            "MQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50\n" +
+            "ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB\n" +
+            "CgKCAQEAvItOutsE75WBTgTyNAHt4JXQ3JoseaGqcC3WQij6vhrleWi5KJ0jh1/M\n" +
+            "Rpry7Fajtwwb4t8VZa0NuM2h2YALv52w1xivql88zce/HU1y7XzbXhxis9o6SCI+\n" +
+            "oVQSbPeXRgBPppFzBEh3ZqYTVhAqw451XhwdA4Aqs3wts7ddjwlUzyMdU44osCUg\n" +
+            "kVg7lfPf9sTm5IoHVcfLSCWH5n6Nr9sH3o2ksyTwxuOAvsN11F/a0mmUoPciYPp+\n" +
+            "q7DzQzdi7akRG601DZ4YVOwo6UITGvDyuAAdxl5isovUXqe6Jmz2/myTSpAKxGFs\n" +
+            "jk9oRoG6WXWB1kni490GIPjJ1OceyQIDAQABo1AwTjAdBgNVHQ4EFgQUH1QIlPKL\n" +
+            "p2OQ/AoLOjKvBW4zK3AwHwYDVR0jBBgwFoAUH1QIlPKLp2OQ/AoLOjKvBW4zK3Aw\n" +
+            "DAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAcMi4voMMJHeQLjtq8Oky\n" +
+            "Azpyk8moDwgCd4llcGj7izOkIIFqq/lyqKdtykVKUWz2bSHO5cLrtaOCiBWVlaCV\n" +
+            "DYAnnVLM8aqaA6hJDIfaGs4zmwz0dY8hVMFCuCBiLWuPfiYtbEmjHGSmpQTG6Qxn\n" +
+            "ZJlaK5CZyt5pgh5EdNdvQmDEbKGmu0wpCq9qjZImwdyAul1t/B0DrsWApZMgZpeI\n" +
+            "d2od0VBrCICB1K4p+C51D93xyQiva7xQcCne+TAnGNy9+gjQ/MyR8MRpwRLv5ikD\n" +
+            "u0anJCN8pXo6IMglfMAsoton1J6o5/ae5uhC6caQU8bNUsCK570gpNfjkzo6rbP0\n" +
+            "wQ==\n" +
+            "-----END CERTIFICATE-----";
 
     @Override
     protected void tearDown() throws Exception {
         mDevicePolicyManager.setSecurityLoggingEnabled(getWho(), false);
         mDevicePolicyManager.setNetworkLoggingEnabled(getWho(), false);
+        mDevicePolicyManager.uninstallCaCert(getWho(), TEST_CA.getBytes());
 
         super.tearDown();
     }
@@ -134,24 +167,55 @@
     }
 
     /**
-     * Test: It should be recored whether the Device Owner or the user set the default IME.
+     * Test: It should be recored whether the Device Owner or the user set the current IME.
      */
     public void testIsDefaultInputMethodSet() throws Exception {
-        final UserHandle userHandle = Process.myUserHandle();
         final String setting = Settings.Secure.DEFAULT_INPUT_METHOD;
         final ContentResolver resolver = getContext().getContentResolver();
         final String ime = Settings.Secure.getString(resolver, setting);
 
         Settings.Secure.putString(resolver, setting, "com.test.1");
         Thread.sleep(500);
-        assertFalse(mDevicePolicyManager.isDefaultInputMethodSetByOwner(userHandle));
+        assertFalse(mDevicePolicyManager.isCurrentInputMethodSetByOwner());
 
         mDevicePolicyManager.setSecureSetting(getWho(), setting, "com.test.2");
         Thread.sleep(500);
-        assertTrue(mDevicePolicyManager.isDefaultInputMethodSetByOwner(userHandle));
+        assertTrue(mDevicePolicyManager.isCurrentInputMethodSetByOwner());
 
         Settings.Secure.putString(resolver, setting, ime);
         Thread.sleep(500);
-        assertFalse(mDevicePolicyManager.isDefaultInputMethodSetByOwner(userHandle));
+        assertFalse(mDevicePolicyManager.isCurrentInputMethodSetByOwner());
+    }
+
+    /**
+     * Test: It should be recored whether the Device Owner or the user installed a CA cert.
+     */
+    public void testGetPolicyInstalledCaCerts() throws Exception {
+        final byte[] rawCert = TEST_CA.getBytes();
+        final Certificate cert = CertificateFactory.getInstance("X.509")
+                .generateCertificate(new ByteArrayInputStream(rawCert));
+        final TrustedCertificateStore store = new TrustedCertificateStore();
+
+        // Install a CA cert.
+        assertNull(store.getCertificateAlias(cert));
+        assertTrue(mDevicePolicyManager.installCaCert(getWho(), rawCert));
+        final String alias = store.getCertificateAlias(cert);
+        assertNotNull(alias);
+
+        // Verify that the CA cert was marked as installed by the Device Owner.
+        verifyOwnerInstalledStatus(alias, true);
+
+        // Uninstall the CA cert.
+        mDevicePolicyManager.uninstallCaCert(getWho(), rawCert);
+
+        // Verify that the CA cert is no longer marked as installed by the Device Owner.
+        verifyOwnerInstalledStatus(alias, false);
+    }
+
+    private void verifyOwnerInstalledStatus(String alias, boolean expectOwnerInstalled) {
+        final List<String> ownerInstalledCerts =
+                mDevicePolicyManager.getOwnerInstalledCaCerts(Process.myUserHandle());
+        assertNotNull(ownerInstalledCerts);
+        assertEquals(expectOwnerInstalled, ownerInstalledCerts.contains(alias));
     }
 }
diff --git a/hostsidetests/devicepolicy/app/ProfileOwner/Android.mk b/hostsidetests/devicepolicy/app/ProfileOwner/Android.mk
index 6b83df7..6f9ef5d 100644
--- a/hostsidetests/devicepolicy/app/ProfileOwner/Android.mk
+++ b/hostsidetests/devicepolicy/app/ProfileOwner/Android.mk
@@ -24,7 +24,7 @@
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
-LOCAL_JAVA_LIBRARIES := android.test.runner cts-junit
+LOCAL_JAVA_LIBRARIES := android.test.runner conscrypt cts-junit
 
 LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner compatibility-device-util ub-uiautomator
 
diff --git a/hostsidetests/devicepolicy/app/ProfileOwner/src/com/android/cts/profileowner/AdminActionBookkeepingTest.java b/hostsidetests/devicepolicy/app/ProfileOwner/src/com/android/cts/profileowner/AdminActionBookkeepingTest.java
index 8bfa080..ba7919d 100644
--- a/hostsidetests/devicepolicy/app/ProfileOwner/src/com/android/cts/profileowner/AdminActionBookkeepingTest.java
+++ b/hostsidetests/devicepolicy/app/ProfileOwner/src/com/android/cts/profileowner/AdminActionBookkeepingTest.java
@@ -22,26 +22,99 @@
 import android.os.UserHandle;
 import android.provider.Settings;
 
+import com.android.org.conscrypt.TrustedCertificateStore;
+
+import java.io.ByteArrayInputStream;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateFactory;
+import java.util.List;
+
 public class AdminActionBookkeepingTest extends BaseProfileOwnerTest {
+    /*
+     * The CA cert below is the content of cacert.pem as generated by:
+     *
+     * openssl req -new -x509 -days 3650 -extensions v3_ca -keyout cakey.pem -out cacert.pem
+     */
+    private static final String TEST_CA =
+            "-----BEGIN CERTIFICATE-----\n" +
+            "MIIDXTCCAkWgAwIBAgIJAK9Tl/F9V8kSMA0GCSqGSIb3DQEBCwUAMEUxCzAJBgNV\n" +
+            "BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX\n" +
+            "aWRnaXRzIFB0eSBMdGQwHhcNMTUwMzA2MTczMjExWhcNMjUwMzAzMTczMjExWjBF\n" +
+            "MQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50\n" +
+            "ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB\n" +
+            "CgKCAQEAvItOutsE75WBTgTyNAHt4JXQ3JoseaGqcC3WQij6vhrleWi5KJ0jh1/M\n" +
+            "Rpry7Fajtwwb4t8VZa0NuM2h2YALv52w1xivql88zce/HU1y7XzbXhxis9o6SCI+\n" +
+            "oVQSbPeXRgBPppFzBEh3ZqYTVhAqw451XhwdA4Aqs3wts7ddjwlUzyMdU44osCUg\n" +
+            "kVg7lfPf9sTm5IoHVcfLSCWH5n6Nr9sH3o2ksyTwxuOAvsN11F/a0mmUoPciYPp+\n" +
+            "q7DzQzdi7akRG601DZ4YVOwo6UITGvDyuAAdxl5isovUXqe6Jmz2/myTSpAKxGFs\n" +
+            "jk9oRoG6WXWB1kni490GIPjJ1OceyQIDAQABo1AwTjAdBgNVHQ4EFgQUH1QIlPKL\n" +
+            "p2OQ/AoLOjKvBW4zK3AwHwYDVR0jBBgwFoAUH1QIlPKLp2OQ/AoLOjKvBW4zK3Aw\n" +
+            "DAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAcMi4voMMJHeQLjtq8Oky\n" +
+            "Azpyk8moDwgCd4llcGj7izOkIIFqq/lyqKdtykVKUWz2bSHO5cLrtaOCiBWVlaCV\n" +
+            "DYAnnVLM8aqaA6hJDIfaGs4zmwz0dY8hVMFCuCBiLWuPfiYtbEmjHGSmpQTG6Qxn\n" +
+            "ZJlaK5CZyt5pgh5EdNdvQmDEbKGmu0wpCq9qjZImwdyAul1t/B0DrsWApZMgZpeI\n" +
+            "d2od0VBrCICB1K4p+C51D93xyQiva7xQcCne+TAnGNy9+gjQ/MyR8MRpwRLv5ikD\n" +
+            "u0anJCN8pXo6IMglfMAsoton1J6o5/ae5uhC6caQU8bNUsCK570gpNfjkzo6rbP0\n" +
+            "wQ==\n" +
+            "-----END CERTIFICATE-----";
+
+    @Override
+    protected void tearDown() throws Exception {
+        mDevicePolicyManager.uninstallCaCert(getWho(), TEST_CA.getBytes());
+
+        super.tearDown();
+    }
+
     /**
-     * Test: It should be recored whether the Profile Owner or the user set the default IME.
+     * Test: It should be recored whether the Profile Owner or the user set the current IME.
      */
     public void testIsDefaultInputMethodSet() throws Exception {
-        final UserHandle userHandle = Process.myUserHandle();
         final String setting = Settings.Secure.DEFAULT_INPUT_METHOD;
         final ContentResolver resolver = getContext().getContentResolver();
         final String ime = Settings.Secure.getString(resolver, setting);
 
         Settings.Secure.putString(resolver, setting, "com.test.1");
         Thread.sleep(500);
-        assertFalse(mDevicePolicyManager.isDefaultInputMethodSetByOwner(userHandle));
+        assertFalse(mDevicePolicyManager.isCurrentInputMethodSetByOwner());
 
         mDevicePolicyManager.setSecureSetting(getWho(), setting, "com.test.2");
         Thread.sleep(500);
-        assertTrue(mDevicePolicyManager.isDefaultInputMethodSetByOwner(userHandle));
+        assertTrue(mDevicePolicyManager.isCurrentInputMethodSetByOwner());
 
         Settings.Secure.putString(resolver, setting, ime);
         Thread.sleep(500);
-        assertFalse(mDevicePolicyManager.isDefaultInputMethodSetByOwner(userHandle));
+        assertFalse(mDevicePolicyManager.isCurrentInputMethodSetByOwner());
+    }
+
+    /**
+     * Test: It should be recored whether the Profile Owner or the user installed a CA cert.
+     */
+    public void testGetPolicyInstalledCaCerts() throws Exception {
+        final byte[] rawCert = TEST_CA.getBytes();
+        final Certificate cert = CertificateFactory.getInstance("X.509")
+                .generateCertificate(new ByteArrayInputStream(rawCert));
+        final TrustedCertificateStore store = new TrustedCertificateStore();
+
+        // Install a CA cert.
+        assertNull(store.getCertificateAlias(cert));
+        assertTrue(mDevicePolicyManager.installCaCert(getWho(), rawCert));
+        final String alias = store.getCertificateAlias(cert);
+        assertNotNull(alias);
+
+        // Verify that the CA cert was marked as installed by the Profile Owner.
+        verifyOwnerInstalledStatus(alias, true);
+
+        // Uninstall the CA cert.
+        mDevicePolicyManager.uninstallCaCert(getWho(), rawCert);
+
+        // Verify that the CA cert is no longer marked as installed by the Profile Owner.
+        verifyOwnerInstalledStatus(alias, false);
+    }
+
+    private void verifyOwnerInstalledStatus(String alias, boolean expectOwnerInstalled) {
+        final List<String> ownerInstalledCerts =
+                mDevicePolicyManager.getOwnerInstalledCaCerts(Process.myUserHandle());
+        assertNotNull(ownerInstalledCerts);
+        assertEquals(expectOwnerInstalled, ownerInstalledCerts.contains(alias));
     }
 }
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/BaseDevicePolicyTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/BaseDevicePolicyTest.java
index f7ba94f..6a68a59 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/BaseDevicePolicyTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/BaseDevicePolicyTest.java
@@ -58,6 +58,22 @@
     private static final long TIMEOUT_USER_REMOVED_MILLIS = TimeUnit.SECONDS.toMillis(15);
     private static final long WAIT_SAMPLE_INTERVAL_MILLIS = 200;
 
+    /**
+     * The defined timeout (in milliseconds) is used as a maximum waiting time when expecting the
+     * command output from the device. At any time, if the shell command does not output anything
+     * for a period longer than defined timeout the Tradefed run terminates.
+     */
+    private static final long DEFAULT_SHELL_TIMEOUT_MILLIS = TimeUnit.MINUTES.toMillis(20);
+
+    /** instrumentation test runner argument key used for individual test timeout */
+    protected static final String TEST_TIMEOUT_INST_ARGS_KEY = "timeout_msec";
+
+    /**
+     * Sets timeout (in milliseconds) that will be applied to each test. In the
+     * event of a test timeout it will log the results and proceed with executing the next test.
+     */
+    private static final long DEFAULT_TEST_TIMEOUT_MILLIS = TimeUnit.MINUTES.toMillis(10);
+
     // From the UserInfo class
     protected static final int FLAG_PRIMARY = 0x00000001;
     protected static final int FLAG_GUEST = 0x00000004;
@@ -283,6 +299,9 @@
 
         RemoteAndroidTestRunner testRunner = new RemoteAndroidTestRunner(
                 pkgName, RUNNER, getDevice().getIDevice());
+        testRunner.setMaxTimeToOutputResponse(DEFAULT_SHELL_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
+        testRunner.addInstrumentationArg(
+                TEST_TIMEOUT_INST_ARGS_KEY, Long.toString(DEFAULT_TEST_TIMEOUT_MILLIS));
         if (testClassName != null && testMethodName != null) {
             testRunner.setMethodName(testClassName, testMethodName);
         } else if (testClassName != null) {
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileTest.java
index 614343b..ba36fbf 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileTest.java
@@ -398,6 +398,9 @@
 
     /** Tests for the API helper class. */
     public void testCurrentApiHelper() throws Exception {
+        if (!mHasFeature) {
+            return;
+        }
         runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CurrentApiHelperTest",
                 mProfileUserId);
     }
diff --git a/hostsidetests/dumpsys/src/android/dumpsys/cts/BatteryStatsDumpsysTest.java b/hostsidetests/dumpsys/src/android/dumpsys/cts/BatteryStatsDumpsysTest.java
index f69ceab..efafca2 100644
--- a/hostsidetests/dumpsys/src/android/dumpsys/cts/BatteryStatsDumpsysTest.java
+++ b/hostsidetests/dumpsys/src/android/dumpsys/cts/BatteryStatsDumpsysTest.java
@@ -325,7 +325,7 @@
     }
 
     private void checkNetwork(String[] parts) {
-        assertEquals(18, parts.length);
+        assertEquals(26, parts.length);
         long mbRx = assertInteger(parts[4]);  // mobileBytesRx
         long mbTx = assertInteger(parts[5]);  // mobileBytesTx
         long wbRx = assertInteger(parts[6]);  // wifiBytesRx
@@ -340,6 +340,14 @@
         assertInteger(parts[15]); // btBytesTx
         assertInteger(parts[16]); // mobileWakeup
         assertInteger(parts[17]); // wifiWakeup
+        long mbBgRx = assertInteger(parts[18]);  // mobileBytesRx
+        long mbBgTx = assertInteger(parts[19]);  // mobileBytesTx
+        long wbBgRx = assertInteger(parts[20]);  // wifiBytesRx
+        long wbBgTx = assertInteger(parts[21]);  // wifiBytesTx
+        long mpBgRx = assertInteger(parts[22]);  // mobilePacketsRx
+        long mpBgTx = assertInteger(parts[23]);  // mobilePacketsTx
+        long wpBgRx = assertInteger(parts[24]); // wifiPacketsRx
+        long wpBgTx = assertInteger(parts[25]); // wifiPacketsTx
 
         // Assuming each packet contains some bytes, bytes >= packets >= 0.
         assertTrue("mobileBytesRx must be >= mobilePacketsRx", mbRx >= mpRx);
@@ -350,6 +358,15 @@
         assertTrue("wifiPacketsRx must be >= 0", wpRx >= 0);
         assertTrue("wifiBytesTx must be >= wifiPacketsTx", wbTx >= wpTx);
         assertTrue("wifiPacketsTx must be >= 0", wpTx >= 0);
+        // Totals should be greater than or equal to background data numbers
+        assertTrue("mobileBytesRx must be >= mobileBytesBgRx", mbRx >= mbBgRx);
+        assertTrue("mobilePacketsRx must be >= mobilePacketsBgRx", mpRx >= mpBgRx);
+        assertTrue("mobileBytesTx must be >= mobileBytesBgTx", mbTx >= mbBgTx);
+        assertTrue("mobilePacketsTx must be >= mobilePacketsBgTx", mpTx >= mpBgTx);
+        assertTrue("wifiBytesRx must be >= wifiBytesBgRx", wbRx >= wbBgRx);
+        assertTrue("wifiPacketsRx must be >= wifiPacketsBgRx", wpRx >= wpBgRx);
+        assertTrue("wifiBytesTx must be >= wifiBytesBgTx", wbTx >= wbBgTx);
+        assertTrue("wifiPacketsTx must be >= wifiPacketsBgTx", wpTx >= wpBgTx);
     }
 
     private void checkUserActivity(String[] parts) {
diff --git a/hostsidetests/incident/apps/batterystatsapp/AndroidManifest.xml b/hostsidetests/incident/apps/batterystatsapp/AndroidManifest.xml
index f52e4ce..41370d8 100644
--- a/hostsidetests/incident/apps/batterystatsapp/AndroidManifest.xml
+++ b/hostsidetests/incident/apps/batterystatsapp/AndroidManifest.xml
@@ -20,8 +20,10 @@
     <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
     <uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/>
     <uses-permission android:name="android.permission.WAKE_LOCK" />
+    <uses-permission android:name="android.permission.READ_SYNC_STATS" />
+    <uses-permission android:name="android.permission.WRITE_SYNC_SETTINGS" />
 
-    <application>
+    <application android:label="@string/app_name">
         <uses-library android:name="android.test.runner" />
 
         <service android:name=".SimpleForegroundService" android:exported="true" />
@@ -32,6 +34,28 @@
         <activity android:name=".SimpleActivity" android:label="BatteryStats Test Activity" />
 
         <activity android:name=".BatteryStatsWifiScanTests$WiFiScanActivity" android:label="BatteryStats Wifi scan Test Activity" />
+
+        <service android:name=".BatteryStatsAuthenticator"
+            android:exported="false">
+            <intent-filter>
+                <action android:name="android.accounts.AccountAuthenticator" />
+            </intent-filter>
+
+            <meta-data android:name="android.accounts.AccountAuthenticator"
+                android:resource="@xml/authenticator" />
+        </service>
+        <service android:name=".BatteryStatsSyncService"
+            android:exported="false" >
+            <intent-filter>
+                <action android:name="android.content.SyncAdapter" />
+            </intent-filter>
+            <meta-data android:name="android.content.SyncAdapter"
+                android:resource="@xml/syncadapter" />
+        </service>
+
+        <provider android:name=".BatteryStatsProvider"
+            android:authorities="com.android.server.cts.device.batterystats.provider" />
+
     </application>
 
     <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
diff --git a/hostsidetests/incident/apps/batterystatsapp/res/values/strings.xml b/hostsidetests/incident/apps/batterystatsapp/res/values/strings.xml
new file mode 100644
index 0000000..20783ed
--- /dev/null
+++ b/hostsidetests/incident/apps/batterystatsapp/res/values/strings.xml
@@ -0,0 +1,20 @@
+<?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.
+-->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_name">BatteryStats CTS</string>
+</resources>
diff --git a/hostsidetests/incident/apps/batterystatsapp/res/xml/authenticator.xml b/hostsidetests/incident/apps/batterystatsapp/res/xml/authenticator.xml
new file mode 100644
index 0000000..3c5a04c
--- /dev/null
+++ b/hostsidetests/incident/apps/batterystatsapp/res/xml/authenticator.xml
@@ -0,0 +1,19 @@
+<?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.
+-->
+
+<account-authenticator xmlns:android="http://schemas.android.com/apk/res/android"
+    android:accountType="com.android.server.cts.device.batterystats"
+    android:label="@string/app_name" />
diff --git a/hostsidetests/incident/apps/batterystatsapp/res/xml/syncadapter.xml b/hostsidetests/incident/apps/batterystatsapp/res/xml/syncadapter.xml
new file mode 100644
index 0000000..fca349c
--- /dev/null
+++ b/hostsidetests/incident/apps/batterystatsapp/res/xml/syncadapter.xml
@@ -0,0 +1,20 @@
+<?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.
+-->
+
+<sync-adapter xmlns:android="http://schemas.android.com/apk/res/android"
+    android:contentAuthority="com.android.server.cts.device.batterystats.provider"
+    android:accountType="com.android.server.cts.device.batterystats"
+/>
diff --git a/hostsidetests/incident/apps/batterystatsapp/src/com/android/server/cts/device/batterystats/BatteryStatsAlarmTest.java b/hostsidetests/incident/apps/batterystatsapp/src/com/android/server/cts/device/batterystats/BatteryStatsAlarmTest.java
new file mode 100644
index 0000000..c49a824
--- /dev/null
+++ b/hostsidetests/incident/apps/batterystatsapp/src/com/android/server/cts/device/batterystats/BatteryStatsAlarmTest.java
@@ -0,0 +1,70 @@
+/*
+ * 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.server.cts.device.batterystats;
+
+import static org.junit.Assert.assertTrue;
+
+import android.app.AlarmManager;
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.SystemClock;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
+import android.util.Log;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+@RunWith(AndroidJUnit4.class)
+public class BatteryStatsAlarmTest {
+    private static final String TAG = "BatteryStatsAlarmTest";
+
+    /**
+     * Set and fire a wakeup alarm 3 times.
+     */
+    @Test
+    public void testAlarms() throws Exception {
+        final int NUM_ALARMS = 3;
+
+        final Context context = InstrumentationRegistry.getContext();
+
+        final Intent intent = new Intent("com.android.server.cts.device.batterystats.ALARM");
+        final IntentFilter inf = new IntentFilter(intent.getAction());
+
+        final CountDownLatch latch = new CountDownLatch(NUM_ALARMS);
+
+        context.registerReceiver(new BroadcastReceiver() {
+            @Override
+            public void onReceive(Context context, Intent intent) {
+                Log.i(TAG, "Received: " + intent);
+                latch.countDown();
+            }}, inf);
+
+        final AlarmManager alm = context.getSystemService(AlarmManager.class);
+        for (int i = 0; i < NUM_ALARMS; i++) {
+            alm.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP,
+                    SystemClock.elapsedRealtime() + (i + 1) * 1000,
+                    PendingIntent.getBroadcast(context, i, intent, 0));
+        }
+        assertTrue("Didn't receive all broadcasts.", latch.await(60 * 1000, TimeUnit.SECONDS));
+    }
+}
diff --git a/hostsidetests/incident/apps/batterystatsapp/src/com/android/server/cts/device/batterystats/BatteryStatsAuthenticator.java b/hostsidetests/incident/apps/batterystatsapp/src/com/android/server/cts/device/batterystats/BatteryStatsAuthenticator.java
new file mode 100644
index 0000000..bfc5740
--- /dev/null
+++ b/hostsidetests/incident/apps/batterystatsapp/src/com/android/server/cts/device/batterystats/BatteryStatsAuthenticator.java
@@ -0,0 +1,136 @@
+/*
+ * 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.server.cts.device.batterystats;
+
+import android.accounts.AbstractAccountAuthenticator;
+import android.accounts.Account;
+import android.accounts.AccountAuthenticatorResponse;
+import android.accounts.AccountManager;
+import android.accounts.NetworkErrorException;
+import android.app.Service;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.util.Log;
+
+import java.util.Arrays;
+
+/**
+ * Authenticator for the sync test.
+ */
+public class BatteryStatsAuthenticator extends Service {
+    private static final String TAG = "TestAuthenticator";
+
+    private static final String ACCOUNT_NAME = "BatteryStatsCts";
+    private static final String ACCOUNT_TYPE = "com.android.server.cts.device.batterystats";
+
+    private static Authenticator sInstance;
+
+    @Override
+    public IBinder onBind(Intent intent) {
+        if (sInstance == null) {
+            sInstance = new Authenticator(getApplicationContext());
+
+        }
+        return sInstance.getIBinder();
+    }
+
+    public static Account getTestAccount() {
+        return new Account(ACCOUNT_NAME, ACCOUNT_TYPE);
+    }
+
+    /**
+     * Adds the test account, if it doesn't exist yet.
+     */
+    public static void ensureTestAccount(Context context) {
+        final Account account = getTestAccount();
+
+        Bundle result = new Bundle();
+        result.putString(AccountManager.KEY_ACCOUNT_TYPE, account.type);
+        result.putString(AccountManager.KEY_ACCOUNT_NAME, account.name);
+
+        final AccountManager am = context.getSystemService(AccountManager.class);
+
+        if (!Arrays.asList(am.getAccountsByType(account.type)).contains(account) ){
+            am.addAccountExplicitly(account, "password", new Bundle());
+        }
+    }
+
+    /**
+     * Remove the test account.
+     */
+    public static void removeAllAccounts(Context context) {
+        final AccountManager am = context.getSystemService(AccountManager.class);
+
+        for (Account account : am.getAccountsByType(BatteryStatsAuthenticator.ACCOUNT_TYPE)) {
+            Log.i(TAG, "Removing " + account + "...");
+            am.removeAccountExplicitly(account);
+            Log.i(TAG, "Removed");
+        }
+    }
+
+    public static class Authenticator extends AbstractAccountAuthenticator {
+
+        private final Context mContxet;
+
+        public Authenticator(Context context) {
+            super(context);
+            mContxet = context;
+        }
+
+        @Override
+        public Bundle addAccount(AccountAuthenticatorResponse response, String accountType,
+                String authTokenType, String[] requiredFeatures, Bundle options)
+                throws NetworkErrorException {
+            return new Bundle();
+        }
+
+        @Override
+        public Bundle editProperties(AccountAuthenticatorResponse response, String accountType) {
+            return new Bundle();
+        }
+
+        @Override
+        public Bundle updateCredentials(AccountAuthenticatorResponse response, Account account,
+                String authTokenType, Bundle options) throws NetworkErrorException {
+            return new Bundle();
+        }
+
+        @Override
+        public Bundle confirmCredentials(AccountAuthenticatorResponse response, Account account,
+                Bundle options) throws NetworkErrorException {
+            return new Bundle();
+        }
+
+        @Override
+        public Bundle getAuthToken(AccountAuthenticatorResponse response, Account account,
+                String authTokenType, Bundle options) throws NetworkErrorException {
+            return new Bundle();
+        }
+
+        @Override
+        public String getAuthTokenLabel(String authTokenType) {
+            return "token_label";
+        }
+
+        @Override
+        public Bundle hasFeatures(AccountAuthenticatorResponse response, Account account,
+                String[] features) throws NetworkErrorException {
+            return new Bundle();
+        }
+    }
+}
diff --git a/hostsidetests/incident/apps/batterystatsapp/src/com/android/server/cts/device/batterystats/BatteryStatsProvider.java b/hostsidetests/incident/apps/batterystatsapp/src/com/android/server/cts/device/batterystats/BatteryStatsProvider.java
new file mode 100644
index 0000000..c9dcedb
--- /dev/null
+++ b/hostsidetests/incident/apps/batterystatsapp/src/com/android/server/cts/device/batterystats/BatteryStatsProvider.java
@@ -0,0 +1,59 @@
+/*
+ * 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.server.cts.device.batterystats;
+
+import android.content.ContentProvider;
+import android.content.ContentValues;
+import android.database.Cursor;
+import android.net.Uri;
+
+/**
+ * Provider for the sync test.
+ */
+public class BatteryStatsProvider extends ContentProvider {
+    public static final String AUTHORITY = "com.android.server.cts.device.batterystats.provider";
+
+    @Override
+    public boolean onCreate() {
+        return false;
+    }
+
+    @Override
+    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
+            String sortOrder) {
+        return null;
+    }
+
+    @Override
+    public String getType(Uri uri) {
+        return null;
+    }
+
+    @Override
+    public Uri insert(Uri uri, ContentValues values) {
+        return null;
+    }
+
+    @Override
+    public int delete(Uri uri, String selection, String[] selectionArgs) {
+        return 0;
+    }
+
+    @Override
+    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
+        return 0;
+    }
+}
diff --git a/hostsidetests/incident/apps/batterystatsapp/src/com/android/server/cts/device/batterystats/BatteryStatsSyncAdapter.java b/hostsidetests/incident/apps/batterystatsapp/src/com/android/server/cts/device/batterystats/BatteryStatsSyncAdapter.java
new file mode 100644
index 0000000..ec1522b
--- /dev/null
+++ b/hostsidetests/incident/apps/batterystatsapp/src/com/android/server/cts/device/batterystats/BatteryStatsSyncAdapter.java
@@ -0,0 +1,135 @@
+/*
+ * 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.server.cts.device.batterystats;
+
+import android.accounts.Account;
+import android.content.AbstractThreadedSyncAdapter;
+import android.content.ContentProviderClient;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.SyncResult;
+import android.os.Bundle;
+import android.os.SystemClock;
+import android.util.Log;
+
+import org.junit.Assert;
+
+import javax.annotation.concurrent.GuardedBy;
+
+/**
+ * Sync adapter for the sync test.
+ */
+public class BatteryStatsSyncAdapter extends AbstractThreadedSyncAdapter {
+    private static final String TAG = "BatteryStatsSyncAdapter";
+
+    private static final int TIMEOUT_SECONDS = 60 * 2;
+
+    private static final Object sLock = new Object();
+
+    /**
+     * # of total syncs happened; used to wait until a request sync finishes.
+     */
+    @GuardedBy("sLock")
+    private static int sSyncCount;
+
+    public BatteryStatsSyncAdapter(Context context) {
+        // No need for auto-initialization because we set isSyncable in the test anyway.
+        super(context, /* autoInitialize= */ false);
+    }
+
+    @Override
+    public void onPerformSync(Account account, Bundle extras, String authority,
+            ContentProviderClient provider, SyncResult syncResult) {
+        try {
+            Thread.sleep(1000);
+        } catch (InterruptedException e) {
+        }
+        synchronized (sLock) {
+            sSyncCount++;
+            Log.i(TAG, "onPerformSync: count -> " + sSyncCount);
+            sLock.notifyAll();
+        }
+    }
+
+    /**
+     * Returns the current sync count.
+     */
+    private static int getSyncCount() {
+        synchronized (sLock) {
+            return sSyncCount;
+        }
+    }
+
+    /**
+     * Wait until the sync count reaches the given value.
+     */
+    private static void waitUntilSyncCount(int expectCount) throws Exception {
+        final long timeout = SystemClock.elapsedRealtime() + (TIMEOUT_SECONDS * 1000);
+
+        synchronized (sLock) {
+            for (;;) {
+                Log.i(TAG, "waitUntilSyncCount: current count=" + sSyncCount);
+                if (sSyncCount >= expectCount) {
+                    return;
+                }
+                final long sleep = timeout - SystemClock.elapsedRealtime();
+                if (sleep <= 0) {
+                    break;
+                }
+                sLock.wait(sleep);
+            }
+        }
+        Assert.fail("Sync didn't happen.");
+    }
+
+    /**
+     * Request a sync on the given account, and wait for it.
+     */
+    public static void requestSync(Account account) throws Exception {
+        final int startCount = BatteryStatsSyncAdapter.getSyncCount();
+
+        final Bundle extras = new Bundle();
+        extras.putBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, true);
+        extras.putBoolean(ContentResolver.SYNC_EXTRAS_IGNORE_BACKOFF, true);
+        extras.putBoolean(ContentResolver.SYNC_EXTRAS_IGNORE_SETTINGS, true);
+
+        ContentResolver.requestSync(account, BatteryStatsProvider.AUTHORITY, extras);
+
+        waitUntilSyncCount(startCount + 1);
+    }
+
+    /**
+     * Cancel all pending sync requests on the given account.
+     */
+    public static void cancelPendingSyncs(Account account) throws Exception {
+        final long timeout = SystemClock.elapsedRealtime() + (TIMEOUT_SECONDS * 1000);
+
+        ContentResolver.cancelSync(account, BatteryStatsProvider.AUTHORITY);
+
+        for (;;) {
+            if (!ContentResolver.isSyncPending(account, BatteryStatsProvider.AUTHORITY)
+                && !ContentResolver.isSyncActive(account, BatteryStatsProvider.AUTHORITY)) {
+                return;
+            }
+            final long sleep = timeout - SystemClock.elapsedRealtime();
+            if (sleep <= 0) {
+                break;
+            }
+            Thread.sleep(sleep);
+        }
+        Assert.fail("Couldn't cancel pending sync.");
+    }
+}
diff --git a/hostsidetests/incident/apps/batterystatsapp/src/com/android/server/cts/device/batterystats/BatteryStatsSyncService.java b/hostsidetests/incident/apps/batterystatsapp/src/com/android/server/cts/device/batterystats/BatteryStatsSyncService.java
new file mode 100644
index 0000000..d657c1a
--- /dev/null
+++ b/hostsidetests/incident/apps/batterystatsapp/src/com/android/server/cts/device/batterystats/BatteryStatsSyncService.java
@@ -0,0 +1,36 @@
+/*
+ * 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.server.cts.device.batterystats;
+
+import android.app.Service;
+import android.content.Intent;
+import android.os.IBinder;
+
+/**
+ * Service for the sync test.
+ */
+public class BatteryStatsSyncService extends Service {
+
+    private static BatteryStatsSyncAdapter sAdapter;
+
+    @Override
+    public synchronized IBinder onBind(Intent intent) {
+        if (sAdapter == null) {
+            sAdapter = new BatteryStatsSyncAdapter(getApplicationContext());
+        }
+        return sAdapter.getSyncAdapterBinder();
+    }
+}
diff --git a/hostsidetests/incident/apps/batterystatsapp/src/com/android/server/cts/device/batterystats/BatteryStatsSyncTest.java b/hostsidetests/incident/apps/batterystatsapp/src/com/android/server/cts/device/batterystats/BatteryStatsSyncTest.java
new file mode 100644
index 0000000..daa6f5e
--- /dev/null
+++ b/hostsidetests/incident/apps/batterystatsapp/src/com/android/server/cts/device/batterystats/BatteryStatsSyncTest.java
@@ -0,0 +1,72 @@
+/*
+ * 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.server.cts.device.batterystats;
+
+import android.accounts.Account;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class BatteryStatsSyncTest {
+    private static final String TAG = "BatteryStatsSyncTest";
+
+    private static Context getContext() {
+        return InstrumentationRegistry.getContext();
+    }
+
+    @Before
+    public void setUp() {
+        BatteryStatsAuthenticator.removeAllAccounts(getContext());
+    }
+
+    @After
+    public void tearDown() {
+        BatteryStatsAuthenticator.removeAllAccounts(getContext());
+    }
+
+    /**
+     * Run a sync N times and make sure it shows up in the battery stats.
+     */
+    @Test
+    public void testRunSyncs() throws Exception {
+        final Account account = BatteryStatsAuthenticator.getTestAccount();
+
+        // Create the test account.
+        BatteryStatsAuthenticator.ensureTestAccount(getContext());
+
+        // Just force set is syncable.
+        ContentResolver.setMasterSyncAutomatically(true);
+        ContentResolver.setIsSyncable(account, BatteryStatsProvider.AUTHORITY, 1);
+
+        // Cancel the initial sync.
+        BatteryStatsSyncAdapter.cancelPendingSyncs(account);
+
+        final int NUM_SYNC = 10;
+
+        // Run syncs.
+        for (int i = 0; i < NUM_SYNC; i++) {
+            BatteryStatsSyncAdapter.requestSync(account);
+            Thread.sleep(1000);
+        }
+    }
+}
diff --git a/hostsidetests/incident/src/com/android/server/cts/BatteryStatsValidationTest.java b/hostsidetests/incident/src/com/android/server/cts/BatteryStatsValidationTest.java
index 287dd53..34d7215 100644
--- a/hostsidetests/incident/src/com/android/server/cts/BatteryStatsValidationTest.java
+++ b/hostsidetests/incident/src/com/android/server/cts/BatteryStatsValidationTest.java
@@ -31,6 +31,17 @@
             = "com.android.server.cts.device.batterystats";
     private static final String DEVICE_SIDE_JOB_COMPONENT
             = "com.android.server.cts.device.batterystats/.SimpleJobService";
+    private static final String DEVICE_SIDE_SYNC_COMPONENT
+            = "com.android.server.cts.device.batterystats.provider/"
+            + "com.android.server.cts.device.batterystats";
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+
+        // Uninstall to clear the history in case it's still on the device.
+        getDevice().uninstallPackage(DEVICE_SIDE_TEST_PACKAGE);
+    }
 
     @Override
     protected void tearDown() throws Exception {
@@ -50,6 +61,19 @@
         getDevice().executeShellCommand("dumpsys batterystats disable pretend-screen-off");
     }
 
+    public void testAlarms() throws Exception {
+        batteryOnScreenOff();
+
+        installPackage(DEVICE_SIDE_TEST_APK, /* grantPermissions= */ true);
+
+        runDeviceTests(DEVICE_SIDE_TEST_PACKAGE, ".BatteryStatsAlarmTest", "testAlarms");
+
+        assertValueRange("wua", "*walarm*:com.android.server.cts.device.batterystats.ALARM",
+                5, 3, 3);
+
+        batteryOffScreenOn();
+    }
+
     public void testWakeLockDuration() throws Exception {
         batteryOnScreenOff();
 
@@ -138,6 +162,26 @@
     }
 
     /**
+     * Tests the total duration and # of syncs reported for sync activities.
+     */
+    public void testSyncs() throws Exception {
+        batteryOnScreenOff();
+
+        installPackage(DEVICE_SIDE_TEST_APK, true);
+
+        runDeviceTests(DEVICE_SIDE_TEST_PACKAGE, ".BatteryStatsSyncTest", "testRunSyncs");
+
+        // First, check the count, which should be 10.
+        // (It could be 11, if the initial sync actually happened before getting cancelled.)
+        assertValueRange("sy", DEVICE_SIDE_SYNC_COMPONENT, 6, 10L, 11L);
+
+        // Should be approximately, but at least 10 seconds. Use 2x as the upper
+        // bounds to account for possible errors due to thread scheduling and cpu load.
+        assertValueRange("sy", DEVICE_SIDE_SYNC_COMPONENT, 5, 10000, 10000 * 2);
+        batteryOffScreenOn();
+    }
+
+    /**
      * Verifies that the recorded time for the specified tag and name in the test package
      * is within the specified range.
      */
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/PipActivity.java b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/PipActivity.java
index defcf4e..ff547a0 100644
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/PipActivity.java
+++ b/hostsidetests/services/activityandwindowmanager/activitymanager/app/src/android/server/cts/PipActivity.java
@@ -42,6 +42,8 @@
 
     // Intent action that this activity dynamically registers to enter picture-in-picture
     private static final String ACTION_ENTER_PIP = "android.server.cts.PipActivity.enter_pip";
+    // Intent action that this activity dynamically registers to move itself to the back
+    private static final String ACTION_MOVE_TO_BACK = "android.server.cts.PipActivity.move_to_back";
 
     // Sets the fixed orientation (can be one of {@link ActivityInfo.ScreenOrientation}
     private static final String EXTRA_FIXED_ORIENTATION = "fixed_orientation";
@@ -75,8 +77,15 @@
     private BroadcastReceiver mReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
-            if (intent != null && intent.getAction().equals(ACTION_ENTER_PIP)) {
-                enterPictureInPictureMode();
+            if (intent != null) {
+                switch (intent.getAction()) {
+                    case ACTION_ENTER_PIP:
+                        enterPictureInPictureMode();
+                        break;
+                    case ACTION_MOVE_TO_BACK:
+                        moveTaskToBack(false /* nonRoot */);
+                        break;
+                }
             }
         }
     };
@@ -141,9 +150,18 @@
     }
 
     @Override
+    protected void onStart() {
+        super.onStart();
+
+        IntentFilter filter = new IntentFilter();
+        filter.addAction(ACTION_ENTER_PIP);
+        filter.addAction(ACTION_MOVE_TO_BACK);
+        registerReceiver(mReceiver, filter);
+    }
+
+    @Override
     protected void onResume() {
         super.onResume();
-        registerReceiver(mReceiver, new IntentFilter(ACTION_ENTER_PIP));
 
         // Finish self if requested
         if (getIntent().hasExtra(EXTRA_FINISH_SELF_ON_RESUME)) {
@@ -154,7 +172,6 @@
     @Override
     protected void onPause() {
         super.onPause();
-        unregisterReceiver(mReceiver);
 
         // Pause if requested
         if (getIntent().hasExtra(EXTRA_ON_PAUSE_DELAY)) {
@@ -170,6 +187,8 @@
     @Override
     protected void onStop() {
         super.onStop();
+        unregisterReceiver(mReceiver);
+
         if (getIntent().hasExtra(EXTRA_ASSERT_NO_ON_STOP_BEFORE_PIP) && !mEnteredPictureInPicture) {
             Log.w("PipActivity", "Unexpected onStop() called before entering picture-in-picture");
             finish();
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerActivityVisibilityTests.java b/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerActivityVisibilityTests.java
index 07d4a06..697143f 100644
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerActivityVisibilityTests.java
+++ b/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerActivityVisibilityTests.java
@@ -225,11 +225,12 @@
 
         // Press back
         pressBackButton();
-        mAmWmState.waitForValidState(mDevice, LAUNCHING_ACTIVITY);
+
+        mAmWmState.waitForValidState(mDevice, ALT_LAUNCHING_ACTIVITY);
 
         // Ensure the alternate launching activity is in focus
         mAmWmState.assertFocusedActivity("Alt Launching Activity must be focused",
-                LAUNCHING_ACTIVITY);
+                ALT_LAUNCHING_ACTIVITY);
     }
 
     /**
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerPinnedStackTests.java b/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerPinnedStackTests.java
index 1ad1251..be07e26 100644
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerPinnedStackTests.java
+++ b/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerPinnedStackTests.java
@@ -59,6 +59,8 @@
 
     private static final String PIP_ACTIVITY_ACTION_ENTER_PIP =
             "android.server.cts.PipActivity.enter_pip";
+    private static final String PIP_ACTIVITY_ACTION_MOVE_TO_BACK =
+            "android.server.cts.PipActivity.move_to_back";
 
     private static final int APP_OPS_OP_ENTER_PICTURE_IN_PICTURE_ON_HIDE = 67;
     private static final int APP_OPS_MODE_ALLOWED = 0;
@@ -483,13 +485,8 @@
         // Remove the stack and ensure that the task is now in the fullscreen stack (when no
         // fullscreen stack existed before)
         removeStacks(PINNED_STACK_ID);
-        mAmWmState.waitForFocusedStack(mDevice, HOME_STACK_ID);
-        mAmWmState.assertFocusedStack("Expect home stack focused", HOME_STACK_ID);
-        mAmWmState.waitForActivityState(mDevice, PIP_ACTIVITY, STATE_STOPPED);
-        assertTrue(mAmWmState.getAmState().hasActivityState(PIP_ACTIVITY, STATE_STOPPED));
-        assertPinnedStackDoesNotExist();
-        assertTrue(mAmWmState.getAmState().getTaskByActivityName(PIP_ACTIVITY,
-                FULLSCREEN_WORKSPACE_STACK_ID) != null);
+        assertPinnedStackStateOnMoveToFullscreen(PIP_ACTIVITY, HOME_STACK_ID,
+                true /* expectTopTaskHasActivity */, true /* expectBottomTaskHasActivity */);
     }
 
     public void testRemovePipWithVisibleFullscreenStack() throws Exception {
@@ -503,17 +500,8 @@
         // Remove the stack and ensure that the task is placed in the fullscreen stack, behind the
         // top fullscreen activity
         removeStacks(PINNED_STACK_ID);
-        mAmWmState.waitForFocusedStack(mDevice, FULLSCREEN_WORKSPACE_STACK_ID);
-        mAmWmState.assertFocusedStack("Expect fullscreen stack focused",
-                FULLSCREEN_WORKSPACE_STACK_ID);
-        mAmWmState.waitForActivityState(mDevice, PIP_ACTIVITY, STATE_STOPPED);
-        assertTrue(mAmWmState.getAmState().hasActivityState(PIP_ACTIVITY, STATE_STOPPED));
-        assertPinnedStackDoesNotExist();
-        ActivityTask bottomTask = mAmWmState.getAmState().getStackById(
-                FULLSCREEN_WORKSPACE_STACK_ID).getBottomTask();
-        Activity bottomActivity = bottomTask.mActivities.get(0);
-        assertTrue(bottomActivity.name.equals(ActivityManagerTestBase.getActivityComponentName(
-                PIP_ACTIVITY)));
+        assertPinnedStackStateOnMoveToFullscreen(PIP_ACTIVITY, FULLSCREEN_WORKSPACE_STACK_ID,
+                false /* expectTopTaskHasActivity */, true /* expectBottomTaskHasActivity */);
     }
 
     public void testRemovePipWithHiddenFullscreenStack() throws Exception {
@@ -529,16 +517,57 @@
         // Remove the stack and ensure that the task is placed on top of the hidden fullscreen
         // stack, but that the home stack is still focused
         removeStacks(PINNED_STACK_ID);
-        mAmWmState.waitForFocusedStack(mDevice, HOME_STACK_ID);
-        mAmWmState.assertFocusedStack("Expect home stack focused", HOME_STACK_ID);
-        mAmWmState.waitForActivityState(mDevice, PIP_ACTIVITY, STATE_STOPPED);
-        assertTrue(mAmWmState.getAmState().hasActivityState(PIP_ACTIVITY, STATE_STOPPED));
-        assertPinnedStackDoesNotExist();
-        ActivityTask topTask = mAmWmState.getAmState().getStackById(
-                FULLSCREEN_WORKSPACE_STACK_ID).getTopTask();
-        Activity topActivity = topTask.mActivities.get(0);
-        assertTrue(topActivity.name.equals(ActivityManagerTestBase.getActivityComponentName(
-                PIP_ACTIVITY)));
+        assertPinnedStackStateOnMoveToFullscreen(PIP_ACTIVITY, HOME_STACK_ID,
+                true /* expectTopTaskHasActivity */, false /* expectBottomTaskHasActivity */);
+    }
+
+    public void testMovePipToBackWithNoFullscreenStack() throws Exception {
+        if (!supportsPip()) return;
+
+        // Start with a clean slate, remove all the stacks but home
+        removeStacks(ALL_STACK_IDS_BUT_HOME);
+
+        // Launch a pip activity
+        launchActivity(PIP_ACTIVITY, EXTRA_ENTER_PIP, "true");
+        assertPinnedStackExists();
+
+        // Remove the stack and ensure that the task is now in the fullscreen stack (when no
+        // fullscreen stack existed before)
+        executeShellCommand("am broadcast -a " + PIP_ACTIVITY_ACTION_MOVE_TO_BACK);
+        assertPinnedStackStateOnMoveToFullscreen(PIP_ACTIVITY, HOME_STACK_ID,
+                true /* expectTopTaskHasActivity */, true /* expectBottomTaskHasActivity */);
+    }
+
+    public void testMovePipToBackWithVisibleFullscreenStack() throws Exception {
+        if (!supportsPip()) return;
+
+        // Launch a fullscreen activity, and a pip activity over that
+        launchActivity(TEST_ACTIVITY);
+        launchActivity(PIP_ACTIVITY, EXTRA_ENTER_PIP, "true");
+        assertPinnedStackExists();
+
+        // Remove the stack and ensure that the task is placed in the fullscreen stack, behind the
+        // top fullscreen activity
+        executeShellCommand("am broadcast -a " + PIP_ACTIVITY_ACTION_MOVE_TO_BACK);
+        assertPinnedStackStateOnMoveToFullscreen(PIP_ACTIVITY, FULLSCREEN_WORKSPACE_STACK_ID,
+                false /* expectTopTaskHasActivity */, true /* expectBottomTaskHasActivity */);
+    }
+
+    public void testMovePipToBackWithHiddenFullscreenStack() throws Exception {
+        if (!supportsPip()) return;
+
+        // Launch a fullscreen activity, return home and while the fullscreen stack is hidden,
+        // launch a pip activity over home
+        launchActivity(TEST_ACTIVITY);
+        launchHomeActivity();
+        launchActivity(PIP_ACTIVITY, EXTRA_ENTER_PIP, "true");
+        assertPinnedStackExists();
+
+        // Remove the stack and ensure that the task is placed on top of the hidden fullscreen
+        // stack, but that the home stack is still focused
+        executeShellCommand("am broadcast -a " + PIP_ACTIVITY_ACTION_MOVE_TO_BACK);
+        assertPinnedStackStateOnMoveToFullscreen(PIP_ACTIVITY, HOME_STACK_ID,
+                true /* expectTopTaskHasActivity */, false /* expectBottomTaskHasActivity */);
     }
 
     public void testPinnedStackAlwaysOnTop() throws Exception {
@@ -658,6 +687,37 @@
     }
 
     /**
+     * Called after the given {@param activityName} has been moved to the fullscreen stack. Ensures
+     * that the {@param focusedStackId} is focused, and checks the top and/or bottom tasks in the
+     * fullscreen stack if {@param expectTopTaskHasActivity} or {@param expectBottomTaskHasActivity}
+     * are set respectively.
+     */
+    private void assertPinnedStackStateOnMoveToFullscreen(String activityName, int focusedStackId,
+            boolean expectTopTaskHasActivity, boolean expectBottomTaskHasActivity)
+                    throws Exception {
+        mAmWmState.waitForFocusedStack(mDevice, focusedStackId);
+        mAmWmState.assertFocusedStack("Wrong focused stack", focusedStackId);
+        mAmWmState.waitForActivityState(mDevice, activityName, STATE_STOPPED);
+        assertTrue(mAmWmState.getAmState().hasActivityState(activityName, STATE_STOPPED));
+        assertPinnedStackDoesNotExist();
+
+        if (expectTopTaskHasActivity) {
+            ActivityTask topTask = mAmWmState.getAmState().getStackById(
+                    FULLSCREEN_WORKSPACE_STACK_ID).getTopTask();
+            Activity topActivity = topTask.mActivities.get(0);
+            assertTrue(topActivity.name.equals(ActivityManagerTestBase.getActivityComponentName(
+                    activityName)));
+        }
+        if (expectBottomTaskHasActivity) {
+            ActivityTask bottomTask = mAmWmState.getAmState().getStackById(
+                    FULLSCREEN_WORKSPACE_STACK_ID).getBottomTask();
+            Activity bottomActivity = bottomTask.mActivities.get(0);
+            assertTrue(bottomActivity.name.equals(ActivityManagerTestBase.getActivityComponentName(
+                    activityName)));
+        }
+    }
+
+    /**
      * Asserts that the pinned stack bounds does not intersect with the IME bounds.
      */
     private void assertPinnedStackDoesNotIntersectIME() throws Exception {
diff --git a/hostsidetests/services/activityandwindowmanager/util/src/android/server/cts/ActivityManagerState.java b/hostsidetests/services/activityandwindowmanager/util/src/android/server/cts/ActivityManagerState.java
index 85e2504..5b2eb14 100644
--- a/hostsidetests/services/activityandwindowmanager/util/src/android/server/cts/ActivityManagerState.java
+++ b/hostsidetests/services/activityandwindowmanager/util/src/android/server/cts/ActivityManagerState.java
@@ -626,6 +626,11 @@
             while (!doneExtracting(dump, exitPatterns)) {
                 final String line = dump.pop().trim();
 
+                // Break the activity extraction once we hit an empty line
+                if (line.isEmpty()) {
+                    break;
+                }
+
                 Matcher matcher = VISIBILITY_PATTERN.matcher(line);
                 if (matcher.matches()) {
                     log(line);
diff --git a/tests/accessibility/res/values/strings.xml b/tests/accessibility/res/values/strings.xml
index 47ba12c..867f466 100644
--- a/tests/accessibility/res/values/strings.xml
+++ b/tests/accessibility/res/values/strings.xml
@@ -26,4 +26,7 @@
     <!-- Description of the speaking accessibility service -->
     <string name="some_description">Some description</string>
 
+    <!-- Summary of the speaking accessibility service -->
+    <string name="some_summary">Some summary</string>
+
 </resources>
diff --git a/tests/accessibility/res/xml/speaking_accessibilityservice.xml b/tests/accessibility/res/xml/speaking_accessibilityservice.xml
index c60fb54..5bc5a33 100644
--- a/tests/accessibility/res/xml/speaking_accessibilityservice.xml
+++ b/tests/accessibility/res/xml/speaking_accessibilityservice.xml
@@ -22,4 +22,5 @@
     android:canRequestFilterKeyEvents="true"
     android:canRequestEnhancedWebAccessibility="true"
     android:settingsActivity="foo.bar.Activity"
-    android:description="@string/some_description" />
+    android:description="@string/some_description"
+    android:summary="@string/some_summary" />
diff --git a/tests/accessibility/src/android/view/accessibility/cts/AccessibilityServiceInfoTest.java b/tests/accessibility/src/android/view/accessibility/cts/AccessibilityServiceInfoTest.java
index bd14ee6..b90178a 100644
--- a/tests/accessibility/src/android/view/accessibility/cts/AccessibilityServiceInfoTest.java
+++ b/tests/accessibility/src/android/view/accessibility/cts/AccessibilityServiceInfoTest.java
@@ -79,6 +79,8 @@
         assertEquals("foo.bar.Activity", speakingService.getSettingsActivityName());
         assertEquals("Some description", speakingService.loadDescription(
                 getInstrumentation().getContext().getPackageManager()));
+        assertEquals("Some summary", speakingService.loadSummary(
+                getInstrumentation().getContext().getPackageManager()));
         assertNotNull(speakingService.getResolveInfo());
     }
 }
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityServiceInfoTest.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityServiceInfoTest.java
index a933099..1761117 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityServiceInfoTest.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityServiceInfoTest.java
@@ -98,7 +98,6 @@
                 AccessibilityServiceInfo.FLAG_REQUEST_FILTER_KEY_EVENTS));
         assertEquals("FLAG_REQUEST_TOUCH_EXPLORATION_MODE", AccessibilityServiceInfo.flagToString(
                 AccessibilityServiceInfo.FLAG_REQUEST_TOUCH_EXPLORATION_MODE));
-        
     }
 
     /**
diff --git a/tests/app/app/AndroidManifest.xml b/tests/app/app/AndroidManifest.xml
index 7102c32..a5ea701 100644
--- a/tests/app/app/AndroidManifest.xml
+++ b/tests/app/app/AndroidManifest.xml
@@ -312,17 +312,6 @@
             </intent-filter>
         </activity>
 
-        <activity android:name="android.app.stubs.PipNotResizeableActivity"
-                  android:label="PipNotResizeableActivity"
-                  android:resizeableActivity="false"
-                  android:supportsPictureInPicture="true"
-                  android:configChanges="smallestScreenSize|orientation|screenSize|screenLayout">
-            <intent-filter>
-                <action android:name="android.intent.action.MAIN" />
-                <category android:name="android.intent.category.FRAMEWORK_INSTRUMENTATION_TEST" />
-            </intent-filter>
-        </activity>
-
         <activity android:name="android.app.stubs.PipNotSupportedActivity"
                   android:label="PipNotSupportedActivity"
                   android:resizeableActivity="true"
diff --git a/tests/app/app/src/android/app/stubs/PipNotResizeableActivity.java b/tests/app/app/src/android/app/stubs/PipNotResizeableActivity.java
deleted file mode 100644
index 7ed1acc..0000000
--- a/tests/app/app/src/android/app/stubs/PipNotResizeableActivity.java
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package android.app.stubs;
-
-import android.app.Activity;
-
-public class PipNotResizeableActivity extends Activity {
-
-}
diff --git a/tests/app/src/android/app/cts/NotificationChannelGroupTest.java b/tests/app/src/android/app/cts/NotificationChannelGroupTest.java
index 9d8f011..9871e05 100644
--- a/tests/app/src/android/app/cts/NotificationChannelGroupTest.java
+++ b/tests/app/src/android/app/cts/NotificationChannelGroupTest.java
@@ -37,6 +37,12 @@
         NotificationChannelGroup group =  new NotificationChannelGroup("1", "one");
         assertEquals("1", group.getId());
         assertEquals("one", group.getName());
+        assertEquals(0, group.getNameResId());
+
+        NotificationChannelGroup group2 =  new NotificationChannelGroup("2", 1234);
+        assertEquals("2", group2.getId());
+        assertEquals(null, group2.getName());
+        assertEquals(1234, group2.getNameResId());
     }
 
     public void testWriteToParcel() {
@@ -48,4 +54,19 @@
                 NotificationChannelGroup.CREATOR.createFromParcel(parcel);
         assertEquals(group, fromParcel);
     }
+
+    public void testClone() {
+        NotificationChannelGroup group =  new NotificationChannelGroup("1", "one");
+        NotificationChannelGroup cloned = group.clone();
+        assertEquals("1", cloned.getId());
+        assertEquals("one", cloned.getName());
+        assertEquals(0, cloned.getNameResId());
+
+        NotificationChannelGroup group2 =  new NotificationChannelGroup("2", 1234);
+        NotificationChannelGroup cloned2 = group2.clone();
+        assertEquals("2", cloned2.getId());
+        assertEquals(null, cloned2.getName());
+        assertEquals(1234, cloned2.getNameResId());
+
+    }
 }
diff --git a/tests/app/src/android/app/cts/PipNotResizeableActivityTest.java b/tests/app/src/android/app/cts/PipNotResizeableActivityTest.java
deleted file mode 100644
index e2a4aff..0000000
--- a/tests/app/src/android/app/cts/PipNotResizeableActivityTest.java
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package android.app.cts;
-
-import android.app.Instrumentation;
-import android.app.stubs.PipNotResizeableActivity;
-import android.test.ActivityInstrumentationTestCase2;
-
-public class PipNotResizeableActivityTest
-        extends ActivityInstrumentationTestCase2<PipNotResizeableActivity> {
-
-        private Instrumentation mInstrumentation;
-        private PipNotResizeableActivity mActivity;
-
-        public PipNotResizeableActivityTest() {
-            super("android.app.stubs", PipNotResizeableActivity.class);
-        }
-
-        @Override
-        protected void setUp() throws Exception {
-            super.setUp();
-            mInstrumentation = getInstrumentation();
-            mActivity = getActivity();
-        }
-
-        public void testLaunchPipNotResizeableActivity() throws Throwable {
-            runTestOnUiThread(new Runnable() {
-                public void run() {
-                    boolean pipSupportDisabled = false;
-                    try {
-                        mActivity.enterPictureInPictureMode();
-                    } catch (IllegalStateException e) {
-                        // Pip not supported
-                        pipSupportDisabled = true;
-                    } catch (IllegalArgumentException e) {
-                        // Pip not supported
-                        pipSupportDisabled = true;
-                    }
-                    assertTrue(pipSupportDisabled);
-                    assertFalse(mActivity.isInPictureInPictureMode());
-                }
-            });
-            mInstrumentation.waitForIdleSync();
-        }
-    }
diff --git a/tests/autofillservice/res/layout/nested_layout.xml b/tests/autofillservice/res/layout/nested_layout.xml
new file mode 100644
index 0000000..d81927a
--- /dev/null
+++ b/tests/autofillservice/res/layout/nested_layout.xml
@@ -0,0 +1,24 @@
+<?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.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/container"
+    android:layout_width="match_parent" android:layout_height="match_parent"
+    android:orientation="vertical">
+
+    <EditText android:id="@+id/field" android:layout_width="match_parent"
+        android:layout_height="wrap_content" />
+</LinearLayout>
diff --git a/tests/autofillservice/res/layout/partially_manually_filled_activity.xml b/tests/autofillservice/res/layout/view_attribute_test_activity.xml
similarity index 74%
rename from tests/autofillservice/res/layout/partially_manually_filled_activity.xml
rename to tests/autofillservice/res/layout/view_attribute_test_activity.xml
index f7bfe4a..00f305d 100644
--- a/tests/autofillservice/res/layout/partially_manually_filled_activity.xml
+++ b/tests/autofillservice/res/layout/view_attribute_test_activity.xml
@@ -16,9 +16,8 @@
 -->
 
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent"
-    android:orientation="vertical">
+    android:layout_width="match_parent" android:layout_height="match_parent"
+    android:orientation="vertical" android:id="@+id/rootContainer">
 
     <EditText android:id="@+id/firstLevelDefault" android:layout_width="wrap_content"
         android:layout_height="wrap_content" />
@@ -48,4 +47,12 @@
             android:layout_height="wrap_content" android:autoFillMode="manual" />
     </LinearLayout>
 
+    <TextView android:id="@+id/textViewNoHint" android:layout_width="wrap_content"
+        android:layout_height="wrap_content" />
+    <TextView android:id="@+id/textViewHintNone" android:layout_width="wrap_content"
+        android:layout_height="wrap_content" android:autoFillHint="none" />
+    <TextView android:id="@+id/textViewPassword" android:layout_width="wrap_content"
+        android:layout_height="wrap_content" android:autoFillHint="password" />
+    <TextView android:id="@+id/textViewPhoneName" android:layout_width="wrap_content"
+        android:layout_height="wrap_content" android:autoFillHint="phone|username" />
 </LinearLayout>
diff --git a/tests/autofillservice/src/android/autofillservice/cts/AbstractAutoFillActivity.java b/tests/autofillservice/src/android/autofillservice/cts/AbstractAutoFillActivity.java
new file mode 100644
index 0000000..652fae3
--- /dev/null
+++ b/tests/autofillservice/src/android/autofillservice/cts/AbstractAutoFillActivity.java
@@ -0,0 +1,53 @@
+/*
+ * 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.autofillservice.cts;
+
+import android.app.Activity;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+/**
+  * Base class for all activities in this test suite
+  */
+abstract class AbstractAutoFillActivity extends Activity {
+
+    /**
+     * Run an action in the UI thread, and blocks caller until the action is finished.
+     */
+    public final void syncRunOnUiThread(Runnable action) {
+        syncRunOnUiThread(action, Helper.UI_TIMEOUT_MS);
+    }
+
+    /**
+     * Run an action in the UI thread, and blocks caller until the action is finished or it times
+     * out.
+     */
+    public final void syncRunOnUiThread(Runnable action, int timeoutMs) {
+        final CountDownLatch latch = new CountDownLatch(1);
+        runOnUiThread(() -> {
+            action.run();
+            latch.countDown();
+        });
+        try {
+            if (!latch.await(timeoutMs, TimeUnit.MILLISECONDS)) {
+                throw new AssertionError("action on UI thread timed out after " + timeoutMs + " ms");
+            }
+        } catch (InterruptedException e) {
+            Thread.currentThread().interrupt();
+            throw new RuntimeException("Interrupted", e);
+        }
+    }
+}
diff --git a/tests/autofillservice/src/android/autofillservice/cts/AbstractDatePickerActivity.java b/tests/autofillservice/src/android/autofillservice/cts/AbstractDatePickerActivity.java
index ed9f01a..f530ad6 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/AbstractDatePickerActivity.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/AbstractDatePickerActivity.java
@@ -19,7 +19,6 @@
 
 import static com.google.common.truth.Truth.assertWithMessage;
 
-import android.app.Activity;
 import android.content.Intent;
 import android.os.Bundle;
 import android.os.SystemClock;
@@ -50,7 +49,7 @@
  * <p>It's abstract because the sub-class must provide the view id, so it can support multiple
  * UI types (like calendar and spinner).
  */
-abstract class AbstractDatePickerActivity extends Activity {
+abstract class AbstractDatePickerActivity extends AbstractAutoFillActivity {
 
     private static final long OK_TIMEOUT_MS = 1000;
 
@@ -128,7 +127,7 @@
      * Visits the {@code output} in the UiThread.
      */
     void onOutput(ViewVisitor<EditText> v) {
-        runOnUiThread(() -> {
+        syncRunOnUiThread(() -> {
             v.visit(mOutput);
         });
     }
@@ -137,7 +136,7 @@
      * Sets the date in the {@link DatePicker}.
      */
     void setDate(int year, int month, int day) {
-        runOnUiThread(() -> {
+        syncRunOnUiThread(() -> {
             mDatePicker.updateDate(year, month, day);
         });
     }
@@ -147,7 +146,7 @@
      */
     void tapOk() throws Exception {
         mOkLatch = new CountDownLatch(1);
-        runOnUiThread(() -> {
+        syncRunOnUiThread(() -> {
             mOk.performClick();
         });
         boolean called = mOkLatch.await(OK_TIMEOUT_MS, TimeUnit.MILLISECONDS);
diff --git a/tests/autofillservice/src/android/autofillservice/cts/AbstractTimePickerActivity.java b/tests/autofillservice/src/android/autofillservice/cts/AbstractTimePickerActivity.java
index 92d67e5..9fc4943 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/AbstractTimePickerActivity.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/AbstractTimePickerActivity.java
@@ -17,7 +17,6 @@
 
 import static com.google.common.truth.Truth.assertWithMessage;
 
-import android.app.Activity;
 import android.content.Intent;
 import android.os.Bundle;
 import android.widget.Button;
@@ -39,7 +38,7 @@
  * <p>It's abstract because the sub-class must provide the view id, so it can support multiple
  * UI types (like clock and spinner).
  */
-abstract class AbstractTimePickerActivity extends Activity {
+abstract class AbstractTimePickerActivity extends AbstractAutoFillActivity {
 
     private static final long OK_TIMEOUT_MS = 1000;
 
@@ -117,7 +116,7 @@
      * Visits the {@code output} in the UiThread.
      */
     void onOutput(ViewVisitor<EditText> v) {
-        runOnUiThread(() -> {
+        syncRunOnUiThread(() -> {
             v.visit(mOutput);
         });
     }
@@ -126,7 +125,7 @@
      * Sets the time in the {@link TimePicker}.
      */
     void setTime(int hour, int minute) {
-        runOnUiThread(() -> {
+        syncRunOnUiThread(() -> {
             mTimePicker.setHour(hour);
             mTimePicker.setMinute(minute);
         });
@@ -137,7 +136,7 @@
      */
     void tapOk() throws Exception {
         mOkLatch = new CountDownLatch(1);
-        runOnUiThread(() -> {
+        syncRunOnUiThread(() -> {
             mOk.performClick();
         });
         boolean called = mOkLatch.await(OK_TIMEOUT_MS, TimeUnit.MILLISECONDS);
diff --git a/tests/autofillservice/src/android/autofillservice/cts/AuthenticationActivity.java b/tests/autofillservice/src/android/autofillservice/cts/AuthenticationActivity.java
index c5ee4d2..e060d3a 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/AuthenticationActivity.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/AuthenticationActivity.java
@@ -16,7 +16,6 @@
 
 package android.autofillservice.cts;
 
-import android.app.Activity;
 import android.app.assist.AssistStructure;
 import android.content.Intent;
 import android.os.Bundle;
@@ -28,7 +27,7 @@
 /**
  * This class simulates authentication at the dataset at reponse level
  */
-public class AuthenticationActivity extends Activity {
+public class AuthenticationActivity extends AbstractAutoFillActivity {
     private static CannedFillResponse sResponse;
     private static CannedFillResponse.CannedDataset sDataset;
 
diff --git a/tests/autofillservice/src/android/autofillservice/cts/AutoFillServiceTestCase.java b/tests/autofillservice/src/android/autofillservice/cts/AutoFillServiceTestCase.java
index 81543b3..cb0a911 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/AutoFillServiceTestCase.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/AutoFillServiceTestCase.java
@@ -27,6 +27,8 @@
 import android.support.test.runner.AndroidJUnit4;
 
 import android.widget.RemoteViews;
+
+import org.junit.After;
 import org.junit.AfterClass;
 import org.junit.Before;
 import org.junit.BeforeClass;
@@ -45,10 +47,17 @@
     protected static UiBot sUiBot;
 
     @BeforeClass
+    public static void removeLockScreen() {
+        runShellCommand("input keyevent KEYCODE_WAKEUP");
+        runShellCommand("wm dismiss-keyguard");
+    }
+
+    @BeforeClass
     public static void setUiBot() throws Exception {
         sUiBot = new UiBot(InstrumentationRegistry.getInstrumentation(), UI_TIMEOUT_MS);
     }
 
+    @BeforeClass
     @AfterClass
     public static void disableService() {
         runShellCommand("settings delete secure %s", AUTO_FILL_SERVICE);
diff --git a/tests/autofillservice/src/android/autofillservice/cts/CannedFillResponse.java b/tests/autofillservice/src/android/autofillservice/cts/CannedFillResponse.java
index f822568..2fa207b 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/CannedFillResponse.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/CannedFillResponse.java
@@ -17,13 +17,10 @@
 
 import static com.google.common.truth.Truth.assertWithMessage;
 
-import static android.service.autofill.SaveInfo.SAVE_DATA_TYPE_GENERIC;
-
 import static android.autofillservice.cts.Helper.dumpStructure;
 import static android.autofillservice.cts.Helper.findNodeByResourceId;
 import android.app.assist.AssistStructure;
 import android.app.assist.AssistStructure.ViewNode;
-import android.autofillservice.cts.CannedFillResponse.CannedDataset.Builder;
 import android.content.IntentSender;
 import android.os.Bundle;
 import android.service.autofill.Dataset;
@@ -91,19 +88,21 @@
                 builder.addDataset(dataset);
             }
         }
-        if (savableIds != null) {
+        if (saveType >= 0 ) {
             final SaveInfo.Builder saveInfo = new SaveInfo.Builder(saveType);
             if (saveDescription != null) {
                 saveInfo.setDescription(saveDescription);
             }
-            for (String resourceId : savableIds) {
-                final ViewNode node = findNodeByResourceId(structure, resourceId);
-                if (node == null) {
-                    dumpStructure("onFillRequest()", structure);
-                    throw new AssertionError("No node with savable resourceId " + resourceId);
+            if (savableIds != null) {
+                for (String resourceId : savableIds) {
+                    final ViewNode node = findNodeByResourceId(structure, resourceId);
+                    if (node == null) {
+                        dumpStructure("onFillRequest()", structure);
+                        throw new AssertionError("No node with savable resourceId " + resourceId);
+                    }
+                    final AutoFillId id = node.getAutoFillId();
+                    saveInfo.addSavableIds(id);
                 }
-                final AutoFillId id = node.getAutoFillId();
-                saveInfo.addSavableIds(id);
             }
             if (negativeActionLabel != null) {
                 saveInfo.setNegativeAction(negativeActionLabel, negativeActionListener);
@@ -130,7 +129,7 @@
         private final List<CannedDataset> mDatasets = new ArrayList<>();
         private String[] mSavableIds;
         private String mSaveDescription;
-        public int mSaveType;
+        public int mSaveType = -1;
         private Bundle mExtras;
         private RemoteViews mPresentation;
         private IntentSender mAuthentication;
@@ -145,7 +144,8 @@
         /**
          * Sets the savable ids based on they {@code resourceId}.
          */
-        public Builder setSavableIds(String... ids) {
+        public Builder setSavableIds(int type, String... ids) {
+            mSaveType = type;
             mSavableIds = ids;
             return this;
         }
@@ -165,6 +165,7 @@
             mSaveType = type;
             return this;
         }
+
         /**
          * Sets the extra passed to {@link
          * android.service.autofill.FillResponse.Builder#setExtras(Bundle)}.
diff --git a/tests/autofillservice/src/android/autofillservice/cts/CheckoutActivity.java b/tests/autofillservice/src/android/autofillservice/cts/CheckoutActivity.java
index a76929d..bb01487 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/CheckoutActivity.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/CheckoutActivity.java
@@ -19,7 +19,6 @@
 
 import static com.google.common.truth.Truth.assertWithMessage;
 
-import android.app.Activity;
 import android.content.Intent;
 import android.os.Bundle;
 import android.view.View;
@@ -47,7 +46,7 @@
  *   <li>Buy Button
  * </ul>
  */
-public class CheckoutActivity extends Activity {
+public class CheckoutActivity extends AbstractAutoFillActivity {
     private static final long BUY_TIMEOUT_MS = 1000;
 
     static final String ID_CC_NUMBER = "cc_number";
@@ -160,7 +159,7 @@
      * Visits the {@code ccNumber} in the UiThread.
      */
     void onCcNumber(ViewVisitor<EditText> v) {
-        runOnUiThread(() -> {
+        syncRunOnUiThread(() -> {
             v.visit(mCcNumber);
         });
     }
@@ -169,7 +168,7 @@
      * Visits the {@code ccExpirationDate} in the UiThread.
      */
     void onCcExpiration(ViewVisitor<Spinner> v) {
-        runOnUiThread(() -> {
+        syncRunOnUiThread(() -> {
             v.visit(mCcExpiration);
         });
     }
@@ -178,7 +177,7 @@
      * Visits the {@code address} in the UiThread.
      */
     void onAddress(ViewVisitor<RadioGroup> v) {
-        runOnUiThread(() -> {
+        syncRunOnUiThread(() -> {
             v.visit(mAddress);
         });
     }
@@ -187,7 +186,7 @@
      * Visits the {@code homeAddress} in the UiThread.
      */
     void onHomeAddress(ViewVisitor<RadioButton> v) {
-        runOnUiThread(() -> {
+        syncRunOnUiThread(() -> {
             v.visit(mHomeAddress);
         });
     }
@@ -196,7 +195,7 @@
      * Visits the {@code saveCC} in the UiThread.
      */
     void onSaveCc(ViewVisitor<CheckBox> v) {
-        runOnUiThread(() -> {
+        syncRunOnUiThread(() -> {
             v.visit(mSaveCc);
         });
     }
@@ -206,7 +205,7 @@
      */
     void tapBuy() throws Exception {
         mBuyLatch = new CountDownLatch(1);
-        runOnUiThread(() -> {
+        syncRunOnUiThread(() -> {
             mBuyButton.performClick();
         });
         boolean called = mBuyLatch.await(BUY_TIMEOUT_MS, TimeUnit.MILLISECONDS);
diff --git a/tests/autofillservice/src/android/autofillservice/cts/CheckoutActivityTest.java b/tests/autofillservice/src/android/autofillservice/cts/CheckoutActivityTest.java
index 8922b78..4eb4873 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/CheckoutActivityTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/CheckoutActivityTest.java
@@ -33,6 +33,7 @@
 import static android.autofillservice.cts.Helper.findNodeByResourceId;
 import static android.autofillservice.cts.InstrumentedAutoFillService.waitUntilConnected;
 import static android.autofillservice.cts.InstrumentedAutoFillService.waitUntilDisconnected;
+import static android.service.autofill.SaveInfo.SAVE_DATA_TYPE_CREDIT_CARD;
 
 import static com.google.common.truth.Truth.assertThat;
 import static com.google.common.truth.Truth.assertWithMessage;
@@ -42,12 +43,12 @@
 import android.autofillservice.cts.InstrumentedAutoFillService.FillRequest;
 import android.autofillservice.cts.InstrumentedAutoFillService.Replier;
 import android.autofillservice.cts.InstrumentedAutoFillService.SaveRequest;
-import android.support.test.filters.SmallTest;
 import android.support.test.rule.ActivityTestRule;
 import android.view.autofill.AutoFillType;
 import android.view.autofill.AutoFillValue;
 import android.widget.Spinner;
 
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
@@ -55,7 +56,6 @@
 /**
  * Test case for an activity containing non-TextField views.
  */
-@SmallTest
 public class CheckoutActivityTest extends AutoFillServiceTestCase {
 
     @Rule
@@ -69,6 +69,11 @@
         mCheckoutActivity = mActivityRule.getActivity();
     }
 
+    @After
+    public void finishWelcomeActivity() {
+        WelcomeActivity.finishIt();
+    }
+
     @Test
     public void testAutoFill() throws Exception {
         // Set service.
@@ -124,7 +129,8 @@
 
         // Set expectations.
         replier.addResponse(new CannedFillResponse.Builder()
-                .setSavableIds(ID_CC_NUMBER, ID_CC_EXPIRATION, ID_ADDRESS, ID_SAVE_CC)
+                .setSavableIds(SAVE_DATA_TYPE_CREDIT_CARD,
+                        ID_CC_NUMBER, ID_CC_EXPIRATION, ID_ADDRESS, ID_SAVE_CC)
                 .build());
 
         // Dynamically change view contents
@@ -158,7 +164,7 @@
         mCheckoutActivity.onSaveCc((v) -> { v.setChecked(true); });
         mCheckoutActivity.tapBuy();
         InstrumentedAutoFillService.setReplier(replier); // Replier was reset onFill()
-        sUiBot.saveForAutofill(true);
+        sUiBot.saveForAutofill(SAVE_DATA_TYPE_CREDIT_CARD, true);
         final SaveRequest saveRequest = replier.getNextSaveRequest();
         assertWithMessage("onSave() not called").that(saveRequest).isNotNull();
 
diff --git a/tests/autofillservice/src/android/autofillservice/cts/DatePickerCalendarActivityTest.java b/tests/autofillservice/src/android/autofillservice/cts/DatePickerCalendarActivityTest.java
index 46164f3..a51a00c 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/DatePickerCalendarActivityTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/DatePickerCalendarActivityTest.java
@@ -15,12 +15,10 @@
  */
 package android.autofillservice.cts;
 
-import android.support.test.filters.SmallTest;
 import android.support.test.rule.ActivityTestRule;
 
 import org.junit.Rule;
 
-@SmallTest
 public class DatePickerCalendarActivityTest extends DatePickerTestCase<DatePickerCalendarActivity> {
 
     @Rule
diff --git a/tests/autofillservice/src/android/autofillservice/cts/DatePickerSpinnerActivityTest.java b/tests/autofillservice/src/android/autofillservice/cts/DatePickerSpinnerActivityTest.java
index 60d54b7..10851cd 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/DatePickerSpinnerActivityTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/DatePickerSpinnerActivityTest.java
@@ -15,12 +15,10 @@
  */
 package android.autofillservice.cts;
 
-import android.support.test.filters.SmallTest;
 import android.support.test.rule.ActivityTestRule;
 
 import org.junit.Rule;
 
-@SmallTest
 public class DatePickerSpinnerActivityTest extends DatePickerTestCase<DatePickerSpinnerActivity> {
 
     @Rule
diff --git a/tests/autofillservice/src/android/autofillservice/cts/DatePickerTestCase.java b/tests/autofillservice/src/android/autofillservice/cts/DatePickerTestCase.java
index a99e4c9..c90b348 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/DatePickerTestCase.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/DatePickerTestCase.java
@@ -24,6 +24,8 @@
 import static android.autofillservice.cts.Helper.findNodeByResourceId;
 import static android.autofillservice.cts.InstrumentedAutoFillService.waitUntilConnected;
 import static android.autofillservice.cts.InstrumentedAutoFillService.waitUntilDisconnected;
+import static android.service.autofill.SaveInfo.SAVE_DATA_TYPE_GENERIC;
+
 import static com.google.common.truth.Truth.assertWithMessage;
 
 import android.autofillservice.cts.CannedFillResponse.CannedDataset;
@@ -33,6 +35,7 @@
 import android.icu.util.Calendar;
 import android.view.autofill.AutoFillValue;
 
+import org.junit.After;
 import org.junit.Test;
 
 /**
@@ -43,6 +46,11 @@
 
     protected abstract T getDatePickerActivity();
 
+    @After
+    public void finishWelcomeActivity() {
+        WelcomeActivity.finishIt();
+    }
+
     @Test
     public void testAutoFillAndSave() throws Exception {
         final T activity = getDatePickerActivity();
@@ -58,10 +66,13 @@
         cal.set(Calendar.MONTH, 11);
         cal.set(Calendar.DAY_OF_MONTH, 20);
 
-        replier.addResponse(new CannedDataset.Builder()
-                .setPresentation(createPresentation("The end of the world"))
-                .setField(ID_OUTPUT, AutoFillValue.forText("Y U NO CHANGE ME?"))
-                .setField(ID_DATE_PICKER, AutoFillValue.forDate(cal.getTimeInMillis()))
+        replier.addResponse(new CannedFillResponse.Builder()
+                .addDataset(new CannedDataset.Builder()
+                    .setPresentation(createPresentation("The end of the world"))
+                    .setField(ID_OUTPUT, AutoFillValue.forText("Y U NO CHANGE ME?"))
+                    .setField(ID_DATE_PICKER, AutoFillValue.forDate(cal.getTimeInMillis()))
+                    .build())
+                .setSaveType(SAVE_DATA_TYPE_GENERIC)
                 .build());
         activity.expectAutoFill("2012/11/20", 2012, 11, 20);
 
@@ -86,7 +97,7 @@
         activity.tapOk();
 
         InstrumentedAutoFillService.setReplier(replier); // Replier was reset onFill()
-        sUiBot.saveForAutofill(true);
+        sUiBot.saveForAutofill(SAVE_DATA_TYPE_GENERIC, true);
         final SaveRequest saveRequest = replier.getNextSaveRequest();
         assertWithMessage("onSave() not called").that(saveRequest).isNotNull();
 
diff --git a/tests/autofillservice/src/android/autofillservice/cts/Helper.java b/tests/autofillservice/src/android/autofillservice/cts/Helper.java
index 58e9abc..dd99b6d 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/Helper.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/Helper.java
@@ -256,7 +256,7 @@
         cal.setTimeInMillis(value.getDateValue());
 
         assertWithMessage("Wrong hour on AutoFillValue %s", value)
-            .that(cal.get(Calendar.HOUR)).isEqualTo(hour);
+            .that(cal.get(Calendar.HOUR_OF_DAY)).isEqualTo(hour);
         assertWithMessage("Wrong minute on AutoFillValue %s", value)
             .that(cal.get(Calendar.MINUTE)).isEqualTo(minute);
     }
diff --git a/tests/autofillservice/src/android/autofillservice/cts/InitializedCheckoutActivityTest.java b/tests/autofillservice/src/android/autofillservice/cts/InitializedCheckoutActivityTest.java
index 33becf8..5d0c19a 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/InitializedCheckoutActivityTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/InitializedCheckoutActivityTest.java
@@ -39,7 +39,6 @@
 /**
  * Test case for an activity containing non-TextField views with initial values set on XML.
  */
-@SmallTest
 public class InitializedCheckoutActivityTest extends AutoFillServiceTestCase {
 
     @Rule
diff --git a/tests/autofillservice/src/android/autofillservice/cts/LoginActivity.java b/tests/autofillservice/src/android/autofillservice/cts/LoginActivity.java
index 6732870..3f12e27 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/LoginActivity.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/LoginActivity.java
@@ -17,7 +17,6 @@
 
 import static com.google.common.truth.Truth.assertWithMessage;
 
-import android.app.Activity;
 import android.content.Intent;
 import android.os.Bundle;
 import android.util.Log;
@@ -40,7 +39,7 @@
  *   <li>Login Button
  * </ul>
  */
-public class LoginActivity extends Activity {
+public class LoginActivity extends AbstractAutoFillActivity {
 
     private static final String TAG = "LoginActivity";
     private static String WELCOME_TEMPLATE = "Welcome to the new activity, %s!";
@@ -166,7 +165,7 @@
      * Visits the {@code username_label} in the UiThread.
      */
     void onUsernameLabel(ViewVisitor<TextView> v) {
-        runOnUiThread(() -> {
+        syncRunOnUiThread(() -> {
             v.visit(mUsernameLabel);
         });
     }
@@ -175,7 +174,7 @@
      * Visits the {@code username} in the UiThread.
      */
     void onUsername(ViewVisitor<EditText> v) {
-        runOnUiThread(() -> {
+        syncRunOnUiThread(() -> {
             v.visit(mUsernameEditText);
         });
     }
@@ -184,7 +183,7 @@
      * Visits the {@code password_label} in the UiThread.
      */
     void onPasswordLabel(ViewVisitor<TextView> v) {
-        runOnUiThread(() -> {
+        syncRunOnUiThread(() -> {
             v.visit(mPasswordLabel);
         });
     }
@@ -193,7 +192,7 @@
      * Visits the {@code password} in the UiThread.
      */
     void onPassword(ViewVisitor<EditText> v) {
-        runOnUiThread(() -> {
+        syncRunOnUiThread(() -> {
             v.visit(mPasswordEditText);
         });
     }
@@ -203,7 +202,7 @@
      */
     String tapLogin() throws Exception {
         mLoginLatch = new CountDownLatch(1);
-        runOnUiThread(() -> {
+        syncRunOnUiThread(() -> {
             mLoginButton.performClick();
         });
         boolean called = mLoginLatch.await(LOGIN_TIMEOUT_MS, TimeUnit.MILLISECONDS);
@@ -217,7 +216,7 @@
      */
     void setFlags(int flags) {
         Log.d(TAG, "setFlags():" + flags);
-        runOnUiThread(() -> {
+        syncRunOnUiThread(() -> {
             getWindow().setFlags(flags, flags);
         });
     }
diff --git a/tests/autofillservice/src/android/autofillservice/cts/LoginActivityTest.java b/tests/autofillservice/src/android/autofillservice/cts/LoginActivityTest.java
index d027f44..0fffef4 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/LoginActivityTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/LoginActivityTest.java
@@ -19,6 +19,7 @@
 import static android.autofillservice.cts.Helper.assertTextIsSanitized;
 import static android.autofillservice.cts.Helper.assertTextOnly;
 import static android.autofillservice.cts.Helper.findNodeByResourceId;
+import static android.autofillservice.cts.Helper.runShellCommand;
 import static android.autofillservice.cts.InstrumentedAutoFillService.waitUntilConnected;
 import static android.autofillservice.cts.InstrumentedAutoFillService.waitUntilDisconnected;
 import static android.autofillservice.cts.LoginActivity.AUTHENTICATION_MESSAGE;
@@ -27,8 +28,10 @@
 import static android.autofillservice.cts.LoginActivity.ID_USERNAME;
 import static android.autofillservice.cts.LoginActivity.ID_USERNAME_LABEL;
 import static android.autofillservice.cts.LoginActivity.getWelcomeMessage;
+import static android.provider.Settings.Secure.AUTO_FILL_SERVICE;
 import static android.service.autofill.SaveInfo.SAVE_DATA_TYPE_ADDRESS;
 import static android.service.autofill.SaveInfo.SAVE_DATA_TYPE_CREDIT_CARD;
+import static android.service.autofill.SaveInfo.SAVE_DATA_TYPE_GENERIC;
 import static android.service.autofill.SaveInfo.SAVE_DATA_TYPE_PASSWORD;
 import static android.text.InputType.TYPE_NULL;
 import static android.text.InputType.TYPE_TEXT_VARIATION_PASSWORD;
@@ -49,11 +52,12 @@
 import android.content.IntentFilter;
 import android.content.IntentSender;
 import android.os.Bundle;
-import android.support.test.filters.SmallTest;
+import android.os.SystemClock;
 import android.support.test.rule.ActivityTestRule;
 import android.support.test.uiautomator.UiObject2;
 import android.view.autofill.AutoFillValue;
 
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
@@ -72,15 +76,13 @@
  *
  *  Save
  *  - test cases where only non-savable-ids are changed
- *  - test case where 'no thanks' is tapped
- *  - make sure snack bar times out (will require a shell cmd to change timeout)
+ *  - test case where 'no thanks' is tapped (similar to testSaveSnackBarGoesAway())
  *
  *  Other assertions
  *  - illegal state thrown on callback calls
  *  - system server state after calls (for example, no pending callback)
  *  - make sure there is no dangling session using 'cmd autofill list sessions'
  */
-@SmallTest
 public class LoginActivityTest extends AutoFillServiceTestCase {
 
     @Rule
@@ -95,6 +97,11 @@
         destroyAllSessions();
     }
 
+    @After
+    public void finishWelcomeActivity() {
+        WelcomeActivity.finishIt();
+    }
+
     @Test
     public void testAutoFillNoDatasets() throws Exception {
         // Set service.
@@ -222,6 +229,7 @@
                         .setField(ID_PASSWORD, AutoFillValue.forText("sweet"))
                         .setPresentation(createPresentation("The Dude"))
                         .build())
+                .setSaveType(SAVE_DATA_TYPE_PASSWORD)
                 .setExtras(extras)
                 .build());
         mLoginActivity.expectAutoFill("dude", "sweet");
@@ -251,7 +259,7 @@
 
         // Assert the snack bar is shown and tap "Save".
         InstrumentedAutoFillService.setReplier(replier); // Replier was reset onFill()
-        sUiBot.saveForAutofill(true);
+        sUiBot.saveForAutofill(SAVE_DATA_TYPE_PASSWORD, true);
 
         final SaveRequest saveRequest = replier.getNextSaveRequest();
         assertWithMessage("onSave() not called").that(saveRequest).isNotNull();
@@ -363,7 +371,7 @@
 
         // Set expectations.
         replier.addResponse(new CannedFillResponse.Builder()
-                .setSavableIds(ID_USERNAME, ID_PASSWORD)
+                .setSavableIds(SAVE_DATA_TYPE_PASSWORD, ID_USERNAME, ID_PASSWORD)
                 .build());
 
         // Trigger auto-fill.
@@ -389,7 +397,7 @@
         InstrumentedAutoFillService.setReplier(replier); // Replier was reset onFill()
 
         // Assert the snack bar is shown and tap "Save".
-        sUiBot.saveForAutofill(true);
+        sUiBot.saveForAutofill(SAVE_DATA_TYPE_PASSWORD, true);
 
         final SaveRequest saveRequest = replier.getNextSaveRequest();
         assertWithMessage("onSave() not called").that(saveRequest).isNotNull();
@@ -411,6 +419,68 @@
         assertNoDanglingSessions();
     }
 
+    private void setSnackBarLifetimeMs(int timeout) {
+        runShellCommand("cmd autofill set save_timeout %s", timeout);
+    }
+
+    @Test
+    public void testSaveSnackBarGoesAway() throws Exception {
+        enableService();
+        final int timeout = 1000;
+        setSnackBarLifetimeMs(timeout);
+
+        try {
+            // Set service.
+            final Replier replier = new Replier();
+            InstrumentedAutoFillService.setReplier(replier);
+
+            // Set expectations.
+            replier.addResponse(new CannedFillResponse.Builder()
+                    .setSavableIds(SAVE_DATA_TYPE_PASSWORD, ID_USERNAME, ID_PASSWORD)
+                    .build());
+
+            // Trigger auto-fill.
+            mLoginActivity.onUsername((v) -> { v.requestFocus(); });
+            waitUntilConnected();
+
+            // Sanity check.
+            sUiBot.assertNoDatasets();
+
+            // Wait for onFill() before proceeding, otherwise the fields might be changed before
+            // the session started
+            replier.getNextFillRequest();
+
+            // Set credentials...
+            mLoginActivity.onUsername((v) -> { v.setText("malkovich"); });
+            mLoginActivity.onPassword((v) -> { v.setText("malkovich"); });
+
+            // ...and login
+            final String expectedMessage = getWelcomeMessage("malkovich");
+            final String actualMessage = mLoginActivity.tapLogin();
+            assertWithMessage("Wrong welcome msg").that(actualMessage).isEqualTo(expectedMessage);
+
+            InstrumentedAutoFillService.setReplier(replier); // Replier was reset onFill()
+
+            // Assert the snack bar is shown.
+            sUiBot.assertSaveShowing(SAVE_DATA_TYPE_PASSWORD);
+            SystemClock.sleep(timeout);
+            sUiBot.assertSaveNotShowing(SAVE_DATA_TYPE_PASSWORD);
+
+            // Sanity check: once timed out, session should be finsihed.
+            assertNoDanglingSessions();
+
+            // Other sanity checks.
+            waitUntilDisconnected();
+        } finally {
+            setSnackBarLifetimeMs(5000);
+        }
+    }
+
+    @Test
+    public void testGenericSave() throws Exception {
+        customizedSaveTest(SAVE_DATA_TYPE_GENERIC);
+    }
+
     @Test
     public void testCustomizedSavePassword() throws Exception {
         customizedSaveTest(SAVE_DATA_TYPE_PASSWORD);
@@ -436,9 +506,8 @@
         // Set expectations.
         final String saveDescription = "Your data will be saved with love and care...";
         replier.addResponse(new CannedFillResponse.Builder()
-                .setSavableIds(ID_USERNAME, ID_PASSWORD)
+                .setSavableIds(type, ID_USERNAME, ID_PASSWORD)
                 .setSaveDescription(saveDescription)
-                .setSaveType(type)
                 .build());
 
         // Trigger auto-fill.
@@ -597,7 +666,7 @@
 
         // Set expectations.
         replier.addResponse(new CannedFillResponse.Builder()
-                .setSavableIds(ID_USERNAME, ID_PASSWORD)
+                .setSavableIds(SAVE_DATA_TYPE_PASSWORD, ID_USERNAME, ID_PASSWORD)
                 .build());
 
         // Change view contents.
@@ -626,7 +695,9 @@
         mLoginActivity.onPassword((v) -> { v.setText("malkovich"); });
         mLoginActivity.tapLogin();
         InstrumentedAutoFillService.setReplier(replier); // Replier was reset onFill()
-        sUiBot.saveForAutofill(true);
+
+        // Assert the snack bar is shown and tap "Save".
+        sUiBot.saveForAutofill(SAVE_DATA_TYPE_PASSWORD, true);
         final SaveRequest saveRequest = replier.getNextSaveRequest();
         assertWithMessage("onSave() not called").that(saveRequest).isNotNull();
 
@@ -651,7 +722,7 @@
         // Set no-op behavior.
         final Replier replier = new Replier();
         replier.addResponse(new CannedFillResponse.Builder()
-                .setSavableIds(ID_USERNAME, ID_PASSWORD)
+                .setSavableIds(SAVE_DATA_TYPE_PASSWORD, ID_USERNAME, ID_PASSWORD)
                 .build());
         InstrumentedAutoFillService.setReplier(replier);
 
@@ -677,7 +748,7 @@
         // Set no-op behavior.
         final Replier replier = new Replier();
         replier.addResponse(new CannedFillResponse.Builder()
-                .setSavableIds(ID_USERNAME, ID_PASSWORD)
+                .setSavableIds(SAVE_DATA_TYPE_PASSWORD, ID_USERNAME, ID_PASSWORD)
                 .build());
         InstrumentedAutoFillService.setReplier(replier);
 
@@ -710,7 +781,7 @@
                 getContext(), 0, new Intent(intentAction), 0).getIntentSender();
 
         replier.addResponse(new CannedFillResponse.Builder()
-                .setSavableIds(ID_USERNAME, ID_PASSWORD)
+                .setSavableIds(SAVE_DATA_TYPE_PASSWORD, ID_USERNAME, ID_PASSWORD)
                 .setNegativeAction("Foo", listener)
                 .build());
         InstrumentedAutoFillService.setReplier(replier);
@@ -739,7 +810,7 @@
         }, intentFilter);
 
         // Trigger the negative button.
-        sUiBot.saveForAutofill(false);
+        sUiBot.saveForAutofill(SAVE_DATA_TYPE_PASSWORD, false);
 
         // Wait for the custom action.
         assertThat(latch.await(5, TimeUnit.SECONDS)).isTrue();
diff --git a/tests/autofillservice/src/android/autofillservice/cts/TimePickerClockActivityTest.java b/tests/autofillservice/src/android/autofillservice/cts/TimePickerClockActivityTest.java
index c8ab9d8..d8651b6 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/TimePickerClockActivityTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/TimePickerClockActivityTest.java
@@ -15,12 +15,10 @@
  */
 package android.autofillservice.cts;
 
-import android.support.test.filters.SmallTest;
 import android.support.test.rule.ActivityTestRule;
 
 import org.junit.Rule;
 
-@SmallTest
 public class TimePickerClockActivityTest extends TimePickerTestCase<TimePickerClockActivity> {
 
     @Rule
diff --git a/tests/autofillservice/src/android/autofillservice/cts/TimePickerSpinnerActivityTest.java b/tests/autofillservice/src/android/autofillservice/cts/TimePickerSpinnerActivityTest.java
index 471ec61..ab86255 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/TimePickerSpinnerActivityTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/TimePickerSpinnerActivityTest.java
@@ -15,12 +15,10 @@
  */
 package android.autofillservice.cts;
 
-import android.support.test.filters.SmallTest;
 import android.support.test.rule.ActivityTestRule;
 
 import org.junit.Rule;
 
-@SmallTest
 public class TimePickerSpinnerActivityTest extends TimePickerTestCase<TimePickerSpinnerActivity> {
 
     @Rule
diff --git a/tests/autofillservice/src/android/autofillservice/cts/TimePickerTestCase.java b/tests/autofillservice/src/android/autofillservice/cts/TimePickerTestCase.java
index 2c5a434..d298281 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/TimePickerTestCase.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/TimePickerTestCase.java
@@ -22,6 +22,7 @@
 import static android.autofillservice.cts.Helper.findNodeByResourceId;
 import static android.autofillservice.cts.InstrumentedAutoFillService.waitUntilConnected;
 import static android.autofillservice.cts.InstrumentedAutoFillService.waitUntilDisconnected;
+import static android.service.autofill.SaveInfo.SAVE_DATA_TYPE_GENERIC;
 import static android.autofillservice.cts.AbstractTimePickerActivity.ID_OUTPUT;
 import static android.autofillservice.cts.AbstractTimePickerActivity.ID_TIME_PICKER;
 
@@ -34,6 +35,7 @@
 import android.icu.util.Calendar;
 import android.view.autofill.AutoFillValue;
 
+import org.junit.After;
 import org.junit.Test;
 
 /**
@@ -44,6 +46,11 @@
 
     protected abstract T getTimePickerActivity();
 
+    @After
+    public void finishWelcomeActivity() {
+        WelcomeActivity.finishIt();
+    }
+
     @Test
     public void testAutoFillAndSave() throws Exception {
         final T activity = getTimePickerActivity();
@@ -55,14 +62,18 @@
 
         // Set expectations.
         final Calendar cal = Calendar.getInstance();
-        cal.set(Calendar.HOUR, 4);
+        cal.set(Calendar.HOUR_OF_DAY, 4);
         cal.set(Calendar.MINUTE, 20);
 
-        replier.addResponse(new CannedDataset.Builder()
-                .setPresentation(createPresentation("Adventure Time"))
-                .setField(ID_OUTPUT, AutoFillValue.forText("Y U NO CHANGE ME?"))
-                .setField(ID_TIME_PICKER, AutoFillValue.forDate(cal.getTimeInMillis()))
+        replier.addResponse(new CannedFillResponse.Builder()
+                .addDataset(new CannedDataset.Builder()
+                    .setPresentation(createPresentation("Adventure Time"))
+                    .setField(ID_OUTPUT, AutoFillValue.forText("Y U NO CHANGE ME?"))
+                    .setField(ID_TIME_PICKER, AutoFillValue.forDate(cal.getTimeInMillis()))
+                    .build())
+                .setSaveType(SAVE_DATA_TYPE_GENERIC)
                 .build());
+
         activity.expectAutoFill("4:20", 4, 20);
 
         // Trigger auto-fill.
@@ -85,7 +96,7 @@
         activity.tapOk();
 
         InstrumentedAutoFillService.setReplier(replier); // Replier was reset onFill()
-        sUiBot.saveForAutofill(true);
+        sUiBot.saveForAutofill(SAVE_DATA_TYPE_GENERIC, true);
         final SaveRequest saveRequest = replier.getNextSaveRequest();
         assertWithMessage("onSave() not called").that(saveRequest).isNotNull();
 
diff --git a/tests/autofillservice/src/android/autofillservice/cts/UiBot.java b/tests/autofillservice/src/android/autofillservice/cts/UiBot.java
index cce0dda..17942b5 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/UiBot.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/UiBot.java
@@ -24,6 +24,7 @@
 import android.app.Instrumentation;
 import android.content.Context;
 import android.content.res.Resources;
+import android.os.SystemClock;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.uiautomator.By;
 import android.support.test.uiautomator.BySelector;
@@ -109,8 +110,22 @@
     /**
      * Asserts the save snackbar is showing and returns it.
      */
-    UiObject2 assertSaveShowing() {
-        return assertSaveShowing(SAVE_DATA_TYPE_GENERIC, null);
+    UiObject2 assertSaveShowing(int type) {
+        return assertSaveShowing(type, null);
+    }
+
+    /**
+     * Asserts the save snackbar is not showing and returns it.
+     */
+    void assertSaveNotShowing(int type) {
+        try {
+            assertSaveShowing(type);
+        } catch (Throwable t) {
+            // TODO(b/33197203): use a more elegant check than catching the expection because it's
+            // not showing (in which case it wouldn't need a type as parameter).
+            return;
+        }
+        throw new AssertionError("snack bar is showing");
     }
 
     UiObject2 assertSaveShowing(int type, String description) {
@@ -163,11 +178,12 @@
     /**
      * Taps an option in the save snackbar.
      *
+     * @param type expected type of save info.
      * @param yesDoIt {@code true} for 'YES', {@code false} for 'NO THANKS'.
      */
-    void saveForAutofill(boolean yesDoIt) {
-        final UiObject2 saveSnackBar = assertSaveShowing();
-        saveForAutofill(saveSnackBar, true);
+    void saveForAutofill(int type, boolean yesDoIt) {
+        final UiObject2 saveSnackBar = assertSaveShowing(type, null);
+        saveForAutofill(saveSnackBar, yesDoIt);
     }
 
     /**
@@ -190,13 +206,17 @@
      * @param selector {@link BySelector} that identifies the object.
      */
     private UiObject2 waitForObject(BySelector selector) {
-        final boolean gotIt = mDevice.wait(Until.hasObject(selector), mTimeout);
-        assertWithMessage("object for '%s' not found in %s ms", selector, mTimeout).that(gotIt)
-                .isTrue();
-
-        final UiObject2 uiObject = mDevice.findObject(selector);
-        assertWithMessage("object for '%s' null in %s ms", selector, mTimeout).that(uiObject)
-                .isNotNull();
-        return uiObject;
+        // NOTE: mDevice.wait does not work for the save snackbar, so we need a polling approach.
+        final int maxTries = 5;
+        final int napTime = mTimeout / maxTries;
+        for (int i = 1; i <= maxTries; i++) {
+            final UiObject2 uiObject = mDevice.findObject(selector);
+            if (uiObject != null) {
+                return uiObject;
+            }
+            SystemClock.sleep(napTime);
+        }
+        throw new AssertionError("Object with selector " + selector + " not found in "
+                + mTimeout + " ms");
     }
 }
diff --git a/tests/autofillservice/src/android/autofillservice/cts/ViewAttributesTest.java b/tests/autofillservice/src/android/autofillservice/cts/ViewAttributesTest.java
index 480464d..b8a7f96 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/ViewAttributesTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/ViewAttributesTest.java
@@ -29,6 +29,8 @@
 import android.view.View;
 import android.view.autofill.AutoFillValue;
 import android.widget.EditText;
+import android.widget.LinearLayout;
+import android.widget.TextView;
 
 import org.junit.Before;
 import org.junit.Rule;
@@ -65,6 +67,29 @@
         mManualContainerInherit = (EditText) mActivity.findViewById(R.id.manualContainerInherit);
     }
 
+    private void runOnUiThreadSync(@NonNull Runnable r) throws InterruptedException {
+        RuntimeException exceptionOnUiThread[] = {null};
+
+        synchronized (this) {
+            mActivity.runOnUiThread(() -> {
+                synchronized (this) {
+                    try {
+                        r.run();
+                    } catch (RuntimeException e) {
+                        exceptionOnUiThread[0] = e;
+                    }
+                    this.notify();
+                }
+            });
+
+            wait();
+        }
+
+        if (exceptionOnUiThread[0] != null) {
+            throw exceptionOnUiThread[0];
+        }
+    }
+
     /**
      * Sets the expectation for an auto-fill request, so it can be asserted through
      * {@link #assertAutoFilled()} later.
@@ -93,11 +118,17 @@
      * @throws Exception If something unexpected happened
      */
     private void checkFieldBehavior(@NonNull EditText field, boolean expectUI) throws Exception {
+        if (expectUI) {
+            assertThat(field.getResolvedAutoFillMode()).isEqualTo(View.AUTO_FILL_MODE_AUTO);
+        } else {
+            assertThat(field.getResolvedAutoFillMode()).isEqualTo(View.AUTO_FILL_MODE_MANUAL);
+        }
+
         // Make sure the requestFocus triggers a change
         if (field == mFirstLevelManual) {
-            mActivity.runOnUiThread(() -> mFirstLevelDefault.requestFocus());
+            runOnUiThreadSync(() -> mFirstLevelDefault.requestFocus());
         } else {
-            mActivity.runOnUiThread(() -> mFirstLevelManual.requestFocus());
+            runOnUiThreadSync(() -> mFirstLevelManual.requestFocus());
         }
 
         enableService();
@@ -123,7 +154,7 @@
 
             expectAutoFill();
 
-            mActivity.runOnUiThread(() -> field.requestFocus());
+            runOnUiThreadSync(() -> field.requestFocus());
 
             Throwable exceptionDuringAutoFillTrigger = null;
             try {
@@ -143,8 +174,6 @@
             } else {
                 assertThat(exceptionDuringAutoFillTrigger).isNotNull();
             }
-
-            waitUntilDisconnected();
         } finally {
             disableService();
         }
@@ -233,17 +262,146 @@
     }
 
     @Test
-    public void checkSet() {
-        View v = mActivity.findViewById(R.id.firstLevelDefault);
-
-        v.setAutoFillMode(View.AUTO_FILL_MODE_MANUAL);
-        assertThat(v.getAutoFillMode()).isEqualTo(View.AUTO_FILL_MODE_MANUAL);
+    public void checkSetAutoFillMode() {
+        mFirstLevelDefault.setAutoFillMode(View.AUTO_FILL_MODE_MANUAL);
+        assertThat(mFirstLevelDefault.getAutoFillMode()).isEqualTo(View.AUTO_FILL_MODE_MANUAL);
     }
 
     @Test
-    public void checkIllegalSet() throws Exception {
-        View v = mActivity.findViewById(R.id.firstLevelDefault);
+    public void checkIllegalAutoFillModeSet() throws Exception {
+        assertThrows(IllegalArgumentException.class, () -> mFirstLevelDefault.setAutoFillMode(-1));
+    }
 
-        assertThrows(IllegalArgumentException.class, () -> v.setAutoFillMode(-1));
+    @Test
+    public void checkTextViewNoHint() {
+        assertThat(mActivity.findViewById(R.id.textViewNoHint).getAutoFillHint()).isEqualTo(
+                View.AUTO_FILL_HINT_NONE);
+    }
+
+    @Test
+    public void checkTextViewHintNone() {
+        assertThat(mActivity.findViewById(R.id.textViewHintNone).getAutoFillHint()).isEqualTo(
+                View.AUTO_FILL_HINT_NONE);
+    }
+
+    @Test
+    public void checkTextViewPassword() {
+        assertThat(mActivity.findViewById(R.id.textViewPassword).getAutoFillHint()).isEqualTo(
+                View.AUTO_FILL_HINT_PASSWORD);
+    }
+
+    @Test
+    public void checkTextViewPhoneName() {
+        assertThat(mActivity.findViewById(R.id.textViewPhoneName).getAutoFillHint()).isEqualTo(
+                View.AUTO_FILL_HINT_PHONE | View.AUTO_FILL_HINT_USERNAME);
+    }
+
+    @Test
+    public void checkSetAutoFill() {
+        View v = mActivity.findViewById(R.id.textViewNoHint);
+
+        v.setAutoFillHint(View.AUTO_FILL_HINT_NONE);
+        assertThat(v.getAutoFillHint()).isEqualTo(View.AUTO_FILL_HINT_NONE);
+
+        v.setAutoFillHint(View.AUTO_FILL_HINT_PASSWORD);
+        assertThat(v.getAutoFillHint()).isEqualTo(View.AUTO_FILL_HINT_PASSWORD);
+
+        v.setAutoFillHint(View.AUTO_FILL_HINT_PASSWORD | View.AUTO_FILL_HINT_EMAIL_ADDRESS);
+        assertThat(v.getAutoFillHint()).isEqualTo(View.AUTO_FILL_HINT_PASSWORD
+                | View.AUTO_FILL_HINT_EMAIL_ADDRESS);
+    }
+
+    @Test
+    public void attachViewToManualContainer() throws Exception {
+        runOnUiThreadSync(() -> mFirstLevelManual.requestFocus());
+        enableService();
+
+        try {
+            View view = new TextView(mActivity);
+
+            view.setLayoutParams(
+                    new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT,
+                            LinearLayout.LayoutParams.WRAP_CONTENT));
+
+            assertThat(view.getAutoFillMode()).isEqualTo(View.AUTO_FILL_MODE_INHERIT);
+            assertThat(view.getResolvedAutoFillMode()).isEqualTo(View.AUTO_FILL_MODE_AUTO);
+
+            // Requesting focus should not trigger any mishaps
+            runOnUiThreadSync(() -> view.requestFocus());
+
+            LinearLayout attachmentPoint = (LinearLayout) mActivity.findViewById(
+                    R.id.manualContainer);
+            runOnUiThreadSync(() -> attachmentPoint.addView(view));
+
+            assertThat(view.getResolvedAutoFillMode()).isEqualTo(View.AUTO_FILL_MODE_MANUAL);
+        } finally {
+            disableService();
+        }
+    }
+
+    @Test
+    public void attachNestedViewToContainer() throws Exception {
+        runOnUiThreadSync(() -> mFirstLevelManual.requestFocus());
+        enableService();
+
+        try {
+            // Create view and viewGroup but do not attach to window
+            LinearLayout container = (LinearLayout) mActivity.getLayoutInflater().inflate(
+                    R.layout.nested_layout, null);
+            EditText field = container.findViewById(R.id.field);
+
+            assertThat(field.getAutoFillMode()).isEqualTo(View.AUTO_FILL_MODE_INHERIT);
+            assertThat(container.getAutoFillMode()).isEqualTo(View.AUTO_FILL_MODE_INHERIT);
+
+            // Resolved mode for detached views should behave as documented
+            assertThat(field.getResolvedAutoFillMode()).isEqualTo(View.AUTO_FILL_MODE_AUTO);
+            assertThat(container.getResolvedAutoFillMode()).isEqualTo(View.AUTO_FILL_MODE_AUTO);
+
+            // Requesting focus should not trigger any mishaps
+            runOnUiThreadSync(() -> field.requestFocus());
+
+            // Set up auto-fill service and response
+            final InstrumentedAutoFillService.Replier replier =
+                    new InstrumentedAutoFillService.Replier();
+            InstrumentedAutoFillService.setReplier(replier);
+
+            replier.addResponse(new CannedFillResponse.Builder()
+                    .addDataset(new CannedFillResponse.CannedDataset.Builder()
+                            .setField("field", AutoFillValue.forText("filled"))
+                            .setPresentation(createPresentation("dataset"))
+                            .build())
+                    .build());
+
+            OneTimeTextWatcher mViewWatcher = new OneTimeTextWatcher("field", field, "filled");
+            field.addTextChangedListener(mViewWatcher);
+
+            // As the focus is set to "field", attaching "container" should trigger an auto-fill
+            // request on "field"
+            LinearLayout attachmentPoint = (LinearLayout) mActivity.findViewById(
+                    R.id.rootContainer);
+            runOnUiThreadSync(() -> attachmentPoint.addView(container));
+
+            // Now the resolved auto-fill modes make sense, hence check them
+            assertThat(field.getResolvedAutoFillMode()).isEqualTo(View.AUTO_FILL_MODE_AUTO);
+            assertThat(container.getResolvedAutoFillMode()).isEqualTo(View.AUTO_FILL_MODE_AUTO);
+
+            // We should now be able to select the data set
+            waitUntilConnected();
+            sUiBot.selectDataset("dataset");
+
+            // Check if auto-fill operation worked
+            mViewWatcher.assertAutoFilled();
+        } finally {
+            disableService();
+        }
+    }
+
+    @Test
+    public void checkSetAutoFillUnknown() {
+        View v = mActivity.findViewById(R.id.textViewNoHint);
+
+        // Unknown values are allowed
+        v.setAutoFillHint(-1);
+        assertThat(v.getAutoFillHint()).isEqualTo(-1);
     }
 }
diff --git a/tests/autofillservice/src/android/autofillservice/cts/ViewAttributesTestActivity.java b/tests/autofillservice/src/android/autofillservice/cts/ViewAttributesTestActivity.java
index 1e1ee72..d9a8cf3 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/ViewAttributesTestActivity.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/ViewAttributesTestActivity.java
@@ -16,15 +16,14 @@
 
 package android.autofillservice.cts;
 
-import android.app.Activity;
 import android.os.Bundle;
 import android.support.annotation.Nullable;
 
-public class ViewAttributesTestActivity extends Activity {
+public class ViewAttributesTestActivity extends AbstractAutoFillActivity {
     @Override
     protected void onCreate(@Nullable Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
 
-        setContentView(R.layout.partially_manually_filled_activity);
+        setContentView(R.layout.view_attribute_test_activity);
     }
 }
diff --git a/tests/autofillservice/src/android/autofillservice/cts/WelcomeActivity.java b/tests/autofillservice/src/android/autofillservice/cts/WelcomeActivity.java
index 3eca661..637966d 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/WelcomeActivity.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/WelcomeActivity.java
@@ -15,7 +15,6 @@
  */
 package android.autofillservice.cts;
 
-import android.app.Activity;
 import android.content.Intent;
 import android.os.Bundle;
 import android.text.TextUtils;
@@ -25,7 +24,9 @@
 /**
  * Activity that displays a "Welcome USER" message after login.
  */
-public class WelcomeActivity extends Activity {
+public class WelcomeActivity extends AbstractAutoFillActivity {
+
+    private static WelcomeActivity sInstance;
 
     private static final String TAG = "WelcomeActivity";
 
@@ -33,6 +34,10 @@
 
     private TextView mOutput;
 
+    public WelcomeActivity() {
+        sInstance = this;
+    }
+
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
@@ -50,4 +55,11 @@
 
         Log.d(TAG, "Output: " + mOutput.getText());
     }
+
+    static void finishIt() {
+        if (sInstance != null) {
+            Log.d(TAG, "So long and thanks for all the fish!");
+            sInstance.finish();
+        }
+    }
 }
diff --git a/tests/camera/Android.mk b/tests/camera/Android.mk
index 8ebca66..af7474e 100644
--- a/tests/camera/Android.mk
+++ b/tests/camera/Android.mk
@@ -62,7 +62,7 @@
 
 LOCAL_PACKAGE_NAME := CtsCameraTestCases
 
-LOCAL_SDK_VERSION := current
+LOCAL_SDK_VERSION := test_current
 
 LOCAL_JAVA_LIBRARIES := android.test.runner
 
diff --git a/tests/camera/src/android/hardware/camera2/cts/CameraDeviceTest.java b/tests/camera/src/android/hardware/camera2/cts/CameraDeviceTest.java
index 965dc1c..d38ddd3 100644
--- a/tests/camera/src/android/hardware/camera2/cts/CameraDeviceTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/CameraDeviceTest.java
@@ -37,6 +37,7 @@
 import android.hardware.camera2.cts.helpers.StaticMetadata;
 import android.hardware.camera2.cts.testcases.Camera2AndroidTestCase;
 import android.hardware.camera2.params.MeteringRectangle;
+import android.hardware.camera2.params.OutputConfiguration;
 import android.media.ImageReader;
 import android.os.Handler;
 import android.os.SystemClock;
@@ -667,6 +668,95 @@
     }
 
     /**
+     * Verify creating a custom session
+     */
+    public void testCreateCustomSession() throws Exception {
+        for (int i = 0; i < mCameraIds.length; i++) {
+            try {
+                openDevice(mCameraIds[i], mCameraMockListener);
+                waitForDeviceState(STATE_OPENED, CAMERA_OPEN_TIMEOUT_MS);
+                if (!mStaticInfo.isColorOutputSupported()) {
+                    Log.i(TAG, "Camera " + mCameraIds[i] +
+                            " does not support color outputs, skipping");
+                    continue;
+                }
+
+                testCreateCustomSessionByCamera(mCameraIds[i]);
+            }
+            finally {
+                closeDevice(mCameraIds[i], mCameraMockListener);
+            }
+        }
+    }
+
+    /**
+     * Verify creating a custom mode session works
+     */
+    private void testCreateCustomSessionByCamera(String cameraId) throws Exception {
+        final int SESSION_TIMEOUT_MS = 1000;
+        final int CAPTURE_TIMEOUT_MS = 3000;
+
+        if (VERBOSE) {
+            Log.v(TAG, "Testing creating custom session for camera " + cameraId);
+        }
+
+        Size yuvSize = mOrderedPreviewSizes.get(0);
+
+        // Create a list of image readers. JPEG for last one and YUV for the rest.
+        ImageReader imageReader = ImageReader.newInstance(yuvSize.getWidth(), yuvSize.getHeight(),
+                ImageFormat.YUV_420_888, /*maxImages*/1);
+
+        try {
+            // Create a normal-mode session via createCustomCaptureSession
+            mSessionMockListener = spy(new BlockingSessionCallback());
+            mSessionWaiter = mSessionMockListener.getStateWaiter();
+            List<OutputConfiguration> outputs = new ArrayList<>();
+            outputs.add(new OutputConfiguration(imageReader.getSurface()));
+            mCamera.createCustomCaptureSession(/*inputConfig*/null, outputs,
+                    CameraDevice.SESSION_OPERATION_MODE_NORMAL, mSessionMockListener, mHandler);
+            mSession = mSessionMockListener.waitAndGetSession(SESSION_CONFIGURE_TIMEOUT_MS);
+
+            // Verify we can capture a frame with the session.
+            SimpleCaptureCallback captureListener = new SimpleCaptureCallback();
+            SimpleImageReaderListener imageListener = new SimpleImageReaderListener();
+            imageReader.setOnImageAvailableListener(imageListener, mHandler);
+
+            CaptureRequest.Builder builder =
+                    mCamera.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
+            builder.addTarget(imageReader.getSurface());
+            CaptureRequest request = builder.build();
+
+            mSession.capture(request, captureListener, mHandler);
+            captureListener.getCaptureResultForRequest(request, CAPTURE_TIMEOUT_MS);
+            imageListener.getImage(CAPTURE_TIMEOUT_MS).close();
+
+            // Create a few invalid custom sessions by using undefined non-vendor mode indices, and
+            // check that they fail to configure
+            mSessionMockListener = spy(new BlockingSessionCallback());
+            mSessionWaiter = mSessionMockListener.getStateWaiter();
+            mCamera.createCustomCaptureSession(/*inputConfig*/null, outputs,
+                    CameraDevice.SESSION_OPERATION_MODE_VENDOR_START - 1, mSessionMockListener, mHandler);
+            mSession = mSessionMockListener.waitAndGetSession(SESSION_CONFIGURE_TIMEOUT_MS);
+            waitForSessionState(BlockingSessionCallback.SESSION_CONFIGURE_FAILED,
+                    SESSION_CONFIGURE_TIMEOUT_MS);
+
+            mSessionMockListener = spy(new BlockingSessionCallback());
+            mCamera.createCustomCaptureSession(/*inputConfig*/null, outputs,
+                    CameraDevice.SESSION_OPERATION_MODE_CONSTRAINED_HIGH_SPEED + 1, mSessionMockListener,
+                    mHandler);
+            mSession = mSessionMockListener.waitAndGetSession(SESSION_CONFIGURE_TIMEOUT_MS);
+            mSessionWaiter = mSessionMockListener.getStateWaiter();
+            waitForSessionState(BlockingSessionCallback.SESSION_CONFIGURE_FAILED,
+                    SESSION_CONFIGURE_TIMEOUT_MS);
+
+        } finally {
+            imageReader.close();
+            mSession.close();
+        }
+    }
+
+
+    /**
      * Verify creating sessions back to back and only the last one is valid for
      * submitting requests.
      */
diff --git a/tests/camera/src/android/hardware/camera2/cts/RobustnessTest.java b/tests/camera/src/android/hardware/camera2/cts/RobustnessTest.java
index e8995ae..a900b84 100644
--- a/tests/camera/src/android/hardware/camera2/cts/RobustnessTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/RobustnessTest.java
@@ -444,13 +444,10 @@
                     continue;
                 }
 
-                int[] availableAfModes = mStaticInfo.getCharacteristics().get(
-                    CameraCharacteristics.CONTROL_AF_AVAILABLE_MODES);
-                int[] availableAeModes = mStaticInfo.getCharacteristics().get(
-                    CameraCharacteristics.CONTROL_AE_AVAILABLE_MODES);
+                int[] availableAfModes = mStaticInfo.getAfAvailableModesChecked();
+                int[] availableAeModes = mStaticInfo.getAeAvailableModesChecked();
 
                 for (int afMode : availableAfModes) {
-
                     if (afMode == CameraCharacteristics.CONTROL_AF_MODE_OFF ||
                             afMode == CameraCharacteristics.CONTROL_AF_MODE_EDOF) {
                         // Only test AF modes that have meaningful trigger behavior
@@ -585,13 +582,10 @@
                     continue;
                 }
 
-                int[] availableAfModes = mStaticInfo.getCharacteristics().get(
-                    CameraCharacteristics.CONTROL_AF_AVAILABLE_MODES);
-                int[] availableAeModes = mStaticInfo.getCharacteristics().get(
-                    CameraCharacteristics.CONTROL_AE_AVAILABLE_MODES);
+                int[] availableAfModes = mStaticInfo.getAfAvailableModesChecked();
+                int[] availableAeModes = mStaticInfo.getAeAvailableModesChecked();
 
                 for (int afMode : availableAfModes) {
-
                     if (afMode == CameraCharacteristics.CONTROL_AF_MODE_OFF ||
                             afMode == CameraCharacteristics.CONTROL_AF_MODE_EDOF) {
                         // Only test AF modes that have meaningful trigger behavior
@@ -689,13 +683,10 @@
                     continue;
                 }
 
-                int[] availableAfModes = mStaticInfo.getCharacteristics().get(
-                    CameraCharacteristics.CONTROL_AF_AVAILABLE_MODES);
-                int[] availableAeModes = mStaticInfo.getCharacteristics().get(
-                    CameraCharacteristics.CONTROL_AE_AVAILABLE_MODES);
+                int[] availableAfModes = mStaticInfo.getAfAvailableModesChecked();
+                int[] availableAeModes = mStaticInfo.getAeAvailableModesChecked();
 
                 for (int afMode : availableAfModes) {
-
                     if (afMode == CameraCharacteristics.CONTROL_AF_MODE_OFF ||
                             afMode == CameraCharacteristics.CONTROL_AF_MODE_EDOF) {
                         // Only test AF modes that have meaningful trigger behavior
@@ -807,13 +798,10 @@
                     continue;
                 }
 
-                int[] availableAfModes = mStaticInfo.getCharacteristics().get(
-                    CameraCharacteristics.CONTROL_AF_AVAILABLE_MODES);
-                int[] availableAeModes = mStaticInfo.getCharacteristics().get(
-                    CameraCharacteristics.CONTROL_AE_AVAILABLE_MODES);
+                int[] availableAfModes = mStaticInfo.getAfAvailableModesChecked();
+                int[] availableAeModes = mStaticInfo.getAeAvailableModesChecked();
 
                 for (int afMode : availableAfModes) {
-
                     if (afMode == CameraCharacteristics.CONTROL_AF_MODE_OFF ||
                             afMode == CameraCharacteristics.CONTROL_AF_MODE_EDOF) {
                         // Only test AF modes that have meaningful trigger behavior
@@ -991,8 +979,8 @@
     private CaptureRequest.Builder prepareTriggerTestSession(
             SurfaceTexture preview, int aeMode, int afMode) throws Exception {
         Log.i(TAG, String.format("Testing AE mode %s, AF mode %s",
-                        StaticMetadata.AE_MODE_NAMES[aeMode],
-                        StaticMetadata.AF_MODE_NAMES[afMode]));
+                        StaticMetadata.getAeModeName(aeMode),
+                        StaticMetadata.getAfModeName(afMode)));
 
         CaptureRequest.Builder previewRequest = preparePreviewTestSession(preview);
         previewRequest.set(CaptureRequest.CONTROL_AE_MODE, aeMode);
@@ -1046,7 +1034,7 @@
                 // After several frames, AF must no longer be in INACTIVE state
                 assertTrue(String.format("In AF mode %s, AF state not PASSIVE_SCAN" +
                                 ", PASSIVE_FOCUSED, or PASSIVE_UNFOCUSED, is %s",
-                                StaticMetadata.AF_MODE_NAMES[afMode],
+                                StaticMetadata.getAfModeName(afMode),
                                 StaticMetadata.AF_STATE_NAMES[afState]),
                         afState == CaptureResult.CONTROL_AF_STATE_PASSIVE_SCAN ||
                         afState == CaptureResult.CONTROL_AF_STATE_PASSIVE_FOCUSED ||
@@ -1067,7 +1055,7 @@
     private boolean verifyAfSequence(int afMode, int afState, boolean focusComplete) {
         if (focusComplete) {
             assertTrue(String.format("AF Mode %s: Focus lock lost after convergence: AF state: %s",
-                            StaticMetadata.AF_MODE_NAMES[afMode],
+                            StaticMetadata.getAfModeName(afMode),
                             StaticMetadata.AF_STATE_NAMES[afState]),
                     afState == CaptureResult.CONTROL_AF_STATE_FOCUSED_LOCKED ||
                     afState ==CaptureResult.CONTROL_AF_STATE_NOT_FOCUSED_LOCKED);
@@ -1075,14 +1063,14 @@
         }
         if (VERBOSE) {
             Log.v(TAG, String.format("AF mode: %s, AF state: %s",
-                            StaticMetadata.AF_MODE_NAMES[afMode],
+                            StaticMetadata.getAfModeName(afMode),
                             StaticMetadata.AF_STATE_NAMES[afState]));
         }
         switch (afMode) {
             case CaptureResult.CONTROL_AF_MODE_AUTO:
             case CaptureResult.CONTROL_AF_MODE_MACRO:
                 assertTrue(String.format("AF mode %s: Unexpected AF state %s",
-                                StaticMetadata.AF_MODE_NAMES[afMode],
+                                StaticMetadata.getAfModeName(afMode),
                                 StaticMetadata.AF_STATE_NAMES[afState]),
                         afState == CaptureResult.CONTROL_AF_STATE_ACTIVE_SCAN ||
                         afState == CaptureResult.CONTROL_AF_STATE_FOCUSED_LOCKED ||
@@ -1092,7 +1080,7 @@
                 break;
             case CaptureResult.CONTROL_AF_MODE_CONTINUOUS_PICTURE:
                 assertTrue(String.format("AF mode %s: Unexpected AF state %s",
-                                StaticMetadata.AF_MODE_NAMES[afMode],
+                                StaticMetadata.getAfModeName(afMode),
                                 StaticMetadata.AF_STATE_NAMES[afState]),
                         afState == CaptureResult.CONTROL_AF_STATE_PASSIVE_SCAN ||
                         afState == CaptureResult.CONTROL_AF_STATE_FOCUSED_LOCKED ||
@@ -1102,14 +1090,14 @@
                 break;
             case CaptureResult.CONTROL_AF_MODE_CONTINUOUS_VIDEO:
                 assertTrue(String.format("AF mode %s: Unexpected AF state %s",
-                                StaticMetadata.AF_MODE_NAMES[afMode],
+                                StaticMetadata.getAfModeName(afMode),
                                 StaticMetadata.AF_STATE_NAMES[afState]),
                         afState == CaptureResult.CONTROL_AF_STATE_FOCUSED_LOCKED ||
                         afState == CaptureResult.CONTROL_AF_STATE_NOT_FOCUSED_LOCKED);
                 focusComplete = true;
                 break;
             default:
-                fail("Unexpected AF mode: " + StaticMetadata.AF_MODE_NAMES[afMode]);
+                fail("Unexpected AF mode: " + StaticMetadata.getAfModeName(afMode));
         }
         return focusComplete;
     }
diff --git a/tests/camera/utils/src/android/hardware/camera2/cts/helpers/StaticMetadata.java b/tests/camera/utils/src/android/hardware/camera2/cts/helpers/StaticMetadata.java
index dea3cfd..319ec91 100644
--- a/tests/camera/utils/src/android/hardware/camera2/cts/helpers/StaticMetadata.java
+++ b/tests/camera/utils/src/android/hardware/camera2/cts/helpers/StaticMetadata.java
@@ -73,7 +73,7 @@
     private final CheckLevel mLevel;
     private final CameraErrorCollector mCollector;
 
-    // Index with android.control.aeMode
+    // Access via getAeModeName() to account for vendor extensions
     public static final String[] AE_MODE_NAMES = new String[] {
         "AE_MODE_OFF",
         "AE_MODE_ON",
@@ -82,7 +82,7 @@
         "AE_MODE_ON_AUTO_FLASH_REDEYE"
     };
 
-    // Index with android.control.afMode
+    // Access via getAfModeName() to account for vendor extensions
     public static final String[] AF_MODE_NAMES = new String[] {
         "AF_MODE_OFF",
         "AF_MODE_AUTO",
@@ -434,6 +434,16 @@
         return calibration;
     }
 
+    public static String getAeModeName(int aeMode) {
+        return (aeMode >= AE_MODE_NAMES.length) ? String.format("VENDOR_AE_MODE_%d", aeMode) :
+                AE_MODE_NAMES[aeMode];
+    }
+
+    public static String getAfModeName(int afMode) {
+        return (afMode >= AF_MODE_NAMES.length) ? String.format("VENDOR_AF_MODE_%d", afMode) :
+                AF_MODE_NAMES[afMode];
+    }
+
     /**
      * Get max AE regions and do sanity check.
      *
@@ -1139,9 +1149,16 @@
         }
         List<Integer> modeList = new ArrayList<Integer>();
         for (int mode : modes) {
-            modeList.add(mode);
+            // Skip vendor-added modes
+            if (mode <= CameraMetadata.CONTROL_AE_MODE_ON_AUTO_FLASH_REDEYE) {
+                modeList.add(mode);
+            }
         }
         checkTrueForKey(modesKey, "value is empty", !modeList.isEmpty());
+        modes = new int[modeList.size()];
+        for (int i = 0; i < modeList.size(); i++) {
+            modes[i] = modeList.get(i);
+        }
 
         // All camera device must support ON
         checkTrueForKey(modesKey, "values " + modeList.toString() + " must contain ON mode",
@@ -1227,7 +1244,17 @@
             return new int[0];
         }
 
-        List<Integer> modesList = Arrays.asList(CameraTestUtils.toObject(afModes));
+        List<Integer> modesList = new ArrayList<Integer>();
+        for (int afMode : afModes) {
+            // Skip vendor-added AF modes
+            if (afMode > CameraCharacteristics.CONTROL_AF_MODE_EDOF) continue;
+            modesList.add(afMode);
+        }
+        afModes = new int[modesList.size()];
+        for (int i = 0; i < modesList.size(); i++) {
+            afModes[i] = modesList.get(i);
+        }
+
         if (isHardwareLevelAtLeastLimited()) {
             // Some LEGACY mode devices do not support AF OFF
             checkTrueForKey(key, " All camera devices must support OFF mode",
diff --git a/tests/fragment/AndroidManifest.xml b/tests/fragment/AndroidManifest.xml
index 05027b5..ecbd39b 100644
--- a/tests/fragment/AndroidManifest.xml
+++ b/tests/fragment/AndroidManifest.xml
@@ -27,6 +27,7 @@
                 <category android:name="android.intent.category.LAUNCHER" />
             </intent-filter>
         </activity>
+        <activity android:name=".LoaderActivity"/>
     </application>
 
     <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
diff --git a/tests/fragment/src/android/fragment/cts/FragmentTestUtil.java b/tests/fragment/src/android/fragment/cts/FragmentTestUtil.java
index b1d7411..5547946 100644
--- a/tests/fragment/src/android/fragment/cts/FragmentTestUtil.java
+++ b/tests/fragment/src/android/fragment/cts/FragmentTestUtil.java
@@ -32,6 +32,9 @@
 import android.view.ViewGroup;
 import android.view.accessibility.AccessibilityNodeInfo;
 
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+
 public class FragmentTestUtil {
     public static void waitForExecution(final ActivityTestRule<FragmentTestActivity> rule) {
         // Wait for two cycles. When starting a postponed transition, it will post to
@@ -43,7 +46,7 @@
         instrumentation.runOnMainSync(() -> {});
     }
 
-    private static void runOnUiThreadRethrow(ActivityTestRule<FragmentTestActivity> rule,
+    private static void runOnUiThreadRethrow(ActivityTestRule<? extends Activity> rule,
             Runnable r) {
         if (Looper.getMainLooper() == Looper.myLooper()) {
             r.run();
@@ -57,12 +60,12 @@
     }
 
     public static boolean executePendingTransactions(
-            final ActivityTestRule<FragmentTestActivity> rule) {
+            final ActivityTestRule<? extends Activity> rule) {
         return executePendingTransactions(rule, rule.getActivity().getFragmentManager());
     }
 
     public static boolean executePendingTransactions(
-            final ActivityTestRule<FragmentTestActivity> rule, final FragmentManager fm) {
+            final ActivityTestRule<? extends Activity> rule, final FragmentManager fm) {
         final boolean[] ret = new boolean[1];
         runOnUiThreadRethrow(rule, new Runnable() {
             @Override
@@ -197,4 +200,21 @@
         accessibilityNodeInfo.recycle();
         return isVisible;
     }
+
+    /**
+     * Allocates until a garbage collection occurs.
+     */
+    public static void forceGC() {
+        // Do it twice so that we know we're not in the middle of the first collection when
+        // returning.
+        for (int i = 0; i < 2; i++) {
+            // Use a random index in the list to detect the garbage collection each time because
+            // .get() may accidentally trigger a strong reference during collection.
+            ArrayList<WeakReference<byte[]>> leak = new ArrayList<>();
+            do {
+                WeakReference<byte[]> arr = new WeakReference<byte[]>(new byte[100]);
+                leak.add(arr);
+            } while (leak.get((int) (Math.random() * leak.size())).get() != null);
+        }
+    }
 }
diff --git a/tests/fragment/src/android/fragment/cts/LoaderActivity.java b/tests/fragment/src/android/fragment/cts/LoaderActivity.java
new file mode 100644
index 0000000..bd5a1cc
--- /dev/null
+++ b/tests/fragment/src/android/fragment/cts/LoaderActivity.java
@@ -0,0 +1,93 @@
+/*
+ * 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.fragment.cts;
+
+import android.app.Activity;
+import android.app.LoaderManager;
+import android.content.AsyncTaskLoader;
+import android.content.Context;
+import android.content.Loader;
+import android.os.Bundle;
+import android.widget.TextView;
+
+import java.util.concurrent.CountDownLatch;
+
+/**
+ * This Activity sets the text when loading completes. It also tracks the Activity in
+ * a static variable, so it must be cleared in test tear down.
+ */
+public class LoaderActivity extends Activity {
+    // These must be cleared after each test using clearState()
+    public static LoaderActivity sActivity;
+    public static CountDownLatch sResumed;
+
+    public TextView textView;
+
+    public static void clearState() {
+        sActivity = null;
+        sResumed = null;
+    }
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        sActivity = this;
+
+        setContentView(R.layout.text_a);
+        textView = (TextView) findViewById(R.id.textA);
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+        getLoaderManager().initLoader(0, null, new TextLoaderCallback());
+        if (sResumed != null) {
+            sResumed.countDown();
+        }
+    }
+
+    class TextLoaderCallback implements LoaderManager.LoaderCallbacks<String> {
+        @Override
+        public Loader<String> onCreateLoader(int id, Bundle args) {
+            return new TextLoader(LoaderActivity.this);
+        }
+
+        @Override
+        public void onLoadFinished(Loader<String> loader, String data) {
+            textView.setText(data);
+        }
+
+        @Override
+        public void onLoaderReset(Loader<String> loader) {
+        }
+    }
+
+    static class TextLoader extends AsyncTaskLoader<String> {
+        TextLoader(Context context) {
+            super(context);
+        }
+
+        @Override
+        protected void onStartLoading() {
+            forceLoad();
+        }
+
+        @Override
+        public String loadInBackground() {
+            return "Loaded!";
+        }
+    }
+}
diff --git a/tests/fragment/src/android/fragment/cts/LoaderTest.java b/tests/fragment/src/android/fragment/cts/LoaderTest.java
new file mode 100755
index 0000000..9e3097c
--- /dev/null
+++ b/tests/fragment/src/android/fragment/cts/LoaderTest.java
@@ -0,0 +1,176 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.fragment.cts;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import android.app.Fragment;
+import android.app.FragmentManager;
+import android.app.Instrumentation;
+import android.app.LoaderManager;
+import android.content.Context;
+import android.content.Intent;
+import android.content.Loader;
+import android.content.pm.ActivityInfo;
+import android.content.res.Configuration;
+import android.os.Bundle;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.MediumTest;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.After;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.lang.ref.WeakReference;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+@MediumTest
+@RunWith(AndroidJUnit4.class)
+public class LoaderTest {
+    @Rule
+    public ActivityTestRule<LoaderActivity> mActivityRule =
+            new ActivityTestRule<>(LoaderActivity.class);
+
+    @After
+    public void clearActivity() {
+        LoaderActivity.clearState();
+    }
+
+    /**
+     * Test to ensure that there is no Activity leak due to Loader
+     */
+    @Test
+    public void testLeak() throws Throwable {
+        Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
+        Intent intent = new Intent(mActivityRule.getActivity(), LoaderActivity.class);
+        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        LoaderActivity.sResumed = new CountDownLatch(1);
+        instrumentation.startActivitySync(intent);
+        assertTrue(LoaderActivity.sResumed.await(1, TimeUnit.SECONDS));
+
+        LoaderFragment fragment = new LoaderFragment();
+        FragmentManager fm = LoaderActivity.sActivity.getFragmentManager();
+
+        fm.beginTransaction()
+                .add(fragment, "1")
+                .commit();
+
+        FragmentTestUtil.executePendingTransactions(mActivityRule, fm);
+
+        fm.beginTransaction()
+                .remove(fragment)
+                .addToBackStack(null)
+                .commit();
+
+        FragmentTestUtil.executePendingTransactions(mActivityRule, fm);
+
+        WeakReference<LoaderActivity> weakActivity = new WeakReference(LoaderActivity.sActivity);
+
+        if (!switchOrientation()) {
+            return; // can't switch orientation for square screens
+        }
+
+        // Now force a garbage collection.
+        FragmentTestUtil.forceGC();
+        assertNull(weakActivity.get());
+    }
+
+    /**
+     * When a LoaderManager is reused, it should notify in onResume
+     */
+    @Test
+    public void startWhenReused() throws Throwable {
+        LoaderActivity activity = mActivityRule.getActivity();
+
+        assertEquals("Loaded!", activity.textView.getText().toString());
+
+        if (!switchOrientation()) {
+            return; // can't switch orientation for square screens
+        }
+
+        // After orientation change, the text should still be loaded properly
+        activity = LoaderActivity.sActivity;
+        assertEquals("Loaded!", activity.textView.getText().toString());
+    }
+
+    private boolean switchOrientation() throws InterruptedException {
+        LoaderActivity activity = LoaderActivity.sActivity;
+
+        int currentOrientation = activity.getResources().getConfiguration().orientation;
+
+        int nextOrientation;
+        if (currentOrientation == Configuration.ORIENTATION_LANDSCAPE) {
+            nextOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
+        } else if (currentOrientation == Configuration.ORIENTATION_PORTRAIT) {
+            nextOrientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
+        } else {
+            return false; // Don't know what to do with square or unknown orientations
+        }
+
+        // Now switch the orientation
+        LoaderActivity.sResumed = new CountDownLatch(1);
+
+        activity.setRequestedOrientation(nextOrientation);
+        assertTrue(LoaderActivity.sResumed.await(1, TimeUnit.SECONDS));
+        return true;
+    }
+
+
+    public static class LoaderFragment extends Fragment {
+        private static final int LOADER_ID = 1;
+        private final LoaderManager.LoaderCallbacks<Boolean> mLoaderCallbacks =
+                new LoaderManager.LoaderCallbacks<Boolean>() {
+                    @Override
+                    public Loader<Boolean> onCreateLoader(int id, Bundle args) {
+                        return new DummyLoader(getContext());
+                    }
+
+                    @Override
+                    public void onLoadFinished(Loader<Boolean> loader, Boolean data) {
+
+                    }
+
+                    @Override
+                    public void onLoaderReset(Loader<Boolean> loader) {
+
+                    }
+                };
+
+        @Override
+        public void onActivityCreated(Bundle savedInstanceState) {
+            super.onActivityCreated(savedInstanceState);
+
+            getLoaderManager().initLoader(LOADER_ID, null, mLoaderCallbacks);
+        }
+    }
+
+    static class DummyLoader extends Loader<Boolean> {
+        DummyLoader(Context context) {
+            super(context);
+        }
+
+        @Override
+        protected void onStartLoading() {
+            deliverResult(true);
+        }
+    }
+}
diff --git a/tests/sensor/Android.mk b/tests/sensor/Android.mk
index 08f88af..d275b1f 100644
--- a/tests/sensor/Android.mk
+++ b/tests/sensor/Android.mk
@@ -48,8 +48,10 @@
 
 LOCAL_SRC_FILES := \
     jni/SensorTest.cpp \
-    jni/android_view_cts_SensorNativeTest.cpp \
-    jni/nativeTestHelper.cpp \
+    jni/SensorTestCases.cpp \
+    jni/android_hardware_cts_SensorDirectReportTest.cpp \
+    jni/android_hardware_cts_SensorNativeTest.cpp \
+    jni/nativeTestHelper.cpp
 
 LOCAL_C_INCLUDES := $(JNI_H_INCLUDE)
 
diff --git a/tests/sensor/jni/SensorTest.cpp b/tests/sensor/jni/SensorTest.cpp
index 3593047..eb2bfe8 100644
--- a/tests/sensor/jni/SensorTest.cpp
+++ b/tests/sensor/jni/SensorTest.cpp
@@ -35,10 +35,6 @@
     }
 }
 
-void SensorTest::testInitialized(JNIEnv *env) {
-    ASSERT_TRUE(mManager->isValid());
-}
-
 TestSensorManager::TestSensorManager(const char *package) {
     mManager = ASensorManager_getInstanceForPackage(package);
 }
@@ -162,6 +158,30 @@
             success = true;
             break;
         }
+        case ASENSOR_DIRECT_CHANNEL_TYPE_HARDWARE_BUFFER: {
+            AHardwareBuffer_Desc desc = {
+                .width = static_cast<uint32_t>(size),
+                .height = 1,
+                .layers = 1,
+                .usage0 = AHARDWAREBUFFER_USAGE0_SENSOR_DIRECT_DATA
+                        | AHARDWAREBUFFER_USAGE0_CPU_READ_OFTEN,
+                .usage1 = 0,
+                .format = AHARDWAREBUFFER_FORMAT_BLOB
+            };
+
+            // allocate
+            if (AHardwareBuffer_allocate(&desc, &mHardwareBuffer) == 0) {
+                // lock
+                if (AHardwareBuffer_lock(mHardwareBuffer, AHARDWAREBUFFER_USAGE0_CPU_READ,
+                                         -1, nullptr, reinterpret_cast<void **>(&mBuffer)) == 0) {
+                    if (mBuffer != nullptr) {
+                        mSize = size;
+                        success = true;
+                    }
+                }
+            }
+            break;
+        }
         default:
             break;
     }
@@ -189,10 +209,23 @@
             mSize = 0;
             break;
         }
+        case ASENSOR_DIRECT_CHANNEL_TYPE_HARDWARE_BUFFER: {
+            if (mHardwareBuffer != nullptr) {
+                if (mBuffer != nullptr) {
+                    int32_t fence = -1;
+                    AHardwareBuffer_unlock(mHardwareBuffer, &fence);
+                    mBuffer = nullptr;
+                }
+                AHardwareBuffer_release(mHardwareBuffer);
+                mHardwareBuffer = nullptr;
+            }
+            mSize = 0;
+            break;
+        }
         default:
             break;
     }
-    if (mSharedMemoryFd > 0 || mSize != 0 || mBuffer != nullptr) {
+    if (mSharedMemoryFd > 0 || mSize != 0 || mBuffer != nullptr || mHardwareBuffer != nullptr) {
         ALOGE("TestSharedMemory %p not properly destructed: "
               "type %d, shared_memory_fd %d, hardware_buffer %p, size %zu, buffer %p",
               this, static_cast<int>(mType), mSharedMemoryFd, mHardwareBuffer, mSize, mBuffer);
@@ -212,69 +245,5 @@
     }
     return m;
 }
-
-// Test direct report of gyroscope at normal rate level through ashmem direct channel
-void SensorTest::testGyroscopeSharedMemoryDirectReport(JNIEnv* env) {
-    constexpr int type = ASENSOR_TYPE_GYROSCOPE;
-    constexpr size_t kEventSize = sizeof(ASensorEvent);
-    constexpr size_t kNEvent = 500;
-    constexpr size_t kMemSize = kEventSize * kNEvent;
-
-    TestSensor sensor = mManager->getDefaultSensor(type);
-
-    if (sensor.getHighestDirectReportRateLevel() == ASENSOR_DIRECT_RATE_STOP
-        || !sensor.isDirectChannelTypeSupported(ASENSOR_DIRECT_CHANNEL_TYPE_SHARED_MEMORY)) {
-        // does not declare support
-        return;
-    }
-
-    std::unique_ptr<TestSharedMemory>
-            mem(TestSharedMemory::create(ASENSOR_DIRECT_CHANNEL_TYPE_SHARED_MEMORY, kMemSize));
-    ASSERT_NE(mem, nullptr);
-    ASSERT_NE(mem->getBuffer(), nullptr);
-    ASSERT_GT(mem->getSharedMemoryFd(), 0);
-
-    char* buffer = mem->getBuffer();
-    // fill memory with data
-    for (size_t i = 0; i < kMemSize; ++i) {
-        buffer[i] = '\xcc';
-    }
-
-    int32_t channel;
-    channel = mManager->createDirectChannel(*mem);
-    ASSERT_GT(channel, 0);
-
-    // check memory is zeroed
-    for (size_t i = 0; i < kMemSize; ++i) {
-        ASSERT_EQ(buffer[i], '\0');
-    }
-
-    int32_t eventToken;
-    eventToken = mManager->configureDirectReport(sensor, channel, ASENSOR_DIRECT_RATE_NORMAL);
-    usleep(1500000); // sleep 1 sec for data, plus 0.5 sec for initialization
-    auto events = mem->parseEvents();
-
-    // allowed to be between 55% and 220% of nominal freq (50Hz)
-    ASSERT_GT(events.size(), 50 / 2);
-    ASSERT_LT(events.size(), static_cast<size_t>(110*1.5));
-
-    int64_t lastTimestamp = 0;
-    for (auto &e : events) {
-        ASSERT_EQ(e.type, type);
-        ASSERT_EQ(e.sensor, eventToken);
-        ASSERT_GT(e.timestamp, lastTimestamp);
-
-        ASensorVector &gyro = e.vector;
-        double gyroNorm = std::sqrt(gyro.x * gyro.x + gyro.y * gyro.y + gyro.z * gyro.z);
-        // assert not drifting
-        ASSERT_TRUE(gyroNorm < 0.1);  // < ~5 degree/sa
-
-        lastTimestamp = e.timestamp;
-    }
-
-    // stop sensor and unregister channel
-    mManager->configureDirectReport(sensor, channel, ASENSOR_DIRECT_RATE_STOP);
-    mManager->destroyDirectChannel(channel);
-}
 } // namespace SensorTest
 } // namespace android
diff --git a/tests/sensor/jni/SensorTest.h b/tests/sensor/jni/SensorTest.h
index 5f8d703..7abd1e9 100644
--- a/tests/sensor/jni/SensorTest.h
+++ b/tests/sensor/jni/SensorTest.h
@@ -42,7 +42,9 @@
 
     // tests
     void testInitialized(JNIEnv *env);
-    void testGyroscopeSharedMemoryDirectReport(JNIEnv *env);
+    void testInvalidParameter(JNIEnv *env);
+    void testDirectReport(JNIEnv *env, int32_t sensorType, int32_t channelType, int32_t rateLevel);
+
 private:
     std::unique_ptr<TestSensorManager> mManager;
 };
diff --git a/tests/sensor/jni/SensorTestCases.cpp b/tests/sensor/jni/SensorTestCases.cpp
new file mode 100644
index 0000000..713eabf
--- /dev/null
+++ b/tests/sensor/jni/SensorTestCases.cpp
@@ -0,0 +1,244 @@
+/*
+ * 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 "SensorTest.h"
+#include <errno.h>
+
+namespace android {
+namespace SensorTest {
+
+// Test if test environment is correctly initialized
+void SensorTest::testInitialized(JNIEnv *env) {
+    ASSERT_TRUE(mManager->isValid());
+}
+
+// Test if invalid parameter cases are handled correctly
+void SensorTest::testInvalidParameter(JNIEnv *env) {
+    ASensorList dummyList;
+    ASSERT_EQ(ASensorManager_getSensorList(nullptr, nullptr), -EINVAL);
+    ASSERT_EQ(ASensorManager_getSensorList(nullptr, &dummyList), -EINVAL);
+
+    ASSERT_EQ(ASensorManager_getDefaultSensor(nullptr, ASENSOR_TYPE_ACCELEROMETER), nullptr);
+
+    ASSERT_EQ(ASensorManager_getDefaultSensorEx(
+            nullptr, ASENSOR_TYPE_ACCELEROMETER, false), nullptr);
+
+    ALooper *nonNullLooper = reinterpret_cast<ALooper *>(1);
+    ASensorManager *nonNullManager = reinterpret_cast<ASensorManager *>(1);
+    ASSERT_EQ(ASensorManager_createEventQueue(nullptr, nullptr, 0, nullptr, nullptr), nullptr);
+    ASSERT_EQ(ASensorManager_createEventQueue(
+            nullptr, nonNullLooper, 0, nullptr, nullptr), nullptr);
+    ASSERT_EQ(ASensorManager_createEventQueue(
+            nonNullManager, nullptr, 0, nullptr, nullptr), nullptr);
+
+    ASensorEventQueue *nonNullQueue = reinterpret_cast<ASensorEventQueue *>(1);
+    ASSERT_EQ(ASensorManager_destroyEventQueue(nullptr, nullptr), -EINVAL);
+    ASSERT_EQ(ASensorManager_destroyEventQueue(nullptr, nonNullQueue), -EINVAL);
+    ASSERT_EQ(ASensorManager_destroyEventQueue(nonNullManager, nullptr), -EINVAL);
+
+    int fakeValidFd = 1;
+    int invalidFd = -1;
+    ASSERT_EQ(ASensorManager_createSharedMemoryDirectChannel(
+            nullptr, fakeValidFd, sizeof(ASensorEvent)), -EINVAL);
+    ASSERT_EQ(ASensorManager_createSharedMemoryDirectChannel(
+            nonNullManager, invalidFd, sizeof(ASensorEvent)), -EINVAL);
+    ASSERT_EQ(ASensorManager_createSharedMemoryDirectChannel(
+            nonNullManager, fakeValidFd, sizeof(ASensorEvent) - 1), -EINVAL);
+    ASSERT_EQ(ASensorManager_createSharedMemoryDirectChannel(
+            nonNullManager, fakeValidFd, 0), -EINVAL);
+
+    AHardwareBuffer *nonNullHardwareBuffer = reinterpret_cast<AHardwareBuffer *>(1);
+    ASSERT_EQ(ASensorManager_createHardwareBufferDirectChannel(
+            nullptr, nonNullHardwareBuffer, sizeof(ASensorEvent)), -EINVAL);
+    ASSERT_EQ(ASensorManager_createHardwareBufferDirectChannel(
+            nonNullManager, nullptr, sizeof(ASensorEvent)), -EINVAL);
+    ASSERT_EQ(ASensorManager_createHardwareBufferDirectChannel(
+            nonNullManager, nonNullHardwareBuffer, sizeof(ASensorEvent) - 1), -EINVAL);
+    ASSERT_EQ(ASensorManager_createHardwareBufferDirectChannel(
+            nonNullManager, nonNullHardwareBuffer, 0), -EINVAL);
+
+    // no return value to test, but call this to test if it will crash
+    ASensorManager_destroyDirectChannel(nullptr, 1);
+
+    ASensor *nonNullSensor = reinterpret_cast<ASensor *>(1);
+    ASSERT_EQ(ASensorManager_configureDirectReport(
+            nullptr, nullptr, 1, ASENSOR_DIRECT_RATE_NORMAL), -EINVAL);
+    ASSERT_EQ(ASensorManager_configureDirectReport(
+            nullptr, nonNullSensor, 1, ASENSOR_DIRECT_RATE_NORMAL), -EINVAL);
+    ASSERT_EQ(ASensorManager_configureDirectReport(
+            nullptr, nonNullSensor, 1, ASENSOR_DIRECT_RATE_STOP), -EINVAL);
+    ASSERT_EQ(ASensorManager_configureDirectReport(
+            nonNullManager, nullptr, 1, ASENSOR_DIRECT_RATE_NORMAL), -EINVAL);
+
+    ASSERT_EQ(ASensorEventQueue_registerSensor(nullptr, nullptr, 1, 1), -EINVAL);
+    ASSERT_EQ(ASensorEventQueue_registerSensor(nullptr, nonNullSensor, 1, 1), -EINVAL);
+    ASSERT_EQ(ASensorEventQueue_registerSensor(nonNullQueue, nullptr, 1, 1), -EINVAL);
+    ASSERT_EQ(ASensorEventQueue_registerSensor(nonNullQueue, nonNullSensor, -1, 1), -EINVAL);
+    ASSERT_EQ(ASensorEventQueue_registerSensor(nonNullQueue, nonNullSensor, 1, -1), -EINVAL);
+    ASSERT_EQ(ASensorEventQueue_registerSensor(nonNullQueue, nonNullSensor, -1, -1), -EINVAL);
+
+    ASSERT_EQ(ASensorEventQueue_enableSensor(nullptr, nullptr), -EINVAL);
+    ASSERT_EQ(ASensorEventQueue_enableSensor(nullptr, nonNullSensor), -EINVAL);
+    ASSERT_EQ(ASensorEventQueue_enableSensor(nonNullQueue, nullptr), -EINVAL);
+
+    ASSERT_EQ(ASensorEventQueue_disableSensor(nullptr, nullptr), -EINVAL);
+    ASSERT_EQ(ASensorEventQueue_disableSensor(nullptr, nonNullSensor), -EINVAL);
+    ASSERT_EQ(ASensorEventQueue_disableSensor(nonNullQueue, nullptr), -EINVAL);
+
+    ASSERT_EQ(ASensorEventQueue_setEventRate(nullptr, nullptr, 1), -EINVAL);
+    ASSERT_EQ(ASensorEventQueue_setEventRate(nullptr, nonNullSensor, 1), -EINVAL);
+    ASSERT_EQ(ASensorEventQueue_setEventRate(nonNullQueue, nullptr, 1), -EINVAL);
+    ASSERT_EQ(ASensorEventQueue_setEventRate(nonNullQueue, nonNullSensor, -1), -EINVAL);
+
+    ASSERT_EQ(ASensorEventQueue_hasEvents(nullptr), -EINVAL);
+
+    ASensorEvent event;
+    ASensorEvent *nonNullEvent = &event;
+    ASSERT_EQ(ASensorEventQueue_getEvents(nullptr, nullptr, 1), -EINVAL)
+    ASSERT_EQ(ASensorEventQueue_getEvents(nullptr, nullptr, 0), -EINVAL)
+    ASSERT_EQ(ASensorEventQueue_getEvents(nullptr, nonNullEvent, 1), -EINVAL)
+    ASSERT_EQ(ASensorEventQueue_getEvents(nullptr, nonNullEvent, 0), -EINVAL);
+    ASSERT_EQ(ASensorEventQueue_getEvents(nonNullQueue, nullptr, 1), -EINVAL)
+    ASSERT_EQ(ASensorEventQueue_getEvents(nonNullQueue, nullptr, 0), -EINVAL);
+
+    ASSERT_EMPTY_CSTR(ASensor_getName(nullptr));
+    ASSERT_EMPTY_CSTR(ASensor_getVendor(nullptr));
+    ASSERT_EQ(ASensor_getType(nullptr), -1);
+    ASSERT_EQ(ASensor_getResolution(nullptr), -1.f);
+    ASSERT_EQ(ASensor_getMinDelay(nullptr), -1);
+    ASSERT_EQ(ASensor_getFifoMaxEventCount(nullptr), -1);
+    ASSERT_EQ(ASensor_getFifoReservedEventCount(nullptr), -1);
+    ASSERT_EMPTY_CSTR(ASensor_getStringType(nullptr));
+    ASSERT_EQ(ASensor_getReportingMode(nullptr), -1);
+    ASSERT_EQ(ASensor_isWakeUpSensor(nullptr), false);
+    ASSERT_EQ(ASensor_isDirectChannelTypeSupported(
+            nullptr, ASENSOR_DIRECT_CHANNEL_TYPE_SHARED_MEMORY), false);
+    ASSERT_EQ(ASensor_isDirectChannelTypeSupported(
+            nullptr, ASENSOR_DIRECT_CHANNEL_TYPE_HARDWARE_BUFFER), false);
+    ASSERT_EQ(ASensor_getHighestDirectReportRateLevel(nullptr), ASENSOR_DIRECT_RATE_STOP);
+}
+
+// Test sensor direct report functionality
+void SensorTest::testDirectReport(JNIEnv* env, int32_t sensorType, int32_t channelType, int32_t rateLevel) {
+    constexpr size_t kEventSize = sizeof(ASensorEvent);
+    constexpr size_t kNEvent = 500;
+    constexpr size_t kMemSize = kEventSize * kNEvent;
+
+    // value check criterion
+    constexpr float GRAVITY_MIN = 9.81f - 0.5f;
+    constexpr float GRAVITY_MAX = 9.81f + 0.5f;
+    constexpr float GYRO_MAX = 0.1f; // ~5 dps
+
+    constexpr float RATE_NORMAL_NOMINAL = 50;
+    constexpr float RATE_FAST_NOMINAL = 200;
+    constexpr float RATE_VERY_FAST_NOMINAL = 800;
+
+    TestSensor sensor = mManager->getDefaultSensor(sensorType);
+    if (!sensor.isValid()
+        || sensor.getHighestDirectReportRateLevel() < rateLevel
+        || !sensor.isDirectChannelTypeSupported(channelType)) {
+        // no sensor of type sensorType or it does not declare support of channelType or rateLevel
+        return;
+    }
+
+    std::unique_ptr<TestSharedMemory> mem(TestSharedMemory::create(channelType, kMemSize));
+    ASSERT_NE(mem, nullptr);
+    ASSERT_NE(mem->getBuffer(), nullptr);
+    switch (channelType) {
+        case ASENSOR_DIRECT_CHANNEL_TYPE_SHARED_MEMORY:
+            ASSERT_GT(mem->getSharedMemoryFd(), 0);
+            break;
+        case ASENSOR_DIRECT_CHANNEL_TYPE_HARDWARE_BUFFER:
+            ASSERT_NOT_NULL(mem->getHardwareBuffer());
+            break;
+    }
+
+    char* buffer = mem->getBuffer();
+    // fill memory with data
+    for (size_t i = 0; i < kMemSize; ++i) {
+        buffer[i] = '\xcc';
+    }
+
+    int32_t channel;
+    channel = mManager->createDirectChannel(*mem);
+    ASSERT_GT(channel, 0);
+
+    // check memory is zeroed
+    for (size_t i = 0; i < kMemSize; ++i) {
+        ASSERT_EQ(buffer[i], '\0');
+    }
+
+    int32_t eventToken;
+    eventToken = mManager->configureDirectReport(sensor, channel, rateLevel);
+    usleep(1500000); // sleep 1 sec for data, plus 0.5 sec for initialization
+    auto events = mem->parseEvents();
+
+    // find norminal rate
+    float nominalFreq = 0.f;
+    float nominalTestTimeSec = 1.f;
+    float maxTestTimeSec = 1.5f;
+    switch (rateLevel) {
+        case ASENSOR_DIRECT_RATE_NORMAL:
+            nominalFreq = RATE_NORMAL_NOMINAL;
+            break;
+        case ASENSOR_DIRECT_RATE_FAST:
+            nominalFreq = RATE_FAST_NOMINAL;
+            break;
+        case ASENSOR_DIRECT_RATE_VERY_FAST:
+            nominalFreq = RATE_VERY_FAST_NOMINAL;
+            break;
+    }
+
+    // allowed to be between 55% and 220% of nominal freq
+    ASSERT_GT(events.size(), static_cast<size_t>(nominalFreq * 0.55f * nominalTestTimeSec));
+    ASSERT_LT(events.size(), static_cast<size_t>(nominalFreq * 2.2f * maxTestTimeSec));
+
+    int64_t lastTimestamp = 0;
+    for (auto &e : events) {
+        ASSERT_EQ(e.type, sensorType);
+        ASSERT_EQ(e.sensor, eventToken);
+        ASSERT_GT(e.timestamp, lastTimestamp);
+
+        // type specific value check
+        switch(sensorType) {
+            case ASENSOR_TYPE_ACCELEROMETER: {
+                ASensorVector &acc = e.vector;
+                double accNorm = std::sqrt(acc.x * acc.x + acc.y * acc.y + acc.z * acc.z);
+                if (accNorm > GRAVITY_MAX || accNorm < GRAVITY_MIN) {
+                    ALOGE("Gravity norm = %f", accNorm);
+                }
+                ASSERT_GE(accNorm, GRAVITY_MIN);
+                ASSERT_LE(accNorm, GRAVITY_MAX);
+                break;
+            }
+            case ASENSOR_TYPE_GYROSCOPE: {
+                ASensorVector &gyro = e.vector;
+                double gyroNorm = std::sqrt(gyro.x * gyro.x + gyro.y * gyro.y + gyro.z * gyro.z);
+                // assert not drifting
+                ASSERT_LE(gyroNorm, GYRO_MAX);  // < ~2.5 degree/s
+                break;
+            }
+        }
+
+        lastTimestamp = e.timestamp;
+    }
+
+    // stop sensor and unregister channel
+    mManager->configureDirectReport(sensor, channel, ASENSOR_DIRECT_RATE_STOP);
+    mManager->destroyDirectChannel(channel);
+}
+} // namespace SensorTest
+} // namespace android
diff --git a/tests/sensor/jni/android_hardware_cts_SensorDirectReportTest.cpp b/tests/sensor/jni/android_hardware_cts_SensorDirectReportTest.cpp
new file mode 100644
index 0000000..2616ff9
--- /dev/null
+++ b/tests/sensor/jni/android_hardware_cts_SensorDirectReportTest.cpp
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"nclude
+ * <android/hardware_buffer_jni.h>);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 "nativeTestHelper.h"
+#include "SensorTest.h"
+
+#include <android/hardware_buffer_jni.h>
+
+namespace {
+jboolean readHardwareBuffer(JNIEnv* env, jclass,
+        jobject hardwareBufferObj, jbyteArray buffer, jint srcOffset, jint destOffset, jint count) {
+    if (hardwareBufferObj == nullptr || buffer == nullptr ||
+        srcOffset < 0 || destOffset < 0 || count <= 0) {
+        return false;
+    }
+
+    if (env->GetArrayLength(buffer) < destOffset + count) {
+        ALOGE("Byte array is not large enough.");
+        return false;
+    }
+
+    AHardwareBuffer *hardwareBuffer = AHardwareBuffer_fromHardwareBuffer(env, hardwareBufferObj);
+    if (hardwareBuffer == nullptr) {
+        ALOGE("Cannot get AHardwareBuffer from HardwareBuffer");
+        return false;
+    }
+
+    void *address;
+    int32_t fence = -1;
+    jboolean ret = false;
+    if (AHardwareBuffer_lock(hardwareBuffer, AHARDWAREBUFFER_USAGE0_CPU_READ,
+                             fence, nullptr, &address) == 0) {
+        if (address != nullptr) {
+            env->SetByteArrayRegion(
+                    buffer, destOffset, count, reinterpret_cast<const jbyte *>(address));
+            ret = true;
+        } else {
+            ALOGE("AHardwareBuffer locked but address is invalid");
+        }
+        AHardwareBuffer_unlock(hardwareBuffer, &fence);
+    }
+    return ret;
+}
+
+JNINativeMethod gMethods[] = {
+    {  "nativeReadHardwareBuffer", "(Landroid/hardware/HardwareBuffer;[BIII)Z",
+            (void *) readHardwareBuffer},
+};
+} // unamed namespace
+
+int register_android_hardware_cts_SensorDirectReportTest(JNIEnv* env) {
+    jclass clazz = env->FindClass("android/hardware/cts/SensorDirectReportTest");
+    return env->RegisterNatives(clazz, gMethods,
+            sizeof(gMethods) / sizeof(JNINativeMethod));
+}
diff --git a/tests/sensor/jni/android_hardware_cts_SensorNativeTest.cpp b/tests/sensor/jni/android_hardware_cts_SensorNativeTest.cpp
new file mode 100644
index 0000000..40a5ff7
--- /dev/null
+++ b/tests/sensor/jni/android_hardware_cts_SensorNativeTest.cpp
@@ -0,0 +1,84 @@
+/*
+ * 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 "nativeTestHelper.h"
+#include "SensorTest.h"
+
+namespace {
+using android::SensorTest::SensorTest;
+
+#define RETURN_ON_EXCEPTION() do { if (env->ExceptionCheck()) { return;} } while(false)
+
+jlong setUp(JNIEnv*, jclass) {
+    SensorTest *test = new SensorTest();
+    if (test != nullptr) {
+        test->SetUp();
+    }
+    return reinterpret_cast<jlong>(test);
+}
+
+void tearDown(JNIEnv*, jclass, jlong instance) {
+    delete reinterpret_cast<SensorTest *>(instance);
+}
+
+void test(JNIEnv* env, jclass, jlong instance) {
+    SensorTest *test = reinterpret_cast<SensorTest *>(instance);
+    ASSERT_NOT_NULL(test);
+
+    // test if SensorTest is intialized
+    ALOGI("testInitialized");
+    test->testInitialized(env);
+    RETURN_ON_EXCEPTION();
+
+    // test if SensorTest is intialized
+    ALOGI("testInvalidParameter");
+    test->testInvalidParameter(env);
+    RETURN_ON_EXCEPTION();
+
+    // test sensor direct report
+    std::vector<int32_t> sensorTypes ={ASENSOR_TYPE_ACCELEROMETER, ASENSOR_TYPE_GYROSCOPE};
+    std::vector<int32_t> rates = {
+        ASENSOR_DIRECT_RATE_NORMAL, ASENSOR_DIRECT_RATE_FAST, ASENSOR_DIRECT_RATE_VERY_FAST};
+    std::vector<int32_t> channelTypes =
+        {ASENSOR_DIRECT_CHANNEL_TYPE_SHARED_MEMORY, ASENSOR_DIRECT_CHANNEL_TYPE_SHARED_MEMORY};
+    for (auto s : sensorTypes) {
+        for (auto c : channelTypes) {
+            for (auto r : rates) {
+                ALOGI("testDirectReport: sensorType = %d, channelType = %d, ratelevel = %d",
+                      s, c, r);
+                test->testDirectReport(env, s, c, r);
+                RETURN_ON_EXCEPTION();
+            }
+        }
+    }
+}
+
+JNINativeMethod gMethods[] = {
+    {  "nativeSetUp", "()J",
+            (void *) setUp},
+    {  "nativeTearDown", "(J)V",
+            (void *) tearDown},
+    {  "nativeTest", "(J)V",
+            (void *) test},
+};
+} // unamed namespace
+
+int register_android_hardware_cts_SensorNativeTest(JNIEnv* env) {
+    jclass clazz = env->FindClass("android/hardware/cts/SensorNativeTest");
+    return env->RegisterNatives(clazz, gMethods,
+            sizeof(gMethods) / sizeof(JNINativeMethod));
+}
diff --git a/tests/sensor/jni/android_view_cts_SensorNativeTest.cpp b/tests/sensor/jni/android_view_cts_SensorNativeTest.cpp
deleted file mode 100644
index 0c67928..0000000
--- a/tests/sensor/jni/android_view_cts_SensorNativeTest.cpp
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * 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 "nativeTestHelper.h"
-#include "SensorTest.h"
-
-namespace {
-using android::SensorTest::SensorTest;
-
-jlong setUp(JNIEnv*, jclass) {
-    SensorTest *test = new SensorTest();
-    if (test != nullptr) {
-        test->SetUp();
-    }
-    return reinterpret_cast<jlong>(test);
-}
-
-void tearDown(JNIEnv*, jclass, jlong instance) {
-    delete reinterpret_cast<SensorTest *>(instance);
-}
-
-void test(JNIEnv* env, jclass, jlong instance) {
-    SensorTest *test = reinterpret_cast<SensorTest *>(instance);
-    ASSERT_NE(test, nullptr);
-
-    // test if SensorTest is intialized
-    test->testInitialized(env);
-
-    // test gyro direct report using shared memory buffer
-    test->testGyroscopeSharedMemoryDirectReport(env);
-}
-
-JNINativeMethod gMethods[] = {
-    {  "nativeSetUp", "()J",
-            (void *) setUp},
-    {  "nativeTearDown", "(J)V",
-            (void *) tearDown},
-    {  "nativeTest", "(J)V",
-            (void *) test},
-};
-} // unamed namespace
-
-int register_android_view_cts_SensorNativeTest(JNIEnv* env)
-{
-    jclass clazz = env->FindClass("android/hardware/cts/SensorNativeTest");
-    return env->RegisterNatives(clazz, gMethods,
-            sizeof(gMethods) / sizeof(JNINativeMethod));
-}
diff --git a/tests/sensor/jni/nativeTestHelper.cpp b/tests/sensor/jni/nativeTestHelper.cpp
index 7c1b378..3c7df9a 100644
--- a/tests/sensor/jni/nativeTestHelper.cpp
+++ b/tests/sensor/jni/nativeTestHelper.cpp
@@ -19,7 +19,8 @@
 #include <cstdlib>
 #include <cstring>
 
-extern int register_android_view_cts_SensorNativeTest(JNIEnv* env);
+extern int register_android_hardware_cts_SensorNativeTest(JNIEnv* env);
+extern int register_android_hardware_cts_SensorDirectReportTest(JNIEnv* env);
 
 void fail(JNIEnv* env, const char* format, ...) {
     va_list args;
@@ -41,7 +42,10 @@
     if (vm->GetEnv((void**)&env, JNI_VERSION_1_4) != JNI_OK) {
         return JNI_ERR;
     }
-    if (register_android_view_cts_SensorNativeTest(env)) {
+    if (register_android_hardware_cts_SensorNativeTest(env)) {
+        return JNI_ERR;
+    }
+    if (register_android_hardware_cts_SensorDirectReportTest(env)) {
         return JNI_ERR;
     }
     return JNI_VERSION_1_4;
diff --git a/tests/sensor/jni/nativeTestHelper.h b/tests/sensor/jni/nativeTestHelper.h
index cf7f1bd..e465667 100644
--- a/tests/sensor/jni/nativeTestHelper.h
+++ b/tests/sensor/jni/nativeTestHelper.h
@@ -44,5 +44,17 @@
         ASSERT((a) < (b), "assert failed on (" #a " < " #b ") at " __FILE__ ":%d", __LINE__)
 #define ASSERT_LE(a, b) \
         ASSERT((a) <= (b), "assert failed on (" #a " <= " #b ") at " __FILE__ ":%d", __LINE__)
+#define ASSERT_NOT_NULL(a) \
+        ASSERT((a) != nullptr, "assert failed on isNotNull(" #a ") at " __FILE__ ":%d", __LINE__)
+#define ASSERT_EMPTY_CSTR(a) do { \
+        const char *tmp = a; \
+        ASSERT(tmp != nullptr, \
+               "assert failed on (empty_cstr(" #a "): " #a " != nullptr) " \
+               "at " __FILE__ ":%d", __LINE__); \
+        ASSERT(tmp[0] == '\0', \
+               "assert failed on (empty_cstr(" #a "): strlen() == 0) " \
+               "at " __FILE__ ":%d", __LINE__); \
+    } while (false)
+
 
 void fail(JNIEnv* env, const char* format, ...);
diff --git a/tests/sensor/src/android/hardware/cts/SensorDirectReportTest.java b/tests/sensor/src/android/hardware/cts/SensorDirectReportTest.java
index edc71aa..547f44c 100644
--- a/tests/sensor/src/android/hardware/cts/SensorDirectReportTest.java
+++ b/tests/sensor/src/android/hardware/cts/SensorDirectReportTest.java
@@ -57,9 +57,9 @@
     private static final float FREQ_UPPER_BOUND = 2.2f;
 
     // sensor reading assumption
-    private static final float GRAVITY_MIN = 9.81f - 1.f;
-    private static final float GRAVITY_MAX = 9.81f + 1.f;
-    private static final float GYRO_NORM_MAX = 0.05f;
+    private static final float GRAVITY_MIN = 9.81f - 0.5f;
+    private static final float GRAVITY_MAX = 9.81f + 0.5f;
+    private static final float GYRO_NORM_MAX = 0.1f;
 
     // test constants
     private static final int REST_PERIOD_BEFORE_TEST_MILLISEC = 3000;
@@ -69,6 +69,9 @@
     private static final int SHARED_MEMORY_SIZE = 2000 * SENSORS_EVENT_SIZE;
     private static final float MERCY_FACTOR = 0.1f;
 
+    private static native boolean nativeReadHardwareBuffer(HardwareBuffer hardwareBuffer,
+            byte[] buffer, int srcOffset, int destOffset, int count);
+
     private boolean mNeedMemoryFile;
     private MemoryFile mMemoryFile;
     private boolean mNeedHardwareBuffer;
@@ -78,6 +81,10 @@
     private SensorManager mSensorManager;
     private SensorDirectChannel mChannel;
 
+    static {
+        System.loadLibrary("cts-sensors-ndk-jni");
+    }
+
     @Override
     protected void setUp() throws Exception {
         mSensorManager = (SensorManager) getContext().getSystemService(Context.SENSOR_SERVICE);
@@ -323,11 +330,9 @@
     private HardwareBuffer allocateHardwareBuffer() {
         HardwareBuffer hardwareBuffer;
 
-        // TODO: remove this after BLOB constant is added into HardwareBuffer.
-        int BLOB = 6;
         hardwareBuffer = HardwareBuffer.create(
-                SHARED_MEMORY_SIZE, 1 /* height */, BLOB, 1 /* layer */,
-                HardwareBuffer.USAGE0_CPU_READ | HardwareBuffer.USAGE0_GPU_DATA_BUFFER
+                SHARED_MEMORY_SIZE, 1 /* height */, HardwareBuffer.BLOB, 1 /* layer */,
+                HardwareBuffer.USAGE0_CPU_READ_OFTEN | HardwareBuffer.USAGE0_GPU_DATA_BUFFER
                     | HardwareBuffer.USAGE0_SENSOR_DIRECT_DATA);
         return hardwareBuffer;
     }
@@ -345,11 +350,14 @@
     private boolean isSharedMemoryFormatted(int memType) {
         if (memType == SensorDirectChannel.TYPE_ASHMEM) {
             if (!readMemoryFileContent()) {
+                Log.e(TAG, "Read MemoryFile content fail");
                 return false;
             }
         } else {
-            // Android API only support memory file read back
-            return true;
+            if (!readHardwareBufferContent()) {
+                Log.e(TAG, "Read HardwareBuffer content fail");
+                return false;
+            }
         }
 
         for (byte b : mBuffer) {
@@ -364,8 +372,7 @@
         if (memType == SensorDirectChannel.TYPE_ASHMEM) {
             assertTrue("read MemoryFile content failed", readMemoryFileContent());
         } else {
-            // Android API only support memory file read back
-            return;
+            assertTrue("read HardwareBuffer content failed", readHardwareBufferContent());
         }
 
         int offset = 0;
@@ -455,6 +462,10 @@
         return true;
     }
 
+    private boolean readHardwareBufferContent() {
+        return nativeReadHardwareBuffer(mHardwareBuffer, mBuffer, 0, 0, SHARED_MEMORY_SIZE);
+    }
+
     private class DirectReportSensorEvent {
         int size;
         int token;
diff --git a/tests/signature/src/android/signature/cts/FailureType.java b/tests/signature/src/android/signature/cts/FailureType.java
index 5aaebc4..a701202 100644
--- a/tests/signature/src/android/signature/cts/FailureType.java
+++ b/tests/signature/src/android/signature/cts/FailureType.java
@@ -10,6 +10,7 @@
     MISSING_FIELD,
     MISMATCH_CLASS,
     MISMATCH_INTERFACE,
+    MISMATCH_INTERFACE_METHOD,
     MISMATCH_METHOD,
     MISMATCH_FIELD,
     CAUGHT_EXCEPTION,
diff --git a/tests/signature/src/android/signature/cts/JDiffClassDescription.java b/tests/signature/src/android/signature/cts/JDiffClassDescription.java
index 35e97d0..2779ea7 100644
--- a/tests/signature/src/android/signature/cts/JDiffClassDescription.java
+++ b/tests/signature/src/android/signature/cts/JDiffClassDescription.java
@@ -51,6 +51,23 @@
     /** Indicates that the method is a synthetic method. */
     private static final int METHOD_MODIFIER_SYNTHETIC = 0x00001000;
 
+    private static final Set<String> HIDDEN_INTERFACE_WHITELIST = new HashSet<>();
+    static {
+        // Interfaces that define @hide methods will by definition contain
+        // methods that do not appear in current.txt. Interfaces added to this
+        // list are probably not meant to be implemented in an application.
+        HIDDEN_INTERFACE_WHITELIST.add("public abstract boolean android.companion.DeviceFilter.matches(D)");
+        HIDDEN_INTERFACE_WHITELIST.add("public static <D> boolean android.companion.DeviceFilter.matches(android.companion.DeviceFilter<D>,D)");
+        HIDDEN_INTERFACE_WHITELIST.add("public abstract void android.nfc.tech.TagTechnology.reconnect() throws java.io.IOException");
+        HIDDEN_INTERFACE_WHITELIST.add("public abstract void android.os.IBinder.shellCommand(java.io.FileDescriptor,java.io.FileDescriptor,java.io.FileDescriptor,java.lang.String[],android.os.ShellCallback,android.os.ResultReceiver) throws android.os.RemoteException");
+        HIDDEN_INTERFACE_WHITELIST.add("public abstract int android.text.ParcelableSpan.getSpanTypeIdInternal()");
+        HIDDEN_INTERFACE_WHITELIST.add("public abstract void android.text.ParcelableSpan.writeToParcelInternal(android.os.Parcel,int)");
+        HIDDEN_INTERFACE_WHITELIST.add("public abstract void android.view.WindowManager.requestAppKeyboardShortcuts(android.view.WindowManager$KeyboardShortcutsReceiver,int)");
+        HIDDEN_INTERFACE_WHITELIST.add("public abstract boolean javax.microedition.khronos.egl.EGL10.eglReleaseThread()");
+        HIDDEN_INTERFACE_WHITELIST.add("public abstract void org.w3c.dom.ls.LSSerializer.setFilter(org.w3c.dom.ls.LSSerializerFilter)");
+        HIDDEN_INTERFACE_WHITELIST.add("public abstract org.w3c.dom.ls.LSSerializerFilter org.w3c.dom.ls.LSSerializer.getFilter()");
+    }
+
     public enum JDiffType {
         INTERFACE, CLASS
     }
@@ -1100,6 +1117,40 @@
     }
 
     /**
+     * Validate that an interfaces method count is as expected.
+     */
+    private List<String> checkInterfaceMethodCompliance() {
+        List<String> unexpectedMethods = new ArrayList<>();
+        for (Method method : mClass.getDeclaredMethods()) {
+            if (method.isDefault()) {
+                continue;
+            }
+            if (method.isSynthetic()) {
+                continue;
+            }
+            if (method.isBridge()) {
+                continue;
+            }
+            if (HIDDEN_INTERFACE_WHITELIST.contains(method.toGenericString())) {
+                continue;
+            }
+
+            boolean foundMatch = false;
+            for (JDiffMethod jdiffMethod : jDiffMethods) {
+                if (matches(jdiffMethod, method)) {
+                    foundMatch = true;
+                }
+            }
+            if (!foundMatch) {
+                unexpectedMethods.add(method.toGenericString());
+            }
+        }
+
+        return unexpectedMethods;
+
+    }
+
+    /**
      * Checks that the class found through reflection matches the
      * specification from the API xml file.
      */
@@ -1123,6 +1174,15 @@
 
                 return;
             }
+
+            List<String> methods = checkInterfaceMethodCompliance();
+            if (JDiffType.INTERFACE.equals(mClassType) && methods.size() > 0) {
+                mResultObserver.notifyFailure(FailureType.MISMATCH_INTERFACE_METHOD,
+                        mAbsoluteClassName, "Interfaces cannot be modified: "
+                                + mAbsoluteClassName + ": " + methods);
+                return;
+            }
+
             if (!checkClassModifiersCompliance()) {
                 logMismatchInterfaceSignature(mAbsoluteClassName,
                         "Non-compatible class found when looking for " +
diff --git a/tests/tests/animation/src/android/animation/cts/AnimatorSetTest.java b/tests/tests/animation/src/android/animation/cts/AnimatorSetTest.java
index 5bc0155..29e8e80 100644
--- a/tests/tests/animation/src/android/animation/cts/AnimatorSetTest.java
+++ b/tests/tests/animation/src/android/animation/cts/AnimatorSetTest.java
@@ -21,6 +21,7 @@
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertSame;
 import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.times;
@@ -470,6 +471,8 @@
 
         mActivityRule.runOnUiThread(() -> {
             set.start();
+            verify(setListener, times(0)).onAnimationEnd(any(AnimatorSet.class),
+                    any(boolean.class));
         });
         verify(setListener, within(100)).onAnimationEnd(set, false);
         verify(listener1, times(1)).onAnimationEnd(a1, false);
diff --git a/tests/tests/appwidget/src/android/appwidget/cts/RequestPinAppWidgetTest.java b/tests/tests/appwidget/src/android/appwidget/cts/RequestPinAppWidgetTest.java
index 2b29071..019fb66 100644
--- a/tests/tests/appwidget/src/android/appwidget/cts/RequestPinAppWidgetTest.java
+++ b/tests/tests/appwidget/src/android/appwidget/cts/RequestPinAppWidgetTest.java
@@ -24,6 +24,7 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.pm.LauncherApps;
+import android.os.Bundle;
 import android.os.Handler;
 
 import java.util.Arrays;
@@ -62,10 +63,13 @@
         BlockingReceiver setupReceiver = new BlockingReceiver()
                 .register(Constants.ACTION_SETUP_REPLY);
 
+        Bundle extras = new Bundle();
+        extras.putString("dummy", launcherPkg + "-dummy");
+
         PendingIntent pinResult = PendingIntent.getBroadcast(context, 0,
                 new Intent(ACTION_PIN_RESULT), PendingIntent.FLAG_ONE_SHOT);
         AppWidgetManager.getInstance(context).requestPinAppWidget(
-                getFirstWidgetComponent(), pinResult);
+                getFirstWidgetComponent(), extras, pinResult);
 
         setupReceiver.await();
         // Verify that the confirmation dialog was opened
@@ -80,6 +84,8 @@
         boolean[] providerInfo = verifyInstalledProviders(Arrays.asList(
                 req.getAppWidgetProviderInfo(context), req.getAppWidgetProviderInfo(context)));
         assertTrue(providerInfo[0]);
+        assertNotNull(req.getExtras());
+        assertEquals(launcherPkg + "-dummy", req.getExtras().getString("dummy"));
 
         // Accept the request
         BlockingReceiver resultReceiver = new BlockingReceiver().register(ACTION_PIN_RESULT);
diff --git a/tests/tests/content/Android.mk b/tests/tests/content/Android.mk
index a365ac2..435e7c3 100644
--- a/tests/tests/content/Android.mk
+++ b/tests/tests/content/Android.mk
@@ -22,6 +22,8 @@
 LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
 LOCAL_USE_AAPT2 := true
 
+LOCAL_JNI_SHARED_LIBRARIES := libnativecursorwindow_jni libnativehelper_compat_libc++
+
 LOCAL_JAVA_LIBRARIES := android.test.runner
 
 LOCAL_STATIC_JAVA_LIBRARIES :=  android-support-v4 \
@@ -57,3 +59,5 @@
 LOCAL_COMPATIBILITY_SUITE := cts
 
 include $(BUILD_CTS_PACKAGE)
+
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/tests/tests/content/AndroidManifest.xml b/tests/tests/content/AndroidManifest.xml
index 0234b8c..b22ab1e 100644
--- a/tests/tests/content/AndroidManifest.xml
+++ b/tests/tests/content/AndroidManifest.xml
@@ -265,6 +265,13 @@
             </intent-filter>
         </activity>
 
+        <provider
+                android:name="android.content.cts.CursorWindowContentProvider"
+                android:authorities="cursorwindow.provider"
+                android:exported="true"
+                android:process=":providerProcess">
+        </provider>
+
     </application>
 
     <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
diff --git a/tests/tests/content/jni/Android.mk b/tests/tests/content/jni/Android.mk
new file mode 100644
index 0000000..4737b35
--- /dev/null
+++ b/tests/tests/content/jni/Android.mk
@@ -0,0 +1,30 @@
+# 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 := libnativecursorwindow_jni
+
+# Don't include this package in any configuration by default.
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_SRC_FILES := NativeCursorWindow.c
+
+LOCAL_C_INCLUDES := $(JNI_H_INCLUDE)
+
+LOCAL_SHARED_LIBRARIES := libnativehelper_compat_libc++ liblog
+LOCAL_CXX_STL := libc++_static
+include $(BUILD_SHARED_LIBRARY)
diff --git a/tests/tests/content/jni/NativeCursorWindow.c b/tests/tests/content/jni/NativeCursorWindow.c
new file mode 100644
index 0000000..a2fb92a
--- /dev/null
+++ b/tests/tests/content/jni/NativeCursorWindow.c
@@ -0,0 +1,99 @@
+/*
+ * 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 "NativeCursorWindow"
+
+#include <jni.h>
+#include <fcntl.h>
+#include <sys/mman.h>
+#include <linux/ashmem.h>
+#include <utils/Log.h>
+
+struct Header {
+    // Offset of the lowest unused byte in the window.
+    uint32_t freeOffset;
+
+    // Offset of the first row slot chunk.
+    uint32_t firstChunkOffset;
+
+    uint32_t numRows;
+    uint32_t numColumns;
+};
+
+struct RowSlot {
+    uint32_t offset;
+};
+
+#define ROW_SLOT_CHUNK_NUM_ROWS 100
+
+struct RowSlotChunk {
+    struct RowSlot slots[ROW_SLOT_CHUNK_NUM_ROWS];
+    uint32_t nextChunkOffset;
+};
+
+/* Field types. */
+enum {
+    FIELD_TYPE_NULL = 0,
+    FIELD_TYPE_INTEGER = 1,
+    FIELD_TYPE_FLOAT = 2,
+    FIELD_TYPE_STRING = 3,
+    FIELD_TYPE_BLOB = 4,
+};
+
+/* Opaque type that describes a field slot. */
+struct FieldSlot {
+    int32_t type;
+    union {
+        double d;
+        int64_t l;
+        struct {
+            uint32_t offset;
+            uint32_t size;
+        } buffer;
+    } data;
+} __attribute((packed));
+
+JNIEXPORT jint JNICALL
+Java_android_content_cts_CursorWindowContentProvider_makeNativeCursorWindowFd(JNIEnv *env, jclass clazz,
+jint offset, jint size, jboolean isBlob) {
+    int fd = open("/dev/ashmem", O_RDWR);
+    ioctl(fd, ASHMEM_SET_NAME, "Fake CursorWindow");
+
+    ioctl(fd, ASHMEM_SET_SIZE, 1024);
+
+    void *data = mmap(NULL, 1024, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+
+    struct Header *header = (struct Header *) data;
+    unsigned rowSlotChunkOffset = sizeof(struct Header);
+    struct RowSlotChunk *rowSlotChunk = (struct RowSlotChunk *)(data + rowSlotChunkOffset);
+    unsigned fieldSlotOffset = rowSlotChunkOffset + sizeof(struct RowSlotChunk);
+    struct FieldSlot *fieldSlot = (struct FieldSlot *) (data + fieldSlotOffset);
+
+    header->numRows = 1;
+    header->numColumns = 1;
+    header->firstChunkOffset = rowSlotChunkOffset;
+
+    rowSlotChunk->slots[0].offset = fieldSlotOffset;
+
+    fieldSlot->type = isBlob ? FIELD_TYPE_BLOB : FIELD_TYPE_STRING;
+    fieldSlot->data.buffer.offset = offset;
+    fieldSlot->data.buffer.size = size;
+
+    munmap(data, 1024);
+
+    return fd;
+
+}
diff --git a/tests/tests/content/src/android/content/cts/AvailableIntentsTest.java b/tests/tests/content/src/android/content/cts/AvailableIntentsTest.java
index 47813f3..b69a8c5 100644
--- a/tests/tests/content/src/android/content/cts/AvailableIntentsTest.java
+++ b/tests/tests/content/src/android/content/cts/AvailableIntentsTest.java
@@ -28,6 +28,7 @@
 import android.provider.AlarmClock;
 import android.provider.MediaStore;
 import android.provider.Settings;
+import android.speech.RecognizerIntent;
 import android.telecom.TelecomManager;
 import android.test.AndroidTestCase;
 
@@ -50,6 +51,23 @@
     }
 
     /**
+     * Assert target intent is not resolved by a filter with priority greater than 0.
+     * @param intent - the Intent will be handled.
+     */
+    private void assertDefaultHandlerValidPriority(final Intent intent) {
+        PackageManager packageManager = mContext.getPackageManager();
+        List<ResolveInfo> resolveInfoList = packageManager.queryIntentActivities(intent, 0);
+        assertNotNull(resolveInfoList);
+        // one or more activity can handle this intent.
+        assertTrue(resolveInfoList.size() > 0);
+        // no activities override defaults with a high priority. Only system activities can override
+        // the priority.
+        for (ResolveInfo resolveInfo : resolveInfoList) {
+            assertTrue(resolveInfo.priority <= 0);
+        }
+    }
+
+    /**
      * Test ACTION_VIEW when url is http://web_address,
      * it will open a browser window to the URL specified.
      */
@@ -308,4 +326,16 @@
     public void testManageStorage() {
         assertCanBeHandled(new Intent(StorageManager.ACTION_MANAGE_STORAGE));
     }
+ 
+    public void testVoiceCommand() {
+        Intent intent = new Intent(Intent.ACTION_VOICE_COMMAND);
+        assertCanBeHandled(intent);
+        assertDefaultHandlerValidPriority(intent);
+    }
+
+    public void testVoiceSearchHandsFree() {
+        Intent intent = new Intent(RecognizerIntent.ACTION_VOICE_SEARCH_HANDS_FREE);
+        assertCanBeHandled(intent);
+        assertDefaultHandlerValidPriority(intent);
+    }
 }
diff --git a/tests/tests/content/src/android/content/cts/ContentProviderCursorWindowTest.java b/tests/tests/content/src/android/content/cts/ContentProviderCursorWindowTest.java
new file mode 100644
index 0000000..004b193
--- /dev/null
+++ b/tests/tests/content/src/android/content/cts/ContentProviderCursorWindowTest.java
@@ -0,0 +1,54 @@
+/*
+ * 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.content.cts;
+
+import android.database.Cursor;
+import android.database.sqlite.SQLiteException;
+import android.net.Uri;
+import android.test.AndroidTestCase;
+import android.util.Log;
+
+import java.io.IOException;
+
+/**
+ * Test {@link CursorWindowContentProvider} .
+ */
+public class ContentProviderCursorWindowTest extends AndroidTestCase {
+    private static final String TAG = "ContentProviderCursorWindowTest";
+
+    public void testQuery() throws IOException {
+        Cursor cursor = getContext().getContentResolver().query(
+                Uri.parse("content://cursorwindow.provider/hello"),
+                null, null, null, null
+        );
+        try {
+            cursor.moveToFirst();
+
+            int type = cursor.getType(0);
+            if (type != Cursor.FIELD_TYPE_BLOB) {
+                fail("Unexpected type " + type);
+            }
+            byte[] blob = cursor.getBlob(0);
+            Log.i(TAG,  "Blob length " + blob.length);
+            fail("getBlob should fail due to invalid offset used in the field slot");
+        } catch (SQLiteException expected) {
+            Log.i(TAG, "Expected exception: " + expected);
+        } finally {
+            cursor.close();
+        }
+    }
+}
diff --git a/tests/tests/content/src/android/content/cts/CursorWindowContentProvider.java b/tests/tests/content/src/android/content/cts/CursorWindowContentProvider.java
new file mode 100644
index 0000000..4266f35
--- /dev/null
+++ b/tests/tests/content/src/android/content/cts/CursorWindowContentProvider.java
@@ -0,0 +1,130 @@
+/*
+ * 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.content.cts;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.ContentProvider;
+import android.content.ContentValues;
+import android.database.AbstractWindowedCursor;
+import android.database.Cursor;
+import android.database.CursorWindow;
+import android.net.Uri;
+import android.os.Parcel;
+import android.os.ParcelFileDescriptor;
+import android.util.Log;
+
+/**
+ * Content provider that uses a custom {@link CursorWindow} to inject file descriptor
+ * pointing to another ashmem region having window slots with references outside of allowed ranges.
+ *
+ * <p>Used in {@link ContentProviderCursorWindowTest}
+ */
+public class CursorWindowContentProvider extends ContentProvider {
+    private static final String TAG = "CursorWindowContentProvider";
+    static {
+        System.loadLibrary("nativecursorwindow_jni");
+    }
+
+    @Override
+    public Cursor query(Uri uri, String[] projection, String selection,
+            String[] selectionArgs, String sortOrder) {
+        AbstractWindowedCursor cursor = new AbstractWindowedCursor() {
+            @Override
+            public int getCount() {
+                return 1;
+            }
+
+            @Override
+            public String[] getColumnNames() {
+                return new String[] {"a"};
+            }
+        };
+        cursor.setWindow(new InjectingCursorWindow("TmpWindow"));
+        return cursor;
+    }
+
+    class InjectingCursorWindow extends CursorWindow {
+        InjectingCursorWindow(String name) {
+            super(name);
+        }
+
+        @Override
+        public void writeToParcel(Parcel dest, int flags) {
+            Parcel tmp = Parcel.obtain();
+
+            super.writeToParcel(tmp, flags);
+            tmp.setDataPosition(0);
+            // Find location of file descriptor
+            int fdPos = -1;
+            while (tmp.dataAvail() > 0) {
+                fdPos = tmp.dataPosition();
+                int frameworkFdMarker = tmp.readInt();
+                if (frameworkFdMarker == 0x66642a85 /* BINDER_TYPE_FD */) {
+                    break;
+                }
+            }
+            if (fdPos == -1) {
+                tmp.recycle();
+                throw new IllegalStateException("File descriptor not found in the output of "
+                        + "CursorWindow.writeToParcel");
+            }
+            // Write reply with replaced file descriptor
+            ParcelFileDescriptor evilFd = ParcelFileDescriptor
+                    .adoptFd(makeNativeCursorWindowFd(1000, 1000, true));
+            dest.appendFrom(tmp, 0, fdPos);
+            dest.writeFileDescriptor(evilFd.getFileDescriptor());
+            tmp.setDataPosition(dest.dataPosition());
+            dest.appendFrom(tmp, dest.dataPosition(), tmp.dataAvail());
+            tmp.recycle();
+        }
+    }
+
+    private native static int makeNativeCursorWindowFd(int offset, int size, boolean isBlob);
+
+    // Stubs
+    @Override
+    public boolean onCreate() {
+        return true;
+    }
+
+    @Override
+    public int delete(Uri uri, String selection, String[] selectionArgs) {
+        Log.e(TAG, "delete() not implemented");
+        return 0;
+    }
+
+    @Override
+    public String getType(Uri uri) {
+        Log.e(TAG, "getType() not implemented");
+        return "";
+    }
+
+    @Override
+    public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) {
+        Log.e(TAG, "insert() not implemented");
+        return null;
+    }
+
+    @Override
+    public int update(Uri uri, ContentValues values, String selection,
+            String[] selectionArgs) {
+        Log.e(TAG, "update() not implemented");
+        return 0;
+    }
+
+}
diff --git a/tests/tests/database/src/android/database/cts/AbstractCursorTest.java b/tests/tests/database/src/android/database/cts/AbstractCursorTest.java
index ec3ca74..ba2f1fd 100644
--- a/tests/tests/database/src/android/database/cts/AbstractCursorTest.java
+++ b/tests/tests/database/src/android/database/cts/AbstractCursorTest.java
@@ -16,7 +16,6 @@
 
 package android.database.cts;
 
-
 import android.content.Context;
 import android.database.AbstractCursor;
 import android.database.CharArrayBuffer;
@@ -27,9 +26,9 @@
 import android.database.sqlite.SQLiteDatabase;
 import android.net.Uri;
 import android.os.Bundle;
+import android.provider.Settings;
 import android.test.InstrumentationTestCase;
 
-import java.lang.Math;
 import java.io.File;
 import java.util.ArrayList;
 import java.util.Random;
@@ -135,9 +134,9 @@
     }
 
     public void testSetNotificationUri() {
-        String MOCK_URI = "content://abstractrcursortest/testtable";
+        final Uri testUri = Settings.System.getUriFor(Settings.System.TIME_12_24);
         mDatabaseCursor.setNotificationUri(getInstrumentation().getContext().getContentResolver(),
-                Uri.parse(MOCK_URI));
+                testUri);
     }
 
     public void testRespond() {
diff --git a/tests/tests/display/src/android/display/cts/DisplayTest.java b/tests/tests/display/src/android/display/cts/DisplayTest.java
index 5992add..0e4c3d6 100644
--- a/tests/tests/display/src/android/display/cts/DisplayTest.java
+++ b/tests/tests/display/src/android/display/cts/DisplayTest.java
@@ -342,7 +342,7 @@
 
             WindowManager.LayoutParams params = getWindow().getAttributes();
             params.preferredDisplayModeId = mModeId;
-            params.type = WindowManager.LayoutParams.TYPE_SYSTEM_ALERT;
+            params.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
             params.setTitle("CtsTestPresentation");
             getWindow().setAttributes(params);
         }
diff --git a/tests/tests/graphics/assets/green-p3.png b/tests/tests/graphics/assets/green-p3.png
new file mode 100644
index 0000000..02f4cd1
--- /dev/null
+++ b/tests/tests/graphics/assets/green-p3.png
Binary files differ
diff --git a/tests/tests/graphics/assets/green-srgb.png b/tests/tests/graphics/assets/green-srgb.png
new file mode 100644
index 0000000..8b4d5ef
--- /dev/null
+++ b/tests/tests/graphics/assets/green-srgb.png
Binary files differ
diff --git a/tests/tests/graphics/src/android/graphics/cts/BitmapColorSpaceTest.java b/tests/tests/graphics/src/android/graphics/cts/BitmapColorSpaceTest.java
new file mode 100644
index 0000000..3f8b564
--- /dev/null
+++ b/tests/tests/graphics/src/android/graphics/cts/BitmapColorSpaceTest.java
@@ -0,0 +1,172 @@
+/*
+ * 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.graphics.cts;
+
+import android.annotation.NonNull;
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.ColorSpace;
+import android.os.Parcel;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.fail;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class BitmapColorSpaceTest {
+    private Resources mResources;
+
+    @Before
+    public void setup() {
+        mResources = InstrumentationRegistry.getTargetContext().getResources();
+    }
+
+    @Test
+    public void sRGB() {
+        Bitmap b = BitmapFactory.decodeResource(mResources, R.drawable.robot);
+        ColorSpace cs = b.getColorSpace();
+        assertNotNull(cs);
+        assertSame(ColorSpace.get(ColorSpace.Named.SRGB), cs);
+    }
+
+    @Test
+    public void p3() {
+        try (InputStream in = mResources.getAssets().open("green-p3.png")) {
+            Bitmap b = BitmapFactory.decodeStream(in);
+            ColorSpace cs = b.getColorSpace();
+            assertNotNull(cs);
+            assertSame(ColorSpace.get(ColorSpace.Named.DISPLAY_P3), cs);
+        } catch (IOException e) {
+            fail();
+        }
+    }
+
+    @Test
+    public void extendedSRGB() {
+        try (InputStream in = mResources.getAssets().open("prophoto-rgba16f.png")) {
+            Bitmap b = BitmapFactory.decodeStream(in);
+            ColorSpace cs = b.getColorSpace();
+            assertNotNull(cs);
+            assertSame(ColorSpace.get(ColorSpace.Named.LINEAR_EXTENDED_SRGB), cs);
+        } catch (IOException e) {
+            fail();
+        }
+    }
+
+    @Test
+    public void reconfigure() {
+        try (InputStream in = mResources.getAssets().open("green-p3.png")) {
+            BitmapFactory.Options opts = new BitmapFactory.Options();
+            opts.inMutable = true;
+
+            Bitmap b = BitmapFactory.decodeStream(in, null, opts);
+            ColorSpace cs = b.getColorSpace();
+            assertNotNull(cs);
+            assertSame(ColorSpace.get(ColorSpace.Named.DISPLAY_P3), cs);
+
+            b.reconfigure(b.getWidth() / 2, b.getHeight() / 2, Bitmap.Config.RGBA_F16);
+            cs = b.getColorSpace();
+            assertNotNull(cs);
+            assertSame(ColorSpace.get(ColorSpace.Named.LINEAR_EXTENDED_SRGB), cs);
+
+            b.reconfigure(b.getWidth(), b.getHeight(), Bitmap.Config.ARGB_8888);
+            cs = b.getColorSpace();
+            assertNotNull(cs);
+            assertSame(ColorSpace.get(ColorSpace.Named.DISPLAY_P3), cs);
+        } catch (IOException e) {
+            fail();
+        }
+    }
+
+    @Test
+    public void reuse() {
+        BitmapFactory.Options opts = new BitmapFactory.Options();
+        opts.inMutable = true;
+
+        Bitmap bitmap1 = null;
+        try (InputStream in = mResources.getAssets().open("green-srgb.png")) {
+            bitmap1 = BitmapFactory.decodeStream(in, null, opts);
+            ColorSpace cs = bitmap1.getColorSpace();
+            assertNotNull(cs);
+            assertSame(ColorSpace.get(ColorSpace.Named.SRGB), cs);
+        } catch (IOException e) {
+            fail();
+        }
+
+        try (InputStream in = mResources.getAssets().open("green-p3.png")) {
+            opts.inBitmap = bitmap1;
+
+            Bitmap bitmap2 = BitmapFactory.decodeStream(in, null, opts);
+            assertSame(bitmap1, bitmap2);
+            ColorSpace cs = bitmap2.getColorSpace();
+            assertNotNull(cs);
+            assertSame(ColorSpace.get(ColorSpace.Named.DISPLAY_P3), cs);
+        } catch (IOException e) {
+            fail();
+        }
+    }
+
+    @Test
+    public void writeColorSpace() {
+        testColorSpaceMarshalling("green-srgb.png", ColorSpace.get(ColorSpace.Named.SRGB));
+        testColorSpaceMarshalling("green-p3.png", ColorSpace.get(ColorSpace.Named.DISPLAY_P3));
+        testColorSpaceMarshalling("prophoto-rgba16f.png",
+                ColorSpace.get(ColorSpace.Named.LINEAR_EXTENDED_SRGB));
+
+        // Special case where the color space will be null in native
+        Bitmap bitmapIn = BitmapFactory.decodeResource(mResources, R.drawable.robot);
+        testParcelUnparcel(bitmapIn, ColorSpace.get(ColorSpace.Named.SRGB));
+    }
+
+    private void testColorSpaceMarshalling(
+            @NonNull String fileName, @NonNull ColorSpace colorSpace) {
+        try (InputStream in = mResources.getAssets().open(fileName)) {
+            Bitmap bitmapIn = BitmapFactory.decodeStream(in);
+            testParcelUnparcel(bitmapIn, colorSpace);
+        } catch (IOException e) {
+            fail();
+        }
+    }
+
+    private void testParcelUnparcel(Bitmap bitmapIn, ColorSpace expected) {
+        ColorSpace cs = bitmapIn.getColorSpace();
+        assertNotNull(cs);
+        assertSame(expected, cs);
+
+        Parcel p = Parcel.obtain();
+        bitmapIn.writeToParcel(p, 0);
+        p.setDataPosition(0);
+
+        Bitmap bitmapOut = Bitmap.CREATOR.createFromParcel(p);
+        cs = bitmapOut.getColorSpace();
+        assertNotNull(cs);
+        assertSame(expected, cs);
+
+        p.recycle();
+    }
+}
diff --git a/tests/tests/graphics/src/android/graphics/cts/ColorSpaceTest.java b/tests/tests/graphics/src/android/graphics/cts/ColorSpaceTest.java
index cd3d2e0..8cbde3a 100644
--- a/tests/tests/graphics/src/android/graphics/cts/ColorSpaceTest.java
+++ b/tests/tests/graphics/src/android/graphics/cts/ColorSpaceTest.java
@@ -29,6 +29,7 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertSame;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
@@ -710,6 +711,15 @@
     }
 
     @Test
+    public void testTransferParameters() {
+        ColorSpace.Rgb colorSpace = (ColorSpace.Rgb) ColorSpace.get(ColorSpace.Named.SRGB);
+        assertNotNull(colorSpace.getTransferParameters());
+
+        colorSpace = (ColorSpace.Rgb) ColorSpace.get(ColorSpace.Named.EXTENDED_SRGB);
+        assertNull(colorSpace.getTransferParameters());
+    }
+
+    @Test
     public void testIdempotentTransferFunctions() {
         Arrays.stream(ColorSpace.Named.values())
                 .map(ColorSpace::get)
@@ -723,6 +733,26 @@
     }
 
     @Test
+    public void testMatch() {
+        for (ColorSpace.Named named : ColorSpace.Named.values()) {
+            ColorSpace cs = ColorSpace.get(named);
+            if (cs.getModel() == ColorSpace.Model.RGB) {
+                ColorSpace.Rgb rgb = (ColorSpace.Rgb) cs;
+                // match() cannot match extended sRGB
+                if (rgb != ColorSpace.get(ColorSpace.Named.EXTENDED_SRGB) &&
+                        rgb != ColorSpace.get(ColorSpace.Named.LINEAR_EXTENDED_SRGB)) {
+                    assertSame(cs,
+                            ColorSpace.match(rgb.getTransform(), rgb.getTransferParameters()));
+                }
+            }
+        }
+
+        assertSame(ColorSpace.get(ColorSpace.Named.SRGB),
+                ColorSpace.match(SRGB_TO_XYZ, new ColorSpace.Rgb.TransferParameters(
+                        1 / 1.055, 0.055 / 1.055, 1 / 12.92, 0.04045, 2.4)));
+    }
+
+    @Test
     public void testRendererSize() {
         Bitmap b = ColorSpace.createRenderer()
                 .size(0)
@@ -751,6 +781,21 @@
         assertNotNull(b);
     }
 
+    @Test
+    public void testUcsRenderer() {
+        Bitmap b = ColorSpace.createRenderer()
+                .size(1024)
+                .clip(true)
+                .showWhitePoint(false)
+                .uniformChromaticityScale(true)
+                .add(ColorSpace.get(ColorSpace.Named.SRGB), 0xffffffff)
+                .add(ColorSpace.get(ColorSpace.Named.DCI_P3), 0xffffffff)
+                .add(ColorSpace.get(ColorSpace.Named.PRO_PHOTO_RGB), 0.1f, 0.5f, 0.1f, 0xff000000)
+                .add(ColorSpace.get(ColorSpace.Named.ADOBE_RGB), 0.1f, 0.5f, 0.1f, 0xff000000)
+                .render();
+        assertNotNull(b);
+    }
+
     @SuppressWarnings("SameParameterValue")
     private static void assertArrayNotEquals(float[] a, float[] b, float eps) {
         for (int i = 0; i < a.length; i++) {
diff --git a/tests/tests/graphics/src/android/graphics/fonts/cts/FontRequestTest.java b/tests/tests/graphics/src/android/graphics/fonts/cts/FontRequestTest.java
index 6cf7e29..1b00975 100644
--- a/tests/tests/graphics/src/android/graphics/fonts/cts/FontRequestTest.java
+++ b/tests/tests/graphics/src/android/graphics/fonts/cts/FontRequestTest.java
@@ -15,6 +15,10 @@
  */
 package android.graphics.fonts.cts;
 
+import static junit.framework.Assert.assertNotNull;
+import static junit.framework.Assert.assertNull;
+import static junit.framework.Assert.assertTrue;
+
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.fail;
 
@@ -22,23 +26,31 @@
 import android.os.Parcel;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
+import android.util.Base64;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import java.util.Arrays;
+import java.util.List;
+
 /**
  * Tests for {@link android.graphics.fonts.FontRequest}.
  */
 @SmallTest
 @RunWith(AndroidJUnit4.class)
 public class FontRequestTest {
-    private static final String PROVIDER = "com.test.fontprovider";
-    private static final String QUERY = "my_family_name";
+    private static final String PROVIDER = "com.test.fontprovider.authority";
+    private static final String QUERY = "query";
+    private static final String PACKAGE = "com.test.fontprovider.package";
+    private static final byte[] BYTE_ARRAY =
+            Base64.decode("e04fd020ea3a6910a2d808002b30", Base64.DEFAULT);
+    private static final List<List<byte[]>> CERTS = Arrays.asList(Arrays.asList(BYTE_ARRAY));
 
     @Test
     public void testWriteToParcel() {
-        // GIVEN a FontRequest
-        FontRequest request = new FontRequest(PROVIDER, QUERY);
+        // GIVEN a FontRequest created with the long constructor
+        FontRequest request = new FontRequest(PROVIDER, PACKAGE, QUERY, CERTS);
 
         // WHEN we write it to a Parcel
         Parcel dest = Parcel.obtain();
@@ -48,30 +60,76 @@
         // THEN we create from that parcel and get the same values.
         FontRequest result = FontRequest.CREATOR.createFromParcel(dest);
         assertEquals(PROVIDER, result.getProviderAuthority());
+        assertEquals(PACKAGE, result.getProviderPackage());
         assertEquals(QUERY, result.getQuery());
+        assertEquals(CERTS.size(), result.getCertificates().size());
+        List<byte[]> cert = CERTS.get(0);
+        List<byte[]> resultCert = result.getCertificates().get(0);
+        assertEquals(cert.size(), resultCert.size());
+        assertTrue(Arrays.equals(cert.get(0), resultCert.get(0)));
     }
 
     @Test
-    public void testConstructorWithNullAuthority() {
-        try {
-            // WHEN we create a request with a null authority
-            new FontRequest(null, QUERY);
-        } catch (NullPointerException e) {
-            // THEN we expect an exception to be raised.
-            return;
-        }
-        fail();
+    public void testWriteToParcel_shortConstructor() {
+        // GIVEN a FontRequest created with the short constructor
+        FontRequest request = new FontRequest(PROVIDER, PACKAGE, QUERY);
+
+        // WHEN we write it to a Parcel
+        Parcel dest = Parcel.obtain();
+        request.writeToParcel(dest, 0);
+        dest.setDataPosition(0);
+
+        // THEN we create from that parcel and get the same values.
+        FontRequest result = FontRequest.CREATOR.createFromParcel(dest);
+        assertEquals(PROVIDER, result.getProviderAuthority());
+        assertEquals(PACKAGE, result.getProviderPackage());
+        assertEquals(QUERY, result.getQuery());
+        assertNotNull(result.getCertificates());
+        assertEquals(0, result.getCertificates().size());
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void testShortConstructor_nullAuthority() {
+        new FontRequest(null, PACKAGE, QUERY);
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void testShortConstructor_nullPackage() {
+        new FontRequest(PROVIDER, null, QUERY);
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void testShortConstructor_nullQuery() {
+        new FontRequest(PROVIDER, PACKAGE, null);
     }
 
     @Test
-    public void testConstructorWithNullQuery() {
-        try {
-            // WHEN we create a request with a null query
-            new FontRequest(PROVIDER, null);
-        } catch (NullPointerException e) {
-            // THEN we expect an exception to be raised.
-            return;
-        }
-        fail();
+    public void testShortConstructor_defaults() {
+        // WHEN we create a request with the short constructor
+        FontRequest request = new FontRequest(PROVIDER, PACKAGE, QUERY);
+
+        // THEN we expect the defaults to be the following values
+        assertNotNull(request.getCertificates());
+        assertEquals(0, request.getCertificates().size());
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void testLongConstructor_nullAuthority() {
+        new FontRequest(null, PACKAGE, QUERY, CERTS);
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void testLongConstructor_nullPackage() {
+        new FontRequest(PROVIDER, null, QUERY, CERTS);
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void testLongConstructor_nullQuery() {
+        new FontRequest(PROVIDER, PACKAGE, null, CERTS);
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void testLongConstructor_nullCerts() {
+        new FontRequest(PROVIDER, PACKAGE, QUERY, null);
     }
 }
diff --git a/tests/tests/graphics/src/android/graphics/fonts/cts/FontResultTest.java b/tests/tests/graphics/src/android/graphics/fonts/cts/FontResultTest.java
index 06e2e3f..4f961de 100644
--- a/tests/tests/graphics/src/android/graphics/fonts/cts/FontResultTest.java
+++ b/tests/tests/graphics/src/android/graphics/fonts/cts/FontResultTest.java
@@ -90,16 +90,9 @@
         assertEquals(STYLE, result.getStyle());
     }
 
-    @Test
+    @Test(expected = NullPointerException.class)
     public void testConstructorWithNullFileDescriptor() {
-        try {
-            // WHEN we create a result with a null file descriptor
-            new FontResult(null, TTC_INDEX, FONT_VARIATION_SETTINGS, STYLE);
-        } catch (NullPointerException e) {
-            // THEN we expect an exception to be raised.
-            return;
-        }
-        fail();
+        new FontResult(null, TTC_INDEX, FONT_VARIATION_SETTINGS, STYLE);
     }
 
     @Test
diff --git a/tests/tests/keystore/src/android/keystore/cts/TestUtils.java b/tests/tests/keystore/src/android/keystore/cts/TestUtils.java
index 5993a95..1642e5c 100644
--- a/tests/tests/keystore/src/android/keystore/cts/TestUtils.java
+++ b/tests/tests/keystore/src/android/keystore/cts/TestUtils.java
@@ -846,8 +846,6 @@
         if (KeyProperties.KEY_ALGORITHM_EC.equalsIgnoreCase(keyAlgorithm)) {
             return new KeyProtection.Builder(KeyProperties.PURPOSE_SIGN)
                     .setDigests(digest)
-                    .setBlockModes(KeyProperties.BLOCK_MODE_CBC)
-                    .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
                     .build();
         } else if (KeyProperties.KEY_ALGORITHM_RSA.equalsIgnoreCase(keyAlgorithm)) {
             String padding = getSignatureAlgorithmPadding(signatureAlgorithm);
diff --git a/tests/tests/media/src/android/media/cts/AudioAttributesTest.java b/tests/tests/media/src/android/media/cts/AudioAttributesTest.java
index 58f5313..4704d3d 100644
--- a/tests/tests/media/src/android/media/cts/AudioAttributesTest.java
+++ b/tests/tests/media/src/android/media/cts/AudioAttributesTest.java
@@ -17,6 +17,7 @@
 package android.media.cts;
 
 import android.media.AudioAttributes;
+import android.media.AudioManager;
 import android.os.Parcel;
 
 import com.android.compatibility.common.util.CtsAndroidTestCase;
@@ -65,4 +66,27 @@
         assertTrue("Source and target attributes are not considered equal",
                 srcAttr.equals(targetAttr));
     }
+
+    // Test case 3: verify going from AudioAttributes to stream type with null arg
+    public void testGetVolumeControlStreamNull() throws Exception {
+        try {
+            int streamType = AudioAttributes.getVolumeControlStream(null);
+            fail("null AudioAttributes are not allowed");
+        } catch (IllegalArgumentException e) {
+            // test success
+        }
+    }
+
+    // Test case 4: verify going from AudioAttributes to stream type, with attributes built from
+    //    stream type.
+    public void testGetVolumeControlStreamVsLegacyStream() throws Exception {
+        for (int testType : new int[] { AudioManager.STREAM_ALARM, AudioManager.STREAM_MUSIC,
+                AudioManager.STREAM_NOTIFICATION, AudioManager.STREAM_RING,
+                AudioManager.STREAM_SYSTEM, AudioManager.STREAM_VOICE_CALL}) {
+            final AudioAttributes aa = new AudioAttributes.Builder().setLegacyStreamType(testType)
+                    .build();
+            final int stream = AudioAttributes.getVolumeControlStream(aa);
+            assertEquals("Volume control from attributes, stream doesn't match", testType, stream);
+        }
+    }
 }
diff --git a/tests/tests/media/src/android/media/cts/AudioPlaybackConfigurationTest.java b/tests/tests/media/src/android/media/cts/AudioPlaybackConfigurationTest.java
index a732bc5..2651af5 100644
--- a/tests/tests/media/src/android/media/cts/AudioPlaybackConfigurationTest.java
+++ b/tests/tests/media/src/android/media/cts/AudioPlaybackConfigurationTest.java
@@ -21,6 +21,7 @@
 import android.media.AudioManager;
 import android.media.MediaPlayer;
 import android.media.SoundPool;
+import android.os.Parcel;
 import android.util.Log;
 import android.media.AudioPlaybackConfiguration;
 
@@ -62,6 +63,51 @@
     private final static int TEST_USAGE = AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_DELAYED;
     private final static int TEST_CONTENT = AudioAttributes.CONTENT_TYPE_SPEECH;
 
+    // test marshalling/unmarshalling of an AudioPlaybackConfiguration instance. Since we can't
+    // create an AudioPlaybackConfiguration directly, we first need to play something to get one.
+    public void testParcelableWriteToParcel() throws Exception {
+        if (!isValidPlatform("testParcelableWriteToParcel")) return;
+
+        // create a player, make it play so we can get an AudioPlaybackConfiguration instance
+        AudioManager am = new AudioManager(getContext());
+        assertNotNull("Could not create AudioManager", am);
+        final AudioAttributes aa = (new AudioAttributes.Builder())
+                .setUsage(TEST_USAGE)
+                .setContentType(TEST_CONTENT)
+                .build();
+        mMp = MediaPlayer.create(getContext(), R.raw.sine1khzs40dblong,
+                aa, am.generateAudioSessionId());
+        mMp.start();
+        Thread.sleep(2*TEST_TIMING_TOLERANCE_MS);// waiting for playback to start
+        List<AudioPlaybackConfiguration> configs = am.getActivePlaybackConfigurations();
+        mMp.stop();
+        assertTrue("No playback reported", configs.size() > 0);
+        AudioPlaybackConfiguration configToMarshall = null;
+        for (AudioPlaybackConfiguration config : configs) {
+            if (config.getAudioAttributes().equals(aa)) {
+                configToMarshall = config;
+                break;
+            }
+        }
+
+        assertNotNull("Configuration not found during playback", configToMarshall);
+        assertEquals(0, configToMarshall.describeContents());
+
+        final Parcel srcParcel = Parcel.obtain();
+        final Parcel dstParcel = Parcel.obtain();
+        final byte[] mbytes;
+
+        configToMarshall.writeToParcel(srcParcel, 0 /*no public flags for marshalling*/);
+        mbytes = srcParcel.marshall();
+        dstParcel.unmarshall(mbytes, 0, mbytes.length);
+        dstParcel.setDataPosition(0);
+        final AudioPlaybackConfiguration restoredConfig =
+                AudioPlaybackConfiguration.CREATOR.createFromParcel(dstParcel);
+
+        assertEquals("Marshalled/restored AudioAttributes don't match",
+                configToMarshall.getAudioAttributes(), restoredConfig.getAudioAttributes());
+    }
+
     public void testGetterMediaPlayer() throws Exception {
         if (!isValidPlatform("testGetterMediaPlayer")) return;
 
diff --git a/tests/tests/media/src/android/media/cts/EncodeVirtualDisplayWithCompositionTest.java b/tests/tests/media/src/android/media/cts/EncodeVirtualDisplayWithCompositionTest.java
index 9dbcd5e..813742a 100644
--- a/tests/tests/media/src/android/media/cts/EncodeVirtualDisplayWithCompositionTest.java
+++ b/tests/tests/media/src/android/media/cts/EncodeVirtualDisplayWithCompositionTest.java
@@ -1350,7 +1350,20 @@
         for (Size sz : standardSizes) {
             MediaFormat format = MediaFormat.createVideoFormat(
                 MIME_TYPE, sz.getWidth(), sz.getHeight());
-            format.setInteger(MediaFormat.KEY_FRAME_RATE, 15); // require at least 15fps
+            format.setInteger(MediaFormat.KEY_COLOR_FORMAT,
+                    MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface);
+            int bitRate = BITRATE_DEFAULT;
+            if (sz.getWidth() == 1920 && sz.getHeight() == 1080) {
+                bitRate = BITRATE_1080p;
+            } else if (sz.getWidth() == 1280 && sz.getHeight() == 720) {
+                bitRate = BITRATE_720p;
+            } else if (sz.getWidth() == 800 && sz.getHeight() == 480) {
+                bitRate = BITRATE_800x480;
+            }
+            format.setInteger(MediaFormat.KEY_BIT_RATE, bitRate);
+            format.setInteger(MediaFormat.KEY_FRAME_RATE, 30);
+            format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, IFRAME_INTERVAL);
+            Log.i(TAG,"format = " + format.toString());
             if (mcl.findEncoderForFormat(format) != null) {
                 return sz;
             }
diff --git a/tests/tests/nativemidi/Android.mk b/tests/tests/nativemidi/Android.mk
new file mode 100755
index 0000000..f82b54d
--- /dev/null
+++ b/tests/tests/nativemidi/Android.mk
@@ -0,0 +1,50 @@
+# 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)
+
+#
+# libnativemidi
+#
+#include $(CLEAR_VARS)
+#LOCAL_MODULE := libnativemidi
+#LOCAL_SRC_FILES := out/target/product/marlin/obj_arm/STATIC_LIBRARIES/libnativemidi_intermediates/libnativemidi.a
+#include $(PREBUILT_STATIC_LIBRARY)
+
+#
+# NativeMidiEchoTest
+#
+include $(CLEAR_VARS)
+
+# Don't include this package in any target.
+LOCAL_MODULE_TAGS := optional
+
+# When built, explicitly put it in the data partition.
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+
+# Tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts
+
+LOCAL_SRC_FILES := $(call all-java-files-under, java)
+
+LOCAL_STATIC_JAVA_LIBRARIES := compatibility-device-util ctstestrunner
+#LOCAL_WHOLE_STATIC_LIBRARIES := libnativemidi
+LOCAL_JNI_SHARED_LIBRARIES := libnativemidi_jni
+#LOCAL_SHARED_LIBRARIES := libnativemidi_jni
+# Must match the package name in CtsTestCaseList.mk
+LOCAL_PACKAGE_NAME := CtsNativeMidiTestCases
+
+#LOCAL_SDK_VERSION := current
+
+include $(BUILD_CTS_PACKAGE)
diff --git a/tests/tests/nativemidi/AndroidManifest.xml b/tests/tests/nativemidi/AndroidManifest.xml
new file mode 100755
index 0000000..1d166a9
--- /dev/null
+++ b/tests/tests/nativemidi/AndroidManifest.xml
@@ -0,0 +1,48 @@
+<?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.nativemidi.cts">
+
+    <uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
+
+    <uses-feature android:name="android.software.midi" android:required="true"/>
+
+    <application>
+        <uses-library android:name="android.test.runner" />
+
+        <service android:name="NativeMidiEchoTestService"
+                android:permission="android.permission.BIND_MIDI_DEVICE_SERVICE">
+            <intent-filter>
+                <action android:name="android.media.midi.MidiDeviceService" />
+            </intent-filter>
+            <meta-data android:name="android.media.midi.MidiDeviceService"
+                android:resource="@xml/echo_device_info" />
+        </service>
+    </application>
+
+    <!--  self-instrumenting test package. -->
+    <instrumentation
+        android:name="android.support.test.runner.AndroidJUnitRunner"
+        android:label="CTS Native MIDI tests"
+        android:targetPackage="android.nativemidi.cts" >
+        <meta-data
+            android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
+</manifest>
+
diff --git a/tests/tests/nativemidi/AndroidTest.xml b/tests/tests/nativemidi/AndroidTest.xml
new file mode 100644
index 0000000..cf0ff45
--- /dev/null
+++ b/tests/tests/nativemidi/AndroidTest.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.
+-->
+<configuration description="Config for CTS MIDI test cases">
+    <target_preparer class="com.android.compatibility.common.tradefed.targetprep.ApkInstaller">
+        <option name="cleanup-apks" value="true" />
+        <option name="test-file-name" value="CtsMidiTestCases.apk" />
+    </target_preparer>
+    <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+        <option name="package" value="android.midi.cts" />
+        <option name="runtime-hint" value="8m" />
+    </test>
+</configuration>
diff --git a/tests/tests/nativemidi/java/android/nativemidi/cts/NativeMidiEchoTest.java b/tests/tests/nativemidi/java/android/nativemidi/cts/NativeMidiEchoTest.java
new file mode 100644
index 0000000..312a88b
--- /dev/null
+++ b/tests/tests/nativemidi/java/android/nativemidi/cts/NativeMidiEchoTest.java
@@ -0,0 +1,548 @@
+/*
+ * 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.nativemidi.cts;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.media.midi.MidiManager;
+import android.media.midi.MidiOutputPort;
+import android.media.midi.MidiDevice;
+import android.media.midi.MidiDevice.MidiConnection;
+import android.media.midi.MidiDeviceInfo;
+import android.media.midi.MidiDeviceInfo.PortInfo;
+import android.media.midi.MidiDeviceStatus;
+import android.media.midi.MidiInputPort;
+import android.media.midi.MidiReceiver;
+import android.media.midi.MidiSender;
+import android.os.Bundle;
+import android.test.AndroidTestCase;
+
+import java.io.IOException;
+
+import java.util.ArrayList;
+import android.util.Log;
+import java.util.Random;
+
+/**
+ * Test MIDI using a virtual MIDI device that echos input to output.
+ */
+public class NativeMidiEchoTest extends AndroidTestCase {
+    // Load the JNI shared library.
+    static {
+        System.loadLibrary("nativemidi_jni");
+    }
+
+    private static final String TAG = "NativeMidiEchoTest";
+
+    public static final String TEST_MANUFACTURER = "AndroidCTS";
+    public static final String ECHO_PRODUCT = "NativeMidiEcho";
+
+    private static final long NANOS_PER_MSEC = 1000L * 1000L;
+
+    // On a fast device in 2016, the test fails if timeout is 3 but works if it is 4.
+    // So this timeout value is very generous.
+    private static final int TIMEOUT_OPEN_MSEC = 2000; // arbitrary
+
+    private MidiManager mMidiManager;
+
+    private MidiDevice mEchoDevice;
+
+    // (Native code) attributes associated with a test/EchoServer instance.
+    private long mTestContext;
+
+    private Random mRandom = new Random(1972941337);
+
+    // Listens for an asynchronous device open and notifies waiting foreground
+    // test.
+    class MyTestOpenCallback implements MidiManager.OnDeviceOpenedListener {
+        MidiDevice mDevice;
+
+        @Override
+        public synchronized void onDeviceOpened(MidiDevice device) {
+            mDevice = device;
+            notifyAll();
+        }
+
+        public synchronized MidiDevice waitForOpen(int msec)
+                throws InterruptedException {
+            long deadline = System.currentTimeMillis() + msec;
+            long timeRemaining = msec;
+            while (mDevice == null && timeRemaining > 0) {
+                wait(timeRemaining);
+                timeRemaining = deadline - System.currentTimeMillis();
+            }
+            return mDevice;
+        }
+    }
+
+    public NativeMidiEchoTest() {
+        super();
+        Log.i(TAG, "NativeMidiEchoTest() [JAVA ctor]");
+        mTestContext = 0;
+    }
+
+    //
+    // Helpers
+    //
+    private boolean hasMidiSupport() {
+        PackageManager pm = mContext.getPackageManager();
+        return pm.hasSystemFeature(PackageManager.FEATURE_MIDI);
+    }
+
+    private void compareMessages(byte[] buffer, long timestamp, NativeMidiMessage nativeMsg) {
+        assertEquals("byte count of message", buffer.length, nativeMsg.len);
+        assertEquals("timestamp in message", timestamp, nativeMsg.timestamp);
+
+         for (int index = 0; index < buffer.length; index++) {
+            assertEquals("message byte[" + index + "]", buffer[index] & 0x0FF,
+                    nativeMsg.buffer[index] & 0x0FF);
+        }
+    }
+
+    private byte[] generateRandomMessage(int len) {
+        byte[] buffer = new byte[len];
+        for(int index = 0; index < len; index++) {
+            buffer[index] = (byte)(mRandom.nextInt() & 0xFF);
+        }
+        return buffer;
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+
+        Log.i(TAG, "++++ setUp() mContext:" + mContext);
+        PackageManager pm = mContext.getPackageManager();
+        if (!pm.hasSystemFeature(PackageManager.FEATURE_MIDI)) {
+            assertTrue("FEATURE_MIDI Not Supported.", false);
+            return; // Not supported so don't test it.
+        }
+        mMidiManager = (MidiManager)mContext.getSystemService(Context.MIDI_SERVICE);
+        assertTrue("Could not get the MidiManger.", mMidiManager != null);
+
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        super.tearDown();
+
+        Log.i(TAG, "++++ tearDown()");
+        mMidiManager = null;
+    }
+
+    // Search through the available devices for the ECHO loop-back device.
+    protected MidiDeviceInfo findEchoDevice() {
+        MidiDeviceInfo[] infos = mMidiManager.getDevices();
+        MidiDeviceInfo echoInfo = null;
+        for (MidiDeviceInfo info : infos) {
+            Bundle properties = info.getProperties();
+            String manufacturer = (String) properties.get(
+                    MidiDeviceInfo.PROPERTY_MANUFACTURER);
+
+            if (TEST_MANUFACTURER.equals(manufacturer)) {
+                String product = (String) properties.get(
+                        MidiDeviceInfo.PROPERTY_PRODUCT);
+                if (ECHO_PRODUCT.equals(product)) {
+                    echoInfo = info;
+                    break;
+                }
+            }
+        }
+        assertTrue("could not find " + ECHO_PRODUCT, echoInfo != null);
+        return echoInfo;
+    }
+
+    protected long setUpEchoServer() throws Exception {
+        // Log.i(TAG, "++++ setUpEchoServer()");
+        MidiDeviceInfo echoInfo = findEchoDevice();
+
+        // Open device.
+        MyTestOpenCallback callback = new MyTestOpenCallback();
+        mMidiManager.openDevice(echoInfo, callback, null);
+        mEchoDevice = callback.waitForOpen(TIMEOUT_OPEN_MSEC);
+        assertTrue("could not open " + ECHO_PRODUCT, mEchoDevice != null);
+
+        // Query echo service directly to see if it is getting status updates.
+        NativeMidiEchoTestService echoService = NativeMidiEchoTestService.getInstance();
+
+        try {
+            mEchoDevice.mirrorToNative();
+        } catch (IOException ex) {
+            Log.i(TAG, "! Failed to mirror to native !" + ex.getMessage() + "\n");
+            assertTrue("! Failed to mirror to native !", false);
+        }
+
+        long testContext = allocTestContext();
+        assertTrue("couldn't allocate test context.", testContext != 0);
+
+        // Open Input
+        int result =
+                startWritingMidi(testContext, mEchoDevice.getInfo().getId(), 0/*mPortNumber*/);
+        assertEquals("Bad start writing (native) MIDI", 0, result);
+
+        // Open Output
+        result = startReadingMidi(testContext, mEchoDevice.getInfo().getId(), 0/*mPortNumber*/);
+        assertEquals("Bad start Reading (native) MIDI", 0, result);
+
+        return testContext;
+    }
+
+    protected void tearDownEchoServer(long testContext) throws IOException {
+        // Log.i(TAG, "++++ tearDownEchoServer()");
+        // Query echo service directly to see if it is getting status updates.
+        NativeMidiEchoTestService echoService = NativeMidiEchoTestService.getInstance();
+
+        int result;
+
+        // Stop inputs
+        result = stopReadingMidi(testContext);
+        assertEquals("Bad stop reading (native) MIDI", 0, result);
+        result = stopReadingMidi(testContext); // should be safe to close twice
+        assertTrue("Didn't get error code on second stop reading MIDI.", result != 0);
+
+        // Stop outputs
+        result = stopWritingMidi(testContext);
+        assertEquals("Bad stop writing (native) MIDI", 0, result);
+        result = stopWritingMidi(testContext); // should be safe to close twice, but get an error
+        assertTrue("Didn't get error code on second stop writing MIDI.", result != 0);
+
+        freeTestContext(testContext);
+
+        mEchoDevice.close();
+    }
+
+    // Is the MidiManager supported?
+    public void testMidiManager() throws Exception {
+        Log.i(TAG, "++++ testMidiManager() this:" + System.identityHashCode(this));
+
+        if (!hasMidiSupport()) {
+            return; // Nothing to test
+        }
+
+        assertNotNull("MidiManager not supported.", mMidiManager);
+
+        // There should be at least one device for the Echo server.
+        MidiDeviceInfo[] infos = mMidiManager.getDevices();
+        assertNotNull("device list was null", infos);
+        assertTrue("device list was empty", infos.length >= 1);
+
+        // Log.i(TAG, "++++ Num MIDI Devices:" + infos.length);
+    }
+
+    public void testSetupTeardownEchoServer() throws Exception {
+        Log.i(TAG, "++++ testSetupTeardownEchoServer() this:" + System.identityHashCode(this));
+
+        long testContext = setUpEchoServer();
+        tearDownEchoServer(testContext);
+    }
+
+    public void testSendData() throws Exception {
+        Log.i(TAG, "++++  testSendData() this:" + System.identityHashCode(this));
+        long testContext = setUpEchoServer();
+
+        clearCounters(testContext);
+
+        assertEquals("Didn't start with 0 sends", 0, getNumSends(testContext));
+        assertEquals("Didn't start with 0 bytes sent", 0, getNumBytesSent(testContext));
+
+        final byte[] buffer = {
+                (byte) 0x93, 0x47, 0x52
+        };
+        long timestamp = 0x0123765489ABFEDCL;
+        writeMidi(testContext, buffer, 0, buffer.length);
+
+        assertTrue("Didn't get 1 send", getNumBytesSent(testContext) == buffer.length);
+        assertEquals("Didn't get right number of bytes sent",
+                buffer.length, getNumBytesSent(testContext));
+
+        tearDownEchoServer(testContext);
+    }
+
+    public void testWriteGetMaxMessageSize() throws Exception {
+        if (!hasMidiSupport()) {
+            return; // nothing to test
+        }
+
+        long testContext = setUpEchoServer();
+
+        assertTrue("Invalid write buffer size", getMaxWriteBufferSize(testContext) > 0);
+        // this is based on some "inside baseball" and may well change
+        // assertEquals("write buffer size != 1015", getMaxWriteBufferSize(testContext), 1015);
+
+        tearDownEchoServer(testContext);
+    }
+
+    public void testEchoSmallMessage() throws Exception {
+        if (!hasMidiSupport()) {
+            return; // nothing to test
+        }
+
+        long testContext = setUpEchoServer();
+
+        final byte[] buffer = {
+                (byte) 0x93, 0x47, 0x52
+        };
+        long timestamp = 0x0123765489ABFEDCL;
+
+        writeMidiWithTimestamp(testContext, buffer, 0, 0, timestamp); // should be a NOOP
+        writeMidiWithTimestamp(testContext, buffer, 0, buffer.length, timestamp);
+        writeMidiWithTimestamp(testContext, buffer, 0, 0, timestamp); // should be a NOOP
+
+        // Wait for message to pass quickly through echo service.
+        final int numMessages = 1;
+        final int timeoutMs = 20;
+        Thread.sleep(timeoutMs);
+        assertEquals("number of messages.", numMessages, getNumReceivedMessages(testContext));
+
+        NativeMidiMessage message = getReceivedMessageAt(testContext, 0);
+        compareMessages(buffer, timestamp, message);
+
+        tearDownEchoServer(testContext);
+    }
+
+    public void testEchoNMessages() throws Exception {
+        if (!hasMidiSupport()) {
+            return; // nothing to test
+        }
+
+        long testContext = setUpEchoServer();
+
+        int N = 100;
+        int messageLen;
+        int maxMessageLen = getMaxWriteBufferSize(testContext);
+        byte[][] buffers = new byte[N][];
+        long timestamps[] = new long[N];
+        for(int buffIndex = 0; buffIndex < N; buffIndex++) {
+            messageLen = (int)(mRandom.nextFloat() * (maxMessageLen-1)) + 1;
+            buffers[buffIndex] = generateRandomMessage(messageLen);
+            timestamps[buffIndex] = mRandom.nextLong();
+        }
+
+        for(int msgIndex = 0; msgIndex < N; msgIndex++) {
+            writeMidiWithTimestamp(testContext, buffers[msgIndex], 0, buffers[msgIndex].length,
+                    timestamps[msgIndex]);
+        }
+
+        // Wait for message to pass quickly through echo service.
+        final int timeoutMs = 20;
+        Thread.sleep(timeoutMs);
+
+        // correct number of messages
+        assertEquals("number of messages.", N, getNumReceivedMessages(testContext));
+
+        // correct data & order?
+        for(int msgIndex = 0; msgIndex < N; msgIndex++) {
+            NativeMidiMessage message = getReceivedMessageAt(testContext, msgIndex);
+            compareMessages(buffers[msgIndex], timestamps[msgIndex], message);
+        }
+
+        tearDownEchoServer(testContext);
+    }
+
+    public void testFlushMessages() throws Exception {
+        if (!hasMidiSupport()) {
+            return; // nothing to test
+        }
+
+        long testContext = setUpEchoServer();
+
+        int N = 7;
+        int messageLen;
+        int maxMessageLen = getMaxWriteBufferSize(testContext);
+        byte[][] buffers = new byte[N][];
+        long timestamps[] = new long[N];
+        for(int buffIndex = 0; buffIndex < N; buffIndex++) {
+            messageLen = (int)(mRandom.nextFloat() * (maxMessageLen-1)) + 1;
+            buffers[buffIndex] = generateRandomMessage(messageLen);
+            timestamps[buffIndex] = mRandom.nextLong();
+        }
+
+        for(int msgIndex = 0; msgIndex < N; msgIndex++) {
+            writeMidiWithTimestamp(testContext, buffers[msgIndex], 0, buffers[msgIndex].length,
+                    timestamps[msgIndex]);
+        }
+
+        // Wait for message to pass quickly through echo service.
+        final int timeoutMs = 20;
+        Thread.sleep(timeoutMs);
+
+        int result = flushSentMessages(testContext);
+        assertEquals("flush messages failed", 0, result);
+
+        // correct number of messages
+        assertEquals("number of messages.", N, getNumReceivedMessages(testContext));
+
+        // correct data & order?
+        for(int msgIndex = 0; msgIndex < N; msgIndex++) {
+            NativeMidiMessage message = getReceivedMessageAt(testContext, msgIndex);
+            compareMessages(buffers[msgIndex], timestamps[msgIndex], message);
+        }
+
+        tearDownEchoServer(testContext);
+    }
+
+    public void testFailHugeMessage() throws Exception {
+        if (!hasMidiSupport()) {
+            return; // nothing to test
+        }
+
+        long testContext = setUpEchoServer();
+
+        int maxMessageLen = getMaxWriteBufferSize(testContext);
+        byte[] buffer = generateRandomMessage(maxMessageLen * 2);
+        int result = writeMidi(testContext, buffer, 0, buffer.length);
+        assertTrue("Huge write didn't fail?", result < 0);
+
+        buffer = generateRandomMessage(maxMessageLen + 1);
+        result = writeMidi(testContext, buffer, 0, buffer.length);
+        assertTrue("Kinda-big write didn't fail?", result < 0);
+
+        tearDownEchoServer(testContext);
+    }
+
+    public void testNativeEchoLatency() throws Exception {
+        if (!hasMidiSupport()) {
+            return; // nothing to test
+        }
+
+        long testContext = setUpEchoServer();
+
+        final int numMessages = 10;
+        final long maxLatencyNanos = 15 * NANOS_PER_MSEC; // generally < 3 msec on N6
+        byte[] buffer = { (byte) 0x93, 0, 64 };
+
+        // Send multiple messages in a burst.
+        for (int index = 0; index < numMessages; index++) {
+            buffer[1] = (byte) (60 + index);
+            writeMidiWithTimestamp(testContext, buffer, 0, buffer.length, System.nanoTime());
+        }
+
+        // Wait for messages to pass quickly through echo service.
+        final int timeoutMs = 20;
+        Thread.sleep(timeoutMs);
+        assertEquals("number of messages.", numMessages, getNumReceivedMessages(testContext));
+
+        for (int msgIndex = 0; msgIndex < numMessages; msgIndex++) {
+            NativeMidiMessage message = getReceivedMessageAt(testContext, msgIndex);
+            assertEquals("message index", (byte) (60 + msgIndex), message.buffer[1]);
+            long elapsedNanos = message.timeReceived - message.timestamp;
+            Log.i(TAG, "---- elapsed:" + elapsedNanos);
+            // If this test fails then there may be a problem with the thread scheduler
+            // or there may be kernel activity that is blocking execution at the user level.
+            assertTrue("MIDI round trip latency[" + msgIndex + "] too large, " + elapsedNanos
+                    + " nanoseconds",
+                    (elapsedNanos < maxLatencyNanos));
+        }
+
+        tearDownEchoServer(testContext);
+    }
+
+    public void testEchoNMessages_PureNative() throws Exception {
+        if (!hasMidiSupport()) {
+            return; // nothing to test
+        }
+
+        long testContext = setUpEchoServer();
+
+        int N = 2;
+        int messageLen;
+        int maxMessageLen = getMaxWriteBufferSize(testContext);
+        byte[][] buffers = new byte[N][];
+        long timestamps[] = new long[N];
+        for(int buffIndex = 0; buffIndex < N; buffIndex++) {
+            messageLen = (int)(mRandom.nextFloat() * (maxMessageLen-1)) + 1;
+            buffers[buffIndex] = generateRandomMessage(messageLen);
+            timestamps[buffIndex] = mRandom.nextLong();
+        }
+
+        for(int msgIndex = 0; msgIndex < N; msgIndex++) {
+            writeMidiWithTimestamp(testContext, buffers[msgIndex], 0, buffers[msgIndex].length,
+                    timestamps[msgIndex]);
+        }
+
+        // Wait for message to pass quickly through echo service.
+        final int numMessages = N;
+        final int timeoutMs = 20;
+        Thread.sleep(timeoutMs);
+
+        int result = matchNativeMessages(testContext);
+        assertEquals("Native Compare Test Failed", result, 0);
+
+        tearDownEchoServer(testContext);
+    }
+
+    public void testNativeEchoLatency_PureNative() throws Exception {
+        if (!hasMidiSupport()) {
+            return; // nothing to test
+        }
+
+        long testContext = setUpEchoServer();
+
+        final int numMessages = 10;
+        final long maxLatencyNanos = 15 * NANOS_PER_MSEC; // generally < 3 msec on N6
+        byte[] buffer = { (byte) 0x93, 0, 64 };
+
+        // Send multiple messages in a burst.
+        for (int index = 0; index < numMessages; index++) {
+            buffer[1] = (byte) (60 + index);
+            writeMidiWithTimestamp(testContext, buffer, 0, buffer.length, System.nanoTime());
+        }
+
+        // Wait for messages to pass quickly through echo service.
+        final int timeoutMs = 20;
+        Thread.sleep(timeoutMs);
+        assertEquals("number of messages.", numMessages, getNumReceivedMessages(testContext));
+
+        int result = checkNativeLatency(testContext, maxLatencyNanos);
+        assertEquals("failed pure native latency test.", 0, result);
+
+        tearDownEchoServer(testContext);
+    }
+
+    // Native Routines
+    public static native void initN();
+
+    public static native long allocTestContext();
+    public static native void freeTestContext(long context);
+
+    public native int startReadingMidi(long ctx, int deviceId, int portNumber);
+    public native int stopReadingMidi(long ctx);
+
+    public native int startWritingMidi(long ctx, int deviceId, int portNumber);
+    public native int stopWritingMidi(long ctx);
+    public native int getMaxWriteBufferSize(long ctx);
+
+    public native int writeMidi(long ctx, byte[] data, int offset, int length);
+    public native int writeMidiWithTimestamp(long ctx, byte[] data, int offset, int length,
+            long timestamp);
+    public native int flushSentMessages(long ctx);
+
+    // Status - Counters
+    public native void clearCounters(long ctx);
+    public native int getNumSends(long ctx);
+    public native int getNumBytesSent(long ctx);
+    public native int getNumReceives(long ctx);
+    public native int getNumBytesReceived(long ctx);
+
+    // Status - Received Messages
+    public native int getNumReceivedMessages(long ctx);
+    public native NativeMidiMessage getReceivedMessageAt(long ctx, int index);
+
+    // Pure Native Checks
+    public native int matchNativeMessages(long ctx);
+    public native int checkNativeLatency(long ctx, long maxLatencyNanos);
+}
diff --git a/tests/tests/nativemidi/java/android/nativemidi/cts/NativeMidiEchoTestService.java b/tests/tests/nativemidi/java/android/nativemidi/cts/NativeMidiEchoTestService.java
new file mode 100644
index 0000000..3ad9fce
--- /dev/null
+++ b/tests/tests/nativemidi/java/android/nativemidi/cts/NativeMidiEchoTestService.java
@@ -0,0 +1,94 @@
+/*
+ * 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.nativemidi.cts;
+
+import android.content.Context;
+
+import android.media.midi.MidiDeviceService;
+import android.media.midi.MidiDeviceStatus;
+import android.media.midi.MidiManager;
+import android.media.midi.MidiReceiver;
+
+import java.io.IOException;
+
+import android.util.Log;
+
+/**
+ * Virtual MIDI Device that copies its input to its output.
+ * This is used for loop-back testing of MIDI I/O.
+ */
+
+public class NativeMidiEchoTestService extends MidiDeviceService {
+    private static final String TAG = "NativeMidiEchoTestService";
+
+    // Other apps will write to this port.
+    private MidiReceiver mInputReceiver = new MyReceiver();
+    // This app will copy the data to this port.
+    private MidiReceiver mOutputReceiver;
+    private static NativeMidiEchoTestService mInstance;
+
+    // These are public so we can easily read them from CTS test.
+    public int statusChangeCount;
+    public boolean inputOpened;
+    public int outputOpenCount;
+
+    @Override
+    public void onCreate() {
+        // Log.i(TAG, "++++ onCreate()");
+        super.onCreate();
+        mInstance = this;
+        MidiManager midiManager = (MidiManager)getSystemService(Context.MIDI_SERVICE);
+        // Log.i(TAG, "++++   midiManager:" + midiManager);
+    }
+
+    @Override
+    public void onDestroy() {
+        // Log.i(TAG, "++++ onDestroy()");
+        super.onDestroy();
+    }
+
+    // For CTS testing, so I can read test fields.
+    public static NativeMidiEchoTestService getInstance() {
+        // Log.i(TAG, "++++ getInstance()");
+        return mInstance;
+    }
+
+    @Override
+    public MidiReceiver[] onGetInputPortReceivers() {
+        // Log.i(TAG, "++++ onGetInputPortReceivers()");
+        return new MidiReceiver[] { mInputReceiver };
+    }
+
+    class MyReceiver extends MidiReceiver {
+        @Override
+        public void onSend(byte[] data, int offset, int count, long timestamp)
+                throws IOException {
+            if (mOutputReceiver == null) {
+                mOutputReceiver = getOutputPortReceivers()[0];
+            }
+            // Copy input to output.
+            mOutputReceiver.send(data, offset, count, timestamp);
+        }
+    }
+
+    @Override
+    public void onDeviceStatusChanged(MidiDeviceStatus status) {
+        statusChangeCount++;
+        inputOpened = status.isInputPortOpen(0);
+        outputOpenCount = status.getOutputPortOpenCount(0);
+    }
+}
diff --git a/tests/tests/nativemidi/java/android/nativemidi/cts/NativeMidiMessage.java b/tests/tests/nativemidi/java/android/nativemidi/cts/NativeMidiMessage.java
new file mode 100644
index 0000000..3db1a89
--- /dev/null
+++ b/tests/tests/nativemidi/java/android/nativemidi/cts/NativeMidiMessage.java
@@ -0,0 +1,57 @@
+/*
+ * 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.nativemidi.cts;
+
+//
+// Corresponds to native AMIDI_Message.
+// Used to pass message data up from the native layer to the Java test routines.
+//
+//typedef struct {
+//    uint32_t opcode;
+//    uint8_t buffer[AMIDI_BUFFER_SIZE];
+//    size_t len;
+//    uint64_t timestamp;
+//} AMIDI_Message;
+
+public class NativeMidiMessage {
+    public static final int AMIDI_PACKET_SIZE = 1024;
+    public static final int AMIDI_PACKET_OVERHEAD = 9;
+    public static final int AMIDI_BUFFER_SIZE = AMIDI_PACKET_SIZE - AMIDI_PACKET_OVERHEAD;
+
+    public int opcode;
+    public byte[] buffer = new byte[AMIDI_BUFFER_SIZE];
+    public int len;
+    public long timestamp;
+    public long timeReceived;
+
+    public NativeMidiMessage() {}
+
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+        sb.append("{opcode:" + opcode + " [len:" + len + "|");
+        for(int index = 0; index < len; index++) {
+            sb.append("0x" + Integer.toHexString(buffer[index] & 0x00FF));
+            if (index < len - 1) {
+                sb.append(" ");
+            }
+        }
+        sb.append("] timestamp:" + Long.toHexString(timestamp));
+
+        return sb.toString();
+    }
+
+}
\ No newline at end of file
diff --git a/tests/tests/nativemidi/jni/Android.mk b/tests/tests/nativemidi/jni/Android.mk
new file mode 100644
index 0000000..91528bf
--- /dev/null
+++ b/tests/tests/nativemidi/jni/Android.mk
@@ -0,0 +1,35 @@
+# 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_MODULE := libnativemidi_jni
+
+LOCAL_SRC_FILES := \
+	native-lib.cpp
+
+LOCAL_CFLAGS += -Wall -Wextra -Werror -O0
+
+LOCAL_C_INCLUDES += \
+    frameworks/base/media/native
+
+LOCAL_CXX_STL := libc++_static
+
+LOCAL_SHARED_LIBRARIES := libmidi
+# LOCAL_SHARED_LIBRARIES := libOpenSLES libmidi
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/tests/tests/nativemidi/jni/native-lib.cpp b/tests/tests/nativemidi/jni/native-lib.cpp
new file mode 100644
index 0000000..f9e88bf
--- /dev/null
+++ b/tests/tests/nativemidi/jni/native-lib.cpp
@@ -0,0 +1,591 @@
+/*
+ * 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 <atomic>
+#include <inttypes.h>
+#include <stdio.h>
+#include <string>
+#include <time.h>
+#include <vector>
+
+#include <jni.h>
+
+#include <pthread.h>
+
+#include <utils/Errors.h>
+//#define LOG_NDEBUG 0
+#define LOG_TAG "NativeMidiManager-JNI"
+#include <utils/Log.h>
+
+#include <midi/midi.h>
+
+extern "C" {
+
+/*
+ * Structures for storing data flowing through the echo server.
+ */
+/*
+ * Received Messages
+ */
+typedef struct {
+    AMIDI_Message message;
+    long timeReceived;
+} ReceivedMessageRecord;
+
+/*
+ * Sent Messages
+ */
+typedef struct {
+    uint8_t buffer[AMIDI_BUFFER_SIZE];
+    size_t len;
+    uint64_t timestamp;
+    long timeSent;
+} SentMessageRecord;
+
+/*
+ * Context
+ * Holds the state of a given test and native MIDI I/O setup for that test.
+ */
+class TestContext {
+private:
+    // counters
+    std::atomic<int> mNumSends;
+    std::atomic<int> mNumBytesSent;
+    std::atomic<int> mNumReceives;
+    std::atomic<int> mNumBytesReceived;
+
+    pthread_mutex_t mLock;
+    std::vector<ReceivedMessageRecord> mReceivedMsgs;
+    std::vector<SentMessageRecord> mSentMsgs;
+
+    // Java NativeMidiMessage class stuff, for passing messages back out to the Java client.
+    jclass mClsNativeMidiMessage;
+    jmethodID mMidNativeMidiMessage_ctor;
+    jfieldID mFid_opcode;
+    jfieldID mFid_buffer;
+    jfieldID mFid_len;
+    jfieldID mFid_timestamp;
+    jfieldID mFid_timeReceived;
+
+public:
+    // read Thread
+    pthread_t mReadThread;
+    std::atomic<bool> mReading;
+
+    AMIDI_Device nativeReceiveDevice;
+    std::atomic<AMIDI_OutputPort> midiOutputPort;
+
+    AMIDI_Device nativeSendDevice;
+    std::atomic<AMIDI_InputPort> midiInputPort;
+
+    TestContext() :
+        mNumSends(0),
+        mNumBytesSent(0),
+        mNumReceives(0),
+        mNumBytesReceived(0),
+        mClsNativeMidiMessage(0),
+        mMidNativeMidiMessage_ctor(0),
+        mFid_opcode(0),
+        mFid_buffer(0),
+        mFid_len(0),
+        mFid_timestamp(0),
+        mFid_timeReceived(0),
+        mReadThread(0),
+        mReading(false),
+        nativeReceiveDevice(AMIDI_INVALID_HANDLE),
+        nativeSendDevice(AMIDI_INVALID_HANDLE)
+   {
+        pthread_mutex_init(&mLock, (const pthread_mutexattr_t *) NULL);
+
+        midiOutputPort.store(AMIDI_INVALID_HANDLE);
+        midiInputPort.store(AMIDI_INVALID_HANDLE);
+    }
+
+    void clearCounters() {
+        mNumSends = 0;
+        mNumBytesSent = 0;
+        mNumReceives = 0;
+        mNumBytesReceived = 0;
+    }
+
+    int getNumSends() { return mNumSends; }
+    void incNumSends() { mNumSends++; }
+
+    int getNumBytesSent() { return mNumBytesSent; }
+    void incNumBytesSent(int numBytes) { mNumBytesSent += numBytes; }
+
+    int getNumReceives() { return mNumReceives; }
+    void incNumReceives() { mNumReceives++; }
+
+    int getNumBytesReceived() { return mNumBytesReceived; }
+    void incNumBytesReceived(int numBytes) { mNumBytesReceived += numBytes; }
+
+    void addSent(SentMessageRecord msg) {
+        pthread_mutex_lock(&mLock);
+        mSentMsgs.push_back(msg);
+        pthread_mutex_unlock(&mLock);
+    }
+    size_t getNumSentMsgs() {
+        pthread_mutex_lock(&mLock);
+        size_t numMsgs = mSentMsgs.size();
+        pthread_mutex_unlock(&mLock);
+        return numMsgs;
+    }
+
+    bool initN(JNIEnv* j_env);
+
+    void addRecieved(ReceivedMessageRecord msg) {
+        pthread_mutex_lock(&mLock);
+        mReceivedMsgs.push_back(msg);
+        pthread_mutex_unlock(&mLock);
+    }
+    size_t getNumReceivedMsgs() {
+        pthread_mutex_lock(&mLock);
+        size_t numMsgs = mReceivedMsgs.size();
+        pthread_mutex_unlock(&mLock);
+        return numMsgs;
+    }
+
+    jobject j_getReceiveMsgAt(JNIEnv* j_env, int index);
+
+    static const int COMPARE_SUCCESS = 0;
+    static const int COMPARE_COUNTMISSMATCH = 1;
+    static const int COMPARE_DATALENMISMATCH = 2;
+    static const int COMPARE_DATAMISMATCH = 3;
+    static const int COMPARE_TIMESTAMPMISMATCH = 4;
+    int compareInsAndOuts();
+
+    static const int CHECKLATENCY_SUCCESS = 0;
+    static const int CHECKLATENCY_COUNTMISSMATCH = 1;
+    static const int CHECKLATENCY_LATENCYEXCEEDED = 2;
+    int checkInOutLatency(long maxLatencyNanos);
+};
+
+//
+// Helpers
+//
+static long System_nanoTime() {
+    // this code is the implementation of System.nanoTime()
+    // from system/code/ojluni/src/main/native/System.
+    struct timespec now;
+    clock_gettime(CLOCK_MONOTONIC, &now);
+    return now.tv_sec * 1000000000LL + now.tv_nsec;
+}
+
+bool TestContext::initN(JNIEnv* j_env) {
+    static const char* clsSigNativeMidiMessage = "android/nativemidi/cts/NativeMidiMessage";
+
+    mClsNativeMidiMessage =
+            (jclass)j_env->NewGlobalRef(j_env->FindClass(clsSigNativeMidiMessage));
+    if (mClsNativeMidiMessage == 0) {
+        return false;
+    }
+    mMidNativeMidiMessage_ctor = j_env->GetMethodID(mClsNativeMidiMessage, "<init>", "()V");
+
+    mFid_opcode = j_env->GetFieldID(mClsNativeMidiMessage, "opcode", "I");
+    mFid_buffer = j_env->GetFieldID(mClsNativeMidiMessage, "buffer", "[B");
+    mFid_len = j_env->GetFieldID(mClsNativeMidiMessage, "len", "I");
+    mFid_timestamp = j_env->GetFieldID(mClsNativeMidiMessage, "timestamp", "J");
+    mFid_timeReceived = j_env->GetFieldID(mClsNativeMidiMessage, "timeReceived", "J");
+//    ALOGI("---- [%p, %p, %p, %p, %p]",
+//        mFid_opcode, mFid_buffer, mFid_len, mFid_timestamp, mFid_timeReceived);
+    return mMidNativeMidiMessage_ctor != 0 &&
+            mFid_opcode != 0 &&
+            mFid_buffer != 0 &&
+            mFid_len != 0 &&
+            mFid_timestamp != 0 &&
+            mFid_timeReceived != 0;
+
+    return false;
+}
+
+jobject TestContext::j_getReceiveMsgAt(JNIEnv* j_env, int index) {
+    jobject msg = NULL;
+
+    pthread_mutex_lock(&mLock);
+
+    if (index < (int)mReceivedMsgs.size()) {
+        ReceivedMessageRecord receiveRec = mReceivedMsgs.at(index);
+        AMIDI_Message amidi_msg = receiveRec.message;
+
+        msg = j_env->NewObject(mClsNativeMidiMessage, mMidNativeMidiMessage_ctor);
+
+        j_env->SetIntField(msg, mFid_opcode, amidi_msg.opcode);
+        j_env->SetIntField(msg, mFid_len, amidi_msg.len);
+        jobject buffer_array = j_env->GetObjectField(msg, mFid_buffer);
+        j_env->SetByteArrayRegion(reinterpret_cast<jbyteArray>(buffer_array), 0,
+                sizeof(amidi_msg.buffer), (jbyte*)amidi_msg.buffer);
+        j_env->SetLongField(msg, mFid_timestamp, amidi_msg.timestamp);
+        j_env->SetLongField(msg, mFid_timeReceived, receiveRec.timeReceived);
+    }
+
+    pthread_mutex_unlock(&mLock);
+    return msg;
+}
+
+int TestContext::compareInsAndOuts() {
+    // Number of messages sent/received
+    pthread_mutex_lock(&mLock);
+
+    if (mReceivedMsgs.size() != mSentMsgs.size()) {
+        ALOGE("---- COMPARE_COUNTMISSMATCH r:%zu s:%zu", mReceivedMsgs.size(), mSentMsgs.size());
+        pthread_mutex_unlock(&mLock);
+       return COMPARE_COUNTMISSMATCH;
+    }
+
+    // we know that both vectors have the same number of messages from the test above.
+    size_t numMessages = mSentMsgs.size();
+    for (size_t msgIndex = 0; msgIndex < numMessages; msgIndex++) {
+        // Data Length?
+        if (mReceivedMsgs[msgIndex].message.len != mSentMsgs[msgIndex].len) {
+            ALOGE("---- COMPARE_DATALENMISMATCH r:%zu s:%zu",
+                    mReceivedMsgs[msgIndex].message.len, mSentMsgs[msgIndex].len);
+            pthread_mutex_unlock(&mLock);
+            return COMPARE_DATALENMISMATCH;
+        }
+
+        // Timestamps
+        if (mReceivedMsgs[msgIndex].message.timestamp != mSentMsgs[msgIndex].timestamp) {
+            ALOGE("---- COMPARE_TIMESTAMPMISMATCH");
+            pthread_mutex_unlock(&mLock);
+            return COMPARE_TIMESTAMPMISMATCH;
+        }
+
+        // we know that the data in both messages have the same number of bytes from the test above.
+        int dataLen = mReceivedMsgs[msgIndex].message.len;
+        for (int dataIndex = 0; dataIndex < dataLen; dataIndex++) {
+            // Data Values?
+            if (mReceivedMsgs[msgIndex].message.buffer[dataIndex] !=
+                    mSentMsgs[msgIndex].buffer[dataIndex]) {
+                ALOGE("---- COMPARE_DATAMISMATCH r:%d s:%d",
+                        (int)mReceivedMsgs[msgIndex].message.buffer[dataIndex],
+                        (int)mSentMsgs[msgIndex].buffer[dataIndex]);
+                pthread_mutex_unlock(&mLock);
+                return COMPARE_DATAMISMATCH;
+            }
+        }
+    }
+
+    pthread_mutex_unlock(&mLock);
+    return COMPARE_SUCCESS;
+}
+
+int TestContext::checkInOutLatency(long maxLatencyNanos) {
+    pthread_mutex_lock(&mLock);
+    if (mReceivedMsgs.size() != mSentMsgs.size()) {
+        pthread_mutex_unlock(&mLock);
+        return CHECKLATENCY_COUNTMISSMATCH;
+    }
+
+    // we know that both vectors have the same number of messages
+    // from the test above.
+    int numMessages = mSentMsgs.size();
+    for (int msgIndex = 0; msgIndex < numMessages; msgIndex++) {
+        long timeDelta =  mSentMsgs[msgIndex].timeSent - mReceivedMsgs[msgIndex].timeReceived;
+        // ALOGI("---- timeDelta:%ld", timeDelta);
+        if (timeDelta > maxLatencyNanos) {
+            pthread_mutex_unlock(&mLock);
+            return CHECKLATENCY_LATENCYEXCEEDED;
+        }
+    }
+
+    pthread_mutex_unlock(&mLock);
+    return CHECKLATENCY_SUCCESS;
+}
+
+JNIEXPORT jlong JNICALL Java_android_nativemidi_cts_NativeMidiEchoTest_allocTestContext(
+        JNIEnv* j_env, jclass) {
+    TestContext* context = new TestContext;
+    if (!context->initN(j_env)) {
+        delete context;
+        context = NULL;
+    }
+
+    return (jlong)context;
+}
+
+JNIEXPORT void JNICALL Java_android_nativemidi_cts_NativeMidiEchoTest_freeTestContext(
+        JNIEnv*, jclass, jlong context) {
+    delete (TestContext*)context;
+}
+
+/*
+ * Receiving API
+ */
+//static void DumpDataMessage(AMIDI_Message* msg) {
+//    char midiDumpBuffer[AMIDI_BUFFER_SIZE * 4]; // more than enough
+//    memset(midiDumpBuffer, 0, sizeof(midiDumpBuffer));
+//    int pos = snprintf(midiDumpBuffer, sizeof(midiDumpBuffer),
+//            "%" PRIx64 " ", msg->timestamp);
+//    for (uint8_t *b = msg->buffer, *e = b + msg->len; b < e; ++b) {
+//        pos += snprintf(midiDumpBuffer + pos, sizeof(midiDumpBuffer) - pos,
+//                "%02x ", *b);
+//    }
+//    ALOGI("---- DUMP %s", midiDumpBuffer);
+//}
+
+void* readThreadRoutine(void * ctx) {
+    TestContext* context  = (TestContext*)ctx;
+
+    context->mReading = true;
+    while (context->mReading) {
+        AMIDI_OutputPort outputPort = context->midiOutputPort.load();
+        if (outputPort != AMIDI_INVALID_HANDLE) {
+            // Amount of messages we are ready to handle during one callback cycle.
+            static const size_t MAX_INCOMING_MIDI_MESSAGES = 20;
+            AMIDI_Message incomingMidiMessages[MAX_INCOMING_MIDI_MESSAGES];
+            ssize_t midiReceived = AMIDI_receive(
+                    outputPort, incomingMidiMessages, MAX_INCOMING_MIDI_MESSAGES);
+            if (midiReceived >= 0) {
+                for (ssize_t i = 0; i < midiReceived; ++i) {
+                    AMIDI_Message* msg = &incomingMidiMessages[i];
+                    if (msg->opcode == AMIDI_OPCODE_DATA) {
+                        // DumpDataMessage(msg);
+                        context->incNumReceives();
+                        context->incNumBytesReceived(msg->len);
+                        ReceivedMessageRecord receiveRec;
+                        receiveRec.message = *msg;
+                        receiveRec.timeReceived = System_nanoTime();
+                        context->addRecieved(receiveRec);
+                    } else if (msg->opcode == AMIDI_OPCODE_FLUSH) {
+                        ALOGI("---- FLUSH %s", "MIDI flush");
+                    }
+                }
+            } else {
+                ALOGE("---- ! MIDI Receive error: %s !", strerror(-midiReceived));
+            }
+        }
+    }
+
+    return NULL;
+}
+
+static int commonDeviceOpen(int deviceId, AMIDI_Device* device) {
+    int result = AMIDI_getDeviceByUid(deviceId, device);
+    if (result == 0) {
+        ALOGI("----   Obtained device token for uid %d: token %d", deviceId, *device);
+    } else {
+        ALOGE("----   Could not obtain device token for uid %d: result:%d", deviceId, result);
+        return result;
+    }
+
+    AMIDI_DeviceInfo deviceInfo;
+    result = AMIDI_getDeviceInfo(*device, &deviceInfo);
+    if (result == 0) {
+        ALOGI("----   Device info: uid %d, type %d, priv %d, ports %d I / %d O",
+                deviceInfo.uid, deviceInfo.type, deviceInfo.is_private,
+                (int)deviceInfo.input_port_count, (int)deviceInfo.output_port_count);
+    } else {
+        ALOGE("----   Could not obtain device info %d", result);
+    }
+
+    return result;
+}
+
+/*
+ * Sending API
+ */
+JNIEXPORT jint JNICALL Java_android_nativemidi_cts_NativeMidiEchoTest_startWritingMidi(
+        JNIEnv*, jobject, jlong ctx, jint deviceId, jint portNumber) {
+
+    TestContext* context = (TestContext*)ctx;
+
+    int result = commonDeviceOpen(deviceId, &context->nativeSendDevice);
+    if (result != 0) {
+        return result;
+    }
+
+    AMIDI_InputPort inputPort;
+    result = AMIDI_openInputPort(context->nativeSendDevice, portNumber, &inputPort);
+    if (result == 0) {
+        ALOGI("---- Opened INPUT port %d: token %d", portNumber, inputPort);
+        context->midiInputPort.store(inputPort);
+    } else {
+        ALOGE("---- Could not open INPUT port %d: %d", deviceId, result);
+        return result;
+    }
+
+    return 0;
+}
+
+JNIEXPORT jint JNICALL Java_android_nativemidi_cts_NativeMidiEchoTest_stopWritingMidi(
+        JNIEnv*, jobject, jlong ctx) {
+
+    TestContext* context = (TestContext*)ctx;
+
+    AMIDI_InputPort inputPort = context->midiInputPort.exchange(AMIDI_INVALID_HANDLE);
+    if (inputPort == AMIDI_INVALID_HANDLE) {
+        return android::BAD_VALUE;
+    }
+
+    int result = AMIDI_closeInputPort(inputPort);
+    if (result == 0) {
+        ALOGI("---- Closed port by token %d", inputPort);
+    } else {
+        ALOGE("---- Could not close port by token %d: %d", inputPort, result);
+    }
+
+    return result;
+}
+
+JNIEXPORT jint JNICALL Java_android_nativemidi_cts_NativeMidiEchoTest_getMaxWriteBufferSize(
+        JNIEnv*, jobject, jlong ctx) {
+    TestContext* context = (TestContext*)ctx;
+    return AMIDI_getMaxMessageSizeInBytes(context->midiInputPort);
+}
+
+JNIEXPORT jint JNICALL Java_android_nativemidi_cts_NativeMidiEchoTest_writeMidiWithTimestamp(
+        JNIEnv* j_env, jobject,
+        jlong ctx, jbyteArray data, jint offset, jint numBytes, jlong timestamp) {
+
+    TestContext* context = (TestContext*)ctx;
+    context->incNumSends();
+    context->incNumBytesSent(numBytes);
+
+    jbyte* bufferPtr = j_env->GetByteArrayElements(data, NULL);
+    if (bufferPtr == NULL) {
+        return android::NO_MEMORY;
+    }
+
+    int numWritten = AMIDI_sendWithTimestamp(
+            context->midiInputPort, (uint8_t*)bufferPtr + offset, numBytes, timestamp);
+    if (numWritten > 0) {
+        // Don't save a send record if we didn't send!
+        SentMessageRecord sendRec;
+        memcpy(sendRec.buffer, (uint8_t*)bufferPtr + offset, numBytes);
+        sendRec.len = numBytes;
+        sendRec.timestamp = timestamp;
+        sendRec.timeSent = System_nanoTime();
+        context->addSent(sendRec);
+    }
+
+    j_env->ReleaseByteArrayElements(data, bufferPtr, JNI_ABORT);
+
+    return numWritten;
+}
+
+JNIEXPORT jint JNICALL Java_android_nativemidi_cts_NativeMidiEchoTest_writeMidi(
+        JNIEnv* j_env, jobject j_object, jlong ctx, jbyteArray data, jint offset, jint numBytes) {
+    return Java_android_nativemidi_cts_NativeMidiEchoTest_writeMidiWithTimestamp(
+            j_env, j_object, ctx, data, offset, numBytes, 0L);
+}
+
+JNIEXPORT jint JNICALL Java_android_nativemidi_cts_NativeMidiEchoTest_flushSentMessages(
+        JNIEnv*, jobject, jlong ctx) {
+    TestContext* context = (TestContext*)ctx;
+    return AMIDI_flush(context->midiInputPort);
+}
+
+JNIEXPORT jint JNICALL Java_android_nativemidi_cts_NativeMidiEchoTest_getNumSends(
+        JNIEnv*, jobject, jlong ctx) {
+    return ((TestContext*)ctx)->getNumSends();
+}
+
+JNIEXPORT jint JNICALL Java_android_nativemidi_cts_NativeMidiEchoTest_getNumBytesSent(
+        JNIEnv*, jobject, jlong ctx) {
+    return ((TestContext*)ctx)->getNumBytesSent();
+}
+
+JNIEXPORT jint JNICALL Java_android_nativemidi_cts_NativeMidiEchoTest_getNumReceives(
+        JNIEnv*, jobject, jlong ctx) {
+    return ((TestContext*)ctx)->getNumReceives();
+}
+
+JNIEXPORT jint JNICALL Java_android_nativemidi_cts_NativeMidiEchoTest_getNumBytesReceived(
+        JNIEnv*, jobject, jlong ctx) {
+    return ((TestContext*)ctx)->getNumBytesReceived();
+}
+
+JNIEXPORT void JNICALL Java_android_nativemidi_cts_NativeMidiEchoTest_clearCounters(
+        JNIEnv*, jobject, jlong ctx) {
+    ((TestContext*)ctx)->clearCounters();
+}
+
+JNIEXPORT jint JNICALL Java_android_nativemidi_cts_NativeMidiEchoTest_startReadingMidi(
+        JNIEnv*, jobject, jlong ctx, jint deviceId, jint portNumber) {
+
+    TestContext* context = (TestContext*)ctx;
+
+    int result = commonDeviceOpen(deviceId, &context->nativeReceiveDevice);
+    if (result != 0) {
+        return result;
+    }
+
+    AMIDI_OutputPort outputPort;
+    result = AMIDI_openOutputPort(context->nativeReceiveDevice, portNumber, &outputPort);
+    if (result == 0) {
+        ALOGI("---- Opened OUTPUT port %d: token %d", portNumber, outputPort);
+        context->midiOutputPort.store(outputPort);
+    } else {
+        ALOGE("---- Could not open OUTPUT port %d: %d", deviceId, result);
+        return result;
+    }
+
+    // Start read thread
+    int pthread_result =
+            pthread_create(&context->mReadThread, NULL, readThreadRoutine, (void*)context);
+    if (pthread_result != 0) {
+        ALOGE("---- pthread_create() Error %d, 0x%X", pthread_result, pthread_result);
+    }
+
+    return result;
+}
+
+JNIEXPORT jint JNICALL Java_android_nativemidi_cts_NativeMidiEchoTest_stopReadingMidi(
+        JNIEnv*, jobject, jlong ctx) {
+
+    TestContext* context = (TestContext*)ctx;
+    context->mReading = false;
+
+    AMIDI_OutputPort outputPort = context->midiOutputPort.exchange(AMIDI_INVALID_HANDLE);
+    if (outputPort == AMIDI_INVALID_HANDLE) {
+        return android::BAD_VALUE;
+    }
+
+    int result = AMIDI_closeOutputPort(outputPort);
+    if (result == 0) {
+        ALOGI("---- Closed OUTPUT port by token %d", outputPort);
+    } else {
+        ALOGI("---- Could not close port by token %d: %d", outputPort, result);
+    }
+
+    return result;
+}
+
+/*
+ * Messages
+ */
+JNIEXPORT jint JNICALL Java_android_nativemidi_cts_NativeMidiEchoTest_getNumReceivedMessages(
+        JNIEnv*, jobject, jlong ctx) {
+    return ((TestContext*)ctx)->getNumReceivedMsgs();
+}
+
+JNIEXPORT jobject JNICALL Java_android_nativemidi_cts_NativeMidiEchoTest_getReceivedMessageAt(
+        JNIEnv* j_env, jobject, jlong ctx, jint index) {
+    return ((TestContext*)ctx)->j_getReceiveMsgAt(j_env, index);
+}
+
+JNIEXPORT jint JNICALL Java_android_nativemidi_cts_NativeMidiEchoTest_matchNativeMessages(
+        JNIEnv*, jobject, jlong ctx) {
+    return ((TestContext*)ctx)->compareInsAndOuts();
+}
+
+JNIEXPORT jint JNICALL Java_android_nativemidi_cts_NativeMidiEchoTest_checkNativeLatency(
+        JNIEnv*, jobject, jlong ctx, jlong maxLatencyNanos) {
+    return ((TestContext*)ctx)->checkInOutLatency(maxLatencyNanos);
+}
+
+} // extern "C"
diff --git a/tests/tests/nativemidi/res/xml/echo_device_info.xml b/tests/tests/nativemidi/res/xml/echo_device_info.xml
new file mode 100644
index 0000000..d3fdda0
--- /dev/null
+++ b/tests/tests/nativemidi/res/xml/echo_device_info.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2015 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<devices>
+    <device manufacturer="AndroidCTS" product="NativeMidiEcho" tags="echo,test">
+        <input-port name="input" />
+        <output-port name="output" />
+    </device>
+</devices>
diff --git a/tests/tests/ndef/src/android/ndef/cts/NdefTest.java b/tests/tests/ndef/src/android/ndef/cts/NdefTest.java
index 47bdfa2..969806a 100644
--- a/tests/tests/ndef/src/android/ndef/cts/NdefTest.java
+++ b/tests/tests/ndef/src/android/ndef/cts/NdefTest.java
@@ -190,13 +190,13 @@
 
         // 3 records, 7 chunks
         assertEquals(new NdefMessage(
-                new NdefRecord(NdefRecord.TNF_EXTERNAL_TYPE, null, null, new byte[] {1,2,3,4}),
+                new NdefRecord(NdefRecord.TNF_EXTERNAL_TYPE, new byte[] {0x21}, null, new byte[] {1,2,3,4}),
                 new NdefRecord(NdefRecord.TNF_EMPTY, null, null, null),
-                new NdefRecord(NdefRecord.TNF_MIME_MEDIA, null, null, new byte[] {11,12,13,14})),
+                new NdefRecord(NdefRecord.TNF_MIME_MEDIA, new byte[] {0x21}, null, new byte[] {11,12,13,14})),
                 new NdefMessage(new byte[] {
-                        (byte)0xB4, 0, 1, 1, (byte)0x36, 0, 2, 2, 3, (byte)0x16, 0, 1, 4,
+                        (byte)0xB4, 1, 1, (byte)0x21, 1, (byte)0x36, 0, 2, 2, 3, (byte)0x16, 0, 1, 4,
                         (byte)0x10, 0, 0,
-                        (byte)0x32, 0, 2, 11, 12, (byte)0x36, 0, 1, 13, (byte)0x56, 0, 1, 14
+                        (byte)0x32, 1, 2, (byte)0x21, 11, 12, (byte)0x36, 0, 1, 13, (byte)0x56, 0, 1, 14
                 }));
 
         // 255 byte payload
diff --git a/tests/tests/net/assets/HSR1ProfileWithCACert.base64 b/tests/tests/net/assets/HSR1ProfileWithCACert.base64
new file mode 100644
index 0000000..995963d
--- /dev/null
+++ b/tests/tests/net/assets/HSR1ProfileWithCACert.base64
@@ -0,0 +1,85 @@
+Q29udGVudC1UeXBlOiBtdWx0aXBhcnQvbWl4ZWQ7IGJvdW5kYXJ5PXtib3VuZGFyeX0KQ29udGVu
+dC1UcmFuc2Zlci1FbmNvZGluZzogYmFzZTY0CgotLXtib3VuZGFyeX0KQ29udGVudC1UeXBlOiBh
+cHBsaWNhdGlvbi94LXBhc3Nwb2ludC1wcm9maWxlCkNvbnRlbnQtVHJhbnNmZXItRW5jb2Rpbmc6
+IGJhc2U2NAoKUEUxbmJYUlVjbVZsSUhodGJHNXpQU0p6ZVc1amJXdzZaRzFrWkdZeExqSWlQZ29n
+SUR4V1pYSkVWRVErTVM0eVBDOVdaWEpFVkVRKwpDaUFnUEU1dlpHVStDaUFnSUNBOFRtOWtaVTVo
+YldVK1VHVnlVSEp2ZG1sa1pYSlRkV0p6WTNKcGNIUnBiMjQ4TDA1dlpHVk9ZVzFsClBnb2dJQ0Fn
+UEZKVVVISnZjR1Z5ZEdsbGN6NEtJQ0FnSUNBZ1BGUjVjR1UrQ2lBZ0lDQWdJQ0FnUEVSRVJrNWhi
+V1UrZFhKdU9uZG0KWVRwdGJ6cG9iM1J6Y0c5ME1tUnZkREF0Y0dWeWNISnZkbWxrWlhKemRXSnpZ
+M0pwY0hScGIyNDZNUzR3UEM5RVJFWk9ZVzFsUGdvZwpJQ0FnSUNBOEwxUjVjR1UrQ2lBZ0lDQThM
+MUpVVUhKdmNHVnlkR2xsY3o0S0lDQWdJRHhPYjJSbFBnb2dJQ0FnSUNBOFRtOWtaVTVoCmJXVSth
+VEF3TVR3dlRtOWtaVTVoYldVK0NpQWdJQ0FnSUR4T2IyUmxQZ29nSUNBZ0lDQWdJRHhPYjJSbFRt
+RnRaVDVJYjIxbFUxQTgKTDA1dlpHVk9ZVzFsUGdvZ0lDQWdJQ0FnSUR4T2IyUmxQZ29nSUNBZ0lD
+QWdJQ0FnUEU1dlpHVk9ZVzFsUGtaeWFXVnVaR3g1VG1GdApaVHd2VG05a1pVNWhiV1UrQ2lBZ0lD
+QWdJQ0FnSUNBOFZtRnNkV1UrUTJWdWRIVnllU0JJYjNWelpUd3ZWbUZzZFdVK0NpQWdJQ0FnCklD
+QWdQQzlPYjJSbFBnb2dJQ0FnSUNBZ0lEeE9iMlJsUGdvZ0lDQWdJQ0FnSUNBZ1BFNXZaR1ZPWVcx
+bFBrWlJSRTQ4TDA1dlpHVk8KWVcxbFBnb2dJQ0FnSUNBZ0lDQWdQRlpoYkhWbFBtMXBOaTVqYnk1
+MWF6d3ZWbUZzZFdVK0NpQWdJQ0FnSUNBZ1BDOU9iMlJsUGdvZwpJQ0FnSUNBZ0lEeE9iMlJsUGdv
+Z0lDQWdJQ0FnSUNBZ1BFNXZaR1ZPWVcxbFBsSnZZVzFwYm1kRGIyNXpiM0owYVhWdFQwazhMMDV2
+ClpHVk9ZVzFsUGdvZ0lDQWdJQ0FnSUNBZ1BGWmhiSFZsUGpFeE1qSXpNeXcwTkRVMU5qWThMMVpo
+YkhWbFBnb2dJQ0FnSUNBZ0lEd3YKVG05a1pUNEtJQ0FnSUNBZ1BDOU9iMlJsUGdvZ0lDQWdJQ0E4
+VG05a1pUNEtJQ0FnSUNBZ0lDQThUbTlrWlU1aGJXVStRM0psWkdWdQpkR2xoYkR3dlRtOWtaVTVo
+YldVK0NpQWdJQ0FnSUNBZ1BFNXZaR1UrQ2lBZ0lDQWdJQ0FnSUNBOFRtOWtaVTVoYldVK1VtVmhi
+RzA4CkwwNXZaR1ZPWVcxbFBnb2dJQ0FnSUNBZ0lDQWdQRlpoYkhWbFBuTm9ZV3RsYmk1emRHbHlj
+bVZrTG1OdmJUd3ZWbUZzZFdVK0NpQWcKSUNBZ0lDQWdQQzlPYjJSbFBnb2dJQ0FnSUNBZ0lEeE9i
+MlJsUGdvZ0lDQWdJQ0FnSUNBZ1BFNXZaR1ZPWVcxbFBsVnpaWEp1WVcxbApVR0Z6YzNkdmNtUThM
+MDV2WkdWT1lXMWxQZ29nSUNBZ0lDQWdJQ0FnUEU1dlpHVStDaUFnSUNBZ0lDQWdJQ0FnSUR4T2Iy
+UmxUbUZ0ClpUNVZjMlZ5Ym1GdFpUd3ZUbTlrWlU1aGJXVStDaUFnSUNBZ0lDQWdJQ0FnSUR4V1lX
+eDFaVDVxWVcxbGN6d3ZWbUZzZFdVK0NpQWcKSUNBZ0lDQWdJQ0E4TDA1dlpHVStDaUFnSUNBZ0lD
+QWdJQ0E4VG05a1pUNEtJQ0FnSUNBZ0lDQWdJQ0FnUEU1dlpHVk9ZVzFsUGxCaApjM04zYjNKa1BD
+OU9iMlJsVG1GdFpUNEtJQ0FnSUNBZ0lDQWdJQ0FnUEZaaGJIVmxQbGx0T1hWYVJFRjNUbmM5UFR3
+dlZtRnNkV1UrCkNpQWdJQ0FnSUNBZ0lDQThMMDV2WkdVK0NpQWdJQ0FnSUNBZ0lDQThUbTlrWlQ0
+S0lDQWdJQ0FnSUNBZ0lDQWdQRTV2WkdWT1lXMWwKUGtWQlVFMWxkR2h2WkR3dlRtOWtaVTVoYldV
+K0NpQWdJQ0FnSUNBZ0lDQWdJRHhPYjJSbFBnb2dJQ0FnSUNBZ0lDQWdJQ0FnSUR4TwpiMlJsVG1G
+dFpUNUZRVkJVZVhCbFBDOU9iMlJsVG1GdFpUNEtJQ0FnSUNBZ0lDQWdJQ0FnSUNBOFZtRnNkV1Ur
+TWpFOEwxWmhiSFZsClBnb2dJQ0FnSUNBZ0lDQWdJQ0E4TDA1dlpHVStDaUFnSUNBZ0lDQWdJQ0Fn
+SUR4T2IyUmxQZ29nSUNBZ0lDQWdJQ0FnSUNBZ0lEeE8KYjJSbFRtRnRaVDVKYm01bGNrMWxkR2h2
+WkR3dlRtOWtaVTVoYldVK0NpQWdJQ0FnSUNBZ0lDQWdJQ0FnUEZaaGJIVmxQazFUTFVOSQpRVkF0
+VmpJOEwxWmhiSFZsUGdvZ0lDQWdJQ0FnSUNBZ0lDQThMMDV2WkdVK0NpQWdJQ0FnSUNBZ0lDQThM
+MDV2WkdVK0NpQWdJQ0FnCklDQWdQQzlPYjJSbFBnb2dJQ0FnSUNBZ0lEeE9iMlJsUGdvZ0lDQWdJ
+Q0FnSUNBZ1BFNXZaR1ZPWVcxbFBrUnBaMmwwWVd4RFpYSjAKYVdacFkyRjBaVHd2VG05a1pVNWhi
+V1UrQ2lBZ0lDQWdJQ0FnSUNBOFRtOWtaVDRLSUNBZ0lDQWdJQ0FnSUNBZ1BFNXZaR1ZPWVcxbApQ
+a05sY25ScFptbGpZWFJsVkhsd1pUd3ZUbTlrWlU1aGJXVStDaUFnSUNBZ0lDQWdJQ0FnSUR4V1lX
+eDFaVDU0TlRBNWRqTThMMVpoCmJIVmxQZ29nSUNBZ0lDQWdJQ0FnUEM5T2IyUmxQZ29nSUNBZ0lD
+QWdJQ0FnUEU1dlpHVStDaUFnSUNBZ0lDQWdJQ0FnSUR4T2IyUmwKVG1GdFpUNURaWEowVTBoQk1q
+VTJSbWx1WjJWeWNISnBiblE4TDA1dlpHVk9ZVzFsUGdvZ0lDQWdJQ0FnSUNBZ0lDQThWbUZzZFdV
+KwpNV1l4WmpGbU1XWXhaakZtTVdZeFpqRm1NV1l4WmpGbU1XWXhaakZtTVdZeFpqRm1NV1l4WmpG
+bU1XWXhaakZtTVdZeFpqRm1NV1l4ClpqRm1NV1l4Wmp3dlZtRnNkV1UrQ2lBZ0lDQWdJQ0FnSUNB
+OEwwNXZaR1UrQ2lBZ0lDQWdJQ0FnUEM5T2IyUmxQZ29nSUNBZ0lDQWcKSUR4T2IyUmxQZ29nSUNB
+Z0lDQWdJQ0FnUEU1dlpHVk9ZVzFsUGxOSlRUd3ZUbTlrWlU1aGJXVStDaUFnSUNBZ0lDQWdJQ0E4
+VG05awpaVDRLSUNBZ0lDQWdJQ0FnSUNBZ1BFNXZaR1ZPWVcxbFBrbE5VMGs4TDA1dlpHVk9ZVzFs
+UGdvZ0lDQWdJQ0FnSUNBZ0lDQThWbUZzCmRXVSthVzF6YVR3dlZtRnNkV1UrQ2lBZ0lDQWdJQ0Fn
+SUNBOEwwNXZaR1UrQ2lBZ0lDQWdJQ0FnSUNBOFRtOWtaVDRLSUNBZ0lDQWcKSUNBZ0lDQWdQRTV2
+WkdWT1lXMWxQa1ZCVUZSNWNHVThMMDV2WkdWT1lXMWxQZ29nSUNBZ0lDQWdJQ0FnSUNBOFZtRnNk
+V1UrTWpROApMMVpoYkhWbFBnb2dJQ0FnSUNBZ0lDQWdQQzlPYjJSbFBnb2dJQ0FnSUNBZ0lEd3ZU
+bTlrWlQ0S0lDQWdJQ0FnUEM5T2IyUmxQZ29nCklDQWdQQzlPYjJSbFBnb2dJRHd2VG05a1pUNEtQ
+QzlOWjIxMFZISmxaVDRLCgotLXtib3VuZGFyeX0KQ29udGVudC1UeXBlOiBhcHBsaWNhdGlvbi94
+LXg1MDktY2EtY2VydApDb250ZW50LVRyYW5zZmVyLUVuY29kaW5nOiBiYXNlNjQKCkxTMHRMUzFD
+UlVkSlRpQkRSVkpVU1VaSlEwRlVSUzB0TFMwdENrMUpTVVJMUkVORFFXaERaMEYzU1VKQlowbEtR
+VWxNYkVaa2QzcE0KVm5WeVRVRXdSME5UY1VkVFNXSXpSRkZGUWtOM1ZVRk5Ra2w0UlVSQlQwSm5U
+bFlLUWtGTlZFSXdWa0pWUTBKRVVWUkZkMGhvWTA1TgpWRmwzVFZSRmVVMVVSVEZOUkVVeFYyaGpU
+azFxV1hkTlZFRTFUVlJGTVUxRVJURlhha0ZUVFZKQmR3cEVaMWxFVmxGUlJFVjNaRVpSClZrRm5V
+VEJGZUUxSlNVSkpha0ZPUW1kcmNXaHJhVWM1ZHpCQ1FWRkZSa0ZCVDBOQlVUaEJUVWxKUWtOblMw
+TkJVVVZCQ25wdVFWQlYKZWpJMlRYTmhaVFIzY3pRelkzcFNOREV2U2pKUmRISlRTVnBWUzIxV1ZY
+TldkVzFFWWxsSWNsQk9kbFJZUzFOTldFRmpaWGRQVWtSUgpXVmdLVW5GMlNIWndiamhEYzJOQ01T
+dHZSMWhhZGtoM2VHbzBlbFl3VjB0dlN6SjZaVmhyWVhVemRtTjViRE5JU1V0MWNFcG1jVEpVClJV
+RkRaV1pXYW1vd2RBcEtWeXRZTXpWUVIxZHdPUzlJTlhwSlZVNVdUbFpxVXpkVmJYTTRORWwyUzJo
+U1FqZzFNVEpRUWpsVmVVaGgKWjFoWlZsZzFSMWR3UVdOV2NIbG1jbXhTQ2taSk9WRmthR2dyVUdK
+ck1IVjVhM1JrWW1ZdlEyUm1aMGhQYjJWaWNsUjBkMUpzYWswdwpiMFIwV0NzeVEzWTJhakIzUWtz
+M2FFUTRjRkIyWmpFcmRYa0tSM3BqZW1sblFWVXZORXQzTjJWYWNYbGtaamxDS3pWU2RYQlNLMGxh
+CmFYQllOREY0UldsSmNrdFNkM0ZwTlRFM1YxZDZXR05xWVVjeVkwNWlaalExTVFwNGNFZzFVRzVX
+TTJreGRIRXdOR3BOUjFGVmVrWjMKU1VSQlVVRkNielJIUVUxSU5IZElVVmxFVmxJd1QwSkNXVVZH
+U1hkWU5IWnpPRUpwUW1OVFkyOWtDalZ1YjFwSVVrMDRSVFFyYVUxRgpTVWRCTVZWa1NYZFJOMDFF
+YlVGR1NYZFlOSFp6T0VKcFFtTlRZMjlrTlc1dldraFNUVGhGTkN0cGIxSmhhMFpFUVZNS1RWSkJk
+MFJuCldVUldVVkZFUlhka1JsRldRV2RSTUVWNFoyZHJRV2QxVlZZelJFMTBWelp6ZDBSQldVUldV
+akJVUWtGVmQwRjNSVUl2ZWtGTVFtZE8KVmdwSVVUaEZRa0ZOUTBGUldYZEVVVmxLUzI5YVNXaDJZ
+MDVCVVVWTVFsRkJSR2RuUlVKQlJtWlJjVTlVUVRkU2RqZExLMngxVVRkdwpibUZ6TkVKWmQwaEZD
+amxIUlZBdmRXOW9kalpMVDNrd1ZFZFJSbUp5VWxScVJtOU1WazVDT1VKYU1YbHRUVVJhTUM5VVNY
+ZEpWV00zCmQyazNZVGgwTlcxRmNWbElNVFV6ZDFjS1lWZHZiMmxUYW5sTVRHaDFTVFJ6VG5KT1Ew
+OTBhWE5rUW5FeWNqSk5SbGgwTm1nd2JVRlIKV1U5UWRqaFNPRXMzTDJablUzaEhSbkY2YUhsT2JX
+MVdUQW94Y1VKS2JHUjRNelJUY0hkelZFRk1VVlpRWWpSb1IzZEtlbHBtY2pGUQpZM0JGVVhnMmVF
+MXVWR3c0ZUVWWFdrVXpUWE01T1hWaFZYaGlVWEZKZDFKMUNreG5RVTlyVGtOdFdUSnRPRGxXYUhw
+aFNFb3hkVlk0Ck5VRmtUUzkwUkN0WmMyMXNibTVxZERsTVVrTmxhbUpDYVhCcVNVZHFUMWh5WnpG
+S1VDdHNlRllLYlhWTk5IWklLMUF2Yld4dGVITlEKVUhvd1pEWTFZaXRGUjIxS1duQnZUR3RQTDNS
+a1RrNTJRMWw2YWtwd1ZFVlhjRVZ6VHpaT1RXaExXVzg5Q2kwdExTMHRSVTVFSUVORgpVbFJKUmts
+RFFWUkZMUzB0TFMwSwotLXtib3VuZGFyeX0tLQo=
diff --git a/tests/tests/net/assets/PerProviderSubscription.xml b/tests/tests/net/assets/PerProviderSubscription.xml
new file mode 100644
index 0000000..7f2d95d
--- /dev/null
+++ b/tests/tests/net/assets/PerProviderSubscription.xml
@@ -0,0 +1,399 @@
+<MgmtTree xmlns="syncml:dmddf1.2">
+  <VerDTD>1.2</VerDTD>
+  <Node>
+    <NodeName>PerProviderSubscription</NodeName>
+    <RTProperties>
+      <Type>
+        <DDFName>urn:wfa:mo:hotspot2dot0-perprovidersubscription:1.0</DDFName>
+      </Type>
+    </RTProperties>
+    <Node>
+      <NodeName>UpdateIdentifier</NodeName>
+      <Value>12</Value>
+    </Node>
+    <Node>
+      <NodeName>i001</NodeName>
+      <Node>
+        <NodeName>HomeSP</NodeName>
+        <Node>
+          <NodeName>FriendlyName</NodeName>
+          <Value>Century House</Value>
+        </Node>
+        <Node>
+          <NodeName>FQDN</NodeName>
+          <Value>mi6.co.uk</Value>
+        </Node>
+        <Node>
+          <NodeName>RoamingConsortiumOI</NodeName>
+          <Value>112233,445566</Value>
+        </Node>
+        <Node>
+          <NodeName>IconURL</NodeName>
+          <Value>icon.test.com</Value>
+        </Node>
+        <Node>
+          <NodeName>NetworkID</NodeName>
+          <Node>
+            <NodeName>n001</NodeName>
+            <Node>
+              <NodeName>SSID</NodeName>
+              <Value>TestSSID</Value>
+            </Node>
+            <Node>
+              <NodeName>HESSID</NodeName>
+              <Value>12345678</Value>
+            </Node>
+          </Node>
+          <Node>
+            <NodeName>n002</NodeName>
+            <Node>
+              <NodeName>SSID</NodeName>
+              <Value>NullHESSID</Value>
+            </Node>
+          </Node>
+        </Node>
+        <Node>
+          <NodeName>HomeOIList</NodeName>
+          <Node>
+            <NodeName>h001</NodeName>
+            <Node>
+              <NodeName>HomeOI</NodeName>
+              <Value>11223344</Value>
+            </Node>
+            <Node>
+              <NodeName>HomeOIRequired</NodeName>
+              <Value>true</Value>
+            </Node>
+          </Node>
+          <Node>
+            <NodeName>h002</NodeName>
+            <Node>
+              <NodeName>HomeOI</NodeName>
+              <Value>55667788</Value>
+            </Node>
+            <Node>
+              <NodeName>HomeOIRequired</NodeName>
+              <Value>false</Value>
+            </Node>
+          </Node>
+        </Node>
+        <Node>
+          <NodeName>OtherHomePartners</NodeName>
+          <Node>
+            <NodeName>o001</NodeName>
+            <Node>
+              <NodeName>FQDN</NodeName>
+              <Value>other.fqdn.com</Value>
+            </Node>
+          </Node>
+        </Node>
+      </Node>
+      <Node>
+        <NodeName>Credential</NodeName>
+        <Node>
+          <NodeName>CreationDate</NodeName>
+          <Value>2016-01-01T10:00:00Z</Value>
+        </Node>
+        <Node>
+          <NodeName>ExpirationDate</NodeName>
+          <Value>2016-02-01T10:00:00Z</Value>
+        </Node>
+        <Node>
+          <NodeName>Realm</NodeName>
+          <Value>shaken.stirred.com</Value>
+        </Node>
+        <Node>
+          <NodeName>CheckAAAServerCertStatus</NodeName>
+          <Value>true</Value>
+        </Node>
+        <Node>
+          <NodeName>UsernamePassword</NodeName>
+          <Node>
+            <NodeName>Username</NodeName>
+            <Value>james</Value>
+          </Node>
+          <Node>
+            <NodeName>Password</NodeName>
+            <Value>Ym9uZDAwNw==</Value>
+          </Node>
+          <Node>
+            <NodeName>MachineManaged</NodeName>
+            <Value>true</Value>
+          </Node>
+          <Node>
+            <NodeName>SoftTokenApp</NodeName>
+            <Value>TestApp</Value>
+          </Node>
+          <Node>
+            <NodeName>AbleToShare</NodeName>
+            <Value>true</Value>
+          </Node>
+          <Node>
+            <NodeName>EAPMethod</NodeName>
+            <Node>
+              <NodeName>EAPType</NodeName>
+              <Value>21</Value>
+            </Node>
+            <Node>
+              <NodeName>InnerMethod</NodeName>
+              <Value>MS-CHAP-V2</Value>
+            </Node>
+          </Node>
+        </Node>
+        <Node>
+          <NodeName>DigitalCertificate</NodeName>
+          <Node>
+            <NodeName>CertificateType</NodeName>
+            <Value>x509v3</Value>
+          </Node>
+          <Node>
+            <NodeName>CertSHA256Fingerprint</NodeName>
+            <Value>1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f</Value>
+          </Node>
+        </Node>
+        <Node>
+          <NodeName>SIM</NodeName>
+          <Node>
+            <NodeName>IMSI</NodeName>
+            <Value>imsi</Value>
+          </Node>
+          <Node>
+            <NodeName>EAPType</NodeName>
+            <Value>24</Value>
+          </Node>
+        </Node>
+      </Node>
+      <Node>
+        <NodeName>Policy</NodeName>
+        <Node>
+          <NodeName>PreferredRoamingPartnerList</NodeName>
+          <Node>
+            <NodeName>p001</NodeName>
+            <Node>
+              <NodeName>FQDN_Match</NodeName>
+              <Value>test1.fqdn.com,exactMatch</Value>
+            </Node>
+            <Node>
+              <NodeName>Priority</NodeName>
+              <Value>127</Value>
+            </Node>
+            <Node>
+              <NodeName>Country</NodeName>
+              <Value>us,fr</Value>
+            </Node>
+          </Node>
+          <Node>
+            <NodeName>p002</NodeName>
+            <Node>
+              <NodeName>FQDN_Match</NodeName>
+              <Value>test2.fqdn.com,includeSubdomains</Value>
+            </Node>
+            <Node>
+              <NodeName>Priority</NodeName>
+              <Value>200</Value>
+            </Node>
+            <Node>
+              <NodeName>Country</NodeName>
+              <Value>*</Value>
+            </Node>
+          </Node>
+        </Node>
+        <Node>
+          <NodeName>MinBackhaulThreshold</NodeName>
+          <Node>
+            <NodeName>m001</NodeName>
+            <Node>
+              <NodeName>NetworkType</NodeName>
+              <Value>home</Value>
+            </Node>
+            <Node>
+              <NodeName>DLBandwidth</NodeName>
+              <Value>23412</Value>
+            </Node>
+            <Node>
+              <NodeName>ULBandwidth</NodeName>
+              <Value>9823</Value>
+            </Node>
+          </Node>
+          <Node>
+            <NodeName>m002</NodeName>
+            <Node>
+              <NodeName>NetworkType</NodeName>
+              <Value>roaming</Value>
+            </Node>
+            <Node>
+              <NodeName>DLBandwidth</NodeName>
+              <Value>9271</Value>
+            </Node>
+            <Node>
+              <NodeName>ULBandwidth</NodeName>
+              <Value>2315</Value>
+            </Node>
+          </Node>
+        </Node>
+        <Node>
+          <NodeName>PolicyUpdate</NodeName>
+          <Node>
+            <NodeName>UpdateInterval</NodeName>
+            <Value>120</Value>
+          </Node>
+          <Node>
+            <NodeName>UpdateMethod</NodeName>
+            <Value>OMA-DM-ClientInitiated</Value>
+          </Node>
+          <Node>
+            <NodeName>Restriction</NodeName>
+            <Value>HomeSP</Value>
+          </Node>
+          <Node>
+            <NodeName>URI</NodeName>
+            <Value>policy.update.com</Value>
+          </Node>
+          <Node>
+            <NodeName>UsernamePassword</NodeName>
+            <Node>
+              <NodeName>Username</NodeName>
+              <Value>updateUser</Value>
+            </Node>
+            <Node>
+              <NodeName>Password</NodeName>
+              <Value>updatePass</Value>
+            </Node>
+          </Node>
+          <Node>
+            <NodeName>TrustRoot</NodeName>
+            <Node>
+              <NodeName>CertURL</NodeName>
+              <Value>update.cert.com</Value>
+            </Node>
+            <Node>
+              <NodeName>CertSHA256Fingerprint</NodeName>
+              <Value>1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f</Value>
+            </Node>
+          </Node>
+        </Node>
+        <Node>
+          <NodeName>SPExclusionList</NodeName>
+          <Node>
+            <NodeName>s001</NodeName>
+            <Node>
+              <NodeName>SSID</NodeName>
+              <Value>excludeSSID</Value>
+            </Node>
+          </Node>
+        </Node>
+        <Node>
+          <NodeName>RequiredProtoPortTuple</NodeName>
+          <Node>
+            <NodeName>r001</NodeName>
+            <Node>
+              <NodeName>IPProtocol</NodeName>
+              <Value>12</Value>
+            </Node>
+            <Node>
+              <NodeName>PortNumber</NodeName>
+              <Value>34,92,234</Value>
+            </Node>
+          </Node>
+        </Node>
+        <Node>
+          <NodeName>MaximumBSSLoadValue</NodeName>
+          <Value>23</Value>
+        </Node>
+      </Node>
+      <Node>
+        <NodeName>CredentialPriority</NodeName>
+        <Value>99</Value>
+      </Node>
+      <Node>
+        <NodeName>AAAServerTrustRoot</NodeName>
+        <Node>
+          <NodeName>a001</NodeName>
+          <Node>
+            <NodeName>CertURL</NodeName>
+            <Value>server1.trust.root.com</Value>
+          </Node>
+          <Node>
+            <NodeName>CertSHA256Fingerprint</NodeName>
+            <Value>1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f</Value>
+          </Node>
+        </Node>
+      </Node>
+      <Node>
+        <NodeName>SubscriptionUpdate</NodeName>
+        <Node>
+          <NodeName>UpdateInterval</NodeName>
+          <Value>120</Value>
+        </Node>
+        <Node>
+          <NodeName>UpdateMethod</NodeName>
+          <Value>SSP-ClientInitiated</Value>
+        </Node>
+        <Node>
+          <NodeName>Restriction</NodeName>
+          <Value>RoamingPartner</Value>
+        </Node>
+        <Node>
+          <NodeName>URI</NodeName>
+          <Value>subscription.update.com</Value>
+        </Node>
+        <Node>
+          <NodeName>UsernamePassword</NodeName>
+          <Node>
+            <NodeName>Username</NodeName>
+            <Value>subscriptionUser</Value>
+          </Node>
+          <Node>
+            <NodeName>Password</NodeName>
+            <Value>subscriptionPass</Value>
+          </Node>
+        </Node>
+        <Node>
+          <NodeName>TrustRoot</NodeName>
+          <Node>
+            <NodeName>CertURL</NodeName>
+            <Value>subscription.update.cert.com</Value>
+          </Node>
+          <Node>
+            <NodeName>CertSHA256Fingerprint</NodeName>
+            <Value>1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f1f</Value>
+          </Node>
+        </Node>
+      </Node>
+      <Node>
+        <NodeName>SubscriptionParameter</NodeName>
+        <Node>
+          <NodeName>CreationDate</NodeName>
+          <Value>2016-02-01T10:00:00Z</Value>
+        </Node>
+        <Node>
+          <NodeName>ExpirationDate</NodeName>
+          <Value>2016-03-01T10:00:00Z</Value>
+        </Node>
+        <Node>
+          <NodeName>TypeOfSubscription</NodeName>
+          <Value>Gold</Value>
+        </Node>
+        <Node>
+          <NodeName>UsageLimits</NodeName>
+          <Node>
+            <NodeName>DataLimit</NodeName>
+            <Value>921890</Value>
+          </Node>
+          <Node>
+            <NodeName>StartDate</NodeName>
+            <Value>2016-12-01T10:00:00Z</Value>
+          </Node>
+          <Node>
+            <NodeName>TimeLimit</NodeName>
+            <Value>120</Value>
+          </Node>
+          <Node>
+            <NodeName>UsageTimePeriod</NodeName>
+            <Value>99910</Value>
+          </Node>
+        </Node>
+      </Node>
+    </Node>
+  </Node>
+</MgmtTree>
diff --git a/tests/tests/net/src/android/net/cts/ConnectivityManagerTest.java b/tests/tests/net/src/android/net/cts/ConnectivityManagerTest.java
index 185ebfa..24871ca 100644
--- a/tests/tests/net/src/android/net/cts/ConnectivityManagerTest.java
+++ b/tests/tests/net/src/android/net/cts/ConnectivityManagerTest.java
@@ -18,6 +18,7 @@
 
 import static android.net.NetworkCapabilities.NET_CAPABILITY_IMS;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
+import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
 
 import android.app.PendingIntent;
 import android.content.BroadcastReceiver;
@@ -384,6 +385,57 @@
     }
 
     /**
+     * Exercises the requestNetwork with NetworkCallback API. This checks to
+     * see if we get a callback for an INTERNET request.
+     */
+    public void testRequestNetworkCallback() {
+        final TestNetworkCallback callback = new TestNetworkCallback();
+        mCm.requestNetwork(new NetworkRequest.Builder()
+                .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
+                .build(), callback);
+
+        try {
+            // Wait to get callback for availability of internet
+            Network internetNetwork = callback.waitForAvailable();
+            assertNotNull("Did not receive NetworkCallback#onAvailable for INTERNET",
+                    internetNetwork);
+        } catch (InterruptedException e) {
+            fail("NetworkCallback wait was interrupted.");
+        } finally {
+            mCm.unregisterNetworkCallback(callback);
+        }
+    }
+
+    /**
+     * Exercises the requestNetwork with NetworkCallback API with timeout - expected to
+     * fail. Use WIFI and switch Wi-Fi off.
+     */
+    public void testRequestNetworkCallback_onUnavailable() {
+        final boolean previousWifiEnabledState = mWifiManager.isWifiEnabled();
+        if (previousWifiEnabledState) {
+            disconnectFromWifi(null);
+        }
+
+        final TestNetworkCallback callback = new TestNetworkCallback();
+        mCm.requestNetwork(new NetworkRequest.Builder()
+                .addTransportType(TRANSPORT_WIFI)
+                .build(), 100, callback);
+
+        try {
+            // Wait to get callback for unavailability of requested network
+            assertTrue("Did not receive NetworkCallback#onUnavailable",
+                    callback.waitForUnavailable());
+        } catch (InterruptedException e) {
+            fail("NetworkCallback wait was interrupted.");
+        } finally {
+            mCm.unregisterNetworkCallback(callback);
+            if (previousWifiEnabledState) {
+                connectToWifi();
+            }
+        }
+    }
+
+    /**
      * Tests reporting of connectivity changed.
      */
     public void testConnectivityChanged_manifestRequestOnly_shouldNotReceiveIntent() {
@@ -639,6 +691,7 @@
     private static class TestNetworkCallback extends ConnectivityManager.NetworkCallback {
         private final CountDownLatch mAvailableLatch = new CountDownLatch(1);
         private final CountDownLatch mLostLatch = new CountDownLatch(1);
+        private final CountDownLatch mUnavailableLatch = new CountDownLatch(1);
 
         public Network currentNetwork;
         public Network lastLostNetwork;
@@ -651,6 +704,11 @@
             return mLostLatch.await(30, TimeUnit.SECONDS) ? lastLostNetwork : null;
         }
 
+        public boolean waitForUnavailable() throws InterruptedException {
+            return mUnavailableLatch.await(2, TimeUnit.SECONDS);
+        }
+
+
         @Override
         public void onAvailable(Network network) {
             currentNetwork = network;
@@ -665,6 +723,11 @@
             }
             mLostLatch.countDown();
         }
+
+        @Override
+        public void onUnavailable() {
+            mUnavailableLatch.countDown();
+        }
     }
 
     private Network getWifiNetwork() {
diff --git a/tests/tests/net/src/android/net/wifi/aware/cts/SingleDeviceTest.java b/tests/tests/net/src/android/net/wifi/aware/cts/SingleDeviceTest.java
new file mode 100644
index 0000000..fcd0454
--- /dev/null
+++ b/tests/tests/net/src/android/net/wifi/aware/cts/SingleDeviceTest.java
@@ -0,0 +1,675 @@
+/*
+ * 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.net.wifi.aware.cts;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.net.ConnectivityManager;
+import android.net.NetworkCapabilities;
+import android.net.NetworkRequest;
+import android.net.wifi.WifiManager;
+import android.net.wifi.aware.AttachCallback;
+import android.net.wifi.aware.Characteristics;
+import android.net.wifi.aware.DiscoverySessionCallback;
+import android.net.wifi.aware.IdentityChangedListener;
+import android.net.wifi.aware.PeerHandle;
+import android.net.wifi.aware.PublishConfig;
+import android.net.wifi.aware.PublishDiscoverySession;
+import android.net.wifi.aware.SubscribeConfig;
+import android.net.wifi.aware.SubscribeDiscoverySession;
+import android.net.wifi.aware.WifiAwareManager;
+import android.net.wifi.aware.WifiAwareSession;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.test.AndroidTestCase;
+
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Wi-Fi Aware CTS test suite: single device testing. Performs tests on a single
+ * device to validate Wi-Fi Aware.
+ */
+public class SingleDeviceTest extends AndroidTestCase {
+    private static final String TAG = "WifiAwareCtsTests";
+
+    // wait for Wi-Fi Aware to become available
+    static private final int WAIT_FOR_AWARE_CHANGE_SECS = 10;
+
+    private final Object mLock = new Object();
+    private final HandlerThread mHandlerThread = new HandlerThread("SingleDeviceTest");
+    private final Handler mHandler;
+    {
+        mHandlerThread.start();
+        mHandler = new Handler(mHandlerThread.getLooper());
+    }
+
+    private WifiAwareManager mWifiAwareManager;
+    private WifiManager mWifiManager;
+    private WifiManager.WifiLock mWifiLock;
+    private ConnectivityManager mConnectivityManager;
+
+    // used to store any WifiAwareSession allocated during tests - will clean-up after tests
+    private List<WifiAwareSession> mSessions = new ArrayList<>();
+
+    private class WifiAwareBroadcastReceiver extends BroadcastReceiver {
+        private CountDownLatch mBlocker = new CountDownLatch(1);
+
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            if (WifiAwareManager.ACTION_WIFI_AWARE_STATE_CHANGED.equals(intent.getAction())) {
+                mBlocker.countDown();
+            }
+        }
+
+        boolean waitForStateChange() throws InterruptedException {
+            return mBlocker.await(WAIT_FOR_AWARE_CHANGE_SECS, TimeUnit.SECONDS);
+        }
+    }
+
+    private class AttachCallbackTest extends AttachCallback {
+        static final int ATTACHED = 0;
+        static final int ATTACH_FAILED = 1;
+        static final int ERROR = 2; // no callback: timeout, interruption
+
+        private CountDownLatch mBlocker = new CountDownLatch(1);
+        private int mCallbackCalled = ERROR; // garbage init
+        private WifiAwareSession mSession = null;
+
+        @Override
+        public void onAttached(WifiAwareSession session) {
+            mCallbackCalled = ATTACHED;
+            mSession = session;
+            synchronized (mLock) {
+                mSessions.add(session);
+            }
+            mBlocker.countDown();
+        }
+
+        @Override
+        public void onAttachFailed() {
+            mCallbackCalled = ATTACH_FAILED;
+            mBlocker.countDown();
+        }
+
+        /**
+         * Waits for any of the callbacks to be called - or an error (timeout, interruption).
+         * Returns one of the ATTACHED, ATTACH_FAILED, or ERROR values.
+         */
+        int waitForAnyCallback() {
+            try {
+                boolean noTimeout = mBlocker.await(WAIT_FOR_AWARE_CHANGE_SECS, TimeUnit.SECONDS);
+                if (noTimeout) {
+                    return mCallbackCalled;
+                } else {
+                    return ERROR;
+                }
+            } catch (InterruptedException e) {
+                return ERROR;
+            }
+        }
+
+        /**
+         * Access the session created by a callback. Only useful to be called after calling
+         * waitForAnyCallback() and getting the ATTACHED code back.
+         */
+        WifiAwareSession getSession() {
+            return mSession;
+        }
+    }
+
+    private class IdentityChangedListenerTest extends IdentityChangedListener {
+        private CountDownLatch mBlocker = new CountDownLatch(1);
+        private byte[] mMac = null;
+
+        @Override
+        public void onIdentityChanged(byte[] mac) {
+            mMac = mac;
+            mBlocker.countDown();
+        }
+
+        /**
+         * Waits for the listener callback to be called - or an error (timeout, interruption).
+         * Returns true on callback called, false on error (timeout, interruption).
+         */
+        boolean waitForListener() {
+            try {
+                return mBlocker.await(WAIT_FOR_AWARE_CHANGE_SECS, TimeUnit.SECONDS);
+            } catch (InterruptedException e) {
+                return false;
+            }
+        }
+
+        /**
+         * Returns the MAC address of the discovery interface supplied to the triggered callback.
+         */
+        byte[] getMac() {
+            return mMac;
+        }
+    }
+
+    private class DiscoverySessionCallbackTest extends DiscoverySessionCallback {
+        static final int ON_PUBLISH_STARTED = 0;
+        static final int ON_SUBSCRIBE_STARTED = 1;
+        static final int ON_SESSION_CONFIG_UPDATED = 2;
+        static final int ON_SESSION_CONFIG_FAILED = 3;
+        static final int ON_SESSION_TERMINATED = 4;
+        static final int ON_SERVICE_DISCOVERED = 5;
+        static final int ON_MESSAGE_SEND_SUCCEEDED = 6;
+        static final int ON_MESSAGE_SEND_FAILED = 7;
+        static final int ON_MESSAGE_RECEIVED = 8;
+
+        private final Object mLocalLock = new Object();
+
+        private CountDownLatch mBlocker;
+        private int mCurrentWaitForCallback;
+        private ArrayDeque<Integer> mCallbackQueue = new ArrayDeque<>();
+
+        private PublishDiscoverySession mPublishDiscoverySession;
+        private SubscribeDiscoverySession mSubscribeDiscoverySession;
+
+        private void processCallback(int callback) {
+            synchronized (mLocalLock) {
+                if (mBlocker != null && mCurrentWaitForCallback == callback) {
+                    mBlocker.countDown();
+                } else {
+                    mCallbackQueue.addLast(callback);
+                }
+            }
+        }
+
+        @Override
+        public void onPublishStarted(PublishDiscoverySession session) {
+            mPublishDiscoverySession = session;
+            processCallback(ON_PUBLISH_STARTED);
+        }
+
+        @Override
+        public void onSubscribeStarted(SubscribeDiscoverySession session) {
+            mSubscribeDiscoverySession = session;
+            processCallback(ON_SUBSCRIBE_STARTED);
+        }
+
+        @Override
+        public void onSessionConfigUpdated() {
+            processCallback(ON_SESSION_CONFIG_UPDATED);
+        }
+
+        @Override
+        public void onSessionConfigFailed() {
+            processCallback(ON_SESSION_CONFIG_FAILED);
+        }
+
+        @Override
+        public void onSessionTerminated() {
+            processCallback(ON_SESSION_TERMINATED);
+        }
+
+        @Override
+        public void onServiceDiscovered(PeerHandle peerHandle, byte[] serviceSpecificInfo,
+                List<byte[]> matchFilter) {
+            processCallback(ON_SERVICE_DISCOVERED);
+        }
+
+        @Override
+        public void onMessageSendSucceeded(int messageId) {
+            processCallback(ON_MESSAGE_SEND_SUCCEEDED);
+        }
+
+        @Override
+        public void onMessageSendFailed(int messageId) {
+            processCallback(ON_MESSAGE_SEND_FAILED);
+        }
+
+        @Override
+        public void onMessageReceived(PeerHandle peerHandle, byte[] message) {
+            processCallback(ON_MESSAGE_RECEIVED);
+        }
+
+        /**
+         * Wait for the specified callback - any of the ON_* constants. Returns a true
+         * on success (specified callback triggered) or false on failure (timed-out or
+         * interrupted while waiting for the requested callback).
+         *
+         * Note: other callbacks happening while while waiting for the specified callback will
+         * be queued.
+         */
+        boolean waitForCallback(int callback) {
+            synchronized (mLocalLock) {
+                boolean found = mCallbackQueue.remove(callback);
+                if (found) {
+                    return true;
+                }
+
+                mCurrentWaitForCallback = callback;
+                mBlocker = new CountDownLatch(1);
+            }
+
+            try {
+                return mBlocker.await(WAIT_FOR_AWARE_CHANGE_SECS, TimeUnit.SECONDS);
+            } catch (InterruptedException e) {
+                return false;
+            }
+        }
+
+        /**
+         * Indicates whether the specified callback (any of the ON_* constants) has already
+         * happened and in the queue. Useful when the order of events is important.
+         */
+        boolean hasCallbackAlreadyHappened(int callback) {
+            synchronized (mLocalLock) {
+                return mCallbackQueue.contains(callback);
+            }
+        }
+
+        /**
+         * Returns the last created publish discovery session.
+         */
+        PublishDiscoverySession getPublishDiscoverySession() {
+            PublishDiscoverySession session = mPublishDiscoverySession;
+            mPublishDiscoverySession = null;
+            return session;
+        }
+
+        /**
+         * Returns the last created subscribe discovery session.
+         */
+        SubscribeDiscoverySession getSubscribeDiscoverySession() {
+            SubscribeDiscoverySession session = mSubscribeDiscoverySession;
+            mSubscribeDiscoverySession = null;
+            return session;
+        }
+    }
+
+    private class NetworkCallbackTest extends ConnectivityManager.NetworkCallback {
+        private CountDownLatch mBlocker = new CountDownLatch(1);
+
+        @Override
+        public void onUnavailable() {
+            mBlocker.countDown();
+        }
+
+        /**
+         * Wait for the onUnavailable() callback to be triggered. Returns true if triggered,
+         * otherwise (timed-out, interrupted) returns false.
+         */
+        boolean waitForOnUnavailable() {
+            try {
+                return mBlocker.await(WAIT_FOR_AWARE_CHANGE_SECS, TimeUnit.SECONDS);
+            } catch (InterruptedException e) {
+                return false;
+            }
+        }
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+
+        if (!TestUtils.shouldTestWifiAware(getContext())) {
+            return;
+        }
+
+        mWifiAwareManager = (WifiAwareManager) getContext().getSystemService(
+                Context.WIFI_AWARE_SERVICE);
+        assertNotNull("Wi-Fi Aware Manager", mWifiAwareManager);
+
+        mWifiManager = (WifiManager) getContext().getSystemService(Context.WIFI_SERVICE);
+        assertNotNull("Wi-Fi Manager", mWifiManager);
+        mWifiLock = mWifiManager.createWifiLock(TAG);
+        mWifiLock.acquire();
+        if (!mWifiManager.isWifiEnabled()) {
+            mWifiManager.setWifiEnabled(true);
+        }
+
+        mConnectivityManager = (ConnectivityManager) getContext().getSystemService(
+                Context.CONNECTIVITY_SERVICE);
+        assertNotNull("Connectivity Manager", mConnectivityManager);
+
+        IntentFilter intentFilter = new IntentFilter();
+        intentFilter.addAction(WifiAwareManager.ACTION_WIFI_AWARE_STATE_CHANGED);
+        WifiAwareBroadcastReceiver receiver = new WifiAwareBroadcastReceiver();
+        mContext.registerReceiver(receiver, intentFilter);
+        if (!mWifiAwareManager.isAvailable()) {
+            assertTrue("Timeout waiting for Wi-Fi Aware to change status",
+                    receiver.waitForStateChange());
+            assertTrue("Wi-Fi Aware is not available (should be)", mWifiAwareManager.isAvailable());
+        }
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        if (!TestUtils.shouldTestWifiAware(getContext())) {
+            super.tearDown();
+            return;
+        }
+
+        synchronized (mLock) {
+            for (WifiAwareSession session : mSessions) {
+                // no damage from destroying twice (i.e. ok if test cleaned up after itself already)
+                session.destroy();
+            }
+            mSessions.clear();
+        }
+
+        super.tearDown();
+    }
+
+    /**
+     * Validate:
+     * - Characteristics are available
+     * - Characteristics values are legitimate. Not in the CDD. However, the tested values are
+     *   based on the Wi-Fi Aware protocol.
+     */
+    public void testCharacteristics() {
+        if (!TestUtils.shouldTestWifiAware(getContext())) {
+            return;
+        }
+
+        Characteristics characteristics = mWifiAwareManager.getCharacteristics();
+        assertNotNull("Wi-Fi Aware characteristics are null", characteristics);
+        assertEquals("Service Name Length", characteristics.getMaxServiceNameLength(), 255);
+        assertEquals("Service Specific Information Length",
+                characteristics.getMaxServiceSpecificInfoLength(), 255);
+        assertEquals("Match Filter Length", characteristics.getMaxMatchFilterLength(), 255);
+    }
+
+    /**
+     * Validate that on Wi-Fi Aware availability change we get a broadcast + the API returns
+     * correct status.
+     */
+    public void testAvailabilityStatusChange() throws Exception {
+        if (!TestUtils.shouldTestWifiAware(getContext())) {
+            return;
+        }
+
+        IntentFilter intentFilter = new IntentFilter();
+        intentFilter.addAction(WifiAwareManager.ACTION_WIFI_AWARE_STATE_CHANGED);
+
+        // 1. Disable Wi-Fi
+        WifiAwareBroadcastReceiver receiver1 = new WifiAwareBroadcastReceiver();
+        mContext.registerReceiver(receiver1, intentFilter);
+        mWifiManager.setWifiEnabled(false);
+
+        assertTrue("Timeout waiting for Wi-Fi Aware to change status",
+                receiver1.waitForStateChange());
+        assertFalse("Wi-Fi Aware is available (should not be)", mWifiAwareManager.isAvailable());
+
+        // 2. Enable Wi-Fi
+        WifiAwareBroadcastReceiver receiver2 = new WifiAwareBroadcastReceiver();
+        mContext.registerReceiver(receiver2, intentFilter);
+        mWifiManager.setWifiEnabled(true);
+
+        assertTrue("Timeout waiting for Wi-Fi Aware to change status",
+                receiver2.waitForStateChange());
+        assertTrue("Wi-Fi Aware is not available (should be)", mWifiAwareManager.isAvailable());
+    }
+
+    /**
+     * Validate that can attach to Wi-Fi Aware.
+     */
+    public void testAttachNoIdentity() {
+        if (!TestUtils.shouldTestWifiAware(getContext())) {
+            return;
+        }
+
+        WifiAwareSession session = attachAndGetSession();
+        session.destroy();
+    }
+
+    /**
+     * Validate that can attach to Wi-Fi Aware and get identity information. Use the identity
+     * information to validate that MAC address changes on every attach.
+     *
+     * Note: relies on no other entity using Wi-Fi Aware during the CTS test. Since if it is used
+     * then the attach/destroy will not correspond to enable/disable and will not result in a new
+     * MAC address being generated.
+     */
+    public void testAttachDiscoveryAddressChanges() {
+        if (!TestUtils.shouldTestWifiAware(getContext())) {
+            return;
+        }
+
+        final int numIterations = 10;
+        Set<TestUtils.MacWrapper> macs = new HashSet<>();
+
+        for (int i = 0; i < numIterations; ++i) {
+            AttachCallbackTest attachCb = new AttachCallbackTest();
+            IdentityChangedListenerTest identityL = new IdentityChangedListenerTest();
+            mWifiAwareManager.attach(attachCb, identityL, mHandler);
+            assertEquals("Wi-Fi Aware attach: iteration " + i, AttachCallbackTest.ATTACHED,
+                    attachCb.waitForAnyCallback());
+            assertTrue("Wi-Fi Aware attach: iteration " + i, identityL.waitForListener());
+
+            WifiAwareSession session = attachCb.getSession();
+            assertNotNull("Wi-Fi Aware session: iteration " + i, session);
+
+            byte[] mac = identityL.getMac();
+            assertNotNull("Wi-Fi Aware discovery MAC: iteration " + i, mac);
+
+            session.destroy();
+
+            macs.add(new TestUtils.MacWrapper(mac));
+        }
+
+        assertEquals("", numIterations, macs.size());
+    }
+
+    /**
+     * Validate a successful publish discovery session lifetime: publish, update publish, destroy.
+     */
+    public void testPublishDiscoverySuccess() {
+        if (!TestUtils.shouldTestWifiAware(getContext())) {
+            return;
+        }
+
+        final String serviceName = "ValidName";
+
+        WifiAwareSession session = attachAndGetSession();
+
+        PublishConfig publishConfig = new PublishConfig.Builder().setServiceName(
+                serviceName).build();
+        DiscoverySessionCallbackTest discoveryCb = new DiscoverySessionCallbackTest();
+
+        // 1. publish
+        session.publish(publishConfig, discoveryCb, mHandler);
+        assertTrue("Publish started",
+                discoveryCb.waitForCallback(DiscoverySessionCallbackTest.ON_PUBLISH_STARTED));
+        PublishDiscoverySession discoverySession = discoveryCb.getPublishDiscoverySession();
+        assertNotNull("Publish session", discoverySession);
+
+        // 2. update-publish
+        publishConfig = new PublishConfig.Builder().setServiceName(
+                serviceName).setServiceSpecificInfo("extras".getBytes()).build();
+        discoverySession.updatePublish(publishConfig);
+        assertTrue("Publish update", discoveryCb.waitForCallback(
+                DiscoverySessionCallbackTest.ON_SESSION_CONFIG_UPDATED));
+
+        // 3. destroy
+        assertFalse("Publish not terminated", discoveryCb.hasCallbackAlreadyHappened(
+                DiscoverySessionCallbackTest.ON_SESSION_TERMINATED));
+        discoverySession.destroy();
+
+        // 4. try update post-destroy: should time-out waiting for cb
+        discoverySession.updatePublish(publishConfig);
+        assertFalse("Publish update post destroy", discoveryCb.waitForCallback(
+                DiscoverySessionCallbackTest.ON_SESSION_CONFIG_UPDATED));
+
+        session.destroy();
+    }
+
+    /**
+     * Validate a successful subscribe discovery session lifetime: subscribe, update subscribe,
+     * destroy.
+     */
+    public void testSubscribeDiscoverySuccess() {
+        if (!TestUtils.shouldTestWifiAware(getContext())) {
+            return;
+        }
+
+        final String serviceName = "ValidName";
+
+        WifiAwareSession session = attachAndGetSession();
+
+        SubscribeConfig subscribeConfig = new SubscribeConfig.Builder().setServiceName(
+                serviceName).build();
+        DiscoverySessionCallbackTest discoveryCb = new DiscoverySessionCallbackTest();
+
+        // 1. subscribe
+        session.subscribe(subscribeConfig, discoveryCb, mHandler);
+        assertTrue("Subscribe started",
+                discoveryCb.waitForCallback(DiscoverySessionCallbackTest.ON_SUBSCRIBE_STARTED));
+        SubscribeDiscoverySession discoverySession = discoveryCb.getSubscribeDiscoverySession();
+        assertNotNull("Subscribe session", discoverySession);
+
+        // 2. update-subscribe
+        subscribeConfig = new SubscribeConfig.Builder().setServiceName(
+                serviceName).setServiceSpecificInfo("extras".getBytes()).build();
+        discoverySession.updateSubscribe(subscribeConfig);
+        assertTrue("Subscribe update", discoveryCb.waitForCallback(
+                DiscoverySessionCallbackTest.ON_SESSION_CONFIG_UPDATED));
+
+        // 3. destroy
+        assertFalse("Subscribe not terminated", discoveryCb.hasCallbackAlreadyHappened(
+                DiscoverySessionCallbackTest.ON_SESSION_TERMINATED));
+        discoverySession.destroy();
+
+        // 4. try update post-destroy: should time-out waiting for cb
+        discoverySession.updateSubscribe(subscribeConfig);
+        assertFalse("Subscribe update post destroy", discoveryCb.waitForCallback(
+                DiscoverySessionCallbackTest.ON_SESSION_CONFIG_UPDATED));
+
+        session.destroy();
+    }
+
+    /**
+     * Test the send message flow. Since testing single device cannot send to a real peer -
+     * validate that sending to a bogus peer fails.
+     */
+    public void testSendMessageFail() {
+        if (!TestUtils.shouldTestWifiAware(getContext())) {
+            return;
+        }
+
+        WifiAwareSession session = attachAndGetSession();
+
+        PublishConfig publishConfig = new PublishConfig.Builder().setServiceName(
+                "ValidName").build();
+        DiscoverySessionCallbackTest discoveryCb = new DiscoverySessionCallbackTest();
+
+        // 1. publish
+        session.publish(publishConfig, discoveryCb, mHandler);
+        assertTrue("Publish started",
+                discoveryCb.waitForCallback(DiscoverySessionCallbackTest.ON_PUBLISH_STARTED));
+        PublishDiscoverySession discoverySession = discoveryCb.getPublishDiscoverySession();
+        assertNotNull("Publish session", discoverySession);
+
+        // 2. send a message with a null peer-handle - expect exception
+        try {
+            discoverySession.sendMessage(null, -1290, "some message".getBytes());
+            fail("Expected IllegalArgumentException");
+        } catch (IllegalArgumentException e) {
+            // empty
+        }
+
+        discoverySession.destroy();
+        session.destroy();
+    }
+
+    /**
+     * Request an Aware data-path on a Publish discovery session (which can be done with a null
+     * peer - to accept all requests). Validate that times-out.
+     */
+    public void testDataPathInContextOfDiscoveryFail() {
+        if (!TestUtils.shouldTestWifiAware(getContext())) {
+            return;
+        }
+
+        WifiAwareSession session = attachAndGetSession();
+
+        PublishConfig publishConfig = new PublishConfig.Builder().setServiceName(
+                "ValidName").build();
+        DiscoverySessionCallbackTest discoveryCb = new DiscoverySessionCallbackTest();
+        NetworkCallbackTest networkCb = new NetworkCallbackTest();
+
+        // 1. publish
+        session.publish(publishConfig, discoveryCb, mHandler);
+        assertTrue("Publish started",
+                discoveryCb.waitForCallback(DiscoverySessionCallbackTest.ON_PUBLISH_STARTED));
+        PublishDiscoverySession discoverySession = discoveryCb.getPublishDiscoverySession();
+        assertNotNull("Publish session", discoverySession);
+
+        // 2. request an AWARE network
+        NetworkRequest nr = new NetworkRequest.Builder().addTransportType(
+                NetworkCapabilities.TRANSPORT_WIFI_AWARE).setNetworkSpecifier(
+                discoverySession.createNetworkSpecifier(null, null)).build();
+        mConnectivityManager.requestNetwork(nr, networkCb, 2000);
+        assertTrue("OnUnavailable received", networkCb.waitForOnUnavailable());
+
+        discoverySession.destroy();
+        session.destroy();
+    }
+
+    /**
+     * Request an Aware data-path as a Responder with no peer MAC address (i.e. accept any peer
+     * request). Validate that times-out.
+     */
+    public void testDataPathOutOfBandFail() {
+        if (!TestUtils.shouldTestWifiAware(getContext())) {
+            return;
+        }
+
+        WifiAwareSession session = attachAndGetSession();
+
+        PublishConfig publishConfig = new PublishConfig.Builder().setServiceName(
+                "ValidName").build();
+        DiscoverySessionCallbackTest discoveryCb = new DiscoverySessionCallbackTest();
+        NetworkCallbackTest networkCb = new NetworkCallbackTest();
+
+        // 1. request an AWARE network
+        NetworkRequest nr = new NetworkRequest.Builder().addTransportType(
+                NetworkCapabilities.TRANSPORT_WIFI_AWARE).setNetworkSpecifier(
+                session.createNetworkSpecifier(
+                        WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_RESPONDER, null, null)).build();
+        mConnectivityManager.requestNetwork(nr, networkCb, 2000);
+        assertTrue("OnUnavailable received", networkCb.waitForOnUnavailable());
+
+        session.destroy();
+    }
+
+    // local utilities
+
+    private WifiAwareSession attachAndGetSession() {
+        AttachCallbackTest attachCb = new AttachCallbackTest();
+        mWifiAwareManager.attach(attachCb, mHandler);
+        int cbCalled = attachCb.waitForAnyCallback();
+        assertEquals("Wi-Fi Aware attach", AttachCallbackTest.ATTACHED, cbCalled);
+
+        WifiAwareSession session = attachCb.getSession();
+        assertNotNull("Wi-Fi Aware session", session);
+
+        return session;
+    }
+}
diff --git a/tests/tests/net/src/android/net/wifi/aware/cts/TestUtils.java b/tests/tests/net/src/android/net/wifi/aware/cts/TestUtils.java
new file mode 100644
index 0000000..a12c8bb
--- /dev/null
+++ b/tests/tests/net/src/android/net/wifi/aware/cts/TestUtils.java
@@ -0,0 +1,69 @@
+/*
+ * 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.net.wifi.aware.cts;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+
+import java.util.Arrays;
+
+/**
+ * Test utilities for Wi-Fi Aware CTS test suite.
+ */
+class TestUtils {
+    static final String TAG = "WifiAwareCtsTests";
+
+    /**
+     * Returns a flag indicating whether or not Wi-Fi Aware should be tested. Wi-Fi Aware
+     * should be tested if the feature is supported on the current device.
+     */
+    static boolean shouldTestWifiAware(Context context) {
+        final PackageManager pm = context.getPackageManager();
+        return pm.hasSystemFeature(PackageManager.FEATURE_WIFI_AWARE);
+    }
+
+    /**
+     * Wraps a byte[] (MAC address representation). Intended to provide hash and equality operators
+     * so that the MAC address can be used in containers.
+     */
+    static class MacWrapper {
+        private byte[] mMac;
+
+        MacWrapper(byte[] mac) {
+            mMac = mac;
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) {
+                return true;
+            }
+
+            if (!(o instanceof MacWrapper)) {
+                return false;
+            }
+
+            MacWrapper lhs = (MacWrapper) o;
+            return Arrays.equals(mMac, lhs.mMac);
+        }
+
+        @Override
+        public int hashCode() {
+            return Arrays.hashCode(mMac);
+        }
+    }
+}
diff --git a/tests/tests/net/src/android/net/wifi/cts/ConfigParserTest.java b/tests/tests/net/src/android/net/wifi/cts/ConfigParserTest.java
new file mode 100644
index 0000000..52ed2a6
--- /dev/null
+++ b/tests/tests/net/src/android/net/wifi/cts/ConfigParserTest.java
@@ -0,0 +1,114 @@
+/*
+ * 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.net.wifi.cts;
+
+import android.net.wifi.hotspot2.ConfigParser;
+import android.net.wifi.hotspot2.PasspointConfiguration;
+import android.net.wifi.hotspot2.pps.Credential;
+import android.net.wifi.hotspot2.pps.HomeSp;
+import android.test.AndroidTestCase;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.Arrays;
+
+/**
+ * CTS tests for Hotspot 2.0 Release 1 installation file parsing API.
+ */
+public class ConfigParserTest extends AndroidTestCase {
+    /**
+     * Hotspot 2.0 Release 1 installation file that contains a Passpoint profile and a
+     * CA (Certificate Authority) X.509 certificate {@link FakeKeys#CA_CERT0}.
+     */
+    private static final String PASSPOINT_INSTALLATION_FILE_WITH_CA_CERT =
+            "assets/HSR1ProfileWithCACert.base64";
+
+    /**
+     * Read the content of the given resource file into a String.
+     *
+     * @param filename String name of the file
+     * @return String
+     * @throws IOException
+     */
+    private String loadResourceFile(String filename) throws IOException {
+        InputStream in = getClass().getClassLoader().getResourceAsStream(filename);
+        BufferedReader reader = new BufferedReader(new InputStreamReader(in));
+        StringBuilder builder = new StringBuilder();
+        String line;
+        while ((line = reader.readLine()) != null) {
+            builder.append(line).append("\n");
+        }
+
+        return builder.toString();
+    }
+
+    /**
+     * Generate a {@link PasspointConfiguration} that matches the configuration specified in the
+     * XML file {@link #PASSPOINT_INSTALLATION_FILE_WITH_CA_CERT}.
+     *
+     * @return {@link PasspointConfiguration}
+     */
+    private PasspointConfiguration generateConfigurationFromProfile() {
+        PasspointConfiguration config = new PasspointConfiguration();
+
+        // HomeSP configuration.
+        HomeSp homeSp = new HomeSp();
+        homeSp.setFriendlyName("Century House");
+        homeSp.setFqdn("mi6.co.uk");
+        homeSp.setRoamingConsortiumOis(new long[] {0x112233L, 0x445566L});
+        config.setHomeSp(homeSp);
+
+        // Credential configuration.
+        Credential credential = new Credential();
+        credential.setRealm("shaken.stirred.com");
+        Credential.UserCredential userCredential = new Credential.UserCredential();
+        userCredential.setUsername("james");
+        userCredential.setPassword("Ym9uZDAwNw==");
+        userCredential.setEapType(21);
+        userCredential.setNonEapInnerMethod("MS-CHAP-V2");
+        credential.setUserCredential(userCredential);
+        Credential.CertificateCredential certCredential = new Credential.CertificateCredential();
+        certCredential.setCertType("x509v3");
+        byte[] certSha256Fingerprint = new byte[32];
+        Arrays.fill(certSha256Fingerprint, (byte)0x1f);
+        certCredential.setCertSha256Fingerprint(certSha256Fingerprint);
+        credential.setCertCredential(certCredential);
+        Credential.SimCredential simCredential = new Credential.SimCredential();
+        simCredential.setImsi("imsi");
+        simCredential.setEapType(24);
+        credential.setSimCredential(simCredential);
+        credential.setCaCertificate(FakeKeys.CA_CERT0);
+        config.setCredential(credential);
+        return config;
+    }
+
+    /**
+     * Verify a valid installation file is parsed successfully with the matching contents.
+     *
+     * @throws Exception
+     */
+    public void testParseConfigFile() throws Exception {
+        String configStr = loadResourceFile(PASSPOINT_INSTALLATION_FILE_WITH_CA_CERT);
+        PasspointConfiguration expectedConfig = generateConfigurationFromProfile();
+        PasspointConfiguration actualConfig =
+                ConfigParser.parsePasspointConfig(
+                        "application/x-wifi-config", configStr.getBytes());
+        assertTrue(actualConfig.equals(expectedConfig));
+    }
+}
\ No newline at end of file
diff --git a/tests/tests/net/src/android/net/wifi/cts/FakeKeys.java b/tests/tests/net/src/android/net/wifi/cts/FakeKeys.java
new file mode 100644
index 0000000..f422c2f
--- /dev/null
+++ b/tests/tests/net/src/android/net/wifi/cts/FakeKeys.java
@@ -0,0 +1,237 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.net.wifi.cts;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+import java.security.KeyFactory;
+import java.security.NoSuchAlgorithmException;
+import java.security.PrivateKey;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.PKCS8EncodedKeySpec;
+
+/**
+ * A class containing test certificates and private keys.
+ */
+public class FakeKeys {
+    private static final String CA_CERT0_STRING = "-----BEGIN CERTIFICATE-----\n" +
+            "MIIDKDCCAhCgAwIBAgIJAILlFdwzLVurMA0GCSqGSIb3DQEBCwUAMBIxEDAOBgNV\n" +
+            "BAMTB0VBUCBDQTEwHhcNMTYwMTEyMTE1MDE1WhcNMjYwMTA5MTE1MDE1WjASMRAw\n" +
+            "DgYDVQQDEwdFQVAgQ0ExMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA\n" +
+            "znAPUz26Msae4ws43czR41/J2QtrSIZUKmVUsVumDbYHrPNvTXKSMXAcewORDQYX\n" +
+            "RqvHvpn8CscB1+oGXZvHwxj4zV0WKoK2zeXkau3vcyl3HIKupJfq2TEACefVjj0t\n" +
+            "JW+X35PGWp9/H5zIUNVNVjS7Ums84IvKhRB8512PB9UyHagXYVX5GWpAcVpyfrlR\n" +
+            "FI9Qdhh+Pbk0uyktdbf/CdfgHOoebrTtwRljM0oDtX+2Cv6j0wBK7hD8pPvf1+uy\n" +
+            "GzczigAU/4Kw7eZqydf9B+5RupR+IZipX41xEiIrKRwqi517WWzXcjaG2cNbf451\n" +
+            "xpH5PnV3i1tq04jMGQUzFwIDAQABo4GAMH4wHQYDVR0OBBYEFIwX4vs8BiBcScod\n" +
+            "5noZHRM8E4+iMEIGA1UdIwQ7MDmAFIwX4vs8BiBcScod5noZHRM8E4+ioRakFDAS\n" +
+            "MRAwDgYDVQQDEwdFQVAgQ0ExggkAguUV3DMtW6swDAYDVR0TBAUwAwEB/zALBgNV\n" +
+            "HQ8EBAMCAQYwDQYJKoZIhvcNAQELBQADggEBAFfQqOTA7Rv7K+luQ7pnas4BYwHE\n" +
+            "9GEP/uohv6KOy0TGQFbrRTjFoLVNB9BZ1ymMDZ0/TIwIUc7wi7a8t5mEqYH153wW\n" +
+            "aWooiSjyLLhuI4sNrNCOtisdBq2r2MFXt6h0mAQYOPv8R8K7/fgSxGFqzhyNmmVL\n" +
+            "1qBJldx34SpwsTALQVPb4hGwJzZfr1PcpEQx6xMnTl8xEWZE3Ms99uaUxbQqIwRu\n" +
+            "LgAOkNCmY2m89VhzaHJ1uV85AdM/tD+Ysmlnnjt9LRCejbBipjIGjOXrg1JP+lxV\n" +
+            "muM4vH+P/mlmxsPPz0d65b+EGmJZpoLkO/tdNNvCYzjJpTEWpEsO6NMhKYo=\n" +
+            "-----END CERTIFICATE-----\n";
+    public static final X509Certificate CA_CERT0 = loadCertificate(CA_CERT0_STRING);
+
+    private static final String CA_CERT1_STRING = "-----BEGIN CERTIFICATE-----\n" +
+            "MIIDKDCCAhCgAwIBAgIJAOM5SzKO2pzCMA0GCSqGSIb3DQEBCwUAMBIxEDAOBgNV\n" +
+            "BAMTB0VBUCBDQTAwHhcNMTYwMTEyMDAxMDQ3WhcNMjYwMTA5MDAxMDQ3WjASMRAw\n" +
+            "DgYDVQQDEwdFQVAgQ0EwMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA\n" +
+            "89ug+IEKVQXnJGKg5g4uVHg6J/8iRUxR5k2eH5o03hrJNMfN2D+cBe/wCiZcnWbI\n" +
+            "GbGZACWm2nQth2wy9Zgm2LOd3b4ocrHYls3XLq6Qb5Dd7a0JKU7pdGufiNVEkrmF\n" +
+            "EB+N64wgwH4COTvCiN4erp5kyJwkfqAl2xLkZo0C464c9XoyQOXbmYD9A8v10wZu\n" +
+            "jyNsEo7Nr2USyw+qhjWSbFbEirP77Tvx+7pJQJwdtk1V9Tn73T2dGF2WHYejei9S\n" +
+            "mcWpdIUqsu9etYH+zDmtu7I1xlkwiaVsNr2+D+qaCJyOYqrDTKVNK5nmbBPXDWZc\n" +
+            "NoDbTOoqquX7xONpq9M6jQIDAQABo4GAMH4wHQYDVR0OBBYEFAZ3A2S4qJZZwuNY\n" +
+            "wkJ6mAdc0gVdMEIGA1UdIwQ7MDmAFAZ3A2S4qJZZwuNYwkJ6mAdc0gVdoRakFDAS\n" +
+            "MRAwDgYDVQQDEwdFQVAgQ0EwggkA4zlLMo7anMIwDAYDVR0TBAUwAwEB/zALBgNV\n" +
+            "HQ8EBAMCAQYwDQYJKoZIhvcNAQELBQADggEBAHmdMwEhtys4d0E+t7owBmoVR+lU\n" +
+            "hMCcRtWs8YKX5WIM2kTweT0h/O1xwE1mWmRv/IbDAEb8od4BjAQLhIcolStr2JaO\n" +
+            "9ZzyxjOnNzqeErh/1DHDbb/moPpqfeJ8YiEz7nH/YU56Q8iCPO7TsgS0sNNE7PfN\n" +
+            "IUsBW0yHRgpQ4OxWmiZG2YZWiECRzAC0ecPzo59N5iH4vLQIMTMYquiDeMPQnn1e\n" +
+            "NDGxG8gCtDKIaS6tMg3a28MvWB094pr2ETou8O1C8Ji0Y4hE8QJmSdT7I4+GZjgW\n" +
+            "g94DZ5RiL7sdp3vC48CXOmeT61YBIvhGUsE1rPhXqkpqQ3Z3C4TFF0jXZZc=\n" +
+            "-----END CERTIFICATE-----\n";
+    public static final X509Certificate CA_CERT1 = loadCertificate(CA_CERT1_STRING);
+
+    private static final String CLIENT_CERT_STR = "-----BEGIN CERTIFICATE-----\n" +
+            "MIIE/DCCAuQCAQEwDQYJKoZIhvcNAQELBQAwRDELMAkGA1UEBhMCVVMxCzAJBgNV\n" +
+            "BAgMAkNBMRYwFAYDVQQHDA1Nb3VudGFpbiBWaWV3MRAwDgYDVQQKDAdUZXN0aW5n\n" +
+            "MB4XDTE2MDkzMDIwNTQyOFoXDTE3MDkzMDIwNTQyOFowRDELMAkGA1UEBhMCVVMx\n" +
+            "CzAJBgNVBAgMAkNBMRYwFAYDVQQHDA1Nb3VudGFpbiBWaWV3MRAwDgYDVQQKDAdU\n" +
+            "ZXN0aW5nMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAnpmcbuaeHfnJ\n" +
+            "k+2QNvxmdVFTawyFMNk0USCq5sexscwmxbewG/Rb8YnixwJWS44v2XkSujB67z5C\n" +
+            "s2qudFEhRXKdEuC6idbAuA97KjipHh0AAniWMsyv61fvbgsUC0b0canx3LiDq81p\n" +
+            "y28NNGmAvoazLZUZ4AhBRiwYZY6FKk723gmZoGbEIeG7J1dlXPusc1662rIjz4eU\n" +
+            "zlmmlvqyHfNqnNk8L14Vug6Xh+lOEGN85xhu1YHAEKGrS89kZxs5rum/cZU8KH2V\n" +
+            "v6eKnY03kxjiVLQtnLpm/7VUEoCMGHyruRj+p3my4+DgqMsmsH52RZCBsjyGlpbU\n" +
+            "NOwOTIX6xh+Rqloduz4AnrMYYIiIw2s8g+2zJM7VbcVKx0fGS26BKdrxgrXWfmNE\n" +
+            "nR0/REQ5AxDGw0jfTUvtdTkXAf+K4MDjcNLEZ+MA4rHfAfQWZtUR5BkHCQYxNpJk\n" +
+            "pA0gyk+BpKdC4WdzI14NSWsu5sRCmBCFqH6BTOSEq/V1cNorBxNwLSSTwFFqUDqx\n" +
+            "Y5nQLXygkJf9WHZWtSKeSjtOYgilz7UKzC2s3CsjmIyGFe+SwpuHJnuE4Uc8Z5Cb\n" +
+            "bjNGHPzqL6XnmzZHJp7RF8kBdKdjGC7dCUltzOfICZeKlzOOq+Kw42T/nXjuXvpb\n" +
+            "nkXNxg741Nwd6RecykXJbseFwm3EYxkCAwEAATANBgkqhkiG9w0BAQsFAAOCAgEA\n" +
+            "Ga1mGwI9aXkL2fTPXO9YkAPzoGeX8aeuVYSQaSkNq+5vnogYCyAt3YDHjRG+ewTT\n" +
+            "WbnPA991xRAPac+biJeXWmwvgGj0YuT7e79phAiGkTTnbAjFHGfYnBy/tI/v7btO\n" +
+            "hRNElA5yTJ1m2fVbBEKXzMR83jrT9iyI+YLRN86zUZIaC86xxSbqnrdWN2jOK6MX\n" +
+            "dS8Arp9tPQjC/4gW+2Ilxv68jiYh+5auWHQZVjppWVY//iu4mAbkq1pTwQEhZ8F8\n" +
+            "Zrmh9DHh60hLFcfSuhIAwf/NMzppwdkjy1ruKVrpijhGKGp4OWu8nvOUgHSzxc7F\n" +
+            "PwpVZ5N2Ku4L8MLO6BG2VasRJK7l17TzDXlfLZHJjkuryOFxVaQKt8ZNFgTOaCXS\n" +
+            "E+gpTLksKU7riYckoiP4+H1sn9qcis0e8s4o/uf1UVc8GSdDw61ReGM5oZEDm1u8\n" +
+            "H9x20QU6igLqzyBpqvCKv7JNgU1uB2PAODHH78zJiUfnKd1y+o+J1iWzaGj3EFji\n" +
+            "T8AXksbTP733FeFXfggXju2dyBH+Z1S5BBTEOd1brWgXlHSAZGm97MKZ94r6/tkX\n" +
+            "qfv3fCos0DKz0oV7qBxYS8wiYhzrRVxG6ITAoH8uuUVVQaZF+G4nJ2jEqNbfuKyX\n" +
+            "ATQsVNjNNlDA0J33GobPMjT326wa4YAWMx8PI5PJZ3g=\n" +
+            "-----END CERTIFICATE-----\n";
+    public static final X509Certificate CLIENT_CERT = loadCertificate(CLIENT_CERT_STR);
+
+    private static final byte[] FAKE_RSA_KEY_1 = new byte[] {
+            (byte) 0x30, (byte) 0x82, (byte) 0x02, (byte) 0x78, (byte) 0x02, (byte) 0x01,
+            (byte) 0x00, (byte) 0x30, (byte) 0x0d, (byte) 0x06, (byte) 0x09, (byte) 0x2a,
+            (byte) 0x86, (byte) 0x48, (byte) 0x86, (byte) 0xf7, (byte) 0x0d, (byte) 0x01,
+            (byte) 0x01, (byte) 0x01, (byte) 0x05, (byte) 0x00, (byte) 0x04, (byte) 0x82,
+            (byte) 0x02, (byte) 0x62, (byte) 0x30, (byte) 0x82, (byte) 0x02, (byte) 0x5e,
+            (byte) 0x02, (byte) 0x01, (byte) 0x00, (byte) 0x02, (byte) 0x81, (byte) 0x81,
+            (byte) 0x00, (byte) 0xce, (byte) 0x29, (byte) 0xeb, (byte) 0xf6, (byte) 0x5b,
+            (byte) 0x25, (byte) 0xdc, (byte) 0xa1, (byte) 0xa6, (byte) 0x2c, (byte) 0x66,
+            (byte) 0xcb, (byte) 0x20, (byte) 0x90, (byte) 0x27, (byte) 0x86, (byte) 0x8a,
+            (byte) 0x44, (byte) 0x71, (byte) 0x50, (byte) 0xda, (byte) 0xd3, (byte) 0x02,
+            (byte) 0x77, (byte) 0x55, (byte) 0xe9, (byte) 0xe8, (byte) 0x08, (byte) 0xf3,
+            (byte) 0x36, (byte) 0x9a, (byte) 0xae, (byte) 0xab, (byte) 0x04, (byte) 0x6d,
+            (byte) 0x00, (byte) 0x99, (byte) 0xbf, (byte) 0x7d, (byte) 0x0f, (byte) 0x67,
+            (byte) 0x8b, (byte) 0x1d, (byte) 0xd4, (byte) 0x2b, (byte) 0x7c, (byte) 0xcb,
+            (byte) 0xcd, (byte) 0x33, (byte) 0xc7, (byte) 0x84, (byte) 0x30, (byte) 0xe2,
+            (byte) 0x45, (byte) 0x21, (byte) 0xb3, (byte) 0x75, (byte) 0xf5, (byte) 0x79,
+            (byte) 0x02, (byte) 0xda, (byte) 0x50, (byte) 0xa3, (byte) 0x8b, (byte) 0xce,
+            (byte) 0xc3, (byte) 0x8e, (byte) 0x0f, (byte) 0x25, (byte) 0xeb, (byte) 0x08,
+            (byte) 0x2c, (byte) 0xdd, (byte) 0x1c, (byte) 0xcf, (byte) 0xff, (byte) 0x3b,
+            (byte) 0xde, (byte) 0xb6, (byte) 0xaa, (byte) 0x2a, (byte) 0xa9, (byte) 0xc4,
+            (byte) 0x8a, (byte) 0x24, (byte) 0x24, (byte) 0xe6, (byte) 0x29, (byte) 0x0d,
+            (byte) 0x98, (byte) 0x4c, (byte) 0x32, (byte) 0xa1, (byte) 0x7b, (byte) 0x23,
+            (byte) 0x2b, (byte) 0x42, (byte) 0x30, (byte) 0xee, (byte) 0x78, (byte) 0x08,
+            (byte) 0x47, (byte) 0xad, (byte) 0xf2, (byte) 0x96, (byte) 0xd5, (byte) 0xf1,
+            (byte) 0x62, (byte) 0x42, (byte) 0x2d, (byte) 0x35, (byte) 0x19, (byte) 0xb4,
+            (byte) 0x3c, (byte) 0xc9, (byte) 0xc3, (byte) 0x5f, (byte) 0x03, (byte) 0x16,
+            (byte) 0x3a, (byte) 0x23, (byte) 0xac, (byte) 0xcb, (byte) 0xce, (byte) 0x9e,
+            (byte) 0x51, (byte) 0x2e, (byte) 0x6d, (byte) 0x02, (byte) 0x03, (byte) 0x01,
+            (byte) 0x00, (byte) 0x01, (byte) 0x02, (byte) 0x81, (byte) 0x80, (byte) 0x16,
+            (byte) 0x59, (byte) 0xc3, (byte) 0x24, (byte) 0x1d, (byte) 0x33, (byte) 0x98,
+            (byte) 0x9c, (byte) 0xc9, (byte) 0xc8, (byte) 0x2c, (byte) 0x88, (byte) 0xbf,
+            (byte) 0x0a, (byte) 0x01, (byte) 0xce, (byte) 0xfb, (byte) 0x34, (byte) 0x7a,
+            (byte) 0x58, (byte) 0x7a, (byte) 0xb0, (byte) 0xbf, (byte) 0xa6, (byte) 0xb2,
+            (byte) 0x60, (byte) 0xbe, (byte) 0x70, (byte) 0x21, (byte) 0xf5, (byte) 0xfc,
+            (byte) 0x85, (byte) 0x0d, (byte) 0x33, (byte) 0x58, (byte) 0xa1, (byte) 0xe5,
+            (byte) 0x09, (byte) 0x36, (byte) 0x84, (byte) 0xb2, (byte) 0x04, (byte) 0x0a,
+            (byte) 0x02, (byte) 0xd3, (byte) 0x88, (byte) 0x1f, (byte) 0x0c, (byte) 0x2b,
+            (byte) 0x1d, (byte) 0xe9, (byte) 0x3d, (byte) 0xe7, (byte) 0x79, (byte) 0xf9,
+            (byte) 0x32, (byte) 0x5c, (byte) 0x8a, (byte) 0x75, (byte) 0x49, (byte) 0x12,
+            (byte) 0xe4, (byte) 0x05, (byte) 0x26, (byte) 0xd4, (byte) 0x2e, (byte) 0x9e,
+            (byte) 0x1f, (byte) 0xcc, (byte) 0x54, (byte) 0xad, (byte) 0x33, (byte) 0x8d,
+            (byte) 0x99, (byte) 0x00, (byte) 0xdc, (byte) 0xf5, (byte) 0xb4, (byte) 0xa2,
+            (byte) 0x2f, (byte) 0xba, (byte) 0xe5, (byte) 0x62, (byte) 0x30, (byte) 0x6d,
+            (byte) 0xe6, (byte) 0x3d, (byte) 0xeb, (byte) 0x24, (byte) 0xc2, (byte) 0xdc,
+            (byte) 0x5f, (byte) 0xb7, (byte) 0x16, (byte) 0x35, (byte) 0xa3, (byte) 0x98,
+            (byte) 0x98, (byte) 0xa8, (byte) 0xef, (byte) 0xe8, (byte) 0xc4, (byte) 0x96,
+            (byte) 0x6d, (byte) 0x38, (byte) 0xab, (byte) 0x26, (byte) 0x6d, (byte) 0x30,
+            (byte) 0xc2, (byte) 0xa0, (byte) 0x44, (byte) 0xe4, (byte) 0xff, (byte) 0x7e,
+            (byte) 0xbe, (byte) 0x7c, (byte) 0x33, (byte) 0xa5, (byte) 0x10, (byte) 0xad,
+            (byte) 0xd7, (byte) 0x1e, (byte) 0x13, (byte) 0x20, (byte) 0xb3, (byte) 0x1f,
+            (byte) 0x41, (byte) 0x02, (byte) 0x41, (byte) 0x00, (byte) 0xf1, (byte) 0x89,
+            (byte) 0x07, (byte) 0x0f, (byte) 0xe8, (byte) 0xcf, (byte) 0xab, (byte) 0x13,
+            (byte) 0x2a, (byte) 0x8f, (byte) 0x88, (byte) 0x80, (byte) 0x11, (byte) 0x9a,
+            (byte) 0x79, (byte) 0xb6, (byte) 0x59, (byte) 0x3a, (byte) 0x50, (byte) 0x6e,
+            (byte) 0x57, (byte) 0x37, (byte) 0xab, (byte) 0x2a, (byte) 0xd2, (byte) 0xaa,
+            (byte) 0xd9, (byte) 0x72, (byte) 0x73, (byte) 0xff, (byte) 0x8b, (byte) 0x47,
+            (byte) 0x76, (byte) 0xdd, (byte) 0xdc, (byte) 0xf5, (byte) 0x97, (byte) 0x44,
+            (byte) 0x3a, (byte) 0x78, (byte) 0xbe, (byte) 0x17, (byte) 0xb4, (byte) 0x22,
+            (byte) 0x6f, (byte) 0xe5, (byte) 0x23, (byte) 0x70, (byte) 0x1d, (byte) 0x10,
+            (byte) 0x5d, (byte) 0xba, (byte) 0x16, (byte) 0x81, (byte) 0xf1, (byte) 0x45,
+            (byte) 0xce, (byte) 0x30, (byte) 0xb4, (byte) 0xab, (byte) 0x80, (byte) 0xe4,
+            (byte) 0x98, (byte) 0x31, (byte) 0x02, (byte) 0x41, (byte) 0x00, (byte) 0xda,
+            (byte) 0x82, (byte) 0x9d, (byte) 0x3f, (byte) 0xca, (byte) 0x2f, (byte) 0xe1,
+            (byte) 0xd4, (byte) 0x86, (byte) 0x77, (byte) 0x48, (byte) 0xa6, (byte) 0xab,
+            (byte) 0xab, (byte) 0x1c, (byte) 0x42, (byte) 0x5c, (byte) 0xd5, (byte) 0xc7,
+            (byte) 0x46, (byte) 0x59, (byte) 0x91, (byte) 0x3f, (byte) 0xfc, (byte) 0xcc,
+            (byte) 0xec, (byte) 0xc2, (byte) 0x40, (byte) 0x12, (byte) 0x2c, (byte) 0x8d,
+            (byte) 0x1f, (byte) 0xa2, (byte) 0x18, (byte) 0x88, (byte) 0xee, (byte) 0x82,
+            (byte) 0x4a, (byte) 0x5a, (byte) 0x5e, (byte) 0x88, (byte) 0x20, (byte) 0xe3,
+            (byte) 0x7b, (byte) 0xe0, (byte) 0xd8, (byte) 0x3a, (byte) 0x52, (byte) 0x9a,
+            (byte) 0x26, (byte) 0x6a, (byte) 0x04, (byte) 0xec, (byte) 0xe8, (byte) 0xb9,
+            (byte) 0x48, (byte) 0x40, (byte) 0xe1, (byte) 0xe1, (byte) 0x83, (byte) 0xa6,
+            (byte) 0x67, (byte) 0xa6, (byte) 0xfd, (byte) 0x02, (byte) 0x41, (byte) 0x00,
+            (byte) 0x89, (byte) 0x72, (byte) 0x3e, (byte) 0xb0, (byte) 0x90, (byte) 0xfd,
+            (byte) 0x4c, (byte) 0x0e, (byte) 0xd6, (byte) 0x13, (byte) 0x63, (byte) 0xcb,
+            (byte) 0xed, (byte) 0x38, (byte) 0x88, (byte) 0xb6, (byte) 0x79, (byte) 0xc4,
+            (byte) 0x33, (byte) 0x6c, (byte) 0xf6, (byte) 0xf8, (byte) 0xd8, (byte) 0xd0,
+            (byte) 0xbf, (byte) 0x9d, (byte) 0x35, (byte) 0xac, (byte) 0x69, (byte) 0xd2,
+            (byte) 0x2b, (byte) 0xc1, (byte) 0xf9, (byte) 0x24, (byte) 0x7b, (byte) 0xce,
+            (byte) 0xcd, (byte) 0xcb, (byte) 0xa7, (byte) 0xb2, (byte) 0x7a, (byte) 0x0a,
+            (byte) 0x27, (byte) 0x19, (byte) 0xc9, (byte) 0xaf, (byte) 0x0d, (byte) 0x21,
+            (byte) 0x89, (byte) 0x88, (byte) 0x7c, (byte) 0xad, (byte) 0x9e, (byte) 0x8d,
+            (byte) 0x47, (byte) 0x6d, (byte) 0x3f, (byte) 0xce, (byte) 0x7b, (byte) 0xa1,
+            (byte) 0x74, (byte) 0xf1, (byte) 0xa0, (byte) 0xa1, (byte) 0x02, (byte) 0x41,
+            (byte) 0x00, (byte) 0xd9, (byte) 0xa8, (byte) 0xf5, (byte) 0xfe, (byte) 0xce,
+            (byte) 0xe6, (byte) 0x77, (byte) 0x6b, (byte) 0xfe, (byte) 0x2d, (byte) 0xe0,
+            (byte) 0x1e, (byte) 0xb6, (byte) 0x2e, (byte) 0x12, (byte) 0x4e, (byte) 0x40,
+            (byte) 0xaf, (byte) 0x6a, (byte) 0x7b, (byte) 0x37, (byte) 0x49, (byte) 0x2a,
+            (byte) 0x96, (byte) 0x25, (byte) 0x83, (byte) 0x49, (byte) 0xd4, (byte) 0x0c,
+            (byte) 0xc6, (byte) 0x78, (byte) 0x25, (byte) 0x24, (byte) 0x90, (byte) 0x90,
+            (byte) 0x06, (byte) 0x15, (byte) 0x9e, (byte) 0xfe, (byte) 0xf9, (byte) 0xdf,
+            (byte) 0x5b, (byte) 0xf3, (byte) 0x7e, (byte) 0x38, (byte) 0x70, (byte) 0xeb,
+            (byte) 0x57, (byte) 0xd0, (byte) 0xd9, (byte) 0xa7, (byte) 0x0e, (byte) 0x14,
+            (byte) 0xf7, (byte) 0x95, (byte) 0x68, (byte) 0xd5, (byte) 0xc8, (byte) 0xab,
+            (byte) 0x9d, (byte) 0x3a, (byte) 0x2b, (byte) 0x51, (byte) 0xf9, (byte) 0x02,
+            (byte) 0x41, (byte) 0x00, (byte) 0x96, (byte) 0xdf, (byte) 0xe9, (byte) 0x67,
+            (byte) 0x6c, (byte) 0xdc, (byte) 0x90, (byte) 0x14, (byte) 0xb4, (byte) 0x1d,
+            (byte) 0x22, (byte) 0x33, (byte) 0x4a, (byte) 0x31, (byte) 0xc1, (byte) 0x9d,
+            (byte) 0x2e, (byte) 0xff, (byte) 0x9a, (byte) 0x2a, (byte) 0x95, (byte) 0x4b,
+            (byte) 0x27, (byte) 0x74, (byte) 0xcb, (byte) 0x21, (byte) 0xc3, (byte) 0xd2,
+            (byte) 0x0b, (byte) 0xb2, (byte) 0x46, (byte) 0x87, (byte) 0xf8, (byte) 0x28,
+            (byte) 0x01, (byte) 0x8b, (byte) 0xd8, (byte) 0xb9, (byte) 0x4b, (byte) 0xcd,
+            (byte) 0x9a, (byte) 0x96, (byte) 0x41, (byte) 0x0e, (byte) 0x36, (byte) 0x6d,
+            (byte) 0x40, (byte) 0x42, (byte) 0xbc, (byte) 0xd9, (byte) 0xd3, (byte) 0x7b,
+            (byte) 0xbc, (byte) 0xa7, (byte) 0x92, (byte) 0x90, (byte) 0xdd, (byte) 0xa1,
+            (byte) 0x9c, (byte) 0xce, (byte) 0xa1, (byte) 0x87, (byte) 0x11, (byte) 0x51
+    };
+    public static final PrivateKey RSA_KEY1 = loadPrivateRSAKey(FAKE_RSA_KEY_1);
+
+    private static X509Certificate loadCertificate(String blob) {
+        try {
+            final CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
+            InputStream stream = new ByteArrayInputStream(blob.getBytes(StandardCharsets.UTF_8));
+
+            return (X509Certificate) certFactory.generateCertificate(stream);
+        } catch (Exception e) {
+            e.printStackTrace();
+            return null;
+        }
+    }
+
+    private static PrivateKey loadPrivateRSAKey(byte[] fakeKey) {
+        try {
+            KeyFactory kf = KeyFactory.getInstance("RSA");
+            return kf.generatePrivate(new PKCS8EncodedKeySpec(fakeKey));
+        } catch (InvalidKeySpecException | NoSuchAlgorithmException e) {
+            return null;
+        }
+    }
+}
diff --git a/tests/tests/net/src/android/net/wifi/cts/PpsMoParserTest.java b/tests/tests/net/src/android/net/wifi/cts/PpsMoParserTest.java
new file mode 100644
index 0000000..5eccc0d
--- /dev/null
+++ b/tests/tests/net/src/android/net/wifi/cts/PpsMoParserTest.java
@@ -0,0 +1,194 @@
+/*
+ * 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.net.wifi.cts;
+
+import android.net.wifi.hotspot2.PasspointConfiguration;
+import android.net.wifi.hotspot2.omadm.PpsMoParser;
+import android.net.wifi.hotspot2.pps.Credential;
+import android.net.wifi.hotspot2.pps.HomeSp;
+import android.net.wifi.hotspot2.pps.Policy;
+import android.net.wifi.hotspot2.pps.UpdateParameter;
+import android.test.AndroidTestCase;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * CTS tests for PPS MO (PerProviderSubscription Management Object) XML string parsing API.
+ */
+public class PpsMoParserTest extends AndroidTestCase {
+    private static final String PPS_MO_XML_FILE = "assets/PerProviderSubscription.xml";
+
+    /**
+     * Read the content of the given resource file into a String.
+     *
+     * @param filename String name of the file
+     * @return String
+     * @throws IOException
+     */
+    private String loadResourceFile(String filename) throws IOException {
+        InputStream in = getClass().getClassLoader().getResourceAsStream(filename);
+        BufferedReader reader = new BufferedReader(new InputStreamReader(in));
+        StringBuilder builder = new StringBuilder();
+        String line;
+        while ((line = reader.readLine()) != null) {
+            builder.append(line).append("\n");
+        }
+        return builder.toString();
+    }
+
+    /**
+     * Generate a {@link PasspointConfiguration} that matches the configuration specified in the
+     * XML file {@link #PPS_MO_XML_FILE}.
+     *
+     * @return {@link PasspointConfiguration}
+     */
+    private PasspointConfiguration generateConfigurationFromPPSMOTree() throws Exception {
+        DateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
+        byte[] certFingerprint = new byte[32];
+        Arrays.fill(certFingerprint, (byte) 0x1f);
+
+        PasspointConfiguration config = new PasspointConfiguration();
+        config.setUpdateIdentifier(12);
+        config.setCredentialPriority(99);
+
+        // AAA Server trust root.
+        Map<String, byte[]> trustRootCertList = new HashMap<>();
+        trustRootCertList.put("server1.trust.root.com", certFingerprint);
+        config.setTrustRootCertList(trustRootCertList);
+
+        // Subscription update.
+        UpdateParameter subscriptionUpdate = new UpdateParameter();
+        subscriptionUpdate.setUpdateIntervalInMinutes(120);
+        subscriptionUpdate.setUpdateMethod(UpdateParameter.UPDATE_METHOD_SSP);
+        subscriptionUpdate.setRestriction(UpdateParameter.UPDATE_RESTRICTION_ROAMING_PARTNER);
+        subscriptionUpdate.setServerUri("subscription.update.com");
+        subscriptionUpdate.setUsername("subscriptionUser");
+        subscriptionUpdate.setBase64EncodedPassword("subscriptionPass");
+        subscriptionUpdate.setTrustRootCertUrl("subscription.update.cert.com");
+        subscriptionUpdate.setTrustRootCertSha256Fingerprint(certFingerprint);
+        config.setSubscriptionUpdate(subscriptionUpdate);
+
+        // Subscription parameters.
+        config.setSubscriptionCreationTimeInMs(format.parse("2016-02-01T10:00:00Z").getTime());
+        config.setSubscriptionExpirationTimeInMs(format.parse("2016-03-01T10:00:00Z").getTime());
+        config.setSubscriptionType("Gold");
+        config.setUsageLimitDataLimit(921890);
+        config.setUsageLimitStartTimeInMs(format.parse("2016-12-01T10:00:00Z").getTime());
+        config.setUsageLimitTimeLimitInMinutes(120);
+        config.setUsageLimitUsageTimePeriodInMinutes(99910);
+
+        // HomeSP configuration.
+        HomeSp homeSp = new HomeSp();
+        homeSp.setFriendlyName("Century House");
+        homeSp.setFqdn("mi6.co.uk");
+        homeSp.setRoamingConsortiumOis(new long[] {0x112233L, 0x445566L});
+        homeSp.setIconUrl("icon.test.com");
+        Map<String, Long> homeNetworkIds = new HashMap<>();
+        homeNetworkIds.put("TestSSID", 0x12345678L);
+        homeNetworkIds.put("NullHESSID", null);
+        homeSp.setHomeNetworkIds(homeNetworkIds);
+        homeSp.setMatchAllOis(new long[] {0x11223344});
+        homeSp.setMatchAnyOis(new long[] {0x55667788});
+        homeSp.setOtherHomePartners(new String[] {"other.fqdn.com"});
+        config.setHomeSp(homeSp);
+
+        // Credential configuration.
+        Credential credential = new Credential();
+        credential.setCreationTimeInMs(format.parse("2016-01-01T10:00:00Z").getTime());
+        credential.setExpirationTimeInMs(format.parse("2016-02-01T10:00:00Z").getTime());
+        credential.setRealm("shaken.stirred.com");
+        credential.setCheckAaaServerCertStatus(true);
+        Credential.UserCredential userCredential = new Credential.UserCredential();
+        userCredential.setUsername("james");
+        userCredential.setPassword("Ym9uZDAwNw==");
+        userCredential.setMachineManaged(true);
+        userCredential.setSoftTokenApp("TestApp");
+        userCredential.setAbleToShare(true);
+        userCredential.setEapType(21);
+        userCredential.setNonEapInnerMethod("MS-CHAP-V2");
+        credential.setUserCredential(userCredential);
+        Credential.CertificateCredential certCredential = new Credential.CertificateCredential();
+        certCredential.setCertType("x509v3");
+        certCredential.setCertSha256Fingerprint(certFingerprint);
+        credential.setCertCredential(certCredential);
+        Credential.SimCredential simCredential = new Credential.SimCredential();
+        simCredential.setImsi("imsi");
+        simCredential.setEapType(24);
+        credential.setSimCredential(simCredential);
+        config.setCredential(credential);
+
+        // Policy configuration.
+        Policy policy = new Policy();
+        List<Policy.RoamingPartner> preferredRoamingPartnerList = new ArrayList<>();
+        Policy.RoamingPartner partner1 = new Policy.RoamingPartner();
+        partner1.setFqdn("test1.fqdn.com");
+        partner1.setFqdnExactMatch(true);
+        partner1.setPriority(127);
+        partner1.setCountries("us,fr");
+        Policy.RoamingPartner partner2 = new Policy.RoamingPartner();
+        partner2.setFqdn("test2.fqdn.com");
+        partner2.setFqdnExactMatch(false);
+        partner2.setPriority(200);
+        partner2.setCountries("*");
+        preferredRoamingPartnerList.add(partner1);
+        preferredRoamingPartnerList.add(partner2);
+        policy.setPreferredRoamingPartnerList(preferredRoamingPartnerList);
+        policy.setMinHomeDownlinkBandwidth(23412);
+        policy.setMinHomeUplinkBandwidth(9823);
+        policy.setMinRoamingDownlinkBandwidth(9271);
+        policy.setMinRoamingUplinkBandwidth(2315);
+        policy.setExcludedSsidList(new String[] {"excludeSSID"});
+        Map<Integer, String> requiredProtoPortMap = new HashMap<>();
+        requiredProtoPortMap.put(12, "34,92,234");
+        policy.setRequiredProtoPortMap(requiredProtoPortMap);
+        policy.setMaximumBssLoadValue(23);
+        UpdateParameter policyUpdate = new UpdateParameter();
+        policyUpdate.setUpdateIntervalInMinutes(120);
+        policyUpdate.setUpdateMethod(UpdateParameter.UPDATE_METHOD_OMADM);
+        policyUpdate.setRestriction(UpdateParameter.UPDATE_RESTRICTION_HOMESP);
+        policyUpdate.setServerUri("policy.update.com");
+        policyUpdate.setUsername("updateUser");
+        policyUpdate.setBase64EncodedPassword("updatePass");
+        policyUpdate.setTrustRootCertUrl("update.cert.com");
+        policyUpdate.setTrustRootCertSha256Fingerprint(certFingerprint);
+        policy.setPolicyUpdate(policyUpdate);
+        config.setPolicy(policy);
+        return config;
+    }
+
+    /**
+     * Parse and verify all supported fields under PPS MO tree.
+     *
+     * @throws Exception
+     */
+    public void testParsePPSMOTree() throws Exception {
+        String ppsMoTree = loadResourceFile(PPS_MO_XML_FILE);
+        PasspointConfiguration expectedConfig = generateConfigurationFromPPSMOTree();
+        PasspointConfiguration actualConfig = PpsMoParser.parseMoText(ppsMoTree);
+        assertTrue(actualConfig.equals(expectedConfig));
+    }
+}
diff --git a/tests/tests/net/src/android/net/wifi/cts/WifiManagerTest.java b/tests/tests/net/src/android/net/wifi/cts/WifiManagerTest.java
index 4185189..d3dc8fa 100644
--- a/tests/tests/net/src/android/net/wifi/cts/WifiManagerTest.java
+++ b/tests/tests/net/src/android/net/wifi/cts/WifiManagerTest.java
@@ -29,6 +29,9 @@
 import android.net.wifi.WifiManager;
 import android.net.wifi.WifiManager.TxPacketCountListener;
 import android.net.wifi.WifiManager.WifiLock;
+import android.net.wifi.hotspot2.PasspointConfiguration;
+import android.net.wifi.hotspot2.pps.Credential;
+import android.net.wifi.hotspot2.pps.HomeSp;
 import android.os.SystemClock;
 import android.provider.Settings;
 import android.test.AndroidTestCase;
@@ -38,6 +41,8 @@
 
 import java.net.HttpURLConnection;
 import java.net.URL;
+import java.security.MessageDigest;
+import java.security.cert.X509Certificate;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
@@ -543,4 +548,130 @@
         }
         assertTrue(i < 15);
     }
+
+    /**
+     * Verify Passpoint configuration management APIs (add, remove, get) for a Passpoint
+     * configuration with an user credential.
+     *
+     * @throws Exception
+     */
+    public void testAddPasspointConfigWithUserCredential() throws Exception {
+        testAddPasspointConfig(generatePasspointConfig(generateUserCredential()));
+    }
+
+    /**
+     * Verify Passpoint configuration management APIs (add, remove, get) for a Passpoint
+     * configuration with a certificate credential.
+     *
+     * @throws Exception
+     */
+    public void testAddPasspointConfigWithCertCredential() throws Exception {
+        testAddPasspointConfig(generatePasspointConfig(generateCertCredential()));
+    }
+
+    /**
+     * Verify Passpoint configuration management APIs (add, remove, get) for a Passpoint
+     * configuration with a SIm credential.
+     *
+     * @throws Exception
+     */
+    public void testAddPasspointConfigWithSimCredential() throws Exception {
+        testAddPasspointConfig(generatePasspointConfig(generateSimCredential()));
+    }
+
+    /**
+     * Helper function for generating a {@link PasspointConfiguration} for testing.
+     *
+     * @return {@link PasspointConfiguration}
+     */
+    private PasspointConfiguration generatePasspointConfig(Credential credential) {
+        PasspointConfiguration config = new PasspointConfiguration();
+        config.setCredential(credential);
+
+        // Setup HomeSp.
+        HomeSp homeSp = new HomeSp();
+        homeSp.setFqdn("Test.com");
+        homeSp.setFriendlyName("Test Provider");
+        config.setHomeSp(homeSp);
+
+        return config;
+    }
+
+    /**
+     * Helper function for generating an user credential for testing.
+     *
+     * @return {@link Credential}
+     */
+    private Credential generateUserCredential() {
+        Credential credential = new Credential();
+        credential.setRealm("test.net");
+        Credential.UserCredential userCred = new Credential.UserCredential();
+        userCred.setEapType(21 /* EAP_TTLS */);
+        userCred.setUsername("username");
+        userCred.setPassword("password");
+        userCred.setNonEapInnerMethod("PAP");
+        credential.setUserCredential(userCred);
+        credential.setCaCertificate(FakeKeys.CA_CERT0);
+        return credential;
+    }
+
+    /**
+     * Helper function for generating a certificate credential for testing.
+     *
+     * @return {@link Credential}
+     */
+    private Credential generateCertCredential() throws Exception {
+        Credential credential = new Credential();
+        credential.setRealm("test.net");
+        Credential.CertificateCredential certCredential = new Credential.CertificateCredential();
+        certCredential.setCertType("x509v3");
+        certCredential.setCertSha256Fingerprint(
+                MessageDigest.getInstance("SHA-256").digest(FakeKeys.CLIENT_CERT.getEncoded()));
+        credential.setCertCredential(certCredential);
+        credential.setCaCertificate(FakeKeys.CA_CERT0);
+        credential.setClientCertificateChain(new X509Certificate[] {FakeKeys.CLIENT_CERT});
+        credential.setClientPrivateKey(FakeKeys.RSA_KEY1);
+        return credential;
+    }
+
+    /**
+     * Helper function for generating a SIM credential for testing.
+     *
+     * @return {@link Credential}
+     */
+    private Credential generateSimCredential() throws Exception {
+        Credential credential = new Credential();
+        credential.setRealm("test.net");
+        Credential.SimCredential simCredential = new Credential.SimCredential();
+        simCredential.setImsi("1234*");
+        simCredential.setEapType(18 /* EAP_SIM */);
+        credential.setSimCredential(simCredential);
+        return credential;
+    }
+
+    /**
+     * Helper function verifying Passpoint configuration management APIs (add, remove, get) for
+     * a given configuration.
+     *
+     * @param config The configuration to test with
+     */
+    private void testAddPasspointConfig(PasspointConfiguration config) throws Exception {
+        assertTrue(mWifiManager.addOrUpdatePasspointConfiguration(config));
+
+        // Certificates and keys will be set to null after it is installed to the KeyStore by
+        // WifiManager.  Reset them in the expected config so that it can be used to compare
+        // against the retrieved config.
+        config.getCredential().setCaCertificate(null);
+        config.getCredential().setClientCertificateChain(null);
+        config.getCredential().setClientPrivateKey(null);
+
+        // Retrieve the configuration and verify it.
+        List<PasspointConfiguration> configList = mWifiManager.getPasspointConfigurations();
+        assertEquals(1, configList.size());
+        assertEquals(config, configList.get(0));
+
+        // Remove the configuration and verify no installed configuration.
+        assertTrue(mWifiManager.removePasspointConfiguration(config.getHomeSp().getFqdn()));
+        assertTrue(mWifiManager.getPasspointConfigurations().isEmpty());
+    }
 }
diff --git a/tests/tests/preference2/src/android/preference2/cts/PreferenceDataStoreTest.java b/tests/tests/preference2/src/android/preference2/cts/PreferenceDataStoreTest.java
index 5c3b181..69456cf 100644
--- a/tests/tests/preference2/src/android/preference2/cts/PreferenceDataStoreTest.java
+++ b/tests/tests/preference2/src/android/preference2/cts/PreferenceDataStoreTest.java
@@ -17,6 +17,7 @@
 package android.preference2.cts;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 import static org.mockito.AdditionalMatchers.or;
 import static org.mockito.Matchers.any;
@@ -35,8 +36,10 @@
 import static org.mockito.Mockito.verifyZeroInteractions;
 
 import android.content.Context;
+import android.content.SharedPreferences;
 import android.preference.Preference;
 import android.preference.PreferenceDataStore;
+import android.preference.PreferenceManager;
 import android.preference.PreferenceScreen;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.SmallTest;
@@ -47,6 +50,7 @@
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.internal.util.ObjectMethodsGuru;
 
 import java.util.HashSet;
 import java.util.Set;
@@ -57,9 +61,14 @@
 
     private PreferenceWrapper mPreference;
     private PreferenceDataStore mDataStore;
+    private PreferenceScreen mScreen;
+    private PreferenceManager mManager;
+    private SharedPreferences mSharedPref;
 
     private static final String KEY = "TestPrefKey";
     private static final String TEST_STR = "Test";
+    private static final String TEST_DEFAULT_STR = "TestDefault";
+    private static final String TEST_WRONG_STR = "TestFromSharedPref";
 
     @Rule
     public ActivityTestRule<PreferenceFragmentActivity> mActivityRule =
@@ -73,188 +82,30 @@
         mPreference.setKey(KEY);
 
         // Assign the Preference to the PreferenceFragment.
-        PreferenceScreen screen =
-                activity.prefFragment.getPreferenceManager().createPreferenceScreen(activity);
-        screen.addPreference(mPreference);
+        mScreen = activity.prefFragment.getPreferenceManager().createPreferenceScreen(activity);
+        mManager = mScreen.getPreferenceManager();
+        mSharedPref = mManager.getSharedPreferences();
 
         mDataStore = mock(PreferenceDataStore.class);
+
+        // Make sure that the key is not present in SharedPreferences to ensure test correctness.
+        mManager.getSharedPreferences().edit().remove(KEY).commit();
     }
 
     @Test
     public void testPutStringWithDataStoreOnPref() {
         mPreference.setPreferenceDataStore(mDataStore);
+        mScreen.addPreference(mPreference);
         putStringTestCommon();
     }
 
     @Test
     public void testPutStringWithDataStoreOnMgr() {
-        mPreference.getPreferenceManager().setPreferenceDataStore(mDataStore);
+        mManager.setPreferenceDataStore(mDataStore);
+        mScreen.addPreference(mPreference);
         putStringTestCommon();
     }
 
-    @Test
-    public void testGetStringWithDataStoreOnPref() {
-        mPreference.setPreferenceDataStore(mDataStore);
-        mPreference.getString(TEST_STR);
-        verify(mDataStore, atLeastOnce()).getString(eq(KEY), eq(TEST_STR));
-    }
-
-    @Test
-    public void testGetStringWithDataStoreOnMgr() {
-        mPreference.getPreferenceManager().setPreferenceDataStore(mDataStore);
-        mPreference.getString(TEST_STR);
-        verify(mDataStore, atLeastOnce()).getString(eq(KEY), eq(TEST_STR));
-    }
-
-    @Test
-    public void testPutStringSetWithDataStoreOnPref() {
-        mPreference.setPreferenceDataStore(mDataStore);
-        putStringSetTestCommon();
-    }
-
-    @Test
-    public void testPutStringSetWithDataStoreOnMgr() {
-        mPreference.getPreferenceManager().setPreferenceDataStore(mDataStore);
-        putStringSetTestCommon();
-    }
-
-    @Test
-    public void testGetStringSetWithDataStoreOnPref() {
-        mPreference.setPreferenceDataStore(mDataStore);
-        Set<String> testSet = new HashSet<>();
-        mPreference.getStringSet(testSet);
-        verify(mDataStore, atLeastOnce()).getStringSet(eq(KEY), eq(testSet));
-    }
-
-    @Test
-    public void testGetStringSetWithDataStoreOnMgr() {
-        mPreference.getPreferenceManager().setPreferenceDataStore(mDataStore);
-        Set<String> testSet = new HashSet<>();
-        mPreference.getStringSet(testSet);
-        verify(mDataStore, atLeastOnce()).getStringSet(eq(KEY), eq(testSet));
-    }
-
-    @Test
-    public void testPutIntWithDataStoreOnPref() {
-        mPreference.setPreferenceDataStore(mDataStore);
-        putIntTestCommon();
-    }
-
-    @Test
-    public void testPutIntWithDataStoreOnMgr() {
-        mPreference.getPreferenceManager().setPreferenceDataStore(mDataStore);
-        putIntTestCommon();
-    }
-
-    @Test
-    public void testGetIntWithDataStoreOnPref() {
-        mPreference.setPreferenceDataStore(mDataStore);
-        mPreference.getInt(1);
-        verify(mDataStore, atLeastOnce()).getInt(eq(KEY), eq(1));
-    }
-
-    @Test
-    public void testGetIntWithDataStoreOnMgr() {
-        mPreference.getPreferenceManager().setPreferenceDataStore(mDataStore);
-        mPreference.getInt(1);
-        verify(mDataStore, atLeastOnce()).getInt(eq(KEY), eq(1));
-    }
-
-    @Test
-    public void testPutLongWithDataStoreOnPref() {
-        mPreference.setPreferenceDataStore(mDataStore);
-        putLongTestCommon();
-    }
-
-    @Test
-    public void testPutLongWithDataStoreOnMgr() {
-        mPreference.getPreferenceManager().setPreferenceDataStore(mDataStore);
-        putLongTestCommon();
-    }
-
-    @Test
-    public void testGetLongWithDataStoreOnPref() {
-        mPreference.setPreferenceDataStore(mDataStore);
-        mPreference.getLong(1L);
-        verify(mDataStore, atLeastOnce()).getLong(eq(KEY), eq(1L));
-    }
-
-    @Test
-    public void testGetLongWithDataStoreOnMgr() {
-        mPreference.getPreferenceManager().setPreferenceDataStore(mDataStore);
-        mPreference.getLong(1L);
-        verify(mDataStore, atLeastOnce()).getLong(eq(KEY), eq(1L));
-    }
-
-    @Test
-    public void testPutFloatWithDataStoreOnPref() {
-        mPreference.setPreferenceDataStore(mDataStore);
-        putFloatTestCommon();
-    }
-
-    @Test
-    public void testPutFloatWithDataStoreOnMgr() {
-        mPreference.getPreferenceManager().setPreferenceDataStore(mDataStore);
-        putFloatTestCommon();
-    }
-
-    @Test
-    public void testGetFloatWithDataStoreOnPref() {
-        mPreference.setPreferenceDataStore(mDataStore);
-        mPreference.getFloat(1f);
-        verify(mDataStore, atLeastOnce()).getFloat(eq(KEY), eq(1f));
-    }
-
-    @Test
-    public void testGetFloatWithDataStoreOnMgr() {
-        mPreference.getPreferenceManager().setPreferenceDataStore(mDataStore);
-        mPreference.getFloat(1f);
-        verify(mDataStore, atLeastOnce()).getFloat(eq(KEY), eq(1f));
-    }
-
-    @Test
-    public void testPutBooleanWithDataStoreOnPref() {
-        mPreference.setPreferenceDataStore(mDataStore);
-        putBooleanTestCommon();
-    }
-
-    @Test
-    public void testPutBooleanWithDataStoreOnMgr() {
-        mPreference.getPreferenceManager().setPreferenceDataStore(mDataStore);
-        putBooleanTestCommon();
-    }
-
-    @Test
-    public void testGetBooleanWithDataStoreOnPref() {
-        mPreference.setPreferenceDataStore(mDataStore);
-        mPreference.getBoolean(true);
-        verify(mDataStore, atLeastOnce()).getBoolean(eq(KEY), eq(true));
-    }
-
-    @Test
-    public void testGetBooleanWithDataStoreOnMgr() {
-        mPreference.getPreferenceManager().setPreferenceDataStore(mDataStore);
-        mPreference.getBoolean(true);
-        verify(mDataStore, atLeastOnce()).getBoolean(eq(KEY), eq(true));
-    }
-
-    @Test
-    public void testDataStoresHierarchy() {
-        mPreference.setPreferenceDataStore(mDataStore);
-        PreferenceDataStore secondaryDataStore = mock(PreferenceDataStore.class);
-        mPreference.getPreferenceManager().setPreferenceDataStore(secondaryDataStore);
-        mPreference.putString(TEST_STR);
-
-        // Check that the Preference returns the correct data store.
-        assertEquals(mDataStore, mPreference.getPreferenceDataStore());
-
-        // Check that the secondary data store assigned to the manager was NOT used.
-        verifyZeroInteractions(secondaryDataStore);
-
-        // Check that the primary data store assigned directly to the preference was used.
-        verify(mDataStore, atLeast(0)).getString(eq(KEY), anyString());
-    }
-
     private void putStringTestCommon() {
         mPreference.putString(TEST_STR);
 
@@ -263,7 +114,54 @@
         verifyNoMoreInteractions(mDataStore);
 
         // Test that the value was NOT propagated to SharedPreferences.
-        assertNull(mPreference.getSharedPreferences().getString(KEY, null));
+        assertNull(mSharedPref.getString(KEY, null));
+    }
+
+    @Test
+    public void testGetStringWithDataStoreOnPref() {
+        mPreference.setPreferenceDataStore(mDataStore);
+        mScreen.addPreference(mPreference);
+        mPreference.getString(TEST_STR);
+        verify(mDataStore, atLeastOnce()).getString(eq(KEY), eq(TEST_STR));
+    }
+
+    @Test
+    public void testGetStringWithDataStoreOnMgr() {
+        mManager.setPreferenceDataStore(mDataStore);
+        mScreen.addPreference(mPreference);
+        mPreference.getString(TEST_STR);
+        verify(mDataStore, atLeastOnce()).getString(eq(KEY), eq(TEST_STR));
+    }
+
+    /**
+     * This test makes sure that when a default value is set to a preference that has a data store
+     * assigned that the default value is correctly propagated to
+     * {@link Preference#onSetInitialValue(boolean, Object)} instead of passing a value from
+     * {@link android.content.SharedPreferences}. We have this test only for String because the
+     * implementation is not dependent on value type so this coverage should be fine.
+     */
+    @Test
+    public void testDefaultStringValue() {
+        mPreference.setPreferenceDataStore(mDataStore);
+        mPreference.setDefaultValue(TEST_DEFAULT_STR);
+        mSharedPref.edit().putString(KEY, TEST_WRONG_STR).commit();
+        mScreen.addPreference(mPreference);
+        mSharedPref.edit().remove(KEY).commit();
+        assertEquals(TEST_DEFAULT_STR, mPreference.defaultValue);
+    }
+
+    @Test
+    public void testPutStringSetWithDataStoreOnPref() {
+        mPreference.setPreferenceDataStore(mDataStore);
+        mScreen.addPreference(mPreference);
+        putStringSetTestCommon();
+    }
+
+    @Test
+    public void testPutStringSetWithDataStoreOnMgr() {
+        mManager.setPreferenceDataStore(mDataStore);
+        mScreen.addPreference(mPreference);
+        putStringSetTestCommon();
     }
 
     private void putStringSetTestCommon() {
@@ -276,7 +174,39 @@
         verifyNoMoreInteractions(mDataStore);
 
         // Test that the value was NOT propagated to SharedPreferences.
-        assertNull(mPreference.getSharedPreferences().getStringSet(KEY, null));
+        assertNull(mSharedPref.getStringSet(KEY, null));
+    }
+
+    @Test
+    public void testGetStringSetWithDataStoreOnPref() {
+        mPreference.setPreferenceDataStore(mDataStore);
+        mScreen.addPreference(mPreference);
+        Set<String> testSet = new HashSet<>();
+        mPreference.getStringSet(testSet);
+        verify(mDataStore, atLeastOnce()).getStringSet(eq(KEY), eq(testSet));
+    }
+
+    @Test
+    public void testGetStringSetWithDataStoreOnMgr() {
+        mManager.setPreferenceDataStore(mDataStore);
+        mScreen.addPreference(mPreference);
+        Set<String> testSet = new HashSet<>();
+        mPreference.getStringSet(testSet);
+        verify(mDataStore, atLeastOnce()).getStringSet(eq(KEY), eq(testSet));
+    }
+
+    @Test
+    public void testPutIntWithDataStoreOnPref() {
+        mPreference.setPreferenceDataStore(mDataStore);
+        mScreen.addPreference(mPreference);
+        putIntTestCommon();
+    }
+
+    @Test
+    public void testPutIntWithDataStoreOnMgr() {
+        mManager.setPreferenceDataStore(mDataStore);
+        mScreen.addPreference(mPreference);
+        putIntTestCommon();
     }
 
     private void putIntTestCommon() {
@@ -287,7 +217,37 @@
         verifyNoMoreInteractions(mDataStore);
 
         // Test that the value was NOT propagated to SharedPreferences.
-        assertEquals(-1, mPreference.getSharedPreferences().getInt(KEY, -1));
+        assertEquals(-1, mSharedPref.getInt(KEY, -1));
+    }
+
+    @Test
+    public void testGetIntWithDataStoreOnPref() {
+        mPreference.setPreferenceDataStore(mDataStore);
+        mScreen.addPreference(mPreference);
+        mPreference.getInt(1);
+        verify(mDataStore, atLeastOnce()).getInt(eq(KEY), eq(1));
+    }
+
+    @Test
+    public void testGetIntWithDataStoreOnMgr() {
+        mManager.setPreferenceDataStore(mDataStore);
+        mScreen.addPreference(mPreference);
+        mPreference.getInt(1);
+        verify(mDataStore, atLeastOnce()).getInt(eq(KEY), eq(1));
+    }
+
+    @Test
+    public void testPutLongWithDataStoreOnPref() {
+        mPreference.setPreferenceDataStore(mDataStore);
+        mScreen.addPreference(mPreference);
+        putLongTestCommon();
+    }
+
+    @Test
+    public void testPutLongWithDataStoreOnMgr() {
+        mManager.setPreferenceDataStore(mDataStore);
+        mScreen.addPreference(mPreference);
+        putLongTestCommon();
     }
 
     private void putLongTestCommon() {
@@ -298,7 +258,37 @@
         verifyNoMoreInteractions(mDataStore);
 
         // Test that the value was NOT propagated to SharedPreferences.
-        assertEquals(-1, mPreference.getSharedPreferences().getLong(KEY, -1L));
+        assertEquals(-1, mSharedPref.getLong(KEY, -1L));
+    }
+
+    @Test
+    public void testGetLongWithDataStoreOnPref() {
+        mPreference.setPreferenceDataStore(mDataStore);
+        mScreen.addPreference(mPreference);
+        mPreference.getLong(1L);
+        verify(mDataStore, atLeastOnce()).getLong(eq(KEY), eq(1L));
+    }
+
+    @Test
+    public void testGetLongWithDataStoreOnMgr() {
+        mManager.setPreferenceDataStore(mDataStore);
+        mScreen.addPreference(mPreference);
+        mPreference.getLong(1L);
+        verify(mDataStore, atLeastOnce()).getLong(eq(KEY), eq(1L));
+    }
+
+    @Test
+    public void testPutFloatWithDataStoreOnPref() {
+        mPreference.setPreferenceDataStore(mDataStore);
+        mScreen.addPreference(mPreference);
+        putFloatTestCommon();
+    }
+
+    @Test
+    public void testPutFloatWithDataStoreOnMgr() {
+        mManager.setPreferenceDataStore(mDataStore);
+        mScreen.addPreference(mPreference);
+        putFloatTestCommon();
     }
 
     private void putFloatTestCommon() {
@@ -309,7 +299,37 @@
         verifyNoMoreInteractions(mDataStore);
 
         // Test that the value was NOT propagated to SharedPreferences.
-        assertEquals(-1, mPreference.getSharedPreferences().getFloat(KEY, -1f), 0.1f /* epsilon */);
+        assertEquals(-1, mSharedPref.getFloat(KEY, -1f), 0.1f /* epsilon */);
+    }
+
+    @Test
+    public void testGetFloatWithDataStoreOnPref() {
+        mPreference.setPreferenceDataStore(mDataStore);
+        mScreen.addPreference(mPreference);
+        mPreference.getFloat(1f);
+        verify(mDataStore, atLeastOnce()).getFloat(eq(KEY), eq(1f));
+    }
+
+    @Test
+    public void testGetFloatWithDataStoreOnMgr() {
+        mManager.setPreferenceDataStore(mDataStore);
+        mScreen.addPreference(mPreference);
+        mPreference.getFloat(1f);
+        verify(mDataStore, atLeastOnce()).getFloat(eq(KEY), eq(1f));
+    }
+
+    @Test
+    public void testPutBooleanWithDataStoreOnPref() {
+        mPreference.setPreferenceDataStore(mDataStore);
+        mScreen.addPreference(mPreference);
+        putBooleanTestCommon();
+    }
+
+    @Test
+    public void testPutBooleanWithDataStoreOnMgr() {
+        mManager.setPreferenceDataStore(mDataStore);
+        mScreen.addPreference(mPreference);
+        putBooleanTestCommon();
     }
 
     private void putBooleanTestCommon() {
@@ -320,7 +340,63 @@
         verifyNoMoreInteractions(mDataStore);
 
         // Test that the value was NOT propagated to SharedPreferences.
-        assertEquals(false, mPreference.getSharedPreferences().getBoolean(KEY, false));
+        assertEquals(false, mSharedPref.getBoolean(KEY, false));
+    }
+
+    @Test
+    public void testGetBooleanWithDataStoreOnPref() {
+        mPreference.setPreferenceDataStore(mDataStore);
+        mScreen.addPreference(mPreference);
+        mPreference.getBoolean(true);
+        verify(mDataStore, atLeastOnce()).getBoolean(eq(KEY), eq(true));
+    }
+
+    @Test
+    public void testGetBooleanWithDataStoreOnMgr() {
+        mManager.setPreferenceDataStore(mDataStore);
+        mScreen.addPreference(mPreference);
+        mPreference.getBoolean(true);
+        verify(mDataStore, atLeastOnce()).getBoolean(eq(KEY), eq(true));
+    }
+
+    @Test
+    public void testDataStoresHierarchy() {
+        mPreference.setPreferenceDataStore(mDataStore);
+        PreferenceDataStore secondaryDataStore = mock(PreferenceDataStore.class);
+        mManager.setPreferenceDataStore(secondaryDataStore);
+        mPreference.putString(TEST_STR);
+
+        // Check that the Preference returns the correct data store.
+        assertEquals(mDataStore, mPreference.getPreferenceDataStore());
+
+        // Check that the secondary data store assigned to the manager was NOT used.
+        verifyZeroInteractions(secondaryDataStore);
+
+        // Check that the primary data store assigned directly to the preference was used.
+        verify(mDataStore, atLeast(0)).getString(eq(KEY), anyString());
+    }
+
+    /**
+     * When {@link PreferenceDataStore} is NOT assigned, the getter for SharedPreferences should not
+     * return null.
+     */
+    @Test
+    public void testSharedPrefNotNullIfNoDS() {
+        mScreen.addPreference(mPreference);
+        assertNotNull(mPreference.getSharedPreferences());
+        assertNotNull(mPreference.getEditor());
+    }
+
+    /**
+     * When {@link PreferenceDataStore} is assigned, the getter for SharedPreferences have to return
+     * null.
+     */
+    @Test
+    public void testSharedPrefNullIfNoDS() {
+        mScreen.addPreference(mPreference);
+        mPreference.setPreferenceDataStore(mDataStore);
+        assertNull(mPreference.getSharedPreferences());
+        assertNull(mPreference.getEditor());
     }
 
     /**
@@ -328,6 +404,8 @@
      */
     private static class PreferenceWrapper extends Preference {
 
+        Object defaultValue;
+
         PreferenceWrapper(Context context) {
             super(context);
         }
@@ -379,6 +457,12 @@
         boolean getBoolean(boolean defaultValue) {
             return getPersistedBoolean(defaultValue);
         }
+
+        @Override
+        protected void onSetInitialValue(boolean restorePersistedValue, Object defaultValue) {
+            this.defaultValue = defaultValue;
+            super.onSetInitialValue(restorePersistedValue, defaultValue);
+        }
     }
 
 }
diff --git a/tests/tests/provider/Android.mk b/tests/tests/provider/Android.mk
index ce0a890..2298378 100644
--- a/tests/tests/provider/Android.mk
+++ b/tests/tests/provider/Android.mk
@@ -31,6 +31,7 @@
 LOCAL_JAVA_LIBRARIES := android.test.runner telephony-common
 
 LOCAL_STATIC_JAVA_LIBRARIES := \
+    android-support-v4 \
     compatibility-device-util \
     ctstestrunner \
     ub-uiautomator \
diff --git a/tests/tests/provider/AndroidManifest.xml b/tests/tests/provider/AndroidManifest.xml
index 8c1c3cc..9a79c64 100644
--- a/tests/tests/provider/AndroidManifest.xml
+++ b/tests/tests/provider/AndroidManifest.xml
@@ -20,7 +20,8 @@
 
     <uses-sdk android:minSdkVersion="21" android:targetSdkVersion="21" />
 
-    <uses-permission android:name="android.permission.DISABLE_KEYGUARD"/>
+    <uses-permission android:name="android.permission.CAMERA" />
+    <uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
     <uses-permission android:name="android.permission.MANAGE_ACCOUNTS" />
     <uses-permission android:name="android.permission.AUTHENTICATE_ACCOUNTS" />
     <uses-permission android:name="android.permission.USE_CREDENTIALS" />
@@ -39,7 +40,6 @@
     <uses-permission android:name="android.permission.READ_SMS" />
     <uses-permission android:name="android.permission.WRITE_SMS" />
 
-
     <application>
         <uses-library android:name="android.test.runner"/>
 
@@ -82,6 +82,14 @@
             <meta-data android:name="android.content.ContactDirectory" android:value="true" />
         </provider>
 
+        <provider android:name="android.support.v4.content.FileProvider"
+            android:authorities="android.provider.cts.fileprovider"
+            android:grantUriPermissions="true">
+            <meta-data
+                android:name="android.support.FILE_PROVIDER_PATHS"
+                android:resource="@xml/file_paths" />
+        </provider>
+
     </application>
 
     <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
diff --git a/tests/tests/provider/res/xml/file_paths.xml b/tests/tests/provider/res/xml/file_paths.xml
new file mode 100644
index 0000000..486e87c
--- /dev/null
+++ b/tests/tests/provider/res/xml/file_paths.xml
@@ -0,0 +1,19 @@
+<?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.
+-->
+
+<paths xmlns:android="http://schemas.android.com/apk/res/android">
+    <files-path name="debug" path="debug/" />
+</paths>
diff --git a/tests/tests/provider/src/android/provider/cts/GetResultActivity.java b/tests/tests/provider/src/android/provider/cts/GetResultActivity.java
index 382ca6f..566a7d6 100644
--- a/tests/tests/provider/src/android/provider/cts/GetResultActivity.java
+++ b/tests/tests/provider/src/android/provider/cts/GetResultActivity.java
@@ -69,4 +69,12 @@
         }
         return result;
     }
+
+    public Result getResult(long timeout, TimeUnit unit) {
+        try {
+            return mResult.poll(timeout, unit);
+        } catch (InterruptedException e) {
+            throw new RuntimeException(e);
+        }
+    }
 }
diff --git a/tests/tests/provider/src/android/provider/cts/MediaStoreUiTest.java b/tests/tests/provider/src/android/provider/cts/MediaStoreUiTest.java
index 3ede99c..73b5559 100644
--- a/tests/tests/provider/src/android/provider/cts/MediaStoreUiTest.java
+++ b/tests/tests/provider/src/android/provider/cts/MediaStoreUiTest.java
@@ -22,25 +22,32 @@
 import android.content.Intent;
 import android.content.UriPermission;
 import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.media.ExifInterface;
 import android.media.MediaScannerConnection;
 import android.net.Uri;
 import android.os.Environment;
 import android.os.ParcelFileDescriptor;
+import android.os.SystemClock;
 import android.os.storage.StorageManager;
 import android.os.storage.StorageVolume;
-import android.provider.DocumentsContract;
 import android.provider.MediaStore;
 import android.provider.cts.GetResultActivity.Result;
 import android.support.test.uiautomator.By;
 import android.support.test.uiautomator.BySelector;
 import android.support.test.uiautomator.UiDevice;
 import android.support.test.uiautomator.UiObject2;
+import android.support.test.uiautomator.UiSelector;
 import android.support.test.uiautomator.Until;
+import android.support.v4.content.FileProvider;
 import android.test.InstrumentationTestCase;
 import android.text.format.DateUtils;
+import android.util.Log;
+import android.view.KeyEvent;
 
 import java.io.BufferedReader;
 import java.io.File;
+import java.io.FileInputStream;
 import java.io.FileOutputStream;
 import java.io.FileReader;
 import java.io.OutputStream;
@@ -48,6 +55,7 @@
 import java.util.concurrent.TimeUnit;
 
 public class MediaStoreUiTest extends InstrumentationTestCase {
+    private static final String TAG = "MediaStoreUiTest";
 
     private static final int REQUEST_CODE = 42;
     private static final String CONTENT = "Test";
@@ -122,6 +130,80 @@
         }
     }
 
+    /**
+     * Verify that whoever handles {@link MediaStore#ACTION_IMAGE_CAPTURE} can
+     * correctly write the contents into a passed {@code content://} Uri.
+     */
+    public void testImageCapture() throws Exception {
+        final Context context = getInstrumentation().getContext();
+        if (!context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA)) {
+            Log.d(TAG, "Skipping due to lack of camera");
+            return;
+        }
+
+        final File targetDir = new File(context.getFilesDir(), "debug");
+        final File target = new File(targetDir, "capture.jpg");
+
+        targetDir.mkdirs();
+        assertFalse(target.exists());
+
+        final Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
+        intent.putExtra(MediaStore.EXTRA_OUTPUT,
+                FileProvider.getUriForFile(context, "android.provider.cts.fileprovider", target));
+
+        // Figure out who is going to answer the phone
+        final ResolveInfo ri = context.getPackageManager().resolveActivity(intent, 0);
+        Log.d(TAG, "We're probably launching " + ri);
+
+        mActivity.startActivityForResult(intent, REQUEST_CODE);
+        mDevice.waitForIdle();
+
+        // Try a couple different strategies for taking a photo: first take a
+        // photo and confirm using hardware keys
+        mDevice.pressKeyCode(KeyEvent.KEYCODE_CAMERA);
+        mDevice.waitForIdle();
+        SystemClock.sleep(5 * DateUtils.SECOND_IN_MILLIS);
+        mDevice.pressKeyCode(KeyEvent.KEYCODE_DPAD_CENTER);
+        mDevice.waitForIdle();
+
+        // Maybe that gave us a result?
+        Result result = mActivity.getResult(15, TimeUnit.SECONDS);
+        Log.d(TAG, "First pass result was " + result);
+
+        // Hrm, that didn't work; let's try an alternative approach of digging
+        // around for a shutter button
+        if (result == null) {
+            final String pkg = ri.activityInfo.packageName;
+            mDevice.findObject(new UiSelector().resourceId(pkg + ":id/shutter_button")).click();
+            mDevice.waitForIdle();
+            SystemClock.sleep(5 * DateUtils.SECOND_IN_MILLIS);
+            mDevice.findObject(new UiSelector().resourceId(pkg + ":id/shutter_button")).click();
+            mDevice.waitForIdle();
+        }
+
+        result = mActivity.getResult(15, TimeUnit.SECONDS);
+        Log.d(TAG, "Second pass result was " + result);
+
+        assertNotNull("Expected to get a IMAGE_CAPTURE result; your camera app should "
+                + "respond to the CAMERA and DPAD_CENTER keycodes", result);
+
+        assertTrue("exists", target.exists());
+
+        // At the very least we expect photos generated by the device to have
+        // sane baseline EXIF data
+        final ExifInterface exif = new ExifInterface(new FileInputStream(target));
+        assertAttribute(exif, ExifInterface.TAG_MAKE);
+        assertAttribute(exif, ExifInterface.TAG_MODEL);
+        assertAttribute(exif, ExifInterface.TAG_DATETIME);
+    }
+
+    private static void assertAttribute(ExifInterface exif, String tag) {
+        final String res = exif.getAttribute(tag);
+        if (res == null || res.length() == 0) {
+            fail("Expected valid EXIF tag for tag " + tag);
+        }
+    }
+
     private boolean supportsHardware() {
         final PackageManager pm = getInstrumentation().getContext().getPackageManager();
         return !pm.hasSystemFeature("android.hardware.type.television")
diff --git a/tests/tests/provider/src/android/provider/cts/Settings_SecureTest.java b/tests/tests/provider/src/android/provider/cts/Settings_SecureTest.java
index 6c93d9e..7bb5908 100644
--- a/tests/tests/provider/src/android/provider/cts/Settings_SecureTest.java
+++ b/tests/tests/provider/src/android/provider/cts/Settings_SecureTest.java
@@ -18,6 +18,7 @@
 
 
 import android.content.ContentResolver;
+import android.database.Cursor;
 import android.net.Uri;
 import android.provider.Settings;
 import android.provider.Settings.Secure;
@@ -156,4 +157,35 @@
         assertEquals("install_non_market_apps is deprecated. Should be set to 1 by default.",
                 1, Settings.Secure.getInt(cr, Settings.Global.INSTALL_NON_MARKET_APPS));
     }
+
+    private static final String BLUETOOTH_MAC_ADDRESS_SETTING_NAME = "bluetooth_address";
+
+    /**
+     * Asserts that the secure setting containing the Android's Bluetooth MAC address is not
+     * available to non-privileged apps, such as the CTS test app in the context of which this test
+     * runs.
+     */
+    public void testBluetoothAddressNotAvailable() {
+        assertNull(Settings.Secure.getString(cr, BLUETOOTH_MAC_ADDRESS_SETTING_NAME));
+
+        // Assert this setting is not accessible when listing all settings
+        try (Cursor c = cr.query(Settings.Secure.CONTENT_URI, null, null, null, null)) {
+            while ((c != null) && (c.moveToNext())) {
+                String name = c.getString(1);
+                if (BLUETOOTH_MAC_ADDRESS_SETTING_NAME.equals(name)) {
+                    fail("Settings.Secure contains " + name + ": " + c.getString(2));
+                }
+            }
+        }
+
+        // Assert this setting is not accessible when listing this specific setting
+        Uri settingUri =
+                Settings.Secure.CONTENT_URI.buildUpon().appendPath("bluetooth_address").build();
+        try (Cursor c = cr.query(settingUri, null, null, null, null)) {
+            while ((c != null) && (c.moveToNext())) {
+                String name = c.getString(1);
+                fail("Settings.Secure contains " + name + ": " + c.getString(2));
+            }
+        }
+    }
 }
diff --git a/tests/tests/telecom/src/android/telecom/cts/CtsConnectionService.java b/tests/tests/telecom/src/android/telecom/cts/CtsConnectionService.java
index 4257067..329031a 100644
--- a/tests/tests/telecom/src/android/telecom/cts/CtsConnectionService.java
+++ b/tests/tests/telecom/src/android/telecom/cts/CtsConnectionService.java
@@ -180,6 +180,7 @@
         Log.i(LOG_TAG, "Service has been unbound");
         sServiceUnBoundLatch.countDown();
         sIsBound = false;
+        sConnectionService = null;
         return super.onUnbind(intent);
     }
 
diff --git a/tests/tests/telecom/src/android/telecom/cts/CtsSelfManagedConnectionService.java b/tests/tests/telecom/src/android/telecom/cts/CtsSelfManagedConnectionService.java
index ee2f842..4185318 100644
--- a/tests/tests/telecom/src/android/telecom/cts/CtsSelfManagedConnectionService.java
+++ b/tests/tests/telecom/src/android/telecom/cts/CtsSelfManagedConnectionService.java
@@ -94,12 +94,14 @@
     }
 
     @Override
-    public void onCreateIncomingConnectionFailed(ConnectionRequest request) {
+    public void onCreateIncomingConnectionFailed(PhoneAccountHandle connectionManagerHandle,
+                                                 ConnectionRequest request) {
         mLocks[CREATE_INCOMING_CONNECTION_FAILED_LOCK].countDown();
     }
 
     @Override
-    public void onCreateOutgoingConnectionFailed(ConnectionRequest request) {
+    public void onCreateOutgoingConnectionFailed(PhoneAccountHandle connectionManagerHandle,
+                                                 ConnectionRequest request) {
         mLocks[CREATE_OUTGOING_CONNECTION_FAILED_LOCK].countDown();
     }
 
diff --git a/tests/tests/telecom/src/android/telecom/cts/SelfManagedConnectionServiceTest.java b/tests/tests/telecom/src/android/telecom/cts/SelfManagedConnectionServiceTest.java
index 084c420..5ace9ff 100644
--- a/tests/tests/telecom/src/android/telecom/cts/SelfManagedConnectionServiceTest.java
+++ b/tests/tests/telecom/src/android/telecom/cts/SelfManagedConnectionServiceTest.java
@@ -27,6 +27,7 @@
 import java.util.function.Predicate;
 
 import static android.telecom.cts.TestUtils.WAIT_FOR_STATE_CHANGE_TIMEOUT_MS;
+import static android.telecom.cts.TestUtils.waitOnAllHandlers;
 
 /**
  * CTS tests for the self-managed {@link android.telecom.ConnectionService} APIs.
@@ -37,7 +38,6 @@
 public class SelfManagedConnectionServiceTest extends BaseTelecomTestWithMockServices {
     private Uri TEST_ADDRESS_1 = Uri.fromParts("sip", "call1@test.com", null);
     private Uri TEST_ADDRESS_2 = Uri.fromParts("sip", "call2@test.com", null);
-    private Uri TEST_ADDRESS_3 = Uri.fromParts("sip", "call3@test.com", null);
 
     @Override
     protected void setUp() throws Exception {
@@ -197,7 +197,6 @@
         // Expect there to be no managed calls at the moment.
         assertFalse(mTelecomManager.isInManagedCall());
 
-        assertMockInCallServiceUnbound();
         setDisconnectedAndVerify(connection);
     }
 
@@ -226,7 +225,6 @@
         // Expect there to be no managed calls at the moment.
         assertFalse(mTelecomManager.isInManagedCall());
 
-        assertMockInCallServiceUnbound();
         setDisconnectedAndVerify(connection);
     }
 
@@ -346,6 +344,8 @@
 
         connections.forEach((selfManagedConnection) ->
                 selfManagedConnection.disconnectAndDestroy());
+
+        waitOnAllHandlers(getInstrumentation());
     }
 
     public void testEmergencyCallOngoing() throws Exception {
diff --git a/tests/tests/telecom/src/android/telecom/cts/VideoCallTest.java b/tests/tests/telecom/src/android/telecom/cts/VideoCallTest.java
index 9f79628..7f2b27b 100644
--- a/tests/tests/telecom/src/android/telecom/cts/VideoCallTest.java
+++ b/tests/tests/telecom/src/android/telecom/cts/VideoCallTest.java
@@ -444,6 +444,16 @@
                                 Connection.VideoProvider.SESSION_EVENT_RX_RESUME);
                     }
                 });
+
+        assertCallSessionEventReceived(inCallService.getVideoCallCallback(call),
+                VideoProvider.SESSION_EVENT_CAMERA_PERMISSION_ERROR,
+                new Work() {
+                    @Override
+                    public void doWork() {
+                        connection.sendMockCallSessionEvent(
+                                Connection.VideoProvider.SESSION_EVENT_CAMERA_PERMISSION_ERROR);
+                    }
+                });
     }
 
     /**
diff --git a/tests/tests/telephony/AndroidManifest.xml b/tests/tests/telephony/AndroidManifest.xml
index 9601833..38d20c6 100644
--- a/tests/tests/telephony/AndroidManifest.xml
+++ b/tests/tests/telephony/AndroidManifest.xml
@@ -106,6 +106,16 @@
 
         </service>
 
+        <service
+            android:name=".PermissionlessVisualVoicemailService"
+            android:enabled="false"
+            android:exported="true">
+            <intent-filter>
+                <action android:name="android.telephony.VisualVoicemailService"/>
+            </intent-filter>
+
+        </service>
+
         <activity android:name="android.telephony.cts.StubDialerActvity">
             <intent-filter>
                 <action android:name="android.intent.action.DIAL"/>
diff --git a/tests/tests/telephony/src/android/telephony/cts/PermissionlessVisualVoicemailService.java b/tests/tests/telephony/src/android/telephony/cts/PermissionlessVisualVoicemailService.java
new file mode 100644
index 0000000..ca6c18f
--- /dev/null
+++ b/tests/tests/telephony/src/android/telephony/cts/PermissionlessVisualVoicemailService.java
@@ -0,0 +1,26 @@
+/*
+ * 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.telephony.cts;
+
+/**
+ * Same as {@link MockVisualVoicemailService} but does not require {@link
+ * android.Manifest.permission.BIND_VISUAL_VOICEMAIL_SERVICE} in the manifest and should never be
+ * bound by the system.
+ */
+public class PermissionlessVisualVoicemailService extends MockVisualVoicemailService{
+
+}
diff --git a/tests/tests/telephony/src/android/telephony/cts/SmsUsageMonitorShortCodeTest.java b/tests/tests/telephony/src/android/telephony/cts/SmsUsageMonitorShortCodeTest.java
index 62f04c9..88475d1 100644
--- a/tests/tests/telephony/src/android/telephony/cts/SmsUsageMonitorShortCodeTest.java
+++ b/tests/tests/telephony/src/android/telephony/cts/SmsUsageMonitorShortCodeTest.java
@@ -154,7 +154,7 @@
             new ShortCodeTest("ch", "123", CATEGORY_NOT_SHORT_CODE),
             new ShortCodeTest("ch", "234", CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE),
             new ShortCodeTest("ch", "3456", CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE),
-            new ShortCodeTest("ch", "98765", CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE),
+            new ShortCodeTest("ch", "98765", CATEGORY_FREE_SHORT_CODE),
             new ShortCodeTest("ch", "543", CATEGORY_PREMIUM_SHORT_CODE),
             new ShortCodeTest("ch", "83111", CATEGORY_PREMIUM_SHORT_CODE),
             new ShortCodeTest("ch", "234567", CATEGORY_NOT_SHORT_CODE),
diff --git a/tests/tests/telephony/src/android/telephony/cts/VisualVoicemailServiceTest.java b/tests/tests/telephony/src/android/telephony/cts/VisualVoicemailServiceTest.java
index 62b13b1..829cc2d 100644
--- a/tests/tests/telephony/src/android/telephony/cts/VisualVoicemailServiceTest.java
+++ b/tests/tests/telephony/src/android/telephony/cts/VisualVoicemailServiceTest.java
@@ -18,6 +18,7 @@
 
 import android.app.Instrumentation;
 import android.content.BroadcastReceiver;
+import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
@@ -36,6 +37,7 @@
 import android.telecom.PhoneAccount;
 import android.telecom.PhoneAccountHandle;
 import android.telecom.TelecomManager;
+import android.telephony.SmsManager;
 import android.telephony.TelephonyManager;
 import android.telephony.VisualVoicemailService;
 import android.telephony.VisualVoicemailSms;
@@ -89,6 +91,14 @@
                     .getDefaultOutgoingPhoneAccount(PhoneAccount.SCHEME_TEL);
             mPhoneNumber = telecomManager.getLine1Number(mPhoneAccountHandle);
         }
+
+        PackageManager packageManager = mContext.getPackageManager();
+        packageManager.setComponentEnabledSetting(
+                new ComponentName(mContext, MockVisualVoicemailService.class),
+                PackageManager.COMPONENT_ENABLED_STATE_ENABLED, PackageManager.DONT_KILL_APP);
+        packageManager.setComponentEnabledSetting(
+                new ComponentName(mContext, PermissionlessVisualVoicemailService.class),
+                PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP);
     }
 
     @Override
@@ -105,6 +115,55 @@
         super.tearDown();
     }
 
+    public void testPermissionlessService_ignored() {
+        if (!hasTelephony(mContext)) {
+            Log.d(TAG, "skipping test that requires telephony feature");
+            return;
+        }
+
+        PackageManager packageManager = mContext.getPackageManager();
+        packageManager.setComponentEnabledSetting(
+                new ComponentName(mContext, MockVisualVoicemailService.class),
+                PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP);
+        packageManager.setComponentEnabledSetting(
+                new ComponentName(mContext, PermissionlessVisualVoicemailService.class),
+                PackageManager.COMPONENT_ENABLED_STATE_ENABLED, PackageManager.DONT_KILL_APP);
+        String clientPrefix = "//CTSVVM";
+        String text =  "//CTSVVM:STATUS:st=R;rc=0;srv=1;dn=1;ipt=1;spt=0;u=eg@example.com;pw=1";
+
+        VisualVoicemailService.setSmsFilterSettings(mContext, mPhoneAccountHandle,
+                new VisualVoicemailSmsFilterSettings.Builder()
+                        .setClientPrefix(clientPrefix)
+                        .build());
+
+        try {
+            VisualVoicemailService
+                    .sendVisualVoicemailSms(mContext, mPhoneAccountHandle, mPhoneNumber, (short) 0,
+                            text, null);
+            fail("SecurityException expected");
+        } catch (SecurityException e){
+            // Expected
+        }
+
+        CompletableFuture<VisualVoicemailSms> future = new CompletableFuture<>();
+        PermissionlessVisualVoicemailService.setSmsFuture(future);
+
+        setupSmsReceiver();
+
+        SmsManager.getDefault().sendTextMessage(mPhoneNumber, null, text, null, null);
+
+        mSmsReceiver.assertReceived(EVENT_RECEIVED_TIMEOUT_MILLIS);
+        try {
+            future.get(EVENT_NOT_RECEIVED_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
+            throw new RuntimeException("Unexpected visual voicemail SMS received");
+        } catch (TimeoutException e) {
+            // expected
+        } catch (ExecutionException | InterruptedException e) {
+            throw new RuntimeException(e);
+        }
+
+    }
+
     public void testFilter() {
         if (!hasTelephony(mContext)) {
             Log.d(TAG, "skipping test that requires telephony feature");
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/LayerTests.java b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/LayerTests.java
index 4fc8bc8..3a1e41c 100644
--- a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/LayerTests.java
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/LayerTests.java
@@ -376,7 +376,7 @@
     public void testSaveLayerClippedWithAlpha() {
         // verify that renderer can draw nested clipped layers with different alpha
         createTest() // picture mode is disable due to bug:34871089
-            .addCanvasClientWithoutUsingPicture((canvas, width, height) -> {
+            .addCanvasClient((canvas, width, height) -> {
                 Paint redPaint = new Paint();
                 redPaint.setColor(0xffff0000);
                 canvas.saveLayerAlpha(40, 5, 80, 70, 0x7f, Canvas.CLIP_TO_LAYER_SAVE_FLAG);
@@ -392,7 +392,7 @@
     public void testSaveLayerUnclippedWithAlpha() {
         // verify that renderer can draw nested unclipped layers with different alpha
         createTest() // picture mode is disable due to bug:34871089
-            .addCanvasClientWithoutUsingPicture((canvas, width, height) -> {
+            .addCanvasClient((canvas, width, height) -> {
                 Paint redPaint = new Paint();
                 redPaint.setColor(0xffff0000);
                 canvas.saveLayerAlpha(40, 5, 80, 70, 0x7f, 0);
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/DisplayModifier.java b/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/DisplayModifier.java
index 99a9ef6..2de4e34 100644
--- a/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/DisplayModifier.java
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/DisplayModifier.java
@@ -17,6 +17,8 @@
 
 import android.graphics.Canvas;
 import android.graphics.ColorFilter;
+import android.graphics.ColorMatrix;
+import android.graphics.ColorMatrixColorFilter;
 import android.graphics.Paint;
 import android.graphics.PorterDuff;
 import android.graphics.PorterDuffColorFilter;
@@ -326,6 +328,22 @@
                                 put(PORTERDUFF_MODES[i].toString(),
                                         new XfermodeModifier(PORTERDUFF_MODES[i]));
                             }
+                            put("lowSaturationColorMatrix", new DisplayModifier() {
+                                @Override
+                                public void modifyDrawing(Paint paint, Canvas canvas) {
+                                    ColorMatrix matrix = new ColorMatrix();
+                                    matrix.setSaturation(0.1f);
+                                    paint.setColorFilter(new ColorMatrixColorFilter(matrix));
+                                }
+                            });
+                            put("highSaturationColorMatrix", new DisplayModifier() {
+                                @Override
+                                public void modifyDrawing(Paint paint, Canvas canvas) {
+                                    ColorMatrix matrix = new ColorMatrix();
+                                    matrix.setSaturation(10.0f);
+                                    paint.setColorFilter(new ColorMatrixColorFilter(matrix));
+                                }
+                            });
                         }
                     });
 
diff --git a/tests/tests/util/src/android/util/cts/ArrayMapTest.java b/tests/tests/util/src/android/util/cts/ArrayMapTest.java
index ec17516..c89972d 100644
--- a/tests/tests/util/src/android/util/cts/ArrayMapTest.java
+++ b/tests/tests/util/src/android/util/cts/ArrayMapTest.java
@@ -16,6 +16,11 @@
 
 package android.util.cts;
 
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
 import android.os.Bundle;
@@ -31,10 +36,14 @@
 
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
+import java.util.AbstractMap;
+import java.util.Arrays;
 import java.util.Collection;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.Iterator;
 import java.util.Map;
+import java.util.NoSuchElementException;
 import java.util.Set;
 
 @SmallTest
@@ -535,4 +544,131 @@
         }
         checkEntrySetToArray(testMap);
     }
+
+    @Test
+    public void testCanNotIteratePastEnd_entrySetIterator() {
+        Map<String, String> map = new ArrayMap<>();
+        map.put("key 1", "value 1");
+        map.put("key 2", "value 2");
+        Set<Map.Entry<String, String>> expectedEntriesToIterate = new HashSet<>(Arrays.asList(
+                entryOf("key 1", "value 1"),
+                entryOf("key 2", "value 2")
+        ));
+        Iterator<Map.Entry<String, String>> iterator = map.entrySet().iterator();
+
+        // Assert iteration over the expected two entries in any order
+        assertTrue(iterator.hasNext());
+        Map.Entry<String, String> firstEntry = copyOf(iterator.next());
+        assertTrue(expectedEntriesToIterate.remove(firstEntry));
+
+        assertTrue(iterator.hasNext());
+        Map.Entry<String, String> secondEntry = copyOf(iterator.next());
+        assertTrue(expectedEntriesToIterate.remove(secondEntry));
+
+        assertFalse(iterator.hasNext());
+
+        try {
+            iterator.next();
+            fail();
+        } catch (NoSuchElementException expected) {
+        }
+    }
+
+    private static<K, V> Map.Entry<K, V> entryOf(K key, V value) {
+        return new AbstractMap.SimpleEntry<>(key, value);
+    }
+
+    private static<K, V> Map.Entry<K, V> copyOf(Map.Entry<K, V> entry) {
+        return entryOf(entry.getKey(), entry.getValue());
+    }
+
+    @Test
+    public void testCanNotIteratePastEnd_keySetIterator() {
+        Map<String, String> map = new ArrayMap<>();
+        map.put("key 1", "value 1");
+        map.put("key 2", "value 2");
+        Set<String> expectedKeysToIterate = new HashSet<>(Arrays.asList("key 1", "key 2"));
+        Iterator<String> iterator = map.keySet().iterator();
+
+        // Assert iteration over the expected two keys in any order
+        assertTrue(iterator.hasNext());
+        String firstKey = iterator.next();
+        assertTrue(expectedKeysToIterate.remove(firstKey));
+
+        assertTrue(iterator.hasNext());
+        String secondKey = iterator.next();
+        assertTrue(expectedKeysToIterate.remove(secondKey));
+
+        assertFalse(iterator.hasNext());
+
+        try {
+            iterator.next();
+            fail();
+        } catch (NoSuchElementException expected) {
+        }
+    }
+
+    @Test
+    public void testCanNotIteratePastEnd_valuesIterator() {
+        Map<String, String> map = new ArrayMap<>();
+        map.put("key 1", "value 1");
+        map.put("key 2", "value 2");
+        Set<String> expectedValuesToIterate = new HashSet<>(Arrays.asList("value 1", "value 2"));
+        Iterator<String> iterator = map.values().iterator();
+
+        // Assert iteration over the expected two values in any order
+        assertTrue(iterator.hasNext());
+        String firstValue = iterator.next();
+        assertTrue(expectedValuesToIterate.remove(firstValue));
+
+        assertTrue(iterator.hasNext());
+        String secondValue = iterator.next();
+        assertTrue(expectedValuesToIterate.remove(secondValue));
+
+        assertFalse(iterator.hasNext());
+
+        try {
+            iterator.next();
+            fail();
+        } catch (NoSuchElementException expected) {
+        }
+    }
+
+    /**
+     * The entrySet Iterator returns itself from each call to {@code next()}.
+     * This is unusual behavior for {@link Iterator#next()}; this test ensures that
+     * any future change to this behavior is deliberate.
+     */
+    @Test
+    public void testUnusualBehavior_eachEntryIsSameAsIterator_entrySetIterator() {
+        Map<String, String> map = new ArrayMap<>();
+        map.put("key 1", "value 1");
+        map.put("key 2", "value 2");
+        Iterator<Map.Entry<String, String>> iterator = map.entrySet().iterator();
+
+        assertSame(iterator, iterator.next());
+        assertSame(iterator, iterator.next());
+    }
+
+    @Test
+    public void testUnusualBehavior_equalsThrowsAfterRemove_entrySetIterator() {
+        Map<String, String> map = new ArrayMap<>();
+        map.put("key 1", "value 1");
+        map.put("key 2", "value 2");
+        Iterator<Map.Entry<String, String>> iterator = map.entrySet().iterator();
+        iterator.next();
+        iterator.remove();
+        try {
+            iterator.equals(iterator);
+            fail();
+        } catch (IllegalStateException expected) {
+        }
+    }
+
+    private static<T> void assertEqualsBothWays(T a, T b) {
+        assertEquals(a, b);
+        assertEquals(b, a);
+        assertEquals(a.hashCode(), b.hashCode());
+    }
+
 }
diff --git a/tests/tests/util/src/android/util/cts/ArraySetTest.java b/tests/tests/util/src/android/util/cts/ArraySetTest.java
index 554d209..f8ff037 100644
--- a/tests/tests/util/src/android/util/cts/ArraySetTest.java
+++ b/tests/tests/util/src/android/util/cts/ArraySetTest.java
@@ -19,6 +19,7 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotSame;
+import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertSame;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
@@ -34,6 +35,7 @@
 import java.util.ArrayList;
 import java.util.HashSet;
 import java.util.Iterator;
+import java.util.NoSuchElementException;
 
 // As is the case with ArraySet itself, ArraySetTest borrows heavily from ArrayMapTest.
 
@@ -513,4 +515,22 @@
         Object[] objectArray = arraySet.toArray();
         compareArraySetAndRawArray(arraySet, objectArray);
     }
+
+    @Test
+    public void testCanNotIteratePastEnd() {
+        ArraySet<String> set = new ArraySet<>();
+        set.add("value");
+        Iterator<String> iterator = set.iterator();
+
+        assertTrue(iterator.hasNext());
+        assertEquals("value", iterator.next());
+        assertFalse(iterator.hasNext());
+
+        try {
+            iterator.next();
+            fail();
+        } catch (NoSuchElementException expected) {
+        }
+    }
+
 }
diff --git a/tests/tests/view/src/android/view/cts/AbsSavedStateTest.java b/tests/tests/view/src/android/view/cts/AbsSavedStateTest.java
index ba781cf..12bba10 100644
--- a/tests/tests/view/src/android/view/cts/AbsSavedStateTest.java
+++ b/tests/tests/view/src/android/view/cts/AbsSavedStateTest.java
@@ -56,7 +56,7 @@
     @Test
     public void testConstructor() {
         AbsSavedState superState = new AbsSavedStateImpl(Parcel.obtain());
-        assertNull(superState.getSuperState());
+        assertSame(AbsSavedState.EMPTY_STATE, superState.getSuperState());
 
         AbsSavedState s = new AbsSavedStateImpl(superState);
         assertSame(superState, s.getSuperState());
@@ -64,22 +64,21 @@
         Parcel source = Parcel.obtain();
         source.writeParcelable(superState, 0);
         source.setDataPosition(0);
-        s = new AbsSavedStateImpl(source);
+        s = new AbsSavedStateImpl(source, AbsSavedStateImpl.class.getClassLoader());
         assertTrue(s.getSuperState() instanceof AbsSavedState);
 
         source = Parcel.obtain();
         s = new AbsSavedStateImpl(source);
         assertSame(AbsSavedState.EMPTY_STATE, s.getSuperState());
 
-        ClassLoader loader = AbsSavedState.class.getClassLoader();
         source = Parcel.obtain();
         source.writeParcelable(superState, 0);
         source.setDataPosition(0);
-        s = new AbsSavedStateImpl(source, loader);
+        s = new AbsSavedStateImpl(source, AbsSavedStateImpl.class.getClassLoader());
         assertTrue(s.getSuperState() instanceof AbsSavedState);
 
         source = Parcel.obtain();
-        s = new AbsSavedStateImpl(source, loader);
+        s = new AbsSavedStateImpl(source, AbsSavedState.class.getClassLoader());
         assertSame(AbsSavedState.EMPTY_STATE, s.getSuperState());
     }
 
@@ -99,17 +98,20 @@
         parcel.setDataPosition(0);
         AbsSavedState unparceled = AbsSavedState.CREATOR.createFromParcel(parcel);
         assertNotNull(unparceled);
-        assertEquals(AbsSavedState.EMPTY_STATE, unparceled.getSuperState());
+        assertNull(unparceled.getSuperState());
 
         AbsSavedState stateWithSuper = new AbsSavedStateImpl(state);
         parcel = Parcel.obtain();
         stateWithSuper.writeToParcel(parcel, 0);
         parcel.setDataPosition(0);
-        try {
-            AbsSavedState.CREATOR.createFromParcel(parcel);
-            fail("Expected IllegalStateException");
-        } catch (IllegalStateException e) {
-            // Expected.
+        if (AbsSavedState.CREATOR instanceof Parcelable.ClassLoaderCreator) {
+            try {
+                ((Parcelable.ClassLoaderCreator) AbsSavedState.CREATOR).createFromParcel(parcel,
+                        AbsSavedStateImpl.class.getClassLoader());
+                fail("Expected IllegalStateException");
+            } catch (IllegalStateException e) {
+                // Expected.
+            }
         }
     }
 
@@ -136,13 +138,19 @@
             super(source, loader);
         }
 
-        public static final Creator<AbsSavedStateImpl> CREATOR = new Creator<AbsSavedStateImpl>() {
+        public static final Creator<AbsSavedStateImpl> CREATOR =
+                new ClassLoaderCreator<AbsSavedStateImpl>() {
             @Override
             public AbsSavedStateImpl createFromParcel(Parcel source) {
                 return new AbsSavedStateImpl(source);
             }
 
             @Override
+            public AbsSavedStateImpl createFromParcel(Parcel source, ClassLoader loader) {
+                return new AbsSavedStateImpl(source, loader);
+            }
+
+            @Override
             public AbsSavedStateImpl[] newArray(int size) {
                 return new AbsSavedStateImpl[size];
             }
diff --git a/tests/tests/view/src/android/view/textclassifier/cts/TextClassificationManagerTest.java b/tests/tests/view/src/android/view/textclassifier/cts/TextClassificationManagerTest.java
index dc15f17..871e880 100644
--- a/tests/tests/view/src/android/view/textclassifier/cts/TextClassificationManagerTest.java
+++ b/tests/tests/view/src/android/view/textclassifier/cts/TextClassificationManagerTest.java
@@ -18,6 +18,7 @@
 
 import static org.junit.Assert.assertThat;
 
+import android.os.LocaleList;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
@@ -41,6 +42,8 @@
 @RunWith(AndroidJUnit4.class)
 public class TextClassificationManagerTest {
 
+    private static final LocaleList LOCALES = LocaleList.forLanguageTags("en");
+
     private TextClassificationManager mTcm;
     private TextClassifier mClassifier;
 
@@ -55,15 +58,15 @@
     public void testSmartSelection() {
         if (isTextClassifierDisabled()) return;
 
-        String text = "Contact me at droid@email.com";
+        String text = "Contact me at droid@android.com";
         String selected = "droid";
-        String suggested = "droid@email.com";
+        String suggested = "droid@android.com";
         int startIndex = text.indexOf(selected);
         int endIndex = startIndex + selected.length();
         int smartStartIndex = text.indexOf(suggested);
         int smartEndIndex = smartStartIndex + suggested.length();
 
-        assertThat(mClassifier.suggestSelection(text, startIndex, endIndex),
+        assertThat(mClassifier.suggestSelection(text, startIndex, endIndex, LOCALES),
                 isTextSelection(smartStartIndex, smartEndIndex, TextClassifier.TYPE_EMAIL));
     }
 
@@ -79,7 +82,7 @@
         int smartStartIndex = text.indexOf(suggested);
         int smartEndIndex = smartStartIndex + suggested.length();
 
-        assertThat(mClassifier.suggestSelection(text, startIndex, endIndex),
+        assertThat(mClassifier.suggestSelection(text, startIndex, endIndex, LOCALES),
                 isTextSelection(smartStartIndex, smartEndIndex, TextClassifier.TYPE_URL));
     }
 
@@ -87,11 +90,11 @@
     public void testTextClassificationResult() {
         if (isTextClassifierDisabled()) return;
 
-        String text = "Contact me at droid@email.com";
-        String classifiedText = "droid@email.com";
+        String text = "Contact me at droid@android.com";
+        String classifiedText = "droid@android.com";
         int startIndex = text.indexOf(classifiedText);
         int endIndex = startIndex + classifiedText.length();
-        assertThat(mClassifier.getTextClassificationResult(text, startIndex, endIndex),
+        assertThat(mClassifier.getTextClassificationResult(text, startIndex, endIndex, LOCALES),
                 isTextClassificationResult(classifiedText, TextClassifier.TYPE_EMAIL));
     }
 
@@ -103,7 +106,7 @@
         String classifiedText = "http://www.android.com";
         int startIndex = text.indexOf(classifiedText);
         int endIndex = startIndex + classifiedText.length();
-        assertThat(mClassifier.getTextClassificationResult(text, startIndex, endIndex),
+        assertThat(mClassifier.getTextClassificationResult(text, startIndex, endIndex, LOCALES),
                 isTextClassificationResult(classifiedText, TextClassifier.TYPE_URL));
     }
 
diff --git a/tests/tests/widget/res/layout/textview_layout.xml b/tests/tests/widget/res/layout/textview_layout.xml
index 5421574..5272f3c 100644
--- a/tests/tests/widget/res/layout/textview_layout.xml
+++ b/tests/tests/widget/res/layout/textview_layout.xml
@@ -295,6 +295,15 @@
                 android:layout_height="wrap_content"
                 android:fontFamily="@font/samplexmlfont" />
 
+            <!-- This is here to test that the TextView constructor ignores references to
+             non Font resource types in the fontFamily attribute.-->
+            <TextView
+                android:id="@+id/textview_fontxmlresource_nonFontReference"
+                android:text="@string/text_view_hello"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:fontFamily="@style/TextView_FontResource" />
+
             <TextView
                 android:id="@+id/textview_fontresource_style"
                 android:text="@string/text_view_hello"
diff --git a/tests/tests/widget/src/android/widget/cts/PopupWindowTest.java b/tests/tests/widget/src/android/widget/cts/PopupWindowTest.java
index 9b03467..68aaa08 100644
--- a/tests/tests/widget/src/android/widget/cts/PopupWindowTest.java
+++ b/tests/tests/widget/src/android/widget/cts/PopupWindowTest.java
@@ -1261,10 +1261,19 @@
         int measuredWidth = popupRoot.getMeasuredWidth();
         int measuredHeight = popupRoot.getMeasuredHeight();
         View anchor = mActivity.findViewById(R.id.anchor_middle);
+
+        // The popup should occupy all available vertical space.
         int[] anchorLocationInWindowXY = new int[2];
         anchor.getLocationInWindow(anchorLocationInWindowXY);
+        assertEquals(measuredHeight,
+                parentHeight - (anchorLocationInWindowXY[1] + anchor.getHeight()));
 
-        assertEquals(measuredHeight, parentHeight - anchorLocationInWindowXY[1]);
+        // The popup should be vertically aligned to the anchor's bottom edge.
+        int[] anchorLocationOnScreenXY = new int[2];
+        anchor.getLocationOnScreen(anchorLocationOnScreenXY);
+        int[] popupLocationOnScreenXY = new int[2];
+        popupRoot.getLocationOnScreen(popupLocationOnScreenXY);
+        assertEquals(anchorLocationOnScreenXY[1] + anchor.getHeight(), popupLocationOnScreenXY[1]);
     }
 
     @Test
diff --git a/tests/tests/widget/src/android/widget/cts/TextViewTest.java b/tests/tests/widget/src/android/widget/cts/TextViewTest.java
index eae7235..4fe7ded 100644
--- a/tests/tests/widget/src/android/widget/cts/TextViewTest.java
+++ b/tests/tests/widget/src/android/widget/cts/TextViewTest.java
@@ -7008,10 +7008,10 @@
         initTextViewForTypingOnUiThread();
         TextClassifier mockClassifier = mock(TextClassifier.class);
         when(mockClassifier.suggestSelection(
-                any(CharSequence.class), anyInt(), anyInt()))
+                any(CharSequence.class), anyInt(), anyInt(), any(LocaleList.class)))
                 .thenReturn(new TextSelection.Builder(SMARTSELECT_START, SMARTSELECT_END).build());
         when(mockClassifier.getTextClassificationResult(
-                any(CharSequence.class), anyInt(), anyInt()))
+                any(CharSequence.class), anyInt(), anyInt(), any(LocaleList.class)))
                 .thenReturn(new TextClassificationResult.Builder().build());
         mActivityRule.runOnUiThread(() -> {
             mTextView.setTextIsSelectable(true);