Merge "Tests for app "category" API."
diff --git a/apps/CtsVerifier/AndroidManifest.xml b/apps/CtsVerifier/AndroidManifest.xml
index 4668541..ce1acbc 100644
--- a/apps/CtsVerifier/AndroidManifest.xml
+++ b/apps/CtsVerifier/AndroidManifest.xml
@@ -74,7 +74,8 @@
 
         <meta-data android:name="com.google.android.backup.api_key"
                 android:value="AEdPqrEAAAAIbK6ldcOzoeRtQ1u1dFVJ1A7KetRhit-a1Xa82Q" />
-
+        <meta-data android:name="android.telephony.HIDE_VOICEMAIL_SETTINGS_MENU"
+            android:value="true"/>
         <uses-library android:name="android.test.runner"/>
 
         <activity android:name=".TestListActivity" android:label="@string/app_name" />
@@ -2272,6 +2273,48 @@
             </intent-filter>
         </activity>
 
+        <activity android:name=".managedprovisioning.EnterprisePrivacyTestDefaultAppActivity"
+                android:label="@string/enterprise_privacy_default_app"
+                android:enabled="false">
+            <intent-filter>
+                <action android:name="android.intent.action.VIEW" />
+                <data android:scheme="http" />
+                <category android:name="android.intent.category.DEFAULT" />
+                <category android:name="android.intent.category.BROWSABLE" />
+            </intent-filter>
+            <intent-filter>
+                <action android:name="android.media.action.IMAGE_CAPTURE" />
+                <action android:name="android.media.action.IMAGE_CAPTURE_SECURE" />
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
+            <intent-filter>
+                <action android:name="android.intent.action.VIEW" />
+                <data android:scheme="geo" />
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
+            <intent-filter>
+                <action android:name="android.intent.action.SENDTO" />
+                <action android:name="android.intent.action.SEND" />
+                <action android:name="android.intent.action.SEND_MULTIPLE" />
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
+            <intent-filter>
+                <action android:name="android.intent.action.INSERT" />
+                <data android:mimeType="vnd.android.cursor.dir/event" />
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
+            <intent-filter>
+                <action android:name="android.intent.action.PICK" />
+                <data android:mimeType="vnd.android.cursor.dir/contact" />
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
+            <intent-filter>
+                <action android:name="android.intent.action.DIAL" />
+                <action android:name="android.intent.action.CALL" />
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
+        </activity>
+
         <activity android:name=".managedprovisioning.CommandReceiverActivity"
                 android:theme="@android:style/Theme.NoDisplay"
                 android:noHistory="true">
@@ -2856,6 +2899,39 @@
                 android:value="android.hardware.telephony"/>
         </activity>
 
+        <activity
+            android:name=".voicemail.CallSettingsCheckActivity"
+            android:label="@string/call_settings_check_test">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN"/>
+                <category android:name="android.cts.intent.category.MANUAL_TEST"/>
+            </intent-filter>
+
+            <meta-data
+                android:name="test_category"
+                android:value="@string/test_category_telephony"/>
+            <meta-data
+                android:name="test_required_features"
+                android:value="android.hardware.telephony"/>
+        </activity>
+
+        <activity
+            android:name=".voicemail.VoicemailSettingsCheckActivity"
+            android:label="@string/ringtone_settings_check_test">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN"/>
+                <category android:name="android.cts.intent.category.MANUAL_TEST"/>
+            </intent-filter>
+
+            <meta-data
+                android:name="test_category"
+                android:value="@string/test_category_telephony"/>
+            <meta-data
+                android:name="test_required_features"
+                android:value="android.hardware.telephony"/>
+        </activity>
+
+
         <service
             android:name=".voicemail.CtsVisualVoicemailService"
             android:permission="android.permission.BIND_VISUAL_VOICEMAIL_SERVICE"
diff --git a/apps/CtsVerifier/res/layout/voicemail_hide_in_call_settings.xml b/apps/CtsVerifier/res/layout/voicemail_hide_in_call_settings.xml
new file mode 100644
index 0000000..e7a832c
--- /dev/null
+++ b/apps/CtsVerifier/res/layout/voicemail_hide_in_call_settings.xml
@@ -0,0 +1,105 @@
+<?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:orientation="vertical"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+    <TextView
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:text="@string/call_settings_check_instructions"/>
+
+    <LinearLayout
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="@dimen/js_padding"
+        android:layout_marginBottom="@dimen/js_padding">
+        <ImageView
+            android:id="@+id/set_default_dialer_image"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:src="@drawable/fs_indeterminate"
+            android:layout_marginRight="@dimen/js_padding"/>
+        <TextView
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/voicemail_set_default_dialer_description"
+            android:textSize="16dp"/>
+    </LinearLayout>
+
+    <Button
+        android:id="@+id/set_default_dialer"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="@string/voicemail_set_default_dialer_button"/>
+
+    <TextView
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="@string/open_call_settings_explanation"
+        android:textSize="16dp"/>
+    <Button
+        android:id="@+id/open_call_settings"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="@string/open_call_settings"/>
+
+    <LinearLayout
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="@dimen/js_padding"
+        android:layout_marginBottom="@dimen/js_padding">
+        <Button
+            android:id="@+id/settings_hidden"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/voicemail_hidden"/>
+        <Button
+            android:id="@+id/settings_not_hidden"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/voicemail_not_hidden"
+        />
+    </LinearLayout>
+
+    <LinearLayout
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="@dimen/js_padding"
+        android:layout_marginBottom="@dimen/js_padding">
+        <ImageView
+            android:id="@+id/restore_default_dialer_image"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:src="@drawable/fs_indeterminate"
+            android:layout_marginRight="@dimen/js_padding"/>
+        <TextView
+            android:id="@+id/restore_default_dialer_text"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/voicemail_restore_default_dialer_description"
+            android:textSize="16dp"/>
+    </LinearLayout>
+
+    <Button
+        android:id="@+id/restore_default_dialer"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="@string/voicemail_restore_default_dialer_button"/>
+
+    <include layout="@layout/pass_fail_buttons"/>
+</LinearLayout>
\ No newline at end of file
diff --git a/apps/CtsVerifier/res/layout/voicemail_hide_ringtone_settings.xml b/apps/CtsVerifier/res/layout/voicemail_hide_ringtone_settings.xml
new file mode 100644
index 0000000..56b815c
--- /dev/null
+++ b/apps/CtsVerifier/res/layout/voicemail_hide_ringtone_settings.xml
@@ -0,0 +1,81 @@
+<?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:orientation="vertical"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+    <TextView
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:text="@string/ringtone_settings_check_instructions"/>
+
+    <TextView
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="@string/open_voicemail_settings_explanation"
+        android:textSize="16dp"/>
+    <Button
+        android:id="@+id/open_voicemail_settings"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="@string/open_voicemail_settings"/>
+
+    <LinearLayout
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="@dimen/js_padding"
+        android:layout_marginBottom="@dimen/js_padding">
+        <Button
+            android:id="@+id/settings_hidden"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/ringtone_hidden"/>
+        <Button
+            android:id="@+id/settings_not_hidden"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/ringtone_not_hidden"
+        />
+    </LinearLayout>
+
+    <LinearLayout
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="@dimen/js_padding"
+        android:layout_marginBottom="@dimen/js_padding">
+        <ImageView
+            android:id="@+id/restore_default_dialer_image"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:src="@drawable/fs_indeterminate"
+            android:layout_marginRight="@dimen/js_padding"/>
+        <TextView
+            android:id="@+id/restore_default_dialer_text"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/voicemail_restore_default_dialer_description"
+            android:textSize="16dp"/>
+    </LinearLayout>
+
+    <Button
+        android:id="@+id/restore_default_dialer"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="@string/voicemail_restore_default_dialer_button"/>
+
+    <include layout="@layout/pass_fail_buttons"/>
+</LinearLayout>
\ No newline at end of file
diff --git a/apps/CtsVerifier/res/raw/company_terms_content.txt b/apps/CtsVerifier/res/raw/company_terms_content.txt
new file mode 100644
index 0000000..ca0623c
--- /dev/null
+++ b/apps/CtsVerifier/res/raw/company_terms_content.txt
@@ -0,0 +1 @@
+Company Terms Content.
diff --git a/apps/CtsVerifier/res/values/strings.xml b/apps/CtsVerifier/res/values/strings.xml
index c85c3542..e00bbfb 100644
--- a/apps/CtsVerifier/res/values/strings.xml
+++ b/apps/CtsVerifier/res/values/strings.xml
@@ -1468,6 +1468,7 @@
     <string name="nls_hints">Check that the listener can set hints.</string>
     <string name="nls_snooze_one">Check that service can snooze a notification.</string>
     <string name="nls_snooze_one_time">Check that service can snooze a notification for a given time.</string>
+    <string name="nls_get_snoozed">Check that service can retrieve snoozed notifications.</string>
     <string name="nls_unsnooze_one">Check that service can unsnooze a notification.</string>
     <string name="nas_note_missed_enqueued">Check that notification was not enqueued.</string>
     <string name="cp_test">Condition Provider test</string>
@@ -1872,7 +1873,7 @@
     <string name="provisioning_tests_byod_custom_color"> Custom provisioning color </string>
     <string name="provisioning_tests_byod_custom_color_info">
         Please press the Go button to start the provisioning.
-        Check that provisioning is colorized in green.
+        Check that the top status bar and "Accept and continue" button are colorized in green.
         Then hit back and stop the provisioning.
     </string>
     <string name="provisioning_tests_byod_custom_image"> Custom provisioning image </string>
@@ -1881,6 +1882,14 @@
         Check that the CtsVerifier logo is displayed at the top of the page.
         Then hit back and stop the provisioning.
     </string>
+    <string name="provisioning_tests_byod_custom_terms">Custom terms</string>
+    <string name="provisioning_tests_byod_custom_terms_instructions">
+        1. Please press the Go button to start the provisioning.\n
+        2. Click \"View Terms\" button\n
+        3. Expand \"Company ABC\" section. Verify the section content is \"Company Terms Content.\"\n
+        4. Then hit back twice and stop the provisioning.
+    </string>
+    <string name="provisioning_tests_byod_custom_term_header1">Company ABC</string>
 
     <!-- Strings for BYOD managed provisioning (ByodFlowTestActivity) -->
     <string name="test_category_managed_provisioning">Managed Provisioning</string>
@@ -2783,11 +2792,14 @@
     <string name="enterprise_privacy_page_info">
         Please press the Go button to open Settings. Verify that:\n
         1) There is an entry for system privacy.\n
-        2) Tapping that entry opens a screen in which you are told your organization can:\n
-        3) Change settings on this device.\n
-        4) See data associated with your work account.\n
-        5) See the list of all apps on your device.\n
-        6) See usage of each app on your device.\n
+        2) Tapping that entry opens a screen.\n
+        3) In this screen, you are told your organization can:\n
+        * Change settings on this device.\n
+        * See data associated with your work account.\n
+        * See the list of all apps on your device.\n
+        * See usage of each app on your device.\n
+        * Lock the device and change the password.\n
+        * Wipe the device.\n
         \n
         Use the Back button to return to this page.
     </string>
@@ -2881,6 +2893,18 @@
         6) Repeat steps (2) through (4), verifying that in step (3), you are told now that your administrator has allowed one app to access your camera.\n
         7) Press the Reset button.
     </string>
+    <string name="enterprise_privacy_default_apps">Default apps</string>
+    <string name="enterprise_privacy_default_apps_info">
+        Please do the following:\n
+        1) Press the Reset button.\n
+        2) Press the Open Settings button.\n
+        3) In the screen that opens, verify that you are not told that your administrator set any default apps.\n
+        4) Use the Back button to return to this page.\n
+        5) Press the Set Default Apps button.\n
+        6) Repeat steps (2) through (4), verifying that in step (3), you are told now that your administrator has set seven default apps.\n
+        7) Press the Reset button.
+    </string>
+    <string name="enterprise_privacy_set_default_apps">Set Default Apps</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
@@ -2964,6 +2988,7 @@
         9) Verify that a screen informing you what your managing organization can do is shown.\n
         11) Use the Back button to return to this page.
     </string>
+    <string name="enterprise_privacy_default_app">CTS Verifier Test</string>
 
     <!-- Strings for Network Logging Tests -->
     <string name="device_owner_network_logging_ui">Network Logging UI</string>
@@ -3524,5 +3549,31 @@
     <string name="visual_voicemail_service_remove_sim">Remove SIM</string>
     <string name="visual_voicemail_service_remove_sim_received">SIM removal event received</string>
 
+    <!-- Strings for CallSettingsCheck test -->
+    <string name="call_settings_check_test">Hide voicemail in call settings test</string>
+    <string name="call_settings_check_instructions">This test verifies that the default dialer can
+        hide voicemail settings in the call settings menu by using
+        TelephonyManager.METADATA_HIDE_VOICEMAIL_SETTINGS_MENU
+    </string>
+    <string name="open_call_settings_explanation">Tap the button, and verify that \"voicemail\" does
+        not exist in the call settings
+    </string>
+    <string name="open_call_settings">Open call settings</string>
+    <string name="voicemail_hidden">\"Voicemail\" does not exist</string>
+    <string name="voicemail_not_hidden">\"Voicemail\" exists</string>
+
+
+    <!-- Strings for VoicemailSettingsCheck test -->
+    <string name="ringtone_settings_check_test">Hide settings in voicemail test</string>
+    <string name="ringtone_settings_check_instructions">This test verifies that voicemail settings
+        accessible with public API can be hidden when launching
+        TelephonyManager.ACTION_CONFIGURE_VOICEMAIL with EXTRA_HIDE_PUBLIC_SETTINGS.
+    </string>
+    <string name="open_voicemail_settings_explanation">Tap the button, ringtone and virbration
+        settings does not exist in the voicemail settings.
+    </string>
+    <string name="open_voicemail_settings">Open voicemail settings</string>
+    <string name="ringtone_hidden">Ringtone settings does not exist</string>
+    <string name="ringtone_not_hidden">Ringtone settings exists</string>
 
 </resources>
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodProvisioningTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodProvisioningTestActivity.java
index dfbbb31..6856d2f 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodProvisioningTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodProvisioningTestActivity.java
@@ -44,14 +44,17 @@
                         R.string.provisioning_tests_byod_custom_color,
                         R.string.provisioning_tests_byod_custom_color_info,
                         new ButtonInfo(R.string.go_button_text, colorIntent)));
-        Uri logoUri = Uri.parse(ContentResolver.SCHEME_ANDROID_RESOURCE + "://" + getPackageName()
-                + "/" + R.drawable.icon);
+        Uri logoUri = getResourceUri(R.drawable.icon);
         Intent imageIntent = new Intent(this, ProvisioningStartingActivity.class)
                 .putExtra(DevicePolicyManager.EXTRA_PROVISIONING_LOGO_URI, logoUri);
         adapter.add(Utils.createInteractiveTestItem(this, "BYOD_CustomImage",
                         R.string.provisioning_tests_byod_custom_image,
                         R.string.provisioning_tests_byod_custom_image_info,
                         new ButtonInfo(R.string.go_button_text, imageIntent)));
+        adapter.add(Utils.createInteractiveTestItem(this, "BYOD_CustomTerms",
+                R.string.provisioning_tests_byod_custom_terms,
+                R.string.provisioning_tests_byod_custom_terms_instructions,
+                new ButtonInfo(R.string.go_button_text, getTestTermsIntent())));
 
         setTestListAdapter(adapter);
     }
@@ -81,4 +84,24 @@
             finish();
         }
     }
+
+    private Intent getTestTermsIntent() {
+        Bundle bundle = new Bundle();
+        bundle.putString(DevicePolicyManager.EXTRA_PROVISIONING_DISCLAIMER_HEADER,
+                getString(R.string.provisioning_tests_byod_custom_term_header1));
+        bundle.putParcelable(DevicePolicyManager.EXTRA_PROVISIONING_DISCLAIMER_CONTENT,
+                getResourceUri(R.raw.company_terms_content));
+
+        return new Intent(this, ProvisioningStartingActivity.class)
+                .putExtra(DevicePolicyManager.EXTRA_PROVISIONING_DISCLAIMERS,
+                        new Bundle[] { bundle });
+    }
+
+    private Uri getResourceUri(int resId) {
+        return new Uri.Builder().scheme(ContentResolver.SCHEME_ANDROID_RESOURCE)
+                .authority(getPackageName())
+                .appendPath(getResources().getResourceTypeName(resId))
+                .appendPath(getResources().getResourceEntryName(resId))
+                .build();
+    }
 }
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 028639d..28c6cfa 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/CommandReceiverActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/CommandReceiverActivity.java
@@ -23,12 +23,16 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
+import android.content.IntentFilter;
 import android.content.pm.PackageInstaller;
+import android.content.pm.PackageManager;
 import android.graphics.BitmapFactory;
 import android.net.ProxyInfo;
 import android.os.Bundle;
 import android.os.UserHandle;
 import android.os.UserManager;
+import android.provider.ContactsContract;
+import android.provider.MediaStore;
 import android.util.Log;
 
 import com.android.cts.verifier.R;
@@ -78,6 +82,10 @@
     public static final String COMMAND_INSTALL_HELPER_PACKAGE = "install-helper-package";
     public static final String COMMAND_UNINSTALL_HELPER_PACKAGE = "uninstall-helper-package";
     public static final String COMMAND_SET_PERMISSION_GRANT_STATE = "set-permission-grant-state";
+    public static final String COMMAND_ADD_PERSISTENT_PREFERRED_ACTIVITIES =
+            "add-persistent-preferred-activities";
+    public static final String COMMAND_CLEAR_PERSISTENT_PREFERRED_ACTIVITIES =
+            "clear-persistent-preferred-activities";
     public static final String COMMAND_CREATE_MANAGED_PROFILE = "create-managed-profile";
     public static final String COMMAND_REMOVE_MANAGED_PROFILE = "remove-managed-profile";
     public static final String COMMAND_SET_ALWAYS_ON_VPN = "set-always-on-vpn";
@@ -267,6 +275,57 @@
                             intent.getIntExtra(EXTRA_GRANT_STATE,
                                     DevicePolicyManager.PERMISSION_GRANT_STATE_DEFAULT));
                 } break;
+                case COMMAND_ADD_PERSISTENT_PREFERRED_ACTIVITIES: {
+                    final ComponentName componentName =
+                            EnterprisePrivacyTestDefaultAppActivity.COMPONENT_NAME;
+                    // Browser
+                    IntentFilter filter = new IntentFilter();
+                    filter.addAction(Intent.ACTION_VIEW);
+                    filter.addCategory(Intent.CATEGORY_BROWSABLE);
+                    filter.addDataScheme("http");
+                    mDpm.addPersistentPreferredActivity(mAdmin, filter, componentName);
+                    // Camera
+                    filter = new IntentFilter();
+                    filter.addAction(MediaStore.ACTION_IMAGE_CAPTURE);
+                    filter.addAction(MediaStore.ACTION_VIDEO_CAPTURE);
+                    mDpm.addPersistentPreferredActivity(mAdmin, filter, componentName);
+                    // Map
+                    filter = new IntentFilter();
+                    filter.addAction(Intent.ACTION_VIEW);
+                    filter.addDataScheme("geo");
+                    mDpm.addPersistentPreferredActivity(mAdmin, filter, componentName);
+                    // E-mail
+                    filter = new IntentFilter();
+                    filter.addAction(Intent.ACTION_SENDTO);
+                    filter.addAction(Intent.ACTION_SEND);
+                    filter.addAction(Intent.ACTION_SEND_MULTIPLE);
+                    mDpm.addPersistentPreferredActivity(mAdmin, filter, componentName);
+                    // Calendar
+                    filter = new IntentFilter();
+                    filter.addAction(Intent.ACTION_INSERT);
+                    filter.addDataType("vnd.android.cursor.dir/event");
+                    mDpm.addPersistentPreferredActivity(mAdmin, filter, componentName);
+                    // Contacts
+                    filter = new IntentFilter();
+                    filter.addAction(Intent.ACTION_PICK);
+                    filter.addDataType(ContactsContract.Contacts.CONTENT_TYPE);
+                    mDpm.addPersistentPreferredActivity(mAdmin, filter, componentName);
+                    // Dialer
+                    filter = new IntentFilter();
+                    filter.addAction(Intent.ACTION_DIAL);
+                    filter.addAction(Intent.ACTION_CALL);
+                    mDpm.addPersistentPreferredActivity(mAdmin, filter, componentName);
+                    getPackageManager().setComponentEnabledSetting(componentName,
+                            PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
+                            PackageManager.DONT_KILL_APP);
+                } break;
+                case COMMAND_CLEAR_PERSISTENT_PREFERRED_ACTIVITIES: {
+                    mDpm.clearPackagePersistentPreferredActivities(mAdmin, getPackageName());
+                    getPackageManager().setComponentEnabledSetting(
+                            EnterprisePrivacyTestDefaultAppActivity.COMPONENT_NAME,
+                            PackageManager.COMPONENT_ENABLED_STATE_DEFAULT,
+                            PackageManager.DONT_KILL_APP);
+                } break;
                 case COMMAND_CREATE_MANAGED_PROFILE: {
                     if (!mDpm.isDeviceOwnerApp(getPackageName())) {
                         return;
@@ -383,11 +442,16 @@
                 DevicePolicyManager.PERMISSION_GRANT_STATE_DEFAULT);
         mDpm.setPermissionGrantState(mAdmin, getPackageName(), Manifest.permission.CAMERA,
                 DevicePolicyManager.PERMISSION_GRANT_STATE_DEFAULT);
+        mDpm.clearPackagePersistentPreferredActivities(mAdmin, getPackageName());
         mDpm.setAlwaysOnVpnPackage(mAdmin, null, false);
         mDpm.setRecommendedGlobalProxy(mAdmin, null);
 
         uninstallHelperPackage();
         removeManagedProfile();
+        getPackageManager().setComponentEnabledSetting(
+                EnterprisePrivacyTestDefaultAppActivity.COMPONENT_NAME,
+                PackageManager.COMPONENT_ENABLED_STATE_DEFAULT,
+                PackageManager.DONT_KILL_APP);
     }
 
     private void clearProfileOwnerRelatedPolicies() {
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/EnterprisePrivacyTestDefaultAppActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/EnterprisePrivacyTestDefaultAppActivity.java
new file mode 100644
index 0000000..ed7d8f4
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/EnterprisePrivacyTestDefaultAppActivity.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 com.android.cts.verifier.managedprovisioning;
+
+import android.app.Activity;
+import android.content.ComponentName;
+
+public class EnterprisePrivacyTestDefaultAppActivity extends Activity {
+
+    public static final ComponentName COMPONENT_NAME = new ComponentName("com.android.cts.verifier",
+            EnterprisePrivacyTestDefaultAppActivity.class.getName());
+}
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 54f1b79..c06ba2b 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/EnterprisePrivacyTestListActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/EnterprisePrivacyTestListActivity.java
@@ -52,6 +52,8 @@
             = "ENTERPRISE_PRIVACY_MICROPHONE_ACCESS";
     private static final String ENTERPRISE_PRIVACY_CAMERA_ACCESS
             = "ENTERPRISE_PRIVACY_CAMERA_ACCESS";
+    private static final String ENTERPRISE_PRIVACY_DEFAULT_APPS
+            = "ENTERPRISE_PRIVACY_DEFAULT_APPS";
     private static final String ENTERPRISE_PRIVACY_ALWAYS_ON_VPN
             = "ENTERPRISE_PRIVACY_ALWAYS_ON_VPN";
     private static final String ENTERPRISE_PRIVACY_COMP_ALWAYS_ON_VPN
@@ -167,6 +169,18 @@
                 R.string.enterprise_privacy_admin_granted_camera_access,
                 R.string.enterprise_privacy_admin_granted_camera_access_info,
                 Manifest.permission.CAMERA));
+        adapter.add(createInteractiveTestItem(this, ENTERPRISE_PRIVACY_DEFAULT_APPS,
+                R.string.enterprise_privacy_default_apps,
+                R.string.enterprise_privacy_default_apps_info,
+                new ButtonInfo[] {
+                        new ButtonInfo(R.string.enterprise_privacy_open_settings,
+                                new Intent(Settings.ACTION_ENTERPRISE_PRIVACY_SETTINGS)),
+                        new ButtonInfo(R.string.enterprise_privacy_reset, buildCommandIntent(
+                                CommandReceiverActivity
+                                        .COMMAND_CLEAR_PERSISTENT_PREFERRED_ACTIVITIES)),
+                        new ButtonInfo(R.string.enterprise_privacy_set_default_apps,
+                                buildCommandIntent(CommandReceiverActivity
+                                        .COMMAND_ADD_PERSISTENT_PREFERRED_ACTIVITIES))}));
         adapter.add(createInteractiveTestItem(this, ENTERPRISE_PRIVACY_ALWAYS_ON_VPN,
                 R.string.enterprise_privacy_always_on_vpn,
                 R.string.enterprise_privacy_always_on_vpn_info,
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/notifications/MockAssistant.java b/apps/CtsVerifier/src/com/android/cts/verifier/notifications/MockAssistant.java
index 1f86f64..36a502c 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/notifications/MockAssistant.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/notifications/MockAssistant.java
@@ -62,6 +62,7 @@
     public static final String SERVICE_ADJUST_ENQUEUE = SERVICE_BASE + "ADJUST_ENQUEUE";
     public static final String SERVICE_SNOOZE_UNTIL_CONTEXT = SERVICE_BASE + "CONTEXT_SNOOZE";
     public static final String SERVICE_SNOOZED = SERVICE_BASE + "SNOOZED_UNTIL_CONTEXT";
+    public static final String SERVICE_UNSNOOZE_ONE = SERVICE_BASE + "SERVICE_UNSNOOZE_ONE";
 
     static final String EXTRA_PAYLOAD = "PAYLOAD";
     static final String EXTRA_INT = "INT";
@@ -226,6 +227,10 @@
                     String snoozeCriterionId = intent.getStringExtra(EXTRA_PAYLOAD);
                     MockAssistant.this.snoozeNotification(
                             mNotificationKeys.get(tag), snoozeCriterionId);
+                } else if (SERVICE_UNSNOOZE_ONE.equals(action)) {
+                    String tag = intent.getStringExtra(EXTRA_TAG);
+                    String key = mNotificationKeys.get(tag);
+                    MockAssistant.this.unsnoozeNotification(key);
                 } else {
                     Log.w(TAG, "unknown action");
                     setResultCode(Activity.RESULT_CANCELED);
@@ -252,6 +257,7 @@
         filter.addAction(SERVICE_ADJUST_ENQUEUE);
         filter.addAction(SERVICE_SNOOZE_UNTIL_CONTEXT);
         filter.addAction(SERVICE_SNOOZED);
+        filter.addAction(SERVICE_UNSNOOZE_ONE);
         registerReceiver(mReceiver, filter);
     }
 
@@ -403,10 +409,6 @@
         requestStringListResult(context, SERVICE_POSTED, catcher);
     }
 
-    public static void probeListenerOrder(Context context, StringListResultCatcher catcher) {
-        requestStringListResult(context, SERVICE_ORDER, catcher);
-    }
-
     public static void probeListenerPayloads(Context context, BundleListResultCatcher catcher) {
         requestBundleListResult(context, SERVICE_PAYLOADS, catcher);
     }
@@ -487,6 +489,10 @@
         context.sendBroadcast(broadcast);
     }
 
+    public static void unsnoozeOne(Context context, String tag) {
+        sendCommand(context, SERVICE_UNSNOOZE_ONE, tag, 0);
+    }
+
     public abstract static class StatusCatcher extends BroadcastReceiver {
         @Override
         public void onReceive(Context context, Intent intent) {
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/notifications/MockListener.java b/apps/CtsVerifier/src/com/android/cts/verifier/notifications/MockListener.java
index 6c294e5..4440104 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/notifications/MockListener.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/notifications/MockListener.java
@@ -56,9 +56,8 @@
     static final String SERVICE_PROBE_HINTS = SERVICE_BASE + "SERVICE_PROBE_HINTS";
     static final String SERVICE_ORDER = SERVICE_BASE + "SERVICE_ORDER";
     static final String SERVICE_DND = SERVICE_BASE + "SERVICE_DND";
-    static final String SERVICE_SNOOZE_ONE = SERVICE_BASE + "SERVICE_SNOOZE_ONE";
-    static final String SERVICE_UNSNOOZE_ONE = SERVICE_BASE + "SERVICE_UNSNOOZE_ONE";
     static final String SERVICE_SNOOZE_UNTIL = SERVICE_BASE + "SERVICE_SNOOZE_UNTIL";
+    static final String SERVICE_GET_SNOOZED = SERVICE_BASE + "GET_SNOOZED";
 
     static final String EXTRA_PAYLOAD = "PAYLOAD";
     static final String EXTRA_INT = "INT";
@@ -85,6 +84,7 @@
     private ArrayMap<String, String> mNotificationKeys = new ArrayMap<>();
     private ArrayList<String> mRemoved = new ArrayList<String>();
     private ArrayMap<String, JSONObject> mRemovedReason = new ArrayMap<>();
+    private ArrayList<String> mSnoozed = new ArrayList<>();
     private ArrayList<String> mOrder = new ArrayList<>();
     private Set<String> mTestPackages = new HashSet<>();
     private BroadcastReceiver mReceiver;
@@ -100,6 +100,7 @@
 
         mPosted = new ArrayList<String>();
         mRemoved = new ArrayList<String>();
+        mSnoozed = new ArrayList<String>();
 
         mReceiver = new BroadcastReceiver() {
             @Override
@@ -161,19 +162,20 @@
                     bundle.putInt(EXTRA_INT, MockListener.this.getCurrentListenerHints());
                     setResultExtras(bundle);
                     setResultCode(Activity.RESULT_OK);
-                } else if (SERVICE_SNOOZE_ONE.equals(action)) {
-                    String tag = intent.getStringExtra(EXTRA_TAG);
-                    String key = mNotificationKeys.get(tag);
-                    MockListener.this.snoozeNotification(key);
-                } else if (SERVICE_UNSNOOZE_ONE.equals(action)) {
-                    String tag = intent.getStringExtra(EXTRA_TAG);
-                    String key = mNotificationKeys.get(tag);
-                    MockListener.this.unsnoozeNotification(key);
                 } else if (SERVICE_SNOOZE_UNTIL.equals(action)) {
                     String tag = intent.getStringExtra(EXTRA_TAG);
                     String key = mNotificationKeys.get(tag);
                     MockListener.this.snoozeNotification(key,
                             intent.getLongExtra(EXTRA_LONG, (long) 0));
+                } else if (SERVICE_GET_SNOOZED.equals(action)) {
+                    mSnoozed.clear();
+                    StatusBarNotification[] snoozed = MockListener.this.getSnoozedNotifications();
+                    for (StatusBarNotification sbn : snoozed) {
+                        mSnoozed.add(sbn.getTag());
+                    }
+                    bundle.putStringArrayList(EXTRA_PAYLOAD, mSnoozed);
+                    setResultExtras(bundle);
+                    setResultCode(Activity.RESULT_OK);
                 } else {
                     Log.w(TAG, "unknown action");
                     setResultCode(Activity.RESULT_CANCELED);
@@ -194,9 +196,8 @@
         filter.addAction(SERVICE_SNOOZE);
         filter.addAction(SERVICE_HINTS);
         filter.addAction(SERVICE_PROBE_HINTS);
-        filter.addAction(SERVICE_SNOOZE_ONE);
-        filter.addAction(SERVICE_UNSNOOZE_ONE);
         filter.addAction(SERVICE_SNOOZE_UNTIL);
+        filter.addAction(SERVICE_GET_SNOOZED);
         registerReceiver(mReceiver, filter);
     }
 
@@ -228,6 +229,7 @@
         mRemoved.clear();
         mOrder.clear();
         mRemovedReason.clear();
+        mSnoozed.clear();
     }
 
     @Override
@@ -316,6 +318,10 @@
         requestStringListResult(context, SERVICE_POSTED, catcher);
     }
 
+    public static void probeListenerSnoozed(Context context, StringListResultCatcher catcher) {
+        requestStringListResult(context, SERVICE_GET_SNOOZED, catcher);
+    }
+
     public static void probeListenerOrder(Context context, StringListResultCatcher catcher) {
         requestStringListResult(context, SERVICE_ORDER, catcher);
     }
@@ -351,14 +357,6 @@
         sendCommand(context, SERVICE_CLEAR_ONE, tag, code);
     }
 
-    public static void snoozeOne(Context context, String tag) {
-        sendCommand(context, SERVICE_SNOOZE_ONE, tag, 0);
-    }
-
-    public static void unsnoozeOne(Context context, String tag) {
-        sendCommand(context, SERVICE_UNSNOOZE_ONE, tag, 0);
-    }
-
     public static void snoozeOneUntil(Context context, String tag, long until) {
         Intent broadcast = new Intent(SERVICE_SNOOZE_UNTIL);
         if (tag != null) {
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/notifications/NotificationAssistantVerifierActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/notifications/NotificationAssistantVerifierActivity.java
index 8d5b911..540240d 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/notifications/NotificationAssistantVerifierActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/notifications/NotificationAssistantVerifierActivity.java
@@ -117,6 +117,7 @@
         tests.add(new AdjustNotificationTest());
         tests.add(new AdjustEnqueuedNotificationTest());
         tests.add(new SnoozeNotificationUntilContextTest());
+        tests.add(new UnsnoozeNotificationTest());
         tests.add(new IsDisabledTest());
         tests.add(new ServiceStoppedTest());
         tests.add(new NotificationNotEnqueuedTest());
@@ -1025,8 +1026,8 @@
     }
 
     private class CreateChannelTest extends InteractiveTestCase {
-        private NotificationChannel channel = new NotificationChannel("channelForOtherPkg", "new",
-                NotificationManager.IMPORTANCE_LOW);
+        private NotificationChannel channel = new NotificationChannel(UUID.randomUUID().toString(),
+                "new", NotificationManager.IMPORTANCE_LOW);
 
         @Override
         View inflate(ViewGroup parent) {
@@ -1074,8 +1075,8 @@
     }
 
     private class DeleteChannelTest extends InteractiveTestCase {
-        private NotificationChannel channel = new NotificationChannel("1channelForOtherPkg", "new",
-                NotificationManager.IMPORTANCE_LOW);
+        private NotificationChannel channel = new NotificationChannel(UUID.randomUUID().toString(),
+                "new", NotificationManager.IMPORTANCE_LOW);
 
         @Override
         View inflate(ViewGroup parent) {
@@ -1098,6 +1099,7 @@
                             public void accept(ArrayList<Parcelable> result) {
                                 if (result == null || result.size() == 0) {
                                     status = FAIL;
+                                    logFail();
                                     return;
                                 }
                                 boolean pass = false;
@@ -1110,6 +1112,7 @@
                                             mContext, OTHER_PKG, channel.getId());
                                     status = RETEST;
                                 }
+                                delay();
                             }
                         });
             } else if (status == RETEST) {
@@ -1120,6 +1123,7 @@
                                 if (result == null || result.size() <= 1) {
                                     status = PASS;
                                 } else {
+                                    logFail();
                                     status = FAIL;
                                 }
                                 next();
@@ -1139,7 +1143,7 @@
     }
 
     private class UpdateChannelTest extends InteractiveTestCase {
-        private String id = "channelToUpdate";
+        private String id = UUID.randomUUID().toString();
         private NotificationChannel channel = new NotificationChannel(id, "new",
                 NotificationManager.IMPORTANCE_LOW);
         private NotificationChannel updatedChannel = new NotificationChannel(id, "new",
@@ -1241,7 +1245,7 @@
             }
             sendNotifications(channel);
             status = READY;
-            delay();
+            delay(6000);
         }
 
         @Override
@@ -1283,6 +1287,10 @@
         @Override
         void tearDown() {
             MockAssistant.deleteChannel(mContext, THIS_PKG, channel.getId());
+            mNm.cancel(mTag1, mId1);
+            mNm.cancel(mTag2, mId2);
+            mNm.cancel(mTag2, mId3);
+            MockAssistant.resetListenerData(mContext);
             delay();
         }
     }
@@ -1378,6 +1386,83 @@
         }
     }
 
+    private class UnsnoozeNotificationTest extends InteractiveTestCase {
+        final static int READY_TO_SNOOZE = 0;
+        final static int SNOOZED = 1;
+        final static int READY_TO_UNSNOOZE = 2;
+        final static int UNSNOOZED = 3;
+        int state = -1;
+        @Override
+        View inflate(ViewGroup parent) {
+            return createAutoItem(parent, R.string.nls_unsnooze_one);
+        }
+
+        @Override
+        void setUp() {
+            sendNotifications();
+            status = READY;
+            delay();
+        }
+
+        @Override
+        void test() {
+            status = RETEST;
+
+            if (state == READY_TO_SNOOZE) {
+                MockAssistant.snoozeUntilContext(mContext, mTag1, "hello");
+                state = SNOOZED;
+            } else if (state == SNOOZED) {
+                MockAssistant.probeListenerRemovedWithReason(mContext,
+                        new MockAssistant.BundleListResultCatcher() {
+                            @Override
+                            public void accept(ArrayList<Parcelable> result) {
+                                if (result == null || result.size() == 0) {
+                                    status = FAIL;
+                                    return;
+                                }
+                                boolean pass = true;
+                                for (Parcelable payloadData : result) {
+                                    Bundle payload = (Bundle) payloadData;
+                                    pass &= checkEquals(mTag1,
+                                            payload.getString(KEY_TAG),
+                                            "data dismissal test: notification tag (%s, %s)");
+                                    pass &= checkEquals(MockAssistant.REASON_SNOOZED,
+                                            payload.getInt(KEY_REASON),
+                                            "data dismissal test: reason (%d, %d)");
+                                }
+                                status = pass ? PASS : FAIL;
+                                next();
+                            }
+                        });
+            } else if (state == READY_TO_UNSNOOZE) {
+                MockAssistant.unsnoozeOne(mContext, mTag1);
+                state = UNSNOOZED;
+            } else {
+                MockAssistant.probeListenerPosted(mContext,
+                        new MockAssistant.StringListResultCatcher() {
+                            @Override
+                            public void accept(List<String> result) {
+                                if (result != null && result.size() != 0
+                                        && result.contains(mTag1)) {
+                                    status = PASS;
+                                } else {
+                                    logFail();
+                                    status = FAIL;
+                                }
+                                next();
+                            }
+                        });
+            }
+        }
+
+        @Override
+        void tearDown() {
+            mNm.cancelAll();
+            MockAssistant.resetListenerData(mContext);
+            delay();
+        }
+    }
+
     private boolean compareChannels(NotificationChannel expected, NotificationChannel actual) {
         boolean pass = true;
         String msg = "Channel mismatch (%s, %s)";
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/notifications/NotificationListenerVerifierActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/notifications/NotificationListenerVerifierActivity.java
index fe23fe0..38fc5bd 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/notifications/NotificationListenerVerifierActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/notifications/NotificationListenerVerifierActivity.java
@@ -79,9 +79,8 @@
         tests.add(new DismissOneTest());
         tests.add(new DismissOneWithReasonTest());
         tests.add(new DismissAllTest());
-        tests.add(new SnoozeNotificationTest());
-        tests.add(new UnsnoozeNotificationTest());
         tests.add(new SnoozeNotificationForTimeTest());
+        tests.add(new GetSnoozedNotificationTest());
         tests.add(new EnableHintsTest());
         tests.add(new SnoozeTest());
         tests.add(new UnsnoozeTest());
@@ -666,143 +665,6 @@
         }
     }
 
-    private class SnoozeNotificationTest extends InteractiveTestCase {
-        @Override
-        View inflate(ViewGroup parent) {
-            return createAutoItem(parent, R.string.nls_snooze_one);
-        }
-
-        @Override
-        void setUp() {
-            sendNotifications();
-            status = READY;
-            delay();
-        }
-
-        @Override
-        void test() {
-            if (status == READY) {
-                MockListener.snoozeOne(mContext, mTag1);
-                status = RETEST;
-            } else {
-                MockListener.probeListenerRemoved(mContext,
-                        new MockListener.StringListResultCatcher() {
-                            @Override
-                            public void accept(List<String> result) {
-                                if (result != null && result.size() != 0
-                                        && result.contains(mTag1)
-                                        && !result.contains(mTag2)
-                                        && !result.contains(mTag3)) {
-                                    status = PASS;
-                                } else {
-                                    logFail();
-                                    status = FAIL;
-                                }
-                                next();
-                            }
-                        });
-            }
-            delay();
-        }
-
-        @Override
-        void tearDown() {
-            mNm.cancel(mTag1, mId1);
-            mNm.cancel(mTag2, mId2);
-            mNm.cancel(mTag2, mId3);
-            MockListener.resetListenerData(mContext);
-            delay();
-        }
-    }
-
-    private class UnsnoozeNotificationTest extends InteractiveTestCase {
-        final static int READY_TO_SNOOZE = 0;
-        final static int SNOOZED = 1;
-        final static int READY_TO_UNSNOOZE = 2;
-        final static int UNSNOOZED = 3;
-        int state = -1;
-        @Override
-        View inflate(ViewGroup parent) {
-            return createAutoItem(parent, R.string.nls_unsnooze_one);
-        }
-
-        @Override
-        void setUp() {
-            sendNotifications();
-            status = READY;
-            delay();
-        }
-
-        @Override
-        void test() {
-            status = RETEST;
-
-            if (state == READY_TO_SNOOZE) {
-                MockListener.snoozeOne(mContext, mTag1);
-                state = SNOOZED;
-            } else if (state == SNOOZED) {
-                MockListener.probeListenerRemovedWithReason(mContext,
-                        new StringListResultCatcher() {
-                            @Override
-                            public void accept(List<String> result) {
-                                if (result == null || result.size() == 0) {
-                                    status = FAIL;
-                                    return;
-                                }
-                                boolean pass = true;
-                                for (String payloadData : result) {
-                                    JSONObject payload = null;
-                                    try {
-                                        payload = new JSONObject(payloadData);
-                                        pass &= checkEquals(mTag1,
-                                                payload.getString(JSON_TAG),
-                                                "data dismissal test: notification tag (%s, %s)");
-                                        pass &= checkEquals(MockListener.REASON_SNOOZED,
-                                                payload.getInt(JSON_TAG),
-                                                "data dismissal test: reason (%d, %d)");
-                                    } catch (JSONException e) {
-                                        e.printStackTrace();
-                                    }
-                                }
-                                if (!pass) {
-                                    logFail();
-                                    status = FAIL;
-                                    next();
-                                    return;
-                                } else {
-                                    state = READY_TO_UNSNOOZE;
-                                }
-                            }
-                        });
-            } else if (state == READY_TO_UNSNOOZE) {
-                MockListener.unsnoozeOne(mContext, mTag1);
-                state = UNSNOOZED;
-            } else {
-                MockListener.probeListenerPosted(mContext,
-                        new MockListener.StringListResultCatcher() {
-                            @Override
-                            public void accept(List<String> result) {
-                                if (result != null && result.size() != 0
-                                        && result.contains(mTag1)) {
-                                    status = PASS;
-                                } else {
-                                    logFail();
-                                    status = FAIL;
-                                }
-                                next();
-                            }
-                        });
-            }
-        }
-
-        @Override
-        void tearDown() {
-            mNm.cancelAll();
-            MockListener.resetListenerData(mContext);
-            delay();
-        }
-    }
-
     private class SnoozeNotificationForTimeTest extends InteractiveTestCase {
         final static int READY_TO_SNOOZE = 0;
         final static int SNOOZED = 1;
@@ -889,4 +751,94 @@
             delay();
         }
     }
+
+    private class GetSnoozedNotificationTest extends InteractiveTestCase {
+        final static int READY_TO_SNOOZE = 0;
+        final static int SNOOZED = 1;
+        final static int READY_TO_CHECK_FOR_GET_SNOOZE = 2;
+        int state = -1;
+        long snoozeTime = 30000;
+        @Override
+        View inflate(ViewGroup parent) {
+            return createAutoItem(parent, R.string.nls_get_snoozed);
+        }
+
+        @Override
+        void setUp() {
+            sendNotifications();
+            status = READY;
+            state = READY_TO_SNOOZE;
+            delay();
+        }
+
+        @Override
+        void test() {
+            status = RETEST;
+            if (state == READY_TO_SNOOZE) {
+                MockListener.snoozeOneUntil(mContext, mTag1,
+                        System.currentTimeMillis() + snoozeTime);
+                MockListener.snoozeOneUntil(mContext, mTag2,
+                        System.currentTimeMillis() + snoozeTime);
+                state = SNOOZED;
+            } else if (state == SNOOZED){
+                MockListener.probeListenerRemovedWithReason(mContext,
+                        new StringListResultCatcher() {
+                            @Override
+                            public void accept(List<String> result) {
+                                if (result == null || result.size() == 0) {
+                                    status = FAIL;
+                                    return;
+                                }
+                                boolean pass = true;
+                                for (String payloadData : result) {
+                                    JSONObject payload = null;
+                                    try {
+                                        payload = new JSONObject(payloadData);
+                                        pass &= checkEquals(mTag1,
+                                                payload.getString(JSON_TAG),
+                                                "data dismissal test: notification tag (%s, %s)");
+                                        pass &= checkEquals(MockListener.REASON_SNOOZED,
+                                                payload.getInt(JSON_TAG),
+                                                "data dismissal test: reason (%d, %d)");
+                                    } catch (JSONException e) {
+                                        e.printStackTrace();
+                                    }
+                                }
+                                if (!pass) {
+                                    logFail();
+                                    status = FAIL;
+                                    next();
+                                    return;
+                                } else {
+                                    state = READY_TO_CHECK_FOR_GET_SNOOZE;
+                                }
+                            }
+                        });
+            } else {
+                MockListener.probeListenerSnoozed(mContext,
+                        new MockListener.StringListResultCatcher() {
+                            @Override
+                            public void accept(List<String> result) {
+                                if (result != null && result.size() == 2
+                                        && result.contains(mTag1)
+                                        && result.contains(mTag2)) {
+                                    status = PASS;
+                                } else {
+                                    logFail();
+                                    status = FAIL;
+                                }
+                                next();
+                            }
+                        });
+            }
+            delay();
+        }
+
+        @Override
+        void tearDown() {
+            mNm.cancelAll();
+            MockListener.resetListenerData(mContext);
+            delay();
+        }
+    }
 }
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/voicemail/CallSettingsCheckActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/voicemail/CallSettingsCheckActivity.java
new file mode 100644
index 0000000..ac0f060
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/voicemail/CallSettingsCheckActivity.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.verifier.voicemail;
+
+import android.content.Intent;
+import android.os.Bundle;
+import android.telecom.TelecomManager;
+import android.view.View;
+import android.view.View.OnClickListener;
+
+import com.android.cts.verifier.PassFailButtons;
+import com.android.cts.verifier.R;
+
+/**
+ * Tests {@link android.telephony.TelephonyManager#METADATA_HIDE_VOICEMAIL_SETTINGS_MENU}
+ */
+public class CallSettingsCheckActivity extends PassFailButtons.Activity {
+
+    private DefaultDialerChanger mDefaultDialerChanger;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        View view = getLayoutInflater().inflate(R.layout.voicemail_hide_in_call_settings, null);
+        setContentView(view);
+        setInfoResources(R.string.call_settings_check_test,
+                R.string.call_settings_check_instructions, -1);
+        setPassFailButtonClickListeners();
+        getPassButton().setEnabled(false);
+
+        mDefaultDialerChanger = new DefaultDialerChanger(this);
+
+        findViewById(R.id.open_call_settings).setOnClickListener(
+                new OnClickListener() {
+                    @Override
+                    public void onClick(View v) {
+                        startActivity(new Intent(TelecomManager.ACTION_SHOW_CALL_SETTINGS));
+                    }
+                }
+        );
+
+        findViewById(R.id.settings_hidden).setOnClickListener(
+                new OnClickListener() {
+                    @Override
+                    public void onClick(View v) {
+                        getPassButton().setEnabled(true);
+                        setTestResultAndFinish(true);
+                    }
+                }
+        );
+
+        findViewById(R.id.settings_not_hidden).setOnClickListener(
+                new OnClickListener() {
+                    @Override
+                    public void onClick(View v) {
+                        setTestResultAndFinish(false);
+                    }
+                }
+        );
+
+    }
+
+    @Override
+    protected void onDestroy() {
+        mDefaultDialerChanger.destroy();
+        super.onDestroy();
+    }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/voicemail/VoicemailSettingsCheckActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/voicemail/VoicemailSettingsCheckActivity.java
new file mode 100644
index 0000000..d4ac0db
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/voicemail/VoicemailSettingsCheckActivity.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.verifier.voicemail;
+
+import android.content.Intent;
+import android.os.Bundle;
+import android.telephony.TelephonyManager;
+import android.view.View;
+import android.view.View.OnClickListener;
+
+import com.android.cts.verifier.PassFailButtons;
+import com.android.cts.verifier.R;
+
+/**
+ * Tests {@link TelephonyManager.EXTRA_HIDE_PUBLIC_SETTINGS}
+ */
+public class VoicemailSettingsCheckActivity extends PassFailButtons.Activity {
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        View view = getLayoutInflater().inflate(R.layout.voicemail_hide_ringtone_settings, null);
+        setContentView(view);
+        setInfoResources(R.string.ringtone_settings_check_test,
+                R.string.ringtone_settings_check_instructions, -1);
+        setPassFailButtonClickListeners();
+        getPassButton().setEnabled(false);
+
+        findViewById(R.id.open_voicemail_settings).setOnClickListener(
+                new OnClickListener() {
+                    @Override
+                    public void onClick(View v) {
+                        startActivity(new Intent(TelephonyManager.ACTION_CONFIGURE_VOICEMAIL)
+                                .putExtra(TelephonyManager.EXTRA_HIDE_PUBLIC_SETTINGS, true));
+                    }
+                }
+        );
+
+        findViewById(R.id.settings_hidden).setOnClickListener(
+                new OnClickListener() {
+                    @Override
+                    public void onClick(View v) {
+                        getPassButton().setEnabled(true);
+                        setTestResultAndFinish(true);
+                    }
+                }
+        );
+
+        findViewById(R.id.settings_not_hidden).setOnClickListener(
+                new OnClickListener() {
+                    @Override
+                    public void onClick(View v) {
+                        setTestResultAndFinish(false);
+                    }
+                }
+        );
+
+    }
+}
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/CtsMouseUtil.java b/common/device-side/util/src/com/android/compatibility/common/util/CtsMouseUtil.java
new file mode 100644
index 0000000..4bd48bd
--- /dev/null
+++ b/common/device-side/util/src/com/android/compatibility/common/util/CtsMouseUtil.java
@@ -0,0 +1,128 @@
+/*
+ * 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.compatibility.common.util;
+
+import static org.mockito.Matchers.argThat;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.inOrder;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+
+import android.os.SystemClock;
+import android.view.InputDevice;
+import android.view.MotionEvent;
+import android.view.View;
+
+import org.hamcrest.Description;
+import org.mockito.ArgumentMatcher;
+import org.mockito.InOrder;
+
+public final class CtsMouseUtil {
+
+    private CtsMouseUtil() {}
+
+    public static View.OnHoverListener installHoverListener(View view) {
+        return installHoverListener(view, true);
+    }
+
+    public static View.OnHoverListener installHoverListener(View view, boolean result) {
+        final View.OnHoverListener mockListener = mock(View.OnHoverListener.class);
+        view.setOnHoverListener((v, event) -> {
+            // Clone the event to work around event instance reuse in the framework.
+            mockListener.onHover(v, MotionEvent.obtain(event));
+            return result;
+        });
+        return mockListener;
+    }
+
+    public static void clearHoverListener(View view) {
+        view.setOnHoverListener(null);
+    }
+
+    public static MotionEvent obtainMouseEvent(int action, View anchor, int offsetX, int offsetY) {
+        final long eventTime = SystemClock.uptimeMillis();
+        final int[] screenPos = new int[2];
+        anchor.getLocationOnScreen(screenPos);
+        final int x = screenPos[0] + offsetX;
+        final int y = screenPos[1] + offsetY;
+        MotionEvent event = MotionEvent.obtain(eventTime, eventTime, action, x, y, 0);
+        event.setSource(InputDevice.SOURCE_MOUSE);
+        return event;
+    }
+
+    public static class ActionMatcher extends ArgumentMatcher<MotionEvent> {
+        private final int mAction;
+
+        public ActionMatcher(int action) {
+            mAction = action;
+        }
+
+        public boolean matches(Object actual) {
+            return (actual instanceof MotionEvent) && ((MotionEvent) actual).getAction() == mAction;
+        }
+
+        public void describeTo(Description description) {
+            description.appendText("action=" + MotionEvent.actionToString(mAction));
+        }
+    }
+
+    public static class PositionMatcher extends ActionMatcher {
+        private final int mX;
+        private final int mY;
+
+        public PositionMatcher(int action, int x, int y) {
+            super(action);
+            mX = x;
+            mY = y;
+        }
+
+        public boolean matches(Object actual) {
+            return super.matches(actual)
+                    && ((int) ((MotionEvent) actual).getX()) == mX
+                    && ((int) ((MotionEvent) actual).getY()) == mY;
+        }
+
+        public void describeTo(Description description) {
+            super.describeTo(description);
+            description.appendText("@(" + mX + "," + mY + ")");
+        }
+    }
+
+    public static void verifyEnterMove(View.OnHoverListener listener, View view, int moveCount) {
+        final InOrder inOrder = inOrder(listener);
+        verifyEnterMoveInternal(listener, view, moveCount, inOrder);
+        inOrder.verifyNoMoreInteractions();
+    }
+
+    public static void verifyEnterMoveExit(
+            View.OnHoverListener listener, View view, int moveCount) {
+        final InOrder inOrder = inOrder(listener);
+        verifyEnterMoveInternal(listener, view, moveCount, inOrder);
+        inOrder.verify(listener, times(1)).onHover(eq(view),
+                argThat(new ActionMatcher(MotionEvent.ACTION_HOVER_EXIT)));
+        inOrder.verifyNoMoreInteractions();
+    }
+
+    private static void verifyEnterMoveInternal(
+            View.OnHoverListener listener, View view, int moveCount, InOrder inOrder) {
+        inOrder.verify(listener, times(1)).onHover(eq(view),
+                argThat(new ActionMatcher(MotionEvent.ACTION_HOVER_ENTER)));
+        inOrder.verify(listener, times(moveCount)).onHover(eq(view),
+                argThat(new ActionMatcher(MotionEvent.ACTION_HOVER_MOVE)));
+    }
+}
+
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/devicepolicy/provisioning/SilentProvisioningTestManager.java b/common/device-side/util/src/com/android/compatibility/common/util/devicepolicy/provisioning/SilentProvisioningTestManager.java
index 5b83357..c63b54a 100644
--- a/common/device-side/util/src/com/android/compatibility/common/util/devicepolicy/provisioning/SilentProvisioningTestManager.java
+++ b/common/device-side/util/src/com/android/compatibility/common/util/devicepolicy/provisioning/SilentProvisioningTestManager.java
@@ -51,9 +51,18 @@
     };
 
     private final Context mContext;
+    private final BlockingReceiver mManagedProfileProvisionedReceiver;
+    private final BlockingReceiver mManagedProfileAddedReceiver;
 
     public SilentProvisioningTestManager(Context context) {
         mContext = context.getApplicationContext();
+        mManagedProfileProvisionedReceiver =
+                new BlockingReceiver(mContext, ACTION_MANAGED_PROFILE_PROVISIONED);
+        mManagedProfileAddedReceiver = new BlockingReceiver(mContext, ACTION_MANAGED_PROFILE_ADDED);
+    }
+
+    public Intent getReceviedProfileProvisionedIntent() {
+        return mManagedProfileProvisionedReceiver.getReceivedIntent();
     }
 
     public boolean startProvisioningAndWait(Intent provisioningIntent) throws InterruptedException {
@@ -73,23 +82,19 @@
     }
 
     private boolean waitManagedProfileProvisioning() throws InterruptedException {
-        BlockingReceiver managedProfileProvisionedReceiver = new BlockingReceiver(mContext,
-                ACTION_MANAGED_PROFILE_PROVISIONED);
-        managedProfileProvisionedReceiver.register();
-        BlockingReceiver managedProfileAddedReceiver = new BlockingReceiver(mContext,
-                ACTION_MANAGED_PROFILE_ADDED);
-        managedProfileAddedReceiver.register();
+        mManagedProfileProvisionedReceiver.register();
+        mManagedProfileAddedReceiver.register();
 
         if (!pollProvisioningResult()) {
             return false;
         }
 
-        if (!managedProfileProvisionedReceiver.await()) {
+        if (!mManagedProfileProvisionedReceiver.await()) {
             Log.i(TAG, "mManagedProfileProvisionedReceiver.await(): false");
             return false;
         }
 
-        if (!managedProfileAddedReceiver.await()) {
+        if (!mManagedProfileAddedReceiver.await()) {
             Log.i(TAG, "mManagedProfileAddedReceiver.await(): false");
             return false;
         }
@@ -132,14 +137,15 @@
 
     private static class BlockingReceiver extends BroadcastReceiver {
 
-        private static final CountDownLatch mLatch = new CountDownLatch(1);
-
+        private final CountDownLatch mLatch = new CountDownLatch(1);
         private final Context mContext;
         private final String mAction;
+        private Intent mReceivedIntent;
 
         private BlockingReceiver(Context context, String action) {
             mContext = context;
             mAction = action;
+            mReceivedIntent = null;
         }
 
         public void register() {
@@ -150,8 +156,13 @@
             return mLatch.await(TIMEOUT_SECONDS, TimeUnit.SECONDS);
         }
 
+        public Intent getReceivedIntent() {
+            return mReceivedIntent;
+        }
+
         @Override
         public void onReceive(Context context, Intent intent) {
+            mReceivedIntent = intent;
             mLatch.countDown();
         }
     }
diff --git a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/build/CompatibilityBuildHelper.java b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/build/CompatibilityBuildHelper.java
index fea9ce8..255c9f5 100644
--- a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/build/CompatibilityBuildHelper.java
+++ b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/build/CompatibilityBuildHelper.java
@@ -15,6 +15,7 @@
  */
 package com.android.compatibility.common.tradefed.build;
 
+import com.android.compatibility.common.util.DynamicConfigHostSide;
 import com.android.tradefed.build.IBuildInfo;
 import com.android.tradefed.build.IFolderBuildInfo;
 
@@ -40,7 +41,6 @@
     private static final String SUITE_VERSION = "SUITE_VERSION";
     private static final String SUITE_PLAN = "SUITE_PLAN";
     private static final String START_TIME_MS = "START_TIME_MS";
-    private static final String CONFIG_PATH_PREFIX = "DYNAMIC_CONFIG_FILE:";
     private static final String DYNAMIC_CONFIG_OVERRIDE_URL = "DYNAMIC_CONFIG_OVERRIDE_URL";
     private static final String COMMAND_LINE_ARGS = "command_line_args";
     private static final String RETRY_COMMAND_LINE_ARGS = "retry_command_line_args";
@@ -104,7 +104,8 @@
     }
 
     public void addDynamicConfigFile(String moduleName, File configFile) {
-        mBuildInfo.addBuildAttribute(CONFIG_PATH_PREFIX + moduleName, configFile.getAbsolutePath());
+        mBuildInfo.addBuildAttribute(DynamicConfigHostSide.CONFIG_PATH_PREFIX + moduleName,
+                configFile.getAbsolutePath());
     }
 
     public void setModuleIds(String[] moduleIds) {
@@ -114,8 +115,8 @@
     public Map<String, File> getDynamicConfigFiles() {
         Map<String, File> configMap = new HashMap<>();
         for (String key : mBuildInfo.getBuildAttributes().keySet()) {
-            if (key.startsWith(CONFIG_PATH_PREFIX)) {
-                configMap.put(key.substring(CONFIG_PATH_PREFIX.length()),
+            if (key.startsWith(DynamicConfigHostSide.CONFIG_PATH_PREFIX)) {
+                configMap.put(key.substring(DynamicConfigHostSide.CONFIG_PATH_PREFIX.length()),
                         new File(mBuildInfo.getBuildAttributes().get(key)));
             }
         }
diff --git a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/targetprep/DynamicConfigPusher.java b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/targetprep/DynamicConfigPusher.java
index c1f2d4b..7ac61c9 100644
--- a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/targetprep/DynamicConfigPusher.java
+++ b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/targetprep/DynamicConfigPusher.java
@@ -50,6 +50,7 @@
     }
 
     private static final String LOG_TAG = DynamicConfigPusher.class.getSimpleName();
+    private static final String TMP_FOLDER_DYNAMIC_FILES = "dynamic-config-files";
 
     @Option(name = "cleanup", description = "Whether to remove config files from the test " +
             "target after test completion.")
@@ -137,11 +138,14 @@
                 break;
 
             case HOST:
-                File storageDir = new File(DynamicConfig.CONFIG_FOLDER_ON_HOST);
-                if (!storageDir.exists()) {
-                    storageDir.mkdir();
+                File storageDir = null;
+                try {
+                    storageDir = FileUtil.createTempDir(TMP_FOLDER_DYNAMIC_FILES);
+                } catch (IOException e) {
+                    throw new TargetSetupError("Fail to create a tmp folder for dynamic config "
+                            + "files", e, device.getDeviceDescriptor());
                 }
-                File hostDest = new File(DynamicConfig.CONFIG_FOLDER_ON_HOST + src.getName());
+                File hostDest = new File(storageDir, src.getName());
                 try {
                     FileUtil.copyFile(src, hostDest);
                 } catch (IOException e) {
@@ -149,7 +153,7 @@
                             src.getAbsolutePath(), hostDest.getAbsolutePath()), e,
                             device.getDeviceDescriptor());
                 }
-                mFilePushed = hostDest.getAbsolutePath();
+                mFilePushed = storageDir.getAbsolutePath();
                 buildHelper.addDynamicConfigFile(mModuleName, src);
                 break;
         }
@@ -169,9 +173,8 @@
                 }
                 break;
             case HOST:
-                if (mFilePushed != null) {
-                    FileUtil.deleteFile(new File(mFilePushed));
-                }
+                FileUtil.recursiveDelete(new File(mFilePushed));
+                break;
         }
     }
 }
diff --git a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/CompatibilityTest.java b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/CompatibilityTest.java
index f1e2b38..a10c3b5 100644
--- a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/CompatibilityTest.java
+++ b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/CompatibilityTest.java
@@ -55,6 +55,7 @@
 import com.android.tradefed.util.AbiFormatter;
 import com.android.tradefed.util.AbiUtils;
 import com.android.tradefed.util.ArrayUtil;
+import com.android.tradefed.util.StreamUtil;
 import com.android.tradefed.util.TimeUtil;
 import com.android.tradefed.util.xml.AbstractXmlParser.ParseException;
 
@@ -64,7 +65,6 @@
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.FileNotFoundException;
-import java.io.IOException;
 import java.io.InputStream;
 import java.io.PrintWriter;
 import java.util.ArrayList;
@@ -365,7 +365,12 @@
                     mLogcatOnFailure, mScreenshotOnFailure, mRebootOnFailure, mMaxLogcatBytes);
             int moduleCount = modules.size();
             if (moduleCount == 0) {
-                CLog.logAndDisplay(LogLevel.INFO, "No module to run.");
+                CLog.logAndDisplay(LogLevel.INFO, "No module to run on %s.",
+                        mDevice.getSerialNumber());
+                // Make sure we unlock other shards.
+                if (sPreparedLatch != null) {
+                    sPreparedLatch.countDown();
+                }
                 return;
             } else {
                 CLog.logAndDisplay(LogLevel.INFO, "Starting %d module%s on %s", moduleCount,
@@ -468,11 +473,7 @@
                     // was successful, and test execution should proceed to next module
                     ByteArrayOutputStream stack = new ByteArrayOutputStream();
                     due.printStackTrace(new PrintWriter(stack, true));
-                    try {
-                        stack.close();
-                    } catch (IOException ioe) {
-                        // won't happen on BAOS
-                    }
+                    StreamUtil.close(stack);
                     CLog.w("Ignored DeviceUnresponsiveException because recovery was successful, "
                             + "proceeding with next module. Stack trace: %s",
                             stack.toString());
@@ -740,7 +741,7 @@
                     mIncludeFilters.add(new TestFilter(mAbiName, module, mTestName).toString());
                 }
             } catch (FileNotFoundException e) {
-                e.printStackTrace();
+                throw new RuntimeException(e);
             }
         } else if (mTestName != null) {
             throw new IllegalArgumentException(
@@ -805,6 +806,7 @@
         mListCheckers = systemCheckers;
     }
 
+    @Override
     public void setCollectTestsOnly(boolean collectTestsOnly) {
         mCollectTestsOnly = collectTestsOnly;
     }
diff --git a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/IModuleDef.java b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/IModuleDef.java
index e6605bf..edb241a 100644
--- a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/IModuleDef.java
+++ b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/IModuleDef.java
@@ -66,8 +66,7 @@
     void setPreparerWhitelist(Set<String> preparerWhitelist);
 
     /**
-     * Push any necessary dynamic configuration, then run the module's precondition checks
-     * and setup tasks.
+     * Pushes dynamic configuration, then runs the module's precondition checks and setup tasks.
      * @param skipPrep whether preparation should be skipped
      * @param preconditionArgs arguments to set on precondition preparers for the module, taking
      * format arg-name:arg-value. If "arg-value" is unset, the value will default to "true".
diff --git a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/ModuleRepo.java b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/ModuleRepo.java
index 33627b2..09d1219 100644
--- a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/ModuleRepo.java
+++ b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/ModuleRepo.java
@@ -222,35 +222,31 @@
                         // skip this name/abi combination.
                         continue;
                     }
-                    {
-                        Map<String, List<String>> args = new HashMap<>();
-                        if (mModuleArgs.containsKey(name)) {
-                            args.putAll(mModuleArgs.get(name));
-                        }
-                        if (mModuleArgs.containsKey(id)) {
-                            args.putAll(mModuleArgs.get(id));
-                        }
-                        if (args != null && args.size() > 0) {
-                            for (Entry<String, List<String>> entry : args.entrySet()) {
-                                for (String value : entry.getValue()) {
-                                    // Collection-type options can be injected with multiple values
-                                    config.injectOptionValue(entry.getKey(), value);
-                                }
-                            }
+
+                    Map<String, List<String>> args = new HashMap<>();
+                    if (mModuleArgs.containsKey(name)) {
+                        args.putAll(mModuleArgs.get(name));
+                    }
+                    if (mModuleArgs.containsKey(id)) {
+                        args.putAll(mModuleArgs.get(id));
+                    }
+                    for (Entry<String, List<String>> entry : args.entrySet()) {
+                        for (String value : entry.getValue()) {
+                            // Collection-type options can be injected with multiple values
+                            config.injectOptionValue(entry.getKey(), value);
                         }
                     }
+
                     List<IRemoteTest> tests = config.getTests();
                     for (IRemoteTest test : tests) {
                         String className = test.getClass().getName();
-                        Map<String, List<String>> args = new HashMap<>();
+                        Map<String, List<String>> testArgsMap = new HashMap<>();
                         if (mTestArgs.containsKey(className)) {
-                            args.putAll(mTestArgs.get(className));
+                            testArgsMap.putAll(mTestArgs.get(className));
                         }
-                        if (args != null && args.size() > 0) {
-                            for (Entry<String, List<String>> entry : args.entrySet()) {
-                                for (String value : entry.getValue()) {
-                                    config.injectOptionValue(entry.getKey(), value);
-                                }
+                        for (Entry<String, List<String>> entry : testArgsMap.entrySet()) {
+                            for (String value : entry.getValue()) {
+                                config.injectOptionValue(entry.getKey(), value);
                             }
                         }
                         addFiltersToTest(test, abi, name);
@@ -490,6 +486,10 @@
         if (res.isEmpty()) {
             return null;
         }
+        if (shardIndex >= res.size()) {
+            // If we could not shard up to expectation
+            return null;
+        }
         return res.get(shardIndex);
     }
 
diff --git a/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/testtype/CompatibilityTestTest.java b/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/testtype/CompatibilityTestTest.java
index 415aa40..54307f8 100644
--- a/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/testtype/CompatibilityTestTest.java
+++ b/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/testtype/CompatibilityTestTest.java
@@ -287,7 +287,7 @@
             }
         }, 0);
         mTest.setDevice(mMockDevice);
-        EasyMock.expect(mMockDevice.getSerialNumber()).andReturn("FAKE_SERIAL");
+        EasyMock.expect(mMockDevice.getSerialNumber()).andReturn("FAKE_SERIAL").times(2);
         EasyMock.replay(mMockDevice, mMockListener);
         mTest.run(mMockListener);
         EasyMock.verify(mMockDevice, mMockListener);
diff --git a/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/testtype/ModuleRepoTest.java b/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/testtype/ModuleRepoTest.java
index 9793d8b..491255a 100644
--- a/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/testtype/ModuleRepoTest.java
+++ b/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/testtype/ModuleRepoTest.java
@@ -421,4 +421,20 @@
         assertEquals(mod3, res.get(0));
         assertEquals(mod4, res.get(1));
     }
+
+    /**
+     * When reaching splitting time, we need to ensure that even after best effort, if we cannot
+     * split into the requested number of shardIndex, we simply return null to report an empty
+     * shard.
+     */
+    public void testGetShard_cannotSplitMore() {
+        List<IModuleDef> testList = new ArrayList<>();
+        TestRuntime test1 = new TestRuntime();
+        test1.runtimeHint = 100l;
+        IModuleDef mod1 = new ModuleDef("test1", new Abi("arm", "32"), test1,
+                new ArrayList<ITargetPreparer>());
+        testList.add(mod1);
+        List<IModuleDef> res = mRepo.getShard(testList, 1, 2);
+        assertNull(res);
+    }
 }
diff --git a/common/host-side/util/src/com/android/compatibility/common/util/DynamicConfigHandler.java b/common/host-side/util/src/com/android/compatibility/common/util/DynamicConfigHandler.java
index 351c08f..8df1ebc 100644
--- a/common/host-side/util/src/com/android/compatibility/common/util/DynamicConfigHandler.java
+++ b/common/host-side/util/src/com/android/compatibility/common/util/DynamicConfigHandler.java
@@ -16,6 +16,8 @@
 
 package com.android.compatibility.common.util;
 
+import com.android.tradefed.util.FileUtil;
+
 import org.json.JSONArray;
 import org.json.JSONException;
 import org.json.JSONObject;
@@ -35,8 +37,7 @@
 
 public class DynamicConfigHandler {
 
-    private static final String LOG_TAG = DynamicConfigHandler.class.getSimpleName();
-
+    private final static String MERGED_CONFIG_FILE_FOLDER = "dynamic-config-files-merged";
     private static final String NS = null; //xml constant representing null namespace
     private static final String ENCODING = "UTF-8";
 
@@ -75,9 +76,7 @@
     private static File storeMergedConfigFile(Map<String, List<String>> configMap,
             String moduleName) throws XmlPullParserException, IOException {
 
-        File folder = new File(DynamicConfig.MERGED_CONFIG_FILE_FOLDER);
-        folder.mkdirs();
-
+        File folder = FileUtil.createTempDir(MERGED_CONFIG_FILE_FOLDER);
         File mergedConfigFile = new File(folder, moduleName + ".dynamic");
         OutputStream stream = new FileOutputStream(mergedConfigFile);
         XmlSerializer serializer = XmlPullParserFactory.newInstance().newSerializer();
diff --git a/common/host-side/util/src/com/android/compatibility/common/util/DynamicConfigHostSide.java b/common/host-side/util/src/com/android/compatibility/common/util/DynamicConfigHostSide.java
index 3e94d36..fd823bf 100644
--- a/common/host-side/util/src/com/android/compatibility/common/util/DynamicConfigHostSide.java
+++ b/common/host-side/util/src/com/android/compatibility/common/util/DynamicConfigHostSide.java
@@ -16,20 +16,54 @@
 
 package com.android.compatibility.common.util;
 
+import com.android.tradefed.build.IBuildInfo;
+
 import org.xmlpull.v1.XmlPullParserException;
 
 import java.io.File;
 import java.io.IOException;
+import java.util.List;
+import java.util.Map;
 
 /**
  * Load dynamic config for device side test cases
  */
-public class DynamicConfigHostSide extends DynamicConfig {
+public class DynamicConfigHostSide {
 
-    private static String LOG_TAG = DynamicConfigHostSide.class.getSimpleName();
+    public static final String CONFIG_PATH_PREFIX = "DYNAMIC_CONFIG_FILE:";
 
-    public DynamicConfigHostSide(String moduleName) throws IOException, XmlPullParserException {
-        File configFile = getConfigFile(new File(CONFIG_FOLDER_ON_HOST), moduleName);
-        initializeConfig(configFile);
+    /**
+     * Returns the value of a key from a downloaded file.
+     *
+     * @param file The file downloaded, can be retrieve via
+     *        {@link #getDynamicConfigFile(IBuildInfo, String)}
+     * @param key the key inside the file which value we want to return
+     * @return the value associated to the key in the config file provided.
+     */
+    public static String getValueFromConfig(File file, String key)
+            throws XmlPullParserException, IOException {
+        Map<String, List<String>> configMap = DynamicConfig.createConfigMap(file);
+        List<String> singleValue = configMap.get(key);
+        if (singleValue == null || singleValue.size() == 0 || singleValue.size() > 1) {
+            // key must exist in the map, and map to a list containing exactly one string
+            return null;
+        }
+        return singleValue.get(0);
+    }
+
+    /**
+     * Returns a dynamic config file downloaded in {@link DynamicConfigPusher} the path is stored
+     * in the build info under a module name.
+     *
+     * @param info the invocation {@link IBuildInfo}
+     * @param moduleName the name of the module of the file.
+     * @return a {@link File} created from the downloaded file.
+     */
+    public static File getDynamicConfigFile(IBuildInfo info, String moduleName) {
+        String path = info.getBuildAttributes().get(CONFIG_PATH_PREFIX + moduleName);
+        if (path!= null && !path.isEmpty()) {
+            return new File(path);
+        }
+        return null;
     }
 }
diff --git a/common/host-side/util/tests/src/com/android/compatibility/common/util/DynamicConfigHandlerTest.java b/common/host-side/util/tests/src/com/android/compatibility/common/util/DynamicConfigHandlerTest.java
index a1d3d0a2..31dd598 100644
--- a/common/host-side/util/tests/src/com/android/compatibility/common/util/DynamicConfigHandlerTest.java
+++ b/common/host-side/util/tests/src/com/android/compatibility/common/util/DynamicConfigHandlerTest.java
@@ -95,8 +95,9 @@
     public void testDynamicConfigHandler() throws Exception {
         String module = "test1";
         File localConfigFile = createFileFromStr(localConfig, module);
+        File mergedFile = null;
         try {
-            File mergedFile = DynamicConfigHandler
+            mergedFile = DynamicConfigHandler
                     .getMergedDynamicConfigFile(localConfigFile, overrideJson, module);
 
             Map<String, List<String>> configMap = DynamicConfig.createConfigMap(mergedFile);
@@ -117,6 +118,7 @@
                     .contains("override-config-list-val-2-1"));
         } finally {
             FileUtil.deleteFile(localConfigFile);
+            FileUtil.recursiveDelete(mergedFile.getParentFile());
         }
     }
 
diff --git a/common/util/src/com/android/compatibility/common/util/DynamicConfig.java b/common/util/src/com/android/compatibility/common/util/DynamicConfig.java
index d316b9d..1efc5e1 100644
--- a/common/util/src/com/android/compatibility/common/util/DynamicConfig.java
+++ b/common/util/src/com/android/compatibility/common/util/DynamicConfig.java
@@ -42,12 +42,7 @@
     public static final String ENTRY_TAG = "entry";
     public static final String VALUE_TAG = "value";
     public static final String KEY_ATTR = "key";
-
     public final static String CONFIG_FOLDER_ON_DEVICE = "/sdcard/dynamic-config-files/";
-    public final static String CONFIG_FOLDER_ON_HOST =
-            System.getProperty("java.io.tmpdir") + "/dynamic-config-files/";
-    public final static String MERGED_CONFIG_FILE_FOLDER =
-            System.getProperty("java.io.tmpdir") + "/dynamic-config-files/merged";
 
     protected Map<String, List<String>> mDynamicConfigMap = new HashMap<String, List<String>>();
 
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/DirectBootHostTest.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/DirectBootHostTest.java
index 2180c96..f6e56a8 100644
--- a/hostsidetests/appsecurity/src/android/appsecurity/cts/DirectBootHostTest.java
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/DirectBootHostTest.java
@@ -217,9 +217,14 @@
         if (featureList.contains("feature:android.hardware.type.watch\n") ||
                 featureList.contains("feature:android.hardware.type.television\n")) {
             return false;
-        } else {
-            return true;
         }
+        final String passwordActivity =
+            getDevice().executeShellCommand("cmd package resolve-activity --components "
+                + "-a android.app.action.SET_NEW_PASSWORD -c android.intent.category.DEFAULT");
+        if (passwordActivity.equals("No activity found\n")) {
+          return false;
+        }
+        return true;
     }
 
     private void waitForBootCompleted() throws Exception {
diff --git a/hostsidetests/appsecurity/test-apps/DocumentClient/src/com/android/cts/documentclient/DocumentsClientTest.java b/hostsidetests/appsecurity/test-apps/DocumentClient/src/com/android/cts/documentclient/DocumentsClientTest.java
index bc8f904..03af2d2 100644
--- a/hostsidetests/appsecurity/test-apps/DocumentClient/src/com/android/cts/documentclient/DocumentsClientTest.java
+++ b/hostsidetests/appsecurity/test-apps/DocumentClient/src/com/android/cts/documentclient/DocumentsClientTest.java
@@ -548,12 +548,9 @@
         assertEquals("doc:web-linkable-file", DocumentsContract.getDocumentId(uri));
 
         final ContentResolver resolver = getInstrumentation().getContext().getContentResolver();
-        final String streamTypes[] = resolver.getStreamTypes(uri, "*/*");
-        assertEquals(1, streamTypes.length);
-        assertEquals("text/plain", streamTypes[0]);
 
         Bundle bundle = new Bundle();
-        bundle.putString(Intent.EXTRA_EMAIL, "x@x.com");
+        bundle.putStringArray(Intent.EXTRA_EMAIL, new String[] { "x@x.com" });
         final IntentSender intentSender = DocumentsContract.createWebLinkIntent(resolver,
                 uri, bundle);
 
@@ -564,7 +561,7 @@
 
         // Confirm the permissions dialog. The dialog is provided by the stub
         // provider.
-        UiObject okButton = new UiObject(new UiSelector().text("OK").clickable(true));
+        UiObject okButton = new UiObject(new UiSelector().resourceId("android:id/button1"));
         assertNotNull(okButton);
         assertTrue(okButton.waitForExists(TIMEOUT));
         okButton.click();
diff --git a/hostsidetests/devicepolicy/app/CorpOwnedManagedProfile/src/com/android/cts/comp/provisioning/ManagedProfileProvisioningTest.java b/hostsidetests/devicepolicy/app/CorpOwnedManagedProfile/src/com/android/cts/comp/provisioning/ManagedProfileProvisioningTest.java
index 553e446..cfba0a0 100644
--- a/hostsidetests/devicepolicy/app/CorpOwnedManagedProfile/src/com/android/cts/comp/provisioning/ManagedProfileProvisioningTest.java
+++ b/hostsidetests/devicepolicy/app/CorpOwnedManagedProfile/src/com/android/cts/comp/provisioning/ManagedProfileProvisioningTest.java
@@ -39,6 +39,7 @@
         SilentProvisioningTestManager provisioningManager =
                 new SilentProvisioningTestManager(getContext());
         assertTrue(provisioningManager.startProvisioningAndWait(intent));
+        assertTrue(isExtraUserPresent(provisioningManager.getReceviedProfileProvisionedIntent()));
     }
 
     // This is only necessary if the profile is created via managed provisioning flow.
@@ -47,4 +48,8 @@
                 getContext().getSystemService(Context.DEVICE_POLICY_SERVICE);
         dpm.setProfileEnabled(AdminReceiver.getComponentName(getContext()));
     }
+
+    private boolean isExtraUserPresent(Intent intent) {
+        return intent != null && intent.getExtras().containsKey(Intent.EXTRA_USER);
+    }
 }
diff --git a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/res/xml/device_admin.xml b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/res/xml/device_admin.xml
index 335eadd..c43856e 100644
--- a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/res/xml/device_admin.xml
+++ b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/res/xml/device_admin.xml
@@ -17,5 +17,6 @@
          <reset-password />
          <limit-password />
          <disable-keyguard-features />
+         <disable-camera />
     </uses-policies>
 </device-admin>
diff --git a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/PolicyTransparencyTest.java b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/PolicyTransparencyTest.java
new file mode 100644
index 0000000..f8582ea
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/PolicyTransparencyTest.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.cts.deviceandprofileowner;
+
+import android.app.admin.DevicePolicyManager;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.os.UserManager;
+import android.provider.Settings;
+import android.util.Log;
+
+/**
+ * Tests for {@link DevicePolicyManager#createAdminSupportIntent} API.
+ */
+public class PolicyTransparencyTest extends BaseDeviceAdminTest {
+
+    private static final String TAG = "PolicyTransparencyTest";
+
+    public void testCameraDisabled() throws Exception {
+        mDevicePolicyManager.setCameraDisabled(ADMIN_RECEIVER_COMPONENT, true);
+
+        Intent intent = mDevicePolicyManager.createAdminSupportIntent(
+                DevicePolicyManager.POLICY_DISABLE_CAMERA);
+        assertNotNull(intent);
+        assertEquals(ADMIN_RECEIVER_COMPONENT,
+                (ComponentName) intent.getParcelableExtra(DevicePolicyManager.EXTRA_DEVICE_ADMIN));
+        assertEquals(DevicePolicyManager.POLICY_DISABLE_CAMERA,
+                intent.getStringExtra(DevicePolicyManager.EXTRA_RESTRICTION));
+
+        mDevicePolicyManager.setCameraDisabled(ADMIN_RECEIVER_COMPONENT, false);
+        intent = mDevicePolicyManager.createAdminSupportIntent(
+                DevicePolicyManager.POLICY_DISABLE_CAMERA);
+        assertNull(intent);
+    }
+
+    public void testScreenCaptureDisabled() throws Exception {
+        mDevicePolicyManager.setScreenCaptureDisabled(ADMIN_RECEIVER_COMPONENT, true);
+
+        Intent intent = mDevicePolicyManager.createAdminSupportIntent(
+                DevicePolicyManager.POLICY_DISABLE_SCREEN_CAPTURE);
+        assertNotNull(intent);
+        assertEquals(ADMIN_RECEIVER_COMPONENT,
+                (ComponentName) intent.getParcelableExtra(DevicePolicyManager.EXTRA_DEVICE_ADMIN));
+        assertEquals(DevicePolicyManager.POLICY_DISABLE_SCREEN_CAPTURE,
+                intent.getStringExtra(DevicePolicyManager.EXTRA_RESTRICTION));
+
+        mDevicePolicyManager.setScreenCaptureDisabled(ADMIN_RECEIVER_COMPONENT, false);
+        intent = mDevicePolicyManager.createAdminSupportIntent(
+                DevicePolicyManager.POLICY_DISABLE_SCREEN_CAPTURE);
+        assertNull(intent);
+    }
+
+    public void testUserRestrictions() throws Exception {
+        // Test with a few random user restrictions:
+        runTestForRestriction(UserManager.DISALLOW_ADJUST_VOLUME);
+        runTestForRestriction(UserManager.DISALLOW_CONFIG_WIFI);
+        runTestForRestriction(UserManager.DISALLOW_CROSS_PROFILE_COPY_PASTE);
+    }
+
+    private void runTestForRestriction(String restriction) throws Exception {
+        mDevicePolicyManager.addUserRestriction(ADMIN_RECEIVER_COMPONENT, restriction);
+
+        Intent intent = mDevicePolicyManager.createAdminSupportIntent(restriction);
+        assertNotNull(intent);
+        assertEquals(ADMIN_RECEIVER_COMPONENT,
+                (ComponentName) intent.getParcelableExtra(DevicePolicyManager.EXTRA_DEVICE_ADMIN));
+        assertEquals(restriction, intent.getStringExtra(DevicePolicyManager.EXTRA_RESTRICTION));
+
+        mDevicePolicyManager.clearUserRestriction(ADMIN_RECEIVER_COMPONENT, restriction);
+        intent = mDevicePolicyManager.createAdminSupportIntent(restriction);
+        assertNull(intent);
+    }
+}
+
diff --git a/hostsidetests/devicepolicy/app/LauncherTests/src/com/android/cts/launchertests/LauncherAppsTests.java b/hostsidetests/devicepolicy/app/LauncherTests/src/com/android/cts/launchertests/LauncherAppsTests.java
index d39f96b..e4af64c 100644
--- a/hostsidetests/devicepolicy/app/LauncherTests/src/com/android/cts/launchertests/LauncherAppsTests.java
+++ b/hostsidetests/devicepolicy/app/LauncherTests/src/com/android/cts/launchertests/LauncherAppsTests.java
@@ -129,36 +129,6 @@
         assertEquals(mUser, UserHandle.getUserHandleForUid(ai.uid));
     }
 
-    public void testAccessPrimaryProfileFromManagedProfile() throws Exception {
-        // Try to access main profile from managed profile, which is not allowed.
-        try {
-            mLauncherApps.getActivityList(null, mUser);
-            fail("Didn't throw SecurityException");
-        } catch (SecurityException e) {
-            assertTrue(e.getMessage().contains("another profile"));
-        }
-        try {
-            mLauncherApps.getApplicationInfo(SIMPLE_APP_PACKAGE, /* flags= */ 0, mUser);
-            fail("Didn't throw SecurityException");
-        } catch (SecurityException e) {
-            assertTrue(e.getMessage().contains("another profile"));
-        }
-    }
-
-    public void testGetProfiles_fromMainProfile() {
-        final List<UserHandle> profiles = mLauncherApps.getProfiles();
-        assertEquals(2, profiles.size());
-        assertTrue(profiles.contains(android.os.Process.myUserHandle()));
-        assertEquals(getContext().getSystemService(UserManager.class).getUserProfiles(),
-                profiles);
-    }
-
-    public void testGetProfiles_fromManagedProfile() {
-        final List<UserHandle> profiles = mLauncherApps.getProfiles();
-        assertEquals(1, profiles.size());
-        assertEquals(android.os.Process.myUserHandle(), profiles.get(0));
-    }
-
     public void testPackageAddedCallbackForUser() throws Throwable {
         int result = sendMessageToCallbacksService(MSG_CHECK_PACKAGE_ADDED,
                 mUser, SIMPLE_APP_PACKAGE);
diff --git a/hostsidetests/devicepolicy/app/ManagedProfile/AndroidManifest.xml b/hostsidetests/devicepolicy/app/ManagedProfile/AndroidManifest.xml
index cbb637a..b8a5eae6 100644
--- a/hostsidetests/devicepolicy/app/ManagedProfile/AndroidManifest.xml
+++ b/hostsidetests/devicepolicy/app/ManagedProfile/AndroidManifest.xml
@@ -123,6 +123,15 @@
                        android:resource="@xml/authenticator" />
         </service>
         <activity android:name="com.android.compatibility.common.util.devicepolicy.provisioning.StartProvisioningActivity"/>
+
+        <activity
+                android:name=".ProvisioningSuccessActivity"
+                android:theme="@android:style/Theme.NoDisplay">
+            <intent-filter>
+                <action android:name="android.app.action.PROVISIONING_SUCCESSFUL"/>
+                <category android:name="android.intent.category.DEFAULT"/>
+            </intent-filter>
+        </activity>
     </application>
 
     <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
diff --git a/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/ProvisioningSuccessActivity.java b/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/ProvisioningSuccessActivity.java
new file mode 100644
index 0000000..cfce578
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/ProvisioningSuccessActivity.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.cts.managedprofile;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+public class ProvisioningSuccessActivity extends Activity {
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        ProvisioningTest.getSharedPreferences(this).edit()
+                .putBoolean(ProvisioningTest.KEY_PROVISIONING_SUCCESSFUL_RECEIVED, true).commit();
+        finish();
+    }
+}
diff --git a/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/ProvisioningTest.java b/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/ProvisioningTest.java
index b31851a..6f8da31 100644
--- a/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/ProvisioningTest.java
+++ b/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/ProvisioningTest.java
@@ -29,7 +29,6 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.SharedPreferences;
-import android.content.SharedPreferences.Editor;
 import android.os.PersistableBundle;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.SmallTest;
@@ -43,7 +42,8 @@
 public class ProvisioningTest {
     private static final String TAG = ProvisioningTest.class.getSimpleName();
 
-    private static final String ADMIN_EXTRAS_BUNDLE_FILENAME = "admin_extras_bundle.txt";
+    private static final String SHARED_PREFERENCE_FILE_NAME = "shared-preferences-file-name";
+
     private static final PersistableBundle ADMIN_EXTRAS_BUNDLE = new PersistableBundle();
     private static final String ADMIN_EXTRAS_BUNDLE_KEY_1 = "KEY_1";
     private static final String ADMIN_EXTRAS_BUNDLE_VALUE_1 = "VALUE_1";
@@ -51,6 +51,9 @@
         ADMIN_EXTRAS_BUNDLE.putString(ADMIN_EXTRAS_BUNDLE_KEY_1, ADMIN_EXTRAS_BUNDLE_VALUE_1);
     }
 
+    public static final String KEY_PROVISIONING_SUCCESSFUL_RECEIVED =
+            "key-provisioning-successful-received";
+
     private static final ComponentName ADMIN_RECEIVER_COMPONENT = new ComponentName(
             ProvisioningAdminReceiver.class.getPackage().getName(),
             ProvisioningAdminReceiver.class.getName());
@@ -95,6 +98,12 @@
         assertEquals(ADMIN_EXTRAS_BUNDLE_VALUE_1, bundle.getString(ADMIN_EXTRAS_BUNDLE_KEY_1));
     }
 
+    @Test
+    public void testVerifySuccessfulIntentWasReceived() {
+        assertTrue(getSharedPreferences(mContext).getBoolean(KEY_PROVISIONING_SUCCESSFUL_RECEIVED,
+                false));
+    }
+
     private void provisionManagedProfile() throws InterruptedException {
         Intent intent = new Intent(ACTION_PROVISION_MANAGED_PROFILE)
                 .putExtra(EXTRA_PROVISIONING_DEVICE_ADMIN_COMPONENT_NAME, ADMIN_RECEIVER_COMPONENT)
@@ -112,21 +121,21 @@
             return;
         }
 
-        getAdminExtraSharedPreferences(context).edit()
+        getSharedPreferences(context).edit()
                 .putString(ADMIN_EXTRAS_BUNDLE_KEY_1, bundle.getString(ADMIN_EXTRAS_BUNDLE_KEY_1))
                 .commit();
     }
 
     private static PersistableBundle loadBundle(Context context) {
-        SharedPreferences pref = getAdminExtraSharedPreferences(context);
+        SharedPreferences pref = getSharedPreferences(context);
         PersistableBundle bundle = new PersistableBundle();
         bundle.putString(ADMIN_EXTRAS_BUNDLE_KEY_1,
                 pref.getString(ADMIN_EXTRAS_BUNDLE_KEY_1, null));
         return bundle;
     }
 
-    private static SharedPreferences getAdminExtraSharedPreferences(Context context) {
-        return context.getSharedPreferences(ADMIN_EXTRAS_BUNDLE_FILENAME, 0);
+    public static SharedPreferences getSharedPreferences(Context context) {
+        return context.getSharedPreferences(SHARED_PREFERENCE_FILE_NAME, 0);
     }
 
 }
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/BaseLauncherAppsTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/BaseLauncherAppsTest.java
index ed885c5..51b9d30 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/BaseLauncherAppsTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/BaseLauncherAppsTest.java
@@ -30,10 +30,10 @@
     protected static final String LAUNCHER_TESTS_CLASS = LAUNCHER_TESTS_PKG + ".LauncherAppsTests";
     protected static final String PARAM_TEST_USER = "testUser";
 
-    protected static final String LAUNCHER_TESTS_APK = "CtsLauncherAppsTests.apk";
-    protected static final String LAUNCHER_TESTS_SUPPORT_PKG =
+    private static final String LAUNCHER_TESTS_APK = "CtsLauncherAppsTests.apk";
+    private static final String LAUNCHER_TESTS_SUPPORT_PKG =
             "com.android.cts.launchertests.support";
-    protected static final String LAUNCHER_TESTS_SUPPORT_APK = "CtsLauncherAppsTestsSupport.apk";
+    private static final String LAUNCHER_TESTS_SUPPORT_APK = "CtsLauncherAppsTestsSupport.apk";
 
     protected void installTestApps() throws Exception {
         uninstallTestApps();
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAndProfileOwnerTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAndProfileOwnerTest.java
index c29b294..d4fe1f7 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAndProfileOwnerTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAndProfileOwnerTest.java
@@ -635,6 +635,13 @@
         executeDeviceTestClass(".RequiredStrongAuthTimeoutTest");
     }
 
+    public void testCreateAdminSupportIntent() throws Exception {
+        if (!mHasFeature) {
+            return;
+        }
+        executeDeviceTestClass(".PolicyTransparencyTest");
+    }
+
     protected void executeDeviceTestClass(String className) throws Exception {
         runDeviceTestsAsUser(DEVICE_ADMIN_PKG, className, mUserId);
     }
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerPlusManagedProfileTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerPlusManagedProfileTest.java
index c37b2e2..fca45db 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerPlusManagedProfileTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerPlusManagedProfileTest.java
@@ -355,7 +355,9 @@
         addDisallowRemoveManagedProfileRestriction();
         // Now we can't delete the managed profile any more to create a new one.
         assertProvisionManagedProfileNotAllowed(COMP_DPC_PKG2);
-        assertProvisionManagedProfileNotAllowed(COMP_DPC_PKG);
+        // But if it is initiated by the device owner, it is still possible, because the device
+        // owner itself has set the restriction
+        assertProvisionManagedProfileAllowed(COMP_DPC_PKG);
     }
 
     private void assertProvisionManagedProfileAllowed(String packageName) throws Exception {
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/LauncherAppsProfileTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/LauncherAppsProfileTest.java
index a978ccf..6521ed8 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/LauncherAppsProfileTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/LauncherAppsProfileTest.java
@@ -43,6 +43,7 @@
         mHasFeature = mHasFeature && hasDeviceFeature("android.software.managed_users");
         if (mHasFeature) {
             removeTestUsers();
+            installTestApps();
             // Create a managed profile
             mParentUserId = mPrimaryUserId;
             mProfileUserId = createManagedProfile(mParentUserId);
@@ -52,13 +53,6 @@
             mProfileSerialNumber = Integer.toString(getUserSerialNumber(mProfileUserId));
             mMainUserSerialNumber = Integer.toString(getUserSerialNumber(mParentUserId));
             startUser(mProfileUserId);
-
-            // Install test APK.
-            installTestApps();
-
-            // Also install on the managed profile too.
-            installAppAsUser(LAUNCHER_TESTS_APK, mProfileUserId);
-            installAppAsUser(LAUNCHER_TESTS_SUPPORT_APK, mProfileUserId);
         }
     }
 
@@ -81,29 +75,12 @@
 
         // Run tests to check SimpleApp exists in both profile and main user.
         runDeviceTestsAsUser(LAUNCHER_TESTS_PKG,
-                LAUNCHER_TESTS_CLASS, "testSimpleAppInstalledForUser",
+                LAUNCHER_TESTS_CLASS,
+                "testSimpleAppInstalledForUser",
                 mParentUserId, Collections.singletonMap(PARAM_TEST_USER, mProfileSerialNumber));
         runDeviceTestsAsUser(LAUNCHER_TESTS_PKG,
-                LAUNCHER_TESTS_CLASS, "testSimpleAppInstalledForUser",
+                LAUNCHER_TESTS_PKG + ".LauncherAppsTests", "testSimpleAppInstalledForUser",
                 mParentUserId, Collections.singletonMap(PARAM_TEST_USER, mMainUserSerialNumber));
-
-        // Run the same test on the managed profile.  This still works.
-        runDeviceTestsAsUser(LAUNCHER_TESTS_PKG,
-                LAUNCHER_TESTS_CLASS, "testSimpleAppInstalledForUser",
-                mProfileUserId, Collections.singletonMap(PARAM_TEST_USER, mProfileSerialNumber));
-
-        // This tries to access main prorfile from work profiel, which is not allowed.
-        runDeviceTestsAsUser(LAUNCHER_TESTS_PKG,
-                LAUNCHER_TESTS_CLASS, "testAccessPrimaryProfileFromManagedProfile",
-                mProfileUserId, Collections.singletonMap(PARAM_TEST_USER, mMainUserSerialNumber));
-
-        // Test for getProfiles.
-        runDeviceTestsAsUser(LAUNCHER_TESTS_PKG,
-                LAUNCHER_TESTS_CLASS, "testGetProfiles_fromMainProfile",
-                mParentUserId);
-        runDeviceTestsAsUser(LAUNCHER_TESTS_PKG,
-                LAUNCHER_TESTS_CLASS, "testGetProfiles_fromManagedProfile",
-                mProfileUserId);
     }
 
     public void testLauncherCallbackPackageAddedProfile() throws Exception {
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileProvisioningTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileProvisioningTest.java
index 08a35d6..f11fe75 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileProvisioningTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileProvisioningTest.java
@@ -68,4 +68,13 @@
         runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".ProvisioningTest",
                 "testVerifyAdminExtraBundle", mProfileUserId);
     }
+
+    public void testVerifySuccessfulIntentWasReceived() throws Exception {
+        if (!mHasFeature) {
+            return;
+        }
+
+        runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".ProvisioningTest",
+                "testVerifySuccessfulIntentWasReceived", mProfileUserId);
+    }
 }
diff --git a/hostsidetests/dumpsys/src/android/dumpsys/cts/BaseDumpsysTest.java b/hostsidetests/dumpsys/src/android/dumpsys/cts/BaseDumpsysTest.java
new file mode 100644
index 0000000..aecb401
--- /dev/null
+++ b/hostsidetests/dumpsys/src/android/dumpsys/cts/BaseDumpsysTest.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.dumpsys.cts;
+
+import com.android.tradefed.build.IBuildInfo;
+import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.testtype.DeviceTestCase;
+import com.android.tradefed.testtype.IBuildReceiver;
+
+import java.util.Set;
+
+public class BaseDumpsysTest extends DeviceTestCase implements IBuildReceiver {
+    protected static final String TAG = "DumpsysHostTest";
+
+    /**
+     * A reference to the device under test.
+     */
+    protected ITestDevice mDevice;
+
+    protected IBuildInfo mCtsBuild;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mDevice = getDevice();
+    }
+
+    @Override
+    public void setBuild(IBuildInfo buildInfo) {
+        mCtsBuild = buildInfo;
+    }
+
+    protected static long assertInteger(String input) {
+        try {
+            return Long.parseLong(input);
+        } catch (NumberFormatException e) {
+            fail("Expected an integer but found \"" + input + "\"");
+            // Won't be hit, above throws AssertException
+            return -1;
+        }
+    }
+
+    protected static double assertDouble(String input) {
+        try {
+            return Double.parseDouble(input);
+        } catch (NumberFormatException e) {
+            fail("Expected a double but found \"" + input + "\"");
+            return -1;
+        }
+    }
+
+    protected static void assertSeenTag(Set<String> seenTags, String tag) {
+        assertTrue("No line starting with \"" + tag + ",\"", seenTags.contains(tag));
+    }
+}
diff --git a/hostsidetests/dumpsys/src/android/dumpsys/cts/DumpsysHostTest.java b/hostsidetests/dumpsys/src/android/dumpsys/cts/BatteryStatsDumpsysTesst.java
similarity index 67%
rename from hostsidetests/dumpsys/src/android/dumpsys/cts/DumpsysHostTest.java
rename to hostsidetests/dumpsys/src/android/dumpsys/cts/BatteryStatsDumpsysTesst.java
index acfc72d..cf06db2 100644
--- a/hostsidetests/dumpsys/src/android/dumpsys/cts/DumpsysHostTest.java
+++ b/hostsidetests/dumpsys/src/android/dumpsys/cts/BatteryStatsDumpsysTesst.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2014 The Android Open Source Project
+ * Copyright (C) 2017 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -17,10 +17,6 @@
 package android.dumpsys.cts;
 
 import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
-import com.android.tradefed.build.IBuildInfo;
-import com.android.tradefed.device.ITestDevice;
-import com.android.tradefed.testtype.DeviceTestCase;
-import com.android.tradefed.testtype.IBuildReceiver;
 
 import java.io.BufferedReader;
 import java.io.File;
@@ -30,315 +26,13 @@
 import java.util.Set;
 
 /**
- * Test to check the format of the dumps of various services.
- * Currently procstats and batterystats are tested.
+ * Test to check the format of the dumps of the batterystats test.
  */
-public class DumpsysHostTest extends DeviceTestCase implements IBuildReceiver {
-    private static final String TAG = "DumpsysHostTest";
-    private static final String TEST_APK = "CtsFramestatsTestApp.apk";
+public class BatteryStatsDumpsysTesst extends BaseDumpsysTest {
+   private static final String TEST_APK = "CtsFramestatsTestApp.apk";
     private static final String TEST_PKG = "com.android.cts.framestatstestapp";
 
     /**
-     * A reference to the device under test.
-     */
-    private ITestDevice mDevice;
-
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
-        mDevice = getDevice();
-    }
-
-    /**
-     * Tests the output of "dumpsys procstats -c". This is a proxy for testing "dumpsys procstats
-     * --checkin", since the latter is not idempotent.
-     *
-     * @throws Exception
-     */
-    public void testProcstatsOutput() throws Exception {
-        String procstats = mDevice.executeShellCommand("dumpsys procstats -c");
-        assertNotNull(procstats);
-        assertTrue(procstats.length() > 0);
-
-        Set<String> seenTags = new HashSet<>();
-        int version = -1;
-
-        try (BufferedReader reader = new BufferedReader(
-                new StringReader(procstats))) {
-
-            String line;
-            while ((line = reader.readLine()) != null) {
-                if (line.isEmpty()) {
-                    continue;
-                }
-
-                // extra space to make sure last column shows up.
-                if (line.endsWith(",")) {
-                  line = line + " ";
-                }
-                String[] parts = line.split(",");
-                seenTags.add(parts[0]);
-
-                switch (parts[0]) {
-                    case "vers":
-                        assertEquals(2, parts.length);
-                        version = Integer.parseInt(parts[1]);
-                        break;
-                    case "period":
-                        checkPeriod(parts);
-                        break;
-                    case "pkgproc":
-                        checkPkgProc(parts, version);
-                        break;
-                    case "pkgpss":
-                        checkPkgPss(parts, version);
-                        break;
-                    case "pkgsvc-bound":
-                    case "pkgsvc-exec":
-                    case "pkgsvc-run":
-                    case "pkgsvc-start":
-                        checkPkgSvc(parts, version);
-                        break;
-                    case "pkgkills":
-                        checkPkgKills(parts, version);
-                        break;
-                    case "proc":
-                        checkProc(parts);
-                        break;
-                    case "pss":
-                        checkPss(parts);
-                        break;
-                    case "kills":
-                        checkKills(parts);
-                        break;
-                    case "total":
-                        checkTotal(parts);
-                        break;
-                    default:
-                        break;
-                }
-            }
-        }
-
-        // spot check a few tags
-        assertSeenTag(seenTags, "pkgproc");
-        assertSeenTag(seenTags, "proc");
-        assertSeenTag(seenTags, "pss");
-        assertSeenTag(seenTags, "total");
-    }
-
-    private void checkPeriod(String[] parts) {
-        assertTrue("Expected 5 or 6, found: " + parts.length,
-                parts.length == 5 || parts.length == 6);
-        assertNotNull(parts[1]); // date
-        assertInteger(parts[2]); // start time (msec)
-        assertInteger(parts[3]); // end time (msec)
-        assertNotNull(parts[4]); // status
-        if (parts.length == 6) {
-            assertNotNull(parts[5]); // swapped-out-pss
-        }
-    }
-
-    private void checkPkgProc(String[] parts, int version) {
-        int statesStartIndex;
-
-        if (version < 4) {
-            assertTrue(parts.length >= 4);
-            assertNotNull(parts[1]); // package name
-            assertInteger(parts[2]); // uid
-            assertNotNull(parts[3]); // process
-            statesStartIndex = 4;
-        } else {
-            assertTrue(parts.length >= 5);
-            assertNotNull(parts[1]); // package name
-            assertInteger(parts[2]); // uid
-            assertInteger(parts[3]); // app version
-            assertNotNull(parts[4]); // process
-            statesStartIndex = 5;
-        }
-
-        for (int i = statesStartIndex; i < parts.length; i++) {
-            String[] subparts = parts[i].split(":");
-            assertEquals(2, subparts.length);
-            checkTag(subparts[0], true); // tag
-            assertInteger(subparts[1]); // duration (msec)
-        }
-    }
-
-    private void checkTag(String tag, boolean hasProcess) {
-        assertEquals(hasProcess ? 3 : 2, tag.length());
-
-        // screen: 0 = off, 1 = on
-        char s = tag.charAt(0);
-        if (s != '0' && s != '1') {
-            fail("malformed tag: " + tag);
-        }
-
-        // memory: n = normal, m = moderate, l = low, c = critical
-        char m = tag.charAt(1);
-        if (m != 'n' && m != 'm' && m != 'l' && m != 'c') {
-            fail("malformed tag: " + tag);
-        }
-
-        if (hasProcess) {
-            char p = tag.charAt(2);
-            assertTrue("malformed tag: " + tag, p >= 'a' && p <= 'z');
-        }
-    }
-
-    private void checkPkgPss(String[] parts, int version) {
-        int statesStartIndex;
-
-        if (version < 4) {
-            assertTrue(parts.length >= 4);
-            assertNotNull(parts[1]); // package name
-            assertInteger(parts[2]); // uid
-            assertNotNull(parts[3]); // process
-            statesStartIndex = 4;
-        } else {
-            assertTrue(parts.length >= 5);
-            assertNotNull(parts[1]); // package name
-            assertInteger(parts[2]); // uid
-            assertInteger(parts[3]); // app version
-            assertNotNull(parts[4]); // process
-            statesStartIndex = 5;
-        }
-
-        for (int i = statesStartIndex; i < parts.length; i++) {
-            String[] subparts = parts[i].split(":");
-            assertEquals(8, subparts.length);
-            checkTag(subparts[0], true); // tag
-            assertInteger(subparts[1]); // sample size
-            assertInteger(subparts[2]); // pss min
-            assertInteger(subparts[3]); // pss avg
-            assertInteger(subparts[4]); // pss max
-            assertInteger(subparts[5]); // uss min
-            assertInteger(subparts[6]); // uss avg
-            assertInteger(subparts[7]); // uss max
-        }
-    }
-
-    private void checkPkgSvc(String[] parts, int version) {
-        int statesStartIndex;
-
-        if (version < 4) {
-            assertTrue(parts.length >= 5);
-            assertNotNull(parts[1]); // package name
-            assertInteger(parts[2]); // uid
-            assertNotNull(parts[3]); // service name
-            assertInteger(parts[4]); // count
-            statesStartIndex = 5;
-        } else {
-            assertTrue(parts.length >= 6);
-            assertNotNull(parts[1]); // package name
-            assertInteger(parts[2]); // uid
-            assertInteger(parts[3]); // app version
-            assertNotNull(parts[4]); // service name
-            assertInteger(parts[5]); // count
-            statesStartIndex = 6;
-        }
-
-        for (int i = statesStartIndex; i < parts.length; i++) {
-            String[] subparts = parts[i].split(":");
-            assertEquals(2, subparts.length);
-            checkTag(subparts[0], false); // tag
-            assertInteger(subparts[1]); // duration (msec)
-        }
-    }
-
-    private void checkPkgKills(String[] parts, int version) {
-        String pssStr;
-
-        if (version < 4) {
-            assertEquals(8, parts.length);
-            assertNotNull(parts[1]); // package name
-            assertInteger(parts[2]); // uid
-            assertNotNull(parts[3]); // process
-            assertInteger(parts[4]); // wakes
-            assertInteger(parts[5]); // cpu
-            assertInteger(parts[6]); // cached
-            pssStr = parts[7];
-        } else {
-            assertEquals(9, parts.length);
-            assertNotNull(parts[1]); // package name
-            assertInteger(parts[2]); // uid
-            assertInteger(parts[3]); // app version
-            assertNotNull(parts[4]); // process
-            assertInteger(parts[5]); // wakes
-            assertInteger(parts[6]); // cpu
-            assertInteger(parts[7]); // cached
-            pssStr = parts[8];
-        }
-
-        String[] subparts = pssStr.split(":");
-        assertEquals(3, subparts.length);
-        assertInteger(subparts[0]); // pss min
-        assertInteger(subparts[1]); // pss avg
-        assertInteger(subparts[2]); // pss max
-    }
-
-    private void checkProc(String[] parts) {
-        assertTrue(parts.length >= 3);
-        assertNotNull(parts[1]); // package name
-        assertInteger(parts[2]); // uid
-
-        for (int i = 3; i < parts.length; i++) {
-            String[] subparts = parts[i].split(":");
-            assertEquals(2, subparts.length);
-            checkTag(subparts[0], true); // tag
-            assertInteger(subparts[1]); // duration (msec)
-        }
-    }
-
-    private void checkPss(String[] parts) {
-        assertTrue(parts.length >= 3);
-        assertNotNull(parts[1]); // package name
-        assertInteger(parts[2]); // uid
-
-        for (int i = 3; i < parts.length; i++) {
-            String[] subparts = parts[i].split(":");
-            assertEquals(8, subparts.length);
-            checkTag(subparts[0], true); // tag
-            assertInteger(subparts[1]); // sample size
-            assertInteger(subparts[2]); // pss min
-            assertInteger(subparts[3]); // pss avg
-            assertInteger(subparts[4]); // pss max
-            assertInteger(subparts[5]); // uss min
-            assertInteger(subparts[6]); // uss avg
-            assertInteger(subparts[7]); // uss max
-        }
-    }
-
-    private void checkKills(String[] parts) {
-        assertEquals(7, parts.length);
-        assertNotNull(parts[1]); // package name
-        assertInteger(parts[2]); // uid
-        assertInteger(parts[3]); // wakes
-        assertInteger(parts[4]); // cpu
-        assertInteger(parts[5]); // cached
-        String pssStr = parts[6];
-
-        String[] subparts = pssStr.split(":");
-        assertEquals(3, subparts.length);
-        assertInteger(subparts[0]); // pss min
-        assertInteger(subparts[1]); // pss avg
-        assertInteger(subparts[2]); // pss max
-    }
-
-    private void checkTotal(String[] parts) {
-        assertTrue(parts.length >= 2);
-        for (int i = 1; i < parts.length; i++) {
-            String[] subparts = parts[i].split(":");
-            checkTag(subparts[0], false); // tag
-
-            if (subparts[1].contains("sysmemusage")) {
-                break; // see b/18340771
-            }
-            assertInteger(subparts[1]); // duration (msec)
-        }
-    }
-
-    /**
      * Tests the output of "dumpsys batterystats --checkin".
      *
      * @throws Exception
@@ -948,37 +642,4 @@
         }
         assertTrue(foundAtLeastOneRow);
     }
-
-    private IBuildInfo mCtsBuild;
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public void setBuild(IBuildInfo buildInfo) {
-        mCtsBuild = buildInfo;
-    }
-
-    private static long assertInteger(String input) {
-        try {
-            return Long.parseLong(input);
-        } catch (NumberFormatException e) {
-            fail("Expected an integer but found \"" + input + "\"");
-            // Won't be hit, above throws AssertException
-            return -1;
-        }
-    }
-
-    private static double assertDouble(String input) {
-        try {
-            return Double.parseDouble(input);
-        } catch (NumberFormatException e) {
-            fail("Expected a double but found \"" + input + "\"");
-            return -1;
-        }
-    }
-
-    private static void assertSeenTag(Set<String> seenTags, String tag) {
-        assertTrue("No line starting with \"" + tag + ",\"", seenTags.contains(tag));
-    }
 }
diff --git a/hostsidetests/dumpsys/src/android/dumpsys/cts/ProcessStatsDumpsysTest.java b/hostsidetests/dumpsys/src/android/dumpsys/cts/ProcessStatsDumpsysTest.java
new file mode 100644
index 0000000..d78bf93
--- /dev/null
+++ b/hostsidetests/dumpsys/src/android/dumpsys/cts/ProcessStatsDumpsysTest.java
@@ -0,0 +1,329 @@
+/*
+ * 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.dumpsys.cts;
+
+import java.io.BufferedReader;
+import java.io.StringReader;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * Test to check the format of the dumps of the processstats test.
+ */
+public class ProcessStatsDumpsysTest extends BaseDumpsysTest {
+    /**
+     * Tests the output of "dumpsys procstats -c". This is a proxy for testing "dumpsys procstats
+     * --checkin", since the latter is not idempotent.
+     *
+     * @throws Exception
+     */
+    public void testProcstatsOutput() throws Exception {
+        String procstats = mDevice.executeShellCommand("dumpsys procstats -c");
+        assertNotNull(procstats);
+        assertTrue(procstats.length() > 0);
+
+        Set<String> seenTags = new HashSet<>();
+        int version = -1;
+
+        try (BufferedReader reader = new BufferedReader(
+                new StringReader(procstats))) {
+
+            String line;
+            while ((line = reader.readLine()) != null) {
+                if (line.isEmpty()) {
+                    continue;
+                }
+
+                // extra space to make sure last column shows up.
+                if (line.endsWith(",")) {
+                    line = line + " ";
+                }
+                String[] parts = line.split(",");
+                seenTags.add(parts[0]);
+
+                switch (parts[0]) {
+                    case "vers":
+                        assertEquals(2, parts.length);
+                        version = Integer.parseInt(parts[1]);
+                        break;
+                    case "period":
+                        checkPeriod(parts);
+                        break;
+                    case "pkgproc":
+                        checkPkgProc(parts, version);
+                        break;
+                    case "pkgpss":
+                        checkPkgPss(parts, version);
+                        break;
+                    case "pkgsvc-bound":
+                    case "pkgsvc-exec":
+                    case "pkgsvc-run":
+                    case "pkgsvc-start":
+                        checkPkgSvc(parts, version);
+                        break;
+                    case "pkgkills":
+                        checkPkgKills(parts, version);
+                        break;
+                    case "proc":
+                        checkProc(parts);
+                        break;
+                    case "pss":
+                        checkPss(parts);
+                        break;
+                    case "kills":
+                        checkKills(parts);
+                        break;
+                    case "total":
+                        checkTotal(parts);
+                        break;
+                    default:
+                        break;
+                }
+            }
+        }
+
+/* more kinds
+total   0n:4426040  0m:5779 1n:6067104  1m:74489
+sysmemusage 0np1791668:791668:791668:40080:40080:40080:1176:1176:1176:147068:147068:147068:230682:230682:230682 ...
+weights 10573412    6.133572860728E12:40  ...
+availablepages  Unmovable   0   69  37  31  14  20  28  16  5   7   0   0
+availablepages  Reclaimable 0   0   0   0   1   1   1   1   0   0   1   0
+availablepages  Movable 0   1   0   1   1   1   2   0   1   1   0   173
+availablepages  Reserve 0   0   0   0   0   0   0   0   0   0   0   2
+availablepages  CMA 0   1   1   98  111 70  35  20  7   9   3   18
+availablepages  Isolate
+ */
+
+        // spot check a few tags
+        assertSeenTag(seenTags, "pkgproc");
+        assertSeenTag(seenTags, "proc");
+        assertSeenTag(seenTags, "pss");
+        assertSeenTag(seenTags, "total");
+    }
+
+    private void checkPeriod(String[] parts) {
+        assertTrue("Expected 5 or 6, found: " + parts.length,
+                parts.length == 5 || parts.length == 6);
+        assertNotNull(parts[1]); // date
+        assertInteger(parts[2]); // start time (msec)
+        assertInteger(parts[3]); // end time (msec)
+        assertNotNull(parts[4]); // status
+        if (parts.length == 6) {
+            assertNotNull(parts[5]); // swapped-out-pss
+        }
+    }
+
+    private void checkPkgProc(String[] parts, int version) {
+        int statesStartIndex;
+
+        if (version < 4) {
+            assertTrue(parts.length >= 4);
+            assertNotNull(parts[1]); // package name
+            assertInteger(parts[2]); // uid
+            assertNotNull(parts[3]); // process
+            statesStartIndex = 4;
+        } else {
+            assertTrue(parts.length >= 5);
+            assertNotNull(parts[1]); // package name
+            assertInteger(parts[2]); // uid
+            assertInteger(parts[3]); // app version
+            assertNotNull(parts[4]); // process
+            statesStartIndex = 5;
+        }
+
+        for (int i = statesStartIndex; i < parts.length; i++) {
+            String[] subparts = parts[i].split(":");
+            assertEquals(2, subparts.length);
+            checkTag(subparts[0], true); // tag
+            assertInteger(subparts[1]); // duration (msec)
+        }
+    }
+
+    private void checkTag(String tag, boolean hasProcess) {
+        assertEquals(hasProcess ? 3 : 2, tag.length());
+
+        // screen: 0 = off, 1 = on
+        char s = tag.charAt(0);
+        if (s != '0' && s != '1') {
+            fail("malformed tag: " + tag);
+        }
+
+        // memory: n = normal, m = moderate, l = low, c = critical
+        char m = tag.charAt(1);
+        if (m != 'n' && m != 'm' && m != 'l' && m != 'c') {
+            fail("malformed tag: " + tag);
+        }
+
+        if (hasProcess) {
+            char p = tag.charAt(2);
+            assertTrue("malformed tag: " + tag, p >= 'a' && p <= 'z');
+        }
+    }
+
+    private void checkPkgPss(String[] parts, int version) {
+        int statesStartIndex;
+
+        if (version < 4) {
+            assertTrue(parts.length >= 4);
+            assertNotNull(parts[1]); // package name
+            assertInteger(parts[2]); // uid
+            assertNotNull(parts[3]); // process
+            statesStartIndex = 4;
+        } else {
+            assertTrue(parts.length >= 5);
+            assertNotNull(parts[1]); // package name
+            assertInteger(parts[2]); // uid
+            assertInteger(parts[3]); // app version
+            assertNotNull(parts[4]); // process
+            statesStartIndex = 5;
+        }
+
+        for (int i = statesStartIndex; i < parts.length; i++) {
+            String[] subparts = parts[i].split(":");
+            assertEquals(8, subparts.length);
+            checkTag(subparts[0], true); // tag
+            assertInteger(subparts[1]); // sample size
+            assertInteger(subparts[2]); // pss min
+            assertInteger(subparts[3]); // pss avg
+            assertInteger(subparts[4]); // pss max
+            assertInteger(subparts[5]); // uss min
+            assertInteger(subparts[6]); // uss avg
+            assertInteger(subparts[7]); // uss max
+        }
+    }
+
+    private void checkPkgSvc(String[] parts, int version) {
+        int statesStartIndex;
+
+        if (version < 4) {
+            assertTrue(parts.length >= 5);
+            assertNotNull(parts[1]); // package name
+            assertInteger(parts[2]); // uid
+            assertNotNull(parts[3]); // service name
+            assertInteger(parts[4]); // count
+            statesStartIndex = 5;
+        } else {
+            assertTrue(parts.length >= 6);
+            assertNotNull(parts[1]); // package name
+            assertInteger(parts[2]); // uid
+            assertInteger(parts[3]); // app version
+            assertNotNull(parts[4]); // service name
+            assertInteger(parts[5]); // count
+            statesStartIndex = 6;
+        }
+
+        for (int i = statesStartIndex; i < parts.length; i++) {
+            String[] subparts = parts[i].split(":");
+            assertEquals(2, subparts.length);
+            checkTag(subparts[0], false); // tag
+            assertInteger(subparts[1]); // duration (msec)
+        }
+    }
+
+    private void checkPkgKills(String[] parts, int version) {
+        String pssStr;
+
+        if (version < 4) {
+            assertEquals(8, parts.length);
+            assertNotNull(parts[1]); // package name
+            assertInteger(parts[2]); // uid
+            assertNotNull(parts[3]); // process
+            assertInteger(parts[4]); // wakes
+            assertInteger(parts[5]); // cpu
+            assertInteger(parts[6]); // cached
+            pssStr = parts[7];
+        } else {
+            assertEquals(9, parts.length);
+            assertNotNull(parts[1]); // package name
+            assertInteger(parts[2]); // uid
+            assertInteger(parts[3]); // app version
+            assertNotNull(parts[4]); // process
+            assertInteger(parts[5]); // wakes
+            assertInteger(parts[6]); // cpu
+            assertInteger(parts[7]); // cached
+            pssStr = parts[8];
+        }
+
+        String[] subparts = pssStr.split(":");
+        assertEquals(3, subparts.length);
+        assertInteger(subparts[0]); // pss min
+        assertInteger(subparts[1]); // pss avg
+        assertInteger(subparts[2]); // pss max
+    }
+
+    private void checkProc(String[] parts) {
+        assertTrue(parts.length >= 3);
+        assertNotNull(parts[1]); // package name
+        assertInteger(parts[2]); // uid
+
+        for (int i = 3; i < parts.length; i++) {
+            String[] subparts = parts[i].split(":");
+            assertEquals(2, subparts.length);
+            checkTag(subparts[0], true); // tag
+            assertInteger(subparts[1]); // duration (msec)
+        }
+    }
+
+    private void checkPss(String[] parts) {
+        assertTrue(parts.length >= 3);
+        assertNotNull(parts[1]); // package name
+        assertInteger(parts[2]); // uid
+
+        for (int i = 3; i < parts.length; i++) {
+            String[] subparts = parts[i].split(":");
+            assertEquals(8, subparts.length);
+            checkTag(subparts[0], true); // tag
+            assertInteger(subparts[1]); // sample size
+            assertInteger(subparts[2]); // pss min
+            assertInteger(subparts[3]); // pss avg
+            assertInteger(subparts[4]); // pss max
+            assertInteger(subparts[5]); // uss min
+            assertInteger(subparts[6]); // uss avg
+            assertInteger(subparts[7]); // uss max
+        }
+    }
+
+    private void checkKills(String[] parts) {
+        assertEquals(7, parts.length);
+        assertNotNull(parts[1]); // package name
+        assertInteger(parts[2]); // uid
+        assertInteger(parts[3]); // wakes
+        assertInteger(parts[4]); // cpu
+        assertInteger(parts[5]); // cached
+        String pssStr = parts[6];
+
+        String[] subparts = pssStr.split(":");
+        assertEquals(3, subparts.length);
+        assertInteger(subparts[0]); // pss min
+        assertInteger(subparts[1]); // pss avg
+        assertInteger(subparts[2]); // pss max
+    }
+
+    private void checkTotal(String[] parts) {
+        assertTrue(parts.length >= 2);
+        for (int i = 1; i < parts.length; i++) {
+            String[] subparts = parts[i].split(":");
+            checkTag(subparts[0], false); // tag
+
+            if (subparts[1].contains("sysmemusage")) {
+                break; // see b/18340771
+            }
+            assertInteger(subparts[1]); // duration (msec)
+        }
+    }
+
+}
diff --git a/hostsidetests/incident/src/com/android/server/cts/NetstatsIncidentTest.java b/hostsidetests/incident/src/com/android/server/cts/NetstatsIncidentTest.java
index 7ca4a5a..48fcca0 100644
--- a/hostsidetests/incident/src/com/android/server/cts/NetstatsIncidentTest.java
+++ b/hostsidetests/incident/src/com/android/server/cts/NetstatsIncidentTest.java
@@ -15,10 +15,22 @@
  */
 package com.android.server.cts;
 
+import android.service.NetworkIdentityProto;
+import android.service.NetworkInterfaceProto;
+import android.service.NetworkStatsCollectionKeyProto;
+import android.service.NetworkStatsCollectionStatsProto;
+import android.service.NetworkStatsHistoryBucketProto;
+import android.service.NetworkStatsHistoryProto;
+import android.service.NetworkStatsRecorderProto;
 import android.service.NetworkStatsServiceDumpProto;
 
+import java.util.List;
+
 /**
  * Test for "dumpsys netstats --proto"
+ *
+ * Note most of the logic here is just heuristics.
+ *
  * Usage:
 
   cts-tradefed run cts --skip-device-info --skip-preconditions \
@@ -56,6 +68,159 @@
         final NetworkStatsServiceDumpProto dump = getDump(NetworkStatsServiceDumpProto.parser(),
                 "dumpsys netstats --proto");
 
-        // TODO Actually check the output.
+        checkInterfaces(dump.getActiveInterfacesList());
+        checkInterfaces(dump.getActiveUidInterfacesList());
+
+        checkStats(dump.getDevStats(), /*withUid=*/ false, /*withTag=*/ false);
+        checkStats(dump.getXtStats(), /*withUid=*/ false, /*withTag=*/ false);
+        checkStats(dump.getUidStats(), /*withUid=*/ true, /*withTag=*/ false);
+        checkStats(dump.getUidTagStats(), /*withUid=*/ true, /*withTag=*/ true);
+    }
+
+    private void assertPositive(String name, long value) {
+        if (value > 0) return;
+        fail(name + " expected to be positive, but was: " + value);
+    }
+
+    private void assertNotNegative(String name, long value) {
+        if (value >= 0) return;
+        fail(name + " expected to be zero or positive, but was: " + value);
+    }
+
+    private void checkInterfaces(List<NetworkInterfaceProto> interfaces) {
+        /* Example:
+    active_interfaces=[
+      NetworkInterfaceProto {
+        interface=wlan0
+        identities=NetworkIdentitySetProto {
+          identities=[
+            NetworkIdentityProto {
+              type=1
+              subscriber_id=
+              network_id="wifiap"
+              roaming=false
+              metered=false
+            }
+          ]
+        }
+      }
+    ]
+         */
+        assertTrue("There must be at least one network device",
+                interfaces.size() > 0);
+
+        boolean allRoaming = true;
+        boolean allMetered = true;
+
+        for (NetworkInterfaceProto iface : interfaces) {
+            assertTrue("Missing interface name", !iface.getInterface().isEmpty());
+
+            assertPositive("# identities", iface.getIdentities().getIdentitiesList().size());
+
+            for (NetworkIdentityProto iden : iface.getIdentities().getIdentitiesList()) {
+                allRoaming &= iden.getRoaming();
+                allMetered &= iden.getMetered();
+
+                // TODO Can we check the other fields too?  type, subscriber_id, and network_id.
+            }
+        }
+        assertFalse("There must be at least one non-roaming interface during CTS", allRoaming);
+        assertFalse("There must be at least one non-metered interface during CTS", allMetered);
+    }
+
+    private void checkStats(NetworkStatsRecorderProto record, boolean withUid, boolean withTag) {
+        /*
+         * Example:
+    dev_stats=NetworkStatsRecorderProto {
+      pending_total_bytes=136
+      complete_history=NetworkStatsCollectionProto {
+        stats=[
+          NetworkStatsCollectionStatsProto {
+            key=NetworkStatsCollectionKeyProto {
+              identity=NetworkIdentitySetProto {
+                identities=[
+                  NetworkIdentityProto {
+                    type=1
+                    subscriber_id=
+                    network_id="wifiap"
+                    roaming=false
+                    metered=false
+                  }
+                ]
+              }
+              uid=-1
+              set=-1
+              tag=0
+            }
+            history=NetworkStatsHistoryProto {
+              bucket_duration_ms=3600000
+              buckets=[
+                NetworkStatsHistoryBucketProto {
+                  bucket_start_ms=2273694336
+                  rx_bytes=2142
+                  rx_packets=10
+                  tx_bytes=1568
+                  tx_packets=12
+                  operations=0
+                }
+                NetworkStatsHistoryBucketProto {
+                  bucket_start_ms=3196682880
+                  rx_bytes=2092039
+                  rx_packets=1987
+                  tx_bytes=236735
+                  tx_packets=1750
+                  operations=0
+                }
+         */
+
+        assertNotNegative("Pending bytes", record.getPendingTotalBytes());
+
+        for (NetworkStatsCollectionStatsProto stats : record.getCompleteHistory().getStatsList()) {
+
+            final NetworkStatsCollectionKeyProto key = stats.getKey();
+
+            // TODO Check the key.
+
+            final NetworkStatsHistoryProto hist = stats.getHistory();
+
+            assertPositive("duration", hist.getBucketDurationMs());
+
+            // Subtract one hour from duration to compensate for possible DTS.
+            final long minInterval = hist.getBucketDurationMs() - (60 * 60 * 1000);
+
+            NetworkStatsHistoryBucketProto prev = null;
+            for (NetworkStatsHistoryBucketProto bucket : hist.getBucketsList()) {
+
+                // Make sure the start time is increasing by at least the "duration",
+                // except we subtract duration from one our to compensate possible DTS.
+
+                if (prev != null) {
+                    assertTrue(
+                            String.format("Last start=%d, current start=%d, diff=%d, duration=%d",
+                                    prev.getBucketStartMs(), bucket.getBucketStartMs(),
+                                    (bucket.getBucketStartMs() - prev.getBucketStartMs()),
+                                    minInterval),
+                            (bucket.getBucketStartMs() - prev.getBucketStartMs()) >=
+                                    minInterval);
+                }
+                assertNotNegative("RX bytes", bucket.getRxBytes());
+                assertNotNegative("RX packets", bucket.getRxPackets());
+                assertNotNegative("TX bytes", bucket.getTxBytes());
+                assertNotNegative("TX packets", bucket.getTxPackets());
+
+                // It should be safe to say # of bytes >= 10 * 10 of packets, due to headers, etc...
+                final long FACTOR = 10;
+                assertTrue(
+                        String.format("# of bytes %d too small for # of packets %d",
+                                bucket.getRxBytes(), bucket.getRxPackets()),
+                        bucket.getRxBytes() >= bucket.getRxPackets() * FACTOR);
+                assertTrue(
+                        String.format("# of bytes %d too small for # of packets %d",
+                                bucket.getTxBytes(), bucket.getTxPackets()),
+                        bucket.getTxBytes() >= bucket.getTxPackets() * FACTOR);
+            }
+        }
+
+        // TODO Make sure test app's UID actually shows up.
     }
 }
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/app/AndroidManifest.xml b/hostsidetests/services/activityandwindowmanager/activitymanager/app/AndroidManifest.xml
index de32000..1c4050c 100755
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/app/AndroidManifest.xml
+++ b/hostsidetests/services/activityandwindowmanager/activitymanager/app/AndroidManifest.xml
@@ -65,7 +65,7 @@
                 android:taskAffinity="nobody.but.LaunchToSideActivity"
         />
         <activity android:name=".PipActivity"
-                android:resizeableActivity="true"
+                android:resizeableActivity="false"
                 android:supportsPictureInPicture="true"
                 android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout"
                 android:exported="true"
@@ -73,41 +73,41 @@
         />
         <activity android:name=".AlwaysFocusablePipActivity"
                   android:theme="@style/Theme.Transparent"
-                  android:resizeableActivity="true"
+                  android:resizeableActivity="false"
                   android:supportsPictureInPicture="true"
                   androidprv:alwaysFocusable="true"
                   android:exported="true"
                   android:taskAffinity="nobody.but.AlwaysFocusablePipActivity"
         />
         <activity android:name=".LaunchIntoPinnedStackPipActivity"
-                  android:resizeableActivity="true"
+                  android:resizeableActivity="false"
                   android:supportsPictureInPicture="true"
                   androidprv:alwaysFocusable="true"
                   android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout"
                   android:exported="true"
         />
         <activity android:name=".VisibleBehindActivity"
-                  android:resizeableActivity="true"
+                  android:resizeableActivity="false"
                   android:supportsPictureInPicture="true"
                   android:exported="true"
                   android:taskAffinity="nobody.but.VisibleBehindActivity"
         />
         <activity android:name=".LaunchPipOnPipActivity"
-                  android:resizeableActivity="true"
+                  android:resizeableActivity="false"
                   android:supportsPictureInPicture="true"
                   android:taskAffinity="nobody.but.LaunchPipOnPipActivity"
                   android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout"
                   android:exported="true"
         />
         <activity android:name=".LaunchTapToFinishPipActivity"
-                  android:resizeableActivity="true"
+                  android:resizeableActivity="false"
                   android:supportsPictureInPicture="true"
                   androidprv:alwaysFocusable="true"
                   android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout"
                   android:exported="true"
         />
         <activity android:name=".LaunchImeWithPipActivity"
-            android:resizeableActivity="true"
+            android:resizeableActivity="false"
             android:supportsPictureInPicture="true"
             androidprv:alwaysFocusable="true"
             android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout"
@@ -115,7 +115,7 @@
             android:windowSoftInputMode="stateAlwaysVisible"
         />
         <activity android:name=".PipOnStopActivity"
-            android:resizeableActivity="true"
+            android:resizeableActivity="false"
             android:supportsPictureInPicture="true"
             android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout"
             android:exported="true"
@@ -144,7 +144,7 @@
                   <layout android:defaultWidth="25%"
                           android:defaultHeight="35%"
                           android:gravity="top|right"
-                          android:minWidth="100dp"
+                          android:minWidth="90dp"
                           android:minHeight="80dp"
                   />
         </activity>
@@ -155,7 +155,7 @@
                   <layout android:defaultWidth="25%"
                           android:defaultHeight="35%"
                           android:gravity="bottom|left"
-                          android:minWidth="100dp"
+                          android:minWidth="90dp"
                           android:minHeight="80dp"
                   />
         </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 855b374..e6be28c 100644
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerPinnedStackTests.java
+++ b/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerPinnedStackTests.java
@@ -52,6 +52,11 @@
     private static final String EXTRA_FINISH_SELF_ON_RESUME = "finish_self_on_resume";
     private static final String EXTRA_REENTER_PIP_ON_EXIT = "reenter_pip_on_exit";
 
+    private static final int APP_OPS_OP_ENTER_PICTURE_IN_PICTURE_ON_HIDE = 67;
+    private static final int APP_OPS_MODE_ALLOWED = 0;
+    private static final int APP_OPS_MODE_IGNORED = 1;
+    private static final int APP_OPS_MODE_ERRORED = 2;
+
     private static final int ROTATION_0 = 0;
     private static final int ROTATION_90 = 1;
     private static final int ROTATION_180 = 2;
@@ -481,6 +486,24 @@
         assertPinnedStackIsOnTop();
     }
 
+    public void testAppOpsDenyPipOnPause() throws Exception {
+        // Disable enter-pip-on-hide and try to enter pip
+        setAppOpsOpToMode(ActivityManagerTestBase.componentName,
+                APP_OPS_OP_ENTER_PICTURE_IN_PICTURE_ON_HIDE, APP_OPS_MODE_IGNORED);
+
+        // Launch the PIP activity on pause
+        launchActivity(PIP_ACTIVITY, EXTRA_ENTER_PIP_ON_PAUSE, "true");
+        assertPinnedStackDoesNotExist();
+
+        // Go home and ensure that there is no pinned stack
+        launchHomeActivity();
+        assertPinnedStackDoesNotExist();
+
+        // Re-enable enter-pip-on-hide
+        setAppOpsOpToMode(ActivityManagerTestBase.componentName,
+                APP_OPS_OP_ENTER_PICTURE_IN_PICTURE_ON_HIDE, APP_OPS_MODE_ALLOWED);
+    }
+
     /**
      * Asserts that the pinned stack bounds does not intersect with the IME bounds.
      */
@@ -567,6 +590,13 @@
         executeShellCommand(String.format("input tap %d %d", tapX, tapY));
     }
 
+    /**
+     * Sets an app-ops op for a given package to a given mode.
+     */
+    private void setAppOpsOpToMode(String packageName, int op, int mode) throws Exception {
+        executeShellCommand(String.format("appops set %s %d %d", packageName, op, mode));
+    }
+
     private void pinnedStackTester(String startActivityCmd, String topActivityName,
             boolean moveTopToPinnedStack, boolean isFocusable) throws Exception {
 
diff --git a/hostsidetests/shortcuts/deviceside/multiuser/src/android/content/pm/cts/shortcut/multiuser/ShortcutManagerManagedUserTest.java b/hostsidetests/shortcuts/deviceside/multiuser/src/android/content/pm/cts/shortcut/multiuser/ShortcutManagerManagedUserTest.java
index 4de7848..0c5e662 100644
--- a/hostsidetests/shortcuts/deviceside/multiuser/src/android/content/pm/cts/shortcut/multiuser/ShortcutManagerManagedUserTest.java
+++ b/hostsidetests/shortcuts/deviceside/multiuser/src/android/content/pm/cts/shortcut/multiuser/ShortcutManagerManagedUserTest.java
@@ -59,75 +59,38 @@
                                 new ComponentName(getContext(), MainActivity.class))).build())));
     }
 
-    public void test04_getAndLaunch_primary() {
-        Launcher.setAsDefaultLauncher(getInstrumentation(), getContext());
-
-        final UserHandle userCurrent = android.os.Process.myUserHandle();
-        final UserHandle userOther = getManagedUser();
-
-        final ShortcutQuery q = new ShortcutQuery()
-                .setQueryFlags(ShortcutQuery.FLAG_MATCH_DYNAMIC)
-                .setPackage(getContext().getPackageName())
-                .setShortcutIds(list("s1"));
-        assertWith(getLauncherApps().getShortcuts(q, userCurrent))
-                .haveIds("s1")
-                .areAllDynamic()
-                .forShortcutWithId("s1", si -> {
-                    assertEquals("label1", si.getShortLabel());
-                    assertEquals(userCurrent, si.getUserHandle());
-                });
-        assertWith(getLauncherApps().getShortcuts(q, userOther))
-                .haveIds("s1")
-                .areAllDynamic()
-                .forShortcutWithId("s1", si -> {
-                    assertEquals("label2", si.getShortLabel());
-                    assertEquals(userOther, si.getUserHandle());
-                });
-
-        // Just call start and make sure they don't throw.
-        getLauncherApps().startShortcut(getContext().getPackageName(), "s1", null, null,
-                userCurrent);
-
-        // TODO Make sure the activity actually starts.
-        getLauncherApps().startShortcut(getContext().getPackageName(), "s1", null, null,
-                userOther);
-    }
-
-    public void test04_getAndLaunch_managed() {
+    public void test04_getAndLaunch() {
         Launcher.setAsDefaultLauncher(getInstrumentation(), getContext());
 
         final UserHandle userMain = android.os.Process.myUserHandle();
-        final UserHandle userOther = getManagedUser();
+        final UserHandle userManaged = getManagedUser();
 
         final ShortcutQuery q = new ShortcutQuery()
                 .setQueryFlags(ShortcutQuery.FLAG_MATCH_DYNAMIC)
                 .setPackage(getContext().getPackageName())
                 .setShortcutIds(list("s1"));
-        try {
-            getLauncherApps().getShortcuts(q, userOther);
-            fail("Didn't throw SecurityException");
-        } catch (SecurityException e) {
-            assertTrue(e.getMessage().contains("another profile"));
-        }
         assertWith(getLauncherApps().getShortcuts(q, userMain))
                 .haveIds("s1")
                 .areAllDynamic()
                 .forShortcutWithId("s1", si -> {
-                    assertEquals("label2", si.getShortLabel());
+                    assertEquals("label1", si.getShortLabel());
                     assertEquals(userMain, si.getUserHandle());
                 });
+        assertWith(getLauncherApps().getShortcuts(q, userManaged))
+                .haveIds("s1")
+                .areAllDynamic()
+                .forShortcutWithId("s1", si -> {
+                    assertEquals("label2", si.getShortLabel());
+                    assertEquals(userManaged, si.getUserHandle());
+                });
 
-        try {
-            getLauncherApps().startShortcut(getContext().getPackageName(), "s1", null, null,
-                    userOther);
-            fail("Didn't throw SecurityException");
-        } catch (SecurityException e) {
-            assertTrue(e.getMessage().contains("another profile"));
-        }
+        // Just call start and make sure they don't throw.
+        getLauncherApps().startShortcut(getContext().getPackageName(), "s1", null, null,
+                userMain);
 
         // TODO Make sure the activity actually starts.
         getLauncherApps().startShortcut(getContext().getPackageName(), "s1", null, null,
-                userMain);
+                userManaged);
     }
 
     private UserHandle getManagedUser() {
diff --git a/hostsidetests/shortcuts/hostside/src/android/content/pm/cts/shortcuthost/ShortcutManagerMultiuserTest.java b/hostsidetests/shortcuts/hostside/src/android/content/pm/cts/shortcuthost/ShortcutManagerMultiuserTest.java
index c5a97e7..a74ab45 100644
--- a/hostsidetests/shortcuts/hostside/src/android/content/pm/cts/shortcuthost/ShortcutManagerMultiuserTest.java
+++ b/hostsidetests/shortcuts/hostside/src/android/content/pm/cts/shortcuthost/ShortcutManagerMultiuserTest.java
@@ -51,11 +51,8 @@
                 "test02_createShortuctsOnPrimaryUser", getPrimaryUserId());
         runDeviceTestsAsUser(TARGET_PKG, ".ShortcutManagerManagedUserTest",
                 "test03_createShortuctsOnManagedProfile", profileId);
-
         runDeviceTestsAsUser(TARGET_PKG, ".ShortcutManagerManagedUserTest",
-                "test04_getAndLaunch_primary", getPrimaryUserId());
-        runDeviceTestsAsUser(TARGET_PKG, ".ShortcutManagerManagedUserTest",
-                "test04_getAndLaunch_managed", profileId);
+                "test04_getAndLaunch", getPrimaryUserId());
     }
 
     public void testSecondaryUser() throws Exception {
diff --git a/hostsidetests/tv/src/com/android/cts/tv/TvInputManagerHostTest.java b/hostsidetests/tv/src/com/android/cts/tv/TvInputManagerHostTest.java
index 67cd752..3cb6a0ec 100644
--- a/hostsidetests/tv/src/com/android/cts/tv/TvInputManagerHostTest.java
+++ b/hostsidetests/tv/src/com/android/cts/tv/TvInputManagerHostTest.java
@@ -17,7 +17,6 @@
 package com.android.cts.tv;
 
 import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
-import com.android.compatibility.common.util.VersionCodes;
 import com.android.ddmlib.Log.LogLevel;
 import com.android.tradefed.build.IBuildInfo;
 import com.android.tradefed.device.DeviceNotAvailableException;
diff --git a/tests/admin/src/android/admin/cts/DevicePolicyManagerTest.java b/tests/admin/src/android/admin/cts/DevicePolicyManagerTest.java
index 454a2ab..f2e0a7b 100644
--- a/tests/admin/src/android/admin/cts/DevicePolicyManagerTest.java
+++ b/tests/admin/src/android/admin/cts/DevicePolicyManagerTest.java
@@ -822,4 +822,16 @@
             assertDeviceOwnerMessage(e.getMessage());
         }
     }
+
+    public void testCreateAdminSupportIntent_returnNullIfRestrictionIsNotSet() {
+        if (!mDeviceAdmin) {
+            Log.w(TAG, "Skipping testCreateAdminSupportIntent");
+            return;
+        }
+        Intent intent = mDevicePolicyManager.createAdminSupportIntent(
+                DevicePolicyManager.POLICY_DISABLE_CAMERA);
+        assertNull(intent);
+        intent = mDevicePolicyManager.createAdminSupportIntent(UserManager.DISALLOW_ADJUST_VOLUME);
+        assertNull(intent);
+    }
 }
diff --git a/tests/app/src/android/app/cts/NotificationChannelGroupTest.java b/tests/app/src/android/app/cts/NotificationChannelGroupTest.java
new file mode 100644
index 0000000..9d8f0111
--- /dev/null
+++ b/tests/app/src/android/app/cts/NotificationChannelGroupTest.java
@@ -0,0 +1,51 @@
+/*
+ * 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.app.cts;
+
+import android.app.NotificationChannelGroup;
+import android.os.Parcel;
+import android.test.AndroidTestCase;
+
+public class NotificationChannelGroupTest extends AndroidTestCase {
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+    }
+
+    public void testDescribeContents() {
+        final int expected = 0;
+        NotificationChannelGroup group = new NotificationChannelGroup("1", "1");
+        assertEquals(expected, group.describeContents());
+    }
+
+    public void testConstructor() {
+        NotificationChannelGroup group =  new NotificationChannelGroup("1", "one");
+        assertEquals("1", group.getId());
+        assertEquals("one", group.getName());
+    }
+
+    public void testWriteToParcel() {
+        NotificationChannelGroup group = new NotificationChannelGroup("1", "one");
+        Parcel parcel = Parcel.obtain();
+        group.writeToParcel(parcel, 0);
+        parcel.setDataPosition(0);
+        NotificationChannelGroup fromParcel =
+                NotificationChannelGroup.CREATOR.createFromParcel(parcel);
+        assertEquals(group, fromParcel);
+    }
+}
diff --git a/tests/app/src/android/app/cts/NotificationManagerTest.java b/tests/app/src/android/app/cts/NotificationManagerTest.java
index 232abb8..d16bffb 100644
--- a/tests/app/src/android/app/cts/NotificationManagerTest.java
+++ b/tests/app/src/android/app/cts/NotificationManagerTest.java
@@ -18,6 +18,7 @@
 
 import android.app.Notification;
 import android.app.NotificationChannel;
+import android.app.NotificationChannelGroup;
 import android.app.NotificationManager;
 import android.app.PendingIntent;
 import android.app.stubs.R;
@@ -72,7 +73,7 @@
         try {
             mNotificationManager.createNotificationChannel(channel);
             final NotificationChannel createdChannel =
-                    mNotificationManager.getNotificationChannel("id");
+                    mNotificationManager.getNotificationChannel(mId);
             compareChannels(channel, createdChannel);
             // Lockscreen Visibility and canBypassDnd no longer settable.
             assertTrue(createdChannel.getLockscreenVisibility() != Notification.VISIBILITY_SECRET);
@@ -112,6 +113,34 @@
         }
     }
 
+    public void testCreateChannelWithGroup() {
+        NotificationChannelGroup ncg = new NotificationChannelGroup("g", "n");
+        mNotificationManager.createNotificationChannelGroup(ncg);
+        NotificationChannel channel =
+                new NotificationChannel(mId, "name", NotificationManager.IMPORTANCE_DEFAULT);
+        channel.setGroup(ncg.getId());
+        try {
+            mNotificationManager.createNotificationChannel(channel);
+            compareChannels(channel, mNotificationManager.getNotificationChannel(channel.getId()));
+        } finally {
+            mNotificationManager.deleteNotificationChannel(channel.getId());
+        }
+    }
+
+    public void testCreateChannelWithBadGroup() {
+        NotificationChannel channel =
+                new NotificationChannel(mId, "name", NotificationManager.IMPORTANCE_DEFAULT);
+        channel.setGroup("garbage");
+        try {
+            try {
+                mNotificationManager.createNotificationChannel(channel);
+                fail("Created notification with bad group");
+            } catch (IllegalArgumentException e) {}
+        } finally {
+            mNotificationManager.deleteNotificationChannel(channel.getId());
+        }
+    }
+
     public void testCreateChannelInvalidImportance() {
         NotificationChannel channel =
                 new NotificationChannel(mId, "name", NotificationManager.IMPORTANCE_UNSPECIFIED);
@@ -284,6 +313,31 @@
         assertTrue("notification list was not empty after cancelAll", sbns.length == 0);
     }
 
+    public void testNotifyWithTimeout() {
+        mNotificationManager.cancelAll();
+        final int id = 128;
+        final long timeout = 1000;
+
+        final Notification notification = new Notification.Builder(mContext)
+                .setSmallIcon(R.drawable.black)
+                .setContentTitle("notify#" + id)
+                .setContentText("This is #" + id + "notification  ")
+                .setTimeout(System.currentTimeMillis() + timeout)
+                .build();
+        mNotificationManager.notify(id, notification);
+
+        if (!checkNotificationExistence(id, /*shouldExist=*/ true)) {
+            fail("couldn't find posted notification id=" + id);
+        }
+
+        try {
+            Thread.sleep(timeout);
+        } catch (InterruptedException ex) {
+            // pass
+        }
+        checkNotificationExistence(id, false);
+    }
+
     private void sendNotification(final int id, final int icon) {
         final Intent intent = new Intent(Intent.ACTION_MAIN, Threads.CONTENT_URI);
 
@@ -301,19 +355,31 @@
                 .build();
         mNotificationManager.notify(id, notification);
 
-
         if (!checkNotificationExistence(id, /*shouldExist=*/ true)) {
             fail("couldn't find posted notification id=" + id);
         }
     }
 
     private boolean checkNotificationExistence(int id, boolean shouldExist) {
+        // notification is a bit asynchronous so it may take a few ms to appear in
+        // getActiveNotifications()
+        // we will check for it for up to 200ms before giving up
         boolean found = false;
-        final StatusBarNotification[] sbns = mNotificationManager.getActiveNotifications();
-        for (StatusBarNotification sbn : sbns) {
-            if (sbn.getId() == id) {
-                found = true;
-                break;
+        for (int tries=3; tries-->0;) {
+            // Need reset flag.
+            found = false;
+            final StatusBarNotification[] sbns = mNotificationManager.getActiveNotifications();
+            for (StatusBarNotification sbn : sbns) {
+                if (sbn.getId() == id) {
+                    found = true;
+                    break;
+                }
+            }
+            if (found == shouldExist) break;
+            try {
+                Thread.sleep(100);
+            } catch (InterruptedException ex) {
+                // pass
             }
         }
         return found == shouldExist;
@@ -335,5 +401,6 @@
         assertEquals(expected.getImportance(), actual.getImportance());
         assertEquals(expected.getSound(), actual.getSound());
         assertTrue(Arrays.equals(expected.getVibrationPattern(), actual.getVibrationPattern()));
+        assertEquals(expected.getGroup(), actual.getGroup());
     }
 }
diff --git a/tests/app/src/android/app/cts/NotificationTest.java b/tests/app/src/android/app/cts/NotificationTest.java
index 42a158e..0988c15 100644
--- a/tests/app/src/android/app/cts/NotificationTest.java
+++ b/tests/app/src/android/app/cts/NotificationTest.java
@@ -27,6 +27,7 @@
 
 public class NotificationTest extends AndroidTestCase {
 
+    private Notification.Action mAction;
     private Notification mNotification;
     private Context mContext;
 
@@ -34,6 +35,7 @@
     private static final String CONTENT_TITLE = "contentTitle";
     private static final String CONTENT_TEXT = "contentText";
     private static final String URI_STRING = "uriString";
+    private static final String ACTION_TITLE = "actionTitle";
     private static final int TOLERANCE = 200;
 
     @Override
@@ -150,6 +152,16 @@
         assertNull(result.sound);
     }
 
+    public void testColorizeNotification() {
+        mNotification = new Notification.Builder(mContext)
+                .setSmallIcon(1)
+                .setContentTitle(CONTENT_TITLE)
+                .setColorized(true)
+                .build();
+
+        assertTrue(mNotification.extras.getBoolean(Notification.EXTRA_COLORIZED));
+    }
+
     public void testBuilder() {
         final Intent intent = new Intent();
         final PendingIntent contentIntent = PendingIntent.getBroadcast(mContext, 0, intent, 0);
@@ -165,6 +177,16 @@
         assertEquals(contentIntent, mNotification.contentIntent);
     }
 
+    public void testActionBuilder() {
+        final Intent intent = new Intent();
+        final PendingIntent actionIntent = PendingIntent.getBroadcast(mContext, 0, intent, 0);
+        mAction = null;
+        mAction = new Notification.Action.Builder(0, ACTION_TITLE, actionIntent).build();
+        assertEquals(ACTION_TITLE, mAction.title);
+        assertEquals(actionIntent, mAction.actionIntent);
+        assertEquals(true, mAction.getAllowGeneratedReplies());
+    }
+
     public void testToString() {
         mNotification = new Notification();
         assertNotNull(mNotification.toString());
diff --git a/tests/autofillservice/src/android/autofillservice/cts/AutoFillServiceTestCase.java b/tests/autofillservice/src/android/autofillservice/cts/AutoFillServiceTestCase.java
index 0b6f235..8a90892 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/AutoFillServiceTestCase.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/AutoFillServiceTestCase.java
@@ -26,6 +26,7 @@
 import android.support.test.runner.AndroidJUnit4;
 
 import org.junit.AfterClass;
+import org.junit.Before;
 import org.junit.BeforeClass;
 import org.junit.runner.RunWith;
 
@@ -42,7 +43,7 @@
     protected static UiBot sUiBot;
 
     @BeforeClass
-    public static void setUiBot() {
+    public static void setUiBot() throws Exception {
         sUiBot = new UiBot(InstrumentationRegistry.getInstrumentation(), UI_TIMEOUT_SEC);
     }
 
@@ -52,6 +53,11 @@
         assertServiceDisabled();
     }
 
+    @Before
+    public void resetServiceState() {
+        InstrumentedAutoFillService.resetNumberFillRequests();
+    }
+
     /**
      * Enables the {@link InstrumentedAutoFillService} for auto-fill for the default user.
      */
diff --git a/tests/autofillservice/src/android/autofillservice/cts/InstrumentedAutoFillService.java b/tests/autofillservice/src/android/autofillservice/cts/InstrumentedAutoFillService.java
index 2237002..c1ff5eb 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/InstrumentedAutoFillService.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/InstrumentedAutoFillService.java
@@ -34,6 +34,7 @@
 
 import java.util.List;
 import java.util.Map;
+import java.util.concurrent.atomic.AtomicInteger;
 import java.util.concurrent.atomic.AtomicReference;
 
 /**
@@ -46,6 +47,8 @@
     private static final AtomicReference<CannedFillResponse> sCannedFillResponse =
             new AtomicReference<>();
 
+    private static AtomicInteger sNumberFillRequests = new AtomicInteger(0);
+
     // TODO(b/33197203, b/33802548): add tests for onConnected() / onDisconnected() and/or remove
     // overriden methods below that are only logging their calls.
 
@@ -62,8 +65,9 @@
     @Override
     public void onFillRequest(AssistStructure structure, Bundle data,
             CancellationSignal cancellationSignal, FillCallback callback) {
+        final int requestNumber = sNumberFillRequests.incrementAndGet();
         final CannedFillResponse cannedResponse = sCannedFillResponse.getAndSet(null);
-        Log.v(TAG, "onFillRequest(): cannedResponse = " + cannedResponse);
+        Log.v(TAG, "onFillRequest(#" + requestNumber + "): cannedResponse = " + cannedResponse);
 
         if (cannedResponse == null) {
             callback.onSuccess(null);
@@ -120,6 +124,24 @@
         }
     }
 
+    /**
+     * Resets the number of requests to {@link #onFillRequest(AssistStructure, Bundle,
+     * CancellationSignal, FillCallback)} so it can be verified by
+     * {@link #assertNumberFillRequests(int)}.
+     */
+    static void resetNumberFillRequests() {
+        sNumberFillRequests.set(0);
+    }
+
+    /**
+     * Asserts the number of calls to {@link #onFillRequest(AssistStructure, Bundle,
+     * CancellationSignal, FillCallback)} since the last call to {@link #resetNumberFillRequests()}.
+     */
+    static void assertNumberFillRequests(int expected) {
+        final int actual = sNumberFillRequests.get();
+        assertWithMessage("Invalid number of fill requests").that(actual).isEqualTo(expected);
+    }
+
     private void fill(Dataset.Builder builder, Map<String, AutoFillValue> fields,
             ViewNode view) {
         final String resourceId = view.getIdEntry();
diff --git a/tests/autofillservice/src/android/autofillservice/cts/LoginActivity.java b/tests/autofillservice/src/android/autofillservice/cts/LoginActivity.java
index 86a38b7..9d94c99 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/LoginActivity.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/LoginActivity.java
@@ -50,6 +50,9 @@
 
     private static final String TAG = "LoginActivity";
 
+    static final String ID_USERNAME = "username";
+    static final String ID_PASSWORD = "password";
+
     private EditText mUsernameEditText;
     private EditText mPasswordEditText;
     private TextView mOutput;
@@ -117,8 +120,8 @@
      */
     void expectAutoFill(CannedDataset.Builder builder, String username, String password) {
         builder
-                .setField("username", AutoFillValue.forText(username))
-                .setField("password", AutoFillValue.forText(password));
+                .setField(ID_USERNAME, AutoFillValue.forText(username))
+                .setField(ID_PASSWORD, AutoFillValue.forText(password));
         mAutoFillExpectation = new AutoFillExpectation(username, password);
         mUsernameEditText
                 .addTextChangedListener(new MyTextWatcher(mAutoFillExpectation.usernameLatch));
diff --git a/tests/autofillservice/src/android/autofillservice/cts/LoginActivityTest.java b/tests/autofillservice/src/android/autofillservice/cts/LoginActivityTest.java
index 43ed714..ba8ff95 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/LoginActivityTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/LoginActivityTest.java
@@ -15,6 +15,10 @@
  */
 package android.autofillservice.cts;
 
+import static android.autofillservice.cts.LoginActivity.ID_PASSWORD;
+import static android.autofillservice.cts.LoginActivity.ID_USERNAME;
+
+import android.autofillservice.cts.CannedFillResponse.CannedDataset;
 import android.support.test.filters.SmallTest;
 import android.support.test.rule.ActivityTestRule;
 
@@ -22,8 +26,6 @@
 import org.junit.Rule;
 import org.junit.Test;
 
-import android.autofillservice.cts.CannedFillResponse.CannedDataset;
-
 @SmallTest
 public class LoginActivityTest extends AutoFillServiceTestCase {
 
@@ -49,10 +51,22 @@
                 .addDataset(dataset.build())
                 .build());
 
-        sUiBot.triggerFillRequest();
+        sUiBot.triggerImeByRelativeId(ID_USERNAME);
+
+        // TODO(b/33197203, b/33802548): temporary hack because UI is based on notifications
+        sUiBot.collapseStatusBar();
+
+        // Make sure tapping on other fields from the dataset does not trigger it again
+        sUiBot.tapByRelativeId(ID_PASSWORD);
+        sUiBot.tapByRelativeId(ID_USERNAME);
+
+        // TODO(b/33197203, b/33802548): temporary hack because UI is based on notifications
+        sUiBot.expandStatusBar();
+
         sUiBot.selectDataset("The Dude");
 
         mLoginActivity.assertAutoFilled();
+        InstrumentedAutoFillService.assertNumberFillRequests(1);
     }
 
     /*
@@ -60,9 +74,16 @@
      *
      *  - no dataset
      *  - multiple datasets
+     *  - partitioned datasets (i.e., multiple fields)
      *  - response-level authentication (custom and fingerprint)
      *  - dataset-level authentication (custom and fingerprint)
      *
+     *  Save:
+     *  - when no dataset is returned initially
+     *  - when a dataset is returned initially
+     *  - make sure password is set
+     *  - test cases where non-savable-ids only are changed
+     *
      *  Other assertions:
      *  - illegal state thrown on callback calls
      *  - system server state after calls (for example, no pending callback)
diff --git a/tests/autofillservice/src/android/autofillservice/cts/UiBot.java b/tests/autofillservice/src/android/autofillservice/cts/UiBot.java
index 9900e19..89e5e02 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/UiBot.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/UiBot.java
@@ -36,10 +36,14 @@
 
     private final UiDevice mDevice;
     private final int mTimeout;
+    private final String mPackageName;
 
-    UiBot(Instrumentation instrumentation, int timeout) {
+    UiBot(Instrumentation instrumentation, int timeout) throws Exception {
         mDevice = UiDevice.getInstance(instrumentation);
         mTimeout = timeout;
+        mPackageName = instrumentation.getContext().getPackageName();
+
+        collapseStatusBar();
     }
 
     /**
@@ -53,13 +57,29 @@
     }
 
     /**
-     * Triggers the auto-fill affordance UI.
+     * Triggers IME by tapping a given field.
+     *
+     * @param id resource id, without the {@code +id} prefix
      */
-    void triggerFillRequest() {
-        Log.v(TAG, "triggerFillRequest()");
+    void triggerImeByRelativeId(String id) throws UiObjectNotFoundException {
+        Log.v(TAG, "triggerImeByRelativeId(): " + id);
+        final String fullId = mPackageName + ":id/" + id;
+        final UiObject field = mDevice.findObject(new UiSelector().resourceId(fullId));
+        final boolean clicked = field.clickAndWaitForNewWindow();
+        assertWithMessage("Failed to tap object with id '%s'", fullId).that(clicked).isTrue();
+    }
 
-        // TODO(b/33197203): use id string when using real auto-fill bar
-        clickOnNotification("AutoFill IME Emulation");
+    /**
+     * Taps a given UI object.
+     *
+     * @param id resource id, without the {@code +id} prefix
+     */
+    void tapByRelativeId(String id) throws UiObjectNotFoundException {
+        Log.v(TAG, "tapFieldByRelativeId(): " + id);
+        final String fullId = mPackageName + ":id/" + id;
+        final UiObject field = mDevice.findObject(new UiSelector().resourceId(fullId));
+        final boolean clicked = field.click();
+        assertWithMessage("Failed to tap object with id '%s'", fullId).that(clicked).isTrue();
     }
 
     /////////////////////////////////////////////////////////////////////////////////
@@ -127,6 +147,15 @@
         assertWithMessage("could not find object with '%s'", text).that(uiObject.exists()).isTrue();
         return uiObject;
     }
+
+    void collapseStatusBar() throws Exception {
+        Helper.runShellCommand("service call statusbar 2");
+    }
+
+    void expandStatusBar() throws Exception {
+        Helper.runShellCommand("service call statusbar 1");
+    }
+
     /////////////////////////////////////////
     // End of temporary notification code. //
     /////////////////////////////////////////
diff --git a/tests/camera/src/android/hardware/camera2/cts/CameraDeviceTest.java b/tests/camera/src/android/hardware/camera2/cts/CameraDeviceTest.java
index 6861989..965dc1c 100644
--- a/tests/camera/src/android/hardware/camera2/cts/CameraDeviceTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/CameraDeviceTest.java
@@ -1484,6 +1484,10 @@
                     checkMeteringRect(afRegions);
                 }
             }
+            // ZSL must default to OFF
+            if (request.get(CONTROL_ENABLE_ZSL) != null) {
+                mCollector.expectKeyValueEquals(request, CONTROL_ENABLE_ZSL, false);
+            }
         }
 
         // Sensor settings.
diff --git a/tests/camera/src/android/hardware/camera2/cts/CaptureResultTest.java b/tests/camera/src/android/hardware/camera2/cts/CaptureResultTest.java
index 802c500..b29fd57 100644
--- a/tests/camera/src/android/hardware/camera2/cts/CaptureResultTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/CaptureResultTest.java
@@ -531,6 +531,10 @@
             waiverKeys.add(CaptureResult.SENSOR_DYNAMIC_WHITE_LEVEL);
         }
 
+        if (!mStaticInfo.isEnableZslSupported()) {
+            waiverKeys.add(CaptureResult.CONTROL_ENABLE_ZSL);
+        }
+
         if (mStaticInfo.isHardwareLevelAtLeastFull()) {
             return waiverKeys;
         }
@@ -765,6 +769,7 @@
         resultKeys.add(CaptureResult.CONTROL_AF_STATE);
         resultKeys.add(CaptureResult.CONTROL_AWB_STATE);
         resultKeys.add(CaptureResult.CONTROL_POST_RAW_SENSITIVITY_BOOST);
+        resultKeys.add(CaptureResult.CONTROL_ENABLE_ZSL);
         resultKeys.add(CaptureResult.EDGE_MODE);
         resultKeys.add(CaptureResult.FLASH_MODE);
         resultKeys.add(CaptureResult.FLASH_STATE);
diff --git a/tests/camera/src/android/hardware/camera2/cts/MultiViewTest.java b/tests/camera/src/android/hardware/camera2/cts/MultiViewTest.java
index 15ff414..2357f54 100644
--- a/tests/camera/src/android/hardware/camera2/cts/MultiViewTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/MultiViewTest.java
@@ -24,6 +24,7 @@
 import android.hardware.camera2.cts.CameraTestUtils.ImageVerifierListener;
 import android.hardware.camera2.cts.testcases.Camera2MultiViewTestCase;
 import android.hardware.camera2.cts.testcases.Camera2MultiViewTestCase.CameraPreviewListener;
+import android.hardware.camera2.params.OutputConfiguration;
 import android.media.ImageReader;
 import android.os.SystemClock;
 import android.util.Log;
@@ -232,6 +233,36 @@
         }
     }
 
+    /*
+     * Verify behavior of sharing surfaces within one OutputConfiguration
+     */
+    public void testSharedSurfaces() throws Exception {
+        for (String cameraId : mCameraIds) {
+            try {
+                openCamera(cameraId);
+                if (getStaticInfo(cameraId).isHardwareLevelLegacy()) {
+                    Log.i(TAG, "Camera " + cameraId + " is legacy, skipping");
+                    continue;
+                }
+                if (!getStaticInfo(cameraId).isColorOutputSupported()) {
+                    Log.i(TAG, "Camera " + cameraId +
+                            " does not support color outputs, skipping");
+                    continue;
+                }
+
+                testSharedSurfacesConfigByCamera(cameraId);
+
+                testSharedSurfacesCaptureSessionByCamera(cameraId);
+
+                testSharedDeferredSurfacesByCamera(cameraId);
+            }
+            finally {
+                closeCamera(cameraId);
+            }
+        }
+    }
+
+
     /**
      * Start camera preview using input texture views and/or one image reader
      */
@@ -287,4 +318,247 @@
 
         stopPreview(cameraId);
     }
+
+    /*
+     * Verify behavior of OutputConfiguration when sharing surfaces
+     */
+    private void testSharedSurfacesConfigByCamera(String cameraId) throws Exception {
+        Size previewSize = getOrderedPreviewSizes(cameraId).get(0);
+
+        SurfaceTexture[] previewTexture = new SurfaceTexture[2];
+        Surface[] surfaces = new Surface[2];
+
+        // Create surface textures with the same size
+        for (int i = 0; i < 2; i++) {
+            previewTexture[i] = getAvailableSurfaceTexture(
+                    WAIT_FOR_COMMAND_TO_COMPLETE, mTextureView[i]);
+            assertNotNull("Unable to get preview surface texture", previewTexture[i]);
+            previewTexture[i].setDefaultBufferSize(previewSize.getWidth(), previewSize.getHeight());
+            // Correct the preview display rotation.
+            updatePreviewDisplayRotation(previewSize, mTextureView[i]);
+            surfaces[i] = new Surface(previewTexture[i]);
+        }
+
+        // Verify that outputConfiguration can be created with 2 surfaces with the same setting.
+        OutputConfiguration previewConfiguration = new OutputConfiguration(
+                OutputConfiguration.SURFACE_GROUP_ID_NONE, surfaces[0]);
+        previewConfiguration.enableSurfaceSharing();
+        previewConfiguration.addSurface(surfaces[1]);
+        List<Surface> previewSurfaces = previewConfiguration.getSurfaces();
+        List<Surface> inputSurfaces = Arrays.asList(surfaces);
+        assertTrue(
+                String.format("Surfaces returned from getSurfaces() don't match those passed in"),
+                previewSurfaces.equals(inputSurfaces));
+
+        // Verify that outputConfiguration throws exception if 2 surfaces are different size
+        SurfaceTexture outputTexture2 = new SurfaceTexture(/* random texture ID*/ 5);
+        outputTexture2.setDefaultBufferSize(previewSize.getWidth()/2,
+                previewSize.getHeight()/2);
+        Surface outputSurface2 = new Surface(outputTexture2);
+        try {
+            OutputConfiguration configuration = new OutputConfiguration(
+                    OutputConfiguration.SURFACE_GROUP_ID_NONE, surfaces[0]);
+            configuration.enableSurfaceSharing();
+            configuration.addSurface(outputSurface2);
+            fail("No error for invalid output config created from different sizes of surfaces");
+        } catch (IllegalArgumentException e) {
+            // expected
+        }
+
+        // Verify that outputConfiguration throws exception if 2 surfaces are different format
+        ImageReader imageReader = makeImageReader(previewSize, ImageFormat.YUV_420_888,
+                MAX_READER_IMAGES, new ImageDropperListener(), mHandler);
+        try {
+            OutputConfiguration configuration = new OutputConfiguration(
+                    OutputConfiguration.SURFACE_GROUP_ID_NONE, surfaces[0]);
+            configuration.enableSurfaceSharing();
+            configuration.addSurface(imageReader.getSurface());
+            fail("No error for invalid output config created from different format surfaces");
+        } catch (IllegalArgumentException e) {
+            // expected
+        }
+
+        // Verify that outputConfiguration can be created with deferred surface with the same
+        // setting.
+        OutputConfiguration deferredPreviewConfigure = new OutputConfiguration(
+                previewSize, SurfaceTexture.class);
+        deferredPreviewConfigure.addSurface(surfaces[0]);
+        assertTrue(String.format("Number of surfaces %d doesn't match expected value 1",
+                deferredPreviewConfigure.getSurfaces().size()),
+                deferredPreviewConfigure.getSurfaces().size() == 1);
+        assertEquals("Surface 0 in OutputConfiguration doesn't match input",
+                deferredPreviewConfigure.getSurfaces().get(0), surfaces[0]);
+
+        // Verify that outputConfiguration throws exception if deferred surface and non-deferred
+        // surface properties don't match
+        try {
+            OutputConfiguration configuration = new OutputConfiguration(
+                previewSize, SurfaceTexture.class);
+            configuration.addSurface(imageReader.getSurface());
+            fail("No error for invalid output config created deferred class with different type");
+        } catch (IllegalArgumentException e) {
+            // expected;
+        }
+    }
+
+    private void testSharedSurfacesCaptureSessionByCamera(String cameraId) throws Exception {
+        Size previewSize = getOrderedPreviewSizes(cameraId).get(0);
+        CameraPreviewListener[] previewListener = new CameraPreviewListener[2];
+        SurfaceTexture[] previewTexture = new SurfaceTexture[2];
+        Surface[] surfaces = new Surface[2];
+
+        // Create surface textures with the same size
+        for (int i = 0; i < 2; i++) {
+            previewListener[i] = new CameraPreviewListener();
+            mTextureView[i].setSurfaceTextureListener(previewListener[i]);
+            previewTexture[i] = getAvailableSurfaceTexture(
+                    WAIT_FOR_COMMAND_TO_COMPLETE, mTextureView[i]);
+            assertNotNull("Unable to get preview surface texture", previewTexture[i]);
+            previewTexture[i].setDefaultBufferSize(previewSize.getWidth(), previewSize.getHeight());
+            // Correct the preview display rotation.
+            updatePreviewDisplayRotation(previewSize, mTextureView[i]);
+            surfaces[i] = new Surface(previewTexture[i]);
+        }
+
+        SimpleCaptureCallback resultListener = new SimpleCaptureCallback();
+
+        // Create shared outputs for the two surface textures
+        OutputConfiguration surfaceSharedOutput = new OutputConfiguration(
+                OutputConfiguration.SURFACE_GROUP_ID_NONE, surfaces[0]);
+        surfaceSharedOutput.enableSurfaceSharing();
+        surfaceSharedOutput.addSurface(surfaces[1]);
+
+        List<OutputConfiguration> outputConfigurations = new ArrayList<>();
+        outputConfigurations.add(surfaceSharedOutput);
+
+        startPreviewWithConfigs(cameraId, outputConfigurations, null);
+
+        for (int i = 0; i < 2; i++) {
+            boolean previewDone =
+                    previewListener[i].waitForPreviewDone(WAIT_FOR_COMMAND_TO_COMPLETE);
+            assertTrue("Unable to start preview " + i, previewDone);
+            mTextureView[i].setSurfaceTextureListener(null);
+        }
+
+        SystemClock.sleep(PREVIEW_TIME_MS);
+
+        stopPreview(cameraId);
+    }
+
+    private void testSharedDeferredSurfacesByCamera(String cameraId) throws Exception {
+        Size previewSize = getOrderedPreviewSizes(cameraId).get(0);
+        CameraPreviewListener[] previewListener = new CameraPreviewListener[2];
+        SurfaceTexture[] previewTexture = new SurfaceTexture[2];
+        Surface[] surfaces = new Surface[2];
+
+        // Create surface textures with the same size
+        for (int i = 0; i < 2; i++) {
+            previewListener[i] = new CameraPreviewListener();
+            mTextureView[i].setSurfaceTextureListener(previewListener[i]);
+            previewTexture[i] = getAvailableSurfaceTexture(
+                    WAIT_FOR_COMMAND_TO_COMPLETE, mTextureView[i]);
+            assertNotNull("Unable to get preview surface texture", previewTexture[i]);
+            previewTexture[i].setDefaultBufferSize(previewSize.getWidth(), previewSize.getHeight());
+            // Correct the preview display rotation.
+            updatePreviewDisplayRotation(previewSize, mTextureView[i]);
+            surfaces[i] = new Surface(previewTexture[i]);
+        }
+
+        SimpleCaptureCallback resultListener = new SimpleCaptureCallback();
+
+        //
+        // Create deferred outputConfiguration, addSurface, createCaptureSession, addSurface, and
+        // finalizeOutputConfigurations.
+        //
+
+        OutputConfiguration surfaceSharedOutput = new OutputConfiguration(
+                previewSize, SurfaceTexture.class);
+        surfaceSharedOutput.enableSurfaceSharing();
+        surfaceSharedOutput.addSurface(surfaces[0]);
+
+        List<OutputConfiguration> outputConfigurations = new ArrayList<>();
+        outputConfigurations.add(surfaceSharedOutput);
+
+        // Run preview with one surface, and verify at least one frame is received.
+        startPreviewWithConfigs(cameraId, outputConfigurations, null);
+        boolean previewDone =
+                previewListener[0].waitForPreviewDone(WAIT_FOR_COMMAND_TO_COMPLETE);
+        assertTrue("Unable to start preview 0", previewDone);
+
+        SystemClock.sleep(PREVIEW_TIME_MS);
+
+        // Add deferred surface to the output configuration
+        surfaceSharedOutput.addSurface(surfaces[1]);
+        List<OutputConfiguration> deferredConfigs = new ArrayList<OutputConfiguration>();
+        deferredConfigs.add(surfaceSharedOutput);
+
+        // Run preview with both surfaces, and verify at least one frame is received for each
+        // surface.
+        updateOutputConfigs(cameraId, deferredConfigs, null);
+        previewDone =
+                previewListener[1].waitForPreviewDone(WAIT_FOR_COMMAND_TO_COMPLETE);
+        assertTrue("Unable to start preview 1", previewDone);
+
+        stopPreview(cameraId);
+
+        previewListener[0].reset();
+        previewListener[1].reset();
+
+        //
+        // Create outputConfiguration with a surface, createCaptureSession, addSurface, and
+        // finalizeOutputConfigurations.
+        //
+
+        surfaceSharedOutput = new OutputConfiguration(
+                OutputConfiguration.SURFACE_GROUP_ID_NONE, surfaces[0]);
+        surfaceSharedOutput.enableSurfaceSharing();
+        outputConfigurations.clear();
+        outputConfigurations.add(surfaceSharedOutput);
+
+        startPreviewWithConfigs(cameraId, outputConfigurations, null);
+        previewDone =
+                previewListener[0].waitForPreviewDone(WAIT_FOR_COMMAND_TO_COMPLETE);
+        assertTrue("Unable to start preview 0", previewDone);
+
+        // Add deferred surface to the output configuration, and continue running preview
+        surfaceSharedOutput.addSurface(surfaces[1]);
+        deferredConfigs.clear();
+        deferredConfigs.add(surfaceSharedOutput);
+        updateOutputConfigs(cameraId, deferredConfigs, null);
+        previewDone =
+                previewListener[1].waitForPreviewDone(WAIT_FOR_COMMAND_TO_COMPLETE);
+        assertTrue("Unable to start preview 1", previewDone);
+
+        SystemClock.sleep(PREVIEW_TIME_MS);
+        stopPreview(cameraId);
+
+        previewListener[0].reset();
+        previewListener[1].reset();
+
+        //
+        // Create deferred output configuration, createCaptureSession, addSurface, addSurface, and
+        // finalizeOutputConfigurations.
+
+        surfaceSharedOutput = new OutputConfiguration(
+                previewSize, SurfaceTexture.class);
+        surfaceSharedOutput.enableSurfaceSharing();
+        outputConfigurations.clear();
+        outputConfigurations.add(surfaceSharedOutput);
+        createSessionWithConfigs(cameraId, outputConfigurations);
+
+        // Add 2 surfaces to the output configuration, and run preview
+        surfaceSharedOutput.addSurface(surfaces[0]);
+        surfaceSharedOutput.addSurface(surfaces[1]);
+        deferredConfigs.clear();
+        deferredConfigs.add(surfaceSharedOutput);
+        updateOutputConfigs(cameraId, deferredConfigs, null);
+        for (int i = 0; i < 2; i++) {
+            previewDone =
+                    previewListener[i].waitForPreviewDone(WAIT_FOR_COMMAND_TO_COMPLETE);
+            assertTrue("Unable to start preview " + i, previewDone);
+        }
+
+        SystemClock.sleep(PREVIEW_TIME_MS);
+        stopPreview(cameraId);
+    }
 }
diff --git a/tests/camera/src/android/hardware/camera2/cts/StillCaptureTest.java b/tests/camera/src/android/hardware/camera2/cts/StillCaptureTest.java
index a9b4ad2..6c48418 100644
--- a/tests/camera/src/android/hardware/camera2/cts/StillCaptureTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/StillCaptureTest.java
@@ -103,7 +103,7 @@
     /**
      * Test normal still capture sequence.
      * <p>
-     * Preview and and jpeg output streams are configured. Max still capture
+     * Preview and jpeg output streams are configured. Max still capture
      * size is used for jpeg capture. The sequence of still capture being test
      * is: start preview, auto focus, precapture metering (if AE is not
      * converged), then capture jpeg. The AWB and AE are in auto modes. AF mode
@@ -128,6 +128,38 @@
     }
 
     /**
+     * Test ZSL still capture sequence.
+     * <p>
+     * Preview and jpeg output streams are configured. Max still capture
+     * size is used for jpeg capture. The sequence of still capture being test
+     * is: start preview, auto focus, precapture metering (if AE is not
+     * converged), then capture jpeg. The AWB and AE are in auto modes. AF mode
+     * is CONTINUOUS_PICTURE. Same as testTakePicture, but with enableZSL set.
+     * </p>
+     */
+    public void testTakePictureZsl() throws Exception{
+        for (String id : mCameraIds) {
+            try {
+                Log.i(TAG, "Testing basic ZSL take picture for Camera " + id);
+                openDevice(id);
+                if (!mStaticInfo.isColorOutputSupported()) {
+                    Log.i(TAG, "Camera " + id + " does not support color outputs, skipping");
+                    continue;
+                }
+                CaptureRequest.Builder stillRequest =
+                        mCamera.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
+                stillRequest.set(CaptureRequest.CONTROL_ENABLE_ZSL, true);
+                takePictureTestByCamera(/*aeRegions*/null, /*awbRegions*/null, /*afRegions*/null,
+                        /*addAeTriggerCancel*/false, /*allocateBitmap*/false,
+                        /*previewRequest*/null, stillRequest);
+            } finally {
+                closeDevice();
+                closeImageReader();
+            }
+        }
+    }
+
+    /**
      * Test basic Raw capture. Raw buffer avaiablility is checked, but raw buffer data is not.
      */
     public void testBasicRawCapture()  throws Exception {
@@ -143,7 +175,33 @@
                    continue;
                }
 
-               rawCaptureTestByCamera();
+               rawCaptureTestByCamera(/*stillRequest*/null);
+           } finally {
+               closeDevice();
+               closeImageReader();
+           }
+       }
+    }
+
+    /**
+     * Test basic Raw ZSL capture. Raw buffer avaiablility is checked, but raw buffer data is not.
+     */
+    public void testBasicRawZslCapture()  throws Exception {
+       for (int i = 0; i < mCameraIds.length; i++) {
+           try {
+               Log.i(TAG, "Testing raw ZSL capture for Camera " + mCameraIds[i]);
+               openDevice(mCameraIds[i]);
+
+               if (!mStaticInfo.isCapabilitySupported(
+                       CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_RAW)) {
+                   Log.i(TAG, "RAW capability is not supported in camera " + mCameraIds[i] +
+                           ". Skip the test.");
+                   continue;
+               }
+               CaptureRequest.Builder stillRequest =
+                       mCamera.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
+               stillRequest.set(CaptureRequest.CONTROL_ENABLE_ZSL, true);
+               rawCaptureTestByCamera(stillRequest);
            } finally {
                closeDevice();
                closeImageReader();
@@ -163,7 +221,7 @@
     public void testFullRawCapture() throws Exception {
         for (int i = 0; i < mCameraIds.length; i++) {
             try {
-                Log.i(TAG, "Testing raw capture for Camera " + mCameraIds[i]);
+                Log.i(TAG, "Testing raw+JPEG capture for Camera " + mCameraIds[i]);
                 openDevice(mCameraIds[i]);
                 if (!mStaticInfo.isCapabilitySupported(
                         CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_RAW)) {
@@ -172,13 +230,44 @@
                     continue;
                 }
 
-                fullRawCaptureTestByCamera();
+                fullRawCaptureTestByCamera(/*stillRequest*/null);
             } finally {
                 closeDevice();
                 closeImageReader();
             }
         }
     }
+
+    /**
+     * Test the full raw capture ZSL use case.
+     *
+     * This includes:
+     * - Configuring the camera with a preview, jpeg, and raw output stream.
+     * - Running preview until AE/AF can settle.
+     * - Capturing with a request targeting all three output streams.
+     */
+    public void testFullRawZSLCapture() throws Exception {
+        for (int i = 0; i < mCameraIds.length; i++) {
+            try {
+                Log.i(TAG, "Testing raw+JPEG ZSL capture for Camera " + mCameraIds[i]);
+                openDevice(mCameraIds[i]);
+                if (!mStaticInfo.isCapabilitySupported(
+                        CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_RAW)) {
+                    Log.i(TAG, "RAW capability is not supported in camera " + mCameraIds[i] +
+                            ". Skip the test.");
+                    continue;
+                }
+                CaptureRequest.Builder stillRequest =
+                        mCamera.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
+                stillRequest.set(CaptureRequest.CONTROL_ENABLE_ZSL, true);
+                fullRawCaptureTestByCamera(stillRequest);
+            } finally {
+                closeDevice();
+                closeImageReader();
+            }
+        }
+    }
+
     /**
      * Test touch for focus.
      * <p>
@@ -376,7 +465,8 @@
                     continue;
                 }
                 takePictureTestByCamera(/*aeRegions*/null, /*awbRegions*/null, /*afRegions*/null,
-                        /*addAeTriggerCancel*/true, /*allocateBitmap*/false);
+                        /*addAeTriggerCancel*/true, /*allocateBitmap*/false,
+                        /*previewRequest*/null, /*stillRequest*/null);
             } finally {
                 closeDevice();
                 closeImageReader();
@@ -402,7 +492,8 @@
                     continue;
                 }
                 takePictureTestByCamera(/*aeRegions*/null, /*awbRegions*/null, /*afRegions*/null,
-                        /*addAeTriggerCancel*/false, /*allocateBitmap*/true);
+                        /*addAeTriggerCancel*/false, /*allocateBitmap*/true,
+                        /*previewRequest*/null, /*stillRequest*/null);
             } finally {
                 closeDevice();
                 closeImageReader();
@@ -469,7 +560,8 @@
             MeteringRectangle[] aeRegions, MeteringRectangle[] awbRegions,
             MeteringRectangle[] afRegions) throws Exception {
         takePictureTestByCamera(aeRegions, awbRegions, afRegions,
-                /*addAeTriggerCancel*/false, /*allocateBitmap*/false);
+                /*addAeTriggerCancel*/false, /*allocateBitmap*/false,
+                /*previewRequest*/null, /*stillRequest*/null);
     }
 
     /**
@@ -488,10 +580,13 @@
      * @param afRegions AF regions for this capture
      * @param addAeTriggerCancel If a AE precapture trigger cancel is sent after the trigger.
      * @param allocateBitmap If a set of bitmaps are allocated during the test for memory test.
+     * @param previewRequest The preview request builder to use, or null to use the default
+     * @param stillRequest The still capture request to use, or null to use the default
      */
     private void takePictureTestByCamera(
             MeteringRectangle[] aeRegions, MeteringRectangle[] awbRegions,
-            MeteringRectangle[] afRegions, boolean addAeTriggerCancel, boolean allocateBitmap)
+            MeteringRectangle[] afRegions, boolean addAeTriggerCancel, boolean allocateBitmap,
+            CaptureRequest.Builder previewRequest, CaptureRequest.Builder stillRequest)
                     throws Exception {
 
         boolean hasFocuser = mStaticInfo.hasFocuser();
@@ -501,10 +596,12 @@
         CaptureResult result;
         SimpleCaptureCallback resultListener = new SimpleCaptureCallback();
         SimpleImageReaderListener imageListener = new SimpleImageReaderListener();
-        CaptureRequest.Builder previewRequest =
-                mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
-        CaptureRequest.Builder stillRequest =
-                mCamera.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
+        if (previewRequest == null) {
+            previewRequest = mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
+        }
+        if (stillRequest == null) {
+            stillRequest = mCamera.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
+        }
         prepareStillCaptureAndStartPreview(previewRequest, stillRequest, maxPreviewSz,
                 maxStillSz, resultListener, imageListener);
 
@@ -734,14 +831,14 @@
     /**
      * Basic raw capture test for each camera.
      */
-    private void rawCaptureTestByCamera() throws Exception {
+    private void rawCaptureTestByCamera(CaptureRequest.Builder stillRequest) throws Exception {
         Size maxPreviewSz = mOrderedPreviewSizes.get(0);
         Size size = mStaticInfo.getRawDimensChecked();
 
         // Prepare raw capture and start preview.
         CaptureRequest.Builder previewBuilder =
                 mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
-        CaptureRequest.Builder rawBuilder =
+        CaptureRequest.Builder rawBuilder = (stillRequest != null) ? stillRequest :
                 mCamera.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
         SimpleCaptureCallback resultListener = new SimpleCaptureCallback();
         SimpleImageReaderListener imageListener = new SimpleImageReaderListener();
@@ -772,7 +869,7 @@
         stopPreview();
     }
 
-    private void fullRawCaptureTestByCamera() throws Exception {
+    private void fullRawCaptureTestByCamera(CaptureRequest.Builder stillRequest) throws Exception {
         Size maxPreviewSz = mOrderedPreviewSizes.get(0);
         Size maxStillSz = mOrderedStillSizes.get(0);
 
@@ -790,7 +887,7 @@
         // Prepare raw capture and start preview.
         CaptureRequest.Builder previewBuilder =
                 mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
-        CaptureRequest.Builder multiBuilder =
+        CaptureRequest.Builder multiBuilder = (stillRequest != null) ? stillRequest :
                 mCamera.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
 
         ImageReader rawReader = null;
diff --git a/tests/camera/src/android/hardware/camera2/cts/SurfaceViewPreviewTest.java b/tests/camera/src/android/hardware/camera2/cts/SurfaceViewPreviewTest.java
index 6663201..2538645 100644
--- a/tests/camera/src/android/hardware/camera2/cts/SurfaceViewPreviewTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/SurfaceViewPreviewTest.java
@@ -538,18 +538,18 @@
         }
 
         // Add deferred surfaces to their configurations
-        surfaceViewOutput.setDeferredSurface(mPreviewSurface);
-        surfaceTextureOutput.setDeferredSurface(sharedOutputSurface1);
+        surfaceViewOutput.addSurface(mPreviewSurface);
+        surfaceTextureOutput.addSurface(sharedOutputSurface1);
 
-        // Verify bad inputs to setDeferredSurface
+        // Verify bad inputs to addSurface
         try {
-            surfaceViewOutput.setDeferredSurface(null);
+            surfaceViewOutput.addSurface(null);
             fail("No error from setting a null deferred surface");
         } catch (NullPointerException e) {
             // expected
         }
         try {
-            surfaceViewOutput.setDeferredSurface(mPreviewSurface);
+            surfaceViewOutput.addSurface(mPreviewSurface);
             fail("Shouldn't be able to set deferred surface twice");
         } catch (IllegalStateException e) {
             // expected
@@ -559,12 +559,12 @@
         List<OutputConfiguration> deferredSurfaces = new ArrayList<>();
         deferredSurfaces.add(surfaceTextureOutput);
 
-        mSession.finishDeferredConfiguration(deferredSurfaces);
+        mSession.finalizeOutputConfigurations(deferredSurfaces);
 
         // Try a second time, this should error
 
         try {
-            mSession.finishDeferredConfiguration(deferredSurfaces);
+            mSession.finalizeOutputConfigurations(deferredSurfaces);
             fail("Should have received ISE for trying to finish a deferred output twice");
         } catch (IllegalArgumentException e) {
             // expected
@@ -593,7 +593,7 @@
         deferredSurfaces.clear();
         deferredSurfaces.add(surfaceViewOutput);
 
-        mSession.finishDeferredConfiguration(deferredSurfaces);
+        mSession.finalizeOutputConfigurations(deferredSurfaces);
 
         // Use final deferred surface for a bit
         imageListener.resetImageCount();
@@ -611,16 +611,16 @@
                 new OutputConfiguration(maxPreviewSize, SurfaceTexture.class);
         deferredSurfaces.clear();
         try {
-            mSession.finishDeferredConfiguration(deferredSurfaces);
-            fail("No error for empty list passed to finishDeferredConfiguration");
+            mSession.finalizeOutputConfigurations(deferredSurfaces);
+            fail("No error for empty list passed to finalizeOutputConfigurations");
         } catch (IllegalArgumentException e) {
             // expected
         }
 
         deferredSurfaces.add(badConfig);
         try {
-            mSession.finishDeferredConfiguration(deferredSurfaces);
-            fail("No error for invalid output config being passed to finishDeferredConfiguration");
+            mSession.finalizeOutputConfigurations(deferredSurfaces);
+            fail("No error for invalid output config being passed to finalizeOutputConfigurations");
         } catch (IllegalArgumentException e) {
             // expected
         }
diff --git a/tests/camera/src/android/hardware/camera2/cts/helpers/StaticMetadata.java b/tests/camera/src/android/hardware/camera2/cts/helpers/StaticMetadata.java
index 07e810f..dea3cfd 100644
--- a/tests/camera/src/android/hardware/camera2/cts/helpers/StaticMetadata.java
+++ b/tests/camera/src/android/hardware/camera2/cts/helpers/StaticMetadata.java
@@ -2224,6 +2224,13 @@
     }
 
     /**
+     * Check if the enable ZSL key is supported.
+     */
+    public boolean isEnableZslSupported() {
+        return areKeysAvailable(CaptureRequest.CONTROL_ENABLE_ZSL);
+    }
+
+    /**
      * Get the value in index for a fixed-size array from a given key.
      *
      * <p>If the camera device is incorrectly reporting values, log a warning and return
diff --git a/tests/camera/src/android/hardware/camera2/cts/testcases/Camera2MultiViewTestCase.java b/tests/camera/src/android/hardware/camera2/cts/testcases/Camera2MultiViewTestCase.java
index 39bf0a5..a8180d1 100644
--- a/tests/camera/src/android/hardware/camera2/cts/testcases/Camera2MultiViewTestCase.java
+++ b/tests/camera/src/android/hardware/camera2/cts/testcases/Camera2MultiViewTestCase.java
@@ -33,6 +33,7 @@
 import android.hardware.camera2.cts.Camera2MultiViewCtsActivity;
 import android.hardware.camera2.cts.helpers.StaticMetadata;
 import android.hardware.camera2.cts.helpers.StaticMetadata.CheckLevel;
+import android.hardware.camera2.params.OutputConfiguration;
 import android.os.ConditionVariable;
 import android.os.Handler;
 import android.os.HandlerThread;
@@ -116,7 +117,7 @@
         mHandler = null;
         mCameraListener = null;
         for (CameraHolder camera : mCameraHolders) {
-            if (camera.isOpenned()) {
+            if (camera.isOpened()) {
                 camera.close();
                 camera = null;
             }
@@ -220,7 +221,7 @@
 
     protected void openCamera(String cameraId) throws Exception {
         CameraHolder camera = getCameraHolder(cameraId);
-        assertFalse("Camera has already opened", camera.isOpenned());
+        assertFalse("Camera has already opened", camera.isOpened());
         camera.open();
         return;
     }
@@ -230,29 +231,51 @@
         camera.close();
     }
 
+    protected void createSessionWithConfigs(String cameraId, List<OutputConfiguration> configs)
+            throws Exception {
+        CameraHolder camera = getCameraHolder(cameraId);
+        camera.createSessionWithConfigs(configs);
+    }
+
     protected void startPreview(
             String cameraId, List<Surface> outputSurfaces, CaptureCallback listener)
             throws Exception {
         CameraHolder camera = getCameraHolder(cameraId);
-        assertTrue("Camera " + cameraId + " is not openned", camera.isOpenned());
+        assertTrue("Camera " + cameraId + " is not openned", camera.isOpened());
         camera.startPreview(outputSurfaces, listener);
     }
 
+    protected void startPreviewWithConfigs(String cameraId,
+            List<OutputConfiguration> outputConfigs,
+            CaptureCallback listener)
+            throws Exception {
+        CameraHolder camera = getCameraHolder(cameraId);
+        assertTrue("Camera " + cameraId + " is not openned", camera.isOpened());
+        camera.startPreviewWithConfigs(outputConfigs, listener);
+    }
+
     protected void stopPreview(String cameraId) throws Exception {
         CameraHolder camera = getCameraHolder(cameraId);
         assertTrue("Camera " + cameraId + " preview is not running", camera.isPreviewStarted());
         camera.stopPreview();
     }
 
+    protected void updateOutputConfigs(String cameraId, List<OutputConfiguration> configs,
+            CaptureCallback listener) throws Exception {
+        CameraHolder camera = getCameraHolder(cameraId);
+        assertTrue("Camera " + cameraId + " is not opened", camera.isOpened());
+        camera.updateOutputConfigs(configs, listener);
+    }
+
     protected StaticMetadata getStaticInfo(String cameraId) {
         CameraHolder camera = getCameraHolder(cameraId);
-        assertTrue("Camera is not openned", camera.isOpenned());
+        assertTrue("Camera is not openned", camera.isOpened());
         return camera.getStaticInfo();
     }
 
     protected List<Size> getOrderedPreviewSizes(String cameraId) {
         CameraHolder camera = getCameraHolder(cameraId);
-        assertTrue("Camera is not openned", camera.isOpenned());
+        assertTrue("Camera is not openned", camera.isOpened());
         return camera.getOrderedPreviewSizes();
     }
 
@@ -325,6 +348,12 @@
             mPreviewDone.close();
             return true;
         }
+
+        /** Reset the Listener */
+        public void reset() {
+            mFirstPreviewAvailable = false;
+            mPreviewDone.close();
+        }
     }
 
     private CameraHolder getCameraHolder(String cameraId) {
@@ -370,12 +399,12 @@
             assertNotNull(String.format("Failed to open camera device ID: %s", mCameraId), mCamera);
         }
 
-        public boolean isOpenned() {
+        public boolean isOpened() {
             return (mCamera != null);
         }
 
         public void close() throws Exception {
-            if (!isOpenned()) {
+            if (!isOpened()) {
                 return;
             }
             mCamera.close();
@@ -401,6 +430,43 @@
             mSession.setRepeatingRequest(captureBuilder.build(), listener, mHandler);
         }
 
+        public void createSessionWithConfigs(List<OutputConfiguration> outputConfigs)
+                throws Exception {
+            mSessionListener = new BlockingSessionCallback();
+            mSession = configureCameraSessionWithConfig(mCamera, outputConfigs, mSessionListener, mHandler);
+        }
+
+        public void startPreviewWithConfigs(List<OutputConfiguration> outputConfigs,
+                CaptureCallback listener)
+                throws Exception {
+            createSessionWithConfigs(outputConfigs);
+
+            CaptureRequest.Builder captureBuilder =
+                    mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
+
+            for (OutputConfiguration config : outputConfigs) {
+                for (Surface surface : config.getSurfaces()) {
+                    captureBuilder.addTarget(surface);
+                }
+            }
+            mSession.setRepeatingRequest(captureBuilder.build(), listener, mHandler);
+        }
+
+        public void updateOutputConfigs(List<OutputConfiguration> configs,
+                CaptureCallback listener) throws Exception {
+            mSession.finalizeOutputConfigurations(configs);
+
+            CaptureRequest.Builder captureBuilder =
+                    mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
+
+            for (OutputConfiguration config : configs) {
+                for (Surface surface : config.getSurfaces()) {
+                    captureBuilder.addTarget(surface);
+                }
+            }
+            mSession.setRepeatingRequest(captureBuilder.build(), listener, mHandler);
+        }
+
         public boolean isPreviewStarted() {
             return (mSession != null);
         }
diff --git a/tests/core/runner/src/com/android/cts/core/runner/CoreTestRunner.java b/tests/core/runner/src/com/android/cts/core/runner/CoreTestRunner.java
index 46d0898..42b6684 100644
--- a/tests/core/runner/src/com/android/cts/core/runner/CoreTestRunner.java
+++ b/tests/core/runner/src/com/android/cts/core/runner/CoreTestRunner.java
@@ -27,8 +27,10 @@
 import com.android.cts.core.runner.support.ExtendedAndroidRunnerBuilder;
 import com.google.common.base.Splitter;
 import java.io.BufferedReader;
+import java.io.ByteArrayOutputStream;
 import java.io.FileReader;
 import java.io.IOException;
+import java.io.PrintStream;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
@@ -39,6 +41,7 @@
 import org.junit.runner.Computer;
 import org.junit.runner.JUnitCore;
 import org.junit.runner.Request;
+import org.junit.runner.Result;
 import org.junit.runner.Runner;
 import org.junit.runner.manipulation.Filter;
 import org.junit.runner.manipulation.Filterable;
@@ -289,16 +292,27 @@
         Log.d(TAG, "Finished preparations, running/listing tests");
 
         Bundle results = new Bundle();
+        Result junitResults = new Result();
         try {
-            core.run(Request.runner(runner));
+            junitResults = core.run(Request.runner(runner));
         } catch (RuntimeException e) {
             final String msg = "Fatal exception when running tests";
             Log.e(TAG, msg, e);
             // report the exception to instrumentation out
             results.putString(Instrumentation.REPORT_KEY_STREAMRESULT,
                     msg + "\n" + Log.getStackTraceString(e));
+        } finally {
+            ByteArrayOutputStream summaryStream = new ByteArrayOutputStream();
+            // create the stream used to output summary data to the user
+            PrintStream summaryWriter = new PrintStream(summaryStream);
+            instrumentationResultPrinter.instrumentationRunFinished(summaryWriter,
+                    results, junitResults);
+            summaryWriter.close();
+            results.putString(Instrumentation.REPORT_KEY_STREAMRESULT,
+                    String.format("\n%s", summaryStream.toString()));
         }
 
+
         Log.d(TAG, "Finished");
         finish(Activity.RESULT_OK, results);
     }
diff --git a/tests/inputmethod/Android.mk b/tests/inputmethod/Android.mk
new file mode 100644
index 0000000..7de22d4
--- /dev/null
+++ b/tests/inputmethod/Android.mk
@@ -0,0 +1,42 @@
+# 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)
+
+# don't include this package in any target
+LOCAL_MODULE_TAGS := tests
+# and when built explicitly put it in the data partition
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+
+# Tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts
+
+LOCAL_MULTILIB := both
+
+LOCAL_JAVA_LIBRARIES := android.test.runner
+
+LOCAL_STATIC_JAVA_LIBRARIES := \
+    android-support-test \
+    compatibility-device-util \
+    ctstestrunner
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_PACKAGE_NAME := CtsInputMethodTestCases
+
+include $(BUILD_CTS_PACKAGE)
+
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/tests/inputmethod/AndroidManifest.xml b/tests/inputmethod/AndroidManifest.xml
new file mode 100644
index 0000000..11f008d
--- /dev/null
+++ b/tests/inputmethod/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.view.inputmethod.cts">
+
+    <application
+        android:label="CtsInputMethodTestCases"
+        android:multiArch="true"
+        android:supportsRtl="true">
+
+        <uses-library android:name="android.test.runner" />
+
+        <activity
+            android:name="android.view.inputmethod.cts.InputMethodCtsActivity"
+            android:label="InputMethodCtsActivity">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.FRAMEWORK_INSTRUMENTATION_TEST" />
+            </intent-filter>
+        </activity>
+
+    </application>
+
+    <instrumentation
+        android:name="android.support.test.runner.AndroidJUnitRunner"
+        android:label="CTS tests of android.view.inputmethod"
+        android:targetPackage="android.view.inputmethod.cts">
+        <meta-data
+            android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
+
+</manifest>
diff --git a/tests/inputmethod/AndroidTest.xml b/tests/inputmethod/AndroidTest.xml
new file mode 100644
index 0000000..ed1e6be
--- /dev/null
+++ b/tests/inputmethod/AndroidTest.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2017 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+
+<configuration description="Config for CTS InputMethod test cases">
+    <target_preparer class="com.android.compatibility.common.tradefed.targetprep.ApkInstaller">
+        <option name="cleanup-apks" value="true" />
+        <option name="test-file-name" value="CtsInputMethodTestCases.apk" />
+    </target_preparer>
+    <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+        <option name="package" value="android.view.inputmethod.cts" />
+        <option name="runtime-hint" value="1m0s" />
+    </test>
+</configuration>
diff --git a/tests/inputmethod/res/layout/inputmethod_edittext.xml b/tests/inputmethod/res/layout/inputmethod_edittext.xml
new file mode 100644
index 0000000..a8f442e
--- /dev/null
+++ b/tests/inputmethod/res/layout/inputmethod_edittext.xml
@@ -0,0 +1,30 @@
+<?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.
+-->
+
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="fill_parent"
+    android:layout_height="wrap_content"
+    android:background="@drawable/blue"
+    android:padding="10px">
+
+    <EditText
+        android:id="@+id/entry"
+        android:layout_width="fill_parent"
+        android:layout_height="wrap_content"
+        android:background="@android:drawable/editbox_background"/>
+
+</RelativeLayout>
diff --git a/tests/inputmethod/res/values/colors.xml b/tests/inputmethod/res/values/colors.xml
new file mode 100644
index 0000000..1d87ea8
--- /dev/null
+++ b/tests/inputmethod/res/values/colors.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>
+    <drawable name="blue">#770000ff</drawable>
+</resources>
diff --git a/tests/inputmethod/res/xml/keyboard.xml b/tests/inputmethod/res/xml/keyboard.xml
new file mode 100644
index 0000000..af8b23b
--- /dev/null
+++ b/tests/inputmethod/res/xml/keyboard.xml
@@ -0,0 +1,30 @@
+<?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.
+-->
+
+<Keyboard xmlns:android="http://schemas.android.com/apk/res/android"
+    android:keyWidth="10%p"
+    android:horizontalGap="0px"
+    android:verticalGap="0px"
+    android:keyHeight="10px"
+    >
+
+    <Row>
+        <Key android:codes="-1" android:keyLabel="Sticky!"
+                android:isModifier="true" android:isSticky="true" />
+        <Key android:codes="120" android:keyLabel="x" />
+    </Row>
+</Keyboard>
diff --git a/tests/inputmethod/src/android/view/inputmethod/cts/BaseInputConnectionTest.java b/tests/inputmethod/src/android/view/inputmethod/cts/BaseInputConnectionTest.java
new file mode 100644
index 0000000..8089739
--- /dev/null
+++ b/tests/inputmethod/src/android/view/inputmethod/cts/BaseInputConnectionTest.java
@@ -0,0 +1,519 @@
+/*
+ * 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.view.inputmethod.cts;
+
+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.assertTrue;
+
+import android.app.Instrumentation;
+import android.content.ClipDescription;
+import android.content.Context;
+import android.net.Uri;
+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 android.text.Editable;
+import android.text.Selection;
+import android.text.Spannable;
+import android.text.SpannableString;
+import android.text.TextUtils;
+import android.view.KeyCharacterMap;
+import android.view.KeyEvent;
+import android.view.View;
+import android.view.Window;
+import android.view.inputmethod.BaseInputConnection;
+import android.view.inputmethod.CompletionInfo;
+import android.view.inputmethod.ExtractedTextRequest;
+import android.view.inputmethod.InputContentInfo;
+import android.view.inputmethod.InputMethodManager;
+import android.view.inputmethod.cts.R;
+import android.view.inputmethod.cts.util.InputConnectionTestUtils;
+import android.widget.EditText;
+
+import com.android.compatibility.common.util.PollingCheck;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@MediumTest
+@RunWith(AndroidJUnit4.class)
+public class BaseInputConnectionTest {
+    private Instrumentation mInstrumentation;
+    private InputMethodCtsActivity mActivity;
+    private Window mWindow;
+    private EditText mView;
+    private BaseInputConnection mConnection;
+
+    @Rule
+    public ActivityTestRule<InputMethodCtsActivity> mActivityRule =
+            new ActivityTestRule<>(InputMethodCtsActivity.class);
+
+    @Before
+    public void setup() {
+        mInstrumentation = InstrumentationRegistry.getInstrumentation();
+        mActivity = mActivityRule.getActivity();
+        PollingCheck.waitFor(mActivity::hasWindowFocus);
+        mWindow = mActivity.getWindow();
+        mView = (EditText) mWindow.findViewById(R.id.entry);
+        mConnection = new BaseInputConnection(mView, true);
+    }
+
+    @Test
+    public void testDefaultMethods() {
+        // These methods are default to return fixed result.
+
+        assertFalse(mConnection.beginBatchEdit());
+        assertFalse(mConnection.endBatchEdit());
+
+        // only fit for test default implementation of commitCompletion.
+        int completionId = 1;
+        String completionString = "commitCompletion test";
+        assertFalse(mConnection.commitCompletion(new CompletionInfo(completionId,
+                0, completionString)));
+
+        assertNull(mConnection.getExtractedText(new ExtractedTextRequest(), 0));
+
+        // only fit for test default implementation of performEditorAction.
+        int actionCode = 1;
+        int actionId = 2;
+        String action = "android.intent.action.MAIN";
+        assertTrue(mConnection.performEditorAction(actionCode));
+        assertFalse(mConnection.performContextMenuAction(actionId));
+        assertFalse(mConnection.performPrivateCommand(action, new Bundle()));
+    }
+
+    @Test
+    public void testOpComposingSpans() {
+        Spannable text = new SpannableString("Test ComposingSpans");
+        BaseInputConnection.setComposingSpans(text);
+        assertTrue(BaseInputConnection.getComposingSpanStart(text) > -1);
+        assertTrue(BaseInputConnection.getComposingSpanEnd(text) > -1);
+        BaseInputConnection.removeComposingSpans(text);
+        assertTrue(BaseInputConnection.getComposingSpanStart(text) == -1);
+        assertTrue(BaseInputConnection.getComposingSpanEnd(text) == -1);
+    }
+
+    /**
+     * getEditable: Return the target of edit operations. The default implementation
+     *              returns its own fake editable that is just used for composing text.
+     * clearMetaKeyStates: Default implementation uses
+     *              MetaKeyKeyListener#clearMetaKeyState(long, int) to clear the state.
+     *              BugId:1738511
+     * commitText: 1. Default implementation replaces any existing composing text with the given
+     *                text.
+     *             2. In addition, only if dummy mode, a key event is sent for the new text and the
+     *                current editable buffer cleared.
+     * deleteSurroundingText: The default implementation performs the deletion around the current
+     *              selection position of the editable text.
+     * getCursorCapsMode: 1. The default implementation uses TextUtils.getCapsMode to get the
+     *                  cursor caps mode for the current selection position in the editable text.
+     *                  TextUtils.getCapsMode is tested fully in TextUtilsTest#testGetCapsMode.
+     *                    2. In dummy mode in which case 0 is always returned.
+     * getTextBeforeCursor, getTextAfterCursor: The default implementation performs the deletion
+     *                          around the current selection position of the editable text.
+     * setSelection: changes the selection position in the current editable text.
+     */
+    @Test
+    public void testOpTextMethods() throws Throwable {
+        // return is an default Editable instance with empty source
+        final Editable text = mConnection.getEditable();
+        assertNotNull(text);
+        assertEquals(0, text.length());
+
+        // Test commitText, not dummy mode
+        CharSequence str = "TestCommit ";
+        Editable inputText = Editable.Factory.getInstance().newEditable(str);
+        mConnection.commitText(inputText, inputText.length());
+        final Editable text2 = mConnection.getEditable();
+        int strLength = str.length();
+        assertEquals(strLength, text2.length());
+        assertEquals(str.toString(), text2.toString());
+        assertEquals(TextUtils.CAP_MODE_WORDS,
+                mConnection.getCursorCapsMode(TextUtils.CAP_MODE_WORDS));
+        int offLength = 3;
+        CharSequence expected = str.subSequence(strLength - offLength, strLength);
+        assertEquals(expected.toString(), mConnection.getTextBeforeCursor(offLength,
+                BaseInputConnection.GET_TEXT_WITH_STYLES).toString());
+        mConnection.setSelection(0, 0);
+        expected = str.subSequence(0, offLength);
+        assertEquals(expected.toString(), mConnection.getTextAfterCursor(offLength,
+                BaseInputConnection.GET_TEXT_WITH_STYLES).toString());
+
+        mActivityRule.runOnUiThread(() -> {
+            assertTrue(mView.requestFocus());
+            assertTrue(mView.isFocused());
+        });
+
+        // dummy mode
+        BaseInputConnection dummyConnection = new BaseInputConnection(mView, false);
+        dummyConnection.commitText(inputText, inputText.length());
+        PollingCheck.waitFor(() -> text2.toString().equals(mView.getText().toString()));
+        assertEquals(0, dummyConnection.getCursorCapsMode(TextUtils.CAP_MODE_WORDS));
+
+        // Test deleteSurroundingText
+        int end = text2.length();
+        mConnection.setSelection(end, end);
+        // Delete the ending space
+        assertTrue(mConnection.deleteSurroundingText(1, 2));
+        Editable text3 = mConnection.getEditable();
+        assertEquals(strLength - 1, text3.length());
+        String expectedDelString = "TestCommit";
+        assertEquals(expectedDelString, text3.toString());
+    }
+
+    /**
+     * finishComposingText: 1. The default implementation removes the composing state from the
+     *                         current editable text.
+     *                      2. In addition, only if dummy mode, a key event is sent for the new
+     *                         text and the current editable buffer cleared.
+     * setComposingText: The default implementation places the given text into the editable,
+     *                  replacing any existing composing text
+     */
+    @Test
+    public void testFinishComposingText() throws Throwable {
+        CharSequence str = "TestFinish";
+        Editable inputText = Editable.Factory.getInstance().newEditable(str);
+        mConnection.commitText(inputText, inputText.length());
+        final Editable text = mConnection.getEditable();
+        // Test finishComposingText, not dummy mode
+        BaseInputConnection.setComposingSpans(text);
+        assertTrue(BaseInputConnection.getComposingSpanStart(text) > -1);
+        assertTrue(BaseInputConnection.getComposingSpanEnd(text) > -1);
+        mConnection.finishComposingText();
+        assertTrue(BaseInputConnection.getComposingSpanStart(text) == -1);
+        assertTrue(BaseInputConnection.getComposingSpanEnd(text) == -1);
+
+        mActivityRule.runOnUiThread(() -> {
+            assertTrue(mView.requestFocus());
+            assertTrue(mView.isFocused());
+        });
+
+        // dummy mode
+        BaseInputConnection dummyConnection = new BaseInputConnection(mView, false);
+        dummyConnection.setComposingText(str, str.length());
+        dummyConnection.finishComposingText();
+        PollingCheck.waitFor(() -> text.toString().equals(mView.getText().toString()));
+    }
+
+    /**
+     * Provides standard implementation for sending a key event to the window
+     * attached to the input connection's view
+     */
+    @Test
+    public void testSendKeyEvent() throws Throwable {
+        mActivityRule.runOnUiThread(() -> {
+            assertTrue(mView.requestFocus());
+            assertTrue(mView.isFocused());
+        });
+
+        // 12-key support
+        KeyCharacterMap keymap = KeyCharacterMap.load(KeyCharacterMap.VIRTUAL_KEYBOARD);
+        if (keymap.getKeyboardType() == KeyCharacterMap.NUMERIC) {
+            // 'Q' in case of 12-key(NUMERIC) keyboard
+            mConnection.sendKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_7));
+            mConnection.sendKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_7));
+        } else {
+            mInstrumentation.sendStringSync("q");
+            mInstrumentation.waitForIdleSync();
+        }
+        PollingCheck.waitFor(() -> "q".equals(mView.getText().toString()));
+    }
+
+    /**
+     * Updates InputMethodManager with the current fullscreen mode.
+     */
+    @Test
+    public void testReportFullscreenMode() {
+        InputMethodManager imManager = (InputMethodManager) mInstrumentation.getTargetContext()
+                .getSystemService(Context.INPUT_METHOD_SERVICE);
+        mConnection.reportFullscreenMode(false);
+        assertFalse(imManager.isFullscreenMode());
+        mConnection.reportFullscreenMode(true);
+        // Only IMEs are allowed to report full-screen mode.  Calling this method from the
+        // application should have no effect.
+        assertFalse(imManager.isFullscreenMode());
+    }
+
+    /**
+     * An utility method to create an instance of {@link BaseInputConnection} in dummy mode with
+     * an initial text and selection range.
+     * @param view the {@link View} to be associated with the {@link BaseInputConnection}.
+     * @param source the initial text.
+     * @return {@link BaseInputConnection} instantiated in dummy mode with {@code source} and
+     * selection range from {@code selectionStart} to {@code selectionEnd}
+     */
+    private static BaseInputConnection createDummyConnectionWithSelection(
+            final View view, final CharSequence source) {
+        final int selectionStart = Selection.getSelectionStart(source);
+        final int selectionEnd = Selection.getSelectionEnd(source);
+        final Editable editable = Editable.Factory.getInstance().newEditable(source);
+        Selection.setSelection(editable, selectionStart, selectionEnd);
+        return new BaseInputConnection(view, false) {
+            @Override
+            public Editable getEditable() {
+                return editable;
+            }
+        };
+    }
+
+    private void verifyDeleteSurroundingTextMain(final String initialState,
+            final int deleteBefore, final int deleteAfter, final String expectedState) {
+        final CharSequence source = InputConnectionTestUtils.formatString(initialState);
+        final BaseInputConnection ic = createDummyConnectionWithSelection(mView, source);
+        ic.deleteSurroundingText(deleteBefore, deleteAfter);
+
+        final CharSequence expectedString = InputConnectionTestUtils.formatString(expectedState);
+        final int expectedSelectionStart = Selection.getSelectionStart(expectedString);
+        final int expectedSelectionEnd = Selection.getSelectionEnd(expectedString);
+
+        // It is sufficient to check the surrounding text up to source.length() characters, because
+        // InputConnection.deleteSurroundingText() is not supposed to increase the text length.
+        final int retrievalLength = source.length();
+        if (expectedSelectionStart == 0) {
+            assertTrue(TextUtils.isEmpty(ic.getTextBeforeCursor(retrievalLength, 0)));
+        } else {
+            assertEquals(expectedString.subSequence(0, expectedSelectionStart).toString(),
+                    ic.getTextBeforeCursor(retrievalLength, 0).toString());
+        }
+        if (expectedSelectionStart == expectedSelectionEnd) {
+            assertTrue(TextUtils.isEmpty(ic.getSelectedText(0)));  // null is allowed.
+        } else {
+            assertEquals(expectedString.subSequence(expectedSelectionStart,
+                    expectedSelectionEnd).toString(), ic.getSelectedText(0).toString());
+        }
+        if (expectedSelectionEnd == expectedString.length()) {
+            assertTrue(TextUtils.isEmpty(ic.getTextAfterCursor(retrievalLength, 0)));
+        } else {
+            assertEquals(expectedString.subSequence(expectedSelectionEnd,
+                    expectedString.length()).toString(),
+                    ic.getTextAfterCursor(retrievalLength, 0).toString());
+        }
+    }
+
+    /**
+     * Tests {@link BaseInputConnection#deleteSurroundingText(int, int)} comprehensively.
+     */
+    @Test
+    public void testDeleteSurroundingText() throws Throwable {
+        verifyDeleteSurroundingTextMain("012[]3456789", 0, 0, "012[]3456789");
+        verifyDeleteSurroundingTextMain("012[]3456789", -1, -1, "012[]3456789");
+        verifyDeleteSurroundingTextMain("012[]3456789", 1, 2, "01[]56789");
+        verifyDeleteSurroundingTextMain("012[]3456789", 10, 1, "[]456789");
+        verifyDeleteSurroundingTextMain("012[]3456789", 1, 10, "01[]");
+        verifyDeleteSurroundingTextMain("[]0123456789", 3, 3, "[]3456789");
+        verifyDeleteSurroundingTextMain("0123456789[]", 3, 3, "0123456[]");
+        verifyDeleteSurroundingTextMain("012[345]6789", 0, 0, "012[345]6789");
+        verifyDeleteSurroundingTextMain("012[345]6789", -1, -1, "012[345]6789");
+        verifyDeleteSurroundingTextMain("012[345]6789", 1, 2, "01[345]89");
+        verifyDeleteSurroundingTextMain("012[345]6789", 10, 1, "[345]789");
+        verifyDeleteSurroundingTextMain("012[345]6789", 1, 10, "01[345]");
+        verifyDeleteSurroundingTextMain("[012]3456789", 3, 3, "[012]6789");
+        verifyDeleteSurroundingTextMain("0123456[789]", 3, 3, "0123[789]");
+        verifyDeleteSurroundingTextMain("[0123456789]", 0, 0, "[0123456789]");
+        verifyDeleteSurroundingTextMain("[0123456789]", 1, 1, "[0123456789]");
+
+        // Surrogate characters do not have any special meanings.  Validating the character sequence
+        // is beyond the goal of this API.
+        verifyDeleteSurroundingTextMain("0<>[]3456789", 1, 0, "0<[]3456789");
+        verifyDeleteSurroundingTextMain("0<>[]3456789", 2, 0, "0[]3456789");
+        verifyDeleteSurroundingTextMain("0<>[]3456789", 3, 0, "[]3456789");
+        verifyDeleteSurroundingTextMain("012[]<>56789", 0, 1, "012[]>56789");
+        verifyDeleteSurroundingTextMain("012[]<>56789", 0, 2, "012[]56789");
+        verifyDeleteSurroundingTextMain("012[]<>56789", 0, 3, "012[]6789");
+        verifyDeleteSurroundingTextMain("0<<[]3456789", 1, 0, "0<[]3456789");
+        verifyDeleteSurroundingTextMain("0<<[]3456789", 2, 0, "0[]3456789");
+        verifyDeleteSurroundingTextMain("0<<[]3456789", 3, 0, "[]3456789");
+        verifyDeleteSurroundingTextMain("012[]<<56789", 0, 1, "012[]<56789");
+        verifyDeleteSurroundingTextMain("012[]<<56789", 0, 2, "012[]56789");
+        verifyDeleteSurroundingTextMain("012[]<<56789", 0, 3, "012[]6789");
+        verifyDeleteSurroundingTextMain("0>>[]3456789", 1, 0, "0>[]3456789");
+        verifyDeleteSurroundingTextMain("0>>[]3456789", 2, 0, "0[]3456789");
+        verifyDeleteSurroundingTextMain("0>>[]3456789", 3, 0, "[]3456789");
+        verifyDeleteSurroundingTextMain("012[]>>56789", 0, 1, "012[]>56789");
+        verifyDeleteSurroundingTextMain("012[]>>56789", 0, 2, "012[]56789");
+        verifyDeleteSurroundingTextMain("012[]>>56789", 0, 3, "012[]6789");
+    }
+
+    private void verifyDeleteSurroundingTextInCodePointsMain(final String initialState,
+            final int deleteBeforeInCodePoints, final int deleteAfterInCodePoints,
+            final String expectedState) {
+        final CharSequence source = InputConnectionTestUtils.formatString(initialState);
+        final BaseInputConnection ic = createDummyConnectionWithSelection(mView, source);
+        ic.deleteSurroundingTextInCodePoints(deleteBeforeInCodePoints, deleteAfterInCodePoints);
+
+        final CharSequence expectedString = InputConnectionTestUtils.formatString(expectedState);
+        final int expectedSelectionStart = Selection.getSelectionStart(expectedString);
+        final int expectedSelectionEnd = Selection.getSelectionEnd(expectedString);
+
+        // It is sufficient to check the surrounding text up to source.length() characters, because
+        // InputConnection.deleteSurroundingTextInCodePoints() is not supposed to increase the text
+        // length.
+        final int retrievalLength = source.length();
+        if (expectedSelectionStart == 0) {
+            assertTrue(TextUtils.isEmpty(ic.getTextBeforeCursor(retrievalLength, 0)));
+        } else {
+            assertEquals(expectedString.subSequence(0, expectedSelectionStart).toString(),
+                    ic.getTextBeforeCursor(retrievalLength, 0).toString());
+        }
+        if (expectedSelectionStart == expectedSelectionEnd) {
+            assertTrue(TextUtils.isEmpty(ic.getSelectedText(0)));  // null is allowed.
+        } else {
+            assertEquals(expectedString.subSequence(expectedSelectionStart,
+                    expectedSelectionEnd).toString(), ic.getSelectedText(0).toString());
+        }
+        if (expectedSelectionEnd == expectedString.length()) {
+            assertTrue(TextUtils.isEmpty(ic.getTextAfterCursor(retrievalLength, 0)));
+        } else {
+            assertEquals(expectedString.subSequence(expectedSelectionEnd,
+                    expectedString.length()).toString(),
+                    ic.getTextAfterCursor(retrievalLength, 0).toString());
+        }
+    }
+
+    /**
+     * Tests {@link BaseInputConnection#deleteSurroundingTextInCodePoints(int, int)}
+     * comprehensively.
+     */
+    @Test
+    public void testDeleteSurroundingTextInCodePoints() throws Throwable {
+        verifyDeleteSurroundingTextInCodePointsMain("012[]3456789", 0, 0, "012[]3456789");
+        verifyDeleteSurroundingTextInCodePointsMain("012[]3456789", -1, -1, "012[]3456789");
+        verifyDeleteSurroundingTextInCodePointsMain("012[]3456789", 1, 2, "01[]56789");
+        verifyDeleteSurroundingTextInCodePointsMain("012[]3456789", 10, 1, "[]456789");
+        verifyDeleteSurroundingTextInCodePointsMain("012[]3456789", 1, 10, "01[]");
+        verifyDeleteSurroundingTextInCodePointsMain("[]0123456789", 3, 3, "[]3456789");
+        verifyDeleteSurroundingTextInCodePointsMain("0123456789[]", 3, 3, "0123456[]");
+        verifyDeleteSurroundingTextInCodePointsMain("012[345]6789", 0, 0, "012[345]6789");
+        verifyDeleteSurroundingTextInCodePointsMain("012[345]6789", -1, -1, "012[345]6789");
+        verifyDeleteSurroundingTextInCodePointsMain("012[345]6789", 1, 2, "01[345]89");
+        verifyDeleteSurroundingTextInCodePointsMain("012[345]6789", 10, 1, "[345]789");
+        verifyDeleteSurroundingTextInCodePointsMain("012[345]6789", 1, 10, "01[345]");
+        verifyDeleteSurroundingTextInCodePointsMain("[012]3456789", 3, 3, "[012]6789");
+        verifyDeleteSurroundingTextInCodePointsMain("0123456[789]", 3, 3, "0123[789]");
+        verifyDeleteSurroundingTextInCodePointsMain("[0123456789]", 0, 0, "[0123456789]");
+        verifyDeleteSurroundingTextInCodePointsMain("[0123456789]", 1, 1, "[0123456789]");
+
+        verifyDeleteSurroundingTextInCodePointsMain("0<>[]3456789", 1, 0, "0[]3456789");
+        verifyDeleteSurroundingTextInCodePointsMain("0<>[]3456789", 2, 0, "[]3456789");
+        verifyDeleteSurroundingTextInCodePointsMain("0<>[]3456789", 3, 0, "[]3456789");
+        verifyDeleteSurroundingTextInCodePointsMain("012[]<>56789", 0, 1, "012[]56789");
+        verifyDeleteSurroundingTextInCodePointsMain("012[]<>56789", 0, 2, "012[]6789");
+        verifyDeleteSurroundingTextInCodePointsMain("012[]<>56789", 0, 3, "012[]789");
+
+        verifyDeleteSurroundingTextInCodePointsMain("[]<><><><><>", 0, 0, "[]<><><><><>");
+        verifyDeleteSurroundingTextInCodePointsMain("[]<><><><><>", 0, 1, "[]<><><><>");
+        verifyDeleteSurroundingTextInCodePointsMain("[]<><><><><>", 0, 2, "[]<><><>");
+        verifyDeleteSurroundingTextInCodePointsMain("[]<><><><><>", 0, 3, "[]<><>");
+        verifyDeleteSurroundingTextInCodePointsMain("[]<><><><><>", 0, 4, "[]<>");
+        verifyDeleteSurroundingTextInCodePointsMain("[]<><><><><>", 0, 5, "[]");
+        verifyDeleteSurroundingTextInCodePointsMain("[]<><><><><>", 0, 6, "[]");
+        verifyDeleteSurroundingTextInCodePointsMain("[]<><><><><>", 0, 1000, "[]");
+        verifyDeleteSurroundingTextInCodePointsMain("<><><><><>[]", 0, 0, "<><><><><>[]");
+        verifyDeleteSurroundingTextInCodePointsMain("<><><><><>[]", 1, 0, "<><><><>[]");
+        verifyDeleteSurroundingTextInCodePointsMain("<><><><><>[]", 2, 0, "<><><>[]");
+        verifyDeleteSurroundingTextInCodePointsMain("<><><><><>[]", 3, 0, "<><>[]");
+        verifyDeleteSurroundingTextInCodePointsMain("<><><><><>[]", 4, 0, "<>[]");
+        verifyDeleteSurroundingTextInCodePointsMain("<><><><><>[]", 5, 0, "[]");
+        verifyDeleteSurroundingTextInCodePointsMain("<><><><><>[]", 6, 0, "[]");
+        verifyDeleteSurroundingTextInCodePointsMain("<><><><><>[]", 1000, 0, "[]");
+
+        verifyDeleteSurroundingTextInCodePointsMain("0<<[]3456789", 1, 0, "0<<[]3456789");
+        verifyDeleteSurroundingTextInCodePointsMain("0<<[]3456789", 2, 0, "0<<[]3456789");
+        verifyDeleteSurroundingTextInCodePointsMain("0<<[]3456789", 3, 0, "0<<[]3456789");
+        verifyDeleteSurroundingTextInCodePointsMain("012[]<<56789", 0, 1, "012[]<<56789");
+        verifyDeleteSurroundingTextInCodePointsMain("012[]<<56789", 0, 2, "012[]<<56789");
+        verifyDeleteSurroundingTextInCodePointsMain("012[]<<56789", 0, 3, "012[]<<56789");
+        verifyDeleteSurroundingTextInCodePointsMain("0>>[]3456789", 1, 0, "0>>[]3456789");
+        verifyDeleteSurroundingTextInCodePointsMain("0>>[]3456789", 2, 0, "0>>[]3456789");
+        verifyDeleteSurroundingTextInCodePointsMain("0>>[]3456789", 3, 0, "0>>[]3456789");
+        verifyDeleteSurroundingTextInCodePointsMain("012[]>>56789", 0, 1, "012[]>>56789");
+        verifyDeleteSurroundingTextInCodePointsMain("012[]>>56789", 0, 2, "012[]>>56789");
+        verifyDeleteSurroundingTextInCodePointsMain("012[]>>56789", 0, 3, "012[]>>56789");
+        verifyDeleteSurroundingTextInCodePointsMain("01<[]>456789", 1, 0, "01<[]>456789");
+        verifyDeleteSurroundingTextInCodePointsMain("01<[]>456789", 0, 1, "01<[]>456789");
+        verifyDeleteSurroundingTextInCodePointsMain("<12[]3456789", 1, 0, "<1[]3456789");
+        verifyDeleteSurroundingTextInCodePointsMain("<12[]3456789", 2, 0, "<[]3456789");
+        verifyDeleteSurroundingTextInCodePointsMain("<12[]3456789", 3, 0, "<12[]3456789");
+        verifyDeleteSurroundingTextInCodePointsMain("<<>[]3456789", 1, 0, "<[]3456789");
+        verifyDeleteSurroundingTextInCodePointsMain("<<>[]3456789", 2, 0, "<<>[]3456789");
+        verifyDeleteSurroundingTextInCodePointsMain("<<>[]3456789", 3, 0, "<<>[]3456789");
+        verifyDeleteSurroundingTextInCodePointsMain("012[]34>6789", 0, 1, "012[]4>6789");
+        verifyDeleteSurroundingTextInCodePointsMain("012[]34>6789", 0, 2, "012[]>6789");
+        verifyDeleteSurroundingTextInCodePointsMain("012[]34>6789", 0, 3, "012[]34>6789");
+        verifyDeleteSurroundingTextInCodePointsMain("012[]<>>6789", 0, 1, "012[]>6789");
+        verifyDeleteSurroundingTextInCodePointsMain("012[]<>>6789", 0, 2, "012[]<>>6789");
+        verifyDeleteSurroundingTextInCodePointsMain("012[]<>>6789", 0, 3, "012[]<>>6789");
+
+        // Atomicity test.
+        verifyDeleteSurroundingTextInCodePointsMain("0<<[]3456789", 1, 1, "0<<[]3456789");
+        verifyDeleteSurroundingTextInCodePointsMain("0<<[]3456789", 2, 1, "0<<[]3456789");
+        verifyDeleteSurroundingTextInCodePointsMain("0<<[]3456789", 3, 1, "0<<[]3456789");
+        verifyDeleteSurroundingTextInCodePointsMain("012[]<<56789", 1, 1, "012[]<<56789");
+        verifyDeleteSurroundingTextInCodePointsMain("012[]<<56789", 1, 2, "012[]<<56789");
+        verifyDeleteSurroundingTextInCodePointsMain("012[]<<56789", 1, 3, "012[]<<56789");
+        verifyDeleteSurroundingTextInCodePointsMain("0>>[]3456789", 1, 1, "0>>[]3456789");
+        verifyDeleteSurroundingTextInCodePointsMain("0>>[]3456789", 2, 1, "0>>[]3456789");
+        verifyDeleteSurroundingTextInCodePointsMain("0>>[]3456789", 3, 1, "0>>[]3456789");
+        verifyDeleteSurroundingTextInCodePointsMain("012[]>>56789", 1, 1, "012[]>>56789");
+        verifyDeleteSurroundingTextInCodePointsMain("012[]>>56789", 1, 2, "012[]>>56789");
+        verifyDeleteSurroundingTextInCodePointsMain("012[]>>56789", 1, 3, "012[]>>56789");
+        verifyDeleteSurroundingTextInCodePointsMain("01<[]>456789", 1, 1, "01<[]>456789");
+
+        // Do not verify the character sequences in the selected region.
+        verifyDeleteSurroundingTextInCodePointsMain("01[><]456789", 1, 0, "0[><]456789");
+        verifyDeleteSurroundingTextInCodePointsMain("01[><]456789", 0, 1, "01[><]56789");
+        verifyDeleteSurroundingTextInCodePointsMain("01[><]456789", 1, 1, "0[><]56789");
+    }
+
+    @Test
+    public void testCloseConnection() {
+        final CharSequence source = "0123456789";
+        mConnection.commitText(source, source.length());
+        final Editable text = mConnection.getEditable();
+        BaseInputConnection.setComposingSpans(text, 2, 5);
+        assertEquals(2, BaseInputConnection.getComposingSpanStart(text));
+        assertEquals(5, BaseInputConnection.getComposingSpanEnd(text));
+
+        // BaseInputConnection#closeConnection() must clear the on-going composition.
+        mConnection.closeConnection();
+        assertEquals(-1, BaseInputConnection.getComposingSpanStart(text));
+        assertEquals(-1, BaseInputConnection.getComposingSpanEnd(text));
+    }
+
+    @Test
+    public void testGetHandler() {
+        // BaseInputConnection must not implement getHandler().
+        assertNull(mConnection.getHandler());
+    }
+
+    @Test
+    public void testCommitContent() {
+        final InputContentInfo inputContentInfo = new InputContentInfo(
+                Uri.parse("content://com.example/path"),
+                new ClipDescription("sample content", new String[]{"image/png"}),
+                Uri.parse("https://example.com"));
+        // The default implementation should do nothing and just return false.
+        assertFalse(mConnection.commitContent(inputContentInfo, 0 /* flags */, null /* opts */));
+    }
+}
diff --git a/tests/inputmethod/src/android/view/inputmethod/cts/CompletionInfoTest.java b/tests/inputmethod/src/android/view/inputmethod/cts/CompletionInfoTest.java
new file mode 100644
index 0000000..9a8d206
--- /dev/null
+++ b/tests/inputmethod/src/android/view/inputmethod/cts/CompletionInfoTest.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.inputmethod.cts;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+import android.os.Parcel;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.view.inputmethod.CompletionInfo;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class CompletionInfoTest {
+    private static final int ID = 1;
+    private static final int POSITION = 1;
+    private static final String TEXT = "CompletionInfoText";
+    private static final String LABEL = "CompletionInfoLabel";
+
+    @Test
+    public void testCompletionInfo() {
+        new CompletionInfo(ID, POSITION, TEXT);
+        CompletionInfo info = new CompletionInfo(ID, POSITION, TEXT, LABEL);
+        assertCompletionInfo(info);
+
+        assertEquals(0, info.describeContents());
+        assertNotNull(info.toString());
+
+        Parcel p = Parcel.obtain();
+        info.writeToParcel(p, 0);
+        p.setDataPosition(0);
+        CompletionInfo targetInfo = CompletionInfo.CREATOR.createFromParcel(p);
+        p.recycle();
+        assertCompletionInfo(targetInfo);
+    }
+
+    private void assertCompletionInfo(CompletionInfo info) {
+        assertEquals(ID, info.getId());
+        assertEquals(POSITION, info.getPosition());
+        assertEquals(TEXT, info.getText().toString());
+        assertEquals(LABEL, info.getLabel().toString());
+    }
+}
diff --git a/tests/inputmethod/src/android/view/inputmethod/cts/EditorInfoTest.java b/tests/inputmethod/src/android/view/inputmethod/cts/EditorInfoTest.java
new file mode 100644
index 0000000..1557511
--- /dev/null
+++ b/tests/inputmethod/src/android/view/inputmethod/cts/EditorInfoTest.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.inputmethod.cts;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Mockito.atLeastOnce;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+import android.os.Bundle;
+import android.os.LocaleList;
+import android.os.Parcel;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.test.MoreAsserts;
+import android.text.TextUtils;
+import android.util.Printer;
+import android.view.inputmethod.EditorInfo;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class EditorInfoTest {
+    @Test
+    public void testEditorInfo() {
+        EditorInfo info = new EditorInfo();
+
+        info.actionId = 1;
+        info.actionLabel = "actionLabel";
+        info.fieldId = 2;
+        info.fieldName = "fieldName";
+        info.hintText = "hintText";
+        info.imeOptions = EditorInfo.IME_FLAG_NO_ENTER_ACTION;
+        info.initialCapsMode = TextUtils.CAP_MODE_CHARACTERS;
+        info.initialSelEnd = 10;
+        info.initialSelStart = 0;
+        info.inputType = EditorInfo.TYPE_MASK_CLASS;
+        info.label = "label";
+        info.packageName = "android.view.cts";
+        info.privateImeOptions = "privateIme";
+        Bundle b = new Bundle();
+        String key = "bundleKey";
+        String value = "bundleValue";
+        b.putString(key, value);
+        info.extras = b;
+        info.hintLocales = LocaleList.forLanguageTags("en-PH,en-US");
+        info.contentMimeTypes = new String[]{"image/gif", "image/png"};
+
+        assertEquals(0, info.describeContents());
+
+        Parcel p = Parcel.obtain();
+        info.writeToParcel(p, 0);
+        p.setDataPosition(0);
+        EditorInfo targetInfo = EditorInfo.CREATOR.createFromParcel(p);
+        p.recycle();
+        assertEquals(info.actionId, targetInfo.actionId);
+        assertEquals(info.fieldId, targetInfo.fieldId);
+        assertEquals(info.fieldName, targetInfo.fieldName);
+        assertEquals(info.imeOptions, targetInfo.imeOptions);
+        assertEquals(info.initialCapsMode, targetInfo.initialCapsMode);
+        assertEquals(info.initialSelEnd, targetInfo.initialSelEnd);
+        assertEquals(info.initialSelStart, targetInfo.initialSelStart);
+        assertEquals(info.inputType, targetInfo.inputType);
+        assertEquals(info.packageName, targetInfo.packageName);
+        assertEquals(info.privateImeOptions, targetInfo.privateImeOptions);
+        assertEquals(info.hintText.toString(), targetInfo.hintText.toString());
+        assertEquals(info.actionLabel.toString(), targetInfo.actionLabel.toString());
+        assertEquals(info.label.toString(), targetInfo.label.toString());
+        assertEquals(info.extras.getString(key), targetInfo.extras.getString(key));
+        assertEquals(info.hintLocales, targetInfo.hintLocales);
+        MoreAsserts.assertEquals(info.contentMimeTypes, targetInfo.contentMimeTypes);
+
+        Printer printer = mock(Printer.class);
+        String prefix = "TestEditorInfo";
+        info.dump(printer, prefix);
+        verify(printer, atLeastOnce()).println(anyString());
+    }
+
+    @Test
+    public void testNullHintLocals() {
+        EditorInfo info = new EditorInfo();
+        info.hintLocales = null;
+        Parcel p = Parcel.obtain();
+        info.writeToParcel(p, 0);
+        p.setDataPosition(0);
+        EditorInfo targetInfo = EditorInfo.CREATOR.createFromParcel(p);
+        p.recycle();
+        assertNull(targetInfo.hintLocales);
+    }
+}
diff --git a/tests/inputmethod/src/android/view/inputmethod/cts/ExtractedTextRequestTest.java b/tests/inputmethod/src/android/view/inputmethod/cts/ExtractedTextRequestTest.java
new file mode 100644
index 0000000..3e12579
--- /dev/null
+++ b/tests/inputmethod/src/android/view/inputmethod/cts/ExtractedTextRequestTest.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.inputmethod.cts;
+
+import static org.junit.Assert.assertEquals;
+
+import android.os.Parcel;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.view.inputmethod.ExtractedTextRequest;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class ExtractedTextRequestTest {
+    @Test
+    public void testExtractedTextRequest() {
+        ExtractedTextRequest request = new ExtractedTextRequest();
+        request.flags = 1;
+        request.hintMaxChars = 100;
+        request.hintMaxLines = 10;
+        request.token = 2;
+
+        assertEquals(0, request.describeContents());
+
+        Parcel p = Parcel.obtain();
+        request.writeToParcel(p, 0);
+        p.setDataPosition(0);
+        ExtractedTextRequest target = ExtractedTextRequest.CREATOR.createFromParcel(p);
+        p.recycle();
+        assertEquals(request.flags, target.flags);
+        assertEquals(request.hintMaxChars, request.hintMaxChars);
+        assertEquals(request.hintMaxLines, target.hintMaxLines);
+        assertEquals(request.token, target.token);
+    }
+}
diff --git a/tests/inputmethod/src/android/view/inputmethod/cts/ExtractedTextTest.java b/tests/inputmethod/src/android/view/inputmethod/cts/ExtractedTextTest.java
new file mode 100644
index 0000000..41e3efa
--- /dev/null
+++ b/tests/inputmethod/src/android/view/inputmethod/cts/ExtractedTextTest.java
@@ -0,0 +1,55 @@
+/*
+ * 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.view.inputmethod.cts;
+
+import static org.junit.Assert.assertEquals;
+
+import android.os.Parcel;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.view.inputmethod.ExtractedText;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class ExtractedTextTest {
+    @Test
+    public void testWriteToParcel() {
+        ExtractedText extractedText = new ExtractedText();
+        extractedText.flags = 1;
+        extractedText.selectionEnd = 11;
+        extractedText.selectionStart = 2;
+        extractedText.startOffset = 1;
+        CharSequence text = "test";
+        extractedText.text = text;
+        Parcel p = Parcel.obtain();
+        extractedText.writeToParcel(p, 0);
+        p.setDataPosition(0);
+        ExtractedText target = ExtractedText.CREATOR.createFromParcel(p);
+        assertEquals(extractedText.flags, target.flags);
+        assertEquals(extractedText.selectionEnd, target.selectionEnd);
+        assertEquals(extractedText.selectionStart, target.selectionStart);
+        assertEquals(extractedText.startOffset, target.startOffset);
+        assertEquals(extractedText.partialStartOffset, target.partialStartOffset);
+        assertEquals(extractedText.partialEndOffset, target.partialEndOffset);
+        assertEquals(extractedText.text.toString(), target.text.toString());
+
+        assertEquals(0, extractedText.describeContents());
+    }
+}
diff --git a/tests/inputmethod/src/android/view/inputmethod/cts/InputBindingTest.java b/tests/inputmethod/src/android/view/inputmethod/cts/InputBindingTest.java
new file mode 100644
index 0000000..faaff3d
--- /dev/null
+++ b/tests/inputmethod/src/android/view/inputmethod/cts/InputBindingTest.java
@@ -0,0 +1,63 @@
+/*
+ * 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.view.inputmethod.cts;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertSame;
+
+import android.os.Binder;
+import android.os.Parcel;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.view.View;
+import android.view.inputmethod.BaseInputConnection;
+import android.view.inputmethod.InputBinding;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class InputBindingTest {
+    @Test
+    public void testInputBinding() {
+        View view = new View(InstrumentationRegistry.getTargetContext());
+        BaseInputConnection bic = new BaseInputConnection(view, false);
+        Binder binder = new Binder();
+        int uid = 1;
+        int pid = 2;
+        InputBinding inputBinding = new InputBinding(bic, binder, uid, pid);
+        new InputBinding(bic, inputBinding);
+        assertSame(bic, inputBinding.getConnection());
+        assertSame(binder, inputBinding.getConnectionToken());
+        assertEquals(uid, inputBinding.getUid());
+        assertEquals(pid, inputBinding.getPid());
+
+        assertNotNull(inputBinding.toString());
+        assertEquals(0, inputBinding.describeContents());
+
+        Parcel p = Parcel.obtain();
+        inputBinding.writeToParcel(p, 0);
+        p.setDataPosition(0);
+        InputBinding target = InputBinding.CREATOR.createFromParcel(p);
+        assertEquals(uid, target.getUid());
+        assertEquals(pid, target.getPid());
+        assertSame(binder, target.getConnectionToken());
+    }
+}
diff --git a/tests/inputmethod/src/android/view/inputmethod/cts/InputConnectionWrapperTest.java b/tests/inputmethod/src/android/view/inputmethod/cts/InputConnectionWrapperTest.java
new file mode 100644
index 0000000..71abacc
--- /dev/null
+++ b/tests/inputmethod/src/android/view/inputmethod/cts/InputConnectionWrapperTest.java
@@ -0,0 +1,170 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.inputmethod.cts;
+
+import static com.android.compatibility.common.util.WidgetTestUtils.sameCharSequence;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.fail;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.content.ClipDescription;
+import android.net.Uri;
+import android.os.Bundle;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.text.TextUtils;
+import android.view.KeyEvent;
+import android.view.inputmethod.CompletionInfo;
+import android.view.inputmethod.CorrectionInfo;
+import android.view.inputmethod.EditorInfo;
+import android.view.inputmethod.ExtractedTextRequest;
+import android.view.inputmethod.InputConnection;
+import android.view.inputmethod.InputConnectionWrapper;
+import android.view.inputmethod.InputContentInfo;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class InputConnectionWrapperTest {
+    @Test
+    public void testInputConnectionWrapper() {
+        InputConnection inputConnection = mock(InputConnection.class);
+        doReturn(true).when(inputConnection).commitContent(any(InputContentInfo.class),
+                anyInt(), any(Bundle.class));
+        InputConnectionWrapper wrapper = new InputConnectionWrapper(null, true);
+        try {
+            wrapper.beginBatchEdit();
+            fail("Failed to throw NullPointerException!");
+        } catch (NullPointerException e) {
+            // expected
+        }
+        wrapper.setTarget(inputConnection);
+
+        wrapper.beginBatchEdit();
+        verify(inputConnection, times(1)).beginBatchEdit();
+
+        wrapper.clearMetaKeyStates(KeyEvent.META_ALT_ON);
+        verify(inputConnection, times(1)).clearMetaKeyStates(KeyEvent.META_ALT_ON);
+
+        wrapper.commitCompletion(new CompletionInfo(1, 1, "testText"));
+        ArgumentCaptor<CompletionInfo> completionInfoCaptor =
+                ArgumentCaptor.forClass(CompletionInfo.class);
+        verify(inputConnection, times(1)).commitCompletion(completionInfoCaptor.capture());
+        assertEquals(1, completionInfoCaptor.getValue().getId());
+        assertEquals(1, completionInfoCaptor.getValue().getPosition());
+        assertEquals("testText", completionInfoCaptor.getValue().getText());
+
+        wrapper.commitCorrection(new CorrectionInfo(0, "oldText", "newText"));
+        ArgumentCaptor<CorrectionInfo> correctionInfoCaptor =
+                ArgumentCaptor.forClass(CorrectionInfo.class);
+        verify(inputConnection, times(1)).commitCorrection(correctionInfoCaptor.capture());
+        assertEquals(0, correctionInfoCaptor.getValue().getOffset());
+        assertEquals("oldText", correctionInfoCaptor.getValue().getOldText());
+        assertEquals("newText", correctionInfoCaptor.getValue().getNewText());
+
+        wrapper.commitText("Text", 1);
+        verify(inputConnection, times(1)).commitText(sameCharSequence("Text"), eq(1));
+
+        wrapper.deleteSurroundingText(10, 100);
+        verify(inputConnection, times(1)).deleteSurroundingText(10, 100);
+
+        wrapper.deleteSurroundingTextInCodePoints(10, 100);
+        verify(inputConnection, times(1)).deleteSurroundingTextInCodePoints(10, 100);
+
+        wrapper.endBatchEdit();
+        verify(inputConnection, times(1)).endBatchEdit();
+
+        wrapper.finishComposingText();
+        verify(inputConnection, times(1)).finishComposingText();
+
+        wrapper.getCursorCapsMode(TextUtils.CAP_MODE_CHARACTERS);
+        verify(inputConnection, times(1)).getCursorCapsMode(TextUtils.CAP_MODE_CHARACTERS);
+
+        wrapper.getExtractedText(new ExtractedTextRequest(), 0);
+        verify(inputConnection, times(1)).getExtractedText(any(ExtractedTextRequest.class), eq(0));
+
+        wrapper.getTextAfterCursor(5, 0);
+        verify(inputConnection, times(1)).getTextAfterCursor(5, 0);
+
+        wrapper.getTextBeforeCursor(3, 0);
+        verify(inputConnection, times(1)).getTextBeforeCursor(3, 0);
+
+        wrapper.performContextMenuAction(1);
+        verify(inputConnection, times(1)).performContextMenuAction(1);
+
+        wrapper.performEditorAction(EditorInfo.IME_ACTION_GO);
+        verify(inputConnection, times(1)).performEditorAction(EditorInfo.IME_ACTION_GO);
+
+        wrapper.performPrivateCommand("com.android.action.MAIN", new Bundle());
+        verify(inputConnection, times(1)).performPrivateCommand(eq("com.android.action.MAIN"),
+                any(Bundle.class));
+
+        wrapper.reportFullscreenMode(true);
+        verify(inputConnection, times(1)).reportFullscreenMode(true);
+
+        wrapper.sendKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_0));
+        ArgumentCaptor<KeyEvent> keyEventCaptor = ArgumentCaptor.forClass(KeyEvent.class);
+        verify(inputConnection, times(1)).sendKeyEvent(keyEventCaptor.capture());
+        assertEquals(KeyEvent.ACTION_DOWN, keyEventCaptor.getValue().getAction());
+        assertEquals(KeyEvent.KEYCODE_0, keyEventCaptor.getValue().getKeyCode());
+
+        wrapper.setComposingText("Text", 1);
+        verify(inputConnection, times(1)).setComposingText("Text", 1);
+
+        wrapper.setSelection(0, 10);
+        verify(inputConnection, times(1)).setSelection(0, 10);
+
+        wrapper.getSelectedText(0);
+        verify(inputConnection, times(1)).getSelectedText(0);
+
+        wrapper.setComposingRegion(0, 3);
+        verify(inputConnection, times(1)).setComposingRegion(0, 3);
+
+        wrapper.requestCursorUpdates(InputConnection.CURSOR_UPDATE_IMMEDIATE);
+        verify(inputConnection, times(1))
+                .requestCursorUpdates(InputConnection.CURSOR_UPDATE_IMMEDIATE);
+
+        wrapper.closeConnection();
+        verify(inputConnection, times(1)).closeConnection();
+
+        verify(inputConnection, never()).getHandler();
+        assertNull(wrapper.getHandler());
+        verify(inputConnection, times(1)).getHandler();
+
+        verify(inputConnection, never()).commitContent(any(InputContentInfo.class), anyInt(),
+                any(Bundle.class));
+
+        final InputContentInfo inputContentInfo = new InputContentInfo(
+                Uri.parse("content://com.example/path"),
+                new ClipDescription("sample content", new String[]{"image/png"}),
+                Uri.parse("https://example.com"));
+        wrapper.commitContent(inputContentInfo, 0 /* flags */, null /* opt */);
+        verify(inputConnection, times(1)).commitContent(inputContentInfo, 0, null);
+    }
+}
diff --git a/tests/inputmethod/src/android/view/inputmethod/cts/InputContentInfoTest.java b/tests/inputmethod/src/android/view/inputmethod/cts/InputContentInfoTest.java
new file mode 100644
index 0000000..777b6d8
--- /dev/null
+++ b/tests/inputmethod/src/android/view/inputmethod/cts/InputContentInfoTest.java
@@ -0,0 +1,162 @@
+/*
+ * 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.view.inputmethod.cts;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+
+import android.content.ClipDescription;
+import android.net.Uri;
+import android.os.Parcel;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.view.inputmethod.InputContentInfo;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.security.InvalidParameterException;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class InputContentInfoTest {
+    @Test
+    public void testInputContentInfo() {
+        InputContentInfo info = new InputContentInfo(
+                Uri.parse("content://com.example/path"),
+                new ClipDescription("sample content", new String[]{"image/png"}),
+                Uri.parse("https://example.com"));
+
+        assertEquals(Uri.parse("content://com.example/path"), info.getContentUri());
+        assertEquals(1, info.getDescription().getMimeTypeCount());
+        assertEquals("image/png", info.getDescription().getMimeType(0));
+        assertEquals("sample content", info.getDescription().getLabel());
+        assertEquals(Uri.parse("https://example.com"), info.getLinkUri());
+        assertEquals(0, info.describeContents());
+
+        Parcel p = Parcel.obtain();
+        info.writeToParcel(p, 0);
+        p.setDataPosition(0);
+        InputContentInfo targetInfo = InputContentInfo.CREATOR.createFromParcel(p);
+        p.recycle();
+
+        assertEquals(info.getContentUri(), targetInfo.getContentUri());
+        assertEquals(info.getDescription().getMimeTypeCount(),
+                targetInfo.getDescription().getMimeTypeCount());
+        assertEquals(info.getDescription().getMimeType(0),
+                targetInfo.getDescription().getMimeType(0));
+        assertEquals(info.getDescription().getLabel(), targetInfo.getDescription().getLabel());
+        assertEquals(info.getLinkUri(), targetInfo.getLinkUri());
+        assertEquals(info.describeContents(), targetInfo.describeContents());
+    }
+
+    @Test
+    public void testOptionalConstructorParam() {
+        InputContentInfo info = new InputContentInfo(
+                Uri.parse("content://com.example/path"),
+                new ClipDescription("sample content", new String[]{"image/png"}));
+
+        assertEquals(Uri.parse("content://com.example/path"), info.getContentUri());
+        assertEquals(1, info.getDescription().getMimeTypeCount());
+        assertEquals("image/png", info.getDescription().getMimeType(0));
+        assertEquals("sample content", info.getDescription().getLabel());
+        assertNull(info.getLinkUri());
+        assertEquals(0, info.describeContents());
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void testContentUriNullContentUri() {
+        new InputContentInfo(
+                null, new ClipDescription("sample content", new String[]{"image/png"}),
+                Uri.parse("https://example.com"));
+    }
+
+    @Test(expected = InvalidParameterException.class)
+    public void testContentUriInvalidContentUri() {
+        new InputContentInfo(
+                Uri.parse("https://example.com"),
+                new ClipDescription("sample content", new String[]{"image/png"}),
+                Uri.parse("https://example.com"));
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void testMimeTypeNulLDescription() {
+        new InputContentInfo(
+                Uri.parse("content://com.example/path"), null,
+                Uri.parse("https://example.com"));
+    }
+
+    @Test
+    public void testLinkUri() {
+        // Test that we accept null link Uri
+        new InputContentInfo(
+                Uri.parse("content://com.example/path"),
+                new ClipDescription("sample content", new String[]{"image/png"}),
+                null);
+
+        // Test that we accept http link Uri
+        new InputContentInfo(
+                Uri.parse("content://com.example/path"),
+                new ClipDescription("sample content", new String[]{"image/png"}),
+                Uri.parse("http://example.com/path"));
+
+        // Test that we accept https link Uri
+        new InputContentInfo(
+                Uri.parse("content://com.example/path"),
+                new ClipDescription("sample content", new String[]{"image/png"}),
+                Uri.parse("https://example.com/path"));
+    }
+
+    @Test(expected = InvalidParameterException.class)
+    public void testLinkUriFtpLinkUri() {
+        // InputContentInfo must accept http and https link Uri only
+        new InputContentInfo(
+                Uri.parse("content://com.example/path"),
+                new ClipDescription("sample content", new String[]{"image/png"}),
+                Uri.parse("ftp://example.com/path"));
+    }
+
+    @Test(expected = InvalidParameterException.class)
+    public void testLinkUriContentLinkUri() {
+        // InputContentInfo must accept http and https link Uri only
+        new InputContentInfo(
+                Uri.parse("content://com.example/path"),
+                new ClipDescription("sample content", new String[]{"image/png"}),
+                Uri.parse("content://com.example/path"));
+    }
+
+    @Test
+    public void testRequestAndReleasePermission() {
+        InputContentInfo info = new InputContentInfo(
+                Uri.parse("content://com.example/path"),
+                new ClipDescription("sample content", new String[]{"image/png"}),
+                Uri.parse("https://example.com"));
+
+        // Here we only assert that {request, release}Permission() do not crash, because ensuring
+        // the entire functionality of these methods requires end-to-end IME test environment, which
+        // we do not have yet in CTS.
+        // Note it is actually intentional that calling these methods here has no effect.  Those
+        // methods would have effect only after the object is passed from the IME process to the
+        // application process.
+        // TODO: Create an end-to-end CTS test for this functionality.
+        info.requestPermission();
+        info.releasePermission();
+        info.requestPermission();
+        info.releasePermission();
+    }
+
+}
diff --git a/tests/inputmethod/src/android/view/inputmethod/cts/InputMethodCtsActivity.java b/tests/inputmethod/src/android/view/inputmethod/cts/InputMethodCtsActivity.java
new file mode 100644
index 0000000..9501d44
--- /dev/null
+++ b/tests/inputmethod/src/android/view/inputmethod/cts/InputMethodCtsActivity.java
@@ -0,0 +1,29 @@
+/*
+ * 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.view.inputmethod.cts;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.view.inputmethod.cts.R;
+
+public class InputMethodCtsActivity extends Activity {
+    @Override
+    protected void onCreate(Bundle icicle) {
+        super.onCreate(icicle);
+        setContentView(R.layout.inputmethod_edittext);
+    }
+}
diff --git a/tests/inputmethod/src/android/view/inputmethod/cts/InputMethodInfoTest.java b/tests/inputmethod/src/android/view/inputmethod/cts/InputMethodInfoTest.java
new file mode 100644
index 0000000..b34673be
--- /dev/null
+++ b/tests/inputmethod/src/android/view/inputmethod/cts/InputMethodInfoTest.java
@@ -0,0 +1,320 @@
+/*
+ * 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.view.inputmethod.cts;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Mockito.atLeastOnce;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
+import android.content.res.Resources;
+import android.os.Parcel;
+import android.os.ParcelFileDescriptor;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.text.TextUtils;
+import android.util.Printer;
+import android.view.inputmethod.InputMethod;
+import android.view.inputmethod.InputMethodInfo;
+import android.view.inputmethod.InputMethodManager;
+import android.view.inputmethod.InputMethodSubtype;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class InputMethodInfoTest {
+    private Context mContext;
+
+    private InputMethodInfo mInputMethodInfo;
+    private String mPackageName;
+    private String mClassName;
+    private CharSequence mLabel;
+    private String mSettingsActivity;
+
+    private int mSubtypeNameResId;
+    private int mSubtypeIconResId;
+    private String mSubtypeLocale;
+    private String mSubtypeMode;
+    private String mSubtypeExtraValue_key;
+    private String mSubtypeExtraValue_value;
+    private String mSubtypeExtraValue;
+    private boolean mSubtypeIsAuxiliary;
+    private boolean mSubtypeOverridesImplicitlyEnabledSubtype;
+    private int mSubtypeId;
+    private InputMethodSubtype mInputMethodSubtype;
+
+    @Before
+    public void setup() {
+        mContext = InstrumentationRegistry.getTargetContext();
+        mPackageName = mContext.getPackageName();
+        mClassName = InputMethodSettingsActivityStub.class.getName();
+        mLabel = "test";
+        mSettingsActivity = "android.view.inputmethod.cts.InputMethodSettingsActivityStub";
+        mInputMethodInfo = new InputMethodInfo(mPackageName, mClassName, mLabel, mSettingsActivity);
+
+        mSubtypeNameResId = 0;
+        mSubtypeIconResId = 0;
+        mSubtypeLocale = "en_US";
+        mSubtypeMode = "keyboard";
+        mSubtypeExtraValue_key = "key1";
+        mSubtypeExtraValue_value = "value1";
+        mSubtypeExtraValue = "tag," + mSubtypeExtraValue_key + "=" + mSubtypeExtraValue_value;
+        mSubtypeIsAuxiliary = false;
+        mSubtypeOverridesImplicitlyEnabledSubtype = false;
+        mSubtypeId = 99;
+        mInputMethodSubtype = new InputMethodSubtype(mSubtypeNameResId, mSubtypeIconResId,
+                mSubtypeLocale, mSubtypeMode, mSubtypeExtraValue, mSubtypeIsAuxiliary,
+                mSubtypeOverridesImplicitlyEnabledSubtype, mSubtypeId);
+    }
+
+    @Test
+    public void testInputMethodInfoProperties() throws XmlPullParserException, IOException {
+        assertEquals(0, mInputMethodInfo.describeContents());
+        assertNotNull(mInputMethodInfo.toString());
+
+        assertInfo(mInputMethodInfo);
+        assertEquals(0, mInputMethodInfo.getIsDefaultResourceId());
+
+        Intent intent = new Intent(InputMethod.SERVICE_INTERFACE);
+        intent.setClass(mContext, InputMethodSettingsActivityStub.class);
+        PackageManager pm = mContext.getPackageManager();
+        List<ResolveInfo> ris = pm.queryIntentServices(intent, PackageManager.GET_META_DATA);
+        for (int i = 0; i < ris.size(); i++) {
+            ResolveInfo resolveInfo = ris.get(i);
+            mInputMethodInfo = new InputMethodInfo(mContext, resolveInfo);
+            assertService(resolveInfo.serviceInfo, mInputMethodInfo.getServiceInfo());
+            assertInfo(mInputMethodInfo);
+        }
+    }
+
+    @Test
+    public void testInputMethodSubtypeProperties() {
+        // TODO: Test InputMethodSubtype.getDisplayName()
+        assertEquals(mSubtypeNameResId, mInputMethodSubtype.getNameResId());
+        assertEquals(mSubtypeIconResId, mInputMethodSubtype.getIconResId());
+        assertEquals(mSubtypeLocale, mInputMethodSubtype.getLocale());
+        assertEquals(mSubtypeMode, mInputMethodSubtype.getMode());
+        assertEquals(mSubtypeExtraValue, mInputMethodSubtype.getExtraValue());
+        assertTrue(mInputMethodSubtype.containsExtraValueKey(mSubtypeExtraValue_key));
+        assertEquals(mSubtypeExtraValue_value,
+                mInputMethodSubtype.getExtraValueOf(mSubtypeExtraValue_key));
+        assertEquals(mSubtypeIsAuxiliary, mInputMethodSubtype.isAuxiliary());
+        assertEquals(mSubtypeOverridesImplicitlyEnabledSubtype,
+                mInputMethodSubtype.overridesImplicitlyEnabledSubtype());
+        assertEquals(mSubtypeId, mInputMethodSubtype.hashCode());
+    }
+
+    private void assertService(ServiceInfo expected, ServiceInfo actual) {
+        assertEquals(expected.getIconResource(), actual.getIconResource());
+        assertEquals(expected.labelRes, actual.labelRes);
+        assertEquals(expected.nonLocalizedLabel, actual.nonLocalizedLabel);
+        assertEquals(expected.icon, actual.icon);
+        assertEquals(expected.permission, actual.permission);
+    }
+
+    private void assertInfo(InputMethodInfo info) {
+        assertEquals(mPackageName, info.getPackageName());
+        assertEquals(mSettingsActivity, info.getSettingsActivity());
+        ComponentName component = info.getComponent();
+        assertEquals(mClassName, component.getClassName());
+        String expectedId = component.flattenToShortString();
+        assertEquals(expectedId, info.getId());
+        assertEquals(mClassName, info.getServiceName());
+    }
+
+    @Test
+    public void testDump() {
+        Printer printer = mock(Printer.class);
+        String prefix = "test";
+        mInputMethodInfo.dump(printer, prefix);
+        verify(printer, atLeastOnce()).println(anyString());
+    }
+
+    @Test
+    public void testLoadIcon() {
+        PackageManager pm = mContext.getPackageManager();
+        assertNotNull(mInputMethodInfo.loadIcon(pm));
+    }
+
+    @Test
+    public void testEquals() {
+        InputMethodInfo inputMethodInfo = new InputMethodInfo(mPackageName, mClassName, mLabel,
+                mSettingsActivity);
+        assertTrue(inputMethodInfo.equals(mInputMethodInfo));
+    }
+
+    @Test
+    public void testLoadLabel() {
+        CharSequence expected = "test";
+        PackageManager pm = mContext.getPackageManager();
+        assertEquals(expected.toString(), mInputMethodInfo.loadLabel(pm).toString());
+    }
+
+    @Test
+    public void testInputMethodInfoWriteToParcel() {
+        final Parcel p = Parcel.obtain();
+        mInputMethodInfo.writeToParcel(p, 0);
+        p.setDataPosition(0);
+        final InputMethodInfo imi = InputMethodInfo.CREATOR.createFromParcel(p);
+        p.recycle();
+
+        assertEquals(mInputMethodInfo.getPackageName(), imi.getPackageName());
+        assertEquals(mInputMethodInfo.getServiceName(), imi.getServiceName());
+        assertEquals(mInputMethodInfo.getSettingsActivity(), imi.getSettingsActivity());
+        assertEquals(mInputMethodInfo.getId(), imi.getId());
+        assertEquals(mInputMethodInfo.getIsDefaultResourceId(), imi.getIsDefaultResourceId());
+        assertService(mInputMethodInfo.getServiceInfo(), imi.getServiceInfo());
+    }
+
+    @Test
+    public void testInputMethodSubtypeWriteToParcel() {
+        final Parcel p = Parcel.obtain();
+        mInputMethodSubtype.writeToParcel(p, 0);
+        p.setDataPosition(0);
+        final InputMethodSubtype subtype = InputMethodSubtype.CREATOR.createFromParcel(p);
+        p.recycle();
+
+        assertEquals(mInputMethodSubtype.containsExtraValueKey(mSubtypeExtraValue_key),
+                subtype.containsExtraValueKey(mSubtypeExtraValue_key));
+        assertEquals(mInputMethodSubtype.getExtraValue(), subtype.getExtraValue());
+        assertEquals(mInputMethodSubtype.getExtraValueOf(mSubtypeExtraValue_key),
+                subtype.getExtraValueOf(mSubtypeExtraValue_key));
+        assertEquals(mInputMethodSubtype.getIconResId(), subtype.getIconResId());
+        assertEquals(mInputMethodSubtype.getLocale(), subtype.getLocale());
+        assertEquals(mInputMethodSubtype.getMode(), subtype.getMode());
+        assertEquals(mInputMethodSubtype.getNameResId(), subtype.getNameResId());
+        assertEquals(mInputMethodSubtype.hashCode(), subtype.hashCode());
+        assertEquals(mInputMethodSubtype.isAuxiliary(), subtype.isAuxiliary());
+        assertEquals(mInputMethodSubtype.overridesImplicitlyEnabledSubtype(),
+                subtype.overridesImplicitlyEnabledSubtype());
+    }
+
+    @Test
+    public void testInputMethodSubtypesOfSystemImes() {
+        if (!mContext.getPackageManager().hasSystemFeature(
+                PackageManager.FEATURE_INPUT_METHODS)) {
+            return;
+        }
+
+        final InputMethodManager imm = mContext.getSystemService(InputMethodManager.class);
+        final List<InputMethodInfo> imis = imm.getInputMethodList();
+        final ArrayList<String> localeList = new ArrayList<>(Arrays.asList(
+                Resources.getSystem().getAssets().getLocales()));
+        boolean foundEnabledSystemImeSubtypeWithValidLanguage = false;
+        for (InputMethodInfo imi : imis) {
+            if ((imi.getServiceInfo().applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 0) {
+                continue;
+            }
+            final int subtypeCount = imi.getSubtypeCount();
+            // System IME must have one subtype at least.
+            assertTrue(subtypeCount > 0);
+            if (foundEnabledSystemImeSubtypeWithValidLanguage) {
+                continue;
+            }
+            final List<InputMethodSubtype> enabledSubtypes =
+                    imm.getEnabledInputMethodSubtypeList(imi, true);
+            SUBTYPE_LOOP:
+            for (InputMethodSubtype subtype : enabledSubtypes) {
+                final String subtypeLocale = subtype.getLocale();
+                if (subtypeLocale.length() < 2) {
+                    continue;
+                }
+                // TODO: Detect language more strictly.
+                final String subtypeLanguage = subtypeLocale.substring(0, 2);
+                for (final String locale : localeList) {
+                    if (locale.startsWith(subtypeLanguage)) {
+                        foundEnabledSystemImeSubtypeWithValidLanguage = true;
+                        break SUBTYPE_LOOP;
+                    }
+                }
+            }
+        }
+        assertTrue(foundEnabledSystemImeSubtypeWithValidLanguage);
+    }
+
+    @Test
+    public void testAtLeastOneEncryptionAwareInputMethodIsAvailable() {
+        if (!mContext.getPackageManager().hasSystemFeature(
+                PackageManager.FEATURE_INPUT_METHODS)) {
+            return;
+        }
+
+        if (!TextUtils.equals("native", getFbeMode())) {
+            // Skip the test unless the device is in native FBE mode.
+            return;
+        }
+
+        final InputMethodManager imm = mContext.getSystemService(InputMethodManager.class);
+        final List<InputMethodInfo> imis = imm.getInputMethodList();
+        boolean hasEncryptionAwareInputMethod = false;
+        for (final InputMethodInfo imi : imis) {
+            final ServiceInfo serviceInfo = imi.getServiceInfo();
+            if (serviceInfo == null) {
+                continue;
+            }
+            if ((serviceInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) !=
+                    ApplicationInfo.FLAG_SYSTEM) {
+                continue;
+            }
+            if (serviceInfo.encryptionAware) {
+                hasEncryptionAwareInputMethod = true;
+                break;
+            }
+        }
+        assertTrue(hasEncryptionAwareInputMethod);
+    }
+
+    private String getFbeMode() {
+        try (ParcelFileDescriptor.AutoCloseInputStream in =
+                     new ParcelFileDescriptor.AutoCloseInputStream(InstrumentationRegistry
+                             .getInstrumentation()
+                             .getUiAutomation()
+                             .executeShellCommand("sm get-fbe-mode"))) {
+            try (BufferedReader br =
+                         new BufferedReader(new InputStreamReader(in, StandardCharsets.UTF_8))) {
+                // Assume that the output of "sm get-fbe-mode" is always one-line.
+                final String line = br.readLine();
+                return line != null ? line.trim() : "";
+            }
+        } catch (IOException e) {
+            return "";
+        }
+    }
+}
diff --git a/tests/inputmethod/src/android/view/inputmethod/cts/InputMethodManagerTest.java b/tests/inputmethod/src/android/view/inputmethod/cts/InputMethodManagerTest.java
new file mode 100644
index 0000000..16eaad0
--- /dev/null
+++ b/tests/inputmethod/src/android/view/inputmethod/cts/InputMethodManagerTest.java
@@ -0,0 +1,136 @@
+/*
+ * 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.view.inputmethod.cts;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.app.Instrumentation;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.ResultReceiver;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.MediumTest;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+import android.view.KeyEvent;
+import android.view.Window;
+import android.view.inputmethod.BaseInputConnection;
+import android.view.inputmethod.InputMethodInfo;
+import android.view.inputmethod.InputMethodManager;
+import android.view.inputmethod.cts.R;
+import android.widget.EditText;
+
+import com.android.compatibility.common.util.PollingCheck;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.List;
+
+@MediumTest
+@RunWith(AndroidJUnit4.class)
+public class InputMethodManagerTest {
+    private Instrumentation mInstrumentation;
+    private InputMethodCtsActivity mActivity;
+
+    @Rule
+    public ActivityTestRule<InputMethodCtsActivity> mActivityRule =
+            new ActivityTestRule<>(InputMethodCtsActivity.class);
+
+    @Before
+    public void setup() {
+        mInstrumentation = InstrumentationRegistry.getInstrumentation();
+        mActivity = mActivityRule.getActivity();
+    }
+
+    @After
+    public void teardown() {
+        mInstrumentation.sendKeyDownUpSync(KeyEvent.KEYCODE_BACK);
+    }
+
+    @Test
+    public void testInputMethodManager() throws Throwable {
+        if (!mActivity.getPackageManager().hasSystemFeature(
+                PackageManager.FEATURE_INPUT_METHODS)) {
+            return;
+        }
+
+        Window window = mActivity.getWindow();
+        final EditText view = (EditText) window.findViewById(R.id.entry);
+
+        PollingCheck.waitFor(1000, view::hasWindowFocus);
+
+        mActivityRule.runOnUiThread(view::requestFocus);
+        mInstrumentation.waitForIdleSync();
+        assertTrue(view.isFocused());
+
+        BaseInputConnection connection = new BaseInputConnection(view, false);
+        Context context = mInstrumentation.getTargetContext();
+        final InputMethodManager imManager = (InputMethodManager) context
+                .getSystemService(Context.INPUT_METHOD_SERVICE);
+
+        PollingCheck.waitFor(imManager::isActive);
+
+        assertTrue(imManager.isAcceptingText());
+        assertTrue(imManager.isActive(view));
+
+        assertFalse(imManager.isFullscreenMode());
+        connection.reportFullscreenMode(true);
+        // Only IMEs are allowed to report full-screen mode.  Calling this method from the
+        // application should have no effect.
+        assertFalse(imManager.isFullscreenMode());
+
+        mActivityRule.runOnUiThread(() -> {
+            IBinder token = view.getWindowToken();
+
+            // Show and hide input method.
+            assertTrue(imManager.showSoftInput(view, InputMethodManager.SHOW_IMPLICIT));
+            assertTrue(imManager.hideSoftInputFromWindow(token, 0));
+
+            Handler handler = new Handler();
+            ResultReceiver receiver = new ResultReceiver(handler);
+            assertTrue(imManager.showSoftInput(view, 0, receiver));
+            receiver = new ResultReceiver(handler);
+            assertTrue(imManager.hideSoftInputFromWindow(token, 0, receiver));
+
+            imManager.showSoftInputFromInputMethod(token, InputMethodManager.SHOW_FORCED);
+            imManager.hideSoftInputFromInputMethod(token, InputMethodManager.HIDE_NOT_ALWAYS);
+
+            // status: hide to show to hide
+            imManager.toggleSoftInputFromWindow(token, 0, InputMethodManager.HIDE_NOT_ALWAYS);
+            imManager.toggleSoftInputFromWindow(token, 0, InputMethodManager.HIDE_NOT_ALWAYS);
+
+            List<InputMethodInfo> enabledImList = imManager.getEnabledInputMethodList();
+            if (enabledImList != null && enabledImList.size() > 0) {
+                imManager.setInputMethod(token, enabledImList.get(0).getId());
+                // cannot test whether setting was successful
+            }
+
+            List<InputMethodInfo> imList = imManager.getInputMethodList();
+            if (imList != null && enabledImList != null) {
+                assertTrue(imList.size() >= enabledImList.size());
+            }
+        });
+        mInstrumentation.waitForIdleSync();
+    }
+}
diff --git a/tests/inputmethod/src/android/view/inputmethod/cts/InputMethodSettingsActivityStub.java b/tests/inputmethod/src/android/view/inputmethod/cts/InputMethodSettingsActivityStub.java
new file mode 100644
index 0000000..58aa364
--- /dev/null
+++ b/tests/inputmethod/src/android/view/inputmethod/cts/InputMethodSettingsActivityStub.java
@@ -0,0 +1,23 @@
+/*
+ * 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.view.inputmethod.cts;
+
+import android.preference.PreferenceActivity;
+
+public class InputMethodSettingsActivityStub extends PreferenceActivity {
+
+}
diff --git a/tests/inputmethod/src/android/view/inputmethod/cts/KeyboardTest.java b/tests/inputmethod/src/android/view/inputmethod/cts/KeyboardTest.java
new file mode 100644
index 0000000..3f61093
--- /dev/null
+++ b/tests/inputmethod/src/android/view/inputmethod/cts/KeyboardTest.java
@@ -0,0 +1,111 @@
+/*
+ * 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.view.inputmethod.cts;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import android.inputmethodservice.Keyboard;
+import android.inputmethodservice.Keyboard.Key;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.view.inputmethod.cts.R;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class KeyboardTest {
+    @Test
+    public void testKeyOnPressedAndReleased() {
+        Key nonStickyKey = null;
+        Key stickyKey = null;
+        // Indirectly instantiate Keyboard.Key with XML resources.
+        final Keyboard keyboard =
+                new Keyboard(InstrumentationRegistry.getTargetContext(), R.xml.keyboard);
+        for (final Key key : keyboard.getKeys()) {
+            if (!key.sticky) {
+                nonStickyKey = key;
+                break;
+            }
+        }
+        for (final Key key : keyboard.getModifierKeys()) {
+            if (key.sticky) {
+                stickyKey = key;
+                break;
+            }
+        }
+
+        // Asserting existences of following keys is not the goal of this test, but this should work
+        // anyway.
+        assertNotNull(nonStickyKey);
+        assertNotNull(stickyKey);
+
+        // At first, both "pressed" and "on" must be false.
+        assertFalse(nonStickyKey.pressed);
+        assertFalse(stickyKey.pressed);
+        assertFalse(nonStickyKey.on);
+        assertFalse(stickyKey.on);
+
+        // Pressing the key must flip the "pressed" state only.
+        nonStickyKey.onPressed();
+        stickyKey.onPressed();
+        assertTrue(nonStickyKey.pressed);
+        assertTrue(stickyKey.pressed);
+        assertFalse(nonStickyKey.on);
+        assertFalse(stickyKey.on);
+
+        // Releasing the key inside the key area must flip the "pressed" state and toggle the "on"
+        // state if the key is marked as sticky.
+        nonStickyKey.onReleased(true /* inside */);
+        stickyKey.onReleased(true /* inside */);
+        assertFalse(nonStickyKey.pressed);
+        assertFalse(stickyKey.pressed);
+        assertFalse(nonStickyKey.on);
+        assertTrue(stickyKey.on);   // The key state is toggled.
+
+        // Pressing the key again must flip the "pressed" state only.
+        nonStickyKey.onPressed();
+        stickyKey.onPressed();
+        assertTrue(nonStickyKey.pressed);
+        assertTrue(stickyKey.pressed);
+        assertFalse(nonStickyKey.on);
+        assertTrue(stickyKey.on);
+
+        // Releasing the key inside the key area must flip the "pressed" state and toggle the "on"
+        // state if the key is marked as sticky hence we will be back to the initial state.
+        nonStickyKey.onReleased(true /* inside */);
+        stickyKey.onReleased(true /* inside */);
+        assertFalse(nonStickyKey.pressed);
+        assertFalse(stickyKey.pressed);
+        assertFalse(nonStickyKey.on);
+        assertFalse(stickyKey.on);
+
+        // Pressing then releasing the key outside the key area must not affect the "on" state.
+        nonStickyKey.onPressed();
+        stickyKey.onPressed();
+        nonStickyKey.onReleased(false /* inside */);
+        stickyKey.onReleased(false /* inside */);
+        assertFalse(nonStickyKey.pressed);
+        assertFalse(stickyKey.pressed);
+        assertFalse(nonStickyKey.on);
+        assertFalse(stickyKey.on);
+    }
+}
diff --git a/tests/inputmethod/src/android/view/inputmethod/cts/util/InputConnectionTestUtils.java b/tests/inputmethod/src/android/view/inputmethod/cts/util/InputConnectionTestUtils.java
new file mode 100644
index 0000000..3735c33
--- /dev/null
+++ b/tests/inputmethod/src/android/view/inputmethod/cts/util/InputConnectionTestUtils.java
@@ -0,0 +1,77 @@
+/*
+ * 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.view.inputmethod.cts.util;
+
+import android.text.Selection;
+import android.text.SpannableStringBuilder;
+
+public final class InputConnectionTestUtils {
+
+    /**
+     * A utility function to generate test string for input method APIs.  There are several
+     * pre-defined meta characters that are useful for unit tests.
+     *
+     * <p>Pre-defined meta characters:</p>
+     * <dl>
+     *     <dl>{@code [}</dl><dd>The text selection starts from here.</dd>
+     *     <dl>{@code ]}</dl><dd>The text selection ends at here.</dd>
+     *     <dl>{@code <}</dl><dd>Represents a high surrogate character.</dd>
+     *     <dl>{@code >}</dl><dd>Represents a low surrogate character.</dd>
+     * </ul>
+     *
+     * <p>Examples: {@code "012[3<>67]89"} will be converted to {@ode "0123HL6789"}, where
+     * {@code "H"} and {@code "L"} indicate certain high and low surrogate characters, respectively,
+     * with selecting {@code "3HL67"}.</p>
+     *
+     * @param formatString
+     * @return A {@link CharSequence} object with text selection specified by the meta characters.
+     */
+    public static CharSequence formatString(final String formatString) {
+        final String U1F427 = "\uD83D\uDC27";
+        final SpannableStringBuilder builder = new SpannableStringBuilder();
+        int selectionStart = -1;
+        int selectionEnd = -1;
+        for (int i = 0; i < formatString.length(); ++i) {
+            final Character c = formatString.charAt(i);
+            switch (c) {
+                case '[':
+                    selectionStart = builder.length();
+                    break;
+                case ']':
+                    selectionEnd = builder.length();
+                    break;
+                case '<':
+                    builder.append(U1F427.charAt(0));  // High surrogate
+                    break;
+                case '>':
+                    builder.append(U1F427.charAt(1));  // Low surrogate
+                    break;
+                default:
+                    builder.append(c);
+                    break;
+            }
+        }
+        if (selectionStart < 0) {
+            throw new UnsupportedOperationException("Selection marker '[' must be specified.");
+        }
+        if (selectionEnd < 0) {
+            throw new UnsupportedOperationException("Selection marker ']' must be specified.");
+        }
+        Selection.setSelection(builder, selectionStart, selectionEnd);
+        return builder;
+    }
+}
diff --git a/tests/sensor/src/android/hardware/cts/SensorDirectReportTest.java b/tests/sensor/src/android/hardware/cts/SensorDirectReportTest.java
new file mode 100644
index 0000000..edc71aa
--- /dev/null
+++ b/tests/sensor/src/android/hardware/cts/SensorDirectReportTest.java
@@ -0,0 +1,483 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.cts;
+
+import android.content.Context;
+import android.hardware.HardwareBuffer;
+import android.hardware.Sensor;
+import android.hardware.SensorAdditionalInfo;
+import android.hardware.SensorDirectChannel;
+import android.hardware.SensorEventCallback;
+import android.hardware.SensorManager;
+import android.hardware.cts.helpers.SensorCtsHelper;
+import android.os.MemoryFile;
+import android.util.Log;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Checks Sensor Direct Report functionality
+ *
+ * This testcase tests operation of:
+ *   - SensorManager.createDirectChannel()
+ *   - SensorManager.configureDirectReport()
+ *   - SensorDirectChannel.*
+ *   - Sensor.getHighestDirectReportRateLevel()
+ *   - Sensor.isDirectChannelTypeSupported()
+ */
+public class SensorDirectReportTest extends SensorTestCase {
+    private static final String TAG = "SensorDirectReportTest";
+    // nominal rates of each rate level supported
+    private static final float RATE_NORMAL_NOMINAL = 50;
+    private static final float RATE_FAST_NOMINAL = 200;
+    private static final float RATE_VERY_FAST_NOMINAL = 800;
+
+    // actuall value is allowed to be 55% to 220% of nominal value
+    private static final float FREQ_LOWER_BOUND = 0.55f;
+    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;
+
+    // test constants
+    private static final int REST_PERIOD_BEFORE_TEST_MILLISEC = 3000;
+    private static final int TEST_RUN_TIME_PERIOD_MILLISEC = 5000;
+    private static final int ALLOWED_SENSOR_INIT_TIME_MILLISEC = 500;
+    private static final int SENSORS_EVENT_SIZE = 104;
+    private static final int SHARED_MEMORY_SIZE = 2000 * SENSORS_EVENT_SIZE;
+    private static final float MERCY_FACTOR = 0.1f;
+
+    private boolean mNeedMemoryFile;
+    private MemoryFile mMemoryFile;
+    private boolean mNeedHardwareBuffer;
+    private HardwareBuffer mHardwareBuffer;
+    private byte[] mBuffer = new byte[SHARED_MEMORY_SIZE];
+
+    private SensorManager mSensorManager;
+    private SensorDirectChannel mChannel;
+
+    @Override
+    protected void setUp() throws Exception {
+        mSensorManager = (SensorManager) getContext().getSystemService(Context.SENSOR_SERVICE);
+
+        mNeedMemoryFile = isMemoryTypeNeeded(SensorDirectChannel.TYPE_ASHMEM);
+        mNeedHardwareBuffer = isMemoryTypeNeeded(SensorDirectChannel.TYPE_HARDWARE_BUFFER);
+
+        if (mNeedMemoryFile) {
+            mMemoryFile = allocateMemoryFile();
+        }
+
+        if (mNeedHardwareBuffer) {
+            mHardwareBuffer = allocateHardwareBuffer();
+        }
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        if (mChannel != null) {
+            mChannel.close();
+            mChannel = null;
+        }
+
+        if (mMemoryFile != null) {
+            mMemoryFile.close();
+            mMemoryFile = null;
+        }
+
+        if (mHardwareBuffer != null) {
+            mHardwareBuffer.destroy();
+            mHardwareBuffer = null;
+        }
+    }
+
+    public void testSharedMemoryAllocation() throws AssertionError {
+        assertTrue("allocating MemoryFile returned null",
+                !mNeedMemoryFile || mMemoryFile != null);
+        assertTrue("allocating HardwareBuffer returned null",
+                !mNeedHardwareBuffer || mHardwareBuffer != null);
+    }
+
+    public void testAccelerometerAshmemNormal() {
+        runSensorDirectReportTest(
+                Sensor.TYPE_ACCELEROMETER,
+                SensorDirectChannel.TYPE_ASHMEM,
+                SensorDirectChannel.RATE_NORMAL);
+    }
+
+    public void testGyroscopeAshmemNormal() {
+        runSensorDirectReportTest(
+                Sensor.TYPE_GYROSCOPE,
+                SensorDirectChannel.TYPE_ASHMEM,
+                SensorDirectChannel.RATE_NORMAL);
+    }
+
+    public void testMagneticFieldAshmemNormal() {
+        runSensorDirectReportTest(
+                Sensor.TYPE_MAGNETIC_FIELD,
+                SensorDirectChannel.TYPE_ASHMEM,
+                SensorDirectChannel.RATE_NORMAL);
+    }
+
+    public void testAccelerometerAshmemFast() {
+        runSensorDirectReportTest(
+                Sensor.TYPE_ACCELEROMETER,
+                SensorDirectChannel.TYPE_ASHMEM,
+                SensorDirectChannel.RATE_FAST);
+
+    }
+
+    public void testGyroscopeAshmemFast() {
+        runSensorDirectReportTest(
+                Sensor.TYPE_GYROSCOPE,
+                SensorDirectChannel.TYPE_ASHMEM,
+                SensorDirectChannel.RATE_FAST);
+    }
+
+    public void testMagneticFieldAshmemFast() {
+        runSensorDirectReportTest(
+                Sensor.TYPE_MAGNETIC_FIELD,
+                SensorDirectChannel.TYPE_ASHMEM,
+                SensorDirectChannel.RATE_FAST);
+    }
+
+    public void testAccelerometerAshmemVeryFast() {
+        runSensorDirectReportTest(
+                Sensor.TYPE_ACCELEROMETER,
+                SensorDirectChannel.TYPE_ASHMEM,
+                SensorDirectChannel.RATE_VERY_FAST);
+
+    }
+
+    public void testGyroscopeAshmemVeryFast() {
+        runSensorDirectReportTest(
+                Sensor.TYPE_GYROSCOPE,
+                SensorDirectChannel.TYPE_ASHMEM,
+                SensorDirectChannel.RATE_VERY_FAST);
+    }
+
+    public void testMagneticFieldAshmemVeryFast() {
+        runSensorDirectReportTest(
+                Sensor.TYPE_MAGNETIC_FIELD,
+                SensorDirectChannel.TYPE_ASHMEM,
+                SensorDirectChannel.RATE_VERY_FAST);
+    }
+
+    public void testAccelerometerHardwareBufferNormal() {
+        runSensorDirectReportTest(
+                Sensor.TYPE_ACCELEROMETER,
+                SensorDirectChannel.TYPE_HARDWARE_BUFFER,
+                SensorDirectChannel.RATE_NORMAL);
+    }
+
+    public void testGyroscopeHardwareBufferNormal() {
+        runSensorDirectReportTest(
+                Sensor.TYPE_GYROSCOPE,
+                SensorDirectChannel.TYPE_HARDWARE_BUFFER,
+                SensorDirectChannel.RATE_NORMAL);
+    }
+
+    public void testMagneticFieldHardwareBufferNormal() {
+        runSensorDirectReportTest(
+                Sensor.TYPE_MAGNETIC_FIELD,
+                SensorDirectChannel.TYPE_HARDWARE_BUFFER,
+                SensorDirectChannel.RATE_NORMAL);
+    }
+
+    public void testAccelerometerHardwareBufferFast() {
+        runSensorDirectReportTest(
+                Sensor.TYPE_ACCELEROMETER,
+                SensorDirectChannel.TYPE_HARDWARE_BUFFER,
+                SensorDirectChannel.RATE_FAST);
+    }
+
+    public void testGyroscopeHardwareBufferFast() {
+        runSensorDirectReportTest(
+                Sensor.TYPE_GYROSCOPE,
+                SensorDirectChannel.TYPE_HARDWARE_BUFFER,
+                SensorDirectChannel.RATE_FAST);
+    }
+
+    public void testMagneticFieldHardwareBufferFast() {
+        runSensorDirectReportTest(
+                Sensor.TYPE_MAGNETIC_FIELD,
+                SensorDirectChannel.TYPE_HARDWARE_BUFFER,
+                SensorDirectChannel.RATE_FAST);
+    }
+
+    public void testAccelerometerHardwareBufferVeryFast() {
+        runSensorDirectReportTest(
+                Sensor.TYPE_ACCELEROMETER,
+                SensorDirectChannel.TYPE_HARDWARE_BUFFER,
+                SensorDirectChannel.RATE_VERY_FAST);
+    }
+
+    public void testGyroscopeHardwareBufferVeryFast() {
+        runSensorDirectReportTest(
+                Sensor.TYPE_GYROSCOPE,
+                SensorDirectChannel.TYPE_HARDWARE_BUFFER,
+                SensorDirectChannel.RATE_VERY_FAST);
+    }
+
+    public void testMagneticFieldHardwareBufferVeryFast() {
+        runSensorDirectReportTest(
+                Sensor.TYPE_MAGNETIC_FIELD,
+                SensorDirectChannel.TYPE_HARDWARE_BUFFER,
+                SensorDirectChannel.RATE_VERY_FAST);
+    }
+
+    private void runSensorDirectReportTest(int sensorType, int memType, int rateLevel)
+            throws AssertionError {
+        Sensor s = mSensorManager.getDefaultSensor(sensorType);
+        if (s == null
+                || s.getHighestDirectReportRateLevel() < rateLevel
+                || !s.isDirectChannelTypeSupported(memType)) {
+            return;
+        }
+
+        mChannel = null;
+        try {
+            switch(memType) {
+                case SensorDirectChannel.TYPE_ASHMEM:
+                    assertTrue("MemoryFile is null", mMemoryFile != null);
+                    mChannel = mSensorManager.createDirectChannel(mMemoryFile);
+                    assertTrue("createDirectChannel(MemoryFile) failed", mChannel != null);
+                    break;
+                case SensorDirectChannel.TYPE_HARDWARE_BUFFER:
+                    assertTrue("HardwareBuffer is null", mHardwareBuffer != null);
+                    mChannel = mSensorManager.createDirectChannel(mHardwareBuffer);
+                    assertTrue("createDirectChannel(HardwareBuffer) failed", mChannel != null);
+                    break;
+                default:
+                    Log.e(TAG, "Specified illegal memory type " + memType);
+                    return;
+            }
+            assertTrue("Shared memory is not formatted", isSharedMemoryFormatted(memType));
+            waitBeforeStartSensor();
+
+            int token = mSensorManager.configureDirectChannel(mChannel, s, rateLevel);
+            assertTrue("configure direct mChannel failed", token > 0);
+
+            waitSensorCollection();
+
+            //stop sensor and analyze content
+            mSensorManager.configureDirectChannel(mChannel, s, SensorDirectChannel.RATE_STOP);
+            checkSharedMemoryContent(s, memType, rateLevel, token);
+        } finally {
+            if (mChannel != null) {
+                mChannel.close();
+                mChannel = null;
+            }
+        }
+    }
+
+    private void waitBeforeStartSensor() {
+        // wait for sensor system to come to a rest after previous test to avoid flakiness.
+        try {
+            SensorCtsHelper.sleep(REST_PERIOD_BEFORE_TEST_MILLISEC, TimeUnit.MILLISECONDS);
+        } catch (InterruptedException e) {
+            Thread.currentThread().interrupt();
+        }
+    }
+
+    private void waitSensorCollection() {
+        // wait for sensor system to come to a rest after previous test to avoid flakiness.
+        try {
+            SensorCtsHelper.sleep(TEST_RUN_TIME_PERIOD_MILLISEC, TimeUnit.MILLISECONDS);
+        } catch (InterruptedException e) {
+            Thread.currentThread().interrupt();
+        }
+    }
+
+    private MemoryFile allocateMemoryFile() {
+        MemoryFile memFile = null;
+        try {
+            memFile = new MemoryFile("Sensor Channel", SHARED_MEMORY_SIZE);
+        } catch (IOException e) {
+            Log.e(TAG, "IOException when allocating MemoryFile");
+        }
+        return memFile;
+    }
+
+    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
+                    | HardwareBuffer.USAGE0_SENSOR_DIRECT_DATA);
+        return hardwareBuffer;
+    }
+
+    private boolean isMemoryTypeNeeded(int memType) {
+        List<Sensor> sensorList = mSensorManager.getSensorList(Sensor.TYPE_ALL);
+        for (Sensor s : sensorList) {
+            if (s.isDirectChannelTypeSupported(memType)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private boolean isSharedMemoryFormatted(int memType) {
+        if (memType == SensorDirectChannel.TYPE_ASHMEM) {
+            if (!readMemoryFileContent()) {
+                return false;
+            }
+        } else {
+            // Android API only support memory file read back
+            return true;
+        }
+
+        for (byte b : mBuffer) {
+            if (b != 0) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    private void checkSharedMemoryContent(Sensor s, int memType, int rateLevel, int token) {
+        if (memType == SensorDirectChannel.TYPE_ASHMEM) {
+            assertTrue("read MemoryFile content failed", readMemoryFileContent());
+        } else {
+            // Android API only support memory file read back
+            return;
+        }
+
+        int offset = 0;
+        int nextSerial = 1;
+        DirectReportSensorEvent e = new DirectReportSensorEvent();
+        while (offset <= SHARED_MEMORY_SIZE - SENSORS_EVENT_SIZE) {
+            parseSensorEvent(mBuffer, offset, e);
+
+            if (e.serial == 0) {
+                // reaches end of events
+                break;
+            }
+
+            assertTrue("incorrect size " + e.size + "  at offset " + offset,
+                    e.size == SENSORS_EVENT_SIZE);
+            assertTrue("incorrect token " + e.token + " at offset " + offset,
+                    e.token == token);
+            assertTrue("incorrect serial " + e.serial + " at offset " + offset,
+                    e.serial == nextSerial);
+            assertTrue("incorrect type " + e.type + " offset " + offset,
+                    e.type == s.getType());
+
+            switch(s.getType()) {
+                case Sensor.TYPE_ACCELEROMETER:
+                    double accNorm = Math.sqrt(e.x * e.x + e.y * e.y + e.z * e.z);
+                    assertTrue("incorrect gravity norm " + accNorm + " at offset " + offset,
+                            accNorm < GRAVITY_MAX && accNorm > GRAVITY_MIN);
+                    break;
+                case Sensor.TYPE_GYROSCOPE:
+                    double gyroNorm = Math.sqrt(e.x * e.x + e.y * e.y + e.z * e.z);
+                    assertTrue("gyro norm too large (" + gyroNorm + ") at offset " + offset,
+                            gyroNorm < GYRO_NORM_MAX);
+                    break;
+            }
+
+            ++nextSerial;
+            offset += SENSORS_EVENT_SIZE;
+        }
+
+        int nEvents = nextSerial - 1;
+        float nominalFreq = 0;
+
+        switch (rateLevel) {
+            case SensorDirectChannel.RATE_NORMAL:
+                nominalFreq = RATE_NORMAL_NOMINAL;
+                break;
+            case SensorDirectChannel.RATE_FAST:
+                nominalFreq = RATE_FAST_NOMINAL;
+                break;
+            case SensorDirectChannel.RATE_VERY_FAST:
+                nominalFreq = RATE_VERY_FAST_NOMINAL;
+                break;
+        }
+
+        if (nominalFreq != 0) {
+            int minEvents;
+            int maxEvents;
+            minEvents = (int) Math.floor(
+                    nominalFreq
+                    * FREQ_LOWER_BOUND
+                    * (TEST_RUN_TIME_PERIOD_MILLISEC - ALLOWED_SENSOR_INIT_TIME_MILLISEC)
+                    * (1 - MERCY_FACTOR)
+                    / 1000);
+            maxEvents = (int) Math.ceil(
+                    nominalFreq
+                    * FREQ_UPPER_BOUND
+                    * (TEST_RUN_TIME_PERIOD_MILLISEC - ALLOWED_SENSOR_INIT_TIME_MILLISEC)
+                    * (1 + MERCY_FACTOR)
+                    / 1000);
+
+            assertTrue("nEvent is " + nEvents + " not between " + minEvents + " and " + maxEvents,
+                    nEvents >= minEvents && nEvents <=maxEvents);
+        }
+    }
+
+    private boolean readMemoryFileContent() {
+        try {
+            if (mMemoryFile.readBytes(mBuffer, 0, 0, SHARED_MEMORY_SIZE)
+                    != SHARED_MEMORY_SIZE) {
+                Log.e(TAG, "cannot read entire MemoryFile");
+                return false;
+            }
+        } catch (IOException e) {
+            Log.e(TAG, "accessing MemoryFile cause IOException");
+            return false;
+        }
+        return true;
+    }
+
+    private class DirectReportSensorEvent {
+        int size;
+        int token;
+        int type;
+        int serial;
+        long ts;
+        float x;
+        float y;
+        float z;
+    };
+
+    // parse sensors_event_t and fill information into DirectReportSensorEvent
+    private static void parseSensorEvent(byte [] buf, int offset, DirectReportSensorEvent ev) {
+        ByteBuffer b = ByteBuffer.wrap(buf, offset, SENSORS_EVENT_SIZE);
+        b.order(ByteOrder.nativeOrder());
+
+        ev.size = b.getInt();
+        ev.token = b.getInt();
+        ev.type = b.getInt();
+        ev.serial = b.getInt();
+        ev.ts = b.getLong();
+        ev.x = b.getFloat();
+        ev.y = b.getFloat();
+        ev.z = b.getFloat();
+    }
+}
diff --git a/tests/tests/animation/src/android/animation/cts/AnimatorSetTest.java b/tests/tests/animation/src/android/animation/cts/AnimatorSetTest.java
index 385f62f..700aff2 100644
--- a/tests/tests/animation/src/android/animation/cts/AnimatorSetTest.java
+++ b/tests/tests/animation/src/android/animation/cts/AnimatorSetTest.java
@@ -15,10 +15,14 @@
  */
 package android.animation.cts;
 
+import static com.android.compatibility.common.util.CtsMockitoUtils.within;
+
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertSame;
 import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
@@ -57,6 +61,7 @@
     private ObjectAnimator yAnimator;
     private ObjectAnimator xAnimator;
     Set<Integer> identityHashes = new HashSet<>();
+    private static final float EPSILON = 0.001f;
 
     @Rule
     public ActivityTestRule<AnimationActivity> mActivityRule =
@@ -147,6 +152,37 @@
         for (int i = 0; i < listeners.length; i++) {
             assertTrue(listeners[i].mEndIsCalled);
         }
+
+        // Now reverse the animations and verify whether the play order is reversed.
+        for (int i = 0; i < animators.length; i++) {
+            if (i == animators.length - 1) {
+                listeners[i] = new MyListener();
+            } else {
+                final int current = i;
+                listeners[i] = new MyListener() {
+                    @Override
+                    public void onAnimationStart(Animator anim) {
+                        super.onAnimationStart(anim);
+                        // Check that the previous animator has finished.
+                        assertTrue(listeners[current + 1].mEndIsCalled);
+                    }
+                };
+            }
+            animators[i].removeAllListeners();
+            animators[i].addListener(listeners[i]);
+        }
+
+        mActivityRule.runOnUiThread(() -> {
+            set.reverse();
+            startLatch.countDown();
+        });
+
+        // Set timeout to 100ms, if current count reaches 0 before the timeout, startLatch.await(..)
+        // will return immediately.
+        assertTrue(startLatch.await(100, TimeUnit.MILLISECONDS));
+        assertTrue(set.isRunning());
+        assertTrue(endLatch.await(totalDuration * 2, TimeUnit.MILLISECONDS));
+
     }
 
     @Test
@@ -473,6 +509,112 @@
         assertSame(animator2.getInterpolator(), clone2.getInterpolator());
     }
 
+    /**
+     * Testing seeking in an AnimatorSet containing sequential animators.
+     */
+    @Test
+    public void testSeeking() throws Throwable {
+        final AnimatorSet set = new AnimatorSet();
+        final ValueAnimator a1 = ValueAnimator.ofFloat(0f, 150f);
+        a1.setDuration(150);
+        final ValueAnimator a2 = ValueAnimator.ofFloat(150f, 250f);
+        a2.setDuration(100);
+        final ValueAnimator a3 = ValueAnimator.ofFloat(250f, 300f);
+        a3.setDuration(50);
+
+        a1.setInterpolator(null);
+        a2.setInterpolator(null);
+        a3.setInterpolator(null);
+
+        set.playSequentially(a1, a2, a3);
+
+        set.setCurrentPlayTime(100);
+        assertEquals(100f, (Float) a1.getAnimatedValue(), EPSILON);
+        assertEquals(150f, (Float) a2.getAnimatedValue(), EPSILON);
+        assertEquals(250f, (Float) a3.getAnimatedValue(), EPSILON);
+
+        set.setCurrentPlayTime(280);
+        assertEquals(150f, (Float) a1.getAnimatedValue(), EPSILON);
+        assertEquals(250f, (Float) a2.getAnimatedValue(), EPSILON);
+        assertEquals(280f, (Float) a3.getAnimatedValue(), EPSILON);
+
+        AnimatorListenerAdapter setListener = new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                super.onAnimationEnd(animation);
+                assertEquals(150f, (Float) a1.getAnimatedValue(), EPSILON);
+                assertEquals(250f, (Float) a2.getAnimatedValue(), EPSILON);
+                assertEquals(300f, (Float) a3.getAnimatedValue(), EPSILON);
+
+            }
+        };
+        AnimatorListenerAdapter mockListener = mock(AnimatorListenerAdapter.class);
+        set.addListener(setListener);
+        set.addListener(mockListener);
+        mActivityRule.runOnUiThread(() -> {
+            set.start();
+        });
+
+        verify(mockListener, within(300)).onAnimationEnd(set, false);
+
+        // Seek after a run to the middle-ish, and verify the first animator is at the end
+        // value and the 3rd at beginning value, and the 2nd animator is at the seeked value.
+        set.setCurrentPlayTime(200);
+        assertEquals(150f, (Float) a1.getAnimatedValue(), EPSILON);
+        assertEquals(200f, (Float) a2.getAnimatedValue(), EPSILON);
+        assertEquals(250f, (Float) a3.getAnimatedValue(), EPSILON);
+    }
+
+    /**
+     * Testing seeking in an AnimatorSet containing infinite animators.
+     */
+    @Test
+    public void testSeekingInfinite() {
+        final AnimatorSet set = new AnimatorSet();
+        final ValueAnimator a1 = ValueAnimator.ofFloat(0f, 100f);
+        a1.setDuration(100);
+        final ValueAnimator a2 = ValueAnimator.ofFloat(100f, 200f);
+        a2.setDuration(100);
+        a2.setRepeatCount(ValueAnimator.INFINITE);
+        a2.setRepeatMode(ValueAnimator.RESTART);
+
+        final ValueAnimator a3 = ValueAnimator.ofFloat(100f, 200f);
+        a3.setDuration(100);
+        a3.setRepeatCount(ValueAnimator.INFINITE);
+        a3.setRepeatMode(ValueAnimator.REVERSE);
+
+        a1.setInterpolator(null);
+        a2.setInterpolator(null);
+        a3.setInterpolator(null);
+        set.play(a1).before(a2);
+        set.play(a1).before(a3);
+
+        set.setCurrentPlayTime(50);
+        assertEquals(50f, (Float) a1.getAnimatedValue(), EPSILON);
+        assertEquals(100f, (Float) a2.getAnimatedValue(), EPSILON);
+        assertEquals(100f, (Float) a3.getAnimatedValue(), EPSILON);
+
+        set.setCurrentPlayTime(100);
+        assertEquals(100f, (Float) a1.getAnimatedValue(), EPSILON);
+        assertEquals(100f, (Float) a2.getAnimatedValue(), EPSILON);
+        assertEquals(100f, (Float) a3.getAnimatedValue(), EPSILON);
+
+        // Seek to the 1st iteration of the infinite repeat animators, and they should have the
+        // same value.
+        set.setCurrentPlayTime(180);
+        assertEquals(100f, (Float) a1.getAnimatedValue(), EPSILON);
+        assertEquals(180f, (Float) a2.getAnimatedValue(), EPSILON);
+        assertEquals(180f, (Float) a3.getAnimatedValue(), EPSILON);
+
+        // Seek to the 2nd iteration of the infinite repeat animators, and they should have
+        // different values as they have different repeat mode.
+        set.setCurrentPlayTime(280);
+        assertEquals(100f, (Float) a1.getAnimatedValue(), EPSILON);
+        assertEquals(180f, (Float) a2.getAnimatedValue(), EPSILON);
+        assertEquals(120f, (Float) a3.getAnimatedValue(), EPSILON);
+
+    }
+
     @Test
     public void testNotifiesAfterEnd() throws Throwable {
         final ValueAnimator animator = ValueAnimator.ofFloat(0, 1);
@@ -498,7 +640,6 @@
             animatorSet.start();
             animator.end();
             assertFalse(animator.isStarted());
-            assertFalse(animatorSet.isStarted());
         });
     }
 
@@ -515,7 +656,7 @@
         int y = 2;
     }
 
-    class MyListener extends AnimatorListenerAdapter {
+    static class MyListener extends AnimatorListenerAdapter {
         boolean mStartIsCalled = false;
         boolean mEndIsCalled = false;
 
diff --git a/tests/tests/animation/src/android/animation/cts/ValueAnimatorTest.java b/tests/tests/animation/src/android/animation/cts/ValueAnimatorTest.java
index 605e1c1..6094362 100644
--- a/tests/tests/animation/src/android/animation/cts/ValueAnimatorTest.java
+++ b/tests/tests/animation/src/android/animation/cts/ValueAnimatorTest.java
@@ -231,10 +231,12 @@
         final float seekFraction = 0.2f;
         final CountDownLatch frameUpdateLatch = new CountDownLatch(1);
 
+        final AnimatorSetTest.MyListener myListener = new AnimatorSetTest.MyListener();
         final ValueAnimator anim  = ValueAnimator.ofFloat(0, 1).setDuration(duration);
         anim.setInterpolator(null);
         final Animator.AnimatorListener listener = mock(Animator.AnimatorListener.class);
         anim.addListener(listener);
+        anim.addListener(myListener);
         mActivityRule.runOnUiThread(() -> {
             anim.start();
             anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@@ -256,7 +258,9 @@
             anim.setCurrentPlayTime(currentPlayTime);
         });
         assertTrue(frameUpdateLatch.await(100, TimeUnit.MILLISECONDS));
-        verify(listener, within(200)).onAnimationEnd(anim);
+        verify(listener, within(200)).onAnimationEnd(anim, false);
+        // Also make sure the onAnimationEnd(anim) is called.
+        assertTrue(myListener.mEndIsCalled);
     }
 
     @Test
@@ -309,6 +313,7 @@
 
     @Test
     public void testUpdateListeners() throws Throwable {
+        final AnimatorSetTest.MyListener myListener = new AnimatorSetTest.MyListener();
         ValueAnimator.AnimatorUpdateListener l1 = mock(ValueAnimator.AnimatorUpdateListener.class);
         ValueAnimator.AnimatorUpdateListener l2 = mock(ValueAnimator.AnimatorUpdateListener.class);
         ValueAnimator.AnimatorUpdateListener l3 = mock(ValueAnimator.AnimatorUpdateListener.class);
@@ -327,13 +332,16 @@
         a1.removeUpdateListener(l3);
 
         a1.addListener(listener);
+        a1.addListener(myListener);
 
         mActivityRule.runOnUiThread(() -> {
             a1.start();
         });
 
         // Wait for the anim to finish.
-        verify(listener, within(200)).onAnimationEnd(a1);
+        verify(listener, within(200)).onAnimationEnd(a1, false);
+        // Also make sure the onAnimationEnd(anim) is called.
+        assertTrue(myListener.mEndIsCalled);
 
         verify(l1, times(0)).onAnimationUpdate(a1);
         verify(l2, times(0)).onAnimationUpdate(a1);
@@ -344,6 +352,7 @@
     @Test
     public void testValuesSetterAndGetter() throws Throwable {
 
+        final AnimatorSetTest.MyListener myListener = new AnimatorSetTest.MyListener();
         ValueAnimator a2 = ValueAnimator.ofPropertyValuesHolder();
         PropertyValuesHolder p1 = PropertyValuesHolder.ofFloat("scaleX", 0f, 1f);
         PropertyValuesHolder p2 = PropertyValuesHolder.ofFloat("scaleY", 1f, 2f);
@@ -366,12 +375,15 @@
         });
         AnimatorListenerAdapter l1 = mock(AnimatorListenerAdapter.class);
         a1.addListener(l1);
+        a1.addListener(myListener);
 
         mActivityRule.runOnUiThread(() -> {
             a1.start();
         });
 
-        verify(l1, within(200)).onAnimationEnd(a1);
+        verify(l1, within(200)).onAnimationEnd(a1, false);
+        // Also make sure the onAnimationEnd(anim) is called.
+        assertTrue(myListener.mEndIsCalled);
     }
 
     @Test
@@ -386,6 +398,7 @@
             }
         };
 
+        final AnimatorSetTest.MyListener myListener = new AnimatorSetTest.MyListener();
         ValueAnimator a1 = new ValueAnimator();
         a1.setDuration(50);
         a1.setObjectValues(new PointF(0, 0), new PointF(1, 1));
@@ -400,11 +413,14 @@
         });
         AnimatorListenerAdapter l1 = mock(AnimatorListenerAdapter.class);
         a1.addListener(l1);
+        a1.addListener(myListener);
         mActivityRule.runOnUiThread(() -> {
             a1.start();
         });
 
-        verify(l1, within(200)).onAnimationEnd(a1);
+        verify(l1, within(200)).onAnimationEnd(a1, false);
+        // Also make sure the onAnimationEnd(anim) is called.
+        assertTrue(myListener.mEndIsCalled);
     }
 
     @Test
@@ -572,7 +588,7 @@
         final Animator.AnimatorListener watcher = mock(Animator.AnimatorListener.class);
         animator.addListener(watcher);
         mActivityRule.runOnUiThread(animator::start);
-        verify(watcher, times(1)).onAnimationStart(animator);
+        verify(watcher, times(1)).onAnimationStart(animator, false);
         assertTrue(((Float)animator.getAnimatedValue()) >= 0.5f);
         assertTrue(animator.getAnimatedFraction() >= 0.5f);
         mActivityRule.runOnUiThread(animator::cancel);
diff --git a/tests/tests/colormode/Android.mk b/tests/tests/colormode/Android.mk
new file mode 100644
index 0000000..d187069
--- /dev/null
+++ b/tests/tests/colormode/Android.mk
@@ -0,0 +1,38 @@
+# Copyright (C) 2011 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+
+LOCAL_DEX_PREOPT := false
+
+LOCAL_PROGUARD_ENABLED := disabled
+
+LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+# Tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts
+
+LOCAL_PACKAGE_NAME := CtsColorModeTestCases
+
+LOCAL_SDK_VERSION := current
+
+include $(BUILD_CTS_PACKAGE)
diff --git a/tests/tests/colormode/AndroidManifest.xml b/tests/tests/colormode/AndroidManifest.xml
new file mode 100644
index 0000000..be2c457
--- /dev/null
+++ b/tests/tests/colormode/AndroidManifest.xml
@@ -0,0 +1,38 @@
+<!--
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+        package="android.colormode.cts">
+
+    <uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
+
+    <application>
+        <uses-library android:name="android.test.runner" />
+        <activity android:name="android.colormode.DefaultColorModeActivity" />
+        <activity android:name="android.colormode.WideColorModeActivity"
+                android:colorMode="wideColorGamut" />
+        <activity android:name="android.colormode.AttributeWideColorModeActivity" />
+    </application>
+
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+            android:targetPackage="android.colormode.cts"
+            android:label="Tests for the Color Mode APIs." >
+        <meta-data android:name="listener"
+                android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
+
+</manifest>
+
diff --git a/tests/tests/colormode/AndroidTest.xml b/tests/tests/colormode/AndroidTest.xml
new file mode 100644
index 0000000..3f797aa
--- /dev/null
+++ b/tests/tests/colormode/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 Color Mode test cases">
+    <target_preparer class="com.android.compatibility.common.tradefed.targetprep.ApkInstaller">
+        <option name="cleanup-apks" value="true" />
+        <option name="test-file-name" value="CtsColorModeTestCases.apk" />
+    </target_preparer>
+    <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+        <option name="package" value="android.colormode.cts" />
+        <option name="runtime-hint" value="1m" />
+    </test>
+</configuration>
diff --git a/tests/tests/colormode/src/android/colormode/AttributeWideColorModeActivity.java b/tests/tests/colormode/src/android/colormode/AttributeWideColorModeActivity.java
new file mode 100644
index 0000000..e78c1fb
--- /dev/null
+++ b/tests/tests/colormode/src/android/colormode/AttributeWideColorModeActivity.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.colormode;
+
+import android.app.Activity;
+import android.content.pm.ActivityInfo;
+import android.os.Bundle;
+
+public class AttributeWideColorModeActivity extends Activity {
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        getWindow().setColorMode(ActivityInfo.COLOR_MODE_WIDE_COLOR_GAMUT);
+    }
+}
diff --git a/tests/tests/colormode/src/android/colormode/DefaultColorModeActivity.java b/tests/tests/colormode/src/android/colormode/DefaultColorModeActivity.java
new file mode 100644
index 0000000..c0906ec
--- /dev/null
+++ b/tests/tests/colormode/src/android/colormode/DefaultColorModeActivity.java
@@ -0,0 +1,22 @@
+/*
+ * 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.colormode;
+
+import android.app.Activity;
+
+public class DefaultColorModeActivity extends Activity {
+}
diff --git a/tests/tests/colormode/src/android/colormode/WideColorModeActivity.java b/tests/tests/colormode/src/android/colormode/WideColorModeActivity.java
new file mode 100644
index 0000000..ff297d8
--- /dev/null
+++ b/tests/tests/colormode/src/android/colormode/WideColorModeActivity.java
@@ -0,0 +1,22 @@
+/*
+ * 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.colormode;
+
+import android.app.Activity;
+
+public class WideColorModeActivity extends Activity {
+}
diff --git a/tests/tests/colormode/src/android/colormode/cts/AttributeWideColorModeTest.java b/tests/tests/colormode/src/android/colormode/cts/AttributeWideColorModeTest.java
new file mode 100644
index 0000000..a129b79
--- /dev/null
+++ b/tests/tests/colormode/src/android/colormode/cts/AttributeWideColorModeTest.java
@@ -0,0 +1,58 @@
+/*
+ * 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.colormode.cts;
+
+import android.app.Activity;
+import android.colormode.AttributeWideColorModeActivity;
+import android.content.pm.ActivityInfo;
+import android.content.pm.PackageManager;
+import android.support.test.filters.MediumTest;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+import android.view.Window;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import static org.junit.Assert.assertEquals;
+
+@MediumTest
+@RunWith(AndroidJUnit4.class)
+public class AttributeWideColorModeTest {
+    @Rule
+    public ActivityTestRule<AttributeWideColorModeActivity> mActivityRule =
+            new ActivityTestRule<>(AttributeWideColorModeActivity.class);
+    private Activity mActivity;
+
+    @Before
+    public void setup() {
+        mActivity = mActivityRule.getActivity();
+    }
+
+    @Test
+    public void testDefaultColorMode() throws Exception {
+        PackageManager pm = mActivity.getPackageManager();
+
+        ActivityInfo info = pm.getActivityInfo(mActivity.getComponentName(), 0);
+        assertEquals(ActivityInfo.COLOR_MODE_DEFAULT, info.colorMode);
+
+        Window window = mActivity.getWindow();
+        assertEquals(ActivityInfo.COLOR_MODE_WIDE_COLOR_GAMUT,
+                window.getAttributes().getColorMode());
+    }
+}
diff --git a/tests/tests/colormode/src/android/colormode/cts/DefaultColorModeTest.java b/tests/tests/colormode/src/android/colormode/cts/DefaultColorModeTest.java
new file mode 100644
index 0000000..06273eb
--- /dev/null
+++ b/tests/tests/colormode/src/android/colormode/cts/DefaultColorModeTest.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.colormode.cts;
+
+import android.colormode.DefaultColorModeActivity;
+import android.app.Activity;
+import android.content.pm.ActivityInfo;
+import android.content.pm.PackageManager;
+import android.support.test.filters.MediumTest;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+import android.view.Window;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import static org.junit.Assert.assertEquals;
+
+@MediumTest
+@RunWith(AndroidJUnit4.class)
+public class DefaultColorModeTest {
+    @Rule
+    public ActivityTestRule<DefaultColorModeActivity> mActivityRule =
+            new ActivityTestRule<>(DefaultColorModeActivity.class);
+    private Activity mActivity;
+
+    @Before
+    public void setup() {
+        mActivity = mActivityRule.getActivity();
+    }
+
+    @Test
+    public void testDefaultColorMode() throws Exception {
+        PackageManager pm = mActivity.getPackageManager();
+
+        ActivityInfo info = pm.getActivityInfo(mActivity.getComponentName(), 0);
+        assertEquals(ActivityInfo.COLOR_MODE_DEFAULT, info.colorMode);
+
+        Window window = mActivity.getWindow();
+        assertEquals(ActivityInfo.COLOR_MODE_DEFAULT, window.getAttributes().getColorMode());
+    }
+}
diff --git a/tests/tests/colormode/src/android/colormode/cts/WideColorModeTest.java b/tests/tests/colormode/src/android/colormode/cts/WideColorModeTest.java
new file mode 100644
index 0000000..64f37c6
--- /dev/null
+++ b/tests/tests/colormode/src/android/colormode/cts/WideColorModeTest.java
@@ -0,0 +1,58 @@
+/*
+ * 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.colormode.cts;
+
+import android.app.Activity;
+import android.colormode.WideColorModeActivity;
+import android.content.pm.ActivityInfo;
+import android.content.pm.PackageManager;
+import android.support.test.filters.MediumTest;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+import android.view.Window;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import static org.junit.Assert.assertEquals;
+
+@MediumTest
+@RunWith(AndroidJUnit4.class)
+public class WideColorModeTest {
+    @Rule
+    public ActivityTestRule<WideColorModeActivity> mActivityRule =
+            new ActivityTestRule<>(WideColorModeActivity.class);
+    private Activity mActivity;
+
+    @Before
+    public void setup() {
+        mActivity = mActivityRule.getActivity();
+    }
+
+    @Test
+    public void testDefaultColorMode() throws Exception {
+        PackageManager pm = mActivity.getPackageManager();
+
+        ActivityInfo info = pm.getActivityInfo(mActivity.getComponentName(), 0);
+        assertEquals(ActivityInfo.COLOR_MODE_WIDE_COLOR_GAMUT, info.colorMode);
+
+        Window window = mActivity.getWindow();
+        assertEquals(ActivityInfo.COLOR_MODE_WIDE_COLOR_GAMUT,
+                window.getAttributes().getColorMode());
+    }
+}
diff --git a/tests/tests/database/src/android/database/cts/DatabaseUtilsTest.java b/tests/tests/database/src/android/database/cts/DatabaseUtilsTest.java
index 2f58702..9b441a0 100644
--- a/tests/tests/database/src/android/database/cts/DatabaseUtilsTest.java
+++ b/tests/tests/database/src/android/database/cts/DatabaseUtilsTest.java
@@ -64,6 +64,8 @@
                 "name TEXT, age INTEGER, address TEXT);");
         mDatabase.execSQL(
                 "CREATE TABLE blob_test (_id INTEGER PRIMARY KEY, name TEXT, data BLOB)");
+        mDatabase.execSQL(
+                "CREATE TABLE boolean_test (_id INTEGER PRIMARY KEY, value BOOLEAN)");
     }
 
     @Override
@@ -264,6 +266,22 @@
         assertEquals("Mike", (String) contentValues.get("name"));
         assertEquals("20", (String) contentValues.get("age"));
         assertEquals("LA", (String) contentValues.get("address"));
+
+        mDatabase.execSQL("INSERT INTO boolean_test (value)" +
+                " VALUES (0);");
+        mDatabase.execSQL("INSERT INTO boolean_test (value)" +
+                " VALUES (1);");
+        cursor = mDatabase.query("boolean_test", new String[] {"value"},
+                null, null, null, null, null);
+        assertNotNull(cursor);
+
+        contentValues = new ContentValues();
+        cursor.moveToNext();
+        DatabaseUtils.cursorRowToContentValues(cursor, contentValues);
+        assertFalse(contentValues.getAsBoolean("value"));
+        cursor.moveToNext();
+        DatabaseUtils.cursorRowToContentValues(cursor, contentValues);
+        assertTrue(contentValues.getAsBoolean("value"));
     }
 
     public void testCursorStringToContentValues() {
diff --git a/tests/tests/graphics/AndroidManifest.xml b/tests/tests/graphics/AndroidManifest.xml
index c6cf40f..19dcf3a 100644
--- a/tests/tests/graphics/AndroidManifest.xml
+++ b/tests/tests/graphics/AndroidManifest.xml
@@ -50,6 +50,8 @@
 
         <activity android:name="android.graphics.drawable.cts.DrawableStubActivity"
                   android:theme="@style/WhiteBackgroundNoWindowAnimation"/>
+
+        <activity android:name="android.graphics.fonts.cts.MockActivity"/>
     </application>
 
     <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
diff --git a/tests/tests/graphics/src/android/graphics/cts/TypefaceTest.java b/tests/tests/graphics/src/android/graphics/cts/TypefaceTest.java
index 8c16193..6edb064 100644
--- a/tests/tests/graphics/src/android/graphics/cts/TypefaceTest.java
+++ b/tests/tests/graphics/src/android/graphics/cts/TypefaceTest.java
@@ -147,6 +147,18 @@
         Typeface.createFromAsset(null, null);
     }
 
+    @Test(expected=NullPointerException.class)
+    public void testCreateFromAssetNullPath() {
+        // input abnormal params.
+        Typeface.createFromAsset(mContext.getAssets(), null);
+    }
+
+    @Test(expected=RuntimeException.class)
+    public void testCreateFromAssetInvalidPath() {
+        // input abnormal params.
+        Typeface.createFromAsset(mContext.getAssets(), "invalid path");
+    }
+
     @Test
     public void testCreateFromAsset() {
         Typeface typeface = Typeface.createFromAsset(mContext.getAssets(), "samplefont.ttf");
@@ -166,12 +178,24 @@
         assertNotNull(typeface);
     }
 
+    @Test(expected=RuntimeException.class)
+    public void testCreateFromFileWithInvalidPath() throws IOException {
+        File file = new File("/invalid/path");
+        Typeface.createFromFile(file);
+    }
+
     @Test(expected=NullPointerException.class)
     public void testCreateFromFileByFileNameNull() throws IOException {
         // input abnormal params.
         Typeface.createFromFile((String) null);
     }
 
+    @Test(expected=RuntimeException.class)
+    public void testCreateFromFileByInvalidFileName() throws IOException {
+        // input abnormal params.
+        Typeface.createFromFile("/invalid/path");
+    }
+
     @Test
     public void testCreateFromFileByFileName() throws IOException {
         Typeface typeface = Typeface.createFromFile(obtainPath());
diff --git a/tests/tests/graphics/src/android/graphics/drawable/cts/MaskableIconDrawableTest.java b/tests/tests/graphics/src/android/graphics/drawable/cts/MaskableIconDrawableTest.java
new file mode 100644
index 0000000..311e7f4
--- /dev/null
+++ b/tests/tests/graphics/src/android/graphics/drawable/cts/MaskableIconDrawableTest.java
@@ -0,0 +1,119 @@
+/*
+ * 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.drawable.cts;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import android.content.res.Resources;
+import android.graphics.Color;
+import android.graphics.Path;
+import android.graphics.Path.Direction;
+import android.graphics.RectF;
+import android.graphics.Region;
+import android.graphics.Region.Op;
+import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.MaskableIconDrawable;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.util.PathParser;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class MaskableIconDrawableTest {
+
+    private static final double SAFEZONE_INSET = .1;
+    private static final double DELTA = .01f;
+    private Path mMask = new Path();
+    private Path mSafeZone = new Path();
+    private ColorDrawable mBlueDrawable;
+    private ColorDrawable mRedDrawable;
+    private MaskableIconDrawable mDrawable;
+
+    @Before
+    public void setup() {
+        mBlueDrawable = new ColorDrawable(Color.BLUE);
+        mRedDrawable = new ColorDrawable(Color.RED);
+        mDrawable = new MaskableIconDrawable( mBlueDrawable, mRedDrawable);
+        mMask = PathParser.createPathFromPathData(
+            Resources.getSystem().getString(com.android.internal.R.string.config_icon_mask));
+        int sInset = (int) (SAFEZONE_INSET * MaskableIconDrawable.MASK_SIZE);
+        mSafeZone.addCircle(MaskableIconDrawable.MASK_SIZE/2,MaskableIconDrawable.MASK_SIZE/2,
+            MaskableIconDrawable.MASK_SIZE/2/2 - sInset, Direction.CW);
+    }
+
+    @Test
+    public void testDeviceConfigMask_bounds() {
+        assertNotNull(mMask);
+
+        // Bounds should be [100 x 100]
+        RectF bounds = new RectF();
+        mMask.computeBounds(bounds, true);
+        System.out.println("MERONG:" + bounds.toShortString());
+        assertTrue("Mask top should be larger than or equal to 0", -DELTA <= bounds.top);
+        assertTrue("Mask left should be larger than or equal to 0", -DELTA <= bounds.left);
+        assertTrue("Mask bottom should be smaller than or equal to" +
+                MaskableIconDrawable.MASK_SIZE,
+            MaskableIconDrawable.MASK_SIZE + DELTA >= bounds.bottom);
+        assertTrue("Mask right should be smaller than or equal to " +
+                MaskableIconDrawable.MASK_SIZE,
+            MaskableIconDrawable.MASK_SIZE + DELTA >= bounds.right);
+    }
+
+    @Test
+    public void testDeviceConfigMask_largerThanSafezone() {
+        assertNotNull(mMask);
+
+        // MASK intersection SafeZone = SafeZone
+        Region maskRegion = new Region(0, 0, (int) MaskableIconDrawable.MASK_SIZE,
+            (int) MaskableIconDrawable.MASK_SIZE);
+        maskRegion.setPath(mMask, maskRegion);
+
+        Region safeZoneRegion = new Region(0, 0, (int) MaskableIconDrawable.MASK_SIZE,
+            (int) MaskableIconDrawable.MASK_SIZE);
+        safeZoneRegion.setPath(mSafeZone, safeZoneRegion);
+
+        Region intersectRegion = new Region();
+        boolean result = maskRegion.op(safeZoneRegion, intersectRegion, Region.Op.INTERSECT);
+
+        // resultRegion should be exactly same as the safezone
+        // Test if intersectRegion is smaller than safeZone, in which case test will fail.
+        Region subtractRegion = new Region();
+        result = safeZoneRegion.op(intersectRegion, subtractRegion, Op.DIFFERENCE);
+        assertFalse("Mask is not respecting the safezone.", result);
+    }
+
+    @Test
+    public void testDeviceConfigMask_isConvex() {
+        assertNotNull(mMask);
+
+        assertTrue("Mask is not convex", mMask.isConvex());
+    }
+
+    @Test
+    public void testGetLayers() {
+        assertEquals("Background layer is not the same.",
+            mBlueDrawable, mDrawable.getBackground());
+        assertEquals("Foreground layer is not the same.",
+            mRedDrawable, mDrawable.getForeground());
+    }
+}
diff --git a/tests/tests/graphics/src/android/graphics/fonts/cts/FontRequestTest.java b/tests/tests/graphics/src/android/graphics/fonts/cts/FontRequestTest.java
new file mode 100644
index 0000000..6cf7e29
--- /dev/null
+++ b/tests/tests/graphics/src/android/graphics/fonts/cts/FontRequestTest.java
@@ -0,0 +1,77 @@
+/*
+ * 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.fonts.cts;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+import android.graphics.fonts.FontRequest;
+import android.os.Parcel;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * 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";
+
+    @Test
+    public void testWriteToParcel() {
+        // GIVEN a FontRequest
+        FontRequest request = new FontRequest(PROVIDER, 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(QUERY, result.getQuery());
+    }
+
+    @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();
+    }
+
+    @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();
+    }
+}
diff --git a/tests/tests/graphics/src/android/graphics/fonts/cts/FontResultTest.java b/tests/tests/graphics/src/android/graphics/fonts/cts/FontResultTest.java
new file mode 100644
index 0000000..06e2e3f
--- /dev/null
+++ b/tests/tests/graphics/src/android/graphics/fonts/cts/FontResultTest.java
@@ -0,0 +1,142 @@
+/*
+ * 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.fonts.cts;
+
+import static android.content.res.AssetManager.ACCESS_BUFFER;
+import static android.os.ParcelFileDescriptor.MODE_READ_ONLY;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.fail;
+
+import android.app.Activity;
+import android.content.res.AssetFileDescriptor;
+import android.content.res.Resources;
+import android.graphics.Typeface;
+import android.graphics.fonts.FontResult;
+import android.os.Parcel;
+import android.os.ParcelFileDescriptor;
+import android.support.test.filters.SmallTest;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * Tests for {@link FontResult}.
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class FontResultTest {
+    private static final ParcelFileDescriptor FILE_DESCRIPTOR =
+            new ParcelFileDescriptor(new FileDescriptor());
+    private static final int TTC_INDEX = 3;
+    private static final String FONT_VARIATION_SETTINGS = "my_settings";
+    private static final int STYLE = Typeface.BOLD_ITALIC;
+    private static final String TEST_FONT_FILE = "samplefont.ttf";
+    private Resources mResources;
+    private Activity mActivity;
+
+    @Rule
+    public ActivityTestRule<MockActivity> mActivityRule =
+            new ActivityTestRule<>(MockActivity.class);
+
+    @Before
+    public void setup() throws Throwable {
+        mActivity = mActivityRule.getActivity();
+        mResources = mActivity.getResources();
+    }
+
+    @Test
+    public void testWriteToParcel() throws IOException {
+        ParcelFileDescriptor pfd = loadFont();
+        // GIVEN a FontResult
+        FontResult fontResult = new FontResult(pfd, TTC_INDEX, FONT_VARIATION_SETTINGS, STYLE);
+
+        // WHEN we write it to a Parcel
+        Parcel dest = Parcel.obtain();
+        fontResult.writeToParcel(dest, 0);
+        dest.setDataPosition(0);
+
+        // THEN we create from that parcel and get the same values.
+        FontResult result = FontResult.CREATOR.createFromParcel(dest);
+        assertNotNull(result.getFileDescriptor());
+        assertEquals(TTC_INDEX, result.getTtcIndex());
+        assertEquals(FONT_VARIATION_SETTINGS, result.getFontVariationSettings());
+        assertEquals(STYLE, result.getStyle());
+    }
+
+    @Test
+    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();
+    }
+
+    @Test
+    public void testConstructorWithNullFontVariationSettings() {
+        // WHEN we create a result with a null fontVariationSettings
+        FontResult fontResult = new FontResult(FILE_DESCRIPTOR, TTC_INDEX, null, STYLE);
+
+        // THEN we expect no exception to be raised, and null to be stored as the value.
+        assertNull(fontResult.getFontVariationSettings());
+    }
+
+    private ParcelFileDescriptor loadFont() {
+        File cacheFile = null;
+        try {
+            cacheFile = new File(mActivity.getCacheDir(), TEST_FONT_FILE);
+            cacheFile.getParentFile().mkdirs();
+            copyToCacheFile("samplefont.ttf", cacheFile);
+            return ParcelFileDescriptor.open(cacheFile, MODE_READ_ONLY);
+        } catch (IOException e) {
+            e.printStackTrace();
+        } finally {
+            if (cacheFile != null) {
+                cacheFile.delete();
+            }
+        }
+        return null;
+    }
+
+    private void copyToCacheFile(final String assetPath, final File cacheFile)
+            throws IOException {
+        try (InputStream is = mResources.getAssets().open(assetPath, ACCESS_BUFFER);
+            FileOutputStream fos = new FileOutputStream(cacheFile, false)) {
+            byte[] buffer = new byte[1024];
+            int readLen;
+            while ((readLen = is.read(buffer)) != -1) {
+                fos.write(buffer, 0, readLen);
+            }
+        }
+    }
+}
diff --git a/tests/tests/graphics/src/android/graphics/fonts/cts/MockActivity.java b/tests/tests/graphics/src/android/graphics/fonts/cts/MockActivity.java
new file mode 100644
index 0000000..3fbf255
--- /dev/null
+++ b/tests/tests/graphics/src/android/graphics/fonts/cts/MockActivity.java
@@ -0,0 +1,23 @@
+/*
+ * 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.fonts.cts;
+
+import android.app.Activity;
+
+public class MockActivity extends Activity {
+
+}
\ No newline at end of file
diff --git a/tests/tests/hardware/Android.mk b/tests/tests/hardware/Android.mk
index 6e14198..957c012 100644
--- a/tests/tests/hardware/Android.mk
+++ b/tests/tests/hardware/Android.mk
@@ -34,7 +34,8 @@
     ctstestrunner \
     mockito-target-minus-junit4 \
     platform-test-annotations \
-    ub-uiautomator
+    ub-uiautomator \
+    legacy-android-test
 
 LOCAL_JNI_SHARED_LIBRARIES := libctshardware_jni libnativehelper_compat_libc++
 
diff --git a/tests/tests/hardware/src/android/hardware/cts/HardwareBufferTest.java b/tests/tests/hardware/src/android/hardware/cts/HardwareBufferTest.java
index 6d1f0d1..0896d71 100644
--- a/tests/tests/hardware/src/android/hardware/cts/HardwareBufferTest.java
+++ b/tests/tests/hardware/src/android/hardware/cts/HardwareBufferTest.java
@@ -61,6 +61,9 @@
         buffer = HardwareBuffer.create(2, 4, HardwareBuffer.RGB_565, 1,
                 HardwareBuffer.USAGE0_CPU_READ);
         assertEquals(HardwareBuffer.RGB_565, buffer.getFormat());
+        buffer = HardwareBuffer.create(2, 1, HardwareBuffer.BLOB, 1,
+                HardwareBuffer.USAGE0_CPU_READ);
+        assertEquals(HardwareBuffer.BLOB, buffer.getFormat());
     }
 
     @Test
@@ -87,6 +90,11 @@
                     HardwareBuffer.USAGE0_CPU_READ);
         } catch (IllegalArgumentException e) {}
         assertEquals(null, buffer);
+        try {
+            buffer = HardwareBuffer.create(2, 2, HardwareBuffer.BLOB, 1,
+                    HardwareBuffer.USAGE0_CPU_READ);
+        } catch (IllegalArgumentException e) {}
+        assertEquals(null, buffer);
     }
 
     @Test
diff --git a/tests/tests/keystore/src/android/keystore/cts/KeyAttestationTest.java b/tests/tests/keystore/src/android/keystore/cts/KeyAttestationTest.java
index 98f85e7..807330e 100644
--- a/tests/tests/keystore/src/android/keystore/cts/KeyAttestationTest.java
+++ b/tests/tests/keystore/src/android/keystore/cts/KeyAttestationTest.java
@@ -53,9 +53,11 @@
 
 import com.google.common.collect.ImmutableSet;
 import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.Context;
 import android.os.Build;
 import android.os.SystemProperties;
 import android.security.KeyStoreException;
+import android.security.keystore.AttestationUtils;
 import android.security.keystore.KeyGenParameterSpec;
 import android.security.keystore.KeyProperties;
 import android.test.AndroidTestCase;
@@ -362,6 +364,12 @@
         }
     }
 
+    public void testDeviceIdAttestation() throws Exception {
+        testDeviceIdAttestationFailure(AttestationUtils.ID_TYPE_SERIAL);
+        testDeviceIdAttestationFailure(AttestationUtils.ID_TYPE_IMEI);
+        testDeviceIdAttestationFailure(AttestationUtils.ID_TYPE_MEID);
+    }
+
     @SuppressWarnings("deprecation")
     private void testRsaAttestation(byte[] challenge, boolean includeValidityDates, int keySize,
             int purposes, String[] paddingModes) throws Exception {
@@ -865,4 +873,14 @@
             }
         }
     }
+
+    private void testDeviceIdAttestationFailure(int idType) throws Exception {
+        try {
+            AttestationUtils.attestDeviceIds(getContext(), new int[] {idType}, "123".getBytes());
+            fail("Attestation should have failed.");
+        } catch (SecurityException e) {
+            // Attestation is expected to fail with a SecurityException as we do not hold
+            // READ_PRIVILEGED_PHONE_STATE permission.
+        }
+    }
 }
diff --git a/tests/tests/media/AndroidManifest.xml b/tests/tests/media/AndroidManifest.xml
index 9064ade..5123f2c 100644
--- a/tests/tests/media/AndroidManifest.xml
+++ b/tests/tests/media/AndroidManifest.xml
@@ -27,6 +27,8 @@
     <uses-permission android:name="android.permission.WAKE_LOCK" />
     <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
     <uses-permission android:name="android.permission.WRITE_SETTINGS" />
+    <uses-permission android:name="android.permission.SET_VOLUME_KEY_LONG_PRESS_LISTENER" />
+    <uses-permission android:name="android.permission.SET_MEDIA_KEY_LISTENER" />
 
     <application>
         <uses-library android:name="android.test.runner" />
diff --git a/tests/tests/media/src/android/media/cts/MediaMuxerTest.java b/tests/tests/media/src/android/media/cts/MediaMuxerTest.java
index 987d7c7..c460519 100644
--- a/tests/tests/media/src/android/media/cts/MediaMuxerTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaMuxerTest.java
@@ -24,6 +24,7 @@
 import android.media.MediaFormat;
 import android.media.MediaMetadataRetriever;
 import android.media.MediaMuxer;
+import android.os.ParcelFileDescriptor;
 import android.test.AndroidTestCase;
 import android.util.Log;
 
@@ -31,6 +32,7 @@
 
 import java.io.File;
 import java.io.IOException;
+import java.io.RandomAccessFile;
 import java.nio.ByteBuffer;
 import java.util.HashMap;
 
@@ -196,6 +198,46 @@
         } finally {
             muxer.release();
         }
+
+        // Test FileDescriptor Constructor expect sucess.
+        RandomAccessFile file = null;
+        try {
+            file = new RandomAccessFile(outputFile, "rws");
+            muxer = new MediaMuxer(file.getFD(), MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
+            muxer.addTrack(MediaFormat.createVideoFormat(MediaFormat.MIMETYPE_VIDEO_AVC, 480, 320));
+        } finally {
+            file.close();
+            muxer.release();
+        }
+
+        // Test FileDescriptor Constructor expect exception with read only mode.
+        RandomAccessFile file2 = null;
+        try {
+            file2 = new RandomAccessFile(outputFile, "r");
+            muxer = new MediaMuxer(file2.getFD(), MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
+            fail("should throw IOException.");
+        } catch (IOException e) {
+            // expected
+        } finally {
+            file2.close();
+            // No need to release the muxer.
+        }
+
+        // Test FileDescriptor Constructor expect NO exception with write only mode.
+        ParcelFileDescriptor out = null;
+        try {
+            out = ParcelFileDescriptor.open(new File(outputFile),
+                    ParcelFileDescriptor.MODE_WRITE_ONLY | ParcelFileDescriptor.MODE_CREATE);
+            muxer = new MediaMuxer(out.getFileDescriptor(), MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
+        } catch (IllegalArgumentException e) {
+            fail("should not throw IllegalArgumentException.");
+        } catch (IOException e) {
+            fail("should not throw IOException.");
+        } finally {
+            out.close();
+            muxer.release();
+        }
+
         new File(outputFile).delete();
     }
 
diff --git a/tests/tests/media/src/android/media/cts/MediaRecorderTest.java b/tests/tests/media/src/android/media/cts/MediaRecorderTest.java
index 2801dd0..3fe76f8 100644
--- a/tests/tests/media/src/android/media/cts/MediaRecorderTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaRecorderTest.java
@@ -680,30 +680,21 @@
                         }
                     }
 
-                    // Test passing a write-only FileDescriptor and expect IOException.
+                    // Test passing a write-only FileDescriptor and expect NO IOException.
                     if (mFileIndex == 3) {
-                        ParcelFileDescriptor out = null;
-                        String path = null;
                         try {
-                            path = OUTPUT_PATH + '9';
+                            ParcelFileDescriptor out = null;
+                            String path = OUTPUT_PATH + mFileIndex;
                             out = ParcelFileDescriptor.open(new File(path),
                                     ParcelFileDescriptor.MODE_WRITE_ONLY | ParcelFileDescriptor.MODE_CREATE);
                             mMediaRecorder.setNextOutputFile(out.getFileDescriptor());
-                            fail("Expect IOException.");
+                            out.close();
+                            recordFileList.add(path);
+                            mFileIndex++;
                         } catch (IOException e) {
-                            // Expected.
-                        } finally {
-                            try {
-                                out.close();
-                                new File(path).delete();
-                            } catch (IOException e) {
-                                fail("Fail to close file");
-                            }
+                            fail("Fail to set next output file error: " + e);
                         }
-                    }
-
-                    // only record 5 files.
-                    if (mFileIndex < 6) {
+                    } else if (mFileIndex < 6) {
                         try {
                             String path = OUTPUT_PATH + mFileIndex;
                             mMediaRecorder.setNextOutputFile(path);
diff --git a/tests/tests/media/src/android/media/cts/MediaSessionManagerTest.java b/tests/tests/media/src/android/media/cts/MediaSessionManagerTest.java
index b10d9df..50b3033 100644
--- a/tests/tests/media/src/android/media/cts/MediaSessionManagerTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaSessionManagerTest.java
@@ -15,15 +15,33 @@
  */
 package android.media.cts;
 
+import com.android.compatibility.common.util.SystemUtil;
+
 import android.content.Context;
+import android.content.Intent;
+import android.media.AudioManager;
 import android.media.session.MediaController;
+import android.media.session.MediaSession;
 import android.media.session.MediaSessionManager;
+import android.media.session.PlaybackState;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
 import android.test.InstrumentationTestCase;
 import android.test.UiThreadTest;
+import android.view.KeyEvent;
 
+import java.io.IOException;
+import java.util.ArrayList;
 import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
 
 public class MediaSessionManagerTest extends InstrumentationTestCase {
+    private static final String TAG = "MediaSessionManagerTest";
+    private static final int TIMEOUT_MS = 3000;
+    private static final int WAIT_MS = 500;
+
     private MediaSessionManager mSessionManager;
 
     @Override
@@ -69,4 +87,166 @@
         // TODO enable a notification listener, test again, disable, verify
         // updates stopped
     }
+
+    private void assertKeyEventEquals(KeyEvent lhs, int keyCode, int action, int repeatCount) {
+        assertTrue(lhs.getKeyCode() == keyCode
+                && lhs.getAction() == action
+                && lhs.getRepeatCount() == repeatCount);
+    }
+
+    private void injectInputEvent(int keyCode, boolean longPress) throws IOException {
+        // Injecting key with instrumentation requires a window/view, but we don't have it.
+        // Inject key event through the adb commend to workaround.
+        final String command = "input keyevent " + (longPress ? "--longpress " : "") + keyCode;
+        SystemUtil.runShellCommand(getInstrumentation(), command);
+    }
+
+    public void testSetOnVolumeKeyLongPressListener() throws Exception {
+        HandlerThread handlerThread = new HandlerThread(TAG);
+        handlerThread.start();
+        Handler handler = new Handler(handlerThread.getLooper());
+
+        // Ensure that the listener is called for long-press.
+        VolumeKeyLongPressListener listener = new VolumeKeyLongPressListener(3, handler);
+        mSessionManager.setOnVolumeKeyLongPressListener(listener, handler);
+        injectInputEvent(KeyEvent.KEYCODE_VOLUME_DOWN, true);
+        assertTrue(listener.mCountDownLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+        assertEquals(listener.mKeyEvents.size(), 3);
+        assertKeyEventEquals(listener.mKeyEvents.get(0),
+                KeyEvent.KEYCODE_VOLUME_DOWN, KeyEvent.ACTION_DOWN, 0);
+        assertKeyEventEquals(listener.mKeyEvents.get(1),
+                KeyEvent.KEYCODE_VOLUME_DOWN, KeyEvent.ACTION_DOWN, 1);
+        assertKeyEventEquals(listener.mKeyEvents.get(2),
+                KeyEvent.KEYCODE_VOLUME_DOWN, KeyEvent.ACTION_UP, 0);
+
+        // Ensure the the listener isn't called for short-press.
+        listener = new VolumeKeyLongPressListener(1, handler);
+        mSessionManager.setOnVolumeKeyLongPressListener(listener, handler);
+        injectInputEvent(KeyEvent.KEYCODE_VOLUME_DOWN, false);
+        assertFalse(listener.mCountDownLatch.await(WAIT_MS, TimeUnit.MILLISECONDS));
+        assertEquals(listener.mKeyEvents.size(), 0);
+
+        // Ensure that the listener isn't called anymore.
+        mSessionManager.setOnVolumeKeyLongPressListener(null, handler);
+        injectInputEvent(KeyEvent.KEYCODE_VOLUME_DOWN, true);
+        assertFalse(listener.mCountDownLatch.await(WAIT_MS, TimeUnit.MILLISECONDS));
+        assertEquals(listener.mKeyEvents.size(), 0);
+    }
+
+    public void testSetOnMediaKeyListener() throws Exception {
+        HandlerThread handlerThread = new HandlerThread(TAG);
+        handlerThread.start();
+        Handler handler = new Handler(handlerThread.getLooper());
+
+        MediaSessionCallback callback = new MediaSessionCallback(2);
+        MediaSession session = new MediaSession(getInstrumentation().getTargetContext(), TAG);
+        session.setFlags(MediaSession.FLAG_HANDLES_MEDIA_BUTTONS
+                | MediaSession.FLAG_HANDLES_TRANSPORT_CONTROLS);
+        session.setCallback(callback, handler);
+        PlaybackState state = new PlaybackState.Builder()
+                .setState(PlaybackState.STATE_PLAYING, 0, 1.0f).build();
+        // Fake the media session service so this session can take the media key events.
+        session.setPlaybackState(state);
+        session.setActive(true);
+
+        // Ensure that the listener is called for media key event,
+        // and any other media sessions don't get the key.
+        MediaKeyListener listener = new MediaKeyListener(2, true, handler);
+        mSessionManager.setOnMediaKeyListener(listener, handler);
+        injectInputEvent(KeyEvent.KEYCODE_HEADSETHOOK, false);
+        assertTrue(listener.mCountDownLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+        assertEquals(listener.mKeyEvents.size(), 2);
+        assertKeyEventEquals(listener.mKeyEvents.get(0),
+                KeyEvent.KEYCODE_HEADSETHOOK, KeyEvent.ACTION_DOWN, 0);
+        assertKeyEventEquals(listener.mKeyEvents.get(1),
+                KeyEvent.KEYCODE_HEADSETHOOK, KeyEvent.ACTION_UP, 0);
+        assertFalse(callback.mCountDownLatch.await(WAIT_MS, TimeUnit.MILLISECONDS));
+        assertEquals(callback.mKeyEvents.size(), 0);
+
+        // Ensure that the listener is called for media key event,
+        // and another media session gets the key.
+        listener = new MediaKeyListener(2, false, handler);
+        mSessionManager.setOnMediaKeyListener(listener, handler);
+        injectInputEvent(KeyEvent.KEYCODE_HEADSETHOOK, false);
+        assertTrue(listener.mCountDownLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+        assertEquals(listener.mKeyEvents.size(), 2);
+        assertKeyEventEquals(listener.mKeyEvents.get(0),
+                KeyEvent.KEYCODE_HEADSETHOOK, KeyEvent.ACTION_DOWN, 0);
+        assertKeyEventEquals(listener.mKeyEvents.get(1),
+                KeyEvent.KEYCODE_HEADSETHOOK, KeyEvent.ACTION_UP, 0);
+        assertTrue(callback.mCountDownLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+        assertEquals(callback.mKeyEvents.size(), 2);
+        assertKeyEventEquals(callback.mKeyEvents.get(0),
+                KeyEvent.KEYCODE_HEADSETHOOK, KeyEvent.ACTION_DOWN, 0);
+        assertKeyEventEquals(callback.mKeyEvents.get(1),
+                KeyEvent.KEYCODE_HEADSETHOOK, KeyEvent.ACTION_UP, 0);
+
+        // Ensure that the listener isn't called anymore.
+        listener = new MediaKeyListener(1, true, handler);
+        mSessionManager.setOnMediaKeyListener(listener, handler);
+        mSessionManager.setOnMediaKeyListener(null, handler);
+        injectInputEvent(KeyEvent.KEYCODE_HEADSETHOOK, false);
+        assertFalse(listener.mCountDownLatch.await(WAIT_MS, TimeUnit.MILLISECONDS));
+        assertEquals(listener.mKeyEvents.size(), 0);
+    }
+
+    private class VolumeKeyLongPressListener
+            implements MediaSessionManager.OnVolumeKeyLongPressListener {
+        private final List<KeyEvent> mKeyEvents = new ArrayList<>();
+        private final CountDownLatch mCountDownLatch;
+        private final Handler mHandler;
+
+        public VolumeKeyLongPressListener(int count, Handler handler) {
+            mCountDownLatch = new CountDownLatch(count);
+            mHandler = handler;
+        }
+
+        @Override
+        public void onVolumeKeyLongPress(KeyEvent event) {
+            mKeyEvents.add(event);
+            // Ensure the listener is called on the thread.
+            assertEquals(mHandler.getLooper(), Looper.myLooper());
+            mCountDownLatch.countDown();
+        }
+    }
+
+    private class MediaKeyListener implements MediaSessionManager.OnMediaKeyListener {
+        private final CountDownLatch mCountDownLatch;
+        private final boolean mConsume;
+        private final Handler mHandler;
+        private final List<KeyEvent> mKeyEvents = new ArrayList<>();
+
+        public MediaKeyListener(int count, boolean consume, Handler handler) {
+            mCountDownLatch = new CountDownLatch(count);
+            mConsume = consume;
+            mHandler = handler;
+        }
+
+        @Override
+        public boolean onMediaKey(KeyEvent event) {
+            mKeyEvents.add(event);
+            // Ensure the listener is called on the thread.
+            assertEquals(mHandler.getLooper(), Looper.myLooper());
+            mCountDownLatch.countDown();
+            return mConsume;
+        }
+    }
+
+    private class MediaSessionCallback extends MediaSession.Callback {
+        private final CountDownLatch mCountDownLatch;
+        private final List<KeyEvent> mKeyEvents = new ArrayList<>();
+
+        private MediaSessionCallback(int count) {
+            mCountDownLatch = new CountDownLatch(count);
+        }
+
+        public boolean onMediaButtonEvent(Intent mediaButtonIntent) {
+            KeyEvent event = (KeyEvent) mediaButtonIntent.getParcelableExtra(
+                    Intent.EXTRA_KEY_EVENT);
+            assertNotNull(event);
+            mKeyEvents.add(event);
+            mCountDownLatch.countDown();
+            return true;
+        }
+    }
 }
diff --git a/tests/tests/mediastress/preconditions/src/android/mediastress/cts/preconditions/MediaPreparer.java b/tests/tests/mediastress/preconditions/src/android/mediastress/cts/preconditions/MediaPreparer.java
index 13ff24b..049eeab 100644
--- a/tests/tests/mediastress/preconditions/src/android/mediastress/cts/preconditions/MediaPreparer.java
+++ b/tests/tests/mediastress/preconditions/src/android/mediastress/cts/preconditions/MediaPreparer.java
@@ -26,17 +26,18 @@
 import com.android.tradefed.device.DeviceNotAvailableException;
 import com.android.tradefed.device.ITestDevice;
 import com.android.tradefed.result.ITestInvocationListener;
-import com.android.tradefed.result.StubTestInvocationListener;
 import com.android.tradefed.targetprep.BuildError;
 import com.android.tradefed.targetprep.TargetSetupError;
 import com.android.tradefed.testtype.AndroidJUnitTest;
 import com.android.tradefed.util.FileUtil;
 import com.android.tradefed.util.ZipUtil;
 
+import org.xmlpull.v1.XmlPullParserException;
+
 import java.io.File;
 import java.io.FileNotFoundException;
-import java.io.InputStream;
 import java.io.IOException;
+import java.io.InputStream;
 import java.net.URL;
 import java.net.URLConnection;
 import java.util.Map;
@@ -44,8 +45,6 @@
 import java.util.regex.Pattern;
 import java.util.zip.ZipFile;
 
-import org.xmlpull.v1.XmlPullParserException;
-
 /**
  * Ensures that the appropriate media files exist on the device
  */
@@ -178,11 +177,13 @@
      * TargetSetupError is thrown. Otherwise, the mLocalMediaPath variable is set to the path of
      * this subdirectory.
      */
-    private void updateLocalMediaPath(File mediaFolder) throws TargetSetupError {
+    private void updateLocalMediaPath(ITestDevice device, File mediaFolder)
+            throws TargetSetupError {
         String[] subDirs = mediaFolder.list();
         if (subDirs.length != 1) {
             throw new TargetSetupError(String.format(
-                    "Unexpected contents in directory %s", mediaFolder.getAbsolutePath()));
+                    "Unexpected contents in directory %s", mediaFolder.getAbsolutePath()),
+                    device.getDeviceDescriptor());
         }
         mLocalMediaPath = new File(mediaFolder, subDirs[0]).getAbsolutePath();
     }
@@ -192,16 +193,19 @@
      * Updates mLocalMediaPath to be the pathname of the directory containing bbb_short and
      * bbb_full media directories.
      */
-    private void downloadMediaToHost(File mediaFolder) throws TargetSetupError {
+    private void downloadMediaToHost(ITestDevice device, IBuildInfo buildInfo, File mediaFolder)
+            throws TargetSetupError {
         URL url;
         try {
             // Get download URL from dynamic configuration service
-            DynamicConfigHostSide config = new DynamicConfigHostSide(DYNAMIC_CONFIG_MODULE);
-            String mediaUrlString = config.getValue(MEDIA_FILES_URL_KEY);
+            File config =
+                    DynamicConfigHostSide.getDynamicConfigFile(buildInfo, DYNAMIC_CONFIG_MODULE);
+            String mediaUrlString =
+                    DynamicConfigHostSide.getValueFromConfig(config, MEDIA_FILES_URL_KEY);
             url = new URL(mediaUrlString);
         } catch (IOException | XmlPullParserException e) {
             throw new TargetSetupError("Trouble finding media file download location with " +
-                    "dynamic configuration", e);
+                    "dynamic configuration", e, device.getDeviceDescriptor());
         }
         File mediaFolderZip = new File(mediaFolder.getAbsolutePath() + ".zip");
         try {
@@ -215,7 +219,8 @@
         } catch (IOException e) {
             FileUtil.recursiveDelete(mediaFolder);
             throw new TargetSetupError("Failed to download and open media files on host, the"
-                    + " device requires these media files for CTS media tests", e);
+                    + " device requires these media files for CTS media tests", e,
+                    device.getDeviceDescriptor());
         } finally {
             FileUtil.deleteFile(mediaFolderZip);
         }
@@ -288,10 +293,10 @@
                 // runs of MediaPreparer. Assume media files exist inside.
                 // Else, create directory if needed and download/extract media files inside.
                 mediaFolder.mkdirs();
-                downloadMediaToHost(mediaFolder);
+                downloadMediaToHost(device, buildInfo, mediaFolder);
             }
             // set mLocalMediaPath to where the CTS media files have been extracted
-            updateLocalMediaPath(mediaFolder);
+            updateLocalMediaPath(device, mediaFolder);
         }
         logInfo("Media files located on host at: %s", mLocalMediaPath);
         copyMediaFiles(device);
@@ -336,7 +341,7 @@
     }
 
     /* Special listener for setting MediaPreparer instance variable values */
-    private class MediaPreparerListener extends StubTestInvocationListener {
+    private class MediaPreparerListener implements ITestInvocationListener {
 
         @Override
         public void testEnded(TestIdentifier test, Map<String, String> metrics) {
diff --git a/tests/tests/mediastress/preconditions/tests/src/android/mediastress/cts/preconditions/MediaPreparerTest.java b/tests/tests/mediastress/preconditions/tests/src/android/mediastress/cts/preconditions/MediaPreparerTest.java
index 04c3900..a175112 100644
--- a/tests/tests/mediastress/preconditions/tests/src/android/mediastress/cts/preconditions/MediaPreparerTest.java
+++ b/tests/tests/mediastress/preconditions/tests/src/android/mediastress/cts/preconditions/MediaPreparerTest.java
@@ -21,7 +21,6 @@
 import com.android.tradefed.build.IBuildInfo;
 import com.android.tradefed.config.OptionSetter;
 import com.android.tradefed.device.ITestDevice;
-import com.android.tradefed.targetprep.TargetSetupError;
 
 import junit.framework.TestCase;
 
@@ -39,7 +38,7 @@
         super.setUp();
         mMediaPreparer = new MediaPreparer();
         mMockDevice = EasyMock.createMock(ITestDevice.class);
-        mMockBuildInfo = new BuildInfo("0", "", "");
+        mMockBuildInfo = new BuildInfo("0", "");
         mOptionSetter = new OptionSetter(mMediaPreparer);
     }
 
diff --git a/tests/tests/nativehardware/Android.mk b/tests/tests/nativehardware/Android.mk
index c19511e..3abaccb 100644
--- a/tests/tests/nativehardware/Android.mk
+++ b/tests/tests/nativehardware/Android.mk
@@ -32,6 +32,7 @@
 
 LOCAL_SHARED_LIBRARIES := \
   libandroid \
+  libandroid_runtime \
   libutils \
   liblog \
 
diff --git a/tests/tests/nativehardware/src/AHardwareBufferTest.cpp b/tests/tests/nativehardware/src/AHardwareBufferTest.cpp
index 37a51e4..f851d87 100644
--- a/tests/tests/nativehardware/src/AHardwareBufferTest.cpp
+++ b/tests/tests/nativehardware/src/AHardwareBufferTest.cpp
@@ -23,6 +23,7 @@
 #include <sys/un.h>
 
 #include <android/hardware_buffer.h>
+#include <android_runtime/android_hardware_HardwareBuffer.h>
 #include <ui/GraphicBuffer.h>
 #include <utils/Errors.h>
 
@@ -42,55 +43,6 @@
     return reinterpret_cast<AHardwareBuffer*>(buffer);
 }
 
-// This function is temporarily necessary to convert between bit versions.
-static inline uint32_t convertGralloc1ToGralloc0UsageBits(uint64_t usage0,
-        uint64_t usage1) {
-    uint32_t bits = 0;
-    if (usage0 & AHARDWAREBUFFER_USAGE0_CPU_READ)
-        bits |= GRALLOC_USAGE_SW_READ_RARELY;
-    if (usage0 & AHARDWAREBUFFER_USAGE0_CPU_READ_OFTEN)
-        bits |= GRALLOC_USAGE_SW_READ_OFTEN;
-    if (usage0 & AHARDWAREBUFFER_USAGE0_CPU_WRITE)
-        bits |= GRALLOC_USAGE_SW_WRITE_RARELY;
-    if (usage0 & AHARDWAREBUFFER_USAGE0_CPU_WRITE_OFTEN)
-        bits |= GRALLOC_USAGE_SW_WRITE_OFTEN;
-    if (usage0 & AHARDWAREBUFFER_USAGE0_GPU_SAMPLED_IMAGE)
-        bits |= GRALLOC_USAGE_HW_TEXTURE;
-    if (usage0 & AHARDWAREBUFFER_USAGE0_GPU_COLOR_OUTPUT)
-        bits |= GRALLOC_USAGE_HW_RENDER;
-    // Not sure what this should be.
-    if (usage0 & AHARDWAREBUFFER_USAGE0_GPU_STORAGE_IMAGE) bits |= 0;
-    // Not sure what this should be.
-    if (usage0 & AHARDWAREBUFFER_USAGE0_GPU_CUBEMAP) bits |= 0;
-    // Not yet supported in gralloc1.
-    // if (usage0 & AHARDWAREBUFFER_USAGE0_GPU_DATA_BUFFER) bits |= 0;
-    if (usage0 & AHARDWAREBUFFER_USAGE0_VIDEO_ENCODE)
-        bits |= GRALLOC_USAGE_HW_VIDEO_ENCODER;
-    if (usage0 & AHARDWAREBUFFER_USAGE0_PROTECTED_CONTENT)
-        bits |= GRALLOC_USAGE_PROTECTED;
-
-    (void)usage1;
-
-    return bits;
-}
-
-static uint32_t convertFromGraphicBufferFormat(uint32_t format) {
-    switch (format) {
-        case PIXEL_FORMAT_RGBA_8888:
-            return AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM;
-        case PIXEL_FORMAT_RGBA_FP16:
-            return AHARDWAREBUFFER_FORMAT_R16G16B16A16_SFLOAT;
-        case PIXEL_FORMAT_RGBX_8888:
-            return AHARDWAREBUFFER_FORMAT_R8G8B8X8_UNORM;
-        case PIXEL_FORMAT_RGB_565:
-            return AHARDWAREBUFFER_FORMAT_R5G6B5_UNORM;
-        case PIXEL_FORMAT_RGB_888:
-            return AHARDWAREBUFFER_FORMAT_R8G8B8_UNORM;
-        default:
-            return 0;
-    }
-}
-
 static ::testing::AssertionResult BuildFailureMessage(uint32_t expected,
         uint32_t actual, const char* type) {
     return ::testing::AssertionFailure() << "Buffer " << type << " do not match"
@@ -110,11 +62,14 @@
         return BuildFailureMessage(desc.layers,
                 static_cast<uint32_t>(buffer->layerCount), "layers");
     if (static_cast<uint32_t>(buffer->usage) !=
-            convertGralloc1ToGralloc0UsageBits(desc.usage0, desc.usage1))
+            android_hardware_HardwareBuffer_convertToGrallocUsageBits(
+                    desc.usage0, desc.usage1))
         return BuildFailureMessage(
-                convertGralloc1ToGralloc0UsageBits(desc.usage0, desc.usage1),
+                android_hardware_HardwareBuffer_convertToGrallocUsageBits(
+                        desc.usage0, desc.usage1),
                 static_cast<uint32_t>(buffer->usage), "usages");
-    if (convertFromGraphicBufferFormat(buffer->getPixelFormat()) != desc.format)
+    if (android_hardware_HardwareBuffer_convertFromPixelFormat(
+            buffer->getPixelFormat()) != desc.format)
         return BuildFailureMessage(desc.format,
                 static_cast<uint32_t>(buffer->format), "formats");
     return ::testing::AssertionSuccess();
@@ -135,6 +90,28 @@
     EXPECT_EQ(BAD_VALUE, res);
 }
 
+// Test that passing in NULL values to allocate works as expected.
+TEST(AHardwareBufferTest, AHardwareBuffer_allocate_BlobFormatRequiresHeight1) {
+    AHardwareBuffer* buffer;
+    AHardwareBuffer_Desc desc;
+
+    desc.width = 2;
+    desc.height = 4;
+    desc.layers = 1;
+    desc.usage0 = AHARDWAREBUFFER_USAGE0_CPU_READ;
+    desc.usage1 = 0;
+    desc.format = AHARDWAREBUFFER_FORMAT_BLOB;
+    int res = AHardwareBuffer_allocate(&desc, &buffer);
+    EXPECT_EQ(BAD_VALUE, res);
+
+    desc.height = 1;
+    res = AHardwareBuffer_allocate(&desc, &buffer);
+    EXPECT_EQ(NO_ERROR, res);
+    EXPECT_TRUE(CheckAHardwareBufferMatchesDesc(buffer, desc));
+    AHardwareBuffer_release(buffer);
+    buffer = NULL;
+}
+
 // Test that allocate can create an AHardwareBuffer correctly.
 TEST(AHardwareBufferTest, AHardwareBuffer_allocate_Succeeds) {
     AHardwareBuffer* buffer = NULL;
diff --git a/tests/tests/nativemedia/aaudio/Android.mk b/tests/tests/nativemedia/aaudio/Android.mk
new file mode 100644
index 0000000..a075f9d
--- /dev/null
+++ b/tests/tests/nativemedia/aaudio/Android.mk
@@ -0,0 +1,47 @@
+# Copyright 2017 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Build the unit tests.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := CtsNativeMediaAAudioTestCases
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA)/nativetest
+LOCAL_MULTILIB := both
+LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
+LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
+
+LOCAL_C_INCLUDES := \
+    frameworks/av/media/liboboe/include
+
+LOCAL_SRC_FILES := \
+    src/test_aaudio.cpp
+
+LOCAL_SHARED_LIBRARIES := \
+    liblog \
+    liboboe \
+    libutils
+
+LOCAL_STATIC_LIBRARIES := \
+    libgtest
+
+LOCAL_CTS_TEST_PACKAGE := android.nativemedia.aaudio
+
+# Tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts
+
+LOCAL_CFLAGS := -Werror -Wall
+
+include $(BUILD_CTS_EXECUTABLE)
diff --git a/tests/tests/nativemedia/aaudio/AndroidTest.xml b/tests/tests/nativemedia/aaudio/AndroidTest.xml
new file mode 100644
index 0000000..2224a27
--- /dev/null
+++ b/tests/tests/nativemedia/aaudio/AndroidTest.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2017 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<configuration description="Config for CTS Native Media AAudio test cases">
+    <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
+        <option name="cleanup" value="true" />
+        <option name="push" value="CtsNativeMediaAAudioTestCases->/data/local/tmp/CtsNativeMediaAAudioTestCases" />
+        <option name="append-bitness" value="true" />
+    </target_preparer>
+    <test class="com.android.tradefed.testtype.GTest" >
+        <option name="native-test-device-path" value="/data/local/tmp" />
+        <option name="module-name" value="CtsNativeMediaAAudioTestCases" />
+        <option name="runtime-hint" value="1m" />
+    </test>
+</configuration>
diff --git a/tests/tests/nativemedia/aaudio/src/test_aaudio.cpp b/tests/tests/nativemedia/aaudio/src/test_aaudio.cpp
new file mode 100644
index 0000000..f6cdadb
--- /dev/null
+++ b/tests/tests/nativemedia/aaudio/src/test_aaudio.cpp
@@ -0,0 +1,373 @@
+/*
+ * Copyright 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_NDEBUG 0
+#define LOG_TAG "AAudioTest"
+
+#include <gtest/gtest.h>
+#include <utils/Log.h>
+
+#include <oboe/OboeAudio.h>
+#include <oboe/OboeDefinitions.h>
+
+// Note that this "Oboe" audio API is in the process of being renamed "AAudio".
+// You may see both names until the conversion is complete.
+// TODO Remove this comment when Oboe has become AAudio.
+
+#define DEFAULT_STATE_TIMEOUT  (500 * OBOE_NANOS_PER_MILLISECOND)
+
+// Test OboeStreamBuilder
+TEST(test_aaudio, oboe_stream_builder) {
+    const oboe_sample_rate_t requestedSampleRate1 = 48000;
+    const oboe_sample_rate_t requestedSampleRate2 = 44100;
+    const int32_t requestedSamplesPerFrame = 2;
+    const oboe_audio_format_t requestedDataFormat = OBOE_AUDIO_FORMAT_PCM16;
+
+    oboe_sample_rate_t sampleRate = -1;
+    int32_t samplesPerFrame = -1;
+    oboe_audio_format_t actualDataFormat;
+    OboeStreamBuilder oboeBuilder1;
+    OboeStreamBuilder oboeBuilder2;
+
+    // Use an OboeStreamBuilder to define the stream.
+    oboe_result_t result = Oboe_createStreamBuilder(&oboeBuilder1);
+    ASSERT_EQ(OBOE_OK, result);
+
+    // Request stream properties.
+    EXPECT_EQ(OBOE_OK, OboeStreamBuilder_setSampleRate(oboeBuilder1, requestedSampleRate1));
+    EXPECT_EQ(OBOE_OK, OboeStreamBuilder_setSamplesPerFrame(oboeBuilder1,
+            requestedSamplesPerFrame));
+    EXPECT_EQ(OBOE_OK, OboeStreamBuilder_setFormat(oboeBuilder1, requestedDataFormat));
+
+    // Check to make sure builder saved the properties.
+    EXPECT_EQ(OBOE_OK, OboeStreamBuilder_getSampleRate(oboeBuilder1, &sampleRate));
+    EXPECT_EQ(requestedSampleRate1, sampleRate);
+
+    EXPECT_EQ(OBOE_OK, OboeStreamBuilder_getSamplesPerFrame(oboeBuilder1, &samplesPerFrame));
+    EXPECT_EQ(requestedSamplesPerFrame, samplesPerFrame);
+
+    EXPECT_EQ(OBOE_OK, OboeStreamBuilder_getFormat(oboeBuilder1, &actualDataFormat));
+    EXPECT_EQ(requestedDataFormat, actualDataFormat);
+
+    result = OboeStreamBuilder_getSampleRate(0x0BADCAFE, &sampleRate); // ridiculous token
+    EXPECT_EQ(OBOE_ERROR_INVALID_HANDLE, result);
+
+    // Create a second builder and make sure they do not collide.
+    ASSERT_EQ(OBOE_OK, Oboe_createStreamBuilder(&oboeBuilder2));
+    ASSERT_NE(oboeBuilder1, oboeBuilder2);
+
+    EXPECT_EQ(OBOE_OK, OboeStreamBuilder_setSampleRate(oboeBuilder2, requestedSampleRate2));
+    EXPECT_EQ(OBOE_OK, OboeStreamBuilder_getSampleRate(oboeBuilder1, &sampleRate));
+    EXPECT_EQ(requestedSampleRate1, sampleRate);
+    EXPECT_EQ(OBOE_OK, OboeStreamBuilder_getSampleRate(oboeBuilder2, &sampleRate));
+    EXPECT_EQ(requestedSampleRate2, sampleRate);
+
+    // Delete the builder.
+    EXPECT_EQ(OBOE_OK, OboeStreamBuilder_delete(oboeBuilder1));
+
+    // Now it should no longer be valid.
+    // Note that test assumes we are using the HandleTracker. If we use plain pointers
+    // then it will be difficult to detect this kind of error.
+    result = OboeStreamBuilder_getSampleRate(oboeBuilder1, &sampleRate); // stale token
+    EXPECT_EQ(OBOE_ERROR_INVALID_HANDLE, result);
+
+    // Second builder should still be valid.
+    EXPECT_EQ(OBOE_OK, OboeStreamBuilder_getSampleRate(oboeBuilder2, &sampleRate));
+    EXPECT_EQ(requestedSampleRate2, sampleRate);
+
+    // Delete the second builder.
+    EXPECT_EQ(OBOE_OK, OboeStreamBuilder_delete(oboeBuilder2));
+
+    // Now it should no longer be valid. Assumes HandlerTracker used.
+    EXPECT_EQ(OBOE_ERROR_INVALID_HANDLE, OboeStreamBuilder_getSampleRate(oboeBuilder2,
+            &sampleRate));
+}
+
+// Test creating a default stream with everything unspecified.
+TEST(test_aaudio, oboe_stream_unspecified) {
+    OboeStreamBuilder oboeBuilder;
+    OboeStream oboeStream;
+    oboe_result_t result = OBOE_OK;
+
+    // Use an OboeStreamBuilder to define the stream.
+    result = Oboe_createStreamBuilder(&oboeBuilder);
+    ASSERT_EQ(OBOE_OK, result);
+
+    // Create an OboeStream using the Builder.
+    ASSERT_EQ(OBOE_OK, OboeStreamBuilder_openStream(oboeBuilder, &oboeStream));
+
+    // Cleanup
+    EXPECT_EQ(OBOE_OK, OboeStreamBuilder_delete(oboeBuilder));
+    EXPECT_EQ(OBOE_OK, OboeStream_close(oboeStream));
+    // Can only close once. Second time should cause an error.
+    EXPECT_NE(OBOE_OK, OboeStream_close(oboeStream));
+}
+
+// Test Writing to an OboeStream
+void runtest_oboe_stream(oboe_sharing_mode_t requestedSharingMode) {
+    const oboe_sample_rate_t requestedSampleRate = 48000;
+    const oboe_sample_rate_t requestedSamplesPerFrame = 2;
+    const oboe_audio_format_t requestedDataFormat = OBOE_AUDIO_FORMAT_PCM16;
+
+    oboe_sample_rate_t actualSampleRate = -1;
+    int32_t actualSamplesPerFrame = -1;
+    oboe_audio_format_t actualDataFormat = OBOE_AUDIO_FORMAT_INVALID;
+    oboe_sharing_mode_t actualSharingMode;
+    oboe_size_frames_t framesPerBurst = -1;
+    int writeLoops = 0;
+
+    oboe_size_frames_t framesWritten = 0;
+    oboe_position_frames_t framesTotal = 0;
+    oboe_position_frames_t oboeFramesRead = 0;
+    oboe_position_frames_t oboeFramesRead1 = 0;
+    oboe_position_frames_t oboeFramesRead2 = 0;
+    oboe_position_frames_t oboeFramesWritten = 0;
+
+    oboe_nanoseconds_t timeoutNanos;
+
+    oboe_stream_state_t state = OBOE_STREAM_STATE_UNINITIALIZED;
+    OboeStreamBuilder oboeBuilder;
+    OboeStream oboeStream;
+
+    oboe_result_t result = OBOE_OK;
+
+    // Use an OboeStreamBuilder to define the stream.
+    result = Oboe_createStreamBuilder(&oboeBuilder);
+    ASSERT_EQ(OBOE_OK, result);
+
+    // Request stream properties.
+    EXPECT_EQ(OBOE_OK, OboeStreamBuilder_setSampleRate(oboeBuilder, requestedSampleRate));
+    EXPECT_EQ(OBOE_OK, OboeStreamBuilder_setSamplesPerFrame(oboeBuilder, requestedSamplesPerFrame));
+    EXPECT_EQ(OBOE_OK, OboeStreamBuilder_setFormat(oboeBuilder, requestedDataFormat));
+    EXPECT_EQ(OBOE_OK, OboeStreamBuilder_setSharingMode(oboeBuilder, requestedSharingMode));
+
+    // Create an OboeStream using the Builder.
+    ASSERT_EQ(OBOE_OK, OboeStreamBuilder_openStream(oboeBuilder, &oboeStream));
+    EXPECT_EQ(OBOE_OK, OboeStreamBuilder_delete(oboeBuilder));
+
+    EXPECT_EQ(OBOE_OK, OboeStream_getState(oboeStream, &state));
+    EXPECT_EQ(OBOE_STREAM_STATE_OPEN, state);
+
+    // Check to see what kind of stream we actually got.
+    EXPECT_EQ(OBOE_OK, OboeStream_getSampleRate(oboeStream, &actualSampleRate));
+    ASSERT_TRUE(actualSampleRate >= 44100 && actualSampleRate <= 96000);  // TODO what is range?
+
+    EXPECT_EQ(OBOE_OK, OboeStream_getSamplesPerFrame(oboeStream, &actualSamplesPerFrame));
+    ASSERT_TRUE(actualSamplesPerFrame >= 1 && actualSamplesPerFrame <= 16); // TODO what is max?
+
+    EXPECT_EQ(OBOE_OK, OboeStream_getSharingMode(oboeStream, &actualSharingMode));
+    ASSERT_TRUE(actualSharingMode == OBOE_SHARING_MODE_EXCLUSIVE
+                || actualSharingMode == OBOE_SHARING_MODE_LEGACY);
+
+    EXPECT_EQ(OBOE_OK, OboeStream_getFormat(oboeStream, &actualDataFormat));
+    EXPECT_NE(OBOE_AUDIO_FORMAT_INVALID, actualDataFormat);
+
+    EXPECT_EQ(OBOE_OK, OboeStream_getFramesPerBurst(oboeStream, &framesPerBurst));
+    ASSERT_TRUE(framesPerBurst >= 16 && framesPerBurst <= 1024); // TODO what is min/max?
+
+    // Allocate a buffer for the audio data.
+    // TODO handle possibility of other data formats
+    ASSERT_TRUE(actualDataFormat == OBOE_AUDIO_FORMAT_PCM16);
+    size_t dataSizeSamples = framesPerBurst * actualSamplesPerFrame;
+    int16_t *data = (int16_t *) calloc(dataSizeSamples, sizeof(int16_t));
+    ASSERT_TRUE(nullptr != data);
+
+    // Prime the buffer.
+    timeoutNanos = 0;
+    do {
+        framesWritten = OboeStream_write(oboeStream, data, framesPerBurst, timeoutNanos);
+        // There should be some room for priming the buffer.
+        framesTotal += framesWritten;
+        ASSERT_GE(framesWritten, 0);
+        ASSERT_LE(framesWritten, framesPerBurst);
+    } while (framesWritten > 0);
+    ASSERT_TRUE(framesTotal > 0);
+
+    // Start/write/pause more than once to see if it fails after the first time.
+    // Write some data and measure the rate to see if the timing is OK.
+    for (int numLoops = 0; numLoops < 2; numLoops++) {
+        // Start and wait for server to respond.
+        ASSERT_EQ(OBOE_OK, OboeStream_requestStart(oboeStream));
+        ASSERT_EQ(OBOE_OK, OboeStream_waitForStateChange(oboeStream,
+                                                         OBOE_STREAM_STATE_STARTING,
+                                                         &state,
+                                                         DEFAULT_STATE_TIMEOUT));
+        EXPECT_EQ(OBOE_STREAM_STATE_STARTED, state);
+
+        // Write some data while we are running. Read counter should be advancing.
+        writeLoops = 1 * actualSampleRate / framesPerBurst; // 1 second
+        ASSERT_LT(2, writeLoops); // detect absurdly high framesPerBurst
+        timeoutNanos = 10 * OBOE_NANOS_PER_SECOND * framesPerBurst / actualSampleRate; // bursts
+        framesWritten = 1;
+        ASSERT_EQ(OBOE_OK, OboeStream_getFramesRead(oboeStream, &oboeFramesRead));
+        oboeFramesRead1 = oboeFramesRead;
+        oboe_nanoseconds_t beginTime = Oboe_getNanoseconds(OBOE_CLOCK_MONOTONIC);
+        do {
+            framesWritten = OboeStream_write(oboeStream, data, framesPerBurst, timeoutNanos);
+            ASSERT_GE(framesWritten, 0);
+            ASSERT_LE(framesWritten, framesPerBurst);
+
+            framesTotal += framesWritten;
+            EXPECT_EQ(OBOE_OK, OboeStream_getFramesWritten(oboeStream, &oboeFramesWritten));
+            EXPECT_EQ(framesTotal, oboeFramesWritten);
+
+            // Try to get a more accurate measure of the sample rate.
+            if (beginTime == 0) {
+                EXPECT_EQ(OBOE_OK, OboeStream_getFramesRead(oboeStream, &oboeFramesRead));
+                if (oboeFramesRead > oboeFramesRead1) { // is read pointer advancing
+                    beginTime = Oboe_getNanoseconds(OBOE_CLOCK_MONOTONIC);
+                    oboeFramesRead1 = oboeFramesRead;
+                }
+            }
+        } while (framesWritten > 0 && writeLoops-- > 0);
+
+        EXPECT_EQ(OBOE_OK, OboeStream_getFramesRead(oboeStream, &oboeFramesRead2));
+        oboe_nanoseconds_t endTime = Oboe_getNanoseconds(OBOE_CLOCK_MONOTONIC);
+        ASSERT_GT(oboeFramesRead2, 0);
+        ASSERT_GT(oboeFramesRead2, oboeFramesRead1);
+        ASSERT_LE(oboeFramesRead2, oboeFramesWritten);
+
+        // TODO why is legacy so inaccurate?
+        const double rateTolerance = 200.0; // arbitrary tolerance for sample rate
+        if (requestedSharingMode != OBOE_SHARING_MODE_LEGACY) {
+            // Calculate approximate sample rate and compare with stream rate.
+            double seconds = (endTime - beginTime) / (double) OBOE_NANOS_PER_SECOND;
+            double measuredRate = (oboeFramesRead2 - oboeFramesRead1) / seconds;
+            ASSERT_NEAR(actualSampleRate, measuredRate, rateTolerance);
+        }
+
+        // Request async pause and wait for server to say that it has completed the pause.
+        ASSERT_EQ(OBOE_OK, OboeStream_requestPause(oboeStream));
+        EXPECT_EQ(OBOE_OK, OboeStream_waitForStateChange(oboeStream,
+                                                OBOE_STREAM_STATE_PAUSING,
+                                                &state,
+                                                DEFAULT_STATE_TIMEOUT));
+        EXPECT_EQ(OBOE_STREAM_STATE_PAUSED, state);
+    }
+
+    // Make sure the read counter is not advancing when we are paused.
+    ASSERT_EQ(OBOE_OK, OboeStream_getFramesRead(oboeStream, &oboeFramesRead));
+    ASSERT_GE(oboeFramesRead, oboeFramesRead2); // monotonic increase
+
+    // Use this to sleep by waiting for something that won't happen.
+    OboeStream_waitForStateChange(oboeStream, OBOE_STREAM_STATE_PAUSED, &state, timeoutNanos);
+    ASSERT_EQ(OBOE_OK, OboeStream_getFramesRead(oboeStream, &oboeFramesRead2));
+    EXPECT_EQ(oboeFramesRead, oboeFramesRead2);
+
+    // ------------------- TEST FLUSH -----------------
+    // Prime the buffer.
+    timeoutNanos = 0;
+    writeLoops = 100;
+    do {
+        framesWritten = OboeStream_write(oboeStream, data, framesPerBurst, timeoutNanos);
+        framesTotal += framesWritten;
+    } while (framesWritten > 0 && writeLoops-- > 0);
+    EXPECT_EQ(0, framesWritten);
+
+    // Flush and wait for server to respond.
+    ASSERT_EQ(OBOE_OK, OboeStream_requestFlush(oboeStream));
+    EXPECT_EQ(OBOE_OK, OboeStream_waitForStateChange(oboeStream,
+                                                     OBOE_STREAM_STATE_FLUSHING,
+                                                     &state,
+                                                     DEFAULT_STATE_TIMEOUT));
+    EXPECT_EQ(OBOE_STREAM_STATE_FLUSHED, state);
+
+    // After a flush, the read counter should be caught up with the write counter.
+    EXPECT_EQ(OBOE_OK, OboeStream_getFramesWritten(oboeStream, &oboeFramesWritten));
+    EXPECT_EQ(framesTotal, oboeFramesWritten);
+    EXPECT_EQ(OBOE_OK, OboeStream_getFramesRead(oboeStream, &oboeFramesRead));
+    EXPECT_EQ(oboeFramesRead, oboeFramesWritten);
+
+    // The buffer should be empty after a flush so we should be able to write.
+    framesWritten = OboeStream_write(oboeStream, data, framesPerBurst, timeoutNanos);
+    // There should be some room for priming the buffer.
+    ASSERT_TRUE(framesWritten > 0 && framesWritten <= framesPerBurst);
+
+    EXPECT_EQ(OBOE_OK, OboeStream_close(oboeStream));
+    free(data);
+}
+
+// Test Writing to an OboeStream using LEGACY sharing mode.
+TEST(test_aaudio, oboe_stream_legacy) {
+    runtest_oboe_stream(OBOE_SHARING_MODE_LEGACY);
+}
+
+/* TODO Enable exclusive mode test.
+// Test Writing to an OboeStream using EXCLUSIVE sharing mode.
+TEST(test_aaudio, oboe_stream_exclusive) {
+    runtest_oboe_stream(OBOE_SHARING_MODE_EXCLUSIVE);
+}
+*/
+
+#define OBOE_THREAD_ANSWER          1826375
+#define OBOE_THREAD_DURATION_MSEC       500
+
+static void *TestOboeStreamThreadProc(void *arg) {
+    OboeStream oboeStream = (OboeStream) reinterpret_cast<size_t>(arg);
+    oboe_stream_state_t state;
+
+    // Use this to sleep by waiting for something that won't happen.
+    EXPECT_EQ(OBOE_OK, OboeStream_getState(oboeStream, &state));
+    OboeStream_waitForStateChange(oboeStream, OBOE_STREAM_STATE_PAUSED, &state,
+            OBOE_THREAD_DURATION_MSEC * OBOE_NANOS_PER_MILLISECOND);
+    return reinterpret_cast<void *>(OBOE_THREAD_ANSWER);
+}
+
+// Test creating a stream related thread.
+TEST(test_aaudio, oboe_stream_thread_basic) {
+    OboeStreamBuilder oboeBuilder;
+    OboeStream oboeStream;
+    oboe_result_t result = OBOE_OK;
+    void *threadResult;
+
+    // Use an OboeStreamBuilder to define the stream.
+    result = Oboe_createStreamBuilder(&oboeBuilder);
+    ASSERT_EQ(OBOE_OK, result);
+
+    // Create an OboeStream using the Builder.
+    ASSERT_EQ(OBOE_OK, OboeStreamBuilder_openStream(oboeBuilder, &oboeStream));
+
+    // Start a thread.
+    ASSERT_EQ(OBOE_OK, OboeStream_createThread(oboeStream,
+            10 * OBOE_NANOS_PER_MILLISECOND,
+            TestOboeStreamThreadProc,
+            reinterpret_cast<void *>(oboeStream)));
+    // Thread already started.
+    ASSERT_NE(OBOE_OK, OboeStream_createThread(oboeStream,   // should fail!
+            10 * OBOE_NANOS_PER_MILLISECOND,
+            TestOboeStreamThreadProc,
+            reinterpret_cast<void *>(oboeStream)));
+
+    // Wait for the thread to finish.
+    ASSERT_EQ(OBOE_OK, OboeStream_joinThread(oboeStream,
+            &threadResult, 2 * OBOE_THREAD_DURATION_MSEC * OBOE_NANOS_PER_MILLISECOND));
+    // The thread returns a special answer.
+    ASSERT_EQ(OBOE_THREAD_ANSWER, (int)reinterpret_cast<size_t>(threadResult));
+
+    // Thread should already be joined.
+    ASSERT_NE(OBOE_OK, OboeStream_joinThread(oboeStream,  // should fail!
+            &threadResult, 2 * OBOE_THREAD_DURATION_MSEC * OBOE_NANOS_PER_MILLISECOND));
+
+    // Cleanup
+    EXPECT_EQ(OBOE_OK, OboeStreamBuilder_delete(oboeBuilder));
+    EXPECT_EQ(OBOE_OK, OboeStream_close(oboeStream));
+}
+
+int main(int argc, char **argv) {
+    testing::InitGoogleTest(&argc, argv);
+
+    return RUN_ALL_TESTS();
+}
diff --git a/tests/tests/preference2/AndroidManifest.xml b/tests/tests/preference2/AndroidManifest.xml
index 8d2b823..2c90fef 100644
--- a/tests/tests/preference2/AndroidManifest.xml
+++ b/tests/tests/preference2/AndroidManifest.xml
@@ -30,6 +30,7 @@
             android:name="android.preference2.cts.PreferenceFromCodeActivity" >
         </activity>
         <activity android:name="android.preference2.cts.PreferencesFromXml" />
+        <activity android:name="android.preference2.cts.PreferencesFromXmlNested" />
         <activity android:name="android.preference2.cts.PreferenceWithHeaders" />
         <activity android:name="android.preference2.cts.FragmentPreferences" />
     </application>
diff --git a/tests/tests/preference2/res/xml/pref_nested.xml b/tests/tests/preference2/res/xml/pref_nested.xml
new file mode 100755
index 0000000..0c03b2d
--- /dev/null
+++ b/tests/tests/preference2/res/xml/pref_nested.xml
@@ -0,0 +1,36 @@
+<?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

+  -->

+<PreferenceScreen

+    xmlns:android="http://schemas.android.com/apk/res/android">

+

+    <PreferenceCategory

+        android:key="pref_category"

+        android:title="@string/launch_preferences">

+

+        <PreferenceScreen

+            android:key="pref_screen_inner"

+            android:title="@string/title_screen_preference"

+            android:summary="@string/summary_screen_preference">

+

+            <CheckBoxPreference

+                android:key="pref_checkbox"

+                android:title="Test"

+                android:summary="Test" />

+

+        </PreferenceScreen>

+

+    </PreferenceCategory>

+</PreferenceScreen>

diff --git a/tests/tests/preference2/src/android/preference2/cts/PreferenceParentGroupTest.java b/tests/tests/preference2/src/android/preference2/cts/PreferenceParentGroupTest.java
new file mode 100644
index 0000000..5de6e81
--- /dev/null
+++ b/tests/tests/preference2/src/android/preference2/cts/PreferenceParentGroupTest.java
@@ -0,0 +1,132 @@
+/*
+ * 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.preference2.cts;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+
+import android.preference.CheckBoxPreference;
+import android.preference.PreferenceCategory;
+import android.preference.PreferenceScreen;
+import android.support.test.filters.SmallTest;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Tests for {@link android.preference.Preference#getParent()} feature.
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class PreferenceParentGroupTest {
+
+    private PreferencesFromXmlNested mActivity;
+
+    @Rule
+    public ActivityTestRule<PreferencesFromXmlNested> mActivityRule =
+            new ActivityTestRule<>(PreferencesFromXmlNested.class);
+
+
+    @Before
+    public void setup() {
+        mActivity = mActivityRule.getActivity();
+    }
+
+    /**
+     * Tests that parent PreferenceGroup is correctly assigned and removed when creating preferences
+     * from code.
+     */
+    @Test
+    public void parentViaCodeTest() {
+        PreferenceScreen screen = mActivity.getPreferenceScreen();
+        assertNull(screen.getParent());
+
+        PreferenceCategory category = new PreferenceCategory(mActivity);
+        assertNull(category.getParent());
+
+        CheckBoxPreference pref = new CheckBoxPreference(mActivity);
+        assertNull(pref.getParent());
+
+        screen.addPreference(category);
+        assertEquals(screen, category.getParent());
+
+        category.addPreference(pref);
+        assertEquals(category, pref.getParent());
+
+        screen.removePreference(category);
+        assertNull(category.getParent());
+
+        category.removePreference(pref);
+        assertNull(pref.getParent());
+    }
+
+    /**
+     * Tests that parent PreferenceGroup is correctly assigned during inflation and can be modified.
+     * To see the tested hierarchy check pref_nested.xml.
+     */
+    @Test
+    public void parentViaInflationTest() {
+        PreferenceScreen screen = mActivity.getPreferenceScreen();
+
+        PreferenceCategory category = (PreferenceCategory) screen.findPreference("pref_category");
+        assertNotNull(category);
+
+        PreferenceScreen screenInner =
+                (PreferenceScreen) screen.findPreference("pref_screen_inner");
+        assertNotNull(screenInner);
+
+        CheckBoxPreference pref = (CheckBoxPreference) screen.findPreference("pref_checkbox");
+        assertNotNull(pref);
+
+        // Validate parents
+        assertEquals(screen, category.getParent());
+        assertEquals(category, screenInner.getParent());
+        assertEquals(screenInner, pref.getParent());
+
+        // Remove and validate
+        pref.getParent().removePreference(pref);
+        assertNull(pref.getParent());
+        assertEquals(0, screenInner.getPreferenceCount());
+    }
+
+    /**
+     * Adds preference into two different groups without removing it first.
+     */
+    @Test
+    public void parentDoubleAddTest() throws InterruptedException {
+        PreferenceScreen screen = mActivity.getPreferenceScreen();
+
+        PreferenceCategory category = new PreferenceCategory(mActivity);
+        screen.addPreference(category);
+
+        PreferenceCategory category2 = new PreferenceCategory(mActivity);
+        screen.addPreference(category2);
+
+        CheckBoxPreference pref = new CheckBoxPreference(mActivity);
+        assertNull(pref.getParent());
+
+        category.addPreference(pref);
+        category2.addPreference(pref);
+
+        assertEquals(category2, pref.getParent());
+    }
+}
diff --git a/tests/tests/preference2/src/android/preference2/cts/PreferencesFromXmlNested.java b/tests/tests/preference2/src/android/preference2/cts/PreferencesFromXmlNested.java
new file mode 100644
index 0000000..df31c1e
--- /dev/null
+++ b/tests/tests/preference2/src/android/preference2/cts/PreferencesFromXmlNested.java
@@ -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
+ */
+
+package android.preference2.cts;
+
+import android.os.Bundle;
+import android.preference.PreferenceActivity;
+import android.preference2.cts.R;
+
+public class PreferencesFromXmlNested extends PreferenceActivity {
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        addPreferencesFromResource(R.xml.pref_nested);
+    }
+}
diff --git a/tests/tests/provider/src/android/provider/cts/VoicemailContractTest.java b/tests/tests/provider/src/android/provider/cts/VoicemailContractTest.java
index 8e0a6cc..6dd4d3c 100644
--- a/tests/tests/provider/src/android/provider/cts/VoicemailContractTest.java
+++ b/tests/tests/provider/src/android/provider/cts/VoicemailContractTest.java
@@ -63,7 +63,11 @@
         final String[] VOICEMAILS_PROJECTION = new String[] {
                 Voicemails._ID, Voicemails.NUMBER, Voicemails.DATE, Voicemails.DURATION,
                 Voicemails.IS_READ, Voicemails.SOURCE_PACKAGE, Voicemails.SOURCE_DATA,
-                Voicemails.HAS_CONTENT, Voicemails.MIME_TYPE};
+                Voicemails.HAS_CONTENT, Voicemails.MIME_TYPE, Voicemails.TRANSCRIPTION,
+                Voicemails.PHONE_ACCOUNT_COMPONENT_NAME,
+                Voicemails.PHONE_ACCOUNT_ID, Voicemails.DIRTY, Voicemails.DELETED,
+                Voicemails.LAST_MODIFIED, Voicemails.BACKED_UP, Voicemails.RESTORED,
+                Voicemails.ARCHIVED, Voicemails.IS_OMTP_VOICEMAIL};
         final int ID_INDEX = 0;
         final int NUMBER_INDEX = 1;
         final int DATE_INDEX = 2;
@@ -73,6 +77,16 @@
         final int SOURCE_DATA_INDEX = 6;
         final int HAS_CONTENT_INDEX = 7;
         final int MIME_TYPE_INDEX = 8;
+        final int TRANSCRIPTION_INDEX= 9;
+        final int PHONE_ACCOUNT_COMPONENT_NAME_INDEX = 10;
+        final int PHONE_ACCOUNT_ID_INDEX = 11;
+        final int DIRTY_INDEX = 12;
+        final int DELETED_INDEX = 13;
+        final int LAST_MODIFIED_INDEX = 14;
+        final int BACKED_UP_INDEX = 15;
+        final int RESTORED_INDEX = 16;
+        final int ARCHIVED_INDEX = 17;
+        final int IS_OMTP_VOICEMAIL_INDEX = 18;
 
         String insertCallsNumber = "0123456789";
         long insertCallsDuration = 120;
@@ -95,6 +109,15 @@
         value.put(Voicemails.MIME_TYPE, insertMimeType);
         value.put(Voicemails.IS_READ, false);
         value.put(Voicemails.HAS_CONTENT, true);
+        value.put(Voicemails.TRANSCRIPTION, "foo");
+        value.put(Voicemails.PHONE_ACCOUNT_COMPONENT_NAME, "com.foo");
+        value.put(Voicemails.PHONE_ACCOUNT_ID, "bar");
+        value.put(Voicemails.DIRTY, 0);
+        value.put(Voicemails.DELETED, 0);
+        value.put(Voicemails.BACKED_UP, 0);
+        value.put(Voicemails.RESTORED, 0);
+        value.put(Voicemails.ARCHIVED, 0);
+        value.put(Voicemails.IS_OMTP_VOICEMAIL, 0);
 
         Uri uri = mVoicemailProvider.insert(mVoicemailContentUri, value);
         Cursor cursor = mVoicemailProvider.query(
@@ -110,6 +133,15 @@
         assertEquals(insertMimeType, cursor.getString(MIME_TYPE_INDEX));
         assertEquals(0, cursor.getInt(IS_READ_INDEX));
         assertEquals(1, cursor.getInt(HAS_CONTENT_INDEX));
+        assertEquals("foo",cursor.getString(TRANSCRIPTION_INDEX));
+        assertEquals("com.foo",cursor.getString(PHONE_ACCOUNT_COMPONENT_NAME_INDEX));
+        assertEquals("bar",cursor.getString(PHONE_ACCOUNT_ID_INDEX));
+        assertEquals(0,cursor.getInt(DIRTY_INDEX));
+        assertEquals(0,cursor.getInt(DELETED_INDEX));
+        assertEquals(0,cursor.getInt(BACKED_UP_INDEX));
+        assertEquals(0,cursor.getInt(RESTORED_INDEX));
+        assertEquals(0,cursor.getInt(ARCHIVED_INDEX));
+        assertEquals(0,cursor.getInt(IS_OMTP_VOICEMAIL_INDEX));
         int id = cursor.getInt(ID_INDEX);
         assertEquals(id, Integer.parseInt(uri.getLastPathSegment()));
         cursor.close();
@@ -120,6 +152,12 @@
         value.put(Voicemails.DATE, updateDate);
         value.put(Voicemails.DURATION, updateCallsDuration);
         value.put(Voicemails.SOURCE_DATA, updateSourceData);
+        value.put(Voicemails.DIRTY, 1);
+        value.put(Voicemails.DELETED, 1);
+        value.put(Voicemails.BACKED_UP, 1);
+        value.put(Voicemails.RESTORED, 1);
+        value.put(Voicemails.ARCHIVED, 1);
+        value.put(Voicemails.IS_OMTP_VOICEMAIL, 1);
 
         mVoicemailProvider.update(uri, value, null, null);
         cursor = mVoicemailProvider.query(mVoicemailContentUri, VOICEMAILS_PROJECTION,
@@ -131,6 +169,12 @@
         assertEquals(updateDate, cursor.getLong(DATE_INDEX));
         assertEquals(updateCallsDuration, cursor.getLong(DURATION_INDEX));
         assertEquals(updateSourceData, cursor.getString(SOURCE_DATA_INDEX));
+        assertEquals(1,cursor.getInt(DIRTY_INDEX));
+        assertEquals(1,cursor.getInt(DELETED_INDEX));
+        assertEquals(1,cursor.getInt(BACKED_UP_INDEX));
+        assertEquals(1,cursor.getInt(RESTORED_INDEX));
+        assertEquals(1,cursor.getInt(ARCHIVED_INDEX));
+        assertEquals(1,cursor.getInt(IS_OMTP_VOICEMAIL_INDEX));
         cursor.close();
 
         // Test: delete
diff --git a/tests/tests/security/src/android/security/cts/AslrTest.java b/tests/tests/security/src/android/security/cts/AslrTest.java
index 2cc4825..a28498b 100644
--- a/tests/tests/security/src/android/security/cts/AslrTest.java
+++ b/tests/tests/security/src/android/security/cts/AslrTest.java
@@ -96,8 +96,6 @@
 
     public void testRandomization() throws Exception {
         testMappingEntropy("stack");
-        testMappingEntropy("heap");
-        testMappingEntropy("anon:libc_malloc");
     }
 
     public void testOneExecutableIsPie() throws IOException {
diff --git a/tests/tests/telecom/src/android/telecom/cts/CallDetailsTest.java b/tests/tests/telecom/src/android/telecom/cts/CallDetailsTest.java
index d167249..1a3206f 100644
--- a/tests/tests/telecom/src/android/telecom/cts/CallDetailsTest.java
+++ b/tests/tests/telecom/src/android/telecom/cts/CallDetailsTest.java
@@ -18,7 +18,7 @@
 
 import static android.telecom.cts.TestUtils.*;
 
-import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.instanceOf;
 import static org.junit.Assert.assertThat;
 
 import android.graphics.drawable.Icon;
@@ -131,7 +131,7 @@
             return;
         }
 
-        assertThat(mCall.getDetails().getAccountHandle(), is(PhoneAccountHandle.class));
+        assertThat(mCall.getDetails().getAccountHandle(), instanceOf(PhoneAccountHandle.class));
         assertEquals(TEST_PHONE_ACCOUNT_HANDLE, mCall.getDetails().getAccountHandle());
     }
 
@@ -143,7 +143,7 @@
             return;
         }
 
-        assertThat(mCall.getDetails().getCallCapabilities(), is(Integer.class));
+        assertThat(mCall.getDetails().getCallCapabilities(), instanceOf(Integer.class));
         assertEquals(CALL_CAPABILITIES, mCall.getDetails().getCallCapabilities());
         assertTrue(mCall.getDetails().can(Call.Details.CAPABILITY_HOLD));
         assertTrue(mCall.getDetails().can(Call.Details.CAPABILITY_MUTE));
@@ -248,7 +248,7 @@
             return;
         }
 
-        assertThat(mCall.getDetails().getCallerDisplayName(), is(String.class));
+        assertThat(mCall.getDetails().getCallerDisplayName(), instanceOf(String.class));
         assertEquals(CALLER_DISPLAY_NAME, mCall.getDetails().getCallerDisplayName());
     }
 
@@ -260,7 +260,7 @@
             return;
         }
 
-        assertThat(mCall.getDetails().getCallerDisplayNamePresentation(), is(Integer.class));
+        assertThat(mCall.getDetails().getCallerDisplayNamePresentation(), instanceOf(Integer.class));
         assertEquals(CALLER_DISPLAY_NAME_PRESENTATION, mCall.getDetails().getCallerDisplayNamePresentation());
     }
 
@@ -272,7 +272,7 @@
             return;
         }
 
-        assertThat(mCall.getDetails().getCallProperties(), is(Integer.class));
+        assertThat(mCall.getDetails().getCallProperties(), instanceOf(Integer.class));
 
         assertEquals(CALL_PROPERTIES, mCall.getDetails().getCallProperties());
     }
@@ -285,7 +285,7 @@
             return;
         }
 
-        assertThat(mCall.getDetails().getConnectTimeMillis(), is(Long.class));
+        assertThat(mCall.getDetails().getConnectTimeMillis(), instanceOf(Long.class));
     }
 
     /**
@@ -296,7 +296,7 @@
             return;
         }
 
-        assertThat(mCall.getDetails().getDisconnectCause(), is(DisconnectCause.class));
+        assertThat(mCall.getDetails().getDisconnectCause(), instanceOf(DisconnectCause.class));
     }
 
     /**
@@ -308,7 +308,7 @@
         }
 
         if (mCall.getDetails().getExtras() != null) {
-            assertThat(mCall.getDetails().getExtras(), is(Bundle.class));
+            assertThat(mCall.getDetails().getExtras(), instanceOf(Bundle.class));
         }
     }
 
@@ -320,7 +320,7 @@
             return;
         }
 
-        assertThat(mCall.getDetails().getIntentExtras(), is(Bundle.class));
+        assertThat(mCall.getDetails().getIntentExtras(), instanceOf(Bundle.class));
     }
 
     /**
@@ -332,7 +332,7 @@
         }
 
         if (mCall.getDetails().getGatewayInfo() != null) {
-            assertThat(mCall.getDetails().getGatewayInfo(), is(GatewayInfo.class));
+            assertThat(mCall.getDetails().getGatewayInfo(), instanceOf(GatewayInfo.class));
         }
     }
 
@@ -344,7 +344,7 @@
             return;
         }
 
-        assertThat(mCall.getDetails().getHandle(), is(Uri.class));
+        assertThat(mCall.getDetails().getHandle(), instanceOf(Uri.class));
         assertEquals(getTestNumber(), mCall.getDetails().getHandle());
     }
 
@@ -356,7 +356,7 @@
             return;
         }
 
-        assertThat(mCall.getDetails().getHandlePresentation(), is(Integer.class));
+        assertThat(mCall.getDetails().getHandlePresentation(), instanceOf(Integer.class));
         assertEquals(MockConnectionService.CONNECTION_PRESENTATION, mCall.getDetails().getHandlePresentation());
     }
 
@@ -368,7 +368,7 @@
             return;
         }
 
-        assertThat(mCall.getDetails().getStatusHints(), is(StatusHints.class));
+        assertThat(mCall.getDetails().getStatusHints(), instanceOf(StatusHints.class));
         assertEquals(mStatusHints.getLabel(), mCall.getDetails().getStatusHints().getLabel());
         assertEquals(
                 mStatusHints.getIcon().toString(),
@@ -384,7 +384,7 @@
             return;
         }
 
-        assertThat(mCall.getDetails().getVideoState(), is(Integer.class));
+        assertThat(mCall.getDetails().getVideoState(), instanceOf(Integer.class));
     }
 
     /**
diff --git a/tests/tests/telephony/src/android/telephony/cts/VisualVoicemailServiceTest.java b/tests/tests/telephony/src/android/telephony/cts/VisualVoicemailServiceTest.java
index eaeea3e..558ef32 100644
--- a/tests/tests/telephony/src/android/telephony/cts/VisualVoicemailServiceTest.java
+++ b/tests/tests/telephony/src/android/telephony/cts/VisualVoicemailServiceTest.java
@@ -35,6 +35,7 @@
 import android.telecom.PhoneAccount;
 import android.telecom.PhoneAccountHandle;
 import android.telecom.TelecomManager;
+import android.telephony.TelephonyManager;
 import android.telephony.VisualVoicemailService;
 import android.telephony.VisualVoicemailSms;
 import android.telephony.VisualVoicemailSmsFilterSettings;
@@ -253,6 +254,15 @@
         assertEquals("", result.getFields().getString("key"));
     }
 
+    public void testGetVisualVoicemailPackageName_isSelf(){
+        if (!hasTelephony(mContext)) {
+            Log.d(TAG, "skipping test that requires telephony feature");
+            return;
+        }
+        TelephonyManager telephonyManager = mContext.getSystemService(TelephonyManager.class);
+        assertEquals(PACKAGE, telephonyManager.getVisualVoicemailPackageName(mPhoneAccountHandle));
+    }
+
     private VisualVoicemailSms getSmsFromText(String clientPrefix, String text) {
         return getSmsFromText(clientPrefix, text, true);
     }
diff --git a/tests/tests/text/src/android/text/cts/SpannableStringBuilderTest.java b/tests/tests/text/src/android/text/cts/SpannableStringBuilderTest.java
index 70a68ea..3e58853 100644
--- a/tests/tests/text/src/android/text/cts/SpannableStringBuilderTest.java
+++ b/tests/tests/text/src/android/text/cts/SpannableStringBuilderTest.java
@@ -629,6 +629,79 @@
     }
 
     @Test
+    public void testGetSpans_returnsInInsertionOrder_regular() {
+        assertGetSpans_returnsInInsertionOrder(Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+    }
+
+    @Test
+    public void testGetSpans_returnsInInsertionOrder_priority() {
+        assertGetSpans_returnsInInsertionOrder(
+                Spanned.SPAN_EXCLUSIVE_EXCLUSIVE | Spanned.SPAN_PRIORITY);
+    }
+
+    private static void assertGetSpans_returnsInInsertionOrder(int flags) {
+        final SpannableStringBuilder builder = new SpannableStringBuilder();
+        final SubscriptSpan[] expected = new SubscriptSpan[5];
+        for (int i = 0; i < expected.length; i++) {
+            final int currentLength = builder.length();
+            builder.append("12\n");
+            expected[i] = new SubscriptSpan();
+            builder.setSpan(expected[i], currentLength + 1, currentLength + 2, flags);
+        }
+
+        final SubscriptSpan[] spans = builder.getSpans(0, builder.length(), SubscriptSpan.class);
+
+        assertNotNull(spans);
+        assertEquals(expected.length, spans.length);
+        for (int i = 0; i < expected.length; i++) {
+            assertSame(expected[i], spans[i]);
+        }
+    }
+
+    @Test
+    public void testGetSpans_returnsInInsertionOrder_priorityAndRegular() {
+        // insert spans from end to start of the string, interleaved. for each alternation:
+        // * regular span start is less than the priority span;
+        // * setSpan for regular span is called before the priority span
+        // expected result is: priority spans in the insertion order, then regular spans in
+        // insertion order
+        final int arrayLength = 5;
+        final SpannableStringBuilder builder = new SpannableStringBuilder();
+        final SubscriptSpan[] regularSpans = new SubscriptSpan[arrayLength];
+        final SubscriptSpan[] prioritySpans = new SubscriptSpan[arrayLength];
+        for (int i = 0; i < arrayLength; i++) {
+            builder.append("12");
+            regularSpans[i] = new SubscriptSpan();
+            prioritySpans[i] = new SubscriptSpan();
+        }
+
+        // set spans on builder with pattern [regular, priority, regular, priority,...]
+        // in reverse order (regularSpan[0] is at the end of the builder, regularSpan[5] is at the
+        // begining.
+        for (int i = 1; i <= arrayLength; i++) {
+            final int spanStart = builder.length() - (i * 2);
+            builder.setSpan(regularSpans[i - 1], spanStart, spanStart + 1,
+                    Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+            builder.setSpan(prioritySpans[i - 1], spanStart + 1, spanStart + 2,
+                    Spanned.SPAN_EXCLUSIVE_EXCLUSIVE | Spanned.SPAN_PRIORITY);
+        }
+
+        final SubscriptSpan[] spans = builder.getSpans(0, builder.length(), SubscriptSpan.class);
+        assertNotNull(spans);
+        assertEquals(arrayLength * 2, spans.length);
+
+        // assert priority spans are returned before the regular spans, in the insertion order
+        for (int i = 0; i < arrayLength; i++) {
+            assertSame(prioritySpans[i], spans[i]);
+        }
+
+        // assert regular spans are returned after priority spans, in the insertion order
+        for (int i = 0; i < arrayLength; i++) {
+            assertSame(regularSpans[i], spans[i + arrayLength]);
+        }
+    }
+
+    @Test
     public void testGetSpans_returnsSpansInInsertionOrderWhenTheLaterCoversTheFirst() {
         String text = "p_in_s";
         SpannableStringBuilder builder = new SpannableStringBuilder(text);
diff --git a/tests/tests/text/src/android/text/cts/StaticLayoutTest.java b/tests/tests/text/src/android/text/cts/StaticLayoutTest.java
index f1e883c..da51c74 100644
--- a/tests/tests/text/src/android/text/cts/StaticLayoutTest.java
+++ b/tests/tests/text/src/android/text/cts/StaticLayoutTest.java
@@ -1196,8 +1196,8 @@
         assertEquals(0, layout.getLineForOffset(lineBreakOffset - 1));
 
         assertEquals(0, layout.getOffsetForHorizontal(0, 0.0f));
-        assertEquals(lineBreakOffset - 2, layout.getOffsetForHorizontal(0, width));
-        assertEquals(lineBreakOffset - 2, layout.getOffsetForHorizontal(0, width * 2));
+        assertEquals(lineBreakOffset, layout.getOffsetForHorizontal(0, width));
+        assertEquals(lineBreakOffset, layout.getOffsetForHorizontal(0, width * 2));
 
         final int lineCount = layout.getLineCount();
         assertEquals(testString.length(), layout.getOffsetForHorizontal(lineCount - 1, width));
diff --git a/tests/tests/tv/src/android/media/tv/cts/TvContractTest.java b/tests/tests/tv/src/android/media/tv/cts/TvContractTest.java
index 8811748..6cd4f4e 100644
--- a/tests/tests/tv/src/android/media/tv/cts/TvContractTest.java
+++ b/tests/tests/tv/src/android/media/tv/cts/TvContractTest.java
@@ -140,10 +140,11 @@
         super.tearDown();
     }
 
-    private static ContentValues createDummyChannelValues(String inputId) {
+    private static ContentValues createDummyChannelValues(String inputId, boolean preview) {
         ContentValues values = new ContentValues();
         values.put(TvContract.Channels.COLUMN_INPUT_ID, inputId);
-        values.put(TvContract.Channels.COLUMN_TYPE, TvContract.Channels.TYPE_OTHER);
+        values.put(TvContract.Channels.COLUMN_TYPE,
+                preview ? TvContract.Channels.TYPE_PREVIEW : TvContract.Channels.TYPE_OTHER);
         values.put(TvContract.Channels.COLUMN_SERVICE_TYPE,
                 TvContract.Channels.SERVICE_TYPE_AUDIO_VIDEO);
         values.put(TvContract.Channels.COLUMN_DISPLAY_NUMBER, "1");
@@ -168,6 +169,31 @@
         return values;
     }
 
+    private static ContentValues createDummyPreviewProgramValues(long channelId) {
+        ContentValues values = new ContentValues();
+        values.put(TvContract.Programs.COLUMN_CHANNEL_ID, channelId);
+        values.put(TvContract.Programs.COLUMN_INTERNAL_PROVIDER_ID, "ID-4321");
+        values.put(TvContract.Programs.COLUMN_PREVIEW_VIDEO_URI, "http://test.com/preview.mp4");
+        values.put(TvContract.Programs.COLUMN_PREVIEW_LAST_PLAYBACK_POSITION, 5000);
+        values.put(TvContract.Programs.COLUMN_PREVIEW_DURATION, 60000);
+        values.put(TvContract.Programs.COLUMN_PREVIEW_INTENT_URI,
+                "preview_app_link_intent");
+        values.put(TvContract.Programs.COLUMN_PREVIEW_WEIGHT, 100);
+        values.put(TvContract.Programs.COLUMN_TITLE, "program_title");
+        values.put(TvContract.Programs.COLUMN_SHORT_DESCRIPTION, "short_description");
+        values.put(TvContract.Programs.COLUMN_EPISODE_DISPLAY_NUMBER , "1A");
+        values.put(TvContract.Programs.COLUMN_EPISODE_TITLE, "episode_title");
+        values.put(TvContract.Programs.COLUMN_SEASON_DISPLAY_NUMBER , "2B");
+        values.put(TvContract.Programs.COLUMN_SEASON_TITLE, "season_title");
+        values.put(TvContract.Programs.COLUMN_CANONICAL_GENRE, TvContract.Programs.Genres.encode(
+                TvContract.Programs.Genres.SPORTS, TvContract.Programs.Genres.DRAMA));
+        TvContentRating rating = TvContentRating.createRating("android.media.tv", "US_TVPG",
+                "US_TVPG_TV_MA", "US_TVPG_S", "US_TVPG_V");
+        values.put(TvContract.Programs.COLUMN_CONTENT_RATING, rating.flattenToString());
+
+        return values;
+    }
+
     private static ContentValues createDummyRecordedProgramValues(String inputId, long channelId) {
         ContentValues values = new ContentValues();
         values.put(TvContract.RecordedPrograms.COLUMN_INPUT_ID, inputId);
@@ -275,7 +301,7 @@
             return;
         }
         // Test: insert
-        ContentValues values = createDummyChannelValues(mInputId);
+        ContentValues values = createDummyChannelValues(mInputId, false);
 
         Uri rowUri = mContentResolver.insert(mChannelsUri, values);
         long channelId = ContentUris.parseId(rowUri);
@@ -300,7 +326,7 @@
 
     private void verifyProgram(Uri programUri, ContentValues expectedValues, long programId) {
         try (Cursor cursor = mContentResolver.query(
-                programUri, PROGRAMS_PROJECTION, null, null, null)) {
+                programUri, null, null, null, null)) {
             assertNotNull(cursor);
             assertEquals(cursor.getCount(), 1);
             assertTrue(cursor.moveToNext());
@@ -331,6 +357,16 @@
             verifyBlobColumn(cursor, expectedValues,
                     TvContract.Programs.COLUMN_INTERNAL_PROVIDER_DATA);
             verifyIntegerColumn(cursor, expectedValues, TvContract.Programs.COLUMN_VERSION_NUMBER);
+
+            verifyStringColumn(cursor, expectedValues,
+                    TvContract.Programs.COLUMN_INTERNAL_PROVIDER_ID);
+            verifyStringColumn(cursor, expectedValues, TvContract.Programs.COLUMN_PREVIEW_VIDEO_URI);
+            verifyIntegerColumn(cursor, expectedValues,
+                    TvContract.Programs.COLUMN_PREVIEW_LAST_PLAYBACK_POSITION);
+            verifyIntegerColumn(cursor, expectedValues, TvContract.Programs.COLUMN_PREVIEW_DURATION);
+            verifyStringColumn(cursor, expectedValues,
+                    TvContract.Programs.COLUMN_PREVIEW_INTENT_URI);
+            verifyIntegerColumn(cursor, expectedValues, TvContract.Programs.COLUMN_PREVIEW_WEIGHT);
         }
     }
 
@@ -363,7 +399,7 @@
             return;
         }
         // Set-up: add a channel.
-        ContentValues values = createDummyChannelValues(mInputId);
+        ContentValues values = createDummyChannelValues(mInputId, false);
         Uri channelUri = mContentResolver.insert(mChannelsUri, values);
         Uri logoUri = TvContract.buildChannelLogoUri(channelUri);
         Bitmap logo = BitmapFactory.decodeResource(getContext().getResources(), R.drawable.robot);
@@ -398,6 +434,33 @@
         verifyProgram(programUri, values, programId);
 
         // Test: update
+        values.put(TvContract.Programs.COLUMN_TITLE, "new_program_title");
+        values.put(TvContract.Programs.COLUMN_SHORT_DESCRIPTION, "");
+        values.put(TvContract.Programs.COLUMN_INTERNAL_PROVIDER_DATA, "Coffee".getBytes());
+
+        mContentResolver.update(programUri, values, null, null);
+        verifyProgram(programUri, values, programId);
+
+        // Test: delete
+        mContentResolver.delete(programsUri, null, null);
+        try (Cursor cursor = mContentResolver.query(programsUri, null, null, null, null)) {
+            assertEquals(0, cursor.getCount());
+        }
+    }
+
+    public void verifyProgramsTableWithPreviewTypeEntry(Uri programsUri, long channelId) {
+        if (!Utils.hasTvInputFramework(getContext())) {
+            return;
+        }
+        // Test: insert
+        ContentValues values = createDummyPreviewProgramValues(channelId);
+
+        Uri rowUri = mContentResolver.insert(programsUri, values);
+        long programId = ContentUris.parseId(rowUri);
+        Uri programUri = TvContract.buildProgramUri(programId);
+        verifyProgram(programUri, values, programId);
+
+        // Test: update
         values.put(TvContract.Programs.COLUMN_EPISODE_TITLE, "Sample title");
         values.put(TvContract.Programs.COLUMN_SHORT_DESCRIPTION, "Short description");
         values.put(TvContract.Programs.COLUMN_INTERNAL_PROVIDER_DATA, "Coffee".getBytes());
@@ -457,7 +520,7 @@
             return;
         }
         // Set-up: add a channel.
-        ContentValues values = createDummyChannelValues(mInputId);
+        ContentValues values = createDummyChannelValues(mInputId, false);
         Uri channelUri = mContentResolver.insert(mChannelsUri, values);
         long channelId = ContentUris.parseId(channelUri);
 
@@ -469,6 +532,19 @@
                 channelId);
     }
 
+    public void testProgramsTableWithPreviewType() throws Exception {
+        if (!Utils.hasTvInputFramework(getContext())) {
+            return;
+        }
+        // Set-up: add a preview type channel.
+        ContentValues values = createDummyChannelValues(mInputId, true);
+        Uri channelUri = mContentResolver.insert(mChannelsUri, values);
+        long channelId = ContentUris.parseId(channelUri);
+
+        verifyProgramsTableWithPreviewTypeEntry(TvContract.buildProgramsUriForChannel(channelUri),
+                channelId);
+    }
+
     private void verifyOverlap(long startMillis, long endMillis, int expectedCount,
             long channelId, Uri channelUri) {
         try (Cursor cursor = mContentResolver.query(TvContract.buildProgramsUriForChannel(
@@ -490,7 +566,7 @@
         final long hour = 3600000l;
 
         // Set-up: add a channel and program.
-        ContentValues values = createDummyChannelValues(mInputId);
+        ContentValues values = createDummyChannelValues(mInputId, false);
         Uri channelUri = mContentResolver.insert(mChannelsUri, values);
         long channelId = ContentUris.parseId(channelUri);
         Uri programsUri = TvContract.buildProgramsUriForChannel(channelId);
@@ -600,7 +676,7 @@
             return;
         }
         // Set-up: add a channel.
-        ContentValues values = createDummyChannelValues(mInputId);
+        ContentValues values = createDummyChannelValues(mInputId, false);
         Uri channelUri = mContentResolver.insert(mChannelsUri, values);
         long channelId = ContentUris.parseId(channelUri);
 
@@ -890,7 +966,7 @@
     }
 
     private Uri insertProgramWithBroadcastGenre(String[] broadcastGenre) {
-        ContentValues values = createDummyChannelValues(mInputId);
+        ContentValues values = createDummyChannelValues(mInputId, false);
         Uri channelUri = mContentResolver.insert(Channels.CONTENT_URI, values);
         long channelId = ContentUris.parseId(channelUri);
         long curTime = System.currentTimeMillis();
diff --git a/tests/tests/view/AndroidManifest.xml b/tests/tests/view/AndroidManifest.xml
index edea284..54f2612 100644
--- a/tests/tests/view/AndroidManifest.xml
+++ b/tests/tests/view/AndroidManifest.xml
@@ -261,6 +261,14 @@
                 <category android:name="android.intent.category.LAUNCHER" />
             </intent-filter>
         </activity>
+
+        <activity android:name="android.view.cts.PointerCaptureCtsActivity"
+                  android:theme="@style/WhiteBackgroundTheme">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
     </application>
 
     <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
diff --git a/tests/tests/view/res/layout/focus_handling_auto_layout.xml b/tests/tests/view/res/layout/focus_handling_auto_layout.xml
new file mode 100644
index 0000000..07b4423
--- /dev/null
+++ b/tests/tests/view/res/layout/focus_handling_auto_layout.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:orientation="vertical" android:layout_width="match_parent"
+    android:layout_height="match_parent">
+
+    <View
+        android:id="@+id/focusableauto"
+        android:focusable="auto"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content" />
+    <View
+        android:id="@+id/focusabledefault"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content" />
+    <View
+        android:id="@+id/onlyclickable"
+        android:clickable="true"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content" />
+    <View
+        android:id="@+id/clickablenotfocusable"
+        android:clickable="true"
+        android:focusable="false"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content" />
+</LinearLayout>
\ No newline at end of file
diff --git a/tests/tests/view/res/layout/focus_handling_layout.xml b/tests/tests/view/res/layout/focus_handling_layout.xml
index 84c8e6a..4c0178a 100644
--- a/tests/tests/view/res/layout/focus_handling_layout.xml
+++ b/tests/tests/view/res/layout/focus_handling_layout.xml
@@ -53,5 +53,15 @@
         android:nextFocusUp="@id/view2"
         android:nextFocusLeft="@id/view3"
         android:text="@string/id_ok"/>
+
+    <LinearLayout
+        android:id="@+id/auto_test_area"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:orientation="vertical"
+        android:layout_below="@id/view4"
+        android:layout_alignParentLeft="true">
+    </LinearLayout>
+
 </RelativeLayout>
 
diff --git a/tests/tests/view/res/layout/pointer_capture_layout.xml b/tests/tests/view/res/layout/pointer_capture_layout.xml
new file mode 100644
index 0000000..1e40070
--- /dev/null
+++ b/tests/tests/view/res/layout/pointer_capture_layout.xml
@@ -0,0 +1,57 @@
+<?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/top"
+              android:layout_width="match_parent"
+              android:layout_height="match_parent"
+              android:orientation="vertical">
+
+    <LinearLayout
+            android:id="@+id/outer"
+            android:orientation="horizontal"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_margin="20dp"
+            android:gravity="center"
+            android:background="#eee">
+
+        <LinearLayout
+                android:id="@+id/inner"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_margin="20dp"
+                android:background="#ddd">
+
+            <LinearLayout
+                    android:id="@+id/target"
+                    android:layout_width="20dp"
+                    android:layout_height="20dp"
+                    android:layout_margin="20dp"
+                    android:background="#bbb"/>
+
+        </LinearLayout>
+
+        <LinearLayout
+                android:id="@+id/target2"
+                android:layout_width="20dp"
+                android:layout_height="20dp"
+                android:layout_margin="40dp"
+                android:background="#bbb"/>
+
+    </LinearLayout>
+
+</LinearLayout>
diff --git a/tests/tests/view/src/android/view/cts/HoverTest.java b/tests/tests/view/src/android/view/cts/HoverTest.java
index 24e3020..45b56e9 100644
--- a/tests/tests/view/src/android/view/cts/HoverTest.java
+++ b/tests/tests/view/src/android/view/cts/HoverTest.java
@@ -16,31 +16,35 @@
 
 package android.view.cts;
 
+import static com.android.compatibility.common.util.CtsMouseUtil.clearHoverListener;
+import static com.android.compatibility.common.util.CtsMouseUtil.installHoverListener;
+import static com.android.compatibility.common.util.CtsMouseUtil.obtainMouseEvent;
+import static com.android.compatibility.common.util.CtsMouseUtil.verifyEnterMove;
+import static com.android.compatibility.common.util.CtsMouseUtil.verifyEnterMoveExit;
+
 import static org.mockito.Matchers.argThat;
 import static org.mockito.Matchers.eq;
 import static org.mockito.Mockito.inOrder;
-import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verifyNoMoreInteractions;
 
 import android.app.Activity;
 import android.app.Instrumentation;
-import android.os.SystemClock;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.SmallTest;
 import android.support.test.rule.ActivityTestRule;
 import android.support.test.runner.AndroidJUnit4;
-import android.view.InputDevice;
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewGroup;
 
-import org.hamcrest.Description;
+import com.android.compatibility.common.util.CtsMouseUtil.ActionMatcher;
+import com.android.compatibility.common.util.CtsMouseUtil.PositionMatcher;
+
 import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.mockito.ArgumentMatcher;
 import org.mockito.InOrder;
 
 /**
@@ -73,48 +77,6 @@
     public ActivityTestRule<HoverCtsActivity> mActivityRule =
             new ActivityTestRule<>(HoverCtsActivity.class);
 
-    static class ActionMatcher extends ArgumentMatcher<MotionEvent> {
-        private final int mAction;
-
-        ActionMatcher(int action) {
-            mAction = action;
-        }
-
-        public boolean matches(Object actual) {
-            return (actual instanceof MotionEvent) && ((MotionEvent) actual).getAction() == mAction;
-        }
-
-        public void describeTo(Description description) {
-            description.appendText("action=" + MotionEvent.actionToString(mAction));
-        }
-    }
-
-    static class MoveMatcher extends ActionMatcher {
-        private final int mX;
-        private final int mY;
-
-        MoveMatcher(int x, int y) {
-            super(MotionEvent.ACTION_HOVER_MOVE);
-            mX = x;
-            mY = y;
-        }
-
-        public boolean matches(Object actual) {
-            return super.matches(actual)
-                    && ((int)((MotionEvent)actual).getX()) == mX
-                    && ((int)((MotionEvent)actual).getY()) == mY;
-        }
-
-        public void describeTo(Description description) {
-            super.describeTo(description);
-            description.appendText("@(" + mX + "," + mY + ")");
-        }
-    }
-
-    private final ActionMatcher mEnterMatcher = new ActionMatcher(MotionEvent.ACTION_HOVER_ENTER);
-    private final ActionMatcher mMoveMatcher = new ActionMatcher(MotionEvent.ACTION_HOVER_MOVE);
-    private final ActionMatcher mExitMatcher = new ActionMatcher(MotionEvent.ACTION_HOVER_EXIT);
-
     @Before
     public void setup() {
         mInstrumentation = InstrumentationRegistry.getInstrumentation();
@@ -136,64 +98,16 @@
         mLayer4Right = mActivity.findViewById(R.id.layer4_right);
     }
 
-    private void verifyHoverSequence(
-            View.OnHoverListener listener, View view, int moveCount, boolean exit) {
-        InOrder inOrder = inOrder(listener);
-        inOrder.verify(listener, times(1)).onHover(eq(view), argThat(mEnterMatcher));
-        inOrder.verify(listener, times(moveCount)).onHover(eq(view), argThat(mMoveMatcher));
-        if (exit) {
-            inOrder.verify(listener, times(1)).onHover(eq(view), argThat(mExitMatcher));
-        }
-        verifyNoMoreInteractions(listener);
-    }
-
-    private void verifyEnterMove(View.OnHoverListener listener, View view, int moveCount) {
-        verifyHoverSequence(listener, view, moveCount, false);
-    }
-
-    private void verifyEnterMoveExit(View.OnHoverListener listener, View view, int moveCount) {
-        verifyHoverSequence(listener, view, moveCount, true);
-    }
-
     private void injectHoverMove(View view) {
         injectHoverMove(view, 0, 0);
     }
 
     private void injectHoverMove(View view, int offsetX, int offsetY) {
         mActivity.getWindow().injectInputEvent(
-                obtainMotionEvent(view, MotionEvent.ACTION_HOVER_MOVE, offsetX, offsetY));
+                obtainMouseEvent(MotionEvent.ACTION_HOVER_MOVE, view, offsetX, offsetY));
         mInstrumentation.waitForIdleSync();
     }
 
-    private MotionEvent obtainMotionEvent(View anchor, int action, int offsetX, int offsetY) {
-        final long eventTime = SystemClock.uptimeMillis();
-        final int[] screenPos = new int[2];
-        anchor.getLocationOnScreen(screenPos);
-        final int x = screenPos[0] + offsetX;
-        final int y = screenPos[1] + offsetY;
-        MotionEvent event = MotionEvent.obtain(eventTime, eventTime, action, x, y, 0);
-        event.setSource(InputDevice.SOURCE_MOUSE);
-        return event;
-    }
-
-    private View.OnHoverListener installHoverListener(View view) {
-        return installHoverListener(view, true);
-    }
-
-    private View.OnHoverListener installHoverListener(View view, boolean result) {
-        final View.OnHoverListener mockListener = mock(View.OnHoverListener.class);
-        view.setOnHoverListener((v, event) -> {
-            // Clone the event to work around event instance reuse in the framework.
-            mockListener.onHover(v, MotionEvent.obtain(event));
-            return result;
-        });
-        return mockListener;
-    }
-
-    private void clearHoverListener(View view) {
-        view.setOnHoverListener(null);
-    }
-
     private void remove(View view) throws Throwable {
         mActivityRule.runOnUiThread(() -> ((ViewGroup)view.getParent()).removeView(view));
     }
@@ -221,10 +135,14 @@
 
         InOrder inOrder = inOrder(listener);
 
-        inOrder.verify(listener, times(1)).onHover(eq(mInner11), argThat(mEnterMatcher));
-        inOrder.verify(listener, times(1)).onHover(eq(mInner11), argThat(new MoveMatcher(1, 2)));
-        inOrder.verify(listener, times(1)).onHover(eq(mInner11), argThat(new MoveMatcher(3, 4)));
-        inOrder.verify(listener, times(1)).onHover(eq(mInner11), argThat(new MoveMatcher(5, 6)));
+        inOrder.verify(listener, times(1)).onHover(eq(mInner11),
+                argThat(new ActionMatcher(MotionEvent.ACTION_HOVER_ENTER)));
+        inOrder.verify(listener, times(1)).onHover(eq(mInner11),
+                argThat(new PositionMatcher(MotionEvent.ACTION_HOVER_MOVE, 1, 2)));
+        inOrder.verify(listener, times(1)).onHover(eq(mInner11),
+                argThat(new PositionMatcher(MotionEvent.ACTION_HOVER_MOVE, 3, 4)));
+        inOrder.verify(listener, times(1)).onHover(eq(mInner11),
+                argThat(new PositionMatcher(MotionEvent.ACTION_HOVER_MOVE, 5, 6)));
 
         verifyNoMoreInteractions(listener);
     }
diff --git a/tests/tests/view/src/android/view/cts/PointerCaptureCtsActivity.java b/tests/tests/view/src/android/view/cts/PointerCaptureCtsActivity.java
new file mode 100644
index 0000000..b9f42de
--- /dev/null
+++ b/tests/tests/view/src/android/view/cts/PointerCaptureCtsActivity.java
@@ -0,0 +1,46 @@
+/*
+ * 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.view.cts;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.view.ContextMenu;
+import android.view.View;
+
+public class PointerCaptureCtsActivity extends Activity {
+    private boolean mHasCapture;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.pointer_capture_layout);
+    }
+
+    public void onCreateContextMenu(
+            ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
+        menu.add("context menu");
+    }
+
+    @Override
+    public void onPointerCaptureChanged(boolean hasCapture) {
+        mHasCapture = hasCapture;
+    }
+
+    public boolean hasPointerCapture() {
+        return mHasCapture;
+    }
+}
diff --git a/tests/tests/view/src/android/view/cts/PointerCaptureTest.java b/tests/tests/view/src/android/view/cts/PointerCaptureTest.java
new file mode 100644
index 0000000..f539d00
--- /dev/null
+++ b/tests/tests/view/src/android/view/cts/PointerCaptureTest.java
@@ -0,0 +1,291 @@
+/*
+ * 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.view.cts;
+
+import static com.android.compatibility.common.util.CtsMouseUtil.PositionMatcher;
+import static com.android.compatibility.common.util.CtsMouseUtil.clearHoverListener;
+import static com.android.compatibility.common.util.CtsMouseUtil.installHoverListener;
+import static com.android.compatibility.common.util.CtsMouseUtil.obtainMouseEvent;
+import static com.android.compatibility.common.util.CtsMouseUtil.verifyEnterMove;
+import static com.android.compatibility.common.util.CtsMouseUtil.verifyEnterMoveExit;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.mockito.Matchers.argThat;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.inOrder;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+
+import android.app.Instrumentation;
+import android.hardware.input.InputManager;
+import android.os.SystemClock;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+import android.view.InputDevice;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+import android.view.View;
+
+import com.android.compatibility.common.util.CtsMouseUtil.ActionMatcher;
+import com.android.compatibility.common.util.PollingCheck;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.InOrder;
+
+/**
+ * Test {@link View}.
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class PointerCaptureTest {
+    private static final long TIMEOUT_DELTA = 10000;
+
+    private Instrumentation mInstrumentation;
+    private PointerCaptureCtsActivity mActivity;
+
+    private View mOuter;
+    private View mInner;
+    private View mTarget;
+    private View mTarget2;
+
+    @Rule
+    public ActivityTestRule<PointerCaptureCtsActivity> mActivityRule =
+            new ActivityTestRule<>(PointerCaptureCtsActivity.class);
+
+    @Rule
+    public ActivityTestRule<CtsActivity> mCtsActivityRule =
+            new ActivityTestRule<>(CtsActivity.class, false, false);
+
+    private void requestFocusSync(View view) throws Throwable {
+        mActivityRule.runOnUiThread(() -> {
+            view.setFocusable(true);
+            view.setFocusableInTouchMode(true);
+            view.requestFocus();
+        });
+        PollingCheck.waitFor(TIMEOUT_DELTA, view::hasFocus);
+    }
+
+    private void requestCaptureSync(View view) throws Throwable {
+        mActivityRule.runOnUiThread(view::requestPointerCapture);
+        PollingCheck.waitFor(TIMEOUT_DELTA, view::hasPointerCapture);
+    }
+
+    private void requestCaptureSync() throws Throwable {
+        requestCaptureSync(mOuter);
+    }
+
+    private void releaseCaptureSync(View view) throws Throwable {
+        mActivityRule.runOnUiThread(view::releasePointerCapture);
+        PollingCheck.waitFor(TIMEOUT_DELTA, () -> !view.hasPointerCapture());
+    }
+
+    private void releaseCaptureSync() throws Throwable {
+        releaseCaptureSync(mOuter);
+    }
+
+    public static View.OnCapturedPointerListener installCapturedPointerListener(View view) {
+        final View.OnCapturedPointerListener mockListener =
+                mock(View.OnCapturedPointerListener.class);
+        view.setOnCapturedPointerListener((v, event) -> {
+            // Clone the event to work around event instance reuse in the framework.
+            mockListener.onCapturedPointer(v, MotionEvent.obtain(event));
+            return true;
+        });
+        return mockListener;
+    }
+
+    public static void clearCapturedPointerListener(View view) {
+        view.setOnCapturedPointerListener(null);
+    }
+
+    private static void injectMotionEvent(MotionEvent event) {
+        InputManager.getInstance().injectInputEvent(event,
+                InputManager.INJECT_INPUT_EVENT_MODE_WAIT_FOR_FINISH);
+    }
+
+    private static void injectRelativeMouseEvent(int action, int x, int y) {
+        final long eventTime = SystemClock.uptimeMillis();
+        MotionEvent event = MotionEvent.obtain(eventTime, eventTime, action, x, y, 0);
+        event.setSource(InputDevice.SOURCE_MOUSE_RELATIVE);
+        injectMotionEvent(event);
+    }
+
+    private static void verifyRelativeMouseEvent(InOrder inOrder,
+                View.OnCapturedPointerListener listener, View view, int action, int x, int y) {
+        inOrder.verify(listener, times(1)).onCapturedPointer(
+                eq(view), argThat(new PositionMatcher(action, x, y)));
+    }
+
+    private void verifyHoverDispatch() {
+        View.OnHoverListener listenerOuter = installHoverListener(mOuter);
+        View.OnHoverListener listenerInner = installHoverListener(mInner);
+        View.OnHoverListener listenerTarget = installHoverListener(mTarget);
+        View.OnHoverListener listenerTarget2 = installHoverListener(mTarget2);
+
+        injectMotionEvent(obtainMouseEvent(MotionEvent.ACTION_HOVER_MOVE, mInner, 0, 0));
+        injectMotionEvent(obtainMouseEvent(MotionEvent.ACTION_HOVER_MOVE, mTarget, 0, 0));
+        injectMotionEvent(obtainMouseEvent(MotionEvent.ACTION_HOVER_MOVE, mTarget2, 0, 0));
+
+        clearHoverListener(mOuter);
+        clearHoverListener(mInner);
+        clearHoverListener(mTarget);
+        clearHoverListener(mTarget2);
+
+        verifyEnterMoveExit(listenerInner, mInner, 2);
+        verifyEnterMoveExit(listenerTarget, mTarget, 2);
+        verifyEnterMove(listenerTarget2, mTarget2, 1);
+
+        verifyNoMoreInteractions(listenerOuter);
+        verifyNoMoreInteractions(listenerInner);
+        verifyNoMoreInteractions(listenerTarget);
+        verifyNoMoreInteractions(listenerTarget2);
+    }
+
+    private void assertPointerCapture(boolean enabled) {
+        assertEquals(enabled, mOuter.hasPointerCapture());
+        assertEquals(enabled, mInner.hasPointerCapture());
+        assertEquals(enabled, mTarget.hasPointerCapture());
+        assertEquals(enabled, mTarget2.hasPointerCapture());
+        assertEquals(enabled, mActivity.hasPointerCapture());
+    }
+
+    @Before
+    public void setup() {
+        mInstrumentation = InstrumentationRegistry.getInstrumentation();
+        mActivity = mActivityRule.getActivity();
+
+        mOuter = mActivity.findViewById(R.id.outer);
+        mInner = mActivity.findViewById(R.id.inner);
+        mTarget = mActivity.findViewById(R.id.target);
+        mTarget2 = mActivity.findViewById(R.id.target2);
+
+        PollingCheck.waitFor(TIMEOUT_DELTA, mActivity::hasWindowFocus);
+    }
+
+    @Test
+    public void testRequestAndReleaseWorkOnAnyView() throws Throwable {
+        requestCaptureSync(mOuter);
+        assertPointerCapture(true);
+
+        releaseCaptureSync(mOuter);
+        assertPointerCapture(false);
+
+        requestCaptureSync(mInner);
+        assertPointerCapture(true);
+
+        releaseCaptureSync(mTarget);
+        assertPointerCapture(false);
+    }
+
+    @Test
+    public void testWindowFocusChangeEndsCapture() throws Throwable {
+        requestCaptureSync();
+        assertPointerCapture(true);
+
+        // Show a context menu on a widget.
+        mActivity.registerForContextMenu(mTarget);
+        mActivityRule.runOnUiThread(() -> mTarget.showContextMenu(0, 0));
+        PollingCheck.waitFor(TIMEOUT_DELTA, () -> !mOuter.hasWindowFocus());
+        assertPointerCapture(false);
+
+        mInstrumentation.sendCharacterSync(KeyEvent.KEYCODE_BACK);
+        PollingCheck.waitFor(TIMEOUT_DELTA, () -> mOuter.hasWindowFocus());
+        assertFalse(mTarget.hasPointerCapture());
+        assertFalse(mActivity.hasPointerCapture());
+    }
+
+    @Test
+    public void testActivityFocusChangeEndsCapture() throws Throwable {
+        requestCaptureSync();
+        assertPointerCapture(true);
+
+        // Launch another activity.
+        CtsActivity activity = mCtsActivityRule.launchActivity(null);
+        PollingCheck.waitFor(TIMEOUT_DELTA, () -> !mActivity.hasWindowFocus());
+        assertPointerCapture(false);
+
+        activity.finish();
+        PollingCheck.waitFor(TIMEOUT_DELTA, () -> mActivity.hasWindowFocus());
+        assertPointerCapture(false);
+    }
+
+    @Test
+    public void testEventDispatch() throws Throwable {
+        verifyHoverDispatch();
+
+        View.OnCapturedPointerListener listenerInner = installCapturedPointerListener(mInner);
+        View.OnCapturedPointerListener listenerTarget = installCapturedPointerListener(mTarget);
+        View.OnCapturedPointerListener listenerTarget2 = installCapturedPointerListener(mTarget2);
+        View.OnHoverListener hoverListenerTarget2 = installHoverListener(mTarget2);
+
+        requestCaptureSync();
+
+        requestFocusSync(mInner);
+        injectRelativeMouseEvent(MotionEvent.ACTION_MOVE, 1, 2);
+        injectRelativeMouseEvent(MotionEvent.ACTION_DOWN, 1, 2);
+        injectRelativeMouseEvent(MotionEvent.ACTION_MOVE, 3, 4);
+        injectRelativeMouseEvent(MotionEvent.ACTION_UP, 3, 4);
+        injectRelativeMouseEvent(MotionEvent.ACTION_MOVE, 1, 2);
+
+        requestFocusSync(mTarget);
+        injectRelativeMouseEvent(MotionEvent.ACTION_MOVE, 5, 6);
+
+        requestFocusSync(mTarget2);
+        injectRelativeMouseEvent(MotionEvent.ACTION_MOVE, 7, 8);
+
+        requestFocusSync(mInner);
+        injectRelativeMouseEvent(MotionEvent.ACTION_MOVE, 9, 10);
+
+        releaseCaptureSync();
+
+        injectRelativeMouseEvent(MotionEvent.ACTION_MOVE, 11, 12);  // Should be ignored.
+
+        clearCapturedPointerListener(mInner);
+        clearCapturedPointerListener(mTarget);
+        clearCapturedPointerListener(mTarget2);
+        clearHoverListener(mTarget2);
+
+        InOrder inOrder = inOrder(
+                listenerInner, listenerTarget, listenerTarget2, hoverListenerTarget2);
+
+        // mTarget2 is left hovered after the call to verifyHoverDispatch.
+        inOrder.verify(hoverListenerTarget2, times(1)).onHover(
+                eq(mTarget2), argThat(new ActionMatcher(MotionEvent.ACTION_HOVER_EXIT)));
+
+        verifyRelativeMouseEvent(inOrder, listenerInner, mInner, MotionEvent.ACTION_MOVE, 1, 2);
+        verifyRelativeMouseEvent(inOrder, listenerInner, mInner, MotionEvent.ACTION_DOWN, 1, 2);
+        verifyRelativeMouseEvent(inOrder, listenerInner, mInner, MotionEvent.ACTION_MOVE, 3, 4);
+        verifyRelativeMouseEvent(inOrder, listenerInner, mInner, MotionEvent.ACTION_UP, 3, 4);
+        verifyRelativeMouseEvent(inOrder, listenerInner, mInner, MotionEvent.ACTION_MOVE, 1, 2);
+
+        verifyRelativeMouseEvent(inOrder, listenerTarget, mTarget, MotionEvent.ACTION_MOVE, 5, 6);
+        verifyRelativeMouseEvent(inOrder, listenerTarget2, mTarget2, MotionEvent.ACTION_MOVE, 7, 8);
+        verifyRelativeMouseEvent(inOrder, listenerInner, mInner, MotionEvent.ACTION_MOVE, 9, 10);
+
+        inOrder.verifyNoMoreInteractions();
+
+        // Check the regular dispatch again.
+        verifyHoverDispatch();
+    }
+}
diff --git a/tests/tests/view/src/android/view/cts/View_FocusHandlingTest.java b/tests/tests/view/src/android/view/cts/View_FocusHandlingTest.java
index c6e7d1f..dd83c16 100644
--- a/tests/tests/view/src/android/view/cts/View_FocusHandlingTest.java
+++ b/tests/tests/view/src/android/view/cts/View_FocusHandlingTest.java
@@ -29,6 +29,7 @@
 import android.support.test.rule.ActivityTestRule;
 import android.support.test.runner.AndroidJUnit4;
 import android.view.View;
+import android.view.ViewGroup;
 
 import org.junit.Rule;
 import org.junit.Test;
@@ -214,4 +215,43 @@
         assertNull(v3.findFocus());
         assertNull(v4.findFocus());
     }
+
+    @UiThreadTest
+    @Test
+    public void testFocusAuto() {
+        Activity activity = mActivityRule.getActivity();
+
+        activity.getLayoutInflater().inflate(R.layout.focus_handling_auto_layout,
+                (ViewGroup) activity.findViewById(R.id.auto_test_area));
+
+        View def = activity.findViewById(R.id.focusabledefault);
+        View auto = activity.findViewById(R.id.focusableauto);
+        View click = activity.findViewById(R.id.onlyclickable);
+        View clickNotFocus = activity.findViewById(R.id.clickablenotfocusable);
+
+        assertEquals(View.FOCUSABLE_AUTO, auto.getFocusable());
+        assertFalse(auto.isFocusable());
+        assertFalse(def.isFocusable());
+        assertTrue(click.isFocusable());
+        assertFalse(clickNotFocus.isFocusable());
+
+        View test = new View(activity);
+        assertFalse(test.isFocusable());
+        test.setClickable(true);
+        assertTrue(test.isFocusable());
+        test.setFocusable(View.NOT_FOCUSABLE);
+        assertFalse(test.isFocusable());
+        test.setFocusable(View.FOCUSABLE_AUTO);
+        assertTrue(test.isFocusable());
+        test.setClickable(false);
+        assertFalse(test.isFocusable());
+
+        // Make sure setFocusable(boolean) unsets FOCUSABLE_AUTO.
+        auto.setFocusable(true);
+        assertSame(View.FOCUSABLE, auto.getFocusable());
+        auto.setFocusable(View.FOCUSABLE_AUTO);
+        assertSame(View.FOCUSABLE_AUTO, auto.getFocusable());
+        auto.setFocusable(false);
+        assertSame(View.NOT_FOCUSABLE, auto.getFocusable());
+    }
 }
diff --git a/tests/tests/view/src/android/view/textclassifier/cts/TextClassificationManagerTest.java b/tests/tests/view/src/android/view/textclassifier/cts/TextClassificationManagerTest.java
new file mode 100644
index 0000000..681b9bf
--- /dev/null
+++ b/tests/tests/view/src/android/view/textclassifier/cts/TextClassificationManagerTest.java
@@ -0,0 +1,160 @@
+/*
+ * 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.view.textclassifier.cts;
+
+import static org.junit.Assert.assertThat;
+
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.view.textclassifier.TextClassificationManager;
+import android.view.textclassifier.TextClassificationResult;
+import android.view.textclassifier.TextClassifier;
+import android.view.textclassifier.TextLanguage;
+import android.view.textclassifier.TextSelection;
+
+import org.hamcrest.BaseMatcher;
+import org.hamcrest.Description;
+import org.hamcrest.Matcher;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.List;
+import java.util.Locale;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class TextClassificationManagerTest {
+
+    private TextClassificationManager mTcm;
+    private TextClassifier mClassifier;
+
+    @Before
+    public void setup() {
+        mTcm = InstrumentationRegistry.getTargetContext()
+                .getSystemService(TextClassificationManager.class);
+        mClassifier = mTcm.getDefaultTextClassifier();
+    }
+
+    @Test
+    public void testSmartSelection() {
+        String text = "Contact me at droid@email.com";
+        String selected = "droid";
+        String suggested = "droid@email.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),
+                isTextSelection(smartStartIndex, smartEndIndex, TextClassifier.TYPE_EMAIL));
+    }
+
+    @Test
+    public void testTextClassificationResult() {
+        String text = "Contact me at droid@email.com";
+        String classifiedText = "droid@email.com";
+        int startIndex = text.indexOf(classifiedText);
+        assertThat(mClassifier.getTextClassificationResult(text, startIndex, text.length()),
+                isTextClassificationResult(classifiedText, TextClassifier.TYPE_EMAIL));
+    }
+
+    @Test
+    public void testLanguageDetection() {
+        String text = "This is english text";
+        assertThat(mTcm.detectLanguages(text), isDetectedLanguage("en"));
+
+        text = "Das ist deutscher text";
+        assertThat(mTcm.detectLanguages(text), isDetectedLanguage("de"));
+
+        text = "これは日本語テキストです";
+        assertThat(mTcm.detectLanguages(text), isDetectedLanguage("ja"));
+    }
+
+    private static Matcher<TextSelection> isTextSelection(
+            final int startIndex, final int endIndex, final String type) {
+        return new BaseMatcher<TextSelection>() {
+            @Override
+            public boolean matches(Object o) {
+                if (o instanceof TextSelection) {
+                    TextSelection selection = (TextSelection) o;
+                    return startIndex == selection.getSelectionStartIndex()
+                            && endIndex == selection.getSelectionEndIndex()
+                            && selection.getEntityCount() > 0
+                            && type.equals(selection.getEntity(0));
+                }
+                return false;
+            }
+
+            @Override
+            public void describeTo(Description description) {
+                description.appendValue(
+                        String.format("%d, %d, %s=1.0", startIndex, endIndex, type));
+            }
+        };
+    }
+
+    private static Matcher<TextClassificationResult> isTextClassificationResult(
+            final String text, final String type) {
+        return new BaseMatcher<TextClassificationResult>() {
+            @Override
+            public boolean matches(Object o) {
+                if (o instanceof TextClassificationResult) {
+                    TextClassificationResult result = (TextClassificationResult) o;
+                    return text.equals(result.getText())
+                            && result.getEntityCount() > 0
+                            && type.equals(result.getEntity(0));
+                    // TODO: Include other properties.
+                }
+                return false;
+            }
+
+            @Override
+            public void describeTo(Description description) {
+                description.appendText("text=").appendValue(text)
+                        .appendText(", type=").appendValue(type);
+            }
+        };
+    }
+
+    private static Matcher<List<TextLanguage>> isDetectedLanguage(final String language) {
+        return new BaseMatcher<List<TextLanguage>>() {
+            @Override
+            public boolean matches(Object o) {
+                if (o instanceof List) {
+                    List languages = (List) o;
+                    if (!languages.isEmpty()) {
+                        Object o1 = languages.get(0);
+                        if (o1 instanceof TextLanguage) {
+                            TextLanguage lang = (TextLanguage) o1;
+                            return lang.getLanguageCount() > 0
+                                    && new Locale(language).getLanguage()
+                                            .equals(lang.getLanguage(0).getLanguage());
+                        }
+                    }
+                }
+                return false;
+            }
+
+            @Override
+            public void describeTo(Description description) {
+                description.appendValue(String.format("%s=1.0", language));
+            }
+        };
+    }
+}
diff --git a/tests/tests/widget/src/android/widget/cts/TextViewTest.java b/tests/tests/widget/src/android/widget/cts/TextViewTest.java
index 21438d5..46755c0 100644
--- a/tests/tests/widget/src/android/widget/cts/TextViewTest.java
+++ b/tests/tests/widget/src/android/widget/cts/TextViewTest.java
@@ -131,6 +131,9 @@
 import android.view.inputmethod.ExtractedTextRequest;
 import android.view.inputmethod.InputConnection;
 import android.view.inputmethod.InputMethodManager;
+import android.view.textclassifier.TextClassificationManager;
+import android.view.textclassifier.TextClassifier;
+import android.view.textclassifier.TextSelection;
 import android.widget.EditText;
 import android.widget.FrameLayout;
 import android.widget.LinearLayout;
@@ -195,6 +198,8 @@
         mTextView.setKeyListener(QwertyKeyListener.getInstance(false, Capitalize.NONE));
         mTextView.setText("", BufferType.EDITABLE);
         mTextView.requestFocus();
+        // Disable smart selection
+        mTextView.setTextClassifier(TextClassifier.NO_OP);
     }
 
     /**
@@ -5803,6 +5808,16 @@
         // TODO(Bug: 22033189): Tests the set callback is actually used.
     }
 
+    @UiThreadTest
+    @Test
+    public void testRespectsViewFocusability() {
+        TextView v = (TextView) mActivity.findViewById(R.id.textview_singleLine);
+        assertFalse(v.isFocusable());
+        // TextView used to set focusable to true or false verbatim which would break the following.
+        v.setClickable(true);
+        assertTrue(v.isFocusable());
+    }
+
     @Test
     public void testTextShadows() throws Throwable {
         final TextView textViewWithConfiguredShadow =
@@ -6752,6 +6767,32 @@
         assertNotEquals(customTextSize, mTextView.getTextSize(), 0f);
     }
 
+    @Test
+    public void testSmartSelection() throws Throwable {
+        mTextView = findTextView(R.id.textview_text);
+        String text = "The president-elect, Filip, is coming to town tomorrow.";
+        int startIndex = text.indexOf("president");
+        int endIndex = startIndex + "president".length();
+        initTextViewForTypingOnUiThread();
+        TextClassificationManager tcm = mActivity.getSystemService(TextClassificationManager.class);
+        mActivityRule.runOnUiThread(() -> {
+            mTextView.setTextIsSelectable(true);
+            mTextView.setText(text, BufferType.EDITABLE);
+            mTextView.setTextClassifier(tcm.getDefaultTextClassifier());
+        });
+        mInstrumentation.waitForIdleSync();
+
+        Point offset = getCenterPositionOfTextAt(mTextView, startIndex, endIndex);
+        CtsTouchUtils.emulateLongPressOnView(mInstrumentation, mTextView, offset.x, offset.y);
+        PollingCheck.waitFor(mTextView::hasSelection);
+
+        TextSelection selection = tcm.getDefaultTextClassifier()
+                .suggestSelection(text, startIndex, endIndex);
+        assertEquals(selection.getSelectionStartIndex(), mTextView.getSelectionStart());
+        assertEquals(selection.getSelectionEndIndex(), mTextView.getSelectionEnd());
+        // TODO: Test the floating toolbar content.
+    }
+
     /**
      * Some TextView attributes require non-fixed width and/or layout height. This function removes
      * all other existing views from the layout leaving only one auto-size TextView (for exercising
@@ -6977,6 +7018,25 @@
         mInstrumentation.waitForIdleSync();
     }
 
+    /**
+     * Returns the x, y coordinates of text at a specified indices relative to the position of the
+     * TextView.
+     *
+     * @param textView
+     * @param startIndex start index of the text in the textView
+     * @param endIndex end index of the text in the textView
+     */
+    private static Point getCenterPositionOfTextAt(
+            TextView textView, int startIndex, int endIndex) {
+        int xStart = (int) textView.getLayout().getPrimaryHorizontal(startIndex, true, true);
+        int xEnd = (int) textView.getLayout().getPrimaryHorizontal(endIndex, true, true);
+        int line = textView.getLayout().getLineForOffset(endIndex);
+        int yTop = textView.getLayout().getLineTop(line);
+        int yBottom = textView.getLayout().getLineBottom(line);
+
+        return new Point((xStart + xEnd) / 2 /* x */, (yTop + yBottom) / 2 /* y */);
+    }
+
     private static abstract class TestSelectedRunnable implements Runnable {
         private TextView mTextView;
         private boolean mIsSelected1;
diff --git a/tests/tests/wrap/Android.mk b/tests/tests/wrap/Android.mk
new file mode 100644
index 0000000..ba3fe08
--- /dev/null
+++ b/tests/tests/wrap/Android.mk
@@ -0,0 +1,80 @@
+# 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_MULTILIB := both
+LOCAL_MODULE_TAGS := tests
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+LOCAL_DEX_PREOPT := false
+LOCAL_PROGUARD_ENABLED := disabled
+LOCAL_STATIC_JAVA_LIBRARIES := compatibility-device-util android-support-test
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_COMPATIBILITY_SUITE := cts
+LOCAL_SDK_VERSION := current
+
+LOCAL_PREBUILT_JNI_LIBS_arm := wrap.sh
+LOCAL_PREBUILT_JNI_LIBS_arm64 := wrap.sh
+LOCAL_PREBUILT_JNI_LIBS_mips := wrap.sh
+LOCAL_PREBUILT_JNI_LIBS_mips64 := wrap.sh
+LOCAL_PREBUILT_JNI_LIBS_x86 := wrap.sh
+LOCAL_PREBUILT_JNI_LIBS_x86_64 := wrap.sh
+
+LOCAL_PACKAGE_NAME := CtsWrapWrapDebugTestCases
+LOCAL_MANIFEST_FILE := wrap_debug/AndroidManifest.xml
+
+include $(BUILD_PACKAGE)
+
+include $(CLEAR_VARS)
+
+LOCAL_MULTILIB := both
+LOCAL_MODULE_TAGS := tests
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+LOCAL_DEX_PREOPT := false
+LOCAL_PROGUARD_ENABLED := disabled
+LOCAL_STATIC_JAVA_LIBRARIES := compatibility-device-util android-support-test
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_COMPATIBILITY_SUITE := cts
+LOCAL_SDK_VERSION := current
+
+LOCAL_PREBUILT_JNI_LIBS_arm := wrap.sh
+LOCAL_PREBUILT_JNI_LIBS_arm64 := wrap.sh
+LOCAL_PREBUILT_JNI_LIBS_mips := wrap.sh
+LOCAL_PREBUILT_JNI_LIBS_mips64 := wrap.sh
+LOCAL_PREBUILT_JNI_LIBS_x86 := wrap.sh
+LOCAL_PREBUILT_JNI_LIBS_x86_64 := wrap.sh
+
+LOCAL_PACKAGE_NAME := CtsWrapWrapNoDebugTestCases
+LOCAL_MANIFEST_FILE := wrap_nodebug/AndroidManifest.xml
+
+include $(BUILD_PACKAGE)
+
+include $(CLEAR_VARS)
+
+LOCAL_MULTILIB := both
+LOCAL_MODULE_TAGS := tests
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+LOCAL_DEX_PREOPT := false
+LOCAL_PROGUARD_ENABLED := disabled
+LOCAL_STATIC_JAVA_LIBRARIES := compatibility-device-util android-support-test
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_COMPATIBILITY_SUITE := cts
+LOCAL_SDK_VERSION := current
+
+LOCAL_PACKAGE_NAME := CtsWrapNoWrapTestCases
+LOCAL_MANIFEST_FILE := nowrap/AndroidManifest.xml
+
+include $(BUILD_PACKAGE)
diff --git a/tests/tests/wrap/AndroidTest.xml b/tests/tests/wrap/AndroidTest.xml
new file mode 100644
index 0000000..505eccb
--- /dev/null
+++ b/tests/tests/wrap/AndroidTest.xml
@@ -0,0 +1,26 @@
+<?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 Wrap test cases">
+    <target_preparer class="com.android.compatibility.common.tradefed.targetprep.ApkInstaller">
+        <option name="cleanup-apks" value="true" />
+        <option name="test-file-name" value="CtsWrapWrapDebugTestCases.apk" />
+        <option name="test-file-name" value="CtsWrapWrapNoDebugTestCases.apk" />
+        <option name="test-file-name" value="CtsWrapNoWrapTestCases.apk" />
+    </target_preparer>
+    <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+        <option name="package" value="android.wrap.cts" />
+    </test>
+</configuration>
diff --git a/tests/tests/wrap/nowrap/AndroidManifest.xml b/tests/tests/wrap/nowrap/AndroidManifest.xml
new file mode 100644
index 0000000..6145390
--- /dev/null
+++ b/tests/tests/wrap/nowrap/AndroidManifest.xml
@@ -0,0 +1,39 @@
+<?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.wrap.nowrap.cts">
+
+    <application android:debuggable="true">
+        <uses-library android:name="android.test.runner" />
+        <meta-data android:name="android.wrap.cts.expext_env" android:value="false" />
+        <activity android:name="android.wrap.WrapActivity" >
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+    </application>
+
+    <!--  self-instrumenting test package. -->
+    <instrumentation
+        android:name="android.support.test.runner.AndroidJUnitRunner"
+        android:label="CTS tests for wrap.sh"
+        android:targetPackage="android.wrap.nowrap.cts" >
+    </instrumentation>
+</manifest>
+
diff --git a/tests/tests/wrap/src/android/wrap/WrapActivity.java b/tests/tests/wrap/src/android/wrap/WrapActivity.java
new file mode 100644
index 0000000..578241a
--- /dev/null
+++ b/tests/tests/wrap/src/android/wrap/WrapActivity.java
@@ -0,0 +1,34 @@
+/*
+ * 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.wrap;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+import java.lang.Override;
+
+/**
+ * A simple no-op activity.
+ */
+public class WrapActivity extends Activity {
+
+    @Override
+    public void onCreate(Bundle icicle) {
+        super.onCreate(icicle);
+    }
+
+}
diff --git a/tests/tests/wrap/src/android/wrap/cts/WrapTest.java b/tests/tests/wrap/src/android/wrap/cts/WrapTest.java
new file mode 100644
index 0000000..8b73ad5
--- /dev/null
+++ b/tests/tests/wrap/src/android/wrap/cts/WrapTest.java
@@ -0,0 +1,76 @@
+/*
+ * 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.wrap.cts;
+
+import android.content.pm.PackageManager;
+import android.wrap.WrapActivity;
+import android.test.ActivityInstrumentationTestCase2;
+
+/**
+ * A simple compatibility test which tests the SharedPreferences API.
+ *
+ * This test uses {@link android.test.ActivityInstrumentationTestCase2} to instrument the
+ * {@link android.WrapActivity}.
+ */
+public class WrapTest extends ActivityInstrumentationTestCase2<WrapActivity> {
+
+    /**
+     * A reference to the activity whose shared preferences are being tested.
+     */
+    private WrapActivity mActivity;
+
+    public WrapTest() {
+        super(WrapActivity.class);
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        // Start the activity.
+        mActivity = getActivity();
+        // Wait for the UI Thread to become idle.
+        getInstrumentation().waitForIdleSync();
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        // Nothing to do here.
+        super.tearDown();
+    }
+
+    /**
+     * Tests the environment for a property.
+     *
+     * This reads the activity meta-data to figure out whether the property is expected, then
+     * check whether the property exists.
+     *
+     * @throws Exception
+     */
+    public void testWrapProperty() throws Exception {
+        boolean expectEnv = mActivity.getPackageManager().getApplicationInfo(
+                mActivity.getPackageName(), PackageManager.GET_META_DATA).metaData.getBoolean(
+                "android.wrap.cts.expext_env");
+
+        String wrapEnvValue = System.getenv("WRAP_PROPERTY");
+        boolean wrapExists = wrapEnvValue != null;
+
+        assertEquals("Unexpected wrap property state", expectEnv, wrapExists);
+
+        if (wrapExists) {
+            assertEquals("Unexpected wrap property value", wrapEnvValue, "test");
+        }
+    }
+}
diff --git a/tests/tests/wrap/wrap.sh b/tests/tests/wrap/wrap.sh
new file mode 100755
index 0000000..58e5f2a
--- /dev/null
+++ b/tests/tests/wrap/wrap.sh
@@ -0,0 +1,15 @@
+#!/system/bin/sh
+# 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.
+WRAP_PROPERTY=test $@
diff --git a/tests/tests/wrap/wrap_debug/AndroidManifest.xml b/tests/tests/wrap/wrap_debug/AndroidManifest.xml
new file mode 100644
index 0000000..f371108
--- /dev/null
+++ b/tests/tests/wrap/wrap_debug/AndroidManifest.xml
@@ -0,0 +1,39 @@
+<?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.wrap.wrap_debug.cts">
+
+    <application android:debuggable="true">
+        <uses-library android:name="android.test.runner" />
+        <meta-data android:name="android.wrap.cts.expext_env" android:value="true" />
+        <activity android:name="android.wrap.WrapActivity" >
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+    </application>
+
+    <!--  self-instrumenting test package. -->
+    <instrumentation
+        android:name="android.support.test.runner.AndroidJUnitRunner"
+        android:label="CTS tests for wrap.sh"
+        android:targetPackage="android.wrap.wrap_debug.cts" >
+    </instrumentation>
+</manifest>
+
diff --git a/tests/tests/wrap/wrap_nodebug/AndroidManifest.xml b/tests/tests/wrap/wrap_nodebug/AndroidManifest.xml
new file mode 100644
index 0000000..127e6a8
--- /dev/null
+++ b/tests/tests/wrap/wrap_nodebug/AndroidManifest.xml
@@ -0,0 +1,39 @@
+<?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.wrap.wrap_nodebug.cts">
+
+    <application>
+        <uses-library android:name="android.test.runner" />
+        <meta-data android:name="android.wrap.cts.expext_env" android:value="false" />
+        <activity android:name="android.wrap.WrapActivity" >
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+    </application>
+
+    <!--  self-instrumenting test package. -->
+    <instrumentation
+        android:name="android.support.test.runner.AndroidJUnitRunner"
+        android:label="CTS tests for wrap.sh"
+        android:targetPackage="android.wrap.wrap_nodebug.cts" >
+    </instrumentation>
+</manifest>
+
diff --git a/tools/cts-tradefed/res/config/cts-global-presubmit.xml b/tools/cts-tradefed/res/config/cts-global-presubmit.xml
new file mode 100644
index 0000000..5a858e7
--- /dev/null
+++ b/tools/cts-tradefed/res/config/cts-global-presubmit.xml
@@ -0,0 +1,30 @@
+<?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="Runs CTS global presubmit test cases">
+
+    <include name="cts-automated" />
+
+    <option name="plan" value="cts" />
+
+    <!-- Include modules with presubmit test cases, repeat for each applicable module -->
+    <!--option name="compatibility:include-filter" value="<CTS module name goes here>" /-->
+
+    <!-- Only run tests with @GlobalPresubmit annotation. -->
+    <option name="compatibility:test-arg" value="com.android.compatibility.common.tradefed.testtype.JarHostTest:include-annotation:android.platform.test.annotations.GlobalPresubmit" />
+    <option name="compatibility:test-arg" value="com.android.tradefed.testtype.AndroidJUnitTest:include-annotation:android.platform.test.annotations.GlobalPresubmit" />
+    <option name="compatibility:test-arg" value="com.android.tradefed.testtype.HostTest:include-annotation:android.platform.test.annotations.GlobalPresubmit" />
+
+</configuration>
diff --git a/tools/cts-tradefed/res/config/cts-system-checkers.xml b/tools/cts-tradefed/res/config/cts-system-checkers.xml
index ac71bbc..c5225ae 100644
--- a/tools/cts-tradefed/res/config/cts-system-checkers.xml
+++ b/tools/cts-tradefed/res/config/cts-system-checkers.xml
@@ -15,4 +15,5 @@
 -->
 <configuration description="CTS system checker configs">
     <system_checker class="com.android.compatibility.common.tradefed.targetprep.NetworkConnectivityChecker" />
+    <system_checker class="com.android.tradefed.suite.checker.KeyguardStatusChecker" />
 </configuration>