Merge "media: test MediaCodecList.find(En|De)coderForFormat() with AAC profiles"
diff --git a/CtsTestCaseList.mk b/CtsTestCaseList.mk
index 39a58ad..1e8fab0 100644
--- a/CtsTestCaseList.mk
+++ b/CtsTestCaseList.mk
@@ -78,6 +78,8 @@
 cts_support_packages := \
     CtsAccountManagementDevicePolicyApp \
     CtsAlarmClockService \
+    CtsAppRestrictionsManagingApp \
+    CtsAppRestrictionsTargetApp \
     CtsAppTestStubs \
     CtsAppUsageTestApp \
     CtsAssistService \
@@ -87,6 +89,7 @@
     CtsContactDirectoryProvider \
     CtsAdminApp \
     CtsWifiConfigCreator \
+    CtsDeviceAdminApp \
     CtsDeviceAndProfileOwnerApp \
     CtsDeviceInfo \
     CtsDeviceOsTestApp \
@@ -95,6 +98,7 @@
     CtsDeviceTaskSwitchingAppA \
     CtsDeviceTaskSwitchingAppB \
     CtsDeviceTaskSwitchingControl \
+    CtsExternalServiceService \
     CtsHostsideNetworkTestsApp \
     CtsIntentReceiverApp \
     CtsIntentSenderApp \
@@ -159,6 +163,7 @@
     CtsDreamsTestCases \
     CtsDrmTestCases \
     CtsEffectTestCases \
+    CtsExternalServiceTestCases \
     CtsFileSystemTestCases \
     CtsGestureTestCases \
     CtsGraphicsTestCases \
diff --git a/apps/CtsVerifier/Android.mk b/apps/CtsVerifier/Android.mk
index 2d8f6a9..f9e052d 100644
--- a/apps/CtsVerifier/Android.mk
+++ b/apps/CtsVerifier/Android.mk
@@ -82,7 +82,7 @@
 include $(CLEAR_VARS)
 
 LOCAL_PREBUILT_STATIC_JAVA_LIBRARIES := \
-        ctsverifier-opencv:libs/opencv-android.jar
+        ctsverifier-opencv:libs/opencv3-android.jar
 
 include $(BUILD_MULTI_PREBUILT)
 
diff --git a/apps/CtsVerifier/AndroidManifest.xml b/apps/CtsVerifier/AndroidManifest.xml
index c1a03c9..b395838 100644
--- a/apps/CtsVerifier/AndroidManifest.xml
+++ b/apps/CtsVerifier/AndroidManifest.xml
@@ -20,7 +20,7 @@
       android:versionCode="5"
       android:versionName="6.0_r2">
 
-    <uses-sdk android:minSdkVersion="19" android:targetSdkVersion="23"/>
+    <uses-sdk android:minSdkVersion="19" android:targetSdkVersion="24"/>
 
     <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
     <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
@@ -437,6 +437,15 @@
             <meta-data android:name="test_required_features" android:value="android.hardware.location.gps" />
         </activity>
 
+        <activity android:name=".location.LocationListenerActivity"
+                android:label="@string/location_listener_activity"
+                android:configChanges="keyboardHidden|orientation|screenSize">
+            <intent-filter>
+                <action android:name="com.android.cts.verifier.location.SET_LOCATION_AND_CHECK" />
+                <category android:name="android.intent.category.DEFAULT"></category>
+                </intent-filter>
+        </activity>
+
         <activity android:name=".net.ConnectivityScreenOffTestActivity"
                 android:label="@string/network_screen_off_test"
                 android:screenOrientation="portrait">
@@ -469,6 +478,16 @@
                 android:configChanges="keyboardHidden|orientation|screenSize">
         </activity>
 
+        <activity android:name="com.android.cts.verifier.nfc.hcef.HceFReaderTestActivity"
+                android:label="@string/nfc_hce_f_reader_tests"
+                android:configChanges="keyboardHidden|orientation|screenSize">
+        </activity>
+
+        <activity android:name="com.android.cts.verifier.nfc.hcef.HceFEmulatorTestActivity"
+                android:label="@string/nfc_hce_f_emulator_tests"
+                android:configChanges="keyboardHidden|orientation|screenSize">
+        </activity>
+
         <activity android:name=".nfc.NdefPushSenderActivity"
                 android:label="@string/nfc_ndef_push_sender"
                 android:configChanges="keyboardHidden|orientation|screenSize" />
@@ -573,6 +592,14 @@
                 android:label="@string/nfc_hce_other_conflicting_prefix_aids_emulator"
                 android:configChanges="keyboardHidden|orientation|screenSize" />
 
+        <activity android:name=".nfc.hcef.HceFEmulatorActivity"
+                android:label="@string/nfc_hce_f_emulator"
+                android:configChanges="keyboardHidden|orientation|screenSize" />
+
+        <activity android:name=".nfc.hcef.HceFReaderActivity"
+                android:label="@string/nfc_hce_f_reader"
+                android:configChanges="keyboardHidden|orientation|screenSize" />
+
         <!-- services used for testing NFC host-based card emulation -->
         <service android:name=".nfc.hce.PaymentService1" android:exported="true"
                  android:permission="android.permission.BIND_NFC_SERVICE"
@@ -702,6 +729,15 @@
             <meta-data android:name="android.nfc.cardemulation.host_apdu_service" android:resource="@xml/payment_aid_list_1"/>
         </service>
 
+        <service
+            android:name=".nfc.hcef.MyHostFelicaService"
+            android:enabled="true"
+            android:exported="true" android:permission="android.permission.BIND_NFC_SERVICE">
+            <intent-filter>
+                <action android:name="android.nfc.cardemulation.action.HOST_NFCF_SERVICE"/>
+            </intent-filter>
+            <meta-data android:name="android.nfc.cardemulation.host_nfcf_service" android:resource="@xml/felicaservice"/>
+        </service>
         <!-- Service used for Camera ITS tests -->
         <service android:name=".camera.its.ItsService" >
             <intent-filter>
@@ -778,7 +814,7 @@
             android:screenOrientation="locked" >
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
-                <category android:name="android.cts.intent.category.MANUAL_TEST_disabled"/>
+                <category android:name="android.cts.intent.category.MANUAL_TEST"/>
             </intent-filter>
 
             <meta-data
@@ -1448,6 +1484,14 @@
             </intent-filter>
         </activity>
 
+        <activity android:name=".managedprovisioning.PolicyTransparencyActivity"
+                android:label="@string/device_owner_policy_transparency_test">
+        </activity>
+
+        <activity android:name=".managedprovisioning.SetMaximumTimeToLockActivity"
+                android:label="@string/maximum_time_to_lock">
+        </activity>
+
         <activity-alias
                 android:name=".managedprovisioning.ManagedProfilePermissionLockdownTestActivity"
                 android:targetActivity=".managedprovisioning.PermissionLockdownTestActivity">
@@ -1511,6 +1555,8 @@
                 <action android:name="com.android.cts.verifier.managedprovisioning.NOTIFICATION" />
                 <action android:name="com.android.cts.verifier.managedprovisioning.LOCKSCREEN_NOTIFICATION" />
                 <action android:name="com.android.cts.verifier.managedprovisioning.CLEAR_NOTIFICATION" />
+                <action android:name="com.android.cts.verifier.managedprovisioning.TEST_SELECT_WORK_CHALLENGE" />
+                <action android:name="com.android.cts.verifier.managedprovisioning.TEST_CONFIRM_WORK_CREDENTIALS" />
                 <category android:name="android.intent.category.DEFAULT"></category>
             </intent-filter>
         </activity>
diff --git a/apps/CtsVerifier/libs/opencv-android.jar b/apps/CtsVerifier/libs/opencv-android.jar
deleted file mode 100644
index 1c13eee..0000000
--- a/apps/CtsVerifier/libs/opencv-android.jar
+++ /dev/null
Binary files differ
diff --git a/apps/CtsVerifier/libs/opencv3-android.jar b/apps/CtsVerifier/libs/opencv3-android.jar
new file mode 100644
index 0000000..80a992b
--- /dev/null
+++ b/apps/CtsVerifier/libs/opencv3-android.jar
Binary files differ
diff --git a/apps/CtsVerifier/res/layout/time_to_lock.xml b/apps/CtsVerifier/res/layout/time_to_lock.xml
new file mode 100644
index 0000000..1629cf7
--- /dev/null
+++ b/apps/CtsVerifier/res/layout/time_to_lock.xml
@@ -0,0 +1,60 @@
+<?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">
+
+    <ScrollView
+            android:layout_width="match_parent"
+            android:layout_height="320dp"
+            android:layout_weight="2">
+        <TextView
+                android:id="@+id/device_owner_max_time_to_lock_instructions"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:text="@string/set_max_time_to_lock_instructions"
+                style="@style/InstructionsFont" />
+    </ScrollView>
+
+    <EditText
+            android:id="@+id/time_to_lock"
+            android:hint="Time(Milliseconds)"
+            android:inputType="number"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content" />
+
+    <LinearLayout android:orientation="horizontal"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content">
+
+        <Button
+                android:id="@+id/set_max_time_to_lock"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="@string/device_owner_user_restriction_set" />
+
+        <Button
+                android:id="@+id/go_button"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="@string/device_owner_settings_go" />
+
+    </LinearLayout>
+
+    <include layout="@layout/pass_fail_buttons" />
+
+</LinearLayout>
diff --git a/apps/CtsVerifier/res/raw/opencv_library_license b/apps/CtsVerifier/res/raw/opencv_library_license
new file mode 100644
index 0000000..a62c0f2
--- /dev/null
+++ b/apps/CtsVerifier/res/raw/opencv_library_license
@@ -0,0 +1,42 @@
+By downloading, copying, installing or using the software you agree to this license.
+If you do not agree to this license, do not download, install,
+copy or use the software.
+
+
+                          License Agreement
+               For Open Source Computer Vision Library
+                       (3-clause BSD License)
+
+Copyright (C) 2000-2015, Intel Corporation, all rights reserved.
+Copyright (C) 2009-2011, Willow Garage Inc., all rights reserved.
+Copyright (C) 2009-2015, NVIDIA Corporation, all rights reserved.
+Copyright (C) 2010-2013, Advanced Micro Devices, Inc., all rights reserved.
+Copyright (C) 2015, OpenCV Foundation, all rights reserved.
+Copyright (C) 2015, Itseez Inc., all rights reserved.
+Third party copyrights are property of their respective owners.
+
+Redistribution and use in source and binary forms, with or without modification,
+are permitted provided that the following conditions are met:
+
+  * Redistributions of source code must retain the above copyright notice,
+    this list of conditions and the following disclaimer.
+
+  * Redistributions in binary form must reproduce the above copyright notice,
+    this list of conditions and the following disclaimer in the documentation
+    and/or other materials provided with the distribution.
+
+  * Neither the names of the copyright holders nor the names of the contributors
+    may be used to endorse or promote products derived from this software
+    without specific prior written permission.
+
+This software is provided by the copyright holders and contributors "as is" and
+any express or implied warranties, including, but not limited to, the implied
+warranties of merchantability and fitness for a particular purpose are disclaimed.
+In no event shall copyright holders or contributors be liable for any direct,
+indirect, incidental, special, exemplary, or consequential damages
+(including, but not limited to, procurement of substitute goods or services;
+loss of use, data, or profits; or business interruption) however caused
+and on any theory of liability, whether in contract, strict liability,
+or tort (including negligence or otherwise) arising in any way out of
+the use of this software, even if advised of the possibility of such damage.
+
diff --git a/apps/CtsVerifier/res/values/strings.xml b/apps/CtsVerifier/res/values/strings.xml
index 5d3bb0e..2bff3a4 100644
--- a/apps/CtsVerifier/res/values/strings.xml
+++ b/apps/CtsVerifier/res/values/strings.xml
@@ -374,6 +374,7 @@
         Make sure the device has line of sight to GPS satellites
         (for example, outside, or near a window)
         and then press OK to run the automated tests.</string>
+    <string name="location_listener_activity">Location listener</string>
 
     <!-- Strings for net.ConnectivityScreenOffTestActivity -->
     <string name="network_screen_off_test">Network Connectivity Screen Off Test</string>
@@ -467,8 +468,13 @@
     <string name="nfc_ndef_content">Id: %1$s\nMime: %2$s\nPayload: %3$s</string>
 
     <string name="nfc_hce">Host-based card emulation</string>
+    <string name="nfc_hce_f">Host-based Felica card emulation</string>
     <string name="nfc_hce_reader_tests">HCE reader tests</string>
+    <string name="nfc_hce_f_reader_tests">HCE Felica reader tests</string>
     <string name="nfc_hce_emulator_tests">HCE emulator tests</string>
+    <string name="nfc_hce_f_emulator_tests">HCE Felica emulator tests</string>
+    <string name="nfc_hce_f_emulator">HCE Felica emulator</string>
+    <string name="nfc_hce_f_reader">HCE Felica reader</string>
     <string name="nfc_hce_emulator_test_info">The host-based card emulation
         tests require two devices to be completed. The HCE emulator tests are used
         to actually test the host-based card emulation feature of the device-under-test. So the
@@ -576,6 +582,7 @@
     <string name="transportService2">TransportService #2</string>
     <string name="accessService">AccessService</string>
     <string name="offhostService">OffhostService</string>
+    <string name="felicaservice">Felica Service</string>
 
     <!-- Strings for Sensor Test Activities -->
     <string name="snsr_device_admin_receiver">Sensor Tests Device Admin Receiver</string>
@@ -588,8 +595,8 @@
     <string name="snsr_test_skipped">SKIPPED</string>
     <string name="snsr_test_fail">FAIL</string>
     <string name="snsr_execution_time">Test execution time %1$s sec</string>
-    <string name="snsr_rvcvxchk_test">Rotation Vector CV XCheck</string>
-    <string name="snsr_rvcvxchk_test_rec">Rotation Vector CV XCheck Recording</string>
+    <string name="snsr_rvcvxchk_test">Rotation Vector CV Crosscheck</string>
+    <string name="snsr_rvcvxchk_test_rec">Rotation Vector CV Recording</string>
 
     <!-- Strings to interact with users in Sensor Tests -->
     <string name="snsr_test_play_sound">A sound will be played once the verification is complete...</string>
@@ -1329,6 +1336,27 @@
     <string name="provisioning_byod_lockscreen_bound_key">Lockscreen-bound key test</string>
     <string name="provisioning_byod_fingerprint_bound_key">Fingerprint-bound key test</string>
     <string name="provisioning_byod_vpn">Vpn test</string>
+    <string name="provisioning_byod_select_work_challenge">Select work challenge test</string>
+    <string name="provisioning_byod_select_work_challenge_description">
+        This test verifies that a work challenge can be chosen.\n
+
+        1. Verify that you get sent to the page for setting up a new work challenge.\n
+        2. Set a new work challenge.
+    </string>
+    <string name="provisioning_byod_confirm_work_credentials">Confirm work challenge test</string>
+    <string name="provisioning_byod_confirm_work_credentials_description">
+        This test verifies that the work challenge was set correctly and it is customized according
+        to the policies set. You can only do this test after you have done the previous test.\n
+
+        1. Verify that a screen asking you for your work credentials is shown.\n
+        2. Verify that the background image contains an orange suitcase.\n
+        3. Verify that the background color of the remaining image is blue.\n
+        4. Verify that the header text says \"CtsVerifier\".\n
+        5. Confirm your credentials and verify that the credentials you entered previously work.
+    </string>
+    <string name="provisioning_byod_confirm_work_credentials_header">
+        CtsVerifier
+    </string>
     <!-- Strings for DeskClock -->
     <string name="deskclock_tests">Alarms and Timers Tests</string>
     <string name="deskclock_tests_info">
@@ -1708,7 +1736,9 @@
     <string name="provisioning_byod_location_settings_instruction">
         Please press the Go button to open Location page in settings.\n
         \n
-        Verify that work profile entry exists in the page.\n
+        Verify that work profile entry exists in the page and it has a toggleable switch.\n
+        Switch the main location switch at the top of the screen off. You should see the work profile location switch go disabled and into \'off\' state.\n
+        Then switch the main location switch on again. You should see the work profile location switch go enabled and into its previous state.\n
         \n
         Then use the Back button to return to this test and mark accordingly.
     </string>
@@ -1793,7 +1823,7 @@
     <string name="provisioning_byod_location_mode_enable_toast_location_change">Location changed</string>
     <string name="provisioning_byod_location_mode_enable_instruction">
         This test verifies that the location updates can be enabled for the managed profile apps.\n
-        1. Press the go button to go to the location settings page, set the location switch enabled.\n
+        1. Press the go button to go to the location settings page, set both the main location switch and the work profile location switch enabled.\n
         2. Move your position a little bit, verify that location updates toast comes up.\n
         Please wait until the location updates or timeout toast message shows up before going back to the cts-verifier tests.\n
         3. Go back to the cts-verifier tests using the back button, then mark the test accordingly.\n
@@ -1802,13 +1832,58 @@
     <string name="provisioning_byod_location_mode_disable">Disable location</string>
     <string name="provisioning_byod_location_mode_time_out_toast">Timeout waiting for gps location change</string>
     <string name="provisioning_byod_location_mode_disable_instruction">
-        This test verifies that the location updates can be disabled for the managed profile apps.\n
-        1. Press the go button to go to the location settings page, set the location switch disabled.\n
-        2. Move your position a little bit, verify that no location updates toast come up and that the timeout message show up after around 15 seconds. 
+        This test verifies that the location updates can be disabled for the managed profile apps through the main location switch.\n
+        1. Press the go button to go to the location settings page, set the main location switch disabled.\n
+        2. Move your position a little bit, verify that no location updates toast come up and that the timeout message show up after around 15 seconds.
         Please wait until the timeout or location updates toast message shows up before going back to the cts-verifier tests.\n
         3. Go back to the cts-verifier tests using the back button, then mark the test accordingly.\n
     </string>
 
+    <string name="provisioning_byod_work_location_mode_disable">Disable location for work profile</string>
+    <string name="provisioning_byod_work_location_mode_disable_instruction">
+        This test verifies that the location updates can be disabled for the managed profile apps through work profile location switch.\n
+        1. Press the go button to go to the location settings page, set the work location switch disabled while the main location switch is still enabled.\n
+        2. Move your position a little bit, verify that no location updates toast come up and that the timeout message show up after around 15 seconds.
+        Please wait until the timeout or location updates toast message shows up before going back to the cts-verifier tests.\n
+        3. Go back to the cts-verifier tests using the back button, then mark the test accordingly.\n
+    </string>
+    <string name="provisioning_byod_primary_location_when_work_disabled">Primary receives updates while work location is disabled</string>
+    <string name="provisioning_byod_primary_location_when_work_disabled_instruction">
+        This test verifies that location updates are still received by primary profile when location updates are disabled for managed profile apps through work profile location switch.\n
+        1. Press the go button to go to the location settings page, set the work location switch disabled while the main location switch is still enabled.\n
+        2. Move your position a little bit, verify that location updates toast comes up.\n
+        Please wait until the location updates or timeout toast message shows up before going back to the cts-verifier tests.\n
+        3. Go back to the cts-verifier tests using the back button, then mark the test accordingly.\n
+    </string>
+
+    <string name="provisioning_byod_turn_off_work_icon">Icon when work mode is off</string>
+    <string name="provisioning_byod_turn_off_work_icon_instruction">
+        This test verifies that a status bar icon indicates if work mode is off.\n
+        1. Press the go button to go to the settings apps, set work mode to off.\n
+        2. Verify that the status bar shows an icon indicating that work mode is off.\n
+        3. Set work mode to on.\n
+        4. Check that the work icon on the status bar is removed.
+    </string>
+    <string name="provisioning_byod_turn_off_work_launcher">Starting work apps when work mode is off</string>
+    <string name="provisioning_byod_turn_off_work_launcher_instruction">
+        This test verifies that work applications cannot be started if work mode is off.\n
+        1. Press the go button to go to the settings apps, set work mode to off.\n
+        2. Press home to go to the launcher.\n
+        3. Verify that work applications are greyed out.\n
+        4. Tap on a work application.\n
+        5. Verify that the application does not start.\n
+        6. Set work mode to on.\n
+        7. Go to the launcher and verify that you can start a work application.\n
+    </string>
+    <string name="provisioning_byod_turn_off_work_notifications">Notifications when work mode is off</string>
+    <string name="provisioning_byod_turn_off_work_notifications_instruction">
+        This test verifies that work notifications are not shown if work mode is off.\n
+        1. Press the go button to send a work notification.\n
+        2. Set work mode to off.\n
+        3. Check that the work notification has disappeared.\n
+        4. Set work mode to on.\n
+    </string>
+
     <!-- Strings for DeviceOwnerProvisioningTest -->
     <string name="provisioning_device_owner">Device Owner Provisioning</string>
     <string name="device_owner_provisioning_tests">Device Owner provisioning tests</string>
@@ -2033,6 +2108,115 @@
     </string>
     <string name="device_owner_user_vpn_restriction_set">Set VPN restriction</string>
 
+    <!-- Policy transparency -->
+    <string name="device_owner_policy_transparency_test">Policy transparency</string>
+    <string name="device_owner_policy_transparency_test_instructions">
+        1. Go through the list of tests.\n
+        2. Mark the overall test pass or fail.\n
+    </string>
+
+    <string name="disallow_add_user">Disallow add user</string>
+    <string name="disallow_adjust_volume">Disallow adjust volume</string>
+    <string name="disallow_app_control">Disallow app control</string>
+    <string name="disallow_config_wifi">Disallow config wifi</string>
+    <string name="disallow_fun">Disallow fun</string>
+    <string name="disallow_modify_accounts">Disallow modify accounts</string>
+    <string name="disallow_share_location">Disallow share location</string>
+    <string name="disallow_usb_file_transfer">Disallow usb file transfer</string>
+    <string name="keyguard_disable_unredacted_notifications">Disable unredacted notifications</string>
+    <string name="maximum_time_to_lock">Maximum time to lock</string>
+    <string name="password_quality">Password quality</string>
+
+    <string name="disallow_add_user_instructions">
+        Please press the Set restriction button to set the user restriction.\n
+        Then go to quick settings -> owners and\n\n
+        Confirm that:\n
+        - No new user/guest can be added.\n
+        - Pressing Add user/Add guest opens up a "Disabled by policy" dialog.\n
+    </string>
+    <string name="disallow_adjust_volume_instructions">
+        Please press the Set restriction button to set the user restriction.\n
+        Then go to quick settings -> Do not disturb.\n\n
+        Confirm that:\n
+        - Do not disturb is disabled.\n
+        - Pressing DnD opens a disabled by policy dialog.\n
+    </string>
+    <string name="disallow_app_control_instructions">
+        Please press the Set restriction button to set the user restriction.\n
+        Then press Go button to open apps settings page.\n\n
+        Confirm that:\n
+        - Pressing DISABLE/UNINSTALL/FORCE STOP on any app opens a disabled by policy dialog.\n
+    </string>
+    <string name="disallow_config_wifi_instructions">
+        Please press the Set restriction button to set the user restriction.\n
+        Then press Go button to open Wifi settings page.\n\n
+        Confirm that:\n
+        - You cannot see any wifi options.\n
+        - You see a disabled by your organizations\'s administrator message.\n
+    </string>
+    <string name="disallow_fun_instructions">
+        Please press the Set restriction button to set the user restriction.\n
+        Then press Go button to open about phone settings page.\n
+        Click multiple times on the Android version tab.\n\n
+        Confirm that:\n
+        - Disabled by policy dialog comes up.\n
+        - You cannot open the game.\n
+    </string>
+    <string name="disallow_modify_accounts_instructions">
+        Please Press Go button and Add an Account.\n
+        Come back to this test and press the Set restriction button to set the user restriction.\n
+        Then press Go button to open the accounts page.\n\n
+        Confirm that:\n
+        - You cannot add an account.\n
+        - You cannot remove account using overflow menu in account page.\n
+        - Attempting to remove an account opens a disabled by policy dialog.\n
+    </string>
+    <string name="disallow_share_location_instructions">
+        Please press the Set restriction button to set the user restriction.\n
+        Then press Go button to open the Location\'s setting page.\n\n
+        Confirm that:\n
+        - You cannot trigger location mode.\n
+        - Pressing "Off" opens a disabled by policy dialog\n
+    </string>
+    <string name="disallow_usb_file_transfer_instructions">
+        Please press the Set restriction button to set the user restriction.\n
+        Then connect you device using usb.\n
+        Press "USB for charging" notification.\n\n
+        Confirm that:\n
+        - File transfer and photo transfer options are disabled.\n
+        - Pressing on them opens a disabled by policy dialog.\n
+    </string>
+    <string name="keyguard_disable_unredacted_notifications_instructions">
+        Please press the Set restriction button.\n
+        Then go to Notifications setting page.\n
+        Go to configure notifications. Press "When device is locked".\n\n
+        Confirm that:\n
+        - "Show all notification content" is disabled.\n
+        - Pressing on it opens a disabled by policy dialog.\n
+    </string>
+    <string name="set_max_time_to_lock_instructions">
+        Enter non zero maximum time to lock in Seconds.\n
+        Please press the Set restriction button.\n
+        Then press Go button to open the display settings page.\n\n
+        Confirm that:\n
+        - If the value set by you is less than 15, the sleep option is disabled.\n
+        - Pressing on it opens a disabled by policy dialog.\n
+        - For values from 15, All the invalid options are disabled.\n
+        - Pressing on them opens a disabled by policy dialog.\n
+    </string>
+    <string name="password_quality_instructions">
+        Please press the Set restriction button.\n
+        Then press Go button to open the security settings page.\n
+        Go to Choose screen lock.\n\n
+        Confirm that:\n
+        - All options other than "Password" are disabled.\n
+        - Pressing on them opens a disabled by policy dialog.\n
+        - You cannot set an only numeric password.\n
+        - You cannot set an only alpha password.\n
+        - You cannot set an alpha-numeric password.\n
+        - You can only set a password which contains at least a number, a letter and a special character.\n
+    </string>
+
     <!-- Strings for JobScheduler Tests -->
     <string name="js_test_description">This test is mostly automated, but requires some user interaction. You can pass this test once the list items below are checked.</string>
 
diff --git a/apps/CtsVerifier/res/xml/device_admin_byod.xml b/apps/CtsVerifier/res/xml/device_admin_byod.xml
index ce44794..f49a29c 100644
--- a/apps/CtsVerifier/res/xml/device_admin_byod.xml
+++ b/apps/CtsVerifier/res/xml/device_admin_byod.xml
@@ -22,6 +22,7 @@
         <reset-password />
         <disable-keyguard-features />
         <force-lock />
+        <limit-password />
     </uses-policies>
 </device-admin>
 <!-- END_INCLUDE(meta_data) -->
diff --git a/apps/CtsVerifier/res/xml/felicaservice.xml b/apps/CtsVerifier/res/xml/felicaservice.xml
new file mode 100644
index 0000000..7d1b164
--- /dev/null
+++ b/apps/CtsVerifier/res/xml/felicaservice.xml
@@ -0,0 +1,5 @@
+<host-nfcf-service xmlns:android="http://schemas.android.com/apk/res/android"
+    android:description="@string/felicaservice">
+    <system-code-filter android:name="4001" />
+    <nfcid2-filter android:name="02FE000000001481" />
+</host-nfcf-service>
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/location/LocationListenerActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/location/LocationListenerActivity.java
new file mode 100644
index 0000000..8140b3f
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/location/LocationListenerActivity.java
@@ -0,0 +1,126 @@
+package com.android.cts.verifier.location;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.location.Location;
+import android.location.LocationListener;
+import android.location.LocationManager;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.os.Process;
+import android.provider.Settings;
+import android.util.Log;
+import android.widget.Toast;
+
+import com.android.cts.verifier.R;
+
+public class LocationListenerActivity extends Activity implements Handler.Callback {
+    // Primary -> managed intent: request to goto the location settings page and listen to updates.
+    public static final String ACTION_SET_LOCATION_AND_CHECK_UPDATES =
+            "com.android.cts.verifier.location.SET_LOCATION_AND_CHECK";
+    private static final int REQUEST_LOCATION_UPDATE = 1;
+
+    private static final int MSG_TIMEOUT_ID = 1;
+
+    private static final long MSG_TIMEOUT_MILLISEC = 15000; // 15 seconds.
+
+    private LocationManager mLocationManager;
+    private Handler mHandler;
+    private boolean mIsLocationUpdated;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        mLocationManager = (LocationManager) getSystemService(LOCATION_SERVICE);
+        mHandler = new Handler(this);
+        mIsLocationUpdated = false;
+        Intent intent = getIntent();
+        if (intent != null) {
+            String action = intent.getAction();
+            if (ACTION_SET_LOCATION_AND_CHECK_UPDATES.equals(action)) {
+                Log.d(getLogTag(), "ACTION_SET_LOCATION_AND_CHECK_UPDATES received in uid "
+                        + Process.myUid());
+                handleLocationAction();
+            }
+        }
+    }
+
+    @Override
+    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+        switch (requestCode) {
+            case REQUEST_LOCATION_UPDATE: {
+                Log.d(getLogTag(), "Exit location settings:OK");
+                mLocationManager.removeUpdates(mLocationListener);
+                mHandler.removeMessages(MSG_TIMEOUT_ID);
+                finish();
+                break;
+            }
+            default: {
+                Log.wtf(getLogTag(), "Unknown requestCode " + requestCode + "; data = " + data);
+                break;
+            }
+        }
+    }
+
+    protected void handleLocationAction() {
+        Intent locationSettingsIntent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);
+        if (locationSettingsIntent.resolveActivity(getPackageManager()) != null) {
+            startActivityForResult(locationSettingsIntent, REQUEST_LOCATION_UPDATE);
+            scheduleTimeout();
+        } else {
+            Log.e(getLogTag(), "Settings.ACTION_LOCATION_SOURCE_SETTINGS could not be resolved");
+            finish();
+        }
+        mLocationManager.requestLocationUpdates(
+                LocationManager.GPS_PROVIDER, 0, 0, mLocationListener);
+    }
+
+    private final LocationListener mLocationListener = new LocationListener() {
+        @Override
+        public void onLocationChanged(Location location) {
+            synchronized (LocationListenerActivity.this) {
+                if (mIsLocationUpdated) return;
+                showToast(R.string.provisioning_byod_location_mode_enable_toast_location_change);
+                mIsLocationUpdated = true;
+            }
+        }
+
+        @Override
+        public void onProviderDisabled(String provider) {
+        }
+
+        @Override
+        public void onProviderEnabled(String provider) {
+        }
+
+        @Override
+        public void onStatusChanged(String provider, int status, Bundle extras) {
+        }
+    };
+
+    private void scheduleTimeout() {
+        mHandler.removeMessages(MSG_TIMEOUT_ID);
+        mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_TIMEOUT_ID), MSG_TIMEOUT_MILLISEC);
+    }
+
+    @Override
+    public boolean handleMessage(Message msg) {
+        if (msg.what == MSG_TIMEOUT_ID) {
+            synchronized (this) {
+                if (mIsLocationUpdated) return true;
+                showToast(R.string.provisioning_byod_location_mode_time_out_toast);
+            }
+        }
+        return true;
+    }
+
+    protected String getLogTag() {
+        return "LocationListenerActivity";
+    }
+
+    protected void showToast(int messageId) {
+        String message = getString(messageId);
+        Toast.makeText(this, message, Toast.LENGTH_SHORT).show();
+    }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodFlowTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodFlowTestActivity.java
index eb18f5a..67f5532f 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodFlowTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodFlowTestActivity.java
@@ -35,6 +35,7 @@
 import com.android.cts.verifier.TestListActivity;
 import com.android.cts.verifier.TestListAdapter.TestListItem;
 import com.android.cts.verifier.TestResult;
+import com.android.cts.verifier.location.LocationListenerActivity;
 
 /**
  * CTS verifier test for BYOD managed provisioning flow.
@@ -76,6 +77,9 @@
     private DialogTestListItem mPrintSettingsVisibleTest;
     private DialogTestListItem mIntentFiltersTest;
     private DialogTestListItem mPermissionLockdownTest;
+    private DialogTestListItem mTurnOffWorkIcon;
+    private DialogTestListItem mTurnOffWorkLauncher;
+    private DialogTestListItem mTurnOffWorkNotifications;
     private DialogTestListItem mCrossProfileImageCaptureSupportTest;
     private DialogTestListItem mCrossProfileVideoCaptureSupportTest;
     private DialogTestListItem mCrossProfileAudioCaptureSupportTest;
@@ -83,7 +87,11 @@
     private DialogTestListItem mDisableNfcBeamTest;
     private TestListItem mAuthenticationBoundKeyTest;
     private DialogTestListItem mEnableLocationModeTest;
-    private DialogTestListItem mDisableLocationModeTest;
+    private DialogTestListItem mDisableLocationModeThroughMainSwitchTest;
+    private DialogTestListItem mDisableLocationModeThroughWorkSwitchTest;
+    private DialogTestListItem mPrimaryLocationWhenWorkDisabledTest;
+    private DialogTestListItem mSelectWorkChallenge;
+    private DialogTestListItem mConfirmWorkCredentials;
     private TestListItem mVpnTest;
     private TestListItem mDisallowAppsControlTest;
 
@@ -335,6 +343,36 @@
                 R.string.profile_owner_permission_lockdown_test_info,
                 permissionCheckIntent);
 
+        mTurnOffWorkIcon = new DialogTestListItem(this,
+                R.string.provisioning_byod_turn_off_work_icon,
+                "BYOD_TurnOffWorkIcon",
+                R.string.provisioning_byod_turn_off_work_icon_instruction,
+                new Intent(Settings.ACTION_SETTINGS));
+
+        mTurnOffWorkLauncher = new DialogTestListItem(this,
+                R.string.provisioning_byod_turn_off_work_launcher,
+                "BYOD_TurnOffWorkStartApps",
+                R.string.provisioning_byod_turn_off_work_launcher_instruction,
+                new Intent(Settings.ACTION_SETTINGS));
+
+        mTurnOffWorkNotifications = new DialogTestListItem(this,
+                R.string.provisioning_byod_turn_off_work_notifications,
+                "BYOD_TurnOffWorkNotifications",
+                R.string.provisioning_byod_turn_off_work_notifications_instruction,
+                new Intent(ByodHelperActivity.ACTION_NOTIFICATION));
+
+        mSelectWorkChallenge = new DialogTestListItem(this,
+                R.string.provisioning_byod_select_work_challenge,
+                "BYOD_SelectWorkChallenge",
+                R.string.provisioning_byod_select_work_challenge_description,
+                new Intent(ByodHelperActivity.ACTION_TEST_SELECT_WORK_CHALLENGE));
+
+        mConfirmWorkCredentials = new DialogTestListItem(this,
+                R.string.provisioning_byod_confirm_work_credentials,
+                "BYOD_ConfirmWorkCredentials",
+                R.string.provisioning_byod_confirm_work_credentials_description,
+                new Intent(ByodHelperActivity.ACTION_TEST_CONFIRM_WORK_CREDENTIALS));
+
         adapter.add(mProfileOwnerInstalled);
 
         // Badge related tests
@@ -363,6 +401,12 @@
         adapter.add(mKeyguardDisabledFeaturesTest);
         adapter.add(mAuthenticationBoundKeyTest);
         adapter.add(mVpnTest);
+        adapter.add(mTurnOffWorkIcon);
+        adapter.add(mTurnOffWorkLauncher);
+        adapter.add(mTurnOffWorkNotifications);
+        adapter.add(mSelectWorkChallenge);
+        adapter.add(mConfirmWorkCredentials);
+
         if (canResolveIntent(new Intent(Settings.ACTION_APPLICATION_SETTINGS))) {
             adapter.add(mDisallowAppsControlTest);
         }
@@ -458,15 +502,26 @@
                     R.string.provisioning_byod_location_mode_enable,
                     "BYOD_LocationModeEnableTest",
                     R.string.provisioning_byod_location_mode_enable_instruction,
-                    new Intent(ByodHelperActivity.ACTION_SET_LOCATION_AND_CHECK_UPDATES));
-            mDisableLocationModeTest = new DialogTestListItem(this,
+                    new Intent(ByodHelperActivity.ACTION_BYOD_SET_LOCATION_AND_CHECK_UPDATES));
+            mDisableLocationModeThroughMainSwitchTest = new DialogTestListItem(this,
                     R.string.provisioning_byod_location_mode_disable,
-                    "BYOD_LocationModeDisableTest",
+                    "BYOD_LocationModeDisableMainTest",
                     R.string.provisioning_byod_location_mode_disable_instruction,
-                    new Intent(ByodHelperActivity.ACTION_SET_LOCATION_AND_CHECK_UPDATES));
-
+                    new Intent(ByodHelperActivity.ACTION_BYOD_SET_LOCATION_AND_CHECK_UPDATES));
+            mDisableLocationModeThroughWorkSwitchTest = new DialogTestListItem(this,
+                    R.string.provisioning_byod_work_location_mode_disable,
+                    "BYOD_LocationModeDisableWorkTest",
+                    R.string.provisioning_byod_work_location_mode_disable_instruction,
+                    new Intent(ByodHelperActivity.ACTION_BYOD_SET_LOCATION_AND_CHECK_UPDATES));
+            mPrimaryLocationWhenWorkDisabledTest = new DialogTestListItem(this,
+                    R.string.provisioning_byod_primary_location_when_work_disabled,
+                    "BYOD_PrimaryLocationWhenWorkDisabled",
+                    R.string.provisioning_byod_primary_location_when_work_disabled_instruction,
+                    new Intent(LocationListenerActivity.ACTION_SET_LOCATION_AND_CHECK_UPDATES));
             adapter.add(mEnableLocationModeTest);
-            adapter.add(mDisableLocationModeTest);
+            adapter.add(mDisableLocationModeThroughMainSwitchTest);
+            adapter.add(mDisableLocationModeThroughWorkSwitchTest);
+            adapter.add(mPrimaryLocationWhenWorkDisabledTest);
         } else {
             // The system does not support GPS feature, so skip test.
             Toast.makeText(ByodFlowTestActivity.this,
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodHelperActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodHelperActivity.java
index 40fac07..0653dee 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodHelperActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodHelperActivity.java
@@ -19,6 +19,7 @@
 import android.app.Activity;
 import android.app.admin.DevicePolicyManager;
 import android.app.Dialog;
+import android.app.KeyguardManager;
 import android.app.Notification;
 import android.app.NotificationManager;
 import android.content.BroadcastReceiver;
@@ -27,6 +28,7 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.pm.PackageManager;
+import android.graphics.Color;
 import android.location.Location;
 import android.location.LocationListener;
 import android.location.LocationManager;
@@ -46,6 +48,7 @@
 import java.io.File;
 import java.util.ArrayList;
 
+import com.android.cts.verifier.location.LocationListenerActivity;
 import com.android.cts.verifier.R;
 import com.android.cts.verifier.managedprovisioning.ByodPresentMediaDialog.DialogCallback;
 
@@ -58,7 +61,8 @@
  *
  * Note: We have to use a dummy activity because cross-profile intents only work for activities.
  */
-public class ByodHelperActivity extends Activity implements DialogCallback, Handler.Callback {
+public class ByodHelperActivity extends LocationListenerActivity
+        implements DialogCallback {
     static final String TAG = "ByodHelperActivity";
 
     // Primary -> managed intent: query if the profile owner has been set up.
@@ -105,7 +109,7 @@
             "com.android.cts.verifier.managedprovisioning.action.TEST_APP_LINKING_DIALOG";
 
     // Primary -> managed intent: request to goto the location settings page and listen to updates.
-    public static final String ACTION_SET_LOCATION_AND_CHECK_UPDATES =
+    public static final String ACTION_BYOD_SET_LOCATION_AND_CHECK_UPDATES =
             "com.android.cts.verifier.managedprovisioning.BYOD_SET_LOCATION_AND_CHECK";
     public static final String ACTION_NOTIFICATION =
             "com.android.cts.verifier.managedprovisioning.NOTIFICATION";
@@ -122,13 +126,20 @@
     public static final String ACTION_CLEAR_USER_RESTRICTION =
             "com.android.cts.verifier.managedprovisioning.BYOD_CLEAR_USER_RESTRICTION";
 
+    // Primary -> managed intent: Start the selection of a work challenge
+    public static final String ACTION_TEST_SELECT_WORK_CHALLENGE =
+            "com.android.cts.verifier.managedprovisioning.TEST_SELECT_WORK_CHALLENGE";
+
+    // Primary -> managed intent: Start the confirm credentials screen for the managed profile
+    public static final String ACTION_TEST_CONFIRM_WORK_CREDENTIALS =
+            "com.android.cts.verifier.managedprovisioning.TEST_CONFIRM_WORK_CREDENTIALS";
+
     public static final int RESULT_FAILED = RESULT_FIRST_USER;
 
-    private static final int REQUEST_INSTALL_PACKAGE = 1;
-    private static final int REQUEST_IMAGE_CAPTURE = 2;
-    private static final int REQUEST_VIDEO_CAPTURE = 3;
-    private static final int REQUEST_AUDIO_CAPTURE = 4;
-    private static final int REQUEST_LOCATION_UPDATE = 5;
+    private static final int REQUEST_INSTALL_PACKAGE = 2;
+    private static final int REQUEST_IMAGE_CAPTURE = 3;
+    private static final int REQUEST_VIDEO_CAPTURE = 4;
+    private static final int REQUEST_AUDIO_CAPTURE = 5;
 
     private static final String ORIGINAL_SETTINGS_NAME = "original settings";
 
@@ -137,15 +148,8 @@
     private NotificationManager mNotificationManager;
     private Bundle mOriginalSettings;
 
-    private static final int MSG_TIMEOUT = 1;
-
-    private static final long MSG_TIMEOUT_MILLISEC = 15 * 1000;
-
     private ComponentName mAdminReceiverComponent;
     private DevicePolicyManager mDevicePolicyManager;
-    private LocationManager mLocationManager;
-    private Handler mHandler;
-    private boolean mIsLocationUpdated;
 
     private Uri mImageUri;
     private Uri mVideoUri;
@@ -176,9 +180,6 @@
         mAdminReceiverComponent = new ComponentName(this, DeviceAdminTestReceiver.class.getName());
         mDevicePolicyManager = (DevicePolicyManager) getSystemService(
                 Context.DEVICE_POLICY_SERVICE);
-        mLocationManager = (LocationManager) getSystemService(LOCATION_SERVICE);
-        mHandler = new Handler(this);
-        mIsLocationUpdated = false;
         mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
         Intent intent = getIntent();
         String action = intent.getAction();
@@ -267,8 +268,7 @@
         } else if (ACTION_KEYGUARD_DISABLED_FEATURES.equals(action)) {
             final int value = intent.getIntExtra(EXTRA_PARAMETER_1,
                     DevicePolicyManager.KEYGUARD_DISABLE_FEATURES_NONE);
-            ComponentName admin = DeviceAdminTestReceiver.getReceiverComponentName();
-            mDevicePolicyManager.setKeyguardDisabledFeatures(admin, value);
+            mDevicePolicyManager.setKeyguardDisabledFeatures(mAdminReceiverComponent, value);
         } else if (ACTION_LOCKNOW.equals(action)) {
             mDevicePolicyManager.lockNow();
             setResult(RESULT_OK);
@@ -300,31 +300,26 @@
                 mDevicePolicyManager.clearUserRestriction(
                         DeviceAdminTestReceiver.getReceiverComponentName(), restriction);
             }
-        } else if (action.equals(ACTION_SET_LOCATION_AND_CHECK_UPDATES)) {
-            // Grant the locaiton permission to the provile owner on cts-verifier.
-            // The permission state does not have to be reverted at the end since the profile onwer
-            // is going to be deleted when BYOD tests ends.
-            grantLocationPermissionToSelf();
-            Intent locationSettingsIntent = getLocationSettingsIntent();
-            if (locationSettingsIntent.resolveActivity(getPackageManager()) != null) {
-                startActivityForResult(locationSettingsIntent, REQUEST_LOCATION_UPDATE);
-                scheduleTimeout();
-            } else {
-                Log.e(TAG, "BYOD settings could not be resolved in managed profile");
-                finish();
-            }
-            mLocationManager.requestLocationUpdates(
-                    LocationManager.GPS_PROVIDER, 0, 0, mLocationListener);
+        } else if (action.equals(ACTION_BYOD_SET_LOCATION_AND_CHECK_UPDATES)) {
+            handleLocationAction();
             return;
         } else if (action.equals(ACTION_NOTIFICATION)) {
             showNotification(Notification.VISIBILITY_PUBLIC);
         } else if (ACTION_NOTIFICATION_ON_LOCKSCREEN.equals(action)) {
-            DevicePolicyManager dpm = (DevicePolicyManager) getSystemService(
-                    Context.DEVICE_POLICY_SERVICE);
-            dpm.lockNow();
+            mDevicePolicyManager.lockNow();
             showNotification(Notification.VISIBILITY_PRIVATE);
         } else if (ACTION_CLEAR_NOTIFICATION.equals(action)) {
             mNotificationManager.cancel(NOTIFICATION_ID);
+        } else if (ACTION_TEST_SELECT_WORK_CHALLENGE.equals(action)) {
+            mDevicePolicyManager.setOrganizationColor(mAdminReceiverComponent, Color.BLUE);
+            mDevicePolicyManager.setOrganizationName(mAdminReceiverComponent, getResources()
+                    .getString(R.string.provisioning_byod_confirm_work_credentials_header));
+            startActivity(new Intent(DevicePolicyManager.ACTION_SET_NEW_PASSWORD));
+        } else if (ACTION_TEST_CONFIRM_WORK_CREDENTIALS.equals(action)) {
+            KeyguardManager keyguardManager =
+                    (KeyguardManager) getSystemService(Context.KEYGUARD_SERVICE);
+            Intent launchIntent = keyguardManager.createConfirmDeviceCredentialIntent(null, null);
+            startActivity(launchIntent);
         }
         // This activity has no UI and is only used to respond to CtsVerifier in the primary side.
         finish();
@@ -380,15 +375,8 @@
                 }
                 break;
             }
-            case REQUEST_LOCATION_UPDATE: {
-                Log.d(TAG, "BYOD exit location settings:OK");
-                mLocationManager.removeUpdates(mLocationListener);
-                mHandler.removeMessages(MSG_TIMEOUT);
-                finish();
-                break;
-            }
             default: {
-                Log.wtf(TAG, "Unknown requestCode " + requestCode + "; data = " + data);
+                super.onActivityResult(requestCode, resultCode, data);
                 break;
             }
         }
@@ -412,10 +400,6 @@
         return new Intent(MediaStore.Audio.Media.RECORD_SOUND_ACTION);
     }
 
-    public static Intent getLocationSettingsIntent() {
-        return new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);
-    }
-
     public static Intent createLockIntent() {
         return new Intent(ACTION_LOCKNOW);
     }
@@ -460,11 +444,6 @@
         startActivity(intent);
     }
 
-    private void showToast(int messageId) {
-        String message = getString(messageId);
-        Toast.makeText(this, message, Toast.LENGTH_SHORT).show();
-    }
-
     private void grantCameraPermissionToSelf() {
         mDevicePolicyManager.setPermissionGrantState(mAdminReceiverComponent, getPackageName(),
                 android.Manifest.permission.CAMERA,
@@ -478,49 +457,28 @@
         startActivity(chooser);
     }
 
+    @Override
+    protected void handleLocationAction() {
+        // Grant the locaiton permission to the provile owner on cts-verifier.
+        // The permission state does not have to be reverted at the end since the profile onwer
+        // is going to be deleted when BYOD tests ends.
+        grantLocationPermissionToSelf();
+        super.handleLocationAction();
+    }
+
     private void grantLocationPermissionToSelf() {
         mDevicePolicyManager.setPermissionGrantState(mAdminReceiverComponent, getPackageName(),
                 android.Manifest.permission.ACCESS_FINE_LOCATION,
                 DevicePolicyManager.PERMISSION_GRANT_STATE_GRANTED);
     }
 
-    private final LocationListener mLocationListener = new LocationListener() {
-        @Override
-        public void onLocationChanged(Location location) {
-            if (mIsLocationUpdated) return;
-            showToast(R.string.provisioning_byod_location_mode_enable_toast_location_change);
-            mIsLocationUpdated = true;
-        }
-
-        @Override
-        public void onProviderDisabled(String provider) {
-        }
-
-        @Override
-        public void onProviderEnabled(String provider) {
-        }
-
-        @Override
-        public void onStatusChanged(String provider, int status, Bundle extras) {
-        }
-    };
-
-    private void scheduleTimeout() {
-        mHandler.removeMessages(MSG_TIMEOUT);
-        mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_TIMEOUT), MSG_TIMEOUT_MILLISEC);
-    }
-
     @Override
     public void onDialogClose() {
         finish();
     }
 
     @Override
-    public boolean handleMessage(Message msg) {
-        if (msg.what == MSG_TIMEOUT) {
-            if (mIsLocationUpdated) return true;
-            showToast(R.string.provisioning_byod_location_mode_time_out_toast);
-        }
-        return true;
+    protected String getLogTag() {
+        return TAG;
     }
 }
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceAdminTestReceiver.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceAdminTestReceiver.java
index f102b51..57987a6 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceAdminTestReceiver.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceAdminTestReceiver.java
@@ -25,6 +25,8 @@
 import android.provider.Settings;
 import android.util.Log;
 
+import com.android.cts.verifier.location.LocationListenerActivity;
+
 /**
  * Profile owner receiver for BYOD flow test.
  * Setup cross-profile intent filter after successful provisioning.
@@ -78,8 +80,10 @@
             filter.addAction(
                     PermissionLockdownTestActivity.ACTION_MANAGED_PROFILE_CHECK_PERMISSION_LOCKDOWN);
             filter.addAction(AuthenticationBoundKeyTestActivity.ACTION_AUTH_BOUND_KEY_TEST);
-            filter.addAction(ByodHelperActivity.ACTION_SET_LOCATION_AND_CHECK_UPDATES);
+            filter.addAction(ByodHelperActivity.ACTION_BYOD_SET_LOCATION_AND_CHECK_UPDATES);
             filter.addAction(VpnTestActivity.ACTION_VPN);
+            filter.addAction(ByodHelperActivity.ACTION_TEST_SELECT_WORK_CHALLENGE);
+            filter.addAction(ByodHelperActivity.ACTION_TEST_CONFIRM_WORK_CREDENTIALS);
             dpm.addCrossProfileIntentFilter(getWho(context), filter,
                     DevicePolicyManager.FLAG_MANAGED_CAN_ACCESS_PARENT);
 
@@ -87,6 +91,7 @@
             filter = new IntentFilter();
             filter.addAction(ByodHelperActivity.ACTION_PROFILE_OWNER_STATUS);
             filter.addAction(CrossProfileTestActivity.ACTION_CROSS_PROFILE_TO_PERSONAL);
+            filter.addAction(LocationListenerActivity.ACTION_SET_LOCATION_AND_CHECK_UPDATES);
             dpm.addCrossProfileIntentFilter(getWho(context), filter,
                     DevicePolicyManager.FLAG_PARENT_CAN_ACCESS_MANAGED);
 
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceOwnerPositiveTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceOwnerPositiveTestActivity.java
index ef2cc25..96a014c 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceOwnerPositiveTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceOwnerPositiveTestActivity.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2015 The Android Open Source Project
+ * 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.
@@ -65,6 +65,9 @@
     static final String COMMAND_SET_GLOBAL_SETTING = "set-global-setting";
     static final String COMMAND_SET_STATUSBAR_DISABLED = "set-statusbar-disabled";
     static final String COMMAND_SET_KEYGUARD_DISABLED = "set-keyguard-disabled";
+    static final String COMMAND_SET_KEYGUARD_DISABLED_FEATURE = "set-keyguard-disabled-feature";
+    static final String COMMAND_SET_MAXIMUM_TIME_TO_LOCK = "set-maximum-time-to-lock";
+    static final String COMMAND_SET_PASSWORD_QUALITY = "set-password-quality";
     static final String COMMAND_CHECK_PERMISSION_LOCKDOWN = "check-permission-lockdown";
     static final String EXTRA_SETTING = "extra-setting";
 
@@ -82,6 +85,7 @@
     //TODO(rgl): This symbol should be available in android.provider.settings
     private static final String ACTION_VPN_SETTINGS = "android.net.vpn.SETTINGS";
     private static final String DISALLOW_DATA_ROAMING_ID = "DISALLOW_DATA_ROAMING";
+    private static final String POLICY_TRANSPARENCY_ID = PolicyTransparencyActivity.class.getName();
     private static final String REMOVE_DEVICE_OWNER_TEST_ID = "REMOVE_DEVICE_OWNER";
 
     @Override
@@ -254,6 +258,11 @@
                 R.string.device_profile_owner_permission_lockdown_test,
                 new Intent(PermissionLockdownTestActivity.ACTION_CHECK_PERMISSION_LOCKDOWN)));
 
+        // Policy Transparency
+        adapter.add(createTestItem(this, POLICY_TRANSPARENCY_ID,
+                R.string.device_owner_policy_transparency_test,
+                new Intent(this, PolicyTransparencyActivity.class)));
+
         // removeDeviceOwner
         adapter.add(createInteractiveTestItem(this, REMOVE_DEVICE_OWNER_TEST_ID,
                 R.string.device_owner_remove_device_owner_test,
@@ -322,6 +331,15 @@
                         dpm.resetPassword(null, 0);
                     }
                     dpm.setKeyguardDisabled(admin, value);
+                } else if (COMMAND_SET_KEYGUARD_DISABLED_FEATURE.equals(command)) {
+                    final int value = intent.getIntExtra(EXTRA_PARAMETER_1, 0);
+                    dpm.setKeyguardDisabledFeatures(admin, value);
+                } else if (COMMAND_SET_MAXIMUM_TIME_TO_LOCK.equals(command)) {
+                    final int value = intent.getIntExtra(EXTRA_PARAMETER_1, 0);
+                    dpm.setMaximumTimeToLock(admin, value);
+                } else if (COMMAND_SET_PASSWORD_QUALITY.equals(command)) {
+                    final int value = intent.getIntExtra(EXTRA_PARAMETER_1, 0);
+                    dpm.setPasswordQuality(admin, value);
                 } else if (COMMAND_CHECK_DEVICE_OWNER.equals(command)) {
                     if (dpm.isDeviceOwnerApp(getPackageName())) {
                         TestResult.setPassedResult(this, intent.getStringExtra(EXTRA_TEST_ID),
@@ -352,6 +370,19 @@
             dpm.clearUserRestriction(admin, UserManager.DISALLOW_CONFIG_WIFI);
             dpm.clearUserRestriction(admin, UserManager.DISALLOW_CONFIG_VPN);
             dpm.clearUserRestriction(admin, UserManager.DISALLOW_DATA_ROAMING);
+
+            // Policy transparency tear down.
+            dpm.clearUserRestriction(admin, UserManager.DISALLOW_ADD_USER);
+            dpm.clearUserRestriction(admin, UserManager.DISALLOW_ADJUST_VOLUME);
+            dpm.clearUserRestriction(admin, UserManager.DISALLOW_APPS_CONTROL);
+            dpm.clearUserRestriction(admin, UserManager.DISALLOW_FUN);
+            dpm.clearUserRestriction(admin, UserManager.DISALLOW_MODIFY_ACCOUNTS);
+            dpm.clearUserRestriction(admin, UserManager.DISALLOW_SHARE_LOCATION);
+            dpm.clearUserRestriction(admin, UserManager.DISALLOW_USB_FILE_TRANSFER);
+            dpm.setKeyguardDisabledFeatures(admin, 0);
+            dpm.setMaximumTimeToLock(admin, 0);
+            dpm.setPasswordQuality(admin, 0);
+
             dpm.clearDeviceOwnerApp(getPackageName());
         }
     }
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/PolicyTransparencyActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/PolicyTransparencyActivity.java
new file mode 100644
index 0000000..4e5b6c9
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/PolicyTransparencyActivity.java
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.verifier.managedprovisioning;
+
+import android.app.admin.DevicePolicyManager;
+import android.content.Intent;
+import android.database.DataSetObserver;
+import android.os.Bundle;
+import android.os.UserManager;
+import android.provider.Settings;
+
+import com.android.cts.verifier.ArrayTestListAdapter;
+import com.android.cts.verifier.IntentDrivenTestActivity.ButtonInfo;
+import com.android.cts.verifier.PassFailButtons;
+import com.android.cts.verifier.R;
+import com.android.cts.verifier.TestListAdapter.TestListItem;
+import com.android.cts.verifier.managedprovisioning.DeviceOwnerPositiveTestActivity.CommandReceiver;
+
+/**
+ * Test class to verify policy transparency on certain device owner restrictions.
+ */
+public class PolicyTransparencyActivity extends PassFailButtons.TestListActivity {
+    private static final String KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS_ID =
+            "KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS";
+    private static final String SET_PASSWORD_QUALITY_ID = "SET_PASSWORD_QUALITY";
+    private static final String SET_MAXIMUM_TIME_TO_LOCK_ID =
+            SetMaximumTimeToLockActivity.class.getName();
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.pass_fail_list);
+        setInfoResources(R.string.device_owner_policy_transparency_test,
+                R.string.device_owner_policy_transparency_test_instructions, 0);
+        setPassFailButtonClickListeners();
+
+        final ArrayTestListAdapter adapter = new ArrayTestListAdapter(this);
+        addTestsToAdapter(adapter);
+        adapter.registerDataSetObserver(new DataSetObserver() {
+            @Override
+            public void onChanged() {
+                updatePassButton();
+            }
+        });
+
+        setTestListAdapter(adapter);
+    }
+
+    private void addTestsToAdapter(final ArrayTestListAdapter adapter) {
+        adapter.add(createSetUserRestrictionButtonInteractiveTestItem(UserManager.DISALLOW_ADD_USER,
+                R.string.disallow_add_user,
+                R.string.disallow_add_user_instructions,
+                UserManager.DISALLOW_ADD_USER));
+        adapter.add(createSetUserRestrictionButtonInteractiveTestItem(
+                UserManager.DISALLOW_ADJUST_VOLUME,
+                R.string.disallow_adjust_volume,
+                R.string.disallow_adjust_volume_instructions,
+                UserManager.DISALLOW_ADJUST_VOLUME));
+        adapter.add(createSetAndGoInteractiveTestItem(UserManager.DISALLOW_APPS_CONTROL,
+                R.string.disallow_app_control,
+                R.string.disallow_app_control_instructions,
+                UserManager.DISALLOW_APPS_CONTROL,
+                new Intent(Settings.ACTION_APPLICATION_SETTINGS)));
+        adapter.add(createSetAndGoInteractiveTestItem(UserManager.DISALLOW_CONFIG_WIFI,
+                R.string.disallow_config_wifi,
+                R.string.disallow_config_wifi_instructions,
+                UserManager.DISALLOW_CONFIG_WIFI,
+                new Intent(Settings.ACTION_WIFI_SETTINGS)));
+        adapter.add(createSetAndGoInteractiveTestItem(UserManager.DISALLOW_FUN,
+                R.string.disallow_fun,
+                R.string.disallow_fun_instructions,
+                UserManager.DISALLOW_FUN,
+                new Intent(Settings.ACTION_DEVICE_INFO_SETTINGS)));
+        adapter.add(createSetAndGoInteractiveTestItem(UserManager.DISALLOW_MODIFY_ACCOUNTS,
+                R.string.disallow_modify_accounts,
+                R.string.disallow_modify_accounts_instructions,
+                UserManager.DISALLOW_MODIFY_ACCOUNTS,
+                new Intent(Settings.ACTION_SYNC_SETTINGS)));
+        adapter.add(createSetAndGoInteractiveTestItem(UserManager.DISALLOW_SHARE_LOCATION,
+                R.string.disallow_share_location,
+                R.string.disallow_share_location_instructions,
+                UserManager.DISALLOW_SHARE_LOCATION,
+                new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS)));
+        adapter.add(createSetUserRestrictionButtonInteractiveTestItem(
+                UserManager.DISALLOW_USB_FILE_TRANSFER,
+                R.string.disallow_usb_file_transfer,
+                R.string.disallow_usb_file_transfer_instructions,
+                UserManager.DISALLOW_USB_FILE_TRANSFER));
+        adapter.add(Utils.createInteractiveTestItem(this,
+                KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS_ID,
+                R.string.keyguard_disable_unredacted_notifications,
+                R.string.keyguard_disable_unredacted_notifications_instructions,
+                new ButtonInfo(R.string.device_owner_user_restriction_set,
+                        createDeviceOwnerIntentWithIntParameter(
+                                DeviceOwnerPositiveTestActivity.
+                                        COMMAND_SET_KEYGUARD_DISABLED_FEATURE,
+                                DevicePolicyManager.KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS))));
+        adapter.add(DeviceOwnerPositiveTestActivity.createTestItem(this,
+                SET_MAXIMUM_TIME_TO_LOCK_ID,
+                R.string.maximum_time_to_lock,
+                new Intent(this, SetMaximumTimeToLockActivity.class)));
+        adapter.add(Utils.createInteractiveTestItem(this,
+                SET_PASSWORD_QUALITY_ID,
+                R.string.password_quality,
+                R.string.password_quality_instructions,
+                new ButtonInfo[] {
+                        new ButtonInfo(R.string.device_owner_user_restriction_set,
+                                createDeviceOwnerIntentWithIntParameter(
+                                        DeviceOwnerPositiveTestActivity.
+                                                COMMAND_SET_PASSWORD_QUALITY,
+                                        DevicePolicyManager.PASSWORD_QUALITY_COMPLEX)),
+                        new ButtonInfo(R.string.device_owner_settings_go,
+                                new Intent(Settings.ACTION_SECURITY_SETTINGS))}));
+    }
+
+    private Intent createSetUserRestrictionIntent(String restriction) {
+        return new Intent(this, CommandReceiver.class)
+                .putExtra(DeviceOwnerPositiveTestActivity.EXTRA_COMMAND,
+                        DeviceOwnerPositiveTestActivity.COMMAND_ADD_USER_RESTRICTION)
+                .putExtra(DeviceOwnerPositiveTestActivity.EXTRA_RESTRICTION, restriction);
+    }
+
+    private Intent createDeviceOwnerIntentWithIntParameter(String command, int value) {
+        return new Intent(this, CommandReceiver.class)
+                .putExtra(DeviceOwnerPositiveTestActivity.EXTRA_COMMAND, command)
+                .putExtra(DeviceOwnerPositiveTestActivity.EXTRA_PARAMETER_1, value);
+    }
+
+    private TestListItem createSetUserRestrictionButtonInteractiveTestItem(String id, int titleRes,
+            int infoRes, String restriction) {
+        return Utils.createInteractiveTestItem(this, id, titleRes, infoRes,
+                new ButtonInfo(R.string.device_owner_user_restriction_set,
+                        createSetUserRestrictionIntent(restriction)));
+    }
+
+    private TestListItem createSetAndGoInteractiveTestItem(String id, int titleRes, int infoRes,
+            String restriction, Intent goIntent) {
+        return Utils.createInteractiveTestItem(this, id, titleRes, infoRes,
+                new ButtonInfo[] {
+                        new ButtonInfo(R.string.device_owner_user_restriction_set,
+                                createSetUserRestrictionIntent(restriction)),
+                        new ButtonInfo(R.string.device_owner_settings_go, goIntent)});
+    }
+
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/SetMaximumTimeToLockActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/SetMaximumTimeToLockActivity.java
new file mode 100644
index 0000000..f6a865c
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/SetMaximumTimeToLockActivity.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.android.cts.verifier.managedprovisioning;
+
+import android.content.Intent;
+import android.os.Bundle;
+import android.provider.Settings;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.EditText;
+import android.widget.Toast;
+
+import com.android.cts.verifier.PassFailButtons;
+import com.android.cts.verifier.R;
+
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Test class to verify policy transparency for maximum time to lock.
+ */
+public class SetMaximumTimeToLockActivity extends PassFailButtons.Activity {
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.time_to_lock);
+        setPassFailButtonClickListeners();
+
+        View setMaxTimeToLockButton = findViewById(R.id.set_max_time_to_lock);
+        setMaxTimeToLockButton.setOnClickListener(new OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                EditText timeEditText = (EditText) findViewById(R.id.time_to_lock);
+                try {
+                    // Convert time to milliseconds.
+                    final int timeInSeconds = Integer.parseInt(timeEditText.getText().toString());
+                    final int timeInMilliseconds =
+                            (int) TimeUnit.MILLISECONDS.convert(timeInSeconds, TimeUnit.SECONDS);
+                    startActivity(new Intent(SetMaximumTimeToLockActivity.this,
+                            DeviceOwnerPositiveTestActivity.CommandReceiver.class)
+                                    .putExtra(DeviceOwnerPositiveTestActivity.EXTRA_COMMAND,
+                                            DeviceOwnerPositiveTestActivity
+                                                    .COMMAND_SET_MAXIMUM_TIME_TO_LOCK)
+                                    .putExtra(DeviceOwnerPositiveTestActivity.EXTRA_PARAMETER_1,
+                                            timeInMilliseconds));
+                } catch (Exception e) {
+                    Toast.makeText(SetMaximumTimeToLockActivity.this, "Please enter a valid number",
+                            Toast.LENGTH_LONG).show();
+                }
+            }
+        });
+
+        View goButton = findViewById(R.id.go_button);
+        goButton.setOnClickListener(new OnClickListener() {
+
+            @Override
+            public void onClick(View v) {
+                startActivity(new Intent(Settings.ACTION_DISPLAY_SETTINGS));
+            }
+        });
+    }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/NfcTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/NfcTestActivity.java
index 68fc027..eb84d1e 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/NfcTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/NfcTestActivity.java
@@ -22,6 +22,8 @@
 import com.android.cts.verifier.TestListAdapter.TestListItem;
 import com.android.cts.verifier.nfc.hce.HceEmulatorTestActivity;
 import com.android.cts.verifier.nfc.hce.HceReaderTestActivity;
+import com.android.cts.verifier.nfc.hcef.HceFEmulatorTestActivity;
+import com.android.cts.verifier.nfc.hcef.HceFReaderTestActivity;
 
 import android.content.Intent;
 import android.content.pm.PackageManager;
@@ -82,6 +84,16 @@
                     new Intent(this, HceEmulatorTestActivity.class), null));
         }
 
+        if (getPackageManager().hasSystemFeature(PackageManager.FEATURE_NFC_HOST_CARD_EMULATION_NFCF)) {
+            adapter.add(TestListItem.newCategory(this, R.string.nfc_hce_f));
+            adapter.add(TestListItem.newTest(this, R.string.nfc_hce_f_reader_tests,
+                    HceFReaderTestActivity.class.getName(),
+                    new Intent(this, HceFReaderTestActivity.class), null));
+            adapter.add(TestListItem.newTest(this, R.string.nfc_hce_f_emulator_tests,
+                    HceFEmulatorTestActivity.class.getName(),
+                    new Intent(this, HceFEmulatorTestActivity.class), null));
+        }
+
         setTestListAdapter(adapter);
     }
 
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hcef/HceFEmulatorActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hcef/HceFEmulatorActivity.java
new file mode 100644
index 0000000..89f040b
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hcef/HceFEmulatorActivity.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.cts.verifier.nfc.hcef;
+
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.nfc.NfcAdapter;
+import android.nfc.cardemulation.NfcFCardEmulation;
+import android.os.Bundle;
+
+import com.android.cts.verifier.PassFailButtons;
+import com.android.cts.verifier.R;
+
+public class HceFEmulatorActivity extends PassFailButtons.Activity{
+    static String ACTION_TEST_SUCCESS = "success";
+
+    NfcAdapter mAdapter;
+    NfcFCardEmulation mNfcFCardEmulation;
+
+    final BroadcastReceiver mReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            String action = intent.getAction();
+
+            if (ACTION_TEST_SUCCESS.equals(action)) {
+                getPassButton().setEnabled(true);
+            }
+        }
+    };
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.pass_fail_text);
+        setPassFailButtonClickListeners();
+        getPassButton().setEnabled(false);
+        mAdapter = NfcAdapter.getDefaultAdapter(this);
+        mNfcFCardEmulation = NfcFCardEmulation.getInstance(mAdapter);
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+        IntentFilter filter = new IntentFilter(ACTION_TEST_SUCCESS);
+        registerReceiver(mReceiver, filter);
+        ComponentName hceFService = new ComponentName("com.android.cts.verifier",
+                MyHostFelicaService.class.getName());
+        mNfcFCardEmulation.enableNfcFForegroundService(this, hceFService);
+    }
+
+    @Override
+    protected void onPause() {
+        super.onPause();
+        unregisterReceiver(mReceiver);
+    }
+
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hcef/HceFEmulatorTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hcef/HceFEmulatorTestActivity.java
new file mode 100644
index 0000000..026bb45
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hcef/HceFEmulatorTestActivity.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.verifier.nfc.hcef;
+
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.nfc.NfcAdapter;
+import android.nfc.cardemulation.CardEmulation;
+import android.os.Bundle;
+
+import com.android.cts.verifier.ArrayTestListAdapter;
+import com.android.cts.verifier.PassFailButtons;
+import com.android.cts.verifier.R;
+import com.android.cts.verifier.TestListAdapter.TestListItem;
+
+/** Activity that lists all the NFC HCE emulator tests. */
+public class HceFEmulatorTestActivity extends PassFailButtons.TestListActivity {
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.pass_fail_list);
+        setInfoResources(R.string.nfc_test, R.string.nfc_hce_emulator_test_info, 0);
+        setPassFailButtonClickListeners();
+
+        ArrayTestListAdapter adapter = new ArrayTestListAdapter(this);
+
+        NfcAdapter nfcAdapter = NfcAdapter.getDefaultAdapter(this);
+        CardEmulation cardEmulation = CardEmulation.getInstance(nfcAdapter);
+        if (getPackageManager().hasSystemFeature(
+                PackageManager.FEATURE_NFC_HOST_CARD_EMULATION_NFCF)) {
+            adapter.add(TestListItem.newCategory(this, R.string.nfc_hce_f_emulator_tests));
+
+            adapter.add(TestListItem.newTest(this, R.string.nfc_hce_f_emulator,
+                    HceFEmulatorActivity.class.getName(),
+                    new Intent(this, HceFEmulatorActivity.class), null));
+        }
+
+        setTestListAdapter(adapter);
+    }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hcef/HceFReaderActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hcef/HceFReaderActivity.java
new file mode 100644
index 0000000..ba31ff7
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hcef/HceFReaderActivity.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.cts.verifier.nfc.hcef;
+
+import android.annotation.TargetApi;
+import android.nfc.NfcAdapter;
+import android.nfc.NfcAdapter.ReaderCallback;
+import android.nfc.Tag;
+import android.nfc.tech.NfcF;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.View;
+import android.widget.AdapterView;
+import android.widget.AdapterView.OnItemSelectedListener;
+
+import com.android.cts.verifier.PassFailButtons;
+import com.android.cts.verifier.R;
+
+import java.io.IOException;
+
+@TargetApi(24)
+public class HceFReaderActivity extends PassFailButtons.Activity implements ReaderCallback,
+        OnItemSelectedListener {
+    public static final String TAG = "HceFReaderActivity";
+
+    NfcAdapter mAdapter;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.pass_fail_text);
+        setPassFailButtonClickListeners();
+        getPassButton().setEnabled(false);
+
+        mAdapter = NfcAdapter.getDefaultAdapter(this);
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+        mAdapter.enableReaderMode(this, this, NfcAdapter.FLAG_READER_NFC_F |
+                NfcAdapter.FLAG_READER_SKIP_NDEF_CHECK, null);
+    }
+
+    static byte[] createEchoCommand(byte[] nfcid2, byte[] payload) {
+        byte length = (byte) (2 + nfcid2.length + payload.length);
+
+        byte[] echo_cmd = new byte[length];
+        echo_cmd[0] = length;
+        echo_cmd[1] = MyHostFelicaService.CMD_ECHO;
+        System.arraycopy(nfcid2, 0, echo_cmd, 2, nfcid2.length);
+        System.arraycopy(payload, 0, echo_cmd, 2 + nfcid2.length, payload.length);
+        return echo_cmd;
+    }
+
+    static byte[] createSuccessCommand(byte[] nfcid2) {
+        byte[] cmd = new byte[2 + nfcid2.length];
+        cmd[0] = (byte) (2 + nfcid2.length);
+        cmd[1] = MyHostFelicaService.CMD_SUCCESS;
+        System.arraycopy(nfcid2, 0, cmd, 2, nfcid2.length);
+        return cmd;
+    }
+
+    static boolean verifyResponse(byte[] cmd, byte[] resp) {
+        if (resp == null) return false;
+
+        // Verify length
+        if (resp[0] != resp.length) return false;
+        if (resp.length != cmd.length) return false;
+        // Verify cmd
+        if (resp[1] != MyHostFelicaService.RESPONSE_ECHO) return false;
+
+        // Verify rest of data
+        for (int i = 2; i < resp.length; i++) {
+            if (resp[i] != cmd[i]) return false;
+        }
+
+        return true;
+    }
+
+    @Override
+    public void onTagDiscovered(Tag tag) {
+        NfcF felica = NfcF.get(tag);
+        if (felica == null) return;
+
+        try {
+            felica.connect();
+            for (int i = 0; i < 32; i++) {
+                byte[] payload = new byte[] {0x14, (byte)i};
+                byte[] echo_cmd = createEchoCommand(MyHostFelicaService.NFCID2, payload);
+                byte[] resp = felica.transceive(echo_cmd);
+                if (!verifyResponse(echo_cmd, resp)) {
+                    Log.e(TAG, "Echo response not correct.");
+                    return;
+                }
+            }
+            // All successful, send success cmd
+            byte[] success_cmd = createSuccessCommand(MyHostFelicaService.NFCID2);
+            felica.transceive(success_cmd);
+
+            runOnUiThread(new Runnable() {
+                @Override
+                public void run() {
+                    getPassButton().setEnabled(true);
+                }
+            });
+        } catch (IOException e) {
+            Log.e(TAG, "IOException, try again.");
+        }
+    }
+
+    @Override
+    public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
+
+    }
+
+    @Override
+    public void onNothingSelected(AdapterView<?> parent) {
+
+    }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hcef/HceFReaderTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hcef/HceFReaderTestActivity.java
new file mode 100644
index 0000000..52de01d
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hcef/HceFReaderTestActivity.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.verifier.nfc.hcef;
+
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.os.Bundle;
+
+import com.android.cts.verifier.ArrayTestListAdapter;
+import com.android.cts.verifier.PassFailButtons;
+import com.android.cts.verifier.R;
+import com.android.cts.verifier.TestListAdapter.TestListItem;
+
+/** Activity that lists all the NFC HCE reader tests. */
+public class HceFReaderTestActivity extends PassFailButtons.TestListActivity {
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.pass_fail_list);
+        setInfoResources(R.string.nfc_test, R.string.nfc_hce_reader_test_info, 0);
+        setPassFailButtonClickListeners();
+
+        ArrayTestListAdapter adapter = new ArrayTestListAdapter(this);
+
+        if (getPackageManager().hasSystemFeature(
+                PackageManager.FEATURE_NFC_HOST_CARD_EMULATION_NFCF)) {
+            adapter.add(TestListItem.newCategory(this, R.string.nfc_hce_f_reader_tests));
+
+            adapter.add(TestListItem.newTest(this, R.string.nfc_hce_f_reader,
+                    HceFReaderActivity.class.getName(),
+                    new Intent(this, HceFReaderActivity.class), null));
+
+        }
+
+        setTestListAdapter(adapter);
+    }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hcef/MyHostFelicaService.java b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hcef/MyHostFelicaService.java
new file mode 100644
index 0000000..941ea2c
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/nfc/hcef/MyHostFelicaService.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.verifier.nfc.hcef;
+
+import android.content.Intent;
+import android.nfc.cardemulation.HostNfcFService;
+import android.os.Bundle;
+import android.util.Log;
+
+import com.android.cts.verifier.nfc.hce.HceUtils;
+
+public class MyHostFelicaService extends HostNfcFService {
+
+    static final String TAG = "MyHostFelicaService";
+    static byte[] NFCID2 = {0x02, (byte) 0xFE, 0x00, 0x00, 0x00, 0x00, 0x14, (byte)0x81};
+
+    static byte CMD_REQUEST_SYSTEM_CODES = 0x0C;
+    static byte RESPONSE_SYSTEM_CODES = 0x0D;
+    static byte CMD_ECHO = (byte) 0xFE;
+    static byte RESPONSE_ECHO = (byte) 0xFF;
+    static byte CMD_SUCCESS = (byte) 0x81;
+
+    static byte[] handleSystemCodeRequest(byte[] sc_request) {
+        // Request system code command
+        byte[] response = new byte[13];
+        response[0] = 0x0D; // length
+        response[1] = RESPONSE_SYSTEM_CODES; // get system codes resp
+        System.arraycopy(sc_request, 2, response, 2, 8);
+        response[10] = 0x01;
+        response[11] = 0x40;
+        response[12] = 0x01;
+        return response;
+    }
+
+    static byte[] handleEchoRequest(byte[] echo_request) {
+        byte[] response = new byte[echo_request.length];
+        response[0] = (byte) echo_request.length;
+        response[1] = RESPONSE_ECHO;
+        for (int i = 2; i < echo_request.length; i++) {
+            // Copy NFCID2 and rest of data
+            response[i] = echo_request[i];
+        }
+        return response;
+
+    }
+
+    @Override
+    public byte[] processNfcFPacket(byte[] bytes, Bundle bundle) {
+        // Verify that NFCID2 matches with this service
+        if (bytes.length < 2 + NFCID2.length) {
+            Log.e(TAG, "Packet not long enough.");
+            return null;
+        }
+        for (int i = 0; i < NFCID2.length; i++) {
+           if (bytes[2 + i] != NFCID2[i]) {
+               Log.e(TAG, "NFCID2 does not match.");
+               return null;
+           }
+        }
+        byte cmd = bytes[1];
+        if (cmd == CMD_REQUEST_SYSTEM_CODES) {
+            return handleSystemCodeRequest(bytes);
+        } else if (cmd == CMD_ECHO) {
+            return handleEchoRequest(bytes);
+        } else if (cmd == CMD_SUCCESS) {
+            // Mark the test a success
+            Intent successIntent = new Intent(HceFEmulatorActivity.ACTION_TEST_SUCCESS);
+            sendBroadcast(successIntent);
+            // And just echo cmd back
+            return bytes;
+        } else {
+            Log.e(TAG, "Invalid command received");
+        }
+
+        return null;
+    }
+
+    @Override
+    public void onDeactivated(int i) {
+
+    }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/notifications/MockConditionProvider.java b/apps/CtsVerifier/src/com/android/cts/verifier/notifications/MockConditionProvider.java
index 4b90d9a..99ecd0c 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/notifications/MockConditionProvider.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/notifications/MockConditionProvider.java
@@ -144,11 +144,6 @@
     }
 
     @Override
-    public void onRequestConditions(int relevance) {
-
-    }
-
-    @Override
     public void onSubscribe(Uri conditionId) {
         Log.d(TAG, "subscribed to " + conditionId);
         mSubscriptions.add(conditionId);
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/RVCVXCheckAnalyzer.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/RVCVXCheckAnalyzer.java
index 3dc7270..7bc87ad 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/RVCVXCheckAnalyzer.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/RVCVXCheckAnalyzer.java
@@ -31,7 +31,7 @@
 import org.opencv.core.MatOfPoint2f;
 import org.opencv.core.MatOfPoint3f;
 import org.opencv.core.Size;
-import org.opencv.highgui.Highgui;
+import org.opencv.imgcodecs.Imgcodecs;
 import org.opencv.imgproc.Imgproc;
 import org.opencv.calib3d.Calib3d;
 import org.opencv.core.Core;
@@ -69,6 +69,7 @@
     private static final boolean FORCE_CV_ANALYSIS  = false;
     private static final boolean TRACE_VIDEO_ANALYSIS = false;
     private static final double DECIMATION_FPS_TARGET = 15.0;
+    private static final double MIN_VIDEO_LENGTH_SEC = 10;
 
     RVCVXCheckAnalyzer(String path)
     {
@@ -201,6 +202,12 @@
                 report.reason = "Unable to to load recorded video.";
                 return report;
             }
+            if (nframe < MIN_VIDEO_LENGTH_SEC*VALID_FRAME_THRESHOLD) {
+                // video is too short
+                Log.w(TAG, "Video record is to short, n frame = " + nframe);
+                report.reason = "Video too short.";
+                return report;
+            }
             if ((double) nvlog / nframe < VALID_FRAME_THRESHOLD) {
                 // too many invalid frames
                 Log.w(TAG, "Too many invalid frames, n valid frame = " + nvlog +
@@ -840,7 +847,7 @@
             // convert to gray manually as by default findCirclesGridDefault uses COLOR_BGR2GRAY
             Imgproc.cvtColor(frame, gray, Imgproc.COLOR_RGB2GRAY);
 
-            boolean foundPattern = Calib3d.findCirclesGridDefault(
+            boolean foundPattern = Calib3d.findCirclesGrid(
                     gray,  patternSize, centers, Calib3d.CALIB_CB_ASYMMETRIC_GRID);
 
             if (!foundPattern) {
@@ -894,7 +901,7 @@
 
             if (OUTPUT_DEBUG_IMAGE) {
                 Calib3d.drawChessboardCorners(frame, patternSize, reprojCenters, true);
-                Highgui.imwrite(Environment.getExternalStorageDirectory().getPath()
+                Imgcodecs.imwrite(Environment.getExternalStorageDirectory().getPath()
                         + "/RVCVRecData/DebugCV/img" + i + ".png", frame);
             }
         }
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/RVCVXCheckTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/RVCVXCheckTestActivity.java
index 35c4d56..e936cf3 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/RVCVXCheckTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/RVCVXCheckTestActivity.java
@@ -16,18 +16,33 @@
 
 package com.android.cts.verifier.sensors;
 
+import android.app.AlertDialog;
 import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.res.Resources;
 import android.hardware.cts.helpers.SensorTestStateNotSupportedException;
 import android.os.Bundle;
 import android.os.PowerManager;
+import android.text.method.LinkMovementMethod;
+import android.text.Html;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup;
+import android.view.ViewGroup.LayoutParams;
+import android.widget.Button;
+import android.widget.TextView;
 
+import com.android.compatibility.common.util.ReportLog;
+import com.android.compatibility.common.util.ResultType;
+import com.android.compatibility.common.util.ResultUnit;
+import com.android.cts.verifier.R;
 import com.android.cts.verifier.sensors.base.SensorCtsVerifierTestActivity;
 import com.android.cts.verifier.sensors.helpers.OpenCVLibrary;
 
 import junit.framework.Assert;
-
-import android.content.Intent;
-
+import java.io.IOException;
+import java.io.InputStream;
 import java.util.concurrent.CountDownLatch;
 
 /**
@@ -61,9 +76,9 @@
         public static final float pitch_rms_error = 0.15f;
         public static final float yaw_rms_error = 0.25f;
 
-        public static final float roll_max_error = 0.30f;
-        public static final float pitch_max_error = 0.30f;
-        public static final float yaw_max_error = 0.45f;
+        public static final float roll_max_error = 0.35f;
+        public static final float pitch_max_error = 0.35f;
+        public static final float yaw_max_error = 0.5f;
 
         public static final float sensor_period_stdev = 0.25e-3f;
     };
@@ -78,9 +93,13 @@
 
         mRecPath = "";
 
-        showUserMessage("Loading OpenCV Library...");
-        int retry = 10;
+        // By default it is a fail, until it is successful.
+        getReportLog().setSummary(
+                "Initialize failed", 0, ResultType.NEUTRAL, ResultUnit.NONE);
 
+        showUserMessage("Loading OpenCV Library...");
+
+        int retry = 10;
         while(retry-->0) {
             try {
                 Thread.sleep(250);
@@ -96,7 +115,8 @@
             clearText();
             return;
         }
-        showUserMessage("OpenCV Library Successfully Loaded");
+        showUserMessage("OpenCV Library Successfully Loaded.");
+        showOpenCVLibaryLicenseDisplayButton();
 
         mOpenCVLoadSuccessful = true;
 
@@ -111,11 +131,12 @@
                    "horizontal surface.\n" +
                 "2. Start the test and align the yellow square on the screen "+
                    "roughly to the yellow sqaure.\n" +
-                "3. Follow the prompt to rotate the phone while keeping the "+
-                   "entire test pattern inside view of camera. This requires " +
-                   "orbiting the phone around and aiming the "+
-                   "camera at the test pattern at the same time.\n" +
-                "4. Wait patiently for the analysis to finish.\n");
+                "3. Follow the prompt to orbit the phone around the test " +
+                   "pattern while aiming the field of view at the test pattern" +
+                   "at the same time.\n" +
+                "4. Wait patiently for the analysis to finish.");
+
+            showDetailedTutorialLink();
 
             waitForUserToContinue();
 
@@ -140,7 +161,10 @@
         }
 
 
-        if (mRecordSuccessful) {
+        if (!mRecordSuccessful) {
+            getReportLog().setSummary(
+                    "Record failed", 0, ResultType.NEUTRAL, ResultUnit.NONE);
+        } else {
             showUserMessage("Please wait for the analysis ... \n"+
                             "It may take a few minutes, you will be noted when "+
                             "its finished by sound and vibration. ");
@@ -164,8 +188,42 @@
                 showUserMessage("Analysis failed due to unknown reason!");
             } else {
                 if (mReport.error) {
+                    getReportLog().setSummary("Analysis failed: "+mReport.reason, 0,
+                            ResultType.NEUTRAL, ResultUnit.NONE);
+
                     showUserMessage("Analysis failed: " + mReport.reason);
                 } else {
+                    getReportLog().setSummary(
+                            "Analysis succeed", 1, ResultType.NEUTRAL, ResultUnit.NONE);
+
+                    getReportLog().addValue("Roll error RMS", mReport.roll_rms_error,
+                            ResultType.LOWER_BETTER, ResultUnit.RADIAN);
+                    getReportLog().addValue("Pitch error RMS", mReport.pitch_rms_error,
+                            ResultType.LOWER_BETTER, ResultUnit.RADIAN);
+                    getReportLog().addValue("Yaw error RMS", mReport.yaw_rms_error,
+                            ResultType.LOWER_BETTER, ResultUnit.RADIAN);
+
+                    getReportLog().addValue("Roll error MAX", mReport.roll_max_error,
+                            ResultType.LOWER_BETTER, ResultUnit.RADIAN);
+                    getReportLog().addValue("Pitch error MAX", mReport.pitch_max_error,
+                            ResultType.LOWER_BETTER, ResultUnit.RADIAN);
+                    getReportLog().addValue("Yaw error MAX", mReport.yaw_max_error,
+                            ResultType.LOWER_BETTER, ResultUnit.RADIAN);
+
+                    getReportLog().addValue("Number of frames", mReport.n_of_frame,
+                            ResultType.NEUTRAL, ResultUnit.COUNT);
+                    getReportLog().addValue("Number of valid frames", mReport.n_of_valid_frame,
+                            ResultType.NEUTRAL, ResultUnit.COUNT);
+
+                    getReportLog().addValue("Sensor period mean", mReport.sensor_period_avg*1000,
+                            ResultType.NEUTRAL, ResultUnit.MS);
+                    getReportLog().addValue("Sensor period stdev", mReport.sensor_period_stdev*1000,
+                            ResultType.NEUTRAL, ResultUnit.MS);
+                    getReportLog().addValue("Time offset", mReport.optimal_delta_t*1000,
+                            ResultType.NEUTRAL, ResultUnit.MS);
+                    getReportLog().addValue("Yaw offset", mReport.yaw_offset,
+                            ResultType.NEUTRAL, ResultUnit.RADIAN);
+
                     showUserMessage(String.format("Analysis finished!\n" +
                                     "Roll error (Rms, max) = %4.3f, %4.3f rad\n" +
                                     "Pitch error (Rms, max) = %4.3f, %4.3f rad\n" +
@@ -210,12 +268,15 @@
 
     /**
      * Test cases.
+     *
+     * Test cases is numbered to make sure they are executed in the right order.
      */
 
     public String test00OpenCV() throws Throwable {
 
         String message = "OpenCV is loaded";
-        Assert.assertTrue("OpenCV library cannot be loaded.", mOpenCVLoadSuccessful);
+        Assert.assertTrue("OpenCV library cannot be loaded. If OpenCV Manager is just installed, " +
+                          "please restart this test.", mOpenCVLoadSuccessful);
         return message;
     }
 
@@ -284,20 +345,6 @@
         return message;
     }
 
-    public String test4SensorPeriod() throws Throwable {
-
-        loadOpenCVSuccessfulOrSkip();
-        recordSuccessfulOrSkip();
-        analyzeSuccessfulOrSkip();
-
-        String message = "Test Sensor Period";
-
-        // we do not know what the maximum frequency can be, so just test the stdev value
-        Assert.assertEquals("Sensor sample period stdev.", 0.0, mReport.sensor_period_stdev,
-                Criterion.sensor_period_stdev);
-        return message;
-    }
-
     private void loadOpenCVSuccessfulOrSkip() throws SensorTestStateNotSupportedException {
         if (!mOpenCVLoadSuccessful)
             throw new SensorTestStateNotSupportedException("Skipped due to OpenCV cannot be loaded");
@@ -314,14 +361,66 @@
     }
 
     /*
-     *  This function serves as a proxy as showUserMessage is marked to be deprecated.
+     *  This function serves as a proxy as appendText is marked to be deprecated.
      *  When appendText is removed, this function will have a different implementation.
      *
      */
-    void showUserMessage(String s) {
+    private void showUserMessage(String s) {
         appendText(s);
     }
 
+    private void showDetailedTutorialLink() {
+        TextView textView = new TextView(this);
+        textView.setText(Html.fromHtml(
+                    "Detailed instructions can be found at " +
+                    "<A href=\"http://goo.gl/xTwB4d\">http://goo.gl/xTwB4d</a><br>"));
+        textView.setMovementMethod(LinkMovementMethod.getInstance());
+        textView.setPadding(10, 0, 0, 0);
+        getTestLogger().logCustomView(textView);
+    }
+
+    private void showOpenCVLibaryLicenseDisplayButton() {
+        Button btnLicense = new Button(this);
+        btnLicense.setText("View OpenCV Library BSD License Agreement");
+        // Avoid default all cap text on button.
+        btnLicense.setTransformationMethod(null);
+        btnLicense.setLayoutParams(new LayoutParams(
+                        ViewGroup.LayoutParams.WRAP_CONTENT,
+                                    ViewGroup.LayoutParams.WRAP_CONTENT));
+
+        // load
+        Resources res = getResources();
+        InputStream rawStream = res.openRawResource(R.raw.opencv_library_license);
+        byte[] byteArray;
+        try {
+            byteArray = new byte[rawStream.available()];
+            rawStream.read(byteArray);
+        } catch (IOException e) {
+            e.printStackTrace();
+            byteArray = "Unable to load license text.".getBytes();
+        }
+        final String licenseText = new String(byteArray);
+
+        btnLicense.setOnClickListener(new OnClickListener() {
+            public void onClick(View v) {
+                AlertDialog dialog =
+                    new AlertDialog.Builder(RVCVXCheckTestActivity.this)
+                        .setTitle("OpenCV Library BSD License")
+                        .setMessage(licenseText)
+                        .setPositiveButton("Acknowledged", new DialogInterface.OnClickListener() {
+                                public void onClick(DialogInterface dialog, int id) {
+                                    dialog.dismiss();
+                                }
+                            })
+                        .show();
+
+                TextView textView = (TextView) dialog.findViewById(android.R.id.message);
+                textView.setTextSize(9);
+            }
+        });
+        getTestLogger().logCustomView(btnLicense);
+    }
+
     @Override
     protected void onCreate(Bundle savedInstanceState) {
 
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/base/BaseSensorTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/base/BaseSensorTestActivity.java
index 0bf9636..c18ccd6 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/base/BaseSensorTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/base/BaseSensorTestActivity.java
@@ -24,7 +24,9 @@
 
 import junit.framework.Assert;
 
-import android.app.Activity;
+//import android.app.Activity;
+import com.android.cts.verifier.PassFailButtons;
+
 import android.content.Context;
 import android.content.Intent;
 import android.hardware.cts.helpers.ActivityResultMultiplexedLatch;
@@ -70,7 +72,7 @@
  *                                            tests that require operator interaction
  */
 public abstract class BaseSensorTestActivity
-        extends Activity
+        extends PassFailButtons.Activity
         implements View.OnClickListener, Runnable, ISensorTestStateContainer {
     @Deprecated
     protected static final String LOG_TAG = "SensorTest";
@@ -538,6 +540,10 @@
 
         private final StringBuilder mOverallSummaryBuilder = new StringBuilder("\n");
 
+        public void logCustomView(View view) {
+            new ViewAppender(view).append();
+        }
+
         void logTestStart(String testName) {
             // TODO: log the sensor information and expected execution time of each test
             TextAppender textAppender = new TextAppender(R.layout.snsr_test_title);
@@ -645,26 +651,18 @@
         }
     }
 
-    private class TextAppender {
-        private final TextView mTextView;
+    private class ViewAppender {
+        protected final View mView;
 
-        public TextAppender(int textViewResId) {
-            mTextView = (TextView) getLayoutInflater().inflate(textViewResId, null /* viewGroup */);
-        }
-
-        public void setText(String text) {
-            mTextView.setText(text);
-        }
-
-        public void setText(int textResId) {
-            mTextView.setText(textResId);
+        public ViewAppender(View view) {
+            mView = view;
         }
 
         public void append() {
             runOnUiThread(new Runnable() {
                 @Override
                 public void run() {
-                    mLogLayout.addView(mTextView);
+                    mLogLayout.addView(mView);
                     mLogScrollView.post(new Runnable() {
                         @Override
                         public void run() {
@@ -675,4 +673,21 @@
             });
         }
     }
+
+    private class TextAppender extends ViewAppender{
+        private final TextView mTextView;
+
+        public TextAppender(int textViewResId) {
+            super(getLayoutInflater().inflate(textViewResId, null /* viewGroup */));
+            mTextView = (TextView) mView;
+        }
+
+        public void setText(String text) {
+            mTextView.setText(text);
+        }
+
+        public void setText(int textResId) {
+            mTextView.setText(textResId);
+        }
+    }
 }
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/helpers/OpenCVLibrary.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/helpers/OpenCVLibrary.java
index 2f5c873..bb8db34 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/helpers/OpenCVLibrary.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/helpers/OpenCVLibrary.java
@@ -42,7 +42,7 @@
         if (isLoaded())  return;
 
         // Load the library through loader
-        OpenCVLoader.initAsync(OpenCVLoader.OPENCV_VERSION_2_4_9, context,
+        OpenCVLoader.initAsync(OpenCVLoader.OPENCV_VERSION_3_0_0, context,
                 new BaseLoaderCallback(context) {
                     @Override
                     public void onManagerConnected(int status) {
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/DynamicConfigDeviceSide.java b/common/device-side/util/src/com/android/compatibility/common/util/DynamicConfigDeviceSide.java
index 303efec..b50b4f7 100644
--- a/common/device-side/util/src/com/android/compatibility/common/util/DynamicConfigDeviceSide.java
+++ b/common/device-side/util/src/com/android/compatibility/common/util/DynamicConfigDeviceSide.java
@@ -28,6 +28,7 @@
  * Load dynamic config for device side test cases
  */
 public class DynamicConfigDeviceSide extends DynamicConfig {
+
     private static String LOG_TAG = DynamicConfigDeviceSide.class.getSimpleName();
 
     public DynamicConfigDeviceSide(String moduleName) throws XmlPullParserException, IOException {
@@ -35,6 +36,6 @@
             throw new IOException("External storage is not mounted");
         }
         File configFile = getConfigFile(new File(CONFIG_FOLDER_ON_DEVICE), moduleName);
-        initConfigFromXml(configFile);
+        initializeConfig(configFile);
     }
 }
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 f2ce00b..e21f98e 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
@@ -30,6 +30,7 @@
 public class CompatibilityBuildHelper {
 
     private static final String ROOT_DIR = "ROOT_DIR";
+    private static final String ROOT_DIR2 = "ROOT_DIR2";
     private static final String SUITE_BUILD = "SUITE_BUILD";
     private static final String SUITE_NAME = "SUITE_NAME";
     private static final String SUITE_FULL_NAME = "SUITE_FULL_NAME";
@@ -129,7 +130,16 @@
      * @throws FileNotFoundException if the directory does not exist
      */
     public File getRootDir() throws FileNotFoundException {
-        File dir = new File(mBuildInfo.getBuildAttributes().get(ROOT_DIR));
+        File dir = null;
+        if (mBuildInfo instanceof IFolderBuildInfo) {
+            dir = ((IFolderBuildInfo) mBuildInfo).getRootDir();
+        }
+        if (dir == null || !dir.exists()) {
+            dir = new File(mBuildInfo.getBuildAttributes().get(ROOT_DIR));
+            if (!dir.exists()) {
+                dir = new File(mBuildInfo.getBuildAttributes().get(ROOT_DIR2));
+            }
+        }
         if (!dir.exists()) {
             throw new FileNotFoundException(String.format(
                     "Compatibility root directory %s does not exist",
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 8c455cd..fabc4e1 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
@@ -88,11 +88,11 @@
         }
 
         String apfeConfigInJson = null;
-        String OriginUrl = buildHelper.getDynamicConfigUrl();
+        String originUrl = buildHelper.getDynamicConfigUrl();
 
-        if (OriginUrl != null) {
+        if (originUrl != null) {
             try {
-                String requestUrl = OriginUrl
+                String requestUrl = originUrl
                         .replace("{module-name}", mModuleName).replace("{version-name}", mVersion);
                 java.net.URL request = new URL(requestUrl);
                 apfeConfigInJson = StreamUtil.getStringFromStream(request.openStream());
@@ -128,7 +128,9 @@
 
             case HOST:
                 File storageDir = new File(DynamicConfig.CONFIG_FOLDER_ON_HOST);
-                if (!storageDir.exists()) storageDir.mkdir();
+                if (!storageDir.exists()) {
+                    storageDir.mkdir();
+                }
                 File hostDest = new File(DynamicConfig.CONFIG_FOLDER_ON_HOST + src.getName());
                 try {
                     FileUtil.copyFile(src, hostDest);
@@ -159,4 +161,4 @@
                 new File(mFilePushed).delete();
         }
     }
-}
\ No newline at end of file
+}
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 b42faca..bedcecd 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
@@ -37,90 +37,66 @@
 
     private static final String LOG_TAG = DynamicConfigHandler.class.getSimpleName();
 
-    // xml constant
-    private static final String NS = null; //representing null namespace
+    private static final String NS = null; //xml constant representing null namespace
     private static final String ENCODING = "UTF-8";
 
-    public static File getMergedDynamicConfigFile(File localConfigFile, String apfeConfigJson,
+    public static File getMergedDynamicConfigFile(File localConfigFile, String apbsConfigJson,
             String moduleName) throws IOException, XmlPullParserException, JSONException {
 
-        DynamicConfig.Params localConfig = DynamicConfig.genParamsFromFile(localConfigFile);
-        DynamicConfig.Params apfeOverride = parseJsonToParam(apfeConfigJson);
-
-        localConfig.mDynamicParams.putAll(apfeOverride.mDynamicParams);
-        localConfig.mDynamicArrayParams.putAll(apfeOverride.mDynamicArrayParams);
-
-        File mergedConfigFile = storeMergedConfigFile(localConfig, moduleName);
-        return mergedConfigFile;
+        Map<String, List<String>> localConfig = DynamicConfig.createConfigMap(localConfigFile);
+        Map<String, List<String>> apbsConfig = parseJsonToConfigMap(apbsConfigJson);
+        localConfig.putAll(apbsConfig);
+        return storeMergedConfigFile(localConfig, moduleName);
     }
 
-    private static DynamicConfig.Params parseJsonToParam(String apfeConfigJson)
+    private static Map<String, List<String>> parseJsonToConfigMap(String apbsConfigJson)
             throws JSONException {
-        if (apfeConfigJson == null) return new DynamicConfig.Params();
 
-        Map<String, String> configMap = new HashMap<>();
-        Map<String, List<String>> configListMap = new HashMap<>();
-
-        JSONObject rootObj  = new JSONObject(new JSONTokener(apfeConfigJson));
-        if (rootObj.has("config")) {
-            JSONArray configArr = rootObj.getJSONArray("config");
-            for (int i = 0; i < configArr.length(); i++) {
-                JSONObject config = configArr.getJSONObject(i);
-                configMap.put(config.getString("key"), config.getString("value"));
-            }
-        }
-        if (rootObj.has("configList")) {
-            JSONArray configListArr = rootObj.getJSONArray("configList");
-            for (int i = 0; i < configListArr.length(); i++) {
-                JSONObject configList = configListArr.getJSONObject(i);
-                String key = configList.getString("key");
-                List<String> values = new ArrayList<>();
-                JSONArray configListValuesArr = configList.getJSONArray("value");
-                for (int j = 0; j < configListValuesArr.length(); j++) {
-                    values.add(configListValuesArr.getString(j));
-                }
-                configListMap.put(key, values);
-            }
+        Map<String, List<String>> configMap = new HashMap<String, List<String>>();
+        if (apbsConfigJson == null) {
+            return configMap;
         }
 
-        DynamicConfig.Params param = new DynamicConfig.Params();
-        param.mDynamicParams = configMap;
-        param.mDynamicArrayParams = configListMap;
-        return param;
+        JSONObject rootObj  = new JSONObject(new JSONTokener(apbsConfigJson));
+        JSONArray configEntries = rootObj.getJSONArray("dynamicConfigEntries");
+        for (int i = 0; i < configEntries.length(); i++) {
+            JSONObject configEntry = configEntries.getJSONObject(i);
+            String key = configEntry.getString("configAttribute");
+            JSONArray configValues = configEntry.getJSONArray("configValues");
+            List<String> values = new ArrayList<>();
+            for (int j = 0; j < configValues.length(); j++) {
+                values.add(configValues.getString(j));
+            }
+            configMap.put(key, values);
+        }
+        return configMap;
     }
 
-    private static File storeMergedConfigFile(DynamicConfig.Params p, String moduleName)
-            throws XmlPullParserException, IOException {
-        XmlSerializer serializer = XmlPullParserFactory.newInstance().newSerializer();
+    private static File storeMergedConfigFile(Map<String, List<String>> configMap,
+            String moduleName) throws XmlPullParserException, IOException {
 
-        File parentFolder = new File(DynamicConfig.CONFIG_FOLDER_ON_HOST);
-        if (!parentFolder.exists()) parentFolder.mkdir();
         File folder = new File(DynamicConfig.MERGED_CONFIG_FILE_FOLDER);
-        if (!folder.exists()) folder.mkdir();
-        File mergedConfigFile = new File(folder, moduleName+".dynamic");
+        folder.mkdirs();
+
+        File mergedConfigFile = new File(folder, moduleName + ".dynamic");
         OutputStream stream = new FileOutputStream(mergedConfigFile);
+        XmlSerializer serializer = XmlPullParserFactory.newInstance().newSerializer();
         serializer.setOutput(stream, ENCODING);
         serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
         serializer.startDocument(ENCODING, false);
 
-        serializer.startTag(NS, DynamicConfig.DYNAMIC_CONFIG_TAG);
-        for (String key : p.mDynamicParams.keySet()) {
-            serializer.startTag(NS, DynamicConfig.CONFIG_TAG);
+        serializer.startTag(NS, DynamicConfig.CONFIG_TAG);
+        for (String key : configMap.keySet()) {
+            serializer.startTag(NS, DynamicConfig.ENTRY_TAG);
             serializer.attribute(NS, DynamicConfig.KEY_ATTR, key);
-            serializer.text(p.mDynamicParams.get(key));
-            serializer.endTag(NS, DynamicConfig.CONFIG_TAG);
-        }
-        for (String key : p.mDynamicArrayParams.keySet()) {
-            serializer.startTag(NS, DynamicConfig.CONFIG_LIST_TAG);
-            serializer.attribute(NS, DynamicConfig.KEY_ATTR, key);
-            for (String item: p.mDynamicArrayParams.get(key)) {
-                serializer.startTag(NS, DynamicConfig.ITEM_TAG);
-                serializer.text(item);
-                serializer.endTag(NS, DynamicConfig.ITEM_TAG);
+            for (String value : configMap.get(key)) {
+                serializer.startTag(NS, DynamicConfig.VALUE_TAG);
+                serializer.text(value);
+                serializer.endTag(NS, DynamicConfig.VALUE_TAG);
             }
-            serializer.endTag(NS, DynamicConfig.CONFIG_LIST_TAG);
+            serializer.endTag(NS, DynamicConfig.ENTRY_TAG);
         }
-        serializer.endTag(NS, DynamicConfig.DYNAMIC_CONFIG_TAG);
+        serializer.endTag(NS, DynamicConfig.CONFIG_TAG);
         serializer.endDocument();
         return mergedConfigFile;
     }
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 ac69034..3e94d36 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
@@ -25,10 +25,11 @@
  * Load dynamic config for device side test cases
  */
 public class DynamicConfigHostSide extends DynamicConfig {
+
     private static String LOG_TAG = DynamicConfigHostSide.class.getSimpleName();
 
     public DynamicConfigHostSide(String moduleName) throws IOException, XmlPullParserException {
         File configFile = getConfigFile(new File(CONFIG_FOLDER_ON_HOST), moduleName);
-        initConfigFromXml(configFile);
+        initializeConfig(configFile);
     }
 }
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 e2001fc..ad0ad9f 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
@@ -22,6 +22,9 @@
 import java.io.FileOutputStream;
 import java.io.IOException;
 
+import java.util.List;
+import java.util.Map;
+
 /**
  * Unit tests for {@link DynamicConfigHandler}
  */
@@ -29,63 +32,76 @@
 
     private static final String localConfig =
             "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" +
-            "<DynamicConfig>\n" +
-            "    <Config key=\"test-config-1\">test config 1</Config>\n" +
-            "    <Config key=\"test-config-2\">test config 2</Config>\n" +
-            "    <Config key=\"override-config-2\">test config 3</Config>\n" +
-            "    <ConfigList key=\"config-list\">\n" +
-            "        <Item>config0</Item>\n" +
-            "        <Item>config1</Item>\n" +
-            "        <Item>config2</Item>\n" +
-            "        <Item>config3</Item>\n" +
-            "        <Item>config4</Item>\n" +
-            "    </ConfigList>\n" +
-            "    <ConfigList key=\"override-config-list-2\">\n" +
-            "        <Item>A</Item>\n" +
-            "        <Item>B</Item>\n" +
-            "        <Item>C</Item>\n" +
-            "        <Item>D</Item>\n" +
-            "        <Item>E</Item>\n" +
-            "    </ConfigList>\n" +
-            "</DynamicConfig>\n";
+            "<dynamicConfig>\n" +
+            "    <entry key=\"test-config-1\">\n" +
+            "        <value>test config 1</value>\n" +
+            "    </entry>\n" +
+            "    <entry key=\"test-config-2\">\n" +
+            "        <value>test config 2</value>\n" +
+            "    </entry>\n" +
+            "    <entry key=\"override-config-2\">\n" +
+            "        <value>test config 3</value>\n" +
+            "    </entry>\n" +
+            "    <entry key=\"config-list\">\n" +
+            "        <value>config0</value>\n" +
+            "        <value>config1</value>\n" +
+            "        <value>config2</value>\n" +
+            "        <value>config3</value>\n" +
+            "        <value>config4</value>\n" +
+            "    </entry>\n" +
+            "    <entry key=\"override-config-list-2\">\n" +
+            "        <value>A</value>\n" +
+            "        <value>B</value>\n" +
+            "        <value>C</value>\n" +
+            "        <value>D</value>\n" +
+            "        <value>E</value>\n" +
+            "    </entry>\n" +
+            "</dynamicConfig>\n";
 
     private static final String overrideJson =
             "{\n" +
-            "  \"config\": [\n" +
+            "  \"dynamicConfigEntries\": [\n" +
             "    {\n" +
-            "      \"key\": \"version\",\n" +
-            "      \"value\": \"1.0\"\n" +
+            "      \"configAttribute\": \"version\",\n" +
+            "      \"configValues\": [\n" +
+            "        \"1.0\"\n" +
+            "      ]\n" +
             "    },\n" +
             "    {\n" +
-            "      \"key\": \"suite\",\n" +
-            "      \"value\": \"CTS_V2\"\n" +
+            "      \"configAttribute\": \"suite\",\n" +
+            "      \"configValues\": [\n" +
+            "        \"CTS_V2\"\n" +
+            "      ]\n" +
             "    },\n" +
             "    {\n" +
-            "      \"key\": \"override-config-1\",\n" +
-            "      \"value\": \"override-config-val-1\"\n" +
+            "      \"configAttribute\": \"override-config-1\",\n" +
+            "      \"configValues\": [\n" +
+            "        \"override-config-val-1\"\n" +
+            "      ]\n" +
             "    },\n" +
             "    {\n" +
-            "      \"key\": \"override-config-2\",\n" +
-            "      \"value\": \"override-config-val-2\"\n" +
-            "    }\n" +
-            "  ],\n" +
-            "  \"configList\": [\n" +
+            "      \"configAttribute\": \"override-config-2\",\n" +
+            "      \"configValues\": [\n" +
+            "        \"override-config-val-2\"\n" +
+            "      ]\n" +
+            "    },\n" +
             "    {\n" +
-            "      \"key\": \"override-config-list-1\",\n" +
-            "      \"value\": [\n" +
+            "      \"configAttribute\": \"override-config-list-1\",\n" +
+            "      \"configValues\": [\n" +
             "        \"override-config-list-val-1-1\",\n" +
             "        \"override-config-list-val-1-2\"\n" +
             "      ]\n" +
             "    },\n" +
             "    {\n" +
-            "      \"key\": \"override-config-list-2\",\n" +
-            "      \"value\": [\n" +
+            "      \"configAttribute\": \"override-config-list-2\",\n" +
+            "      \"configValues\": [\n" +
             "        \"override-config-list-val-2-1\"\n" +
             "      ]\n" +
             "    },\n" +
             "    {\n" +
-            "      \"key\": \"override-config-list-3\",\n" +
-            "      \"value\": []\n" +
+            "      \"configAttribute\": \"override-config-list-3\",\n" +
+            "      \"configValues\": [\n" +
+            "      ]\n" +
             "    }\n" +
             "  ]\n" +
             "}";
@@ -97,21 +113,21 @@
         File mergedFile = DynamicConfigHandler
                 .getMergedDynamicConfigFile(localConfigFile, overrideJson, module);
 
-        DynamicConfig.Params params = DynamicConfig.genParamsFromFile(mergedFile);
+        Map<String, List<String>> configMap = DynamicConfig.createConfigMap(mergedFile);
 
-        assertEquals("override-config-val-1", params.mDynamicParams.get("override-config-1"));
-        assertTrue(params.mDynamicArrayParams.get("override-config-list-1")
+        assertEquals("override-config-val-1", configMap.get("override-config-1").get(0));
+        assertTrue(configMap.get("override-config-list-1")
                 .contains("override-config-list-val-1-1"));
-        assertTrue(params.mDynamicArrayParams.get("override-config-list-1")
+        assertTrue(configMap.get("override-config-list-1")
                 .contains("override-config-list-val-1-2"));
-        assertTrue(params.mDynamicArrayParams.get("override-config-list-3").size() == 0);
+        assertTrue(configMap.get("override-config-list-3").size() == 0);
 
-        assertEquals("test config 1", params.mDynamicParams.get("test-config-1"));
-        assertTrue(params.mDynamicArrayParams.get("config-list").contains("config2"));
+        assertEquals("test config 1", configMap.get("test-config-1").get(0));
+        assertTrue(configMap.get("config-list").contains("config2"));
 
-        assertEquals("override-config-val-2", params.mDynamicParams.get("override-config-2"));
-        assertEquals(1, params.mDynamicArrayParams.get("override-config-list-2").size());
-        assertTrue(params.mDynamicArrayParams.get("override-config-list-2")
+        assertEquals("override-config-val-2", configMap.get("override-config-2").get(0));
+        assertEquals(1, configMap.get("override-config-list-2").size());
+        assertTrue(configMap.get("override-config-list-2")
                 .contains("override-config-list-val-2-1"));
     }
 
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 a0b1d5e..18c5d26 100644
--- a/common/util/src/com/android/compatibility/common/util/DynamicConfig.java
+++ b/common/util/src/com/android/compatibility/common/util/DynamicConfig.java
@@ -34,14 +34,14 @@
  * Load dynamic config for test cases
  */
 public class DynamicConfig {
+
     public final static String MODULE_NAME = "module-name";
 
     //XML constant
     public static final String NS = null;
-    public static final String DYNAMIC_CONFIG_TAG = "DynamicConfig";
-    public static final String CONFIG_TAG = "Config";
-    public static final String CONFIG_LIST_TAG = "ConfigList";
-    public static final String ITEM_TAG = "Item";
+    public static final String CONFIG_TAG = "dynamicConfig";
+    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/";
@@ -50,19 +50,18 @@
     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>>();
 
-    protected Params params;
-
-    protected void initConfigFromXml(File file) throws XmlPullParserException, IOException {
-        params = genParamsFromFile(file);
+    protected void initializeConfig(File file) throws XmlPullParserException, IOException {
+        mDynamicConfigMap = createConfigMap(file);
     }
 
-    public String getConfig(String key) {
-        return params.mDynamicParams.get(key);
+    public String getValue(String key) {
+        return mDynamicConfigMap.get(key).get(0);
     }
 
-    public List<String> getConfigList(String key) {
-        return params.mDynamicArrayParams.get(key);
+    public List<String> getValues(String key) {
+        return mDynamicConfigMap.get(key);
     }
 
     public static File getConfigFile(File configFolder, String moduleName)
@@ -74,44 +73,31 @@
         return config;
     }
 
-    public static class Params {
-        public Map<String, String> mDynamicParams = new HashMap<>();
-        public Map<String, List<String>> mDynamicArrayParams = new HashMap<>();
-    }
+    public static Map<String, List<String>> createConfigMap(File file)
+            throws XmlPullParserException, IOException {
 
-    public static Params genParamsFromFile(File file) throws XmlPullParserException, IOException {
-        Params param = new Params();
-
+        Map<String, List<String>> dynamicConfigMap = new HashMap<String, List<String>>();
         XmlPullParser parser = XmlPullParserFactory.newInstance().newPullParser();
         parser.setInput(new InputStreamReader(new FileInputStream(file)));
-
         parser.nextTag();
-        parser.require(XmlPullParser.START_TAG, NS, DYNAMIC_CONFIG_TAG);
+        parser.require(XmlPullParser.START_TAG, NS, CONFIG_TAG);
 
         while (parser.nextTag() == XmlPullParser.START_TAG) {
-            if (parser.getName().equals(CONFIG_TAG)) {
-                String key = parser.getAttributeValue(NS, KEY_ATTR);
-                String value = parser.nextText();
-                parser.require(XmlPullParser.END_TAG, NS, CONFIG_TAG);
-                if (key != null && !key.isEmpty()) {
-                    param.mDynamicParams.put(key, value);
-                }
-            } else {
-                List<String> arrayValue = new ArrayList<>();
-                parser.require(XmlPullParser.START_TAG, NS, CONFIG_LIST_TAG);
-                String key = parser.getAttributeValue(NS, KEY_ATTR);
-                while (parser.nextTag() == XmlPullParser.START_TAG) {
-                    parser.require(XmlPullParser.START_TAG, NS, ITEM_TAG);
-                    arrayValue.add(parser.nextText());
-                    parser.require(XmlPullParser.END_TAG, NS, ITEM_TAG);
-                }
-                parser.require(XmlPullParser.END_TAG, NS, CONFIG_LIST_TAG);
-                if (key != null && !key.isEmpty()) {
-                    param.mDynamicArrayParams.put(key, arrayValue);
-                }
+            parser.require(XmlPullParser.START_TAG, NS, ENTRY_TAG);
+            String key = parser.getAttributeValue(NS, KEY_ATTR);
+            List<String> valueList = new ArrayList<String>();
+            while (parser.nextTag() == XmlPullParser.START_TAG) {
+                parser.require(XmlPullParser.START_TAG, NS, VALUE_TAG);
+                valueList.add(parser.nextText());
+                parser.require(XmlPullParser.END_TAG, NS, VALUE_TAG);
+            }
+            parser.require(XmlPullParser.END_TAG, NS, ENTRY_TAG);
+            if (key != null && !key.isEmpty()) {
+                dynamicConfigMap.put(key, valueList);
             }
         }
-        parser.require(XmlPullParser.END_TAG, NS, DYNAMIC_CONFIG_TAG);
-        return param;
+
+        parser.require(XmlPullParser.END_TAG, NS, CONFIG_TAG);
+        return dynamicConfigMap;
     }
 }
diff --git a/common/util/src/com/android/compatibility/common/util/ResultUnit.java b/common/util/src/com/android/compatibility/common/util/ResultUnit.java
index 5b082f9..31aecbb 100644
--- a/common/util/src/com/android/compatibility/common/util/ResultUnit.java
+++ b/common/util/src/com/android/compatibility/common/util/ResultUnit.java
@@ -37,7 +37,9 @@
     /** tell how many times it did happen. */
     COUNT,
     /** unit for benchmarking with generic score. */
-    SCORE;
+    SCORE,
+    /** radian */
+    RADIAN;
 
     /**
      * @return a string to be used in the report.
diff --git a/common/util/tests/src/com/android/compatibility/common/util/DynamicConfigTest.java b/common/util/tests/src/com/android/compatibility/common/util/DynamicConfigTest.java
index 6d6ba2d..cf1892a 100644
--- a/common/util/tests/src/com/android/compatibility/common/util/DynamicConfigTest.java
+++ b/common/util/tests/src/com/android/compatibility/common/util/DynamicConfigTest.java
@@ -30,56 +30,64 @@
 public class DynamicConfigTest extends TestCase {
     private static final String correctConfig =
             "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" +
-            "<DynamicConfig>\n" +
-            "    <Config key=\"test-config-1\">test config 1</Config>\n" +
-            "    <Config key=\"test-config-2\">testconfig2</Config>\n" +
-            "    <ConfigList key=\"config-list\">\n" +
-            "        <Item>config0</Item>\n" +
-            "        <Item>config1</Item>\n" +
-            "        <Item>config2</Item>\n" +
-            "        <Item>config3</Item>\n" +
-            "        <Item>config4</Item>\n" +
-            "    </ConfigList>\n" +
-            "    <ConfigList key=\"config-list-2\">\n" +
-            "        <Item>A</Item>\n" +
-            "        <Item>B</Item>\n" +
-            "        <Item>C</Item>\n" +
-            "        <Item>D</Item>\n" +
-            "        <Item>E</Item>\n" +
-            "    </ConfigList>\n" +
-            "</DynamicConfig>\n";
+            "<dynamicConfig>\n" +
+            "    <entry key=\"test-config-1\">\n" +
+            "        <value>test config 1</value>\n" +
+            "    </entry>\n" +
+            "    <entry key=\"test-config-2\">\n" +
+            "        <value>testconfig2</value>\n" +
+            "    </entry>\n" +
+            "    <entry key=\"config-list\">\n" +
+            "        <value>config0</value>\n" +
+            "        <value>config1</value>\n" +
+            "        <value>config2</value>\n" +
+            "        <value>config3</value>\n" +
+            "        <value>config4</value>\n" +
+            "    </entry>\n" +
+            "    <entry key=\"config-list-2\">\n" +
+            "        <value>A</value>\n" +
+            "        <value>B</value>\n" +
+            "        <value>C</value>\n" +
+            "        <value>D</value>\n" +
+            "        <value>E</value>\n" +
+            "    </entry>\n" +
+            "</dynamicConfig>\n";
 
     private static final String configWrongNodeName =
             "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" +
-            "<DynamicCsonfig>\n" +  //The node name DynamicConfig is intentionally mistyped
-            "    <Config key=\"test-config-1\">test config 1</Config>\n" +
-            "    <Config key=\"test-config-2\">testconfig2</Config>\n" +
-            "    <ConfigList key=\"config-list\">\n" +
-            "        <Item>Nevermore</Item>\n" +
-            "        <Item>Puck</Item>\n" +
-            "        <Item>Zeus</Item>\n" +
-            "        <Item>Earth Shaker</Item>\n" +
-            "        <Item>Vengeful Spirit</Item>\n" +
-            "    </ConfigList>\n" +
-            "    <ConfigList key=\"config-list-2\">\n" +
-            "        <Item>A</Item>\n" +
-            "        <Item>B</Item>\n" +
-            "        <Item>C</Item>\n" +
-            "        <Item>D</Item>\n" +
-            "        <Item>E</Item>\n" +
-            "    </ConfigList>\n" +
-            "</DynamicConfig>\n";
+            "<dynamicCsonfig>\n" +  //The node name dynamicConfig is intentionally mistyped
+            "    <entry key=\"test-config-1\">\n" +
+            "        <value>test config 1</value>\n" +
+            "    </entry>\n" +
+            "    <entry key=\"test-config-2\">\n" +
+            "        <value>testconfig2</value>\n" +
+            "    </entry>\n" +
+            "    <entry key=\"config-list\">\n" +
+            "        <value>Nevermore</value>\n" +
+            "        <value>Puck</value>\n" +
+            "        <value>Zeus</value>\n" +
+            "        <value>Earth Shaker</value>\n" +
+            "        <value>Vengeful Spirit</value>\n" +
+            "    </entry>\n" +
+            "    <entry key=\"config-list-2\">\n" +
+            "        <value>A</value>\n" +
+            "        <value>B</value>\n" +
+            "        <value>C</value>\n" +
+            "        <value>D</value>\n" +
+            "        <value>E</value>\n" +
+            "    </entry>\n" +
+            "</dynamicConfig>\n";
 
     public void testCorrectConfig() throws Exception {
         DynamicConfig config = new DynamicConfig();
         File file = createFileFromStr(correctConfig);
-        config.initConfigFromXml(file);
+        config.initializeConfig(file);
 
-        assertEquals("Wrong Config", config.getConfig("test-config-1"), "test config 1");
-        assertEquals("Wrong Config", config.getConfig("test-config-2"), "testconfig2");
-        assertEquals("Wrong Config List", config.getConfigList("config-list").get(0), "config0");
-        assertEquals("Wrong Config List", config.getConfigList("config-list").get(2), "config2");
-        assertEquals("Wrong Config List", config.getConfigList("config-list-2").get(2), "C");
+        assertEquals("Wrong Config", config.getValue("test-config-1"), "test config 1");
+        assertEquals("Wrong Config", config.getValue("test-config-2"), "testconfig2");
+        assertEquals("Wrong Config List", config.getValues("config-list").get(0), "config0");
+        assertEquals("Wrong Config List", config.getValues("config-list").get(2), "config2");
+        assertEquals("Wrong Config List", config.getValues("config-list-2").get(2), "C");
     }
 
     public void testConfigWithWrongNodeName() throws Exception {
@@ -87,7 +95,7 @@
         File file = createFileFromStr(configWrongNodeName);
 
         try {
-            config.initConfigFromXml(file);
+            config.initializeConfig(file);
             fail("Cannot detect error when config file has wrong node name");
         } catch (XmlPullParserException e) {
             //expected
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/DocumentsTest.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/DocumentsTest.java
index 300aa3a..0451060 100644
--- a/hostsidetests/appsecurity/src/android/appsecurity/cts/DocumentsTest.java
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/DocumentsTest.java
@@ -88,6 +88,28 @@
         runDeviceTests(CLIENT_PKG, ".DocumentsClientTest", "testTree");
     }
 
+    public void testOpenExternalDirectory_invalidPath() throws Exception {
+        runDeviceTests(CLIENT_PKG, ".DocumentsClientTest", "testOpenExternalDirectory_invalidPath");
+    }
+
+    public void testOpenExternalDirectory_userRejects() throws Exception {
+        runDeviceTests(CLIENT_PKG, ".DocumentsClientTest", "testOpenExternalDirectory_userRejects");
+    }
+
+    public void testOpenExternalDirectory_userAccepts() throws Exception {
+        runDeviceTests(CLIENT_PKG, ".DocumentsClientTest", "testOpenExternalDirectory_userAccepts");
+    }
+
+    public void testOpenExternalDirectory_userAcceptsNewDirectory() throws Exception {
+        // TODO: figure out a better way to remove the directory.
+        final String command = "rm -rf /sdcard/Pictures";
+        final String output = getDevice().executeShellCommand(command);
+        if (!output.trim().isEmpty()) {
+            fail("Command '" + command + "' failed: '" + output + "'");
+        }
+        runDeviceTests(CLIENT_PKG, ".DocumentsClientTest", "testOpenExternalDirectory_userAccepts");
+    }
+
     public void testGetContent() throws Exception {
         runDeviceTests(CLIENT_PKG, ".DocumentsClientTest", "testGetContent");
     }
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 c2c4345..bfb96c1 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
@@ -16,21 +16,46 @@
 
 package com.android.cts.documentclient;
 
+import static android.os.Environment.DIRECTORY_ALARMS;
+import static android.os.Environment.DIRECTORY_DCIM;
+import static android.os.Environment.DIRECTORY_DOCUMENTS;
+import static android.os.Environment.DIRECTORY_DOWNLOADS;
+import static android.os.Environment.DIRECTORY_MOVIES;
+import static android.os.Environment.DIRECTORY_MUSIC;
+import static android.os.Environment.DIRECTORY_NOTIFICATIONS;
+import static android.os.Environment.DIRECTORY_PICTURES;
+import static android.os.Environment.DIRECTORY_PODCASTS;
+import static android.os.Environment.DIRECTORY_RINGTONES;
+import static android.os.Environment.getExternalStorageDirectory;
+import static android.test.MoreAsserts.assertContainsRegex;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import android.app.Activity;
 import android.content.ContentResolver;
+import android.content.Context;
 import android.content.Intent;
 import android.content.pm.PackageManager;
 import android.database.Cursor;
 import android.net.Uri;
+import android.os.Environment;
 import android.os.SystemClock;
 import android.provider.DocumentsContract;
 import android.provider.DocumentsContract.Document;
 import android.provider.DocumentsProvider;
+import android.support.test.uiautomator.By;
 import android.support.test.uiautomator.Configurator;
 import android.support.test.uiautomator.UiDevice;
 import android.support.test.uiautomator.UiObject;
 import android.support.test.uiautomator.UiObjectNotFoundException;
 import android.support.test.uiautomator.UiScrollable;
 import android.support.test.uiautomator.UiSelector;
+import android.support.test.uiautomator.Until;
 import android.test.InstrumentationTestCase;
 import android.test.MoreAsserts;
 import android.text.format.DateUtils;
@@ -39,12 +64,6 @@
 
 import com.android.cts.documentclient.MyActivity.Result;
 
-import java.io.ByteArrayOutputStream;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-
 /**
  * Tests for {@link DocumentsProvider} and interaction with platform intents
  * like {@link Intent#ACTION_OPEN_DOCUMENT}.
@@ -57,6 +76,21 @@
 
     private static final long TIMEOUT = 30 * DateUtils.SECOND_IN_MILLIS;
 
+    private static final int REQUEST_CODE = 42;
+
+    private static final String[] STANDARD_DIRECTORIES = {
+        DIRECTORY_MUSIC,
+        DIRECTORY_PODCASTS,
+        DIRECTORY_RINGTONES,
+        DIRECTORY_ALARMS,
+        DIRECTORY_NOTIFICATIONS,
+        DIRECTORY_PICTURES,
+        DIRECTORY_MOVIES,
+        DIRECTORY_DOWNLOADS,
+        DIRECTORY_DCIM,
+        DIRECTORY_DOCUMENTS
+    };
+
     @Override
     public void setUp() throws Exception {
         super.setUp();
@@ -130,7 +164,7 @@
         final Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
         intent.addCategory(Intent.CATEGORY_OPENABLE);
         intent.setType("*/*");
-        mActivity.startActivityForResult(intent, 42);
+        mActivity.startActivityForResult(intent, REQUEST_CODE);
 
         // Ensure that we see both of our roots
         mDevice.waitForIdle();
@@ -170,7 +204,7 @@
 
         final Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
         intent.setType("*/*");
-        mActivity.startActivityForResult(intent, 42);
+        mActivity.startActivityForResult(intent, REQUEST_CODE);
 
         // Pick a virtual file from the local root.
         mDevice.waitForIdle();
@@ -195,7 +229,7 @@
         intent.addCategory(Intent.CATEGORY_OPENABLE);
         intent.putExtra(Intent.EXTRA_TITLE, DISPLAY_NAME);
         intent.setType(MIME_TYPE);
-        mActivity.startActivityForResult(intent, 42);
+        mActivity.startActivityForResult(intent, REQUEST_CODE);
 
         mDevice.waitForIdle();
         findRoot("CtsCreate").click();
@@ -219,7 +253,7 @@
         intent.addCategory(Intent.CATEGORY_OPENABLE);
         intent.putExtra(Intent.EXTRA_TITLE, "NEVERUSED");
         intent.setType("mime2/file2");
-        mActivity.startActivityForResult(intent, 42);
+        mActivity.startActivityForResult(intent, REQUEST_CODE);
 
         mDevice.waitForIdle();
         findRoot("CtsCreate").click();
@@ -244,7 +278,7 @@
         if (!supportedHardware()) return;
 
         final Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
-        mActivity.startActivityForResult(intent, 42);
+        mActivity.startActivityForResult(intent, REQUEST_CODE);
 
         mDevice.waitForIdle();
         findRoot("CtsCreate").click();
@@ -312,13 +346,110 @@
         }
     }
 
+    // Not called by hostside tests, but useful during development...
+    public void testOpenExternalDirectory() throws Exception {
+        testOpenExternalDirectory_invalidPath();
+        testOpenExternalDirectory_userRejects();
+        testOpenExternalDirectory_userAccepts();
+    }
+
+    public void testOpenExternalDirectory_invalidPath() throws Exception {
+        if (!supportedHardware()) return;
+
+        final String externalRoot = getExternalStorageDirectory().getPath();
+        openExternalDirectoryInvalidPath("");
+        openExternalDirectoryInvalidPath("/dev/null");
+        openExternalDirectoryInvalidPath(externalRoot + "/../");
+        openExternalDirectoryInvalidPath(externalRoot + "/HiddenStuff");
+    }
+
+    public void testOpenExternalDirectory_userRejects() throws Exception {
+        if (!supportedHardware()) return;
+
+        final String externalRoot = getExternalStorageDirectory().getPath();
+
+        // Tests user clicking DENY button, for all valid directories.
+        for (String directory : STANDARD_DIRECTORIES) {
+            final Uri uri = Uri.fromFile(new File(externalRoot, directory));
+
+            final UiAlertDialog dialog = openExternalDirectoryValidPath(uri);
+            dialog.noButton.click();
+            assertActivityFailed();
+        }
+
+        // Also test user clicking back button - one directory is enough.
+        openExternalDirectoryValidPath(Uri.fromFile(new File(externalRoot, DIRECTORY_PICTURES)));
+        mDevice.pressBack();
+        assertActivityFailed();
+    }
+
+    public void testOpenExternalDirectory_userAccepts() throws Exception {
+        if (!supportedHardware())
+            return;
+
+        // TODO: once there is an API to get all volumes, use a for loop to call method below
+        // to all of them (rather than hard-coding).
+        userAcceptsOpenExternalDirectoryTest(getExternalStorageDirectory().getPath(),
+                "Internal storage");
+    }
+
+    private void userAcceptsOpenExternalDirectoryTest(String rootPath, String rootDescription)
+            throws Exception {
+        final Uri requestedUri = Uri.fromFile(new File(rootPath, DIRECTORY_PICTURES));
+
+        // Asserts dialog contain the proper message.
+        final UiAlertDialog dialog = openExternalDirectoryValidPath(requestedUri);
+        final String message = dialog.messageText.getText();
+        Log.v(TAG, "request permission message: " + message);
+        final Context context = getInstrumentation().getTargetContext();
+        final String appLabel = context.getPackageManager().getApplicationLabel(
+                context.getApplicationInfo()).toString();
+        assertContainsRegex("missing app label", appLabel, message);
+        assertContainsRegex("missing folder", DIRECTORY_PICTURES, message);
+        assertContainsRegex("missing root", rootDescription, message);
+
+        // Call API...
+        dialog.yesButton.click();
+
+        // ...and get its response.
+        final Intent data = assertActivitySucceeded();
+        final Uri grantedUri = data.getData();
+
+        // Test granted permission directly by persisting it...
+        final ContentResolver resolver = getInstrumentation().getContext().getContentResolver();
+        final int modeFlags = data.getFlags()
+                & (Intent.FLAG_GRANT_READ_URI_PERMISSION
+                | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+        resolver.takePersistableUriPermission(grantedUri, modeFlags);
+
+        // ...and indirectly by creating some documents
+        final Uri doc = DocumentsContract.buildDocumentUriUsingTree(grantedUri,
+                DocumentsContract.getTreeDocumentId(grantedUri));
+        assertNotNull("could not get tree UURI", doc);
+        final Uri pic = DocumentsContract.createDocument(resolver, doc, "image/png", "pic.png");
+        assertNotNull("could not create file (pic.png) on tree root", pic);
+        final Uri dir = DocumentsContract.createDocument(resolver, doc, Document.MIME_TYPE_DIR,
+                "my dir");
+        assertNotNull("could not create child dir (my dir)", pic);
+        final Uri dirPic = DocumentsContract.createDocument(resolver, dir, "image/png", "pic2.png");
+        assertNotNull("could not create file (pic.png) on child dir (my dir)", dirPic);
+
+        writeFully(pic, "pic".getBytes());
+        writeFully(dirPic, "dirPic".getBytes());
+
+        // Clean up created documents.
+        assertTrue("delete", DocumentsContract.deleteDocument(resolver, pic));
+        assertTrue("delete", DocumentsContract.deleteDocument(resolver, dirPic));
+        assertTrue("delete", DocumentsContract.deleteDocument(resolver, dir));
+    }
+
     public void testGetContent() throws Exception {
         if (!supportedHardware()) return;
 
         final Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
         intent.addCategory(Intent.CATEGORY_OPENABLE);
         intent.setType("*/*");
-        mActivity.startActivityForResult(intent, 42);
+        mActivity.startActivityForResult(intent, REQUEST_CODE);
 
         // Look around, we should be able to see both DocumentsProviders and
         // other GET_CONTENT sources.
@@ -379,4 +510,53 @@
         }
         return true;
     }
+
+    private void assertActivityFailed() {
+        final Result result = mActivity.getResult();
+        assertEquals(REQUEST_CODE, result.requestCode);
+        assertEquals(Activity.RESULT_CANCELED, result.resultCode);
+        assertNull(result.data);
+    }
+
+    private Intent assertActivitySucceeded() {
+        final Result result = mActivity.getResult();
+        assertEquals(REQUEST_CODE, result.requestCode);
+        assertEquals(Activity.RESULT_OK, result.resultCode);
+        assertNotNull(result.data);
+        return result.data;
+    }
+
+    private void openExternalDirectoryInvalidPath(String path) {
+        sendOpenExternalDirectoryIntent(Uri.fromFile(new File(path)));
+        assertActivityFailed();
+    }
+
+    private UiAlertDialog openExternalDirectoryValidPath(Uri uri) throws UiObjectNotFoundException {
+        sendOpenExternalDirectoryIntent(uri);
+        return new UiAlertDialog();
+    }
+
+    private void sendOpenExternalDirectoryIntent(Uri uri) {
+        final Intent intent = new Intent(Intent.ACTION_OPEN_EXTERNAL_DIRECTORY);
+        intent.setData(uri);
+        mActivity.startActivityForResult(intent, REQUEST_CODE);
+        mDevice.waitForIdle();
+    }
+
+    private final class UiAlertDialog {
+        final UiObject messageText;
+        final UiObject yesButton;
+        final UiObject noButton;
+
+        UiAlertDialog() throws UiObjectNotFoundException {
+            final String id = "android:id/parentPanel";
+            boolean gotIt = mDevice.wait(Until.hasObject(By.res(id)), TIMEOUT);
+            assertTrue("object with id '(" + id + "') not visible yet", gotIt);
+            final UiObject dialog = mDevice.findObject(new UiSelector().resourceId(id));
+            assertTrue("object with id '(" + id + "') doesn't exist", dialog.exists());
+            messageText = dialog.getChild(new UiSelector().resourceId("android:id/message"));
+            yesButton = dialog.getChild(new UiSelector().resourceId("android:id/button1"));
+            noButton  = dialog.getChild(new UiSelector().resourceId("android:id/button2"));
+        }
+    }
 }
diff --git a/hostsidetests/appsecurity/test-apps/DocumentClient/src/com/android/cts/documentclient/MyActivity.java b/hostsidetests/appsecurity/test-apps/DocumentClient/src/com/android/cts/documentclient/MyActivity.java
index a6cb28d..7033ae8 100644
--- a/hostsidetests/appsecurity/test-apps/DocumentClient/src/com/android/cts/documentclient/MyActivity.java
+++ b/hostsidetests/appsecurity/test-apps/DocumentClient/src/com/android/cts/documentclient/MyActivity.java
@@ -28,10 +28,12 @@
     private final SynchronousQueue<Result> mResult = new SynchronousQueue<>();
 
     public static class Result {
+        public final int requestCode;
         public final int resultCode;
         public final Intent data;
 
-        public Result(int resultCode, Intent data) {
+        public Result(int requestCode, int resultCode, Intent data) {
+            this.requestCode = requestCode;
             this.resultCode = resultCode;
             this.data = data;
         }
@@ -49,17 +51,22 @@
     @Override
     protected void onActivityResult(int requestCode, int resultCode, Intent data) {
         try {
-            mResult.offer(new Result(resultCode, data), 5, TimeUnit.SECONDS);
+            mResult.offer(new Result(requestCode, resultCode, data), 5, TimeUnit.SECONDS);
         } catch (InterruptedException e) {
             throw new RuntimeException(e);
         }
     }
 
     public Result getResult() {
+        final Result result;
         try {
-            return mResult.take();
+            result = mResult.poll(30, TimeUnit.SECONDS);
         } catch (InterruptedException e) {
             throw new RuntimeException(e);
         }
+        if (result == null) {
+            throw new IllegalStateException("Activity didn't receive a Result in 30 seconds");
+        }
+        return result;
     }
 }
diff --git a/hostsidetests/devicepolicy/app/AppRestrictionsManagingApp/Android.mk b/hostsidetests/devicepolicy/app/AppRestrictionsManagingApp/Android.mk
new file mode 100644
index 0000000..e0c6d4e
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/AppRestrictionsManagingApp/Android.mk
@@ -0,0 +1,36 @@
+# 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.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_PACKAGE_NAME := CtsAppRestrictionsManagingApp
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_JAVA_LIBRARIES := android.test.runner cts-junit
+
+LOCAL_STATIC_JAVA_LIBRARIES = android-support-v4 ctstestrunner android-support-test
+
+LOCAL_SDK_VERSION := current
+
+# tag this module as a cts_v2 test artifact
+LOCAL_COMPATIBILITY_SUITE := cts_v2
+
+include $(BUILD_CTS_PACKAGE)
diff --git a/hostsidetests/devicepolicy/app/AppRestrictionsManagingApp/AndroidManifest.xml b/hostsidetests/devicepolicy/app/AppRestrictionsManagingApp/AndroidManifest.xml
new file mode 100644
index 0000000..4a6f2a5
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/AppRestrictionsManagingApp/AndroidManifest.xml
@@ -0,0 +1,29 @@
+<?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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.cts.apprestrictions.managingapp">
+
+    <uses-sdk android:minSdkVersion="24"/>
+
+    <application>
+        <uses-library android:name="android.test.runner" />
+    </application>
+
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+                     android:targetPackage="com.android.cts.apprestrictions.managingapp"
+                     android:label="App restrictions managing package CTS Tests"/>
+</manifest>
diff --git a/hostsidetests/devicepolicy/app/AppRestrictionsManagingApp/src/com/android/cts/apprestrictions/managingapp/ApplicationRestrictionsManagerTest.java b/hostsidetests/devicepolicy/app/AppRestrictionsManagingApp/src/com/android/cts/apprestrictions/managingapp/ApplicationRestrictionsManagerTest.java
new file mode 100644
index 0000000..d9b05b9
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/AppRestrictionsManagingApp/src/com/android/cts/apprestrictions/managingapp/ApplicationRestrictionsManagerTest.java
@@ -0,0 +1,200 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.cts.apprestrictions.managingapp;
+
+import android.app.admin.DevicePolicyManager;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Bundle;
+import android.os.Parcelable;
+import android.os.UserManager;
+import android.test.InstrumentationTestCase;
+import android.test.MoreAsserts;
+
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Tests that a package other than the DPC can manage app restrictions if allowed by the DPC
+ * via {@link DevicePolicyManager#setApplicationRestrictionsManagingPackage(ComponentName, String)}
+ */
+public class ApplicationRestrictionsManagerTest extends InstrumentationTestCase {
+
+    private static final String APP_RESTRICTIONS_TARGET_PKG =
+            "com.android.cts.apprestrictions.targetapp";
+    private static final String APP_RESTRICTIONS_ACTIVITY_NAME =
+            APP_RESTRICTIONS_TARGET_PKG + ".ApplicationRestrictionsActivity";
+    private static final String ACTION_RESTRICTIONS_VALUE =
+            "com.android.cts.apprestrictions.targetapp.RESTRICTIONS_VALUE";
+
+    private static final ComponentName TEST_COMPONENT_NAME = new ComponentName(
+            APP_RESTRICTIONS_TARGET_PKG, ApplicationRestrictionsManagerTest.class.getName());
+
+    private static final Bundle BUNDLE_0 = createBundle0();
+    private static final Bundle BUNDLE_1 = createBundle1();
+
+    private static final long WAIT_FOR_ACTIVITY_TIMEOUT_SECONDS = 10;
+
+    private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            String action = intent.getAction();
+            if (ACTION_RESTRICTIONS_VALUE.equals(action)) {
+                mReceivedRestrictions = intent.getBundleExtra("value");
+                mOnRestrictionsSemaphore.release();
+            }
+        }
+    };
+
+    private Context mContext;
+    private DevicePolicyManager mDevicePolicyManager;
+    private UserManager mUserManager;
+    private final Semaphore mOnRestrictionsSemaphore = new Semaphore(0);
+    private Bundle mReceivedRestrictions;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+
+        mContext = getInstrumentation().getContext();
+        mDevicePolicyManager = (DevicePolicyManager)
+                mContext.getSystemService(Context.DEVICE_POLICY_SERVICE);
+        mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
+
+        mContext.registerReceiver(mReceiver, new IntentFilter(ACTION_RESTRICTIONS_VALUE));
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        mContext.unregisterReceiver(mReceiver);
+
+        super.tearDown();
+    }
+
+    public void testCannotManageAppRestrictions() {
+        assertFalse(mDevicePolicyManager.isCallerApplicationRestrictionsManagingPackage());
+        try {
+            mDevicePolicyManager.setApplicationRestrictions(
+                    null, APP_RESTRICTIONS_TARGET_PKG, null);
+            fail("Expected SecurityException not thrown");
+        } catch (SecurityException expected) {
+            MoreAsserts.assertContainsRegex(
+                    "cannot manage application restrictions", expected.getMessage());
+        }
+        try {
+            mDevicePolicyManager.getApplicationRestrictions(null, APP_RESTRICTIONS_TARGET_PKG);
+            fail("Expected SecurityException not thrown");
+        } catch (SecurityException expected) {
+            MoreAsserts.assertContainsRegex(
+                    "cannot manage application restrictions", expected.getMessage());
+        }
+
+        // Should still be able to retrieve our own restrictions via user manager
+        mUserManager.getApplicationRestrictions(mContext.getPackageName());
+    }
+
+    public void testCanManageAppRestrictions() {
+        assertTrue(mDevicePolicyManager.isCallerApplicationRestrictionsManagingPackage());
+        try {
+            mDevicePolicyManager.setApplicationRestrictions(
+                    null, APP_RESTRICTIONS_TARGET_PKG, BUNDLE_0);
+            assertBundle0(mDevicePolicyManager.getApplicationRestrictions(
+                    null, APP_RESTRICTIONS_TARGET_PKG));
+
+            // Check that the target app can retrieve the same restrictions.
+            assertBundle0(waitForChangedRestriction());
+
+            // Test overwriting
+            mDevicePolicyManager.setApplicationRestrictions(
+                    null, APP_RESTRICTIONS_TARGET_PKG, BUNDLE_1);
+            assertBundle1(mDevicePolicyManager.getApplicationRestrictions(
+                    null, APP_RESTRICTIONS_TARGET_PKG));
+            assertBundle1(waitForChangedRestriction());
+        } finally {
+            mDevicePolicyManager.setApplicationRestrictions(
+                    null, APP_RESTRICTIONS_TARGET_PKG, new Bundle());
+            assertTrue(mDevicePolicyManager.getApplicationRestrictions(
+                    null, APP_RESTRICTIONS_TARGET_PKG).isEmpty());
+        }
+    }
+
+    public void testSettingComponentNameThrowsException() {
+        assertTrue(mDevicePolicyManager.isCallerApplicationRestrictionsManagingPackage());
+        try {
+            mDevicePolicyManager.setApplicationRestrictions(
+                    TEST_COMPONENT_NAME, APP_RESTRICTIONS_TARGET_PKG, null);
+            fail("Expected SecurityException not thrown");
+        } catch (SecurityException expected) {
+            MoreAsserts.assertContainsRegex("No active admin", expected.getMessage());
+        }
+        try {
+            mDevicePolicyManager.getApplicationRestrictions(
+                    TEST_COMPONENT_NAME, APP_RESTRICTIONS_TARGET_PKG);
+            fail("Expected SecurityException not thrown");
+        } catch (SecurityException expected) {
+            MoreAsserts.assertContainsRegex("No active admin", expected.getMessage());
+        }
+    }
+
+    // Should be consistent with assertBundle0
+    private static Bundle createBundle0() {
+        Bundle result = new Bundle();
+        result.putString("dummyString", "value");
+        return result;
+    }
+
+    // Should be consistent with createBundle0
+    private void assertBundle0(Bundle bundle) {
+        assertEquals(1, bundle.size());
+        assertEquals("value", bundle.getString("dummyString"));
+    }
+
+    // Should be consistent with assertBundle1
+    private static Bundle createBundle1() {
+        Bundle result = new Bundle();
+        result.putInt("dummyInt", 1);
+        return result;
+    }
+
+    // Should be consistent with createBundle1
+    private void assertBundle1(Bundle bundle) {
+        assertEquals(1, bundle.size());
+        assertEquals(1, bundle.getInt("dummyInt"));
+    }
+
+    private void startTestActivity() {
+        mContext.startActivity(new Intent()
+                .setComponent(new ComponentName(
+                        APP_RESTRICTIONS_TARGET_PKG, APP_RESTRICTIONS_ACTIVITY_NAME))
+                .addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_NEW_TASK));
+    }
+
+    private Bundle waitForChangedRestriction() {
+        startTestActivity();
+
+        try {
+            assertTrue(mOnRestrictionsSemaphore.tryAcquire(
+                    WAIT_FOR_ACTIVITY_TIMEOUT_SECONDS, TimeUnit.SECONDS));
+        } catch (InterruptedException e) {
+            fail("waitForChangedRestriction() interrupted");
+        }
+
+        return mReceivedRestrictions;
+    }
+}
diff --git a/hostsidetests/devicepolicy/app/AppRestrictionsTargetApp/Android.mk b/hostsidetests/devicepolicy/app/AppRestrictionsTargetApp/Android.mk
new file mode 100644
index 0000000..2b6b482
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/AppRestrictionsTargetApp/Android.mk
@@ -0,0 +1,32 @@
+# 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.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_PACKAGE_NAME := CtsAppRestrictionsTargetApp
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_SDK_VERSION := current
+
+# tag this module as a cts_v2 test artifact
+LOCAL_COMPATIBILITY_SUITE := cts_v2
+
+include $(BUILD_CTS_PACKAGE)
diff --git a/hostsidetests/devicepolicy/app/AppRestrictionsTargetApp/AndroidManifest.xml b/hostsidetests/devicepolicy/app/AppRestrictionsTargetApp/AndroidManifest.xml
new file mode 100644
index 0000000..40f6993
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/AppRestrictionsTargetApp/AndroidManifest.xml
@@ -0,0 +1,28 @@
+<?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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.cts.apprestrictions.targetapp">
+
+    <uses-sdk android:minSdkVersion="24"/>
+
+    <application>
+        <activity
+            android:name="com.android.cts.apprestrictions.targetapp.ApplicationRestrictionsActivity"
+            android:exported="true">
+        </activity>
+    </application>
+</manifest>
diff --git a/hostsidetests/devicepolicy/app/AppRestrictionsTargetApp/src/com/android/cts/apprestrictions/targetapp/ApplicationRestrictionsActivity.java b/hostsidetests/devicepolicy/app/AppRestrictionsTargetApp/src/com/android/cts/apprestrictions/targetapp/ApplicationRestrictionsActivity.java
new file mode 100644
index 0000000..c44fecd
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/AppRestrictionsTargetApp/src/com/android/cts/apprestrictions/targetapp/ApplicationRestrictionsActivity.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.cts.apprestrictions.targetapp;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.UserManager;
+
+/**
+ * Test activity for {@link android.app.admin.DevicePolicyManager#setApplicationRestrictions}.
+ *
+ * The actual test will set restrictions for this package, and the purpose of this
+ * activity is to retrieve those restrictions and relay them back to the test for validation.
+ */
+public class ApplicationRestrictionsActivity extends Activity {
+
+    private static final String ACTION_RESTRICTIONS_VALUE =
+            "com.android.cts.apprestrictions.targetapp.RESTRICTIONS_VALUE";
+
+    private UserManager mUserManager;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        mUserManager = (UserManager) getSystemService(Context.USER_SERVICE);
+        handleIntent(getIntent());
+    }
+
+    @Override
+    protected void onNewIntent(Intent intent) {
+        super.onNewIntent(intent);
+        handleIntent(intent);
+    }
+
+    private void handleIntent(Intent intent) {
+        Bundle restrictions = mUserManager.getApplicationRestrictions(getPackageName());
+        sendBroadcast(new Intent(ACTION_RESTRICTIONS_VALUE)
+                .addFlags(Intent.FLAG_RECEIVER_FOREGROUND)
+                .putExtra("value", restrictions));
+
+        finish();
+    }
+}
diff --git a/hostsidetests/devicepolicy/app/DeviceAdmin/Android.mk b/hostsidetests/devicepolicy/app/DeviceAdmin/Android.mk
new file mode 100644
index 0000000..752d8b6
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/DeviceAdmin/Android.mk
@@ -0,0 +1,34 @@
+# 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.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_PACKAGE_NAME := CtsDeviceAdminApp
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner compatibility-device-util
+
+LOCAL_SDK_VERSION := current
+
+# tag this module as a cts_v2 test artifact
+LOCAL_COMPATIBILITY_SUITE := cts_v2
+
+include $(BUILD_CTS_PACKAGE)
diff --git a/hostsidetests/devicepolicy/app/DeviceAdmin/AndroidManifest.xml b/hostsidetests/devicepolicy/app/DeviceAdmin/AndroidManifest.xml
new file mode 100644
index 0000000..f819fbb
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/DeviceAdmin/AndroidManifest.xml
@@ -0,0 +1,50 @@
+<?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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+        package="com.android.cts.deviceadmin" >
+
+    <uses-sdk android:minSdkVersion="23"/>
+
+    <application>
+        <uses-library android:name="android.test.runner" />
+        <receiver
+                android:name="com.android.cts.deviceadmin.DeviceAdminTest$AdminReceiver"
+                android:permission="android.permission.BIND_DEVICE_ADMIN"
+                >
+            <meta-data android:name="android.app.device_admin"
+                    android:resource="@xml/device_admin" />
+            <intent-filter>
+                <action android:name="android.app.action.DEVICE_ADMIN_ENABLED" />
+            </intent-filter>
+        </receiver>
+
+        <receiver
+                android:name="com.android.cts.deviceadmin.DeviceAdminReceiverWithNoProtection"
+                >
+            <meta-data android:name="android.app.device_admin"
+                    android:resource="@xml/device_admin" />
+            <intent-filter>
+                <action android:name="android.app.action.DEVICE_ADMIN_ENABLED" />
+            </intent-filter>
+        </receiver>
+
+    </application>
+
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+            android:targetPackage="com.android.cts.deviceadmin"
+            android:label="Device Admin CTS tests"/>
+</manifest>
diff --git a/tests/tests/content/res/values-fa-rIR/strings.xml b/hostsidetests/devicepolicy/app/DeviceAdmin/res/xml/device_admin.xml
similarity index 71%
copy from tests/tests/content/res/values-fa-rIR/strings.xml
copy to hostsidetests/devicepolicy/app/DeviceAdmin/res/xml/device_admin.xml
index 25fd20c..2fbc392 100644
--- a/tests/tests/content/res/values-fa-rIR/strings.xml
+++ b/hostsidetests/devicepolicy/app/DeviceAdmin/res/xml/device_admin.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2015 The Android Open Source Project
+<!-- 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.
@@ -13,7 +13,8 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="am">صبح</string>
-</resources>
+<device-admin xmlns:android="http://schemas.android.com/apk/res/android" android:visible="false">
+    <uses-policies>
+        <reset-password />
+    </uses-policies>
+</device-admin>
diff --git a/hostsidetests/devicepolicy/app/DeviceAdmin/src/com.android.cts.deviceadmin/ClearDeviceAdminTest.java b/hostsidetests/devicepolicy/app/DeviceAdmin/src/com.android.cts.deviceadmin/ClearDeviceAdminTest.java
new file mode 100644
index 0000000..f0ee2ca
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/DeviceAdmin/src/com.android.cts.deviceadmin/ClearDeviceAdminTest.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.cts.deviceadmin;
+
+import android.app.admin.DevicePolicyManager;
+import android.test.AndroidTestCase;
+
+/**
+ * Remove myself as active admin.
+ */
+public class ClearDeviceAdminTest extends AndroidTestCase {
+
+    public void testRemoveActiveAdmin() throws Exception {
+
+        final DevicePolicyManager dpm = getContext().getSystemService(DevicePolicyManager.class);
+
+        if (dpm.isAdminActive(DeviceAdminTest.ADMIN_COMPONENT)) {
+            dpm.removeActiveAdmin(DeviceAdminTest.ADMIN_COMPONENT);
+            for (int i = 0; i < 1000 && dpm.isAdminActive(DeviceAdminTest.ADMIN_COMPONENT); i++) {
+                Thread.sleep(100);
+            }
+        }
+        assertFalse("Still active admin", dpm.isAdminActive(DeviceAdminTest.ADMIN_COMPONENT));
+    }
+}
diff --git a/hostsidetests/devicepolicy/app/DeviceAdmin/src/com.android.cts.deviceadmin/ClearDeviceOwnerTest.java b/hostsidetests/devicepolicy/app/DeviceAdmin/src/com.android.cts.deviceadmin/ClearDeviceOwnerTest.java
new file mode 100644
index 0000000..030d7db
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/DeviceAdmin/src/com.android.cts.deviceadmin/ClearDeviceOwnerTest.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.cts.deviceadmin;
+
+import android.app.admin.DevicePolicyManager;
+import android.test.AndroidTestCase;
+
+/**
+ * Remove myself as DO.
+ */
+public class ClearDeviceOwnerTest extends AndroidTestCase {
+
+    public void testRemoveActiveAdmin() throws Exception {
+
+        final DevicePolicyManager dpm = getContext().getSystemService(DevicePolicyManager.class);
+
+        dpm.clearDeviceOwnerApp(DeviceAdminTest.ADMIN_PACKAGE);
+        assertFalse("Still device owner", dpm.isDeviceOwnerApp(DeviceAdminTest.ADMIN_PACKAGE));
+    }
+}
diff --git a/hostsidetests/devicepolicy/app/DeviceAdmin/src/com.android.cts.deviceadmin/DeviceAdminReceiverWithNoProtection.java b/hostsidetests/devicepolicy/app/DeviceAdmin/src/com.android.cts.deviceadmin/DeviceAdminReceiverWithNoProtection.java
new file mode 100644
index 0000000..99d9a61
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/DeviceAdmin/src/com.android.cts.deviceadmin/DeviceAdminReceiverWithNoProtection.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.cts.deviceadmin;
+
+import android.app.admin.DeviceAdminReceiver;
+
+public class DeviceAdminReceiverWithNoProtection extends DeviceAdminReceiver {
+}
diff --git a/hostsidetests/devicepolicy/app/DeviceAdmin/src/com.android.cts.deviceadmin/DeviceAdminTest.java b/hostsidetests/devicepolicy/app/DeviceAdmin/src/com.android.cts.deviceadmin/DeviceAdminTest.java
new file mode 100644
index 0000000..e0c3547
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/DeviceAdmin/src/com.android.cts.deviceadmin/DeviceAdminTest.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.cts.deviceadmin;
+
+import android.app.admin.DeviceAdminReceiver;
+import android.app.admin.DevicePolicyManager;
+import android.content.ComponentName;
+import android.test.AndroidTestCase;
+import android.test.MoreAsserts;
+
+public class DeviceAdminTest extends AndroidTestCase {
+    public static class AdminReceiver extends DeviceAdminReceiver {
+    }
+
+    public static final String ADMIN_PACKAGE = "com.android.cts.deviceadmin";
+    public static final ComponentName ADMIN_COMPONENT = new ComponentName(ADMIN_PACKAGE,
+            AdminReceiver.class.getName());
+
+    public void testSetPassword_success() {
+        final DevicePolicyManager dpm = getContext().getSystemService(DevicePolicyManager.class);
+
+        assertTrue(dpm.resetPassword("1234", /* flags= */ 0));
+    }
+
+    public void testSetPassword_failure() {
+        final DevicePolicyManager dpm = getContext().getSystemService(DevicePolicyManager.class);
+
+        try {
+            assertFalse(dpm.resetPassword("1234", /* flags= */ 0));
+        } catch (SecurityException e) {
+            MoreAsserts.assertContainsRegex("Admin cannot change current password", e.getMessage());
+        }
+    }
+
+    public void testClearPassword_success() {
+        final DevicePolicyManager dpm = getContext().getSystemService(DevicePolicyManager.class);
+
+        assertTrue(dpm.resetPassword("", /* flags= */ 0));
+    }
+
+    public void testClearPassword_failure() {
+        final DevicePolicyManager dpm = getContext().getSystemService(DevicePolicyManager.class);
+
+        try {
+            assertFalse(dpm.resetPassword("", /* flags= */ 0));
+        } catch (SecurityException e) {
+            MoreAsserts.assertContainsRegex("Cannot call with null password", e.getMessage());
+        }
+    }
+}
diff --git a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/Android.mk b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/Android.mk
index e1f1a89..438be8c 100644
--- a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/Android.mk
+++ b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/Android.mk
@@ -24,8 +24,6 @@
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
-LOCAL_JAVA_LIBRARIES := android.test.runner cts-junit
-
 LOCAL_STATIC_JAVA_LIBRARIES = android-support-v4 ctstestrunner ub-uiautomator
 
 LOCAL_SDK_VERSION := current
diff --git a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/AndroidManifest.xml b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/AndroidManifest.xml
index 223b919..9c0c2ee 100644
--- a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/AndroidManifest.xml
+++ b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/AndroidManifest.xml
@@ -23,7 +23,6 @@
     <uses-permission android:name="android.permission.MANAGE_ACCOUNTS" />
 
     <application>
-        <uses-library android:name="android.test.runner" />
         <receiver
             android:name="com.android.cts.deviceandprofileowner.BaseDeviceAdminTest$BasicAdminReceiver"
             android:permission="android.permission.BIND_DEVICE_ADMIN">
@@ -43,8 +42,6 @@
             </intent-filter>
         </receiver>
         <activity
-            android:name="com.android.cts.deviceandprofileowner.ApplicationRestrictionsActivity" />
-        <activity
             android:name="com.android.cts.deviceandprofileowner.ScreenCaptureDisabledActivity" />
         <activity
             android:name="com.android.cts.deviceandprofileowner.ExampleIntentReceivingActivity1">
diff --git a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/ApplicationRestrictionsActivity.java b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/ApplicationRestrictionsActivity.java
deleted file mode 100644
index ccf8222..0000000
--- a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/ApplicationRestrictionsActivity.java
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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.Activity;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.os.Bundle;
-import android.os.UserManager;
-
-/**
- * Test activity for setApplicationRestrictions().
- *
- * The actual test will set restrictions for this package, and the purpose of this
- * activity is to listen for the ACTION_APPLICATION_RESTRICTIONS_CHANGED broadcast
- * and relay the retrieved restriction bundle back to the test for validation.
- */
-public class ApplicationRestrictionsActivity extends Activity {
-
-    // Incoming intent type
-    public static final String FINISH = "finishActivity";
-
-    // Outgoing broadcast
-    public static final String REGISTERED_ACTION =
-            "com.android.cts.deviceandprofileowner.APP_RESTRICTION_REGISTERED";
-    public static final String RESTRICTION_ACTION =
-            "com.android.cts.deviceandprofileowner.APP_RESTRICTION_VALUE";
-
-    private UserManager mUserManager;
-
-    private final BroadcastReceiver mAppRestrictionReceiver = new BroadcastReceiver() {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            broadcastRestriction();
-        }
-    };
-
-    private void broadcastRestriction() {
-        Bundle restrictions = mUserManager.getApplicationRestrictions(getPackageName());
-        Intent intent = new Intent(RESTRICTION_ACTION);
-        intent.putExtra("value", restrictions);
-        sendBroadcast(intent);
-    }
-
-    @Override
-    protected void onNewIntent(Intent intent) {
-        super.onNewIntent(intent);
-        handleIntent(intent);
-    }
-
-    @Override
-    protected void onCreate(android.os.Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        handleIntent(getIntent());
-    }
-
-    @Override
-    protected void onResume() {
-        super.onResume();
-        mUserManager = (UserManager) getSystemService(Context.USER_SERVICE);
-        IntentFilter filter = new IntentFilter(Intent.ACTION_APPLICATION_RESTRICTIONS_CHANGED);
-        registerReceiver(mAppRestrictionReceiver, filter);
-        sendBroadcast(new Intent(REGISTERED_ACTION));
-    }
-
-    @Override
-    protected void onPause() {
-        super.onPause();
-        unregisterReceiver(mAppRestrictionReceiver);
-    }
-
-    private void handleIntent(Intent intent) {
-        if (intent.getBooleanExtra(FINISH, false)) {
-            finish();
-        }
-    }
-
-}
diff --git a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/ApplicationRestrictionsTest.java b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/ApplicationRestrictionsTest.java
index 98ae202..923fc44 100644
--- a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/ApplicationRestrictionsTest.java
+++ b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/ApplicationRestrictionsTest.java
@@ -16,104 +16,197 @@
 package com.android.cts.deviceandprofileowner;
 
 import android.content.BroadcastReceiver;
+import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.os.Bundle;
 import android.os.Parcelable;
+import android.os.UserManager;
+import android.test.MoreAsserts;
 
 import java.util.concurrent.Semaphore;
 import java.util.concurrent.TimeUnit;
 
 /**
- * Functionality tests for setApplicationRestrictions and getApplicationRestrictions
- * in DevicePolicyManager.
+ * Functionality tests for application restrictions APIs.
  *
- * First of all, these two APIs are executed locally to assert that what you set
- * can later be retrieved via the getter. It also fires up an external activity
- * (which runs in com.google.android.xts.gmscore, unlike the test code itself
- * which runs in the test target package com.google.android.gms due to
- * instrumentation) to observe an application's view of its restrictions.
- * The activity listens to ACTION_APPLICATION_RESTRICTIONS_CHANGED broadcast
- * which is fired by the system whenever its restriction is modified,
- * and relays the value back to this test for verification.
+ * <p>APIs are executed locally to assert that what you set can later be retrieved via the getter.
+ * It also fires up an external activity to observe an application's view of its restrictions.
+ *
+ * <p>Finally, it checks that the {@link Intent#ACTION_APPLICATION_RESTRICTIONS_CHANGED} broadcast
+ * is sent whenever app restrictions are modified for a given package.
  */
 public class ApplicationRestrictionsTest extends BaseDeviceAdminTest {
 
-    private static final String[] testStrings = new String[] {
+    private static final String APP_RESTRICTIONS_TARGET_PKG =
+            "com.android.cts.apprestrictions.targetapp";
+    private static final String APP_RESTRICTIONS_ACTIVITY_NAME =
+            APP_RESTRICTIONS_TARGET_PKG + ".ApplicationRestrictionsActivity";
+    private static final String ACTION_RESTRICTIONS_VALUE =
+            "com.android.cts.apprestrictions.targetapp.RESTRICTIONS_VALUE";
+
+    private static final String OTHER_PACKAGE = APP_RESTRICTIONS_TARGET_PKG + "dummy";
+
+    private static final String[] TEST_STRINGS = new String[] {
             "<bad/>",
             ">worse!\"£$%^&*()'<",
             "<JSON>\"{ \\\"One\\\": { \\\"OneOne\\\": \\\"11\\\", \\\""
                     + "OneTwo\\\": \\\"12\\\" }, \\\"Two\\\": \\\"2\\\" } <JSON/>\""
     };
 
-    private final Semaphore mOnRegisteredSemaphore = new Semaphore(0);
-    private final Semaphore mOnRestrictionSemaphore = new Semaphore(0);
+    private static final Bundle BUNDLE_0 = createBundle0();
+    private static final Bundle BUNDLE_1 = createBundle1();
+
+    private static final long RESTRICTIONS_TIMEOUT_SECONDS = 10;
+
+    private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            String action = intent.getAction();
+            if (ACTION_RESTRICTIONS_VALUE.equals(action)) {
+                mReceivedRestrictions = intent.getBundleExtra("value");
+                mOnRestrictionsReceivedFromAppSemaphore.release();
+            } else if (Intent.ACTION_APPLICATION_RESTRICTIONS_CHANGED.equals(action)) {
+                mOnAppRestrictionsChangedSemahore.release();
+            }
+        }
+    };
+
+    private final Semaphore mOnAppRestrictionsChangedSemahore = new Semaphore(0);
+    private final Semaphore mOnRestrictionsReceivedFromAppSemaphore = new Semaphore(0);
     private Bundle mReceivedRestrictions;
+    private UserManager mUserManager;
 
     @Override
     protected void setUp() throws Exception {
         super.setUp();
+
+        mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
+
         IntentFilter filter = new IntentFilter();
-        filter.addAction(ApplicationRestrictionsActivity.REGISTERED_ACTION);
-        filter.addAction(ApplicationRestrictionsActivity.RESTRICTION_ACTION);
+        filter.addAction(ACTION_RESTRICTIONS_VALUE);
+        filter.addAction(Intent.ACTION_APPLICATION_RESTRICTIONS_CHANGED);
         mContext.registerReceiver(mReceiver, filter);
     }
 
     @Override
     protected void tearDown() throws Exception {
         mContext.unregisterReceiver(mReceiver);
+
+        mDevicePolicyManager.setApplicationRestrictions(
+                ADMIN_RECEIVER_COMPONENT, APP_RESTRICTIONS_TARGET_PKG, new Bundle());
+        mDevicePolicyManager.setApplicationRestrictions(
+                ADMIN_RECEIVER_COMPONENT, OTHER_PACKAGE, new Bundle());
+        mDevicePolicyManager.setApplicationRestrictions(
+                ADMIN_RECEIVER_COMPONENT, mContext.getPackageName(), new Bundle());
+
         super.tearDown();
     }
 
+    public void testNullComponentThrowsException() {
+        try {
+            mDevicePolicyManager.setApplicationRestrictions(
+                    null, APP_RESTRICTIONS_TARGET_PKG, null);
+            fail("Expected SecurityException not thrown");
+        } catch (SecurityException expected) {
+            MoreAsserts.assertContainsRegex(
+                    "cannot manage application restrictions", expected.getMessage());
+        }
+        try {
+            mDevicePolicyManager.getApplicationRestrictions(null, APP_RESTRICTIONS_TARGET_PKG);
+            fail("Expected SecurityException not thrown");
+        } catch (SecurityException expected) {
+            MoreAsserts.assertContainsRegex(
+                    "cannot manage application restrictions", expected.getMessage());
+        }
+    }
+
     public void testSetApplicationRestrictions() {
-        final String CTS_PACKAGE = PACKAGE_NAME;
-        final String OTHER_PACKAGE = CTS_PACKAGE + "dummy";
-
-        startAndWait();
-
-        Bundle bundle0 = createBundle0();
-        Bundle bundle1 = createBundle1();
-
         // Test setting restrictions
-        mDevicePolicyManager.setApplicationRestrictions(ADMIN_RECEIVER_COMPONENT, CTS_PACKAGE,
-                bundle0);
-        mDevicePolicyManager.setApplicationRestrictions(ADMIN_RECEIVER_COMPONENT, OTHER_PACKAGE,
-                bundle1);
+        mDevicePolicyManager.setApplicationRestrictions(
+                ADMIN_RECEIVER_COMPONENT, APP_RESTRICTIONS_TARGET_PKG, BUNDLE_0);
+        mDevicePolicyManager.setApplicationRestrictions(
+                ADMIN_RECEIVER_COMPONENT, OTHER_PACKAGE, BUNDLE_1);
 
         // Retrieve restrictions locally and make sure they are what we put in.
-        assertBundle0(mDevicePolicyManager.getApplicationRestrictions(ADMIN_RECEIVER_COMPONENT,
-                CTS_PACKAGE));
-        assertBundle1(mDevicePolicyManager.getApplicationRestrictions(ADMIN_RECEIVER_COMPONENT,
-                OTHER_PACKAGE));
+        assertBundle0(mDevicePolicyManager.getApplicationRestrictions(
+                ADMIN_RECEIVER_COMPONENT, APP_RESTRICTIONS_TARGET_PKG));
+        assertBundle1(mDevicePolicyManager.getApplicationRestrictions(
+                ADMIN_RECEIVER_COMPONENT, OTHER_PACKAGE));
 
-        // The test activity should have received a change_restriction broadcast
-        // and relay the value back to us.
-        assertBundle0(waitForChangedRestriction());
+        // Check that the target app can retrieve the same restrictions.
+        assertBundle0(waitForRestrictionsValueFromTestActivity());
 
         // Test overwriting
-        mDevicePolicyManager.setApplicationRestrictions(ADMIN_RECEIVER_COMPONENT, CTS_PACKAGE,
-                bundle1);
-        assertBundle1(mDevicePolicyManager.getApplicationRestrictions(ADMIN_RECEIVER_COMPONENT,
-                CTS_PACKAGE));
-        assertBundle1(waitForChangedRestriction());
+        mDevicePolicyManager.setApplicationRestrictions(
+                ADMIN_RECEIVER_COMPONENT, APP_RESTRICTIONS_TARGET_PKG, BUNDLE_1);
+        assertBundle1(mDevicePolicyManager.getApplicationRestrictions(
+                ADMIN_RECEIVER_COMPONENT, APP_RESTRICTIONS_TARGET_PKG));
+        assertBundle1(waitForRestrictionsValueFromTestActivity());
 
-        // Cleanup
-        mDevicePolicyManager.setApplicationRestrictions(ADMIN_RECEIVER_COMPONENT, CTS_PACKAGE,
-                new Bundle());
+        mDevicePolicyManager.setApplicationRestrictions(
+                ADMIN_RECEIVER_COMPONENT, APP_RESTRICTIONS_TARGET_PKG, new Bundle());
         assertTrue(mDevicePolicyManager.getApplicationRestrictions(
-                ADMIN_RECEIVER_COMPONENT, CTS_PACKAGE).isEmpty());
-        assertTrue(waitForChangedRestriction().isEmpty());
-        mDevicePolicyManager.setApplicationRestrictions(ADMIN_RECEIVER_COMPONENT, OTHER_PACKAGE,
-                new Bundle());
+                ADMIN_RECEIVER_COMPONENT, APP_RESTRICTIONS_TARGET_PKG).isEmpty());
+        assertTrue(waitForRestrictionsValueFromTestActivity().isEmpty());
+
+        mDevicePolicyManager.setApplicationRestrictions(
+                ADMIN_RECEIVER_COMPONENT, OTHER_PACKAGE, null);
         assertTrue(mDevicePolicyManager.getApplicationRestrictions(
                 ADMIN_RECEIVER_COMPONENT, OTHER_PACKAGE).isEmpty());
+    }
 
-        finish();
+    public void testCanRetrieveOwnRestrictionsViaUserManager() {
+        final String packageName = mContext.getPackageName();
+
+        mDevicePolicyManager.setApplicationRestrictions(
+                ADMIN_RECEIVER_COMPONENT, packageName, BUNDLE_0);
+        assertBundle0(mDevicePolicyManager.getApplicationRestrictions(
+                ADMIN_RECEIVER_COMPONENT, packageName));
+
+        // Check that we got the restrictions changed callback.
+        assertBundle0(waitForRestrictionsChangedBroadcast());
+
+        mDevicePolicyManager.setApplicationRestrictions(
+                ADMIN_RECEIVER_COMPONENT, packageName, BUNDLE_1);
+        assertBundle1(mDevicePolicyManager.getApplicationRestrictions(
+                ADMIN_RECEIVER_COMPONENT, packageName));
+        assertBundle1(waitForRestrictionsChangedBroadcast());
+    }
+
+    public void testCannotRetrieveOtherPackageRestrictionsViaUserManager() {
+        try {
+            mUserManager.getApplicationRestrictions(OTHER_PACKAGE);
+            fail("Expected SecurityException not thrown");
+        } catch (SecurityException expected) {
+        }
+    }
+
+    public void testSetApplicationRestrictionsManagingPackage() {
+        final String previousValue = mDevicePolicyManager.getApplicationRestrictionsManagingPackage(
+                ADMIN_RECEIVER_COMPONENT);
+        try {
+            mDevicePolicyManager.setApplicationRestrictionsManagingPackage(
+                    ADMIN_RECEIVER_COMPONENT, OTHER_PACKAGE);
+            assertEquals(OTHER_PACKAGE,
+                    mDevicePolicyManager.getApplicationRestrictionsManagingPackage(
+                            ADMIN_RECEIVER_COMPONENT));
+            mDevicePolicyManager.setApplicationRestrictionsManagingPackage(
+                    ADMIN_RECEIVER_COMPONENT, null);
+            assertNull(mDevicePolicyManager.getApplicationRestrictionsManagingPackage(
+                    ADMIN_RECEIVER_COMPONENT));
+        } finally {
+            mDevicePolicyManager.setApplicationRestrictionsManagingPackage(
+                    ADMIN_RECEIVER_COMPONENT, previousValue);
+            assertEquals(previousValue,
+                    mDevicePolicyManager.getApplicationRestrictionsManagingPackage(
+                            ADMIN_RECEIVER_COMPONENT));
+        }
     }
 
     // Should be consistent with assertBundle0
-    private Bundle createBundle0() {
+    private static Bundle createBundle0() {
         Bundle result = new Bundle();
         // Tests for 6 allowed types: Integer, Boolean, String, String[], Bundle and Parcelable[]
         // Also test for string escaping handling
@@ -123,7 +216,7 @@
         // If a null is stored, "" will be read back
         result.putString("empty", "");
         result.putString("string", "text");
-        result.putStringArray("string[]", testStrings);
+        result.putStringArray("string[]", TEST_STRINGS);
 
         // Adding a bundle, which contain 2 nested restrictions - bundle_string and bundle_int
         Bundle bundle = new Bundle();
@@ -153,9 +246,9 @@
         assertEquals("text", bundle.getString("string"));
 
         String[] strings = bundle.getStringArray("string[]");
-        assertTrue(strings != null && strings.length == testStrings.length);
+        assertTrue(strings != null && strings.length == TEST_STRINGS.length);
         for (int i = 0; i < strings.length; i++) {
-            assertEquals(strings[i], testStrings[i]);
+            assertEquals(strings[i], TEST_STRINGS[i]);
         }
 
         Bundle childBundle = bundle.getBundle("bundle");
@@ -177,7 +270,7 @@
     }
 
     // Should be consistent with assertBundle1
-    private Bundle createBundle1() {
+    private static Bundle createBundle1() {
         Bundle result = new Bundle();
         result.putInt("dummy", 1);
         return result;
@@ -189,53 +282,34 @@
         assertEquals(1, bundle.getInt("dummy"));
     }
 
-    private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            String action = intent.getAction();
-            if (ApplicationRestrictionsActivity.REGISTERED_ACTION.equals(action)) {
-                mOnRegisteredSemaphore.release();
-            } else if (ApplicationRestrictionsActivity.RESTRICTION_ACTION.equals(action)) {
-                mReceivedRestrictions = intent.getBundleExtra("value");
-                mOnRestrictionSemaphore.release();
-            }
-        }
-    };
-
-    private void startTestActivity(String command) {
-        Intent intent = new Intent();
-        intent.setClassName(PACKAGE_NAME, ApplicationRestrictionsActivity.class.getName());
-        intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_NEW_TASK);
-
-        if (command != null) {
-            intent.putExtra(command, true);
-        }
-        mContext.startActivity(intent);
+    private void startTestActivity() {
+        mContext.startActivity(new Intent()
+                .setComponent(new ComponentName(
+                        APP_RESTRICTIONS_TARGET_PKG, APP_RESTRICTIONS_ACTIVITY_NAME))
+                .addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_NEW_TASK));
     }
 
-    private void startAndWait() {
-        startTestActivity(null);
-        // Wait until the activity has registered its broadcast receiver and ready for incoming
-        // restriction changes.
-        try {
-            assertTrue(mOnRegisteredSemaphore.tryAcquire(5, TimeUnit.SECONDS));
-        } catch (InterruptedException e) {
-            fail("Start ApplicationRestrictionActivity interrupted");
-        }
-    }
+    private Bundle waitForRestrictionsValueFromTestActivity() {
+        startTestActivity();
 
-    private Bundle waitForChangedRestriction() {
         try {
-            assertTrue(mOnRestrictionSemaphore.tryAcquire(5, TimeUnit.SECONDS));
+            assertTrue(mOnRestrictionsReceivedFromAppSemaphore.tryAcquire(
+                    RESTRICTIONS_TIMEOUT_SECONDS, TimeUnit.SECONDS));
         } catch (InterruptedException e) {
-            fail("getRestrictionsAndWait() interrupted");
+            fail("waitForRestrictionsValueFromTestActivity() interrupted");
         }
 
         return mReceivedRestrictions;
     }
 
-    private void finish() {
-        startTestActivity(ApplicationRestrictionsActivity.FINISH);
-    }
+    private Bundle waitForRestrictionsChangedBroadcast() {
+        try {
+            assertTrue(mOnAppRestrictionsChangedSemahore.tryAcquire(
+                    RESTRICTIONS_TIMEOUT_SECONDS, TimeUnit.SECONDS));
+        } catch (InterruptedException e) {
+            fail("waitForRestrictionsChangedBroadcast() interrupted");
+        }
 
+        return mUserManager.getApplicationRestrictions(mContext.getPackageName());
+    }
 }
diff --git a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/BaseDeviceAdminTest.java b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/BaseDeviceAdminTest.java
index 148d8a8..18b922e 100644
--- a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/BaseDeviceAdminTest.java
+++ b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/BaseDeviceAdminTest.java
@@ -19,6 +19,7 @@
 import android.app.admin.DevicePolicyManager;
 import android.content.ComponentName;
 import android.content.Context;
+import android.os.UserManager;
 import android.test.InstrumentationTestCase;
 
 /**
@@ -37,6 +38,7 @@
             PACKAGE_NAME, BasicAdminReceiver.class.getName());
 
     protected DevicePolicyManager mDevicePolicyManager;
+    protected UserManager mUserManager;
     protected Context mContext;
 
     @Override
@@ -44,10 +46,12 @@
         super.setUp();
         mContext = getInstrumentation().getContext();
 
-        mDevicePolicyManager = (DevicePolicyManager)
-            mContext.getSystemService(Context.DEVICE_POLICY_SERVICE);
+        mDevicePolicyManager = mContext.getSystemService(DevicePolicyManager.class);
         assertNotNull(mDevicePolicyManager);
 
+        mUserManager = mContext.getSystemService(UserManager.class);
+        assertNotNull(mUserManager);
+
         assertTrue(mDevicePolicyManager.isAdminActive(ADMIN_RECEIVER_COMPONENT));
         assertTrue("App is neither device nor profile owner",
                 mDevicePolicyManager.isProfileOwnerApp(PACKAGE_NAME) ||
diff --git a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/ClearDeviceOwnerTest.java b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/ClearDeviceOwnerTest.java
index 2fe0569..a7bb762e 100644
--- a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/ClearDeviceOwnerTest.java
+++ b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/ClearDeviceOwnerTest.java
@@ -30,13 +30,12 @@
         mDevicePolicyManager = (DevicePolicyManager)
                 mContext.getSystemService(Context.DEVICE_POLICY_SERVICE);
         if (mDevicePolicyManager != null) {
-            removeActiveAdmin(BaseDeviceAdminTest.ADMIN_RECEIVER_COMPONENT);
             if (mDevicePolicyManager.isDeviceOwnerApp(BaseDeviceAdminTest.PACKAGE_NAME)) {
                 mDevicePolicyManager.clearDeviceOwnerApp(BaseDeviceAdminTest.PACKAGE_NAME);
             }
-            assertFalse(mDevicePolicyManager.isAdminActive(
-                    BaseDeviceAdminTest.ADMIN_RECEIVER_COMPONENT));
             assertFalse(mDevicePolicyManager.isDeviceOwnerApp(BaseDeviceAdminTest.PACKAGE_NAME));
+
+            Utils.assertNotActiveAdmin(getContext(), BaseDeviceAdminTest.ADMIN_RECEIVER_COMPONENT);
         }
 
         super.tearDown();
@@ -46,13 +45,4 @@
     // side test once a test case is finished.
     public void testClearDeviceOwner() {
     }
-
-    private void removeActiveAdmin(ComponentName cn) throws InterruptedException {
-        if (mDevicePolicyManager.isAdminActive(cn)) {
-            mDevicePolicyManager.removeActiveAdmin(cn);
-            for (int i = 0; i < 1000 && mDevicePolicyManager.isAdminActive(cn); i++) {
-                Thread.sleep(100);
-            }
-        }
-    }
 }
diff --git a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/ClearProfileOwnerNegativeTest.java b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/ClearProfileOwnerNegativeTest.java
new file mode 100644
index 0000000..30ae0cd
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/ClearProfileOwnerNegativeTest.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.deviceandprofileowner;
+
+import android.app.admin.DevicePolicyManager;
+import android.content.Context;
+import android.test.AndroidTestCase;
+import android.test.MoreAsserts;
+
+public class ClearProfileOwnerNegativeTest extends AndroidTestCase {
+
+    private DevicePolicyManager mDevicePolicyManager;
+
+    @Override
+    protected void tearDown() throws Exception {
+        mDevicePolicyManager = (DevicePolicyManager)
+                mContext.getSystemService(Context.DEVICE_POLICY_SERVICE);
+        if (mDevicePolicyManager.isProfileOwnerApp(BaseDeviceAdminTest.PACKAGE_NAME)) {
+            try {
+                mDevicePolicyManager.clearProfileOwner(BaseDeviceAdminTest.ADMIN_RECEIVER_COMPONENT);
+            } catch (SecurityException e) {
+                MoreAsserts.assertContainsRegex("clear profile owner", e.getMessage());
+            }
+        }
+
+        super.tearDown();
+    }
+
+    public void testClearProfileOwnerNegative() {
+    }
+}
diff --git a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/ClearProfileOwnerTest.java b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/ClearProfileOwnerTest.java
new file mode 100644
index 0000000..3e912e1
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/ClearProfileOwnerTest.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.deviceandprofileowner;
+
+import android.app.admin.DevicePolicyManager;
+import android.content.ComponentName;
+import android.content.Context;
+import android.test.AndroidTestCase;
+
+public class ClearProfileOwnerTest extends AndroidTestCase {
+
+    private DevicePolicyManager mDevicePolicyManager;
+
+    @Override
+    protected void tearDown() throws Exception {
+        mDevicePolicyManager = (DevicePolicyManager)
+                mContext.getSystemService(Context.DEVICE_POLICY_SERVICE);
+        if (mDevicePolicyManager != null) {
+            if (mDevicePolicyManager.isProfileOwnerApp(BaseDeviceAdminTest.PACKAGE_NAME)) {
+                mDevicePolicyManager.clearProfileOwner(
+                        BaseDeviceAdminTest.ADMIN_RECEIVER_COMPONENT);
+            }
+            assertFalse(mDevicePolicyManager.isProfileOwnerApp(BaseDeviceAdminTest.PACKAGE_NAME));
+
+            Utils.assertNotActiveAdmin(getContext(), BaseDeviceAdminTest.ADMIN_RECEIVER_COMPONENT);
+        }
+
+        super.tearDown();
+    }
+
+    // This test clears the profile owner and active admin on tearDown(). To be called from the host
+    // side test once a test case is finished.
+    public void testClearProfileOwner() {
+    }
+}
diff --git a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/ResetPasswordTest.java b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/ResetPasswordTest.java
new file mode 100644
index 0000000..635ff6d
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/ResetPasswordTest.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.cts.deviceandprofileowner;
+
+public class ResetPasswordTest extends BaseDeviceAdminTest {
+    public void testResetPassword() {
+        try {
+            // DO/PO can set a password.
+            mDevicePolicyManager.resetPassword("12345abcdef!!##1", 0);
+
+            // DO/PO can change the password, even if one is set already.
+            mDevicePolicyManager.resetPassword("12345abcdef!!##2", 0);
+        } finally {
+            // DO/PO can clear the password.
+            mDevicePolicyManager.resetPassword("", 0);
+        }
+    }
+}
diff --git a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/SetPolicyActivity.java b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/SetPolicyActivity.java
index 448f730..890daa3 100644
--- a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/SetPolicyActivity.java
+++ b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/SetPolicyActivity.java
@@ -33,11 +33,14 @@
     private static final String EXTRA_RESTRICTION_KEY = "extra-restriction-key";
     private static final String EXTRA_COMMAND = "extra-command";
     private static final String EXTRA_ACCOUNT_TYPE = "extra-account-type";
+    private static final String EXTRA_PACKAGE_NAME = "extra-package-name";
 
     private static final String COMMAND_ADD_USER_RESTRICTION = "add-restriction";
     private static final String COMMAND_CLEAR_USER_RESTRICTION = "clear-restriction";
     private static final String COMMAND_BLOCK_ACCOUNT_TYPE = "block-accounttype";
     private static final String COMMAND_UNBLOCK_ACCOUNT_TYPE = "unblock-accounttype";
+    private static final String COMMAND_SET_APP_RESTRICTIONS_MANAGER =
+            "set-app-restrictions-manager";
 
     @Override
     public void onCreate(Bundle savedInstanceState) {
@@ -89,6 +92,11 @@
                     accountType, false);
             Log.i(TAG, "Unblocking account management for account type: " + accountType
                     + " for user " + Process.myUserHandle());
+        } else if (COMMAND_SET_APP_RESTRICTIONS_MANAGER.equals(command)) {
+            String packageName = intent.getStringExtra(EXTRA_PACKAGE_NAME);
+            dpm.setApplicationRestrictionsManagingPackage(
+                    BaseDeviceAdminTest.ADMIN_RECEIVER_COMPONENT, packageName);
+            Log.i(TAG, "Setting the application restrictions managing package to " + packageName);
         } else {
             Log.e(TAG, "Invalid command: " + command);
         }
diff --git a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/SupportMessageTest.java b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/SupportMessageTest.java
new file mode 100644
index 0000000..4cd3537
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/SupportMessageTest.java
@@ -0,0 +1,183 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.deviceandprofileowner;
+
+import java.lang.Character;
+
+public class SupportMessageTest extends BaseDeviceAdminTest {
+
+    /**
+     * Longest allowed length of a short support message before the system may truncate it.
+     *
+     * Taken from documentation for
+     * {@link DevicePolicyManager#setShortSupportMessage(android.content.ComponentName, String)}.
+     */
+    private static final int MAX_SHORT_MSG_LENGTH = 200;
+
+    private static final int REASONABLE_LONG_MSG_LENGTH = 4000;
+
+    // Declare a different string of the same type for both long and short messages, so we can be
+    // sure they aren't mixed up by any API calls.
+    private static class ShortMessage {
+        static final String EMPTY = "";
+        static final String SIMPLE = "short-message-short";
+        static final String MAX_LENGTH =
+                new String(new char[MAX_SHORT_MSG_LENGTH]).replace('\0', 'X');
+        static final String TOO_LONG =
+                new String(new char[MAX_SHORT_MSG_LENGTH + 10]).replace('\0', 'A');
+        static final String UNICODE = new String(Character.toChars(0x1F634)) + " zzz";
+        static final String CONTAINS_NULL = "short\0null";
+    }
+    private static class LongMessage {
+        static final String EMPTY = "";
+        static final String SIMPLE = "long-message-long";
+        static final String LONG =
+                new String(new char[REASONABLE_LONG_MSG_LENGTH]).replace('\0', 'B');
+        static final String UNICODE = new String(Character.toChars(0x1F609)) + " ;)";
+        static final String CONTAINS_NULL = "long\0null";
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        clearSupportMessages();
+        super.tearDown();
+    }
+
+    public void testShortSupportMessageSetGetAndClear() {
+        setShortMessage(ShortMessage.SIMPLE);
+        setShortMessage(null);
+    }
+
+    public void testLongSupportMessageSetGetAndClear() {
+        setLongMessage(LongMessage.SIMPLE);
+        setLongMessage(null);
+    }
+
+    public void testLongAndShortMessagesDoNotClobber() {
+        setShortMessage(ShortMessage.SIMPLE);
+        setLongMessage(LongMessage.SIMPLE);
+
+        assertEquals(ShortMessage.SIMPLE, getShortMessage());
+        assertEquals(LongMessage.SIMPLE, getLongMessage());
+    }
+
+    public void testMaximumLengthPrefixIsSaved() {
+        // Save and restore a string of exactly the maximum length
+        setShortMessage(ShortMessage.MAX_LENGTH);
+
+        /*
+         * Save and restore a "short message" string that is too large -- this may only store the
+         * first N characters, not the whole thing, so we need to use {@link String#startsWith}
+         * here.
+         */
+        mDevicePolicyManager.setShortSupportMessage(ADMIN_RECEIVER_COMPONENT,
+                ShortMessage.TOO_LONG);
+        assertStartsWith(ShortMessage.TOO_LONG.substring(0, MAX_SHORT_MSG_LENGTH),
+                getShortMessage());
+
+        // Long support messages should not be affected; verify that.
+        mDevicePolicyManager.setLongSupportMessage(ADMIN_RECEIVER_COMPONENT, LongMessage.LONG);
+        assertEquals(LongMessage.LONG, getLongMessage());
+    }
+
+    public void testEmptySupportMessage() {
+        setShortMessage(ShortMessage.EMPTY);
+        setLongMessage(LongMessage.EMPTY);
+    }
+
+    public void testUnicodeCharactersInMessage() {
+        setShortMessage(ShortMessage.UNICODE);
+        setLongMessage(LongMessage.UNICODE);
+    }
+
+    public void testNullCharacterInMessage() {
+        setShortMessage(ShortMessage.CONTAINS_NULL);
+        setLongMessage(LongMessage.CONTAINS_NULL);
+    }
+
+    public void testSetOrGetSupportMessageWithNullAdminFails() {
+        // Short support message
+        try {
+            mDevicePolicyManager.setShortSupportMessage(null, ShortMessage.SIMPLE);
+            fail("Exception should have been thrown for null admin ComponentName");
+        } catch (NullPointerException expected) {
+        }
+        try {
+            String message = mDevicePolicyManager.getShortSupportMessage(null);
+            fail("Exception should have been thrown for null admin ComponentName");
+        } catch (NullPointerException expected) {
+        }
+
+        // Long support message
+        try {
+            mDevicePolicyManager.setLongSupportMessage(null, LongMessage.SIMPLE);
+            fail("Exception should have been thrown for null admin ComponentName");
+        } catch (NullPointerException expected) {
+        }
+
+        try {
+            String message = mDevicePolicyManager.getLongSupportMessage(null);
+            fail("Exception should have been thrown for null admin ComponentName");
+        } catch (NullPointerException expected) {
+        }
+    }
+
+    /**
+     * Delete all admin-set support messsages.
+     */
+    private void clearSupportMessages() {
+        setShortMessage(null);
+        setLongMessage(null);
+    }
+
+    /**
+     * Update the short support message.
+     *
+     * @throws AssertionError in the case that the message could not be set.
+     */
+    private void setShortMessage(String message) {
+        mDevicePolicyManager.setShortSupportMessage(ADMIN_RECEIVER_COMPONENT, message);
+        assertEquals(message, getShortMessage());
+    }
+
+    /**
+     * Update the long support message.
+     *
+     * @throws AssertionError in the case that the message could not be set.
+     */
+    private void setLongMessage(String message) {
+        mDevicePolicyManager.setLongSupportMessage(ADMIN_RECEIVER_COMPONENT, message);
+        assertEquals(message, getLongMessage());
+    }
+
+    private String getShortMessage() {
+        return mDevicePolicyManager.getShortSupportMessage(ADMIN_RECEIVER_COMPONENT);
+    }
+
+    private String getLongMessage() {
+        return mDevicePolicyManager.getLongSupportMessage(ADMIN_RECEIVER_COMPONENT);
+    }
+
+    private static void assertStartsWith(String expectPrefix, String actual) {
+        assertNotNull(expectPrefix);
+        assertNotNull(actual);
+        if (!actual.startsWith(expectPrefix)) {
+            fail("Expected prefix: '" + expectPrefix + "'\n" +
+                 "            got: '" + actual + "'");
+        }
+    }
+}
diff --git a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/Utils.java b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/Utils.java
new file mode 100644
index 0000000..fefc2ce
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/Utils.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.cts.deviceandprofileowner;
+
+import android.app.admin.DevicePolicyManager;
+import android.content.ComponentName;
+import android.content.Context;
+
+import junit.framework.Assert;
+
+public class Utils {
+    private Utils() {
+    }
+
+    public static void removeActiveAdmin(Context context, ComponentName cn) throws Exception {
+        final DevicePolicyManager dpm = context.getSystemService(DevicePolicyManager.class);
+        if (dpm.isAdminActive(cn)) {
+            dpm.removeActiveAdmin(cn);
+            assertNotActiveAdmin(context, cn);
+        }
+    }
+
+    public static void assertNotActiveAdmin(Context context, ComponentName cn) throws Exception {
+        final DevicePolicyManager dpm = context.getSystemService(DevicePolicyManager.class);
+
+        for (int i = 0; i < 1000 && dpm.isAdminActive(cn); i++) {
+            Thread.sleep(100);
+        }
+        Assert.assertFalse(dpm.isAdminActive(cn));
+    }
+}
diff --git a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/userrestrictions/BaseUserRestrictionsTest.java b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/userrestrictions/BaseUserRestrictionsTest.java
new file mode 100644
index 0000000..a7d8110
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/userrestrictions/BaseUserRestrictionsTest.java
@@ -0,0 +1,219 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.cts.deviceandprofileowner.userrestrictions;
+
+import android.os.UserManager;
+
+import com.android.cts.deviceandprofileowner.BaseDeviceAdminTest;
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+
+public abstract class BaseUserRestrictionsTest extends BaseDeviceAdminTest {
+    protected static final String[] ALL_USER_RESTRICTIONS = new String[]{
+            UserManager.DISALLOW_CONFIG_WIFI,
+            UserManager.DISALLOW_MODIFY_ACCOUNTS,
+            UserManager.DISALLOW_INSTALL_APPS,
+            UserManager.DISALLOW_UNINSTALL_APPS,
+            UserManager.DISALLOW_SHARE_LOCATION,
+            UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES,
+            UserManager.DISALLOW_CONFIG_BLUETOOTH,
+            UserManager.DISALLOW_USB_FILE_TRANSFER,
+            UserManager.DISALLOW_CONFIG_CREDENTIALS,
+            UserManager.DISALLOW_REMOVE_USER,
+            UserManager.DISALLOW_DEBUGGING_FEATURES,
+            UserManager.DISALLOW_CONFIG_VPN,
+            UserManager.DISALLOW_CONFIG_TETHERING,
+            UserManager.DISALLOW_NETWORK_RESET,
+            UserManager.DISALLOW_FACTORY_RESET,
+            UserManager.DISALLOW_ADD_USER,
+            UserManager.ENSURE_VERIFY_APPS,
+            UserManager.DISALLOW_CONFIG_CELL_BROADCASTS,
+            UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS,
+            UserManager.DISALLOW_APPS_CONTROL,
+            UserManager.DISALLOW_MOUNT_PHYSICAL_MEDIA,
+            UserManager.DISALLOW_UNMUTE_MICROPHONE,
+            UserManager.DISALLOW_ADJUST_VOLUME,
+            UserManager.DISALLOW_OUTGOING_CALLS,
+            UserManager.DISALLOW_SMS,
+            UserManager.DISALLOW_FUN,
+            UserManager.DISALLOW_CREATE_WINDOWS,
+            UserManager.DISALLOW_CROSS_PROFILE_COPY_PASTE,
+            UserManager.DISALLOW_OUTGOING_BEAM,
+            UserManager.DISALLOW_SAFE_BOOT,
+            UserManager.ALLOW_PARENT_PROFILE_APP_LINKING,
+            UserManager.DISALLOW_DATA_ROAMING,
+            UserManager.DISALLOW_SET_USER_ICON
+    };
+
+    /**
+     * Restrictions that affect all users when DO sets.
+     */
+    protected static final String[] DO_GLOBAL_RESTRICTIONS = new String[] {
+            UserManager.DISALLOW_USB_FILE_TRANSFER,
+            UserManager.DISALLOW_CONFIG_TETHERING,
+            UserManager.DISALLOW_NETWORK_RESET,
+            UserManager.DISALLOW_FACTORY_RESET,
+            UserManager.DISALLOW_ADD_USER,
+            UserManager.DISALLOW_CONFIG_CELL_BROADCASTS,
+            UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS,
+            UserManager.DISALLOW_MOUNT_PHYSICAL_MEDIA,
+            UserManager.DISALLOW_SMS,
+            UserManager.DISALLOW_FUN,
+            UserManager.DISALLOW_SAFE_BOOT,
+            UserManager.DISALLOW_CREATE_WINDOWS,
+            // UserManager.DISALLOW_DATA_ROAMING, // Not set during CTS
+
+            // PO can set them too, but when DO sets them, they're global.
+            UserManager.DISALLOW_ADJUST_VOLUME,
+            UserManager.DISALLOW_UNMUTE_MICROPHONE
+    };
+
+    public static final String[] HIDDEN_AND_PROHIBITED = new String[] {
+            "no_record_audio",
+            "no_wallpaper"
+    };
+
+    protected void assertLayeredRestriction(String restriction, boolean expected) {
+        assertEquals("Restriction " + restriction + ": expected=" + expected,
+                expected, mUserManager.hasUserRestriction(restriction));
+    }
+
+    protected void assertOwnerRestriction(String restriction, boolean expected) {
+        assertEquals("Restriction " + restriction + ": expected=" + expected,
+                expected, mDevicePolicyManager.getUserRestrictions(ADMIN_RECEIVER_COMPONENT)
+                        .getBoolean(restriction));
+    }
+
+    protected void assertRestrictions(Set<String> expected) {
+        for (String r : ALL_USER_RESTRICTIONS) {
+            assertLayeredRestriction(r, expected.contains(r));
+        }
+    }
+
+    /**
+     * Test that the given restriction can be set and cleared, then leave it set again.
+     */
+    protected void assertSetClearUserRestriction(String restriction) {
+        final boolean hadRestriction = mUserManager.hasUserRestriction(restriction);
+
+        assertOwnerRestriction(restriction, false);
+
+        // Set.  Shouldn't throw.
+        mDevicePolicyManager.addUserRestriction(ADMIN_RECEIVER_COMPONENT, restriction);
+
+        assertOwnerRestriction(restriction, true);
+        assertLayeredRestriction(restriction, true);
+
+        // Then clear.
+        assertClearUserRestriction(restriction);
+
+        assertLayeredRestriction(restriction, hadRestriction);
+
+        // Then set again.
+        mDevicePolicyManager.addUserRestriction(ADMIN_RECEIVER_COMPONENT, restriction);
+    }
+
+    /**
+     * Test that the given restriction can be cleared.  (and leave it cleared.)
+     */
+    protected void assertClearUserRestriction(String restriction) {
+        mDevicePolicyManager.clearUserRestriction(ADMIN_RECEIVER_COMPONENT, restriction);
+
+        assertOwnerRestriction(restriction, false);
+    }
+
+    /**
+     * Test that the given restriction *cannot* be set (or clear).
+     */
+    protected void assertCannotSetUserRestriction(String restriction) {
+        final boolean hadRestriction = mUserManager.hasUserRestriction(restriction);
+
+        assertOwnerRestriction(restriction, false);
+
+        // Set should fail.
+        try {
+            mDevicePolicyManager.addUserRestriction(ADMIN_RECEIVER_COMPONENT, restriction);
+            fail("Restriction=" + restriction);
+        } catch (SecurityException e) {
+            assertTrue("Restriction=" + restriction + " Message was: " + e.getMessage(),
+                    e.getMessage().contains("cannot set user restriction"));
+        }
+
+        // Shouldn't have changed.
+        assertOwnerRestriction(restriction, false);
+        assertLayeredRestriction(restriction, hadRestriction);
+
+        // Clear should fail too.
+        try {
+            mDevicePolicyManager.clearUserRestriction(ADMIN_RECEIVER_COMPONENT, restriction);
+            fail("Restriction=" + restriction);
+        } catch (SecurityException e) {
+            assertTrue("Restriction=" + restriction + " Message was: " + e.getMessage(),
+                    e.getMessage().contains("cannot set user restriction"));
+        }
+
+        // Shouldn't have changed.
+        assertOwnerRestriction(restriction, false);
+        assertLayeredRestriction(restriction, hadRestriction);
+    }
+
+    /** For {@link #testSetAllRestrictions} */
+    protected abstract String[] getAllowedRestrictions();
+
+    /** For {@link #testSetAllRestrictions} */
+    protected abstract String[] getDisallowedRestrictions();
+
+    /**
+     * Set only one restriction, and make sure only that's set, and then clear it.
+     */
+    public void testSetAllRestrictionsIndividually() {
+        for (String r : getAllowedRestrictions()) {
+            // Set it.
+            assertSetClearUserRestriction(r);
+
+            assertRestrictions(new HashSet<>(Arrays.asList(new String[]{r})));
+
+            // Then clear it.
+            assertClearUserRestriction(r);
+        }
+    }
+
+    /**
+     * Make sure all allowed restrictions can be set, and the others can't.
+     */
+    public void testSetAllRestrictions() {
+        for (String r : getAllowedRestrictions()) {
+            assertSetClearUserRestriction(r);
+        }
+        for (String r : getDisallowedRestrictions()) {
+            assertCannotSetUserRestriction(r);
+        }
+        for (String r : HIDDEN_AND_PROHIBITED) {
+            assertCannotSetUserRestriction(r);
+        }
+    }
+
+    /**
+     * Clear all allowed restrictions.
+     */
+    public void testClearAllRestrictions() {
+        for (String r : getAllowedRestrictions()) {
+            assertClearUserRestriction(r);
+        }
+    }
+}
diff --git a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/userrestrictions/CheckNoOwnerRestrictionsTest.java b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/userrestrictions/CheckNoOwnerRestrictionsTest.java
new file mode 100644
index 0000000..14e5f84
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/userrestrictions/CheckNoOwnerRestrictionsTest.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.cts.deviceandprofileowner.userrestrictions;
+
+import android.os.UserManager;
+import android.test.AndroidTestCase;
+
+/**
+ * Used after after DO/PO are removed to make sure user restrictions set by them are no longer
+ * set.
+ */
+public class CheckNoOwnerRestrictionsTest extends AndroidTestCase {
+    public void testNoOwnerRestrictions() {
+        assertFalse(mContext.getSystemService(UserManager.class).hasUserRestriction(
+                UserManager.DISALLOW_UNMUTE_MICROPHONE));
+    }
+}
diff --git a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/userrestrictions/DeviceOwnerUserRestrictionsTest.java b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/userrestrictions/DeviceOwnerUserRestrictionsTest.java
new file mode 100644
index 0000000..1ff9b6c
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/userrestrictions/DeviceOwnerUserRestrictionsTest.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.cts.deviceandprofileowner.userrestrictions;
+
+import android.os.UserManager;
+
+public class DeviceOwnerUserRestrictionsTest extends BaseUserRestrictionsTest {
+    public static final String[] ALLOWED = new String[] {
+            // UserManager.DISALLOW_CONFIG_WIFI, // Has unrecoverable side effects.
+            UserManager.DISALLOW_MODIFY_ACCOUNTS,
+            UserManager.DISALLOW_INSTALL_APPS,
+            UserManager.DISALLOW_UNINSTALL_APPS,
+            // UserManager.DISALLOW_SHARE_LOCATION, // Has unrecoverable side effects.
+            // UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES, // Has unrecoverable side effects.
+            UserManager.DISALLOW_CONFIG_BLUETOOTH,
+            UserManager.DISALLOW_USB_FILE_TRANSFER,
+            UserManager.DISALLOW_CONFIG_CREDENTIALS,
+            UserManager.DISALLOW_REMOVE_USER,
+            // UserManager.DISALLOW_DEBUGGING_FEATURES, // Need for CTS
+            UserManager.DISALLOW_CONFIG_VPN,
+            UserManager.DISALLOW_CONFIG_TETHERING,
+            UserManager.DISALLOW_NETWORK_RESET,
+            UserManager.DISALLOW_FACTORY_RESET,
+            UserManager.DISALLOW_ADD_USER,
+            // UserManager.ENSURE_VERIFY_APPS, // Has unrecoverable side effects.
+            UserManager.DISALLOW_CONFIG_CELL_BROADCASTS,
+            UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS,
+            UserManager.DISALLOW_APPS_CONTROL,
+            UserManager.DISALLOW_MOUNT_PHYSICAL_MEDIA,
+            UserManager.DISALLOW_UNMUTE_MICROPHONE,
+            UserManager.DISALLOW_ADJUST_VOLUME,
+            UserManager.DISALLOW_OUTGOING_CALLS,
+            UserManager.DISALLOW_SMS,
+            UserManager.DISALLOW_FUN,
+            UserManager.DISALLOW_CREATE_WINDOWS,
+            UserManager.DISALLOW_CROSS_PROFILE_COPY_PASTE,
+            UserManager.DISALLOW_OUTGOING_BEAM,
+            UserManager.DISALLOW_SAFE_BOOT,
+            UserManager.ALLOW_PARENT_PROFILE_APP_LINKING,
+            // UserManager.DISALLOW_DATA_ROAMING, // Has unrecoverable side effects.
+            UserManager.DISALLOW_SET_USER_ICON
+    };
+
+    public static final String[] DISALLOWED = new String[] {
+            // DO can set all public restrictions.
+    };
+
+    @Override
+    protected String[] getAllowedRestrictions() {
+        return ALLOWED;
+    }
+
+    @Override
+    protected String[] getDisallowedRestrictions() {
+        return DISALLOWED;
+    }
+}
+
diff --git a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/userrestrictions/PrimaryProfileOwnerUserRestrictionsTest.java b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/userrestrictions/PrimaryProfileOwnerUserRestrictionsTest.java
new file mode 100644
index 0000000..57c49cf
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/userrestrictions/PrimaryProfileOwnerUserRestrictionsTest.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.cts.deviceandprofileowner.userrestrictions;
+
+public class PrimaryProfileOwnerUserRestrictionsTest extends BaseUserRestrictionsTest {
+    @Override
+    protected String[] getAllowedRestrictions() {
+        // PO on user-0 can set DO user restrictions too. (for now?)
+        return DeviceOwnerUserRestrictionsTest.ALLOWED;
+    }
+
+    @Override
+    protected String[] getDisallowedRestrictions() {
+        return DeviceOwnerUserRestrictionsTest.DISALLOWED;
+    }
+}
diff --git a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/userrestrictions/SecondaryProfileOwnerUserRestrictionsTest.java b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/userrestrictions/SecondaryProfileOwnerUserRestrictionsTest.java
new file mode 100644
index 0000000..8cd56bf
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/userrestrictions/SecondaryProfileOwnerUserRestrictionsTest.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.cts.deviceandprofileowner.userrestrictions;
+
+import android.os.UserManager;
+
+import java.util.Arrays;
+import java.util.HashSet;
+
+public class SecondaryProfileOwnerUserRestrictionsTest extends BaseUserRestrictionsTest {
+    public static final String[] ALLOWED = new String[] {
+            // UserManager.DISALLOW_CONFIG_WIFI, // Has unrecoverable side effects.
+            UserManager.DISALLOW_MODIFY_ACCOUNTS,
+            UserManager.DISALLOW_INSTALL_APPS,
+            UserManager.DISALLOW_UNINSTALL_APPS,
+            // UserManager.DISALLOW_SHARE_LOCATION, // Has unrecoverable side effects.
+            // UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES, // Has unrecoverable side effects.
+            UserManager.DISALLOW_CONFIG_BLUETOOTH,
+            UserManager.DISALLOW_CONFIG_CREDENTIALS,
+            UserManager.DISALLOW_REMOVE_USER,
+            // UserManager.DISALLOW_DEBUGGING_FEATURES, // Need for CTS
+            UserManager.DISALLOW_CONFIG_VPN,
+            // UserManager.ENSURE_VERIFY_APPS, // Has unrecoverable side effects.
+            UserManager.DISALLOW_APPS_CONTROL,
+            UserManager.DISALLOW_UNMUTE_MICROPHONE,
+            UserManager.DISALLOW_ADJUST_VOLUME,
+            UserManager.DISALLOW_OUTGOING_CALLS,
+            UserManager.DISALLOW_CROSS_PROFILE_COPY_PASTE,
+            UserManager.DISALLOW_OUTGOING_BEAM,
+            UserManager.ALLOW_PARENT_PROFILE_APP_LINKING,
+            UserManager.DISALLOW_SET_USER_ICON
+    };
+
+    public static final String[] DISALLOWED = new String[] {
+            UserManager.DISALLOW_USB_FILE_TRANSFER,
+            UserManager.DISALLOW_CONFIG_TETHERING,
+            UserManager.DISALLOW_NETWORK_RESET,
+            UserManager.DISALLOW_FACTORY_RESET,
+            UserManager.DISALLOW_ADD_USER,
+            UserManager.DISALLOW_CONFIG_CELL_BROADCASTS,
+            UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS,
+            UserManager.DISALLOW_MOUNT_PHYSICAL_MEDIA,
+            UserManager.DISALLOW_SMS,
+            UserManager.DISALLOW_FUN,
+            UserManager.DISALLOW_SAFE_BOOT,
+            UserManager.DISALLOW_CREATE_WINDOWS,
+            UserManager.DISALLOW_DATA_ROAMING
+    };
+
+    @Override
+    protected String[] getAllowedRestrictions() {
+        return ALLOWED;
+    }
+
+    @Override
+    protected String[] getDisallowedRestrictions() {
+        return DISALLOWED;
+    }
+
+    /**
+     * This is called after DO setting all DO restrictions.  Global restrictions should be
+     * visible on other users.
+     */
+    public void testHasGlobalRestrictions() {
+        assertRestrictions(new HashSet<>(Arrays.asList(DO_GLOBAL_RESTRICTIONS)));
+    }
+
+    /**
+     * This is called after DO setting all DO restrictions, and PO setting all PO restrictions.
+     * All global + local restrictions should be visible.
+     */
+    public void testHasBothGlobalAndLocalRestrictions() {
+        final HashSet<String> expected = new HashSet<>();
+
+        // Should see all global ones from DO.
+        expected.addAll(Arrays.asList(DO_GLOBAL_RESTRICTIONS));
+
+        // Should also see all global ones from itself.
+        expected.addAll(Arrays.asList(ALLOWED));
+
+        assertRestrictions(expected);
+    }
+
+    /**
+     * This is called after DO setting all DO restrictions, and PO setting all PO restrictions,
+     * then DO clearing all restrictions.  Only PO restrictions should be set.
+     */
+    public void testLocalRestrictionsOnly() {
+        // Now should only see the ones that are set by this PO.
+        assertRestrictions(new HashSet<>(Arrays.asList(ALLOWED)));
+    }
+
+    /**
+     * Only the default restrictions should be set.
+     */
+    public void testDefaultRestrictionsOnly() {
+        final HashSet<String> expected = new HashSet<>(
+                // No restrictions.
+        );
+
+        assertRestrictions(expected);
+    }
+}
diff --git a/hostsidetests/devicepolicy/app/DeviceOwner/Android.mk b/hostsidetests/devicepolicy/app/DeviceOwner/Android.mk
index d75e632..880c777 100644
--- a/hostsidetests/devicepolicy/app/DeviceOwner/Android.mk
+++ b/hostsidetests/devicepolicy/app/DeviceOwner/Android.mk
@@ -26,7 +26,7 @@
 
 LOCAL_JAVA_LIBRARIES := android.test.runner cts-junit
 
-LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner compatibility-device-util
+LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner compatibility-device-util ub-uiautomator
 
 LOCAL_SDK_VERSION := current
 
diff --git a/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/CreateAndManageUserTest.java b/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/CreateAndManageUserTest.java
new file mode 100644
index 0000000..3246401
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/CreateAndManageUserTest.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.deviceowner;
+
+import android.app.admin.DevicePolicyManager;
+
+import java.lang.reflect.Field;
+
+/**
+ * Test {@link DevicePolicyManager#createAndManageUser}.
+ *
+ * <p>The test creates users by calling to {@link DevicePolicyManager#createAndManageUser}, it
+ * doesn't remove the users afterwards, so their properties can be queried and tested by host-side
+ * tests.
+ */
+public class CreateAndManageUserTest extends BaseDeviceOwnerTest {
+
+    /**
+     * Test creating an ephemeral user using the {@link DevicePolicyManager#createAndManageUser}
+     * method.
+     *
+     * <p>The user's flags will be checked from the corresponding host-side test.
+     */
+    public void testCreateAndManageEphemeralUser() throws Exception {
+        String testUserName = "TestUser_" + System.currentTimeMillis();
+
+        // Use reflection to get the value of the hidden flag to make the new user ephemeral.
+        Field field = DevicePolicyManager.class.getField("MAKE_USER_EPHEMERAL");
+        int makeEphemeralFlag = field.getInt(null);
+
+        mDevicePolicyManager.createAndManageUser(
+                getWho(),
+                testUserName,
+                getWho(),
+                null,
+                makeEphemeralFlag);
+    }
+
+}
diff --git a/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/DeviceLoggingTest.java b/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/DeviceLoggingTest.java
new file mode 100644
index 0000000..642915f
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/DeviceLoggingTest.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.cts.deviceowner;
+
+import android.auditing.SecurityLog.SecurityEvent;
+import android.os.UserHandle;
+
+import java.util.List;
+
+public class DeviceLoggingTest extends BaseDeviceOwnerTest {
+
+    private static final String MESSAGE_ONLY_ONE_MANAGED_USER_ALLOWED =
+            "There should only be one user, managed by Device Owner";
+
+    /**
+     * Test: setting device logging can only be done if there's one user on the device.
+     */
+    public void testSetDeviceLoggingEnabledNotPossibleIfMoreThanOneUserPresent() {
+        try {
+            mDevicePolicyManager.setDeviceLoggingEnabled(getWho(), true);
+            fail("did not throw expected SecurityException");
+        } catch (SecurityException e) {
+            assertEquals(e.getMessage(), MESSAGE_ONLY_ONE_MANAGED_USER_ALLOWED);
+        }
+    }
+
+    /**
+     * Test: retrieving device logs can only be done if there's one user on the device.
+     */
+    public void testRetrievingDeviceLogsNotPossibleIfMoreThanOneUserPresent() {
+        try {
+            mDevicePolicyManager.retrieveDeviceLogs(getWho());
+            fail("did not throw expected SecurityException");
+        } catch (SecurityException e) {
+            assertEquals(e.getMessage(), MESSAGE_ONLY_ONE_MANAGED_USER_ALLOWED);
+        }
+    }
+
+    /**
+     * Test: retrieving previous device logs can only be done if there's one user on the device.
+     */
+    public void testRetrievingPreviousDeviceLogsNotPossibleIfMoreThanOneUserPresent() {
+        try {
+            mDevicePolicyManager.retrievePreviousDeviceLogs(getWho());
+            fail("did not throw expected SecurityException");
+        } catch (SecurityException e) {
+            assertEquals(e.getMessage(), MESSAGE_ONLY_ONE_MANAGED_USER_ALLOWED);
+        }
+    }
+
+    /**
+     * Test: retrieving device logs should be rate limited - subsequent attempts should return null.
+     * TODO(mkarpinski): consider how we can test that the rate limiting is set to 2 hours.
+     */
+    public void testRetrievingDeviceLogsNotPossibleImmediatelyAfterPreviousSuccessfulRetrieval() {
+        List<SecurityEvent> logs = mDevicePolicyManager.retrieveDeviceLogs(getWho());
+        // if logs is null it means that that attempt was rate limited => test PASS
+        if (logs != null) {
+            assertNull(mDevicePolicyManager.retrieveDeviceLogs(getWho()));
+            assertNull(mDevicePolicyManager.retrieveDeviceLogs(getWho()));
+        }
+    }
+}
diff --git a/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/ForceEphemeralUsersTest.java b/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/ForceEphemeralUsersTest.java
new file mode 100644
index 0000000..a5f59d4
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/ForceEphemeralUsersTest.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.deviceowner;
+
+import android.app.admin.DevicePolicyManager;
+import android.content.ComponentName;
+
+import java.lang.reflect.Method;
+
+/**
+ * Test {@link DevicePolicyManager#getForceEphemeralUser} and
+ * {@link DevicePolicyManager#setForceEphemeralUser}.
+ *
+ * <p>The test toggles force-ephemeral-user policy on and leaves it that way which enables
+ * combining it with additional host-side tests.
+ */
+public class ForceEphemeralUsersTest extends BaseDeviceOwnerTest {
+
+    /**
+     * Test setting and subsequently getting the force-ephemeral-user policy.
+     */
+    public void testSetForceEphemeralUsers() throws Exception {
+        Method setForceEphemeralUsersMethod = DevicePolicyManager.class.getDeclaredMethod(
+                "setForceEphemeralUsers", ComponentName.class, boolean.class);
+        setForceEphemeralUsersMethod.invoke(mDevicePolicyManager, getWho(), true);
+
+        Method getForceEphemeralUsersMethod = DevicePolicyManager.class.getDeclaredMethod(
+                "getForceEphemeralUsers", ComponentName.class);
+        assertTrue((boolean) getForceEphemeralUsersMethod.invoke(mDevicePolicyManager, getWho()));
+    }
+
+}
diff --git a/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/LockScreenInfoTest.java b/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/LockScreenInfoTest.java
new file mode 100644
index 0000000..7d7b57c
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/LockScreenInfoTest.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.deviceowner;
+
+import java.lang.Character;
+
+public class LockScreenInfoTest extends BaseDeviceOwnerTest {
+
+    @Override
+    public void tearDown() throws Exception {
+        mDevicePolicyManager.setDeviceOwnerLockScreenInfo(getWho(), null);
+        super.tearDown();
+    }
+
+    public void testSetAndGetLockInfo() {
+        setLockInfo("testSetAndGet");
+    }
+
+    public void testClearLockInfo() {
+        setLockInfo("testClear");
+        setLockInfo(null);
+
+    }
+
+    public void testEmptyStringClearsLockInfo() {
+        final String message = "";
+        assertTrue(mDevicePolicyManager.setDeviceOwnerLockScreenInfo(getWho(), message));
+        assertNull(mDevicePolicyManager.getDeviceOwnerLockScreenInfo());
+    }
+
+    public void testWhitespaceOnly() {
+        setLockInfo("\t");
+    }
+
+    public void testUnicode() {
+        final String smiley = new String(Character.toChars(0x1F601));
+        final String phone = new String(Character.toChars(0x1F4F1));
+        setLockInfo(smiley + phone + "\t" + phone + smiley);
+    }
+
+    public void testNullInString() {
+        setLockInfo("does \0 this \1 work?");
+    }
+
+    public void testReasonablyLongString() {
+        final int messageLength = 128;
+        setLockInfo(new String(new char[messageLength]).replace('\0', 'Z'));
+    }
+
+    public void testSetLockInfoWithNullAdminFails() {
+        final String message = "nulladmin";
+
+        // Set message
+        try {
+            mDevicePolicyManager.setDeviceOwnerLockScreenInfo(null, message);
+            fail("Exception should have been thrown for null admin ComponentName");
+        } catch (NullPointerException expected) {
+        }
+    }
+
+    /**
+     * Sets device owner lock screen info on behalf of the current device owner admin.
+     *
+     * @throws AssertionError if the setting did not take effect.
+     */
+    private void setLockInfo(String message) {
+        assertTrue(mDevicePolicyManager.setDeviceOwnerLockScreenInfo(getWho(), message));
+        assertEquals(message, mDevicePolicyManager.getDeviceOwnerLockScreenInfo());
+    }
+}
diff --git a/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/RemoteBugreportTest.java b/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/RemoteBugreportTest.java
new file mode 100644
index 0000000..3cdaef0
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/RemoteBugreportTest.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.cts.deviceowner;
+
+import android.app.Instrumentation;
+import android.app.admin.DevicePolicyManager;
+import android.content.ComponentName;
+import android.content.Context;
+import android.support.test.uiautomator.By;
+import android.support.test.uiautomator.UiDevice;
+import android.support.test.uiautomator.UiObject;
+import android.support.test.uiautomator.UiObjectNotFoundException;
+import android.support.test.uiautomator.UiSelector;
+import android.support.test.uiautomator.Until;
+import android.test.InstrumentationTestCase;
+
+/**
+ * Test class for remote bugreports.
+ *
+ * This class also handles making sure that the test is the device owner
+ * and that it has an active admin registered, so that all tests may
+ * assume these are done. The admin component can be accessed through
+ * {@link BaseDeviceOwnerTest#getWho()}.
+ */
+public class RemoteBugreportTest extends InstrumentationTestCase {
+
+    private static final int UI_TIMEOUT = 5000; //5 seconds
+
+    private static final String MESSAGE_ONLY_ONE_MANAGED_USER_ALLOWED =
+            "There should only be one user, managed by Device Owner";
+
+    private static final String TAKING_BUG_REPORT = "Taking bug report";
+
+    private DevicePolicyManager mDevicePolicyManager;
+    private Context mContext;
+    private UiDevice mUiDevice;
+    private ComponentName mComponentName;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        Instrumentation instrumentation = getInstrumentation();
+        mContext = instrumentation.getTargetContext();
+        mUiDevice = UiDevice.getInstance(instrumentation);
+        mDevicePolicyManager = (DevicePolicyManager)
+                mContext.getSystemService(Context.DEVICE_POLICY_SERVICE);
+        BaseDeviceOwnerTest.assertDeviceOwner(mDevicePolicyManager);
+        mComponentName = BaseDeviceOwnerTest.getWho();
+    }
+
+    /**
+     * Test: only one remote bugreport flow can be running on the device at one time.
+     */
+    public void testSubsequentRemoteBugreportThrottled() {
+        boolean startedSuccessfully = mDevicePolicyManager.requestBugreport(mComponentName);
+        assertTrue(startedSuccessfully);
+
+        // subsequent attempts should be throttled
+        assertFalse(mDevicePolicyManager.requestBugreport(mComponentName));
+        assertFalse(mDevicePolicyManager.requestBugreport(mComponentName));
+
+        cancelRemoteBugreportFlowIfStartedSuccessfully(startedSuccessfully);
+    }
+
+    /**
+     * Test: remote bugreport flow can only be started if there's one user on the device.
+     */
+    public void testRequestBugreportNotStartedIfMoreThanOneUserPresent() {
+        boolean startedSuccessfully = false;
+        try {
+            startedSuccessfully = mDevicePolicyManager.requestBugreport(mComponentName);
+            fail("did not throw expected SecurityException");
+        } catch (SecurityException e) {
+            assertEquals(e.getMessage(), MESSAGE_ONLY_ONE_MANAGED_USER_ALLOWED);
+        } finally {
+            cancelRemoteBugreportFlowIfStartedSuccessfully(startedSuccessfully);
+        }
+    }
+
+    /**
+     * Clicks on "Taking bugreport..." notification to cancel the whole
+     * remote bugreport flow (including stopping the dumpstate service).
+     */
+    private void cancelRemoteBugreportFlowIfStartedSuccessfully(boolean startedSuccessfully) {
+        if (!startedSuccessfully) {
+            return;
+        }
+        mUiDevice.openNotification();
+        // give it max 5 seconds to find the notification
+        boolean notificationPresent = mUiDevice.wait(
+                Until.hasObject(By.textStartsWith(TAKING_BUG_REPORT)), UI_TIMEOUT);
+        assertTrue(notificationPresent);
+
+        UiObject bugreportNotification = mUiDevice.findObject(
+                new UiSelector().textStartsWith(TAKING_BUG_REPORT));
+        assertNotNull(bugreportNotification);
+        try {
+            bugreportNotification.click();
+        } catch (UiObjectNotFoundException e) {
+            throw new IllegalStateException(
+                    "Exception when clicking on 'taking bugreport' notification", e);
+        }
+    }
+}
diff --git a/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/ContactsTest.java b/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/ContactsTest.java
index a606c72..5a59aa3 100644
--- a/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/ContactsTest.java
+++ b/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/ContactsTest.java
@@ -598,21 +598,33 @@
 
     public void testPrimaryProfileEnterpriseCallableFilter_canAccessPrimaryDirectories() {
         assertFalse(isManagedProfile());
+        callableFilterAccessDirectoryInternal(PRIMARY_CONTACT_PHONE,
+                PRIMARY_CONTACT_DISPLAY_NAME, getPrimaryRemoteDirectoryId(),
+                PRIMARY_DIRECTORY_CONTACT_NAME);
+    }
 
+    public void testManagedProfileEnterpriseCallableFilter_canAccessManagedDirectories() {
+        assertTrue(isManagedProfile());
+        callableFilterAccessDirectoryInternal(MANAGED_CONTACT_PHONE, MANAGED_CONTACT_DISPLAY_NAME,
+                getEnterpriseRemoteDirectoryIdInManagedProfile(), MANAGED_DIRECTORY_CONTACT_NAME);
+    }
+
+    private void callableFilterAccessDirectoryInternal(String phone, String displayName,
+                                                       long remoteDirectoryId,
+                                                       String directoryDisplayName) {
         // local directory
         final ContactInfo defaultContactInfo
                 = getContactInfoFromEnterpriseCallableFilterUriInDirectory(
-                PRIMARY_CONTACT_PHONE, Directory.DEFAULT);
+                phone, Directory.DEFAULT);
         assertNotNull(defaultContactInfo);
-        assertEquals(PRIMARY_CONTACT_DISPLAY_NAME, defaultContactInfo.displayName);
+        assertEquals(displayName, defaultContactInfo.displayName);
 
         // remote directory
-        final long directoryId = getPrimaryRemoteDirectoryId();
         final ContactInfo directoryContactInfo
                 = getContactInfoFromEnterpriseCallableFilterUriInDirectory(
-                PRIMARY_CONTACT_PHONE, directoryId);
+                displayName, remoteDirectoryId);
         assertNotNull(directoryContactInfo);
-        assertEquals(PRIMARY_DIRECTORY_CONTACT_NAME, directoryContactInfo.displayName);
+        assertEquals(directoryDisplayName, directoryContactInfo.displayName);
     }
 
     public void testPrimaryProfileEnterpriseCallableFilter_canAccessManagedDirectories() {
@@ -658,21 +670,32 @@
 
     public void testPrimaryProfileEnterpriseEmailFilter_canAccessPrimaryDirectories() {
         assertFalse(isManagedProfile());
+        emailFilterCanAccessDirectoriesInternal(
+                PRIMARY_CONTACT_EMAIL, PRIMARY_CONTACT_DISPLAY_NAME,
+                getPrimaryRemoteDirectoryId(), PRIMARY_DIRECTORY_CONTACT_NAME);
+    }
 
+    public void testEnterpriseProfileEnterpriseEmailFilter_canAccessManagedDirectories() {
+        assertTrue(isManagedProfile());
+        emailFilterCanAccessDirectoriesInternal(
+                MANAGED_CONTACT_EMAIL, MANAGED_CONTACT_DISPLAY_NAME,
+                getEnterpriseRemoteDirectoryIdInManagedProfile(), MANAGED_DIRECTORY_CONTACT_NAME);
+    }
+
+    public void emailFilterCanAccessDirectoriesInternal(String email, String displayName,
+                                                        long remoteDirectoryId,
+                                                        String directoryDisplayName) {
         // local directory
         final ContactInfo defaultContactInfo
-                = getContactInfoFromEnterpriseEmailFilterUriInDirectory(
-                PRIMARY_CONTACT_EMAIL, Directory.DEFAULT);
+                = getContactInfoFromEnterpriseEmailFilterUriInDirectory(email, Directory.DEFAULT);
         assertNotNull(defaultContactInfo);
-        assertEquals(PRIMARY_CONTACT_DISPLAY_NAME, defaultContactInfo.displayName);
+        assertEquals(displayName, defaultContactInfo.displayName);
 
         // remote directory
-        final long directoryId = getPrimaryRemoteDirectoryId();
         final ContactInfo directoryContactInfo
-                = getContactInfoFromEnterpriseEmailFilterUriInDirectory(
-                PRIMARY_CONTACT_EMAIL, directoryId);
+                = getContactInfoFromEnterpriseEmailFilterUriInDirectory(email, remoteDirectoryId);
         assertNotNull(directoryContactInfo);
-        assertEquals(PRIMARY_DIRECTORY_CONTACT_NAME, directoryContactInfo.displayName);
+        assertEquals(directoryDisplayName, directoryContactInfo.displayName);
     }
 
     public void testPrimaryProfileEnterpriseEmailFilter_canAccessManagedDirectories() {
@@ -718,21 +741,32 @@
 
     public void testPrimaryProfileEnterpriseContactFilter_canAccessPrimaryDirectories() {
         assertFalse(isManagedProfile());
+        contactFilterCanAccessPrimaryDirectoriesInternal(PRIMARY_CONTACT_DISPLAY_NAME,
+                getPrimaryRemoteDirectoryId(), PRIMARY_DIRECTORY_CONTACT_NAME);
+    }
 
+    public void testManagedProfileEnterpriseContactFilter_canAccessManagedDirectories() {
+        assertTrue(isManagedProfile());
+        contactFilterCanAccessPrimaryDirectoriesInternal(MANAGED_CONTACT_DISPLAY_NAME,
+                getEnterpriseRemoteDirectoryIdInManagedProfile(), MANAGED_DIRECTORY_CONTACT_NAME);
+    }
+
+    public void contactFilterCanAccessPrimaryDirectoriesInternal(String displayName,
+                                                                 long remoteDirectoryId,
+                                                                 String directoryDisplayName) {
         // local directory
         final ContactInfo defaultContactInfo
                 = getContactInfoFromEnterpriseContactFilterUriInDirectory(
-                PRIMARY_CONTACT_DISPLAY_NAME, Directory.DEFAULT);
+                displayName, Directory.DEFAULT);
         assertNotNull(defaultContactInfo);
-        assertEquals(PRIMARY_CONTACT_DISPLAY_NAME, defaultContactInfo.displayName);
+        assertEquals(displayName, defaultContactInfo.displayName);
 
         // remote directory
-        final long directoryId = getPrimaryRemoteDirectoryId();
         final ContactInfo directoryContactInfo
                 = getContactInfoFromEnterpriseEmailFilterUriInDirectory(
-                PRIMARY_CONTACT_DISPLAY_NAME, directoryId);
+                displayName, remoteDirectoryId);
         assertNotNull(directoryContactInfo);
-        assertEquals(PRIMARY_DIRECTORY_CONTACT_NAME, directoryContactInfo.displayName);
+        assertEquals(directoryDisplayName, directoryContactInfo.displayName);
     }
 
     public void testPrimaryProfileEnterpriseContactFilter_canAccessManagedDirectories() {
@@ -778,21 +812,32 @@
 
     public void testPrimaryProfileEnterprisePhoneFilter_canAccessPrimaryDirectories() {
         assertFalse(isManagedProfile());
+        phoneFilterCanAccessPrimaryDirectoriesInternal(
+                PRIMARY_CONTACT_PHONE, PRIMARY_CONTACT_DISPLAY_NAME,
+                getPrimaryRemoteDirectoryId(), PRIMARY_DIRECTORY_CONTACT_NAME);
+    }
 
+    public void testManagedProfileEnterprisePhoneFilter_canAccessManagedDirectories() {
+        assertTrue(isManagedProfile());
+        phoneFilterCanAccessPrimaryDirectoriesInternal(
+                MANAGED_CONTACT_PHONE, MANAGED_CONTACT_DISPLAY_NAME,
+                getEnterpriseRemoteDirectoryIdInManagedProfile(), MANAGED_DIRECTORY_CONTACT_NAME);
+    }
+
+    public void phoneFilterCanAccessPrimaryDirectoriesInternal(String phone, String displayName,
+                                                               long remoteDirectoryId,
+                                                               String directoryDisplayName) {
         // local directory
         final ContactInfo defaultContactInfo
-                = getContactInfoFromEnterprisePhoneFilterUriInDirectory(
-                PRIMARY_CONTACT_PHONE, Directory.DEFAULT);
+                = getContactInfoFromEnterprisePhoneFilterUriInDirectory(phone, Directory.DEFAULT);
         assertNotNull(defaultContactInfo);
-        assertEquals(PRIMARY_CONTACT_DISPLAY_NAME, defaultContactInfo.displayName);
+        assertEquals(displayName, defaultContactInfo.displayName);
 
         // remote directory
-        final long directoryId = getPrimaryRemoteDirectoryId();
         final ContactInfo directoryContactInfo
-                = getContactInfoFromEnterprisePhoneFilterUriInDirectory(
-                PRIMARY_CONTACT_PHONE, directoryId);
+                = getContactInfoFromEnterprisePhoneFilterUriInDirectory(phone, remoteDirectoryId);
         assertNotNull(directoryContactInfo);
-        assertEquals(PRIMARY_DIRECTORY_CONTACT_NAME, directoryContactInfo.displayName);
+        assertEquals(directoryDisplayName, directoryContactInfo.displayName);
     }
 
     public void testPrimaryProfileEnterprisePhoneFilter_canAccessManagedDirectories() {
@@ -840,7 +885,7 @@
         assertFalse(isManagedProfile());
 
         final Cursor cursor = mResolver.query(Directory.ENTERPRISE_CONTENT_URI,
-                new String[] { Directory._ID }, null, null, null);
+                new String[]{Directory._ID}, null, null, null);
         try {
             while (cursor.moveToNext()) {
                 final long directoryId = cursor.getLong(0);
@@ -900,6 +945,15 @@
 
     private long getPrimaryRemoteDirectoryId() {
         assertFalse(isManagedProfile());
+        return getRemoteDirectoryIdInternal();
+    }
+
+    private long getEnterpriseRemoteDirectoryIdInManagedProfile() {
+        assertTrue(isManagedProfile());
+        return getRemoteDirectoryIdInternal();
+    }
+
+    private long getRemoteDirectoryIdInternal() {
         final Cursor cursor = mResolver.query(Directory.ENTERPRISE_CONTENT_URI,
                 new String[]{
                         Directory._ID
diff --git a/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/OrganizationInfoTest.java b/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/OrganizationInfoTest.java
new file mode 100644
index 0000000..d426ba4
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/OrganizationInfoTest.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.managedprofile;
+
+import android.graphics.Color;
+
+public class OrganizationInfoTest extends BaseManagedProfileTest {
+
+    public void testDefaultOrganizationColorIsGray() {
+        int defaultColor = mDevicePolicyManager.getOrganizationColor(ADMIN_RECEIVER_COMPONENT);
+        assertEquals(Color.GRAY, defaultColor);
+    }
+
+    public void testSetOrganizationColor() {
+        int previousColor = mDevicePolicyManager.getOrganizationColor(ADMIN_RECEIVER_COMPONENT);
+
+        try {
+            final int[] colors = {
+                Color.TRANSPARENT,
+                Color.WHITE,
+                Color.RED,
+                Color.GREEN,
+                Color.BLUE,
+                0x7FFE5B35 /* HTML name: "Sunset orange". Opacity: 50%. */
+            };
+
+            for (int color : colors) {
+                mDevicePolicyManager.setOrganizationColor(ADMIN_RECEIVER_COMPONENT, color);
+                assertEquals(color,
+                        mDevicePolicyManager.getOrganizationColor(ADMIN_RECEIVER_COMPONENT));
+            }
+        } finally {
+            // Put the organization color back how it was.
+            mDevicePolicyManager.setOrganizationColor(ADMIN_RECEIVER_COMPONENT, previousColor);
+        }
+    }
+
+    public void testSetOrGetOrganizationColorWithNullAdminFails() {
+        try {
+            mDevicePolicyManager.setOrganizationColor(null, Color.GRAY);
+            fail("Exception should have been thrown for null admin ComponentName");
+        } catch (NullPointerException expected) {
+        }
+
+        try {
+            int color = mDevicePolicyManager.getOrganizationColor(null);
+            fail("Exception should have been thrown for null admin ComponentName");
+        } catch (NullPointerException expected) {
+        }
+    }
+}
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/BaseDevicePolicyTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/BaseDevicePolicyTest.java
index 6ee1aec..1114888 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/BaseDevicePolicyTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/BaseDevicePolicyTest.java
@@ -51,15 +51,26 @@
 
     private static final String RUNNER = "android.support.test.runner.AndroidJUnitRunner";
 
-    static final int USER_SYSTEM = 0; // From the UserHandle class.
+    protected static final int USER_SYSTEM = 0; // From the UserHandle class.
 
-    private static final int FLAG_PRIMARY = 1; // From the UserInfo class
+    protected static final int USER_OWNER = 0;
+
+    // From the UserInfo class
+    protected static final int FLAG_PRIMARY = 0x00000001;
+    protected static final int FLAG_GUEST = 0x00000004;
+    protected static final int FLAG_EPHEMERAL = 0x00000100;
 
     protected IBuildInfo mCtsBuild;
 
     private String mPackageVerifier;
     private HashSet<String> mAvailableFeatures;
+
+    /** Whether DPM is supported. */
     protected boolean mHasFeature;
+
+    /** Whether multi-user is supported. */
+    protected boolean mSupportsMultiUser;
+
     private ArrayList<Integer> mOriginalUsers;
 
     @Override
@@ -73,6 +84,8 @@
         assertNotNull(mCtsBuild);  // ensure build has been set before test is run.
         mHasFeature = getDevice().getApiLevel() >= 21 /* Build.VERSION_CODES.L */
                 && hasDeviceFeature("android.software.device_admin");
+        mSupportsMultiUser = getMaxNumberOfUsersSupported() > 1;
+
         // disable the package verifier to avoid the dialog when installing an app
         mPackageVerifier = getDevice().executeShellCommand(
                 "settings get global package_verifier_enable");
@@ -199,8 +212,11 @@
         stopUser(userId);
         String removeUserCommand = "pm remove-user " + userId;
         CLog.logAndDisplay(LogLevel.INFO, "starting command " + removeUserCommand);
+
+        String removeUserOutput = getDevice().executeShellCommand(removeUserCommand);
         CLog.logAndDisplay(LogLevel.INFO, "Output for command " + removeUserCommand + ": "
-                + getDevice().executeShellCommand(removeUserCommand));
+                + removeUserOutput);
+        assertTrue("Couldn't remove user", removeUserOutput.startsWith("Success:"));
     }
 
     protected void removeTestUsers() throws Exception {
@@ -275,6 +291,12 @@
         return false;
     }
 
+    /** Checks whether it is possible to create the desired number of users. */
+    protected boolean canCreateAdditionalUsers(int numberOfUsers)
+            throws DeviceNotAvailableException {
+        return listUsers().size() + numberOfUsers <= getMaxNumberOfUsersSupported();
+    }
+
     /** Helper method to run tests and return the listener that collected the results. */
     private TestRunResult doRunTests(
             String pkgName, String testClassName,
@@ -307,7 +329,7 @@
         }
         String command = "am instrument --user " + userId + " " + params + " -w -r "
                 + testsToRun + " " + pkgName + "/" + RUNNER;
-        CLog.i("Running " + command);
+        CLog.logAndDisplay(LogLevel.INFO, "Running " + command);
 
         CollectingTestListener listener = new CollectingTestListener();
         InstrumentationResultParser parser = new InstrumentationResultParser(pkgName, listener);
@@ -354,12 +376,14 @@
     }
 
     protected int createUser() throws Exception {
-        return createUser(false);
+        return createUser(0);
     }
 
-    protected int createUser(boolean ephemeral) throws Exception {
-        String command ="pm create-user " + (ephemeral ? "--ephemeral " : "")
-                + "TestUser_" + System.currentTimeMillis();
+    protected int createUser(int flags) throws Exception {
+        boolean guest = FLAG_GUEST == (flags & FLAG_GUEST);
+        boolean ephemeral = FLAG_EPHEMERAL == (flags & FLAG_EPHEMERAL);
+        String command ="pm create-user " + (guest ? "--guest " : "")
+                + (ephemeral ? "--ephemeral " : "") + "TestUser_" + System.currentTimeMillis();
         CLog.logAndDisplay(LogLevel.INFO, "Starting command " + command);
         String commandOutput = getDevice().executeShellCommand(command);
         CLog.logAndDisplay(LogLevel.INFO, "Output for command " + command + ": " + commandOutput);
@@ -441,15 +465,30 @@
         }
     }
 
-    protected void setDeviceAdmin(String componentName, int userId)
+    private String setDeviceAdminInner(String componentName, int userId)
             throws DeviceNotAvailableException {
         String command = "dpm set-active-admin --user " + userId + " '" + componentName + "'";
         String commandOutput = getDevice().executeShellCommand(command);
-        CLog.logAndDisplay(LogLevel.INFO, "Output for command " + command + ": " + commandOutput);
+        return commandOutput;
+    }
+
+    protected void setDeviceAdmin(String componentName, int userId)
+            throws DeviceNotAvailableException {
+        String commandOutput = setDeviceAdminInner(componentName, userId);
+        CLog.logAndDisplay(LogLevel.INFO, "Output for command " + commandOutput
+                + ": " + commandOutput);
         assertTrue(commandOutput + " expected to start with \"Success:\"",
                 commandOutput.startsWith("Success:"));
     }
 
+    protected void setDeviceAdminExpectingFailure(String componentName, int userId,
+            String errorMessage) throws DeviceNotAvailableException {
+        String commandOutput = setDeviceAdminInner(componentName, userId);
+        if (!commandOutput.contains(errorMessage)) {
+            fail(commandOutput + " expected to contain \"" + errorMessage + "\"");
+        }
+    }
+
     protected boolean setDeviceOwner(String componentName) throws DeviceNotAvailableException {
         String command = "dpm set-device-owner '" + componentName + "'";
         String commandOutput = getDevice().executeShellCommand(command);
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAdminHostSideTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAdminHostSideTest.java
new file mode 100644
index 0000000..7750fef
--- /dev/null
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAdminHostSideTest.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.cts.devicepolicy;
+
+import com.android.tradefed.device.DeviceNotAvailableException;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+/**
+ * DeviceAdmin host side tests.
+ *
+ * TODO Add tests for device admin targeting API 23.
+ */
+public class DeviceAdminHostSideTest extends BaseDevicePolicyTest {
+    private static final String DEVICE_ADMIN_APK = "CtsDeviceAdminApp.apk";
+
+    private static final String DEVICE_ADMIN_PKG = "com.android.cts.deviceadmin";
+
+    private static final String ADMIN_RECEIVER_TEST_CLASS = "DeviceAdminTest$AdminReceiver";
+
+    private static final String ADMIN_RECEIVER_COMPONENT =
+            DEVICE_ADMIN_PKG + "/." + ADMIN_RECEIVER_TEST_CLASS;
+
+    private static final String UNPROTECTED_ADMIN_RECEIVER_TEST_CLASS =
+            "DeviceAdminReceiverWithNoProtection";
+
+    private int mUserId;
+
+    private boolean mDeactivateInTearDown;
+    private boolean mClearPasswordInTearDown;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+
+        mUserId = USER_OWNER;
+
+        mDeactivateInTearDown = false;
+        mClearPasswordInTearDown = false;
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        if (mHasFeature) {
+            // If a password has been set, we need to register the DA as DO to clear it.
+            if (mClearPasswordInTearDown) {
+                setDeviceOwner(ADMIN_RECEIVER_COMPONENT);
+
+                assertTrue("Failed to clear password",
+                        runTests(DEVICE_ADMIN_PKG, "DeviceAdminTest", "testClearPassword_success"));
+
+                assertTrue("Failed to clear device owner",
+                        runTests(DEVICE_ADMIN_PKG, "ClearDeviceOwnerTest"));
+            }
+
+            if (mDeactivateInTearDown) {
+                assertTrue("Failed to remove device admin", runTests(
+                        DEVICE_ADMIN_PKG, "ClearDeviceAdminTest"));
+            }
+        }
+
+        super.tearDown();
+    }
+
+    /** DA can only set a password when there's none, and can't clear it. */
+    public void testResetPassword() throws Exception {
+        if (!mHasFeature) {
+            return;
+        }
+
+        mDeactivateInTearDown = true;
+        mClearPasswordInTearDown = true;
+
+        installApp(DEVICE_ADMIN_APK);
+        setDeviceAdmin(ADMIN_RECEIVER_COMPONENT, mUserId);
+
+        // Can't clear the password, even if there's no passsword set currently.
+        assertTrue(runTests(DEVICE_ADMIN_PKG, "DeviceAdminTest", "testClearPassword_failure"));
+
+        // No password -> setting one is okay.
+        assertTrue(runTests(DEVICE_ADMIN_PKG, "DeviceAdminTest", "testSetPassword_success"));
+
+        // But once set, DA can't change the password.
+        assertTrue(runTests(DEVICE_ADMIN_PKG, "DeviceAdminTest", "testSetPassword_failure"));
+    }
+
+    /** Device admin must be protected with BIND_DEVICE_ADMIN */
+    public void testAdminWithNoProtection() throws Exception {
+        if (!mHasFeature) {
+            return;
+        }
+
+        installApp(DEVICE_ADMIN_APK);
+        setDeviceAdminExpectingFailure(DEVICE_ADMIN_PKG + "/." +
+                UNPROTECTED_ADMIN_RECEIVER_TEST_CLASS, mUserId,
+                "must be protected with android.permission.BIND_DEVICE_ADMIN");
+    }
+
+    private boolean runTests(@Nonnull String apk, @Nonnull String className,
+            @Nullable String method) throws DeviceNotAvailableException {
+        return runDeviceTestsAsUser(apk, "." + className, method, mUserId);
+    }
+
+    private boolean runTests(@Nonnull String apk, @Nonnull String className)
+            throws DeviceNotAvailableException {
+        return runTests(apk, className, null);
+    }
+}
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAndProfileOwnerTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAndProfileOwnerTest.java
index bc8d9c2..bd96052 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAndProfileOwnerTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAndProfileOwnerTest.java
@@ -24,9 +24,12 @@
 import java.io.File;
 
 /**
- * Set of tests for usecases that apply to profile and device owner.
+ * Set of tests for use cases that apply to profile and device owner.
  * This class is the base class of MixedProfileOwnerTest and MixedDeviceOwnerTest and is abstract
  * to avoid running spurious tests.
+ *
+ * NOTE: Not all tests are executed in the subclasses.  Sometimes, if a test is not applicable to
+ * a subclass, they override it with an empty method.
  */
 public abstract class DeviceAndProfileOwnerTest extends BaseDevicePolicyTest {
 
@@ -41,6 +44,14 @@
     private static final String SIMPLE_PRE_M_APP_PKG = "com.android.cts.launcherapps.simplepremapp";
     private static final String SIMPLE_PRE_M_APP_APK = "CtsSimplePreMApp.apk";
 
+    private static final String APP_RESTRICTIONS_MANAGING_APP_PKG
+            = "com.android.cts.apprestrictions.managingapp";
+    private static final String APP_RESTRICTIONS_MANAGING_APP_APK
+            = "CtsAppRestrictionsManagingApp.apk";
+    private static final String APP_RESTRICTIONS_TARGET_APP_PKG
+            = "com.android.cts.apprestrictions.targetapp";
+    private static final String APP_RESTRICTIONS_TARGET_APP_APK = "CtsAppRestrictionsTargetApp.apk";
+
     private static final String CERT_INSTALLER_PKG = "com.android.cts.certinstaller";
     private static final String CERT_INSTALLER_APK = "CtsCertInstallerApp.apk";
 
@@ -55,8 +66,6 @@
             = "com.android.cts.devicepolicy.accountmanagement";
     private static final String ACCOUNT_MANAGEMENT_APK = "CtsAccountManagementDevicePolicyApp.apk";
 
-    protected static final int USER_OWNER = 0;
-
     private static final String COMMAND_ADD_USER_RESTRICTION = "add-restriction";
     private static final String COMMAND_CLEAR_USER_RESTRICTION = "clear-restriction";
     private static final String COMMAND_BLOCK_ACCOUNT_TYPE = "block-accounttype";
@@ -76,17 +85,61 @@
             getDevice().uninstallPackage(DEVICE_ADMIN_PKG);
             getDevice().uninstallPackage(PERMISSIONS_APP_PKG);
             getDevice().uninstallPackage(SIMPLE_PRE_M_APP_PKG);
+            getDevice().uninstallPackage(APP_RESTRICTIONS_MANAGING_APP_PKG);
+            getDevice().uninstallPackage(APP_RESTRICTIONS_TARGET_APP_PKG);
             getDevice().uninstallPackage(CERT_INSTALLER_PKG);
             getDevice().uninstallPackage(ACCOUNT_MANAGEMENT_PKG);
         }
         super.tearDown();
     }
 
+    public void testResetPassword() throws Exception {
+        if (!mHasFeature) {
+            return;
+        }
+        executeDeviceTestClass(".ResetPasswordTest");
+    }
+
     public void testApplicationRestrictions() throws Exception {
         if (!mHasFeature) {
             return;
         }
-        executeDeviceTestClass(".ApplicationRestrictionsTest");
+
+        installAppAsUser(APP_RESTRICTIONS_MANAGING_APP_APK, mUserId);
+        installAppAsUser(APP_RESTRICTIONS_TARGET_APP_APK, mUserId);
+
+        try {
+            // Only the DPC can manage app restrictions by default.
+            executeDeviceTestClass(".ApplicationRestrictionsTest");
+            executeAppRestrictionsManagingPackageTest("testCannotManageAppRestrictions");
+
+            // Letting the APP_RESTRICTIONS_MANAGING_APP_PKG manage app restrictions too.
+            changeApplicationRestrictionsManagingPackage(APP_RESTRICTIONS_MANAGING_APP_PKG);
+            executeAppRestrictionsManagingPackageTest("testCanManageAppRestrictions");
+            executeAppRestrictionsManagingPackageTest("testSettingComponentNameThrowsException");
+
+            // The DPC should still be able to manage app restrictions normally.
+            executeDeviceTestClass(".ApplicationRestrictionsTest");
+
+            // The app shouldn't be able to manage app restrictions for other users.
+            int parentUserId = getPrimaryUser();
+            if (parentUserId != mUserId) {
+                installAppAsUser(APP_RESTRICTIONS_MANAGING_APP_APK, parentUserId);
+                installAppAsUser(APP_RESTRICTIONS_TARGET_APP_APK, parentUserId);
+                assertTrue(runDeviceTestsAsUser(
+                        APP_RESTRICTIONS_MANAGING_APP_PKG, ".ApplicationRestrictionsManagerTest",
+                        "testCannotManageAppRestrictions", parentUserId));
+            }
+
+            // Revoking the permission for APP_RESTRICTIONS_MANAGING_APP_PKG to manage restrictions.
+            changeApplicationRestrictionsManagingPackage(null);
+            executeAppRestrictionsManagingPackageTest("testCannotManageAppRestrictions");
+
+            // The DPC should still be able to manage app restrictions normally.
+            executeDeviceTestClass(".ApplicationRestrictionsTest");
+        } finally {
+            changeApplicationRestrictionsManagingPackage(null);
+        }
     }
 
     public void testPermissionGrant() throws Exception {
@@ -183,6 +236,13 @@
         }
     }
 
+    public void testSupportMessage() throws Exception {
+        if (!mHasFeature) {
+            return;
+        }
+        executeDeviceTestClass(".SupportMessageTest");
+    }
+
     public void testApplicationHidden() throws Exception {
         if (!mHasFeature) {
             return;
@@ -354,6 +414,11 @@
         CLog.i("Output for command " + command + ": " + getDevice().executeShellCommand(command));
     }
 
+    private void executeAppRestrictionsManagingPackageTest(String testName) throws Exception {
+        assertTrue(runDeviceTestsAsUser(APP_RESTRICTIONS_MANAGING_APP_PKG,
+                ".ApplicationRestrictionsManagerTest", testName, mUserId));
+    }
+
     private void changeUserRestrictionForUser(String key, String command, int userId)
             throws DeviceNotAvailableException {
         changePolicy(command, "--es extra-restriction-key " + key, userId);
@@ -364,6 +429,13 @@
         changePolicy(command, "--es extra-account-type " + accountType, userId);
     }
 
+    private void changeApplicationRestrictionsManagingPackage(String packageName)
+            throws DeviceNotAvailableException {
+        String packageNameExtra = (packageName != null)
+                ? "--es extra-package-name " + packageName : "";
+        changePolicy("set-app-restrictions-manager", packageNameExtra, mUserId);
+    }
+
     private void changePolicy(String command, String extras, int userId)
             throws DeviceNotAvailableException {
         String adbCommand = "am start -W --user " + userId
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerTest.java
index 96ca469..2d235f5 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerTest.java
@@ -16,6 +16,8 @@
 
 package com.android.cts.devicepolicy;
 
+import java.util.ArrayList;
+
 /**
  * Set of tests for Device Owner use cases.
  */
@@ -57,6 +59,8 @@
             assertTrue("Failed to remove device owner.",
                     runDeviceTests(DEVICE_OWNER_PKG, CLEAR_DEVICE_OWNER_TEST_CLASS));
             getDevice().uninstallPackage(DEVICE_OWNER_PKG);
+            switchUser(0);
+            removeTestUsers();
         }
 
         super.tearDown();
@@ -74,6 +78,173 @@
         executeDeviceOwnerTest("KeyManagementTest");
     }
 
+    public void testLockScreenInfo() throws Exception {
+        executeDeviceOwnerTest("LockScreenInfoTest");
+    }
+
+    public void testRemoteBugreportWithTwoUsers() throws Exception {
+        if (!mHasFeature || getMaxNumberOfUsersSupported() < 2) {
+            return;
+        }
+        int userId = -1;
+        try {
+            userId = createUser();
+            executeDeviceTestMethod(".RemoteBugreportTest",
+                    "testRequestBugreportNotStartedIfMoreThanOneUserPresent");
+        } finally {
+            removeUser(userId);
+        }
+    }
+
+    public void testRemoteBugreportWithSingleUser() throws Exception {
+        executeDeviceTestMethod(".RemoteBugreportTest", "testSubsequentRemoteBugreportThrottled");
+    }
+
+    /** Tries to toggle the force-ephemeral-users on and checks it was really set. */
+    public void testSetForceEphemeralUsers() throws Exception {
+        if (!mHasFeature || getDevice().getApiLevel() < 24 /* Build.VERSION_CODES.N */
+                || !canCreateAdditionalUsers(1)) {
+            return;
+        }
+        // Set force-ephemeral-users policy and verify it was set.
+        executeDeviceOwnerTest("ForceEphemeralUsersTest");
+    }
+
+    /**
+     * All users (except of the system user) must be removed after toggling the
+     * force-ephemeral-users policy to true.
+     *
+     * <p>If the current user is the system user, the other users are removed straight away.
+     */
+    public void testRemoveUsersOnSetForceEphemeralUsers() throws Exception {
+        if (!mHasFeature || getDevice().getApiLevel() < 24 /* Build.VERSION_CODES.N */
+                || !canCreateAdditionalUsers(1)) {
+            return;
+        }
+
+        // Create a user.
+        int userId = createUser();
+        assertTrue("User must have been created", listUsers().contains(userId));
+
+        // Set force-ephemeral-users policy and verify it was set.
+        executeDeviceOwnerTest("ForceEphemeralUsersTest");
+
+        // Users have to be removed when force-ephemeral-users is toggled on.
+        assertFalse("User must have been removed", listUsers().contains(userId));
+    }
+
+    /**
+     * All users (except of the system user) must be removed after toggling the
+     * force-ephemeral-users policy to true.
+     *
+     * <p>If the current user is not the system user, switching to the system user should happen
+     * before all other users are removed.
+     */
+    public void testRemoveUsersOnSetForceEphemeralUsersWithUserSwitch() throws Exception {
+        if (!mHasFeature || getDevice().getApiLevel() < 24 /* Build.VERSION_CODES.N */
+                || !canCreateAdditionalUsers(1)) {
+            return;
+        }
+
+        // Create a user.
+        int userId = createUser();
+        assertTrue("User must have been created", listUsers().contains(userId));
+
+        // Switch to the new (non-system) user.
+        switchUser(userId);
+
+        // Set force-ephemeral-users policy and verify it was set.
+        executeDeviceOwnerTestAsUser("ForceEphemeralUsersTest", 0);
+
+        // Make sure the user has been removed. As it is not a synchronous operation - switching to
+        // the system user must happen first - give the system a little bit of time for finishing
+        // it.
+        final int sleepMs = 500;
+        final int maxSleepMs = 10000;
+        for (int totalSleptMs = 0; totalSleptMs < maxSleepMs; totalSleptMs += sleepMs) {
+            // Wait a little while for the user's removal.
+            Thread.sleep(sleepMs);
+
+            if (!listUsers().contains(userId)) {
+                // Success - the user has been removed.
+                return;
+            }
+        }
+
+        // The user hasn't been removed within the given time.
+        fail("User must have been removed");
+    }
+
+    /** The users created after setting force-ephemeral-users policy to true must be ephemeral. */
+    public void testCreateUserAfterSetForceEphemeralUsers() throws Exception {
+        if (!mHasFeature || getDevice().getApiLevel() < 24 /* Build.VERSION_CODES.N */
+                || !canCreateAdditionalUsers(1)) {
+            return;
+        }
+
+        // Set force-ephemeral-users policy and verify it was set.
+        executeDeviceOwnerTest("ForceEphemeralUsersTest");
+
+        int userId = createUser();
+        assertTrue("User must be ephemeral", 0 != (getUserFlags(userId) & FLAG_EPHEMERAL));
+    }
+
+    /**
+     * Test creating an epehemeral user using the DevicePolicyManager's createAndManageUser method.
+     */
+    public void testCreateAndManageEphemeralUser() throws Exception {
+        if (!mHasFeature || getDevice().getApiLevel() < 24 /* Build.VERSION_CODES.N */
+                || !canCreateAdditionalUsers(1)) {
+            return;
+        }
+
+        ArrayList<Integer> originalUsers = listUsers();
+        assertTrue(runDeviceTests(DEVICE_OWNER_PKG, ".CreateAndManageUserTest",
+                "testCreateAndManageEphemeralUser", 0));
+
+        ArrayList<Integer> newUsers = listUsers();
+
+        // Check that exactly one new user was created.
+        assertEquals(
+                "One user should have been created", originalUsers.size() + 1, newUsers.size());
+
+        // Get the id of the newly created user.
+        int newUserId = -1;
+        for (int userId : newUsers) {
+            if (!originalUsers.contains(userId)) {
+                newUserId = userId;
+                break;
+            }
+        }
+
+        // Get the flags of the new user and check the user is ephemeral.
+        int flags = getUserFlags(newUserId);
+        assertEquals("Ephemeral flag must be set", FLAG_EPHEMERAL, flags & FLAG_EPHEMERAL);
+    }
+
+    public void testDeviceLoggingWithTwoUsers() throws Exception {
+        if (!mHasFeature || getMaxNumberOfUsersSupported() < 2) {
+            return;
+        }
+        int userId = -1;
+        try {
+            userId = createUser();
+            executeDeviceTestMethod(".DeviceLoggingTest",
+                    "testSetDeviceLoggingEnabledNotPossibleIfMoreThanOneUserPresent");
+            executeDeviceTestMethod(".DeviceLoggingTest",
+                    "testRetrievingDeviceLogsNotPossibleIfMoreThanOneUserPresent");
+            executeDeviceTestMethod(".DeviceLoggingTest",
+                    "testRetrievingPreviousDeviceLogsNotPossibleIfMoreThanOneUserPresent");
+        } finally {
+            removeUser(userId);
+        }
+    }
+
+    public void testDeviceLoggingWithSingleUser() throws Exception {
+        executeDeviceTestMethod(".DeviceLoggingTest",
+                "testRetrievingDeviceLogsNotPossibleImmediatelyAfterPreviousSuccessfulRetrieval");
+    }
+
     public void testLockTask() throws Exception {
         try {
             installApp(INTENT_RECEIVER_APK);
@@ -122,4 +293,17 @@
         String testClass = DEVICE_OWNER_PKG + "." + testClassName;
         assertTrue(testClass + " failed.", runDeviceTests(DEVICE_OWNER_PKG, testClass));
     }
+
+    private void executeDeviceTestMethod(String className, String testName) throws Exception {
+        assertTrue(runDeviceTestsAsUser(DEVICE_OWNER_PKG, className, testName,
+                /* deviceOwnerUserId */ 0));
+    }
+
+    private void executeDeviceOwnerTestAsUser(String testClassName, int userId) throws Exception {
+        if (!mHasFeature) {
+            return;
+        }
+        String testClass = DEVICE_OWNER_PKG + "." + testClassName;
+        assertTrue(testClass + " failed.", runDeviceTestsAsUser(DEVICE_OWNER_PKG, testClass, userId));
+    }
 }
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/EphemeralUserTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/EphemeralUserTest.java
index cb7e67b..2133431 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/EphemeralUserTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/EphemeralUserTest.java
@@ -16,22 +16,16 @@
 
 package com.android.cts.devicepolicy;
 
-import com.android.ddmlib.Log.LogLevel;
-import com.android.tradefed.device.DeviceNotAvailableException;
-import com.android.tradefed.log.LogUtil.CLog;
-
 /**
  * Tests for emphemeral users and profiles.
  */
 public class EphemeralUserTest extends BaseDevicePolicyTest {
 
-    private static final int FLAG_EPHEMERAL = 0x00000100;
-
     @Override
     protected void setUp() throws Exception {
         super.setUp();
         mHasFeature = getDevice().getApiLevel() >= 24 /* Build.VERSION_CODES.N */
-                && canCreateUsers(1);
+                && canCreateAdditionalUsers(1);
     }
 
     @Override
@@ -45,7 +39,7 @@
         if (!mHasFeature) {
             return;
         }
-        int userId = createUser(true);
+        int userId = createUser(FLAG_EPHEMERAL);
         int flags = getUserFlags(userId);
         assertTrue("ephemeral flag must be set", FLAG_EPHEMERAL == (flags & FLAG_EPHEMERAL));
     }
@@ -55,7 +49,7 @@
         if (!mHasFeature) {
             return;
         }
-        int userId = createUser(false);
+        int userId = createUser();
         int flags = getUserFlags(userId);
         assertTrue("ephemeral flag must not be set", 0 == (flags & FLAG_EPHEMERAL));
     }
@@ -66,10 +60,10 @@
      */
     public void testProfileInheritsEphemeral() throws Exception {
         if (!mHasFeature || !hasDeviceFeature("android.software.managed_users")
-                || !hasUserSplit() || !canCreateUsers(2)) {
+                || !hasUserSplit() || !canCreateAdditionalUsers(2)) {
             return;
         }
-        int userId = createUser(true);
+        int userId = createUser(FLAG_EPHEMERAL);
         int profileId = createManagedProfile(userId);
         int flags = getUserFlags(profileId);
         assertTrue("ephemeral flag must be set", FLAG_EPHEMERAL == (flags & FLAG_EPHEMERAL));
@@ -82,16 +76,43 @@
         if (!mHasFeature) {
             return;
         }
-        int userId = createUser(true);
+        int userId = createUser(FLAG_EPHEMERAL);
         startUser(userId);
         assertTrue("ephemeral user must exists after start", listUsers().contains(userId));
         stopUser(userId);
         assertFalse("ephemeral user must be removed after stop", listUsers().contains(userId));
     }
 
-    /** Checks whether it is possible to create the desired number of users. */
-    private boolean canCreateUsers(int numberOfUsers) throws DeviceNotAvailableException {
-        return listUsers().size() + numberOfUsers <= getMaxNumberOfUsersSupported();
+    /**
+     * The guest should be automatically created ephemeral when the ephemeral-guest feature is set
+     * and not ephemeral when the feature is not set.
+     */
+    public void testEphemeralGuestFeature() throws Exception {
+        if (!mHasFeature) {
+            return;
+        }
+        // Create a guest user.
+        int userId = createUser(FLAG_GUEST);
+        int flags = getUserFlags(userId);
+        if (getGuestUsersEphemeral()) {
+            // Check the guest was automatically created ephemeral.
+            assertTrue("ephemeral flag must be set for guest",
+                    FLAG_EPHEMERAL == (flags & FLAG_EPHEMERAL));
+        } else {
+            // The guest should not be ephemeral.
+            assertTrue("ephemeral flag must not be set for guest", 0 == (flags & FLAG_EPHEMERAL));
+        }
     }
 
+    private boolean getGuestUsersEphemeral() throws Exception {
+        String commandOutput = getDevice().executeShellCommand("dumpsys user");
+        String[] outputLines = commandOutput.split("\n");
+        for (String line : outputLines) {
+            String[] lineParts = line.split(":");
+            if (lineParts.length == 2 && lineParts[0].trim().equals("All guests ephemeral")) {
+                return Boolean.parseBoolean(lineParts[1].trim());
+            }
+        }
+        return false;
+    }
 }
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileTest.java
index 282d316..46f798e 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileTest.java
@@ -411,6 +411,7 @@
                 contactsTestSet.setContactsSearchEnabled(true);
                 contactsTestSet.checkIfCanLookupEnterpriseContacts(true);
                 contactsTestSet.checkIfCanFilterEnterpriseContacts(true);
+                contactsTestSet.checkIfCanFilterSelfContacts();
                 return null;
             }
         });
@@ -438,18 +439,18 @@
                     contactsTestSet.setContactsSearchEnabled(false);
                     contactsTestSet.checkIfCanLookupEnterpriseContacts(true);
                     contactsTestSet.checkIfCanFilterEnterpriseContacts(false);
-
+                    contactsTestSet.checkIfCanFilterSelfContacts();
                     contactsTestSet.setCallerIdEnabled(false);
                     contactsTestSet.setContactsSearchEnabled(true);
                     contactsTestSet.checkIfCanLookupEnterpriseContacts(false);
                     contactsTestSet.checkIfCanFilterEnterpriseContacts(true);
-
+                    contactsTestSet.checkIfCanFilterSelfContacts();
                     contactsTestSet.setCallerIdEnabled(false);
                     contactsTestSet.setContactsSearchEnabled(false);
                     contactsTestSet.checkIfCanLookupEnterpriseContacts(false);
                     contactsTestSet.checkIfCanFilterEnterpriseContacts(false);
+                    contactsTestSet.checkIfCanFilterSelfContacts();
                     contactsTestSet.checkIfNoEnterpriseDirectoryFound();
-
                     return null;
                 } finally {
                     // reset policies
@@ -460,6 +461,14 @@
         });
     }
 
+    public void testOrganizationInfo() throws Exception {
+        if (!mHasFeature) {
+            return;
+        }
+        assertTrue(runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".OrganizationInfoTest",
+                mProfileUserId));
+    }
+
     public void testBluetoothContactSharingDisabled() throws Exception {
         if (!mHasFeature) {
             return;
@@ -896,21 +905,39 @@
             }
         }
 
-        public void checkIfCanFilterEnterpriseContacts(boolean expected)
-                throws DeviceNotAvailableException {
+        public void checkIfCanFilterSelfContacts() throws DeviceNotAvailableException {
             assertTrue(runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
                     "testPrimaryProfileEnterpriseCallableFilter_canAccessPrimaryDirectories",
                     mParentUserId));
             assertTrue(runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
+                    "testManagedProfileEnterpriseCallableFilter_canAccessManagedDirectories",
+                    mProfileUserId));
+
+            assertTrue(runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
                     "testPrimaryProfileEnterpriseEmailFilter_canAccessPrimaryDirectories",
                     mParentUserId));
             assertTrue(runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
+                    "testEnterpriseProfileEnterpriseEmailFilter_canAccessManagedDirectories",
+                    mProfileUserId));
+
+            assertTrue(runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
                     "testPrimaryProfileEnterpriseContactFilter_canAccessPrimaryDirectories",
                     mParentUserId));
             assertTrue(runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
+                    "testManagedProfileEnterpriseContactFilter_canAccessManagedDirectories",
+                    mProfileUserId));
+
+            assertTrue(runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
                     "testPrimaryProfileEnterprisePhoneFilter_canAccessPrimaryDirectories",
                     mParentUserId));
             assertTrue(runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
+                    "testManagedProfileEnterprisePhoneFilter_canAccessManagedDirectories",
+                    mProfileUserId));
+        }
+
+        public void checkIfCanFilterEnterpriseContacts(boolean expected)
+                throws DeviceNotAvailableException {
+            assertTrue(runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
                     "testFilterUriWhenDirectoryParamMissing", mParentUserId));
             if (expected) {
                 assertTrue(runDeviceTestsAsUser(mManagedProfilePackage, ".ContactsTest",
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedManagedProfileOwnerTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedManagedProfileOwnerTest.java
new file mode 100644
index 0000000..2b64a4d
--- /dev/null
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedManagedProfileOwnerTest.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.devicepolicy;
+
+/**
+ * Set of tests for managed profile owner use cases that also apply to device owners.
+ * Tests that should be run identically in both cases are added in DeviceAndProfileOwnerTest.
+ */
+public class MixedManagedProfileOwnerTest extends DeviceAndProfileOwnerTest {
+
+    protected static final String CLEAR_PROFILE_OWNER_NEGATIVE_TEST_CLASS =
+            DEVICE_ADMIN_PKG + ".ClearProfileOwnerNegativeTest";
+
+    private int mParentUserId = -1;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+
+        // We need managed users to be supported in order to create a profile of the user owner.
+        mHasFeature &= hasDeviceFeature("android.software.managed_users");
+
+        if (mHasFeature) {
+            removeTestUsers();
+            mParentUserId = getPrimaryUser();
+            mUserId = createManagedProfile(mParentUserId);
+            switchUser(mParentUserId);
+            startUser(mUserId);
+
+            installAppAsUser(DEVICE_ADMIN_APK, mUserId);
+            setProfileOwnerOrFail(DEVICE_ADMIN_PKG + "/" + ADMIN_RECEIVER_TEST_CLASS, mUserId);
+            startUser(mUserId);
+        }
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        if (mHasFeature) {
+            removeUser(mUserId);
+        }
+        super.tearDown();
+    }
+
+    // Most tests for this class are defined in DeviceAndProfileOwnerTest
+
+    /**
+     * Verify that screenshots are still possible for activities in the primary user when the policy
+     * is set on the profile owner.
+     */
+    public void testScreenCaptureDisabled_allowedPrimaryUser() throws Exception {
+        if (!mHasFeature) {
+            return;
+        }
+        executeDeviceTestMethod(".ScreenCaptureDisabledTest", "testSetScreenCaptureDisabled_true");
+        // start the ScreenCaptureDisabledActivity in the parent
+        installAppAsUser(DEVICE_ADMIN_APK, mParentUserId);
+        String command = "am start -W --user " + mParentUserId + " " + DEVICE_ADMIN_PKG + "/"
+                + DEVICE_ADMIN_PKG + ".ScreenCaptureDisabledActivity";
+        getDevice().executeShellCommand(command);
+        executeDeviceTestMethod(".ScreenCaptureDisabledTest", "testScreenCapturePossible");
+    }
+
+    @Override
+    public void testResetPassword() {
+        // Managed profile owner can't call resetPassword().
+    }
+
+    public void testCannotClearProfileOwner() throws Exception {
+        if (mHasFeature) {
+            assertTrue("Managed profile owner shouldn't be removed",
+                    runDeviceTests(DEVICE_ADMIN_PKG, CLEAR_PROFILE_OWNER_NEGATIVE_TEST_CLASS));
+        }
+    }
+}
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedProfileOwnerTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedProfileOwnerTest.java
index 0b33e8f..a527598 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedProfileOwnerTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/MixedProfileOwnerTest.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2015 The Android Open Source Project
+ * 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.
@@ -16,64 +16,121 @@
 
 package com.android.cts.devicepolicy;
 
-import com.android.ddmlib.Log.LogLevel;
-import com.android.tradefed.device.DeviceNotAvailableException;
-import com.android.tradefed.log.LogUtil.CLog;
-
-import junit.framework.AssertionFailedError;
-
 /**
- * Set of tests for profile owner use cases that also apply to device owners.
+ * Set of tests for pure (non-managed) profile owner use cases that also apply to device owners.
  * Tests that should be run identically in both cases are added in DeviceAndProfileOwnerTest.
  */
 public class MixedProfileOwnerTest extends DeviceAndProfileOwnerTest {
 
-    private int mParentUserId = -1;
+    protected static final String CLEAR_PROFILE_OWNER_TEST_CLASS =
+            DEVICE_ADMIN_PKG + ".ClearProfileOwnerTest";
 
     @Override
     protected void setUp() throws Exception {
         super.setUp();
 
-        // We need managed users to be supported in order to create a profile of the user owner.
-        mHasFeature &= hasDeviceFeature("android.software.managed_users");
-
         if (mHasFeature) {
-            removeTestUsers();
-            mParentUserId = getPrimaryUser();
-            mUserId = createManagedProfile(mParentUserId);
-            switchUser(mParentUserId);
-            startUser(mUserId);
+            mUserId = USER_OWNER;
 
             installAppAsUser(DEVICE_ADMIN_APK, mUserId);
             setProfileOwnerOrFail(DEVICE_ADMIN_PKG + "/" + ADMIN_RECEIVER_TEST_CLASS, mUserId);
-            startUser(mUserId);
         }
     }
 
     @Override
     protected void tearDown() throws Exception {
         if (mHasFeature) {
-            removeUser(mUserId);
+            assertTrue("Failed to remove profile owner.",
+                    runDeviceTests(DEVICE_ADMIN_PKG, CLEAR_PROFILE_OWNER_TEST_CLASS));
         }
         super.tearDown();
     }
 
-    // Most tests for this class are defined in DeviceAndProfileOwnerTest
+    @Override
+    public void testPermissionAppUpdate() {
+        // TODO Should it be failing?
+        /*
+01-27 14:11:14 I/BaseDevicePolicyTest: Test com.android.cts.deviceandprofileowner.PermissionsTest#testPermissionUpdate_setAutoDeniedPolicy: FAILURE
+01-27 14:11:14 W/BaseDevicePolicyTest: junit.framework.AssertionFailedError
+at junit.framework.Assert.fail(Assert.java:48)
+at junit.framework.Assert.assertTrue(Assert.java:20)
+at junit.framework.Assert.assertNotNull(Assert.java:218)
+at junit.framework.Assert.assertNotNull(Assert.java:211)
+at com.android.cts.deviceandprofileowner.PermissionsTest$PermissionBroadcastReceiver.waitForBroadcast(PermissionsTest.java:315)
+at com.android.cts.deviceandprofileowner.PermissionsTest.assertPermissionRequest(PermissionsTest.java:241)
+at com.android.cts.deviceandprofileowner.PermissionsTest.assertPermissionRequest(PermissionsTest.java:229)
+at com.android.cts.deviceandprofileowner.PermissionsTest.testPermissionUpdate_setAutoDeniedPolicy(PermissionsTest.java:193)
+at java.lang.reflect.Method.invoke(Native Method)
+at android.test.InstrumentationTestCase.runMethod(InstrumentationTestCase.java:214)
+at android.test.InstrumentationTestCase.runTest(InstrumentationTestCase.java:199)
+at junit.framework.TestCase.runBare(TestCase.java:134)
+at junit.framework.TestResult$1.protect(TestResult.java:115)
+at android.support.test.internal.runner.junit3.AndroidTestResult.runProtected(AndroidTestResult.java:77)
+at junit.framework.TestResult.run(TestResult.java:118)
+at android.support.test.internal.runner.junit3.AndroidTestResult.run(AndroidTestResult.java:55)
+at junit.framework.TestCase.run(TestCase.java:124)
+at android.support.test.internal.runner.junit3.NonLeakyTestSuite$NonLeakyTest.run(NonLeakyTestSuite.java:63)
+at junit.framework.TestSuite.runTest(TestSuite.java:243)
+at junit.framework.TestSuite.run(TestSuite.java:238)
+at android.support.test.internal.runner.junit3.DelegatingTestSuite.run(DelegatingTestSuite.java:103)
+at android.support.test.internal.runner.junit3.AndroidTestSuite.run(AndroidTestSuite.java:69)
+at android.support.test.internal.runner.junit3.JUnit38ClassRunner.run(JUnit38ClassRunner.java:90)
+at org.junit.runners.Suite.runChild(Suite.java:128)
+at org.junit.runners.Suite.runChild(Suite.java:27)
+at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
+at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
+at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
+at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
+at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
+at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
+at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
+at org.junit.runner.JUnitCore.run(JUnitCore.java:115)
+at android.support.test.internal.runner.TestExecutor.execute(TestExecutor.java:54)
+at android.support.test.runner.AndroidJUnitRunner.onStart(AndroidJUnitRunner.java:240)
+at android.app.Instrumentation$InstrumentationThread.run(Instrumentation.java:1870)
+         */
+    }
 
-    /**
-     * Verify that screenshots are still possible for activities in the primary user when the policy
-     * is set on the profile owner.
-     */
-    public void testScreenCaptureDisabled_allowedPrimaryUser() throws Exception {
-        if (!mHasFeature) {
-            return;
-        }
-        executeDeviceTestMethod(".ScreenCaptureDisabledTest", "testSetScreenCaptureDisabled_true");
-        // start the ScreenCaptureDisabledActivity in the parent
-        installAppAsUser(DEVICE_ADMIN_APK, mParentUserId);
-        String command = "am start -W --user " + mParentUserId + " " + DEVICE_ADMIN_PKG + "/"
-                + DEVICE_ADMIN_PKG + ".ScreenCaptureDisabledActivity";
-        getDevice().executeShellCommand(command);
-        executeDeviceTestMethod(".ScreenCaptureDisabledTest", "testScreenCapturePossible");
+    @Override
+    public void testPermissionPrompts() {
+        // TODO Should it be failing?
+        /*
+01-27 14:15:54 I/BaseDevicePolicyTest: Test com.android.cts.deviceandprofileowner.PermissionsTest#testPermissionPrompts: FAILURE
+01-27 14:15:54 W/BaseDevicePolicyTest: junit.framework.AssertionFailedError: Couldn't find button with resource id: permission_deny_button
+at junit.framework.Assert.fail(Assert.java:50)
+at junit.framework.Assert.assertTrue(Assert.java:20)
+at junit.framework.Assert.assertNotNull(Assert.java:218)
+at com.android.cts.deviceandprofileowner.PermissionsTest.pressPermissionPromptButton(PermissionsTest.java:298)
+at com.android.cts.deviceandprofileowner.PermissionsTest.assertPermissionRequest(PermissionsTest.java:240)
+at com.android.cts.deviceandprofileowner.PermissionsTest.testPermissionPrompts(PermissionsTest.java:177)
+at java.lang.reflect.Method.invoke(Native Method)
+at android.test.InstrumentationTestCase.runMethod(InstrumentationTestCase.java:214)
+at android.test.InstrumentationTestCase.runTest(InstrumentationTestCase.java:199)
+at junit.framework.TestCase.runBare(TestCase.java:134)
+at junit.framework.TestResult$1.protect(TestResult.java:115)
+at android.support.test.internal.runner.junit3.AndroidTestResult.runProtected(AndroidTestResult.java:77)
+at junit.framework.TestResult.run(TestResult.java:118)
+at android.support.test.internal.runner.junit3.AndroidTestResult.run(AndroidTestResult.java:55)
+at junit.framework.TestCase.run(TestCase.java:124)
+at android.support.test.internal.runner.junit3.NonLeakyTestSuite$NonLeakyTest.run(NonLeakyTestSuite.java:63)
+at junit.framework.TestSuite.runTest(TestSuite.java:243)
+at junit.framework.TestSuite.run(TestSuite.java:238)
+at android.support.test.internal.runner.junit3.DelegatingTestSuite.run(DelegatingTestSuite.java:103)
+at android.support.test.internal.runner.junit3.AndroidTestSuite.run(AndroidTestSuite.java:69)
+at android.support.test.internal.runner.junit3.JUnit38ClassRunner.run(JUnit38ClassRunner.java:90)
+at org.junit.runners.Suite.runChild(Suite.java:128)
+at org.junit.runners.Suite.runChild(Suite.java:27)
+at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
+at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
+at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
+at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
+at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
+at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
+at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
+at org.junit.runner.JUnitCore.run(JUnitCore.java:115)
+at android.support.test.internal.runner.TestExecutor.execute(TestExecutor.java:54)
+at android.support.test.runner.AndroidJUnitRunner.onStart(AndroidJUnitRunner.java:240)
+at android.app.Instrumentation$InstrumentationThread.run(Instrumentation.java:1870)
+         */
     }
 }
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/UserRestrictionsTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/UserRestrictionsTest.java
new file mode 100644
index 0000000..beef986
--- /dev/null
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/UserRestrictionsTest.java
@@ -0,0 +1,199 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.cts.devicepolicy;
+
+import com.android.tradefed.device.DeviceNotAvailableException;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+public class UserRestrictionsTest extends BaseDevicePolicyTest {
+    private static final String DEVICE_ADMIN_PKG = "com.android.cts.deviceandprofileowner";
+    private static final String DEVICE_ADMIN_APK = "CtsDeviceAndProfileOwnerApp.apk";
+    private static final String ADMIN_RECEIVER_TEST_CLASS
+            = ".BaseDeviceAdminTest$BasicAdminReceiver";
+
+    private boolean mRemoveDeviceOwnerInTearDown;
+    private boolean mRemovePrimaryProfileOwnerInTearDown;
+    private int mDeviceOwnerUserId = USER_SYSTEM;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+
+        mRemoveDeviceOwnerInTearDown = false;
+        mRemovePrimaryProfileOwnerInTearDown = false;
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        if (mHasFeature) {
+            if (mRemoveDeviceOwnerInTearDown) {
+                assertTrue("Failed to clear device owner",
+                        runTests("ClearDeviceOwnerTest", mDeviceOwnerUserId));
+            }
+            if (mRemovePrimaryProfileOwnerInTearDown) {
+                assertTrue("Failed to clear profile owner",
+                        runTests("ClearProfileOwnerTest", mDeviceOwnerUserId));
+            }
+            assertTrue("Some user restrictions are still set",
+                    runTests("userrestrictions.CheckNoOwnerRestrictionsTest", mDeviceOwnerUserId));
+
+            // DO/PO might have set DISALLOW_REMOVE_USER, so it needs to be done after removing
+            // them.
+            removeTestUsers();
+        }
+        super.tearDown();
+    }
+
+    private boolean runTests(@Nonnull String className,
+            @Nullable String method, int userId) throws DeviceNotAvailableException {
+        return runDeviceTestsAsUser(DEVICE_ADMIN_PKG, "." + className, method, userId);
+    }
+
+    private boolean runTests(@Nonnull String className, int userId)
+            throws DeviceNotAvailableException {
+        return runTests(className, null, userId);
+    }
+
+    public void testUserRestrictions_deviceOwnerOnly() throws Exception {
+        if (!mHasFeature) {
+            return;
+        }
+        installApp(DEVICE_ADMIN_APK);
+        assertTrue("Failed to set device owner",
+                setDeviceOwner(DEVICE_ADMIN_PKG + "/" + ADMIN_RECEIVER_TEST_CLASS));
+        mRemoveDeviceOwnerInTearDown = true;
+
+        runTests("userrestrictions.DeviceOwnerUserRestrictionsTest",
+                "testSetAllRestrictions", mDeviceOwnerUserId);
+    }
+
+    public void testUserRestrictions_primaryProfileOwnerOnly() throws Exception {
+        if (!mHasFeature) {
+            return;
+        }
+        if (hasUserSplit()) {
+            // Can't set PO on user-0 in this mode.
+            return;
+        }
+
+        installApp(DEVICE_ADMIN_APK);
+        assertTrue("Failed to set profile owner",
+                setProfileOwner(DEVICE_ADMIN_PKG + "/" + ADMIN_RECEIVER_TEST_CLASS,
+                        mDeviceOwnerUserId));
+        mRemovePrimaryProfileOwnerInTearDown = true;
+
+        runTests("userrestrictions.PrimaryProfileOwnerUserRestrictionsTest",
+                "testSetAllRestrictions", mDeviceOwnerUserId);
+    }
+
+    public void testUserRestrictions_secondaryProfileOwnerOnly() throws Exception {
+        if (!mHasFeature || !mSupportsMultiUser) {
+            return;
+        }
+        final int secondaryUserId = createUser();
+
+        installAppAsUser(DEVICE_ADMIN_APK, secondaryUserId);
+        assertTrue("Failed to set profile owner",
+                setProfileOwner(DEVICE_ADMIN_PKG + "/" + ADMIN_RECEIVER_TEST_CLASS,
+                        secondaryUserId));
+
+        runTests("userrestrictions.SecondaryProfileOwnerUserRestrictionsTest",
+                "testSetAllRestrictions", secondaryUserId);
+    }
+
+    /**
+     * DO + PO combination.  Make sure global DO restrictions are visible on secondary users.
+     */
+    public void testUserRestrictions_layering() throws Exception {
+        if (!mHasFeature || !mSupportsMultiUser) {
+            return;
+        }
+        // Set DO
+        installApp(DEVICE_ADMIN_APK);
+        assertTrue("Failed to set device owner",
+                setDeviceOwner(DEVICE_ADMIN_PKG + "/" + ADMIN_RECEIVER_TEST_CLASS));
+        mRemoveDeviceOwnerInTearDown = true;
+
+        // Create another user and set PO.
+        final int secondaryUserId = createUser();
+
+        installAppAsUser(DEVICE_ADMIN_APK, secondaryUserId);
+        assertTrue("Failed to set profile owner",
+                setProfileOwner(DEVICE_ADMIN_PKG + "/" + ADMIN_RECEIVER_TEST_CLASS,
+                        secondaryUserId));
+
+        // Let DO set all restrictions.
+        runTests("userrestrictions.DeviceOwnerUserRestrictionsTest",
+                "testSetAllRestrictions", mDeviceOwnerUserId);
+
+        // Make sure the global restrictions are visible to secondary users.
+        runTests("userrestrictions.SecondaryProfileOwnerUserRestrictionsTest",
+                "testHasGlobalRestrictions", secondaryUserId);
+
+        // Then let PO set all restrictions.
+        runTests("userrestrictions.SecondaryProfileOwnerUserRestrictionsTest",
+                "testSetAllRestrictions", secondaryUserId);
+
+        // Make sure both local and global restrictions are visible on secondary users.
+        runTests("userrestrictions.SecondaryProfileOwnerUserRestrictionsTest",
+                "testHasBothGlobalAndLocalRestrictions", secondaryUserId);
+
+        // Let DO clear all restrictions.
+        runTests("userrestrictions.DeviceOwnerUserRestrictionsTest",
+                "testClearAllRestrictions", mDeviceOwnerUserId);
+
+        // Now only PO restrictions should be set on the secondary user.
+        runTests("userrestrictions.SecondaryProfileOwnerUserRestrictionsTest",
+                "testLocalRestrictionsOnly", secondaryUserId);
+    }
+
+    /**
+     * PO on user-0.  It can set DO restrictions too, but they shouldn't leak to other users.
+     */
+    public void testUserRestrictions_layering_profileOwnerNoLeaking() throws Exception {
+        if (!mHasFeature || !mSupportsMultiUser) {
+            return;
+        }
+        if (hasUserSplit()) {
+            // Can't set PO on user-0 in this mode.
+            return;
+        }
+        // Set DO on user 0
+        installApp(DEVICE_ADMIN_APK);
+        assertTrue("Failed to set device owner",
+                setProfileOwner(DEVICE_ADMIN_PKG + "/" + ADMIN_RECEIVER_TEST_CLASS,
+                        mDeviceOwnerUserId));
+        mRemovePrimaryProfileOwnerInTearDown = true;
+
+        // Create another user and set PO.
+        final int secondaryUserId = createUser();
+
+        installAppAsUser(DEVICE_ADMIN_APK, secondaryUserId);
+        assertTrue("Failed to set profile owner",
+                setProfileOwner(DEVICE_ADMIN_PKG + "/" + ADMIN_RECEIVER_TEST_CLASS,
+                        secondaryUserId));
+
+        // Let user-0 PO sets all restrictions.
+        runTests("userrestrictions.PrimaryProfileOwnerUserRestrictionsTest",
+                "testSetAllRestrictions", mDeviceOwnerUserId);
+
+        // Secondary users shouldn't see any of them.
+        runTests("userrestrictions.SecondaryProfileOwnerUserRestrictionsTest",
+                "testDefaultRestrictionsOnly", secondaryUserId);
+    }
+}
\ No newline at end of file
diff --git a/hostsidetests/net/app/src/com/android/cts/net/hostside/ConnectivityManagerTest.java b/hostsidetests/net/app/src/com/android/cts/net/hostside/ConnectivityManagerTest.java
new file mode 100644
index 0000000..5d3812c
--- /dev/null
+++ b/hostsidetests/net/app/src/com/android/cts/net/hostside/ConnectivityManagerTest.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 com.android.cts.net.hostside;
+
+import static android.net.ConnectivityManager.RESTRICT_BACKGROUND_STATUS_DISABLED;
+import static android.net.ConnectivityManager.RESTRICT_BACKGROUND_STATUS_ENABLED;
+import static android.net.ConnectivityManager.RESTRICT_BACKGROUND_STATUS_WHITELISTED;
+import android.app.Activity;
+import android.net.ConnectivityManager;
+import android.test.InstrumentationTestCase;
+import android.util.Log;
+
+/**
+ * Tests for the {@link ConnectivityManager} API.
+ *
+ * <p>These tests rely on a host-side test to use {@code adb shell cmd netpolicy} to put the device
+ * in the proper state. In fact, they're more like "assertions" than tests per se - the real test
+ * logic is done on {@code HostsideNetworkTests}.
+ */
+public class ConnectivityManagerTest extends InstrumentationTestCase {
+    private static final String TAG = "ConnectivityManagerTest";
+
+    private ConnectivityManager mCM;
+
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+        mCM = (ConnectivityManager) getInstrumentation().getContext().getSystemService(
+                Activity.CONNECTIVITY_SERVICE);
+    }
+
+    public void testGetRestrictBackgroundStatus_disabled() {
+        assertRestrictBackgroundStatus(RESTRICT_BACKGROUND_STATUS_DISABLED);
+    }
+
+    public void testGetRestrictBackgroundStatus_whitelisted() {
+        assertRestrictBackgroundStatus(RESTRICT_BACKGROUND_STATUS_WHITELISTED);
+    }
+
+    public void testGetRestrictBackgroundStatus_enabled() {
+        assertRestrictBackgroundStatus(RESTRICT_BACKGROUND_STATUS_ENABLED);
+    }
+
+    private void assertRestrictBackgroundStatus(int expectedStatus) {
+        final String expected = toString(expectedStatus);
+        Log.d(TAG, getName() + " (expecting " + expected + ")");
+        final int actualStatus = mCM.getRestrictBackgroundStatus();
+        assertEquals("wrong status", expected, toString(actualStatus));
+    }
+
+    private String toString(int status) {
+        switch (status) {
+            case RESTRICT_BACKGROUND_STATUS_DISABLED:
+                return "DISABLED";
+            case RESTRICT_BACKGROUND_STATUS_WHITELISTED:
+                return "WHITELISTED";
+            case RESTRICT_BACKGROUND_STATUS_ENABLED:
+                return "ENABLED";
+            default:
+                return "UNKNOWN_STATUS_" + status;
+        }
+    }
+}
diff --git a/hostsidetests/net/src/com/android/cts/net/HostsideNetworkTests.java b/hostsidetests/net/src/com/android/cts/net/HostsideNetworkTests.java
index f42de0e..dd2424c 100644
--- a/hostsidetests/net/src/com/android/cts/net/HostsideNetworkTests.java
+++ b/hostsidetests/net/src/com/android/cts/net/HostsideNetworkTests.java
@@ -17,6 +17,7 @@
 package com.android.cts.net;
 
 import com.android.cts.migration.MigrationHelper;
+import com.android.ddmlib.Log;
 import com.android.ddmlib.testrunner.RemoteAndroidTestRunner;
 import com.android.ddmlib.testrunner.TestIdentifier;
 import com.android.ddmlib.testrunner.TestResult;
@@ -24,17 +25,20 @@
 import com.android.ddmlib.testrunner.TestRunResult;
 import com.android.tradefed.build.IBuildInfo;
 import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.result.CollectingTestListener;
 import com.android.tradefed.testtype.DeviceTestCase;
 import com.android.tradefed.testtype.IAbi;
 import com.android.tradefed.testtype.IAbiReceiver;
 import com.android.tradefed.testtype.IBuildReceiver;
-import com.android.tradefed.device.DeviceNotAvailableException;
-import com.android.tradefed.device.ITestDevice;
-import com.android.tradefed.result.CollectingTestListener;
 
+import java.io.FileNotFoundException;
 import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
 
 public class HostsideNetworkTests extends DeviceTestCase implements IAbiReceiver, IBuildReceiver {
+    private static final boolean DEBUG = false;
+    private static final String TAG = "HostsideNetworkTests";
     private static final String TEST_PKG = "com.android.cts.net.hostside";
     private static final String TEST_APK = "CtsHostsideNetworkTestsApp.apk";
 
@@ -58,28 +62,130 @@
         assertNotNull(mAbi);
         assertNotNull(mCtsBuild);
 
-        getDevice().uninstallPackage(TEST_PKG);
-
-        assertNull(getDevice().installPackage(
-            MigrationHelper.getTestFile(mCtsBuild, TEST_APK), false));
+        setRestrictBackground(false);
+        uninstallTestPackage(false);
+        installTestPackage();
     }
 
     @Override
     protected void tearDown() throws Exception {
         super.tearDown();
 
-        getDevice().uninstallPackage(TEST_PKG);
+        uninstallTestPackage(false);
+        setRestrictBackground(false);
     }
 
     public void testVpn() throws Exception {
-        runDeviceTests(TEST_PKG, ".VpnTest");
+        runDeviceTests(TEST_PKG, TEST_PKG + ".VpnTest");
+    }
+
+    public void testConnectivityManager_getRestrictBackgroundStatus_disabled() throws Exception {
+        final int uid = getUid(TEST_PKG);
+        removeRestrictBackgroundWhitelist(uid);
+        assertRestrictBackgroundStatusDisabled();
+        // Sanity check: make sure status is always disabled, never whitelisted
+        addRestrictBackgroundWhitelist(uid);
+        assertRestrictBackgroundStatusDisabled();
+    }
+
+    public void testConnectivityManager_getRestrictBackgroundStatus_whitelisted() throws Exception {
+        final int uid = getUid(TEST_PKG);
+        setRestrictBackground(true);
+        addRestrictBackgroundWhitelist(uid);
+        assertRestrictBackgroundStatusWhitelisted();
+    }
+
+    public void testConnectivityManager_getRestrictBackgroundStatus_enabled() throws Exception {
+        final int uid = getUid(TEST_PKG);
+        setRestrictBackground(true);
+        removeRestrictBackgroundWhitelist(uid);
+        assertRestrictBackgroundStatusEnabled();
+    }
+
+    public void testConnectivityManager_getRestrictBackgroundStatus_uninstall() throws Exception {
+        final int uid = getUid(TEST_PKG);
+
+        addRestrictBackgroundWhitelist(uid);
+        assertRestrictBackgroundWhitelist(uid, true);
+
+        uninstallTestPackage(true);
+        assertPackageUninstalled(TEST_PKG);
+        assertRestrictBackgroundWhitelist(uid, false);
+
+        installTestPackage();
+        final int newUid = getUid(TEST_PKG);
+        assertRestrictBackgroundWhitelist(uid, false);
+        assertRestrictBackgroundWhitelist(newUid, false);
+    }
+
+    private void installTestPackage() throws DeviceNotAvailableException, FileNotFoundException {
+        assertNull(getDevice().installPackage(
+                MigrationHelper.getTestFile(mCtsBuild, TEST_APK), false));
+    }
+
+    private void uninstallTestPackage(boolean shouldSucceed) throws DeviceNotAvailableException {
+        final String result = getDevice().uninstallPackage(TEST_PKG);
+        if (shouldSucceed) {
+            assertNull("uninstallPackage failed: " + result, result);
+        }
+    }
+
+    private void assertPackageUninstalled(String packageName) throws DeviceNotAvailableException {
+        final String command = "cmd package list packages -f " + packageName;
+        final int max_tries = 5;
+        for (int i = 1; i <= max_tries; i++) {
+            final String result = runCommand(command);
+            if (result.trim().isEmpty()) {
+                return;
+            }
+            i++;
+            Log.v(TAG, "Package " + packageName + " not uninstalled yet (" + result
+                    + "); sleeping 1s before polling again");
+            try {
+                Thread.sleep(1000);
+            } catch (InterruptedException e) {
+                Thread.currentThread().interrupt();
+            }
+        }
+        fail("Package '" + packageName + "' not uinstalled after " + max_tries + " seconds");
+    }
+
+    private void assertRestrictBackgroundStatusDisabled() throws DeviceNotAvailableException {
+        runDeviceTests(TEST_PKG, TEST_PKG + ".ConnectivityManagerTest",
+                "testGetRestrictBackgroundStatus_disabled");
+    }
+
+    private void assertRestrictBackgroundStatusWhitelisted() throws DeviceNotAvailableException {
+        runDeviceTests(TEST_PKG, TEST_PKG + ".ConnectivityManagerTest",
+                "testGetRestrictBackgroundStatus_whitelisted");
+    }
+
+    private void assertRestrictBackgroundStatusEnabled() throws DeviceNotAvailableException {
+        runDeviceTests(TEST_PKG, TEST_PKG + ".ConnectivityManagerTest",
+                "testGetRestrictBackgroundStatus_enabled");
     }
 
     public void runDeviceTests(String packageName, String testClassName)
-           throws DeviceNotAvailableException {
+            throws DeviceNotAvailableException {
+        runDeviceTests(packageName, testClassName, null);
+    }
+
+    public void runDeviceTests(String packageName, String testClassName, String methodName)
+            throws DeviceNotAvailableException {
         RemoteAndroidTestRunner testRunner = new RemoteAndroidTestRunner(packageName,
                 "android.support.test.runner.AndroidJUnitRunner", getDevice().getIDevice());
 
+        if (testClassName != null) {
+            // TODO: figure out why testRunner.setMethodName() / testRunner.setClassName() doesn't
+            // work
+            final StringBuilder runOptions = new StringBuilder("-e class ").append(testClassName);
+            if (methodName != null) {
+                runOptions.append('#').append(methodName);
+            }
+            Log.i(TAG, "Setting runOptions() as " + runOptions);
+            testRunner.setRunOptions(runOptions.toString());
+        }
+
         final CollectingTestListener listener = new CollectingTestListener();
         getDevice().runInstrumentationTests(testRunner, listener);
 
@@ -103,4 +209,57 @@
             throw new AssertionError(errorBuilder.toString());
         }
     }
+
+    private static final Pattern UID_PATTERN =
+            Pattern.compile(".*userId=([0-9]+)$", Pattern.MULTILINE);
+
+    private int getUid(String packageName) throws DeviceNotAvailableException {
+        final String output = runCommand("dumpsys package " + packageName);
+        final Matcher matcher = UID_PATTERN.matcher(output);
+        while (matcher.find()) {
+            final String match = matcher.group(1);
+            return Integer.parseInt(match);
+        }
+        throw new RuntimeException("Did not find regexp '" + UID_PATTERN + "' on adb output\n"
+                + output);
+    }
+
+    private void addRestrictBackgroundWhitelist(int uid) throws DeviceNotAvailableException {
+        runCommand("cmd netpolicy add restrict-background-whitelist " + uid);
+        assertRestrictBackgroundWhitelist(uid, true);
+    }
+
+    private void removeRestrictBackgroundWhitelist(int uid) throws DeviceNotAvailableException {
+        runCommand("cmd netpolicy remove restrict-background-whitelist " + uid);
+        assertRestrictBackgroundWhitelist(uid, false);
+    }
+
+    private void assertRestrictBackgroundWhitelist(int uid, boolean expected)
+            throws DeviceNotAvailableException {
+        final String output = runCommand("cmd netpolicy list restrict-background-whitelist ");
+        // TODO: use MoreAsserts
+        if (expected) {
+            assertTrue("Did not find uid '" + uid + "' on '" + output + "'",
+                    output.contains(Integer.toString(uid)));
+        } else {
+            assertFalse("Found uid '" + uid + "' on '" + output + "'",
+                    output.contains(Integer.toString(uid)));
+        }
+    }
+
+    private void setRestrictBackground(boolean enabled) throws DeviceNotAvailableException {
+        runCommand("cmd netpolicy set restrict-background " + enabled);
+        final String output = runCommand("cmd netpolicy get restrict-background ").trim();
+        final String expectedSuffix = enabled ? "enabled" : "disabled";
+        // TODO: use MoreAsserts?
+        assertTrue("output '" + output + "' should end with '" + expectedSuffix + "'",
+                output.endsWith(expectedSuffix));
+    }
+
+    private String runCommand(String command) throws DeviceNotAvailableException {
+        Log.d(TAG, "Command: '" + command + "'");
+        final String output = getDevice().executeShellCommand(command);
+        if (DEBUG) Log.v(TAG, "Output: " + output.trim());
+        return output;
+    }
 }
diff --git a/hostsidetests/services/activitymanager/app/AndroidManifest.xml b/hostsidetests/services/activitymanager/app/AndroidManifest.xml
index 09dc4d8..ed3cece 100755
--- a/hostsidetests/services/activitymanager/app/AndroidManifest.xml
+++ b/hostsidetests/services/activitymanager/app/AndroidManifest.xml
@@ -24,9 +24,14 @@
                 android:resizeable="true"
                 android:exported="true"
         />
+        <activity android:name=".DockedActivity"
+                android:resizeable="true"
+                android:exported="true"
+                android:taskAffinity="nobody.but.DockedActivity"
+        />
         <activity android:name=".NoRelaunchActivity"
                 android:resizeable="true"
-                android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout|layoutDirection"
+                android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout"
                 android:exported="true"
         />
         <activity android:name=".SlowCreateActivity"
@@ -72,7 +77,7 @@
                   android:resizeable="true"
                   android:supportsPictureInPicture="true"
                   android:taskAffinity="nobody.but.LaunchPipOnPipActivity"
-                  android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout|layoutDirection"
+                  android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout"
                   android:exported="true"
         />
     </application>
diff --git a/hostsidetests/services/activitymanager/app/src/android/server/app/AutoEnterPipActivity.java b/hostsidetests/services/activitymanager/app/src/android/server/app/AutoEnterPipActivity.java
index 97bc041..dbd92b5 100644
--- a/hostsidetests/services/activitymanager/app/src/android/server/app/AutoEnterPipActivity.java
+++ b/hostsidetests/services/activitymanager/app/src/android/server/app/AutoEnterPipActivity.java
@@ -22,6 +22,6 @@
     @Override
     protected void onResume() {
         super.onResume();
-        enterPictureInPictureMode();
+        enterPictureInPicture();
     }
 }
diff --git a/hostsidetests/services/activitymanager/app/src/android/server/app/DockedActivity.java b/hostsidetests/services/activitymanager/app/src/android/server/app/DockedActivity.java
new file mode 100644
index 0000000..427fd29
--- /dev/null
+++ b/hostsidetests/services/activitymanager/app/src/android/server/app/DockedActivity.java
@@ -0,0 +1,22 @@
+/*
+ * 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.server.app;
+
+import android.app.Activity;
+
+public class DockedActivity extends Activity {
+}
diff --git a/hostsidetests/services/activitymanager/app/src/android/server/app/LaunchPipOnPipActivity.java b/hostsidetests/services/activitymanager/app/src/android/server/app/LaunchPipOnPipActivity.java
index b328a09..dba7cde 100644
--- a/hostsidetests/services/activitymanager/app/src/android/server/app/LaunchPipOnPipActivity.java
+++ b/hostsidetests/services/activitymanager/app/src/android/server/app/LaunchPipOnPipActivity.java
@@ -20,8 +20,8 @@
 
 public class LaunchPipOnPipActivity extends Activity {
     @Override
-    public void onPictureInPictureModeChanged(boolean pictureInPictureMode) {
-        super.onPictureInPictureModeChanged(pictureInPictureMode);
+    public void onPictureInPictureChanged(boolean inPictureInPicture) {
+        super.onPictureInPictureChanged(inPictureInPicture);
         AlwaysFocusablePipActivity.launchAlwaysFocusablePipActivity(this);
     }
 }
diff --git a/hostsidetests/services/activitymanager/app/src/android/server/app/LaunchToSideActivity.java b/hostsidetests/services/activitymanager/app/src/android/server/app/LaunchToSideActivity.java
index 388c9a6..9714393 100644
--- a/hostsidetests/services/activitymanager/app/src/android/server/app/LaunchToSideActivity.java
+++ b/hostsidetests/services/activitymanager/app/src/android/server/app/LaunchToSideActivity.java
@@ -1,12 +1,11 @@
 package android.server.app;
 
-import static android.content.Intent.FLAG_ACTIVITY_LAUNCH_TO_SIDE;
+import static android.content.Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT;
 import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
 
 import android.app.Activity;
 import android.content.Intent;
 import android.os.Bundle;
-import android.util.Log;
 
 public class LaunchToSideActivity extends Activity {
     @Override
@@ -15,7 +14,7 @@
         final Bundle extras = intent.getExtras();
          if (extras != null && extras.getBoolean("launch_to_the_side")) {
             Intent newIntent = new Intent(this, TestActivity.class);
-            newIntent.addFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_LAUNCH_TO_SIDE);
+            newIntent.addFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_LAUNCH_ADJACENT);
             startActivity(newIntent);
         }
     }
diff --git a/hostsidetests/services/activitymanager/src/android/server/cts/ActivityManagerDockedStackTests.java b/hostsidetests/services/activitymanager/src/android/server/cts/ActivityManagerDockedStackTests.java
index 731ff47..92be4b8 100644
--- a/hostsidetests/services/activitymanager/src/android/server/cts/ActivityManagerDockedStackTests.java
+++ b/hostsidetests/services/activitymanager/src/android/server/cts/ActivityManagerDockedStackTests.java
@@ -16,17 +16,23 @@
 
 package android.server.cts;
 
-import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.log.LogUtil.CLog;
 
-import java.lang.Exception;
-import java.lang.String;
+import java.awt.Rectangle;
+
+import static com.android.ddmlib.Log.LogLevel.*;
 
 public class ActivityManagerDockedStackTests extends ActivityManagerTestBase {
 
     private static final String TEST_ACTIVITY_NAME = "TestActivity";
+    private static final String DOCKED_ACTIVITY_NAME = "DockedActivity";
     private static final String LAUNCH_TO_SIDE_ACTIVITY_NAME = "LaunchToSideActivity";
 
     private static final String AM_MOVE_TASK = "am stack movetask ";
+    private static final String AM_RESIZE_DOCKED_STACK = "am stack resize-docked-stack ";
+
+    private static final int TASK_SIZE = 600;
+    private static final int STACK_SIZE = 300;
 
     // TODO: Add test for non-resizeable activity.
 
@@ -140,6 +146,27 @@
         mDevice.executeShellCommand(cmd);
     }
 
+    public void testResizeDockedStack() throws Exception {
+        mDevice.executeShellCommand(getAmStartCmd(TEST_ACTIVITY_NAME));
+        mDevice.executeShellCommand(getAmStartCmd(DOCKED_ACTIVITY_NAME));
+        mDevice.executeShellCommand(AM_MOVE_TASK + getActivityTaskId(DOCKED_ACTIVITY_NAME) + " "
+                + DOCKED_STACK_ID + " true");
+        mDevice.executeShellCommand(AM_RESIZE_DOCKED_STACK
+                + "0 0 " + STACK_SIZE + " " + STACK_SIZE
+                + " 0 0 " + TASK_SIZE + " " + TASK_SIZE);
+        mAmWmState.computeState(mDevice);
+        mAmWmState.assertSanity();
+        mAmWmState.assertContainsStack("Must contain docked stack", DOCKED_STACK_ID);
+        mAmWmState.assertContainsStack("Must contain fullscreen stack",
+                FULLSCREEN_WORKSPACE_STACK_ID);
+        assertEquals(new Rectangle(0, 0, STACK_SIZE, STACK_SIZE),
+                mAmWmState.getAmState().getStackById(DOCKED_STACK_ID).getBounds());
+        assertEquals(new Rectangle(0, 0, TASK_SIZE, TASK_SIZE),
+                mAmWmState.getAmState().getTaskByActivityName(DOCKED_ACTIVITY_NAME).getBounds());
+        mAmWmState.assertVisibility(DOCKED_ACTIVITY_NAME, true);
+        mAmWmState.assertVisibility(TEST_ACTIVITY_NAME, true);
+    }
+
     private void launchActivityToSide(String activityName) throws Exception {
         mDevice.executeShellCommand(
                 getAmStartCmd(activityName) + " -f 0x20000000 --ez launch_to_the_side true");
diff --git a/hostsidetests/services/activitymanager/src/android/server/cts/ActivityManagerReplaceWindowTests.java b/hostsidetests/services/activitymanager/src/android/server/cts/ActivityManagerReplaceWindowTests.java
index 785e487d..88fcc97 100644
--- a/hostsidetests/services/activitymanager/src/android/server/cts/ActivityManagerReplaceWindowTests.java
+++ b/hostsidetests/services/activitymanager/src/android/server/cts/ActivityManagerReplaceWindowTests.java
@@ -77,11 +77,8 @@
         CLog.logAndDisplay(INFO, "==========After Docking========");
         final String newToken = getFocusedWindowToken(windowName, false);
 
-        if (relaunch) {
-            Assert.assertFalse("Window not replaced after relaunch.", oldToken.equals(newToken));
-        } else {
-            Assert.assertEquals("Window replaced without relaunch.", oldToken, newToken);
-        }
+        // For both relaunch and not relaunch case, we'd like the window to be kept.
+        Assert.assertEquals("Window replaced while docking.", oldToken, newToken);
     }
 
     private String getFocusedWindowToken(String windowName, boolean visibleOnly)
diff --git a/hostsidetests/services/activitymanager/src/android/server/cts/ActivityManagerState.java b/hostsidetests/services/activitymanager/src/android/server/cts/ActivityManagerState.java
index 003d8e6..fcee898 100644
--- a/hostsidetests/services/activitymanager/src/android/server/cts/ActivityManagerState.java
+++ b/hostsidetests/services/activitymanager/src/android/server/cts/ActivityManagerState.java
@@ -174,6 +174,15 @@
         return false;
     }
 
+    ActivityStack getStackById(int stackId) {
+        for (ActivityStack stack : mStacks) {
+            if (stackId == stack.mStackId) {
+                return stack;
+            }
+        }
+        return null;
+    }
+
     List<ActivityStack> getStacks() {
         return new ArrayList(mStacks);
     }
@@ -195,6 +204,20 @@
         return false;
     }
 
+    ActivityTask getTaskByActivityName(String activityName) {
+        String fullName = ActivityManagerTestBase.getActivityComponentName(activityName);
+        for (ActivityStack stack : mStacks) {
+            for (ActivityTask task : stack.mTasks) {
+                for (Activity activity : task.mActivities) {
+                    if (activity.name.equals(fullName)) {
+                        return task;
+                    }
+                }
+            }
+        }
+        return null;
+    }
+
     static class ActivityStack extends ActivityContainer {
 
         private static final Pattern TASK_ID_PATTERN = Pattern.compile("Task id #(\\d+)");
diff --git a/hostsidetests/services/activitymanager/src/android/server/cts/ActivityManagerTestBase.java b/hostsidetests/services/activitymanager/src/android/server/cts/ActivityManagerTestBase.java
index d0cd178..ceb2b63 100644
--- a/hostsidetests/services/activitymanager/src/android/server/cts/ActivityManagerTestBase.java
+++ b/hostsidetests/services/activitymanager/src/android/server/cts/ActivityManagerTestBase.java
@@ -187,7 +187,7 @@
                 "settings put system accelerometer_rotation " + rotation);
     }
 
-    private String runCommandAndPrintOutput(String command) throws DeviceNotAvailableException {
+    protected String runCommandAndPrintOutput(String command) throws DeviceNotAvailableException {
         final String output = mDevice.executeShellCommand(command);
         CLog.logAndDisplay(LogLevel.INFO, command);
         CLog.logAndDisplay(LogLevel.INFO, output);
diff --git a/tests/accessibility/src/android/view/accessibility/cts/AccessibilityNodeInfoTest.java b/tests/accessibility/src/android/view/accessibility/cts/AccessibilityNodeInfoTest.java
index 30847a5..9c9b0db 100644
--- a/tests/accessibility/src/android/view/accessibility/cts/AccessibilityNodeInfoTest.java
+++ b/tests/accessibility/src/android/view/accessibility/cts/AccessibilityNodeInfoTest.java
@@ -35,7 +35,7 @@
 public class AccessibilityNodeInfoTest extends AndroidTestCase {
 
     /** The number of properties of the {@link AccessibilityNodeInfo} class. */
-    private static final int NON_STATIC_FIELD_COUNT = 30;
+    private static final int NON_STATIC_FIELD_COUNT = 31;
 
     @SmallTest
     public void testMarshaling() throws Exception {
@@ -184,6 +184,7 @@
         info.setEnabled(true);
         info.setFocusable(true);
         info.setFocused(true);
+        info.setImportantForAccessibility(true);
         info.setLongClickable(true);
         info.setContextClickable(true);
         info.setPassword(true);
@@ -198,6 +199,7 @@
         info.setLabeledBy(new View(getContext()));
         info.setLabelFor(new View(getContext()));
         info.setViewIdResourceName("foo.bar:id/baz");
+        info.setDrawingOrder(5);
     }
 
     /**
@@ -234,6 +236,9 @@
                 receivedInfo.isFocusable());
         assertSame("focused has incorrect value", expectedInfo.isFocused(),
                 receivedInfo.isFocused());
+        assertSame("importantForAccessibility has incorrect value",
+                expectedInfo.isImportantForAccessibility(),
+                receivedInfo.isImportantForAccessibility());
         assertSame("longClickable has incorrect value", expectedInfo.isLongClickable(),
                 receivedInfo.isLongClickable());
         assertSame("contextClickable has incorrect value", expectedInfo.isContextClickable(),
@@ -260,6 +265,8 @@
                 receivedInfo.getMovementGranularities());
         assertEquals("viewId has incorrect value", expectedInfo.getViewIdResourceName(),
                 receivedInfo.getViewIdResourceName());
+        assertEquals("drawing order has incorrect value", expectedInfo.getDrawingOrder(),
+                receivedInfo.getDrawingOrder());
     }
 
     /**
@@ -283,6 +290,8 @@
         assertFalse("enabled not properly recycled", info.isEnabled());
         assertFalse("focusable not properly recycled", info.isFocusable());
         assertFalse("focused not properly recycled", info.isFocused());
+        assertFalse("importantForAccessibility not properly recycled",
+                info.isImportantForAccessibility());
         assertFalse("longClickable not properly recycled", info.isLongClickable());
         assertFalse("contextClickable not properly recycled", info.isContextClickable());
         assertFalse("password not properly recycled", info.isPassword());
@@ -293,5 +302,6 @@
         assertSame("movementGranularities not properly recycled", 0,
                 info.getMovementGranularities());
         assertNull("viewId not properly recycled", info.getViewIdResourceName());
+        assertEquals(0, info.getDrawingOrder());
     }
 }
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityViewTreeReportingTest.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityViewTreeReportingTest.java
index 04a9d9d..a292e4e 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityViewTreeReportingTest.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityViewTreeReportingTest.java
@@ -14,10 +14,18 @@
 
 package android.accessibilityservice.cts;
 
+import android.accessibilityservice.AccessibilityServiceInfo;
+import android.app.UiAutomation;
+import android.content.Context;
 import android.test.suitebuilder.annotation.MediumTest;
+import android.view.View;
 import android.view.accessibility.AccessibilityNodeInfo;
 
 import android.accessibilityservice.cts.R;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import java.util.ArrayList;
 
 /**
  * Test cases for testing the accessibility focus APIs exposed to accessibility
@@ -33,88 +41,264 @@
 
     @MediumTest
     public void testDescendantsOfNotImportantViewReportedInOrder1() throws Exception {
-        AccessibilityNodeInfo firstFrameLayout = getInstrumentation().getUiAutomation()
-            .getRootInActiveWindow().findAccessibilityNodeInfosByText(
-                    getString(R.string.firstFrameLayout)).get(0);
+        UiAutomation uiAutomation = getUiAutomation(false);
+        AccessibilityNodeInfo firstFrameLayout =
+                getNodeByText(uiAutomation, R.string.firstFrameLayout);
         assertNotNull(firstFrameLayout);
         assertSame(3, firstFrameLayout.getChildCount());
 
         // Check if the first child is the right one.
-        AccessibilityNodeInfo firstTextView = getInstrumentation().getUiAutomation()
-            .getRootInActiveWindow().findAccessibilityNodeInfosByText(getString(
-                    R.string.firstTextView)).get(0);
+        AccessibilityNodeInfo firstTextView = getNodeByText(uiAutomation, R.string.firstTextView);
         assertEquals(firstTextView, firstFrameLayout.getChild(0));
 
         // Check if the second child is the right one.
-        AccessibilityNodeInfo firstEditText = getInstrumentation().getUiAutomation()
-            .getRootInActiveWindow().findAccessibilityNodeInfosByText(getString(
-                    R.string.firstEditText)).get(0);
+        AccessibilityNodeInfo firstEditText = getNodeByText(uiAutomation, R.string.firstEditText);
         assertEquals(firstEditText, firstFrameLayout.getChild(1));
 
         // Check if the third child is the right one.
-        AccessibilityNodeInfo firstButton = getInstrumentation().getUiAutomation()
-            .getRootInActiveWindow().findAccessibilityNodeInfosByText(
-                    getString(R.string.firstButton)).get(0);
+        AccessibilityNodeInfo firstButton = getNodeByText(uiAutomation, R.string.firstButton);
         assertEquals(firstButton, firstFrameLayout.getChild(2));
     }
 
     @MediumTest
     public void testDescendantsOfNotImportantViewReportedInOrder2() throws Exception {
-        AccessibilityNodeInfo secondFrameLayout = getInstrumentation().getUiAutomation()
-            .getRootInActiveWindow().findAccessibilityNodeInfosByText(
-                    getString(R.string.secondFrameLayout)).get(0);
+        UiAutomation uiAutomation = getUiAutomation(false);
+        AccessibilityNodeInfo secondFrameLayout =
+                getNodeByText(uiAutomation, R.string.secondFrameLayout);
         assertNotNull(secondFrameLayout);
         assertSame(3, secondFrameLayout.getChildCount());
 
         // Check if the first child is the right one.
-        AccessibilityNodeInfo secondTextView = getInstrumentation().getUiAutomation()
-            .getRootInActiveWindow().findAccessibilityNodeInfosByText(
-                    getString(R.string.secondTextView)).get(0);
+        AccessibilityNodeInfo secondTextView = getNodeByText(uiAutomation, R.string.secondTextView);
         assertEquals(secondTextView, secondFrameLayout.getChild(0));
 
         // Check if the second child is the right one.
-        AccessibilityNodeInfo secondEditText = getInstrumentation().getUiAutomation()
-            .getRootInActiveWindow().findAccessibilityNodeInfosByText(
-                    getString(R.string.secondEditText)).get(0);
+        AccessibilityNodeInfo secondEditText = getNodeByText(uiAutomation, R.string.secondEditText);
         assertEquals(secondEditText, secondFrameLayout.getChild(1));
 
         // Check if the third child is the right one.
-        AccessibilityNodeInfo secondButton = getInstrumentation().getUiAutomation()
-            .getRootInActiveWindow().findAccessibilityNodeInfosByText(
-                    getString(R.string.secondButton)).get(0);
+        AccessibilityNodeInfo secondButton = getNodeByText(uiAutomation, R.string.secondButton);
         assertEquals(secondButton, secondFrameLayout.getChild(2));
     }
 
     @MediumTest
     public void testDescendantsOfNotImportantViewReportedInOrder3() throws Exception {
-        AccessibilityNodeInfo rootLinearLayout = getInstrumentation().getUiAutomation()
-            .getRootInActiveWindow().findAccessibilityNodeInfosByText(
-                    getString(R.string.rootLinearLayout)).get(0);
+        UiAutomation uiAutomation = getUiAutomation(false);
+        AccessibilityNodeInfo rootLinearLayout =
+                getNodeByText(uiAutomation, R.string.rootLinearLayout);
         assertNotNull(rootLinearLayout);
         assertSame(4, rootLinearLayout.getChildCount());
 
         // Check if the first child is the right one.
-        AccessibilityNodeInfo firstFrameLayout = getInstrumentation().getUiAutomation()
-            .getRootInActiveWindow().findAccessibilityNodeInfosByText(
-                    getString(R.string.firstFrameLayout)).get(0);
+        AccessibilityNodeInfo firstFrameLayout =
+                getNodeByText(uiAutomation, R.string.firstFrameLayout);
         assertEquals(firstFrameLayout, rootLinearLayout.getChild(0));
 
         // Check if the second child is the right one.
-        AccessibilityNodeInfo secondTextView = getInstrumentation().getUiAutomation()
-            .getRootInActiveWindow().findAccessibilityNodeInfosByText(
-                    getString(R.string.secondTextView)).get(0);
+        AccessibilityNodeInfo secondTextView = getNodeByText(uiAutomation, R.string.secondTextView);
         assertEquals(secondTextView, rootLinearLayout.getChild(1));
 
         // Check if the third child is the right one.
-        AccessibilityNodeInfo secondEditText = getInstrumentation().getUiAutomation()
-            .getRootInActiveWindow().findAccessibilityNodeInfosByText(
-                    getString(R.string.secondEditText)).get(0);
+        AccessibilityNodeInfo secondEditText = getNodeByText(uiAutomation, R.string.secondEditText);
         assertEquals(secondEditText, rootLinearLayout.getChild(2));
 
         // Check if the fourth child is the right one.
-        AccessibilityNodeInfo secondButton = getInstrumentation().getUiAutomation()
-            .getRootInActiveWindow().findAccessibilityNodeInfosByText(
-                    getString(R.string.secondButton)).get(0);
+        AccessibilityNodeInfo secondButton = getNodeByText(uiAutomation, R.string.secondButton);
         assertEquals(secondButton, rootLinearLayout.getChild(3));
     }
+
+    @MediumTest
+    public void testDrawingOrderInImportantParentFollowsXmlOrder() throws Exception {
+        getInstrumentation().runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                getActivity().findViewById(R.id.firstLinearLayout)
+                        .setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
+            }
+        });
+
+        UiAutomation uiAutomation = getUiAutomation(false);
+        AccessibilityNodeInfo firstTextView = getNodeByText(uiAutomation, R.string.firstTextView);
+        AccessibilityNodeInfo firstEditText = getNodeByText(uiAutomation, R.string.firstEditText);
+        AccessibilityNodeInfo firstButton = getNodeByText(uiAutomation, R.string.firstButton);
+
+        // Drawing order is: firstTextView, firstEditText, firstButton
+        assertTrue(firstTextView.getDrawingOrder() < firstEditText.getDrawingOrder());
+        assertTrue(firstEditText.getDrawingOrder() < firstButton.getDrawingOrder());
+
+        // Confirm that obtaining copies doesn't change our results
+        AccessibilityNodeInfo copyOfFirstEditText = AccessibilityNodeInfo.obtain(firstEditText);
+        assertTrue(firstTextView.getDrawingOrder() < copyOfFirstEditText.getDrawingOrder());
+        assertTrue(copyOfFirstEditText.getDrawingOrder() < firstButton.getDrawingOrder());
+    }
+
+    @MediumTest
+    public void testDrawingOrderGettingAllViewsFollowsXmlOrder() throws Exception {
+        UiAutomation uiAutomation = getUiAutomation(true);
+        AccessibilityNodeInfo firstTextView = getNodeByText(uiAutomation, R.string.firstTextView);
+        AccessibilityNodeInfo firstEditText = getNodeByText(uiAutomation, R.string.firstEditText);
+        AccessibilityNodeInfo firstButton = getNodeByText(uiAutomation, R.string.firstButton);
+
+        // Drawing order is: firstTextView, firstEditText, firstButton
+        assertTrue(firstTextView.getDrawingOrder() < firstEditText.getDrawingOrder());
+        assertTrue(firstEditText.getDrawingOrder() < firstButton.getDrawingOrder());
+    }
+
+    @MediumTest
+    public void testDrawingOrderWithZCoordsDrawsHighestZLast() throws Exception {
+        getInstrumentation().runOnMainSync(new Runnable() {
+           @Override
+           public void run() {
+               AccessibilityViewTreeReportingActivity activity = getActivity();
+               activity.findViewById(R.id.firstTextView).setZ(50);
+               activity.findViewById(R.id.firstEditText).setZ(100);
+           }
+        });
+
+        UiAutomation uiAutomation = getUiAutomation(true);
+        AccessibilityNodeInfo firstTextView = getNodeByText(uiAutomation, R.string.firstTextView);
+        AccessibilityNodeInfo firstEditText = getNodeByText(uiAutomation, R.string.firstEditText);
+        AccessibilityNodeInfo firstButton = getNodeByText(uiAutomation, R.string.firstButton);
+
+        // Drawing order is firstButton (no z), firstTextView (z=50), firstEditText (z=100)
+        assertTrue(firstButton.getDrawingOrder() < firstTextView.getDrawingOrder());
+        assertTrue(firstTextView.getDrawingOrder() < firstEditText.getDrawingOrder());
+    }
+
+    @MediumTest
+    public void testDrawingOrderWithCustomDrawingOrder() throws Exception {
+        getInstrumentation().runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                // Reorganize the hiearchy to replace firstLinearLayout with one that allows us to
+                // control the draw order
+                AccessibilityViewTreeReportingActivity activity = getActivity();
+                LinearLayout rootLinearLayout =
+                        (LinearLayout) activity.findViewById(R.id.rootLinearLayout);
+                LinearLayout firstLinearLayout =
+                        (LinearLayout) activity.findViewById(R.id.firstLinearLayout);
+                View firstTextView = activity.findViewById(R.id.firstTextView);
+                View firstEditText = activity.findViewById(R.id.firstEditText);
+                View firstButton = activity.findViewById(R.id.firstButton);
+                firstLinearLayout.removeAllViews();
+                LinearLayoutWithDrawingOrder layoutWithDrawingOrder =
+                        new LinearLayoutWithDrawingOrder(activity);
+                rootLinearLayout.addView(layoutWithDrawingOrder);
+                layoutWithDrawingOrder.addView(firstTextView);
+                layoutWithDrawingOrder.addView(firstEditText);
+                layoutWithDrawingOrder.addView(firstButton);
+                layoutWithDrawingOrder.childDrawingOrder = new int[] {2, 0, 1};
+            }
+        });
+
+        UiAutomation uiAutomation = getUiAutomation(true);
+        AccessibilityNodeInfo firstTextView = getNodeByText(uiAutomation, R.string.firstTextView);
+        AccessibilityNodeInfo firstEditText = getNodeByText(uiAutomation, R.string.firstEditText);
+        AccessibilityNodeInfo firstButton = getNodeByText(uiAutomation, R.string.firstButton);
+
+        // Drawing order is firstEditText, firstButton, firstTextView
+        assertTrue(firstEditText.getDrawingOrder() < firstButton.getDrawingOrder());
+        assertTrue(firstButton.getDrawingOrder() < firstTextView.getDrawingOrder());
+    }
+
+    @MediumTest
+    public void testDrawingOrderWithNotImportantSiblingConsidersItsChildren() throws Exception {
+        getInstrumentation().runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                // Make the first frame layout a higher Z so it's drawn last
+                getActivity().findViewById(R.id.firstFrameLayout).setZ(100);
+            }
+        });
+        UiAutomation uiAutomation = getUiAutomation(false);
+        AccessibilityNodeInfo secondTextView = getNodeByText(uiAutomation, R.string.secondTextView);
+        AccessibilityNodeInfo secondEditText = getNodeByText(uiAutomation, R.string.secondEditText);
+        AccessibilityNodeInfo secondButton = getNodeByText(uiAutomation, R.string.secondButton);
+        AccessibilityNodeInfo firstFrameLayout =
+                getNodeByText(uiAutomation, R.string.firstFrameLayout);
+        assertTrue(secondTextView.getDrawingOrder() < firstFrameLayout.getDrawingOrder());
+        assertTrue(secondEditText.getDrawingOrder() < firstFrameLayout.getDrawingOrder());
+        assertTrue(secondButton.getDrawingOrder() < firstFrameLayout.getDrawingOrder());
+    }
+
+    @MediumTest
+    public void testDrawingOrderWithNotImportantParentConsidersParentSibling() throws Exception {
+        UiAutomation uiAutomation = getUiAutomation(false);
+        AccessibilityNodeInfo firstFrameLayout =
+                getNodeByText(uiAutomation, R.string.firstFrameLayout);
+        AccessibilityNodeInfo secondTextView = getNodeByText(uiAutomation, R.string.secondTextView);
+        AccessibilityNodeInfo secondEditText = getNodeByText(uiAutomation, R.string.secondEditText);
+        AccessibilityNodeInfo secondButton = getNodeByText(uiAutomation, R.string.secondButton);
+
+        assertTrue(secondTextView.getDrawingOrder() > firstFrameLayout.getDrawingOrder());
+        assertTrue(secondEditText.getDrawingOrder() > firstFrameLayout.getDrawingOrder());
+        assertTrue(secondButton.getDrawingOrder() > firstFrameLayout.getDrawingOrder());
+    }
+
+    @MediumTest
+    public void testDrawingOrderRootNodeHasIndex0() throws Exception {
+        assertEquals(0, getUiAutomation(false).getRootInActiveWindow().getDrawingOrder());
+    }
+
+    @MediumTest
+    public void testAccessibilityImportanceReportingForImportantView() throws Exception {
+        getInstrumentation().runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                // Manually control importance for firstButton
+                AccessibilityViewTreeReportingActivity activity = getActivity();
+                View firstButton = activity.findViewById(R.id.firstButton);
+                firstButton.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
+            }
+        });
+
+        UiAutomation uiAutomation = getUiAutomation(true);
+        AccessibilityNodeInfo firstButtonNode = getNodeByText(uiAutomation, R.string.firstButton);
+        assertTrue(firstButtonNode.isImportantForAccessibility());
+    }
+
+    @MediumTest
+    public void testAccessibilityImportanceReportingForUnimportantView() throws Exception {
+        getInstrumentation().runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                // Manually control importance for firstButton
+                AccessibilityViewTreeReportingActivity activity = getActivity();
+                View firstButton = activity.findViewById(R.id.firstButton);
+                firstButton.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO);
+            }
+        });
+
+        UiAutomation uiAutomation = getUiAutomation(true);
+        AccessibilityNodeInfo firstButtonNode = getNodeByText(uiAutomation, R.string.firstButton);
+        assertFalse(firstButtonNode.isImportantForAccessibility());
+    }
+
+    private UiAutomation getUiAutomation(boolean getNonImportantViews) {
+        UiAutomation uiAutomation = getInstrumentation().getUiAutomation();
+        AccessibilityServiceInfo serviceInfo = uiAutomation.getServiceInfo();
+        serviceInfo.flags &= ~AccessibilityServiceInfo.FLAG_INCLUDE_NOT_IMPORTANT_VIEWS;
+        serviceInfo.flags |= getNonImportantViews ?
+                AccessibilityServiceInfo.FLAG_INCLUDE_NOT_IMPORTANT_VIEWS : 0;
+        uiAutomation.setServiceInfo(serviceInfo);
+        return uiAutomation;
+    }
+
+    private AccessibilityNodeInfo getNodeByText(UiAutomation uiAutomation, int stringId) {
+        return uiAutomation.getRootInActiveWindow()
+                .findAccessibilityNodeInfosByText(getString(stringId)).get(0);
+    }
+
+    class LinearLayoutWithDrawingOrder extends LinearLayout {
+        public int[] childDrawingOrder;
+        LinearLayoutWithDrawingOrder(Context context) {
+            super(context);
+            setChildrenDrawingOrderEnabled(true);
+        }
+
+        @Override
+        protected int getChildDrawingOrder(int childCount, int i) {
+            return childDrawingOrder[i];
+        }
+    }
 }
diff --git a/tests/admin/src/android/admin/cts/DevicePolicyManagerTest.java b/tests/admin/src/android/admin/cts/DevicePolicyManagerTest.java
index 7654a32..17caccb 100644
--- a/tests/admin/src/android/admin/cts/DevicePolicyManagerTest.java
+++ b/tests/admin/src/android/admin/cts/DevicePolicyManagerTest.java
@@ -83,7 +83,14 @@
         mDeviceAdmin = mPackageManager.hasSystemFeature(PackageManager.FEATURE_DEVICE_ADMIN);
         mManagedProfiles = mDeviceAdmin
                 && mPackageManager.hasSystemFeature(PackageManager.FEATURE_MANAGED_USERS);
-        setBlankPassword();
+        if (mDeviceAdmin) {
+            assertTrue("Device owner hasn't been set properly." +
+                            "  Make sure the device doesn't have multiple users and" +
+                            " the primary user has no accounts (and use CTS v2).",
+                    mDevicePolicyManager.isDeviceOwnerApp("android.admin.app"));
+
+            setBlankPassword();
+        }
     }
 
     @Override
@@ -547,6 +554,71 @@
         }
     }
 
+    public void testRequestRemoteBugreport_failIfNotDeviceOwner() {
+        if (!mDeviceAdmin) {
+            Log.w(TAG, "Skipping testRequestRemoteBugreport_failIfNotDeviceOwner");
+            return;
+        }
+        try {
+            mDevicePolicyManager.requestBugreport(mComponent);
+            fail("did not throw expected SecurityException");
+        } catch (SecurityException e) {
+            assertDeviceOwnerMessage(e.getMessage());
+        }
+    }
+
+    public void testSetDeviceLoggingEnabled_failIfNotDeviceOwner() {
+        if (!mDeviceAdmin) {
+            Log.w(TAG, "Skipping testSetDeviceLoggingEnabled_failIfNotDeviceOwner");
+            return;
+        }
+        try {
+            mDevicePolicyManager.setDeviceLoggingEnabled(mComponent, true);
+            fail("did not throw expected SecurityException");
+        } catch (SecurityException e) {
+            assertDeviceOwnerMessage(e.getMessage());
+        }
+    }
+
+    public void testGetDeviceLoggingEnabled_failIfNotDeviceOwner() {
+        if (!mDeviceAdmin) {
+            Log.w(TAG, "Skipping testGetDeviceLoggingEnabled_failIfNotDeviceOwner");
+            return;
+        }
+        try {
+            mDevicePolicyManager.getDeviceLoggingEnabled(mComponent);
+            fail("did not throw expected SecurityException");
+        } catch (SecurityException e) {
+            assertDeviceOwnerMessage(e.getMessage());
+        }
+    }
+
+    public void testRetrieveDeviceLogs_failIfNotDeviceOwner() {
+        if (!mDeviceAdmin) {
+            Log.w(TAG, "Skipping testRetrieveDeviceLogs_failIfNotDeviceOwner");
+            return;
+        }
+        try {
+            mDevicePolicyManager.retrieveDeviceLogs(mComponent);
+            fail("did not throw expected SecurityException");
+        } catch (SecurityException e) {
+            assertDeviceOwnerMessage(e.getMessage());
+        }
+    }
+
+    public void testRetrievePreviousDeviceLogs_failIfNotDeviceOwner() {
+        if (!mDeviceAdmin) {
+            Log.w(TAG, "Skipping testRetrievePreviousDeviceLogs_failIfNotDeviceOwner");
+            return;
+        }
+        try {
+            mDevicePolicyManager.retrievePreviousDeviceLogs(mComponent);
+            fail("did not throw expected SecurityException");
+        } catch (SecurityException e) {
+            assertDeviceOwnerMessage(e.getMessage());
+        }
+    }
+
     public void testCreateUser_failIfNotDeviceOwner() {
         if (!mDeviceAdmin) {
             Log.w(TAG, "Skipping testCreateUser_failIfNotDeviceOwner");
@@ -721,6 +793,19 @@
         }
     }
 
+    public void testCreateAndManageUser_failIfNotDeviceOwner() {
+        if (!mDeviceAdmin) {
+            Log.w(TAG, "Skipping testCreateAndManageUser_failIfNotDeviceOwner");
+            return;
+        }
+        try {
+            mDevicePolicyManager.createAndManageUser(mComponent, "name", mComponent, null, 0);
+            fail("did not throw expected SecurityException");
+        } catch (SecurityException e) {
+            assertDeviceOwnerMessage(e.getMessage());
+        }
+    }
+
     public void testInstallCaCert_failIfNotProfileOwner() {
         if (!mDeviceAdmin) {
             Log.w(TAG, "Skipping testInstallCaCert_failIfNotProfileOwner");
diff --git a/tests/app/src/android/app/cts/DownloadManagerTest.java b/tests/app/src/android/app/cts/DownloadManagerTest.java
index 21a227b..50eb96b 100644
--- a/tests/app/src/android/app/cts/DownloadManagerTest.java
+++ b/tests/app/src/android/app/cts/DownloadManagerTest.java
@@ -474,9 +474,9 @@
     }
 
     private boolean hasInternetConnection() {
-        // TODO: expand this to include devices with ethernet
         final PackageManager pm = getContext().getPackageManager();
         return pm.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)
-                || pm.hasSystemFeature(PackageManager.FEATURE_WIFI);
+                || pm.hasSystemFeature(PackageManager.FEATURE_WIFI)
+                || pm.hasSystemFeature(PackageManager.FEATURE_ETHERNET);
     }
 }
diff --git a/tests/app/src/android/app/cts/PipActivityTest.java b/tests/app/src/android/app/cts/PipActivityTest.java
index a553169..3f053cb 100644
--- a/tests/app/src/android/app/cts/PipActivityTest.java
+++ b/tests/app/src/android/app/cts/PipActivityTest.java
@@ -44,20 +44,20 @@
                 final boolean supportsPip =
                         mActivity.getPackageManager().hasSystemFeature(FEATURE_PICTURE_IN_PICTURE);
                 if (supportsPip) {
-                    mActivity.enterPictureInPictureMode();
-                    assertTrue(mActivity.inMultiWindowMode());
-                    assertTrue(mActivity.inPictureInPictureMode());
+                    mActivity.enterPictureInPicture();
+                    assertTrue(mActivity.inMultiWindow());
+                    assertTrue(mActivity.inPictureInPicture());
                 } else {
                     boolean pipSupportDisabled = false;
                     try {
-                        mActivity.enterPictureInPictureMode();
+                        mActivity.enterPictureInPicture();
                     } catch (IllegalStateException e) {
                         // Pip not supported
                         pipSupportDisabled = true;
                     }
                     assertTrue(pipSupportDisabled);
-                    assertFalse(mActivity.inMultiWindowMode());
-                    assertFalse(mActivity.inPictureInPictureMode());
+                    assertFalse(mActivity.inMultiWindow());
+                    assertFalse(mActivity.inPictureInPicture());
                 }
             }
         });
diff --git a/tests/app/src/android/app/cts/PipNotResizeableActivityTest.java b/tests/app/src/android/app/cts/PipNotResizeableActivityTest.java
index 4f7f63e..e2908cd 100644
--- a/tests/app/src/android/app/cts/PipNotResizeableActivityTest.java
+++ b/tests/app/src/android/app/cts/PipNotResizeableActivityTest.java
@@ -42,7 +42,7 @@
                 public void run() {
                     boolean pipSupportDisabled = false;
                     try {
-                        mActivity.enterPictureInPictureMode();
+                        mActivity.enterPictureInPicture();
                     } catch (IllegalStateException e) {
                         // Pip not supported
                         pipSupportDisabled = true;
@@ -51,8 +51,8 @@
                         pipSupportDisabled = true;
                     }
                     assertTrue(pipSupportDisabled);
-                    assertFalse(mActivity.inMultiWindowMode());
-                    assertFalse(mActivity.inPictureInPictureMode());
+                    assertFalse(mActivity.inMultiWindow());
+                    assertFalse(mActivity.inPictureInPicture());
                 }
             });
             mInstrumentation.waitForIdleSync();
diff --git a/tests/app/src/android/app/cts/PipNotSupportedActivityTest.java b/tests/app/src/android/app/cts/PipNotSupportedActivityTest.java
index 245421a..c9ce69c 100644
--- a/tests/app/src/android/app/cts/PipNotSupportedActivityTest.java
+++ b/tests/app/src/android/app/cts/PipNotSupportedActivityTest.java
@@ -42,7 +42,7 @@
             public void run() {
                 boolean pipSupportDisabled = false;
                 try {
-                    mActivity.enterPictureInPictureMode();
+                    mActivity.enterPictureInPicture();
                 } catch (IllegalStateException e) {
                     // Pip not supported
                     pipSupportDisabled = true;
@@ -51,8 +51,8 @@
                     pipSupportDisabled = true;
                 }
                 assertTrue(pipSupportDisabled);
-                assertFalse(mActivity.inMultiWindowMode());
-                assertFalse(mActivity.inPictureInPictureMode());
+                assertFalse(mActivity.inMultiWindow());
+                assertFalse(mActivity.inPictureInPicture());
             }
         });
         mInstrumentation.waitForIdleSync();
diff --git a/tests/app/src/android/app/cts/SystemFeaturesTest.java b/tests/app/src/android/app/cts/SystemFeaturesTest.java
index 028ca89..c34ecdf 100644
--- a/tests/app/src/android/app/cts/SystemFeaturesTest.java
+++ b/tests/app/src/android/app/cts/SystemFeaturesTest.java
@@ -146,7 +146,8 @@
             CameraCharacteristics chars = mCameraManager.getCameraCharacteristics(cameraId);
             Integer hwLevel = chars.get(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL);
             int[] capabilities = chars.get(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES);
-            if (hwLevel == CameraMetadata.INFO_SUPPORTED_HARDWARE_LEVEL_FULL) {
+            if (hwLevel == CameraMetadata.INFO_SUPPORTED_HARDWARE_LEVEL_FULL ||
+                    hwLevel >= CameraMetadata.INFO_SUPPORTED_HARDWARE_LEVEL_3) {
                 fullCamera = true;
             }
             for (int capability : capabilities) {
diff --git a/tests/camera/Android.mk b/tests/camera/Android.mk
index d072975..4d653a6 100644
--- a/tests/camera/Android.mk
+++ b/tests/camera/Android.mk
@@ -20,11 +20,16 @@
 
 LOCAL_MODULE_TAGS := tests
 
+# Include both the 32 and 64 bit versions
+LOCAL_MULTILIB := both
+
 LOCAL_STATIC_JAVA_LIBRARIES := ctsdeviceutil \
 	ctstestrunner \
 	mockito-target \
 	android-ex-camera2
 
+LOCAL_JNI_SHARED_LIBRARIES := libctscamera2ndk_jni libnativehelper_compat_libc++
+
 LOCAL_SRC_FILES := $(call all-java-files-under, src) $(call all-renderscript-files-under, src)
 
 # Tag this module as a cts_v2 test artifact
@@ -39,3 +44,5 @@
 cts_runtime_hint := 120
 
 include $(BUILD_CTS_PACKAGE)
+
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/tests/camera/libcamera2ndkjni/Android.mk b/tests/camera/libcamera2ndkjni/Android.mk
new file mode 100644
index 0000000..89690a2
--- /dev/null
+++ b/tests/camera/libcamera2ndkjni/Android.mk
@@ -0,0 +1,40 @@
+# 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.
+#
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE    := libctscamera2ndk_jni
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_SRC_FILES := \
+	native-camera-jni.cpp
+
+LOCAL_C_INCLUDES := \
+	$(JNI_H_INCLUDE) \
+	system/core/include \
+	frameworks/av/include/camera/ndk \
+	frameworks/av/include/ndk \
+
+LOCAL_SHARED_LIBRARIES := libandroid \
+    libnativehelper_compat_libc++ \
+    liblog \
+    libcamera2ndk \
+    libmediandk
+
+LOCAL_CXX_STL := libc++_static
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/tests/camera/libcamera2ndkjni/native-camera-jni.cpp b/tests/camera/libcamera2ndkjni/native-camera-jni.cpp
new file mode 100644
index 0000000..84e2b39
--- /dev/null
+++ b/tests/camera/libcamera2ndkjni/native-camera-jni.cpp
@@ -0,0 +1,1808 @@
+/*
+ * 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.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "NativeCamera"
+#include <log/log.h>
+
+#include <string>
+#include <map>
+#include <mutex>
+#include <unistd.h>
+#include <assert.h>
+#include <jni.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <android/native_window_jni.h>
+
+#include "NdkCameraError.h"
+#include "NdkCameraManager.h"
+#include "NdkCameraDevice.h"
+#include "NdkCameraCaptureSession.h"
+#include "NdkImage.h"
+#include "NdkImageReader.h"
+
+#define LOG_ERROR(buf, ...) sprintf(buf, __VA_ARGS__); \
+                            ALOGE("%s", buf);
+
+namespace {
+    const int MAX_ERROR_STRING_LEN = 512;
+    char errorString[MAX_ERROR_STRING_LEN];
+}
+
+class CameraServiceListener {
+  public:
+    static void onAvailable(void* obj, const char* cameraId) {
+        ALOGV("Camera %s onAvailable", cameraId);
+        if (obj == nullptr) {
+            return;
+        }
+        CameraServiceListener* thiz = reinterpret_cast<CameraServiceListener*>(obj);
+        std::lock_guard<std::mutex> lock(thiz->mMutex);
+        thiz->mOnAvailableCount++;
+        thiz->mAvailableMap[cameraId] = true;
+        return;
+    }
+
+    static void onUnavailable(void* obj, const char* cameraId) {
+        ALOGV("Camera %s onUnavailable", cameraId);
+        if (obj == nullptr) {
+            return;
+        }
+        CameraServiceListener* thiz = reinterpret_cast<CameraServiceListener*>(obj);
+        std::lock_guard<std::mutex> lock(thiz->mMutex);
+        thiz->mOnUnavailableCount++;
+        thiz->mAvailableMap[cameraId] = false;
+        return;
+    }
+
+    void resetCount() {
+        std::lock_guard<std::mutex> lock(mMutex);
+        mOnAvailableCount = 0;
+        mOnUnavailableCount = 0;
+        return;
+    }
+
+    int getAvailableCount() {
+        std::lock_guard<std::mutex> lock(mMutex);
+        return mOnAvailableCount;
+    }
+
+    int getUnavailableCount() {
+        std::lock_guard<std::mutex> lock(mMutex);
+        return mOnUnavailableCount;
+    }
+
+    bool isAvailable(const char* cameraId) {
+        std::lock_guard<std::mutex> lock(mMutex);
+        if (mAvailableMap.count(cameraId) == 0) {
+            return false;
+        }
+        return mAvailableMap[cameraId];
+    }
+
+  private:
+    std::mutex mMutex;
+    int mOnAvailableCount = 0;
+    int mOnUnavailableCount = 0;
+    std::map<std::string, bool> mAvailableMap;
+};
+
+class CameraDeviceListener {
+  public:
+    static void onDisconnected(void* obj, ACameraDevice* device) {
+        ALOGV("Camera %s is disconnected!", ACameraDevice_getId(device));
+        if (obj == nullptr) {
+            return;
+        }
+        CameraDeviceListener* thiz = reinterpret_cast<CameraDeviceListener*>(obj);
+        std::lock_guard<std::mutex> lock(thiz->mMutex);
+        thiz->mOnDisconnect++;
+        return;
+    }
+
+    static void onError(void* obj, ACameraDevice* device, int errorCode) {
+        ALOGV("Camera %s receive error %d!", ACameraDevice_getId(device), errorCode);
+        if (obj == nullptr) {
+            return;
+        }
+        CameraDeviceListener* thiz = reinterpret_cast<CameraDeviceListener*>(obj);
+        std::lock_guard<std::mutex> lock(thiz->mMutex);
+        thiz->mOnError++;
+        thiz->mLatestError = errorCode;
+        return;
+    }
+
+  private:
+    std::mutex mMutex;
+    int mOnDisconnect = 0;
+    int mOnError = 0;
+    int mLatestError = 0;
+};
+
+class CaptureSessionListener {
+
+  public:
+    static void onClosed(void* obj, ACameraCaptureSession *session) {
+        // TODO: might want an API to query cameraId even session is closed?
+        ALOGV("Session %p is closed!", session);
+        if (obj == nullptr) {
+            return;
+        }
+        CaptureSessionListener* thiz = reinterpret_cast<CaptureSessionListener*>(obj);
+        std::lock_guard<std::mutex> lock(thiz->mMutex);
+        thiz->mIsClosed = true;
+        thiz->mOnClosed++; // Should never > 1
+    }
+
+    static void onReady(void* obj, ACameraCaptureSession *session) {
+        ALOGV("%s", __FUNCTION__);
+        if (obj == nullptr) {
+            return;
+        }
+        CaptureSessionListener* thiz = reinterpret_cast<CaptureSessionListener*>(obj);
+        std::lock_guard<std::mutex> lock(thiz->mMutex);
+        ACameraDevice* device = nullptr;
+        camera_status_t ret = ACameraCaptureSession_getDevice(session, &device);
+        // There will be one onReady fired after session closed
+        if (ret != ACAMERA_OK && !thiz->mIsClosed) {
+            ALOGE("%s Getting camera device from session callback failed!",
+                    __FUNCTION__);
+            thiz->mInError = true;
+        }
+        ALOGV("Session for camera %s is ready!", ACameraDevice_getId(device));
+        thiz->mIsIdle = true;
+        thiz->mOnReady++;
+    }
+
+    static void onActive(void* obj, ACameraCaptureSession *session) {
+        ALOGV("%s", __FUNCTION__);
+        if (obj == nullptr) {
+            return;
+        }
+        CaptureSessionListener* thiz = reinterpret_cast<CaptureSessionListener*>(obj);
+        std::lock_guard<std::mutex> lock(thiz->mMutex);
+        ACameraDevice* device = nullptr;
+        camera_status_t ret = ACameraCaptureSession_getDevice(session, &device);
+        if (ret != ACAMERA_OK) {
+            ALOGE("%s Getting camera device from session callback failed!",
+                    __FUNCTION__);
+            thiz->mInError = true;
+        }
+        ALOGV("Session for camera %s is busy!", ACameraDevice_getId(device));
+        thiz->mIsIdle = false;
+        thiz->mOnActive;
+    }
+
+    bool isClosed() {
+        std::lock_guard<std::mutex> lock(mMutex);
+        return mIsClosed;
+    }
+
+    bool isIdle() {
+        std::lock_guard<std::mutex> lock(mMutex);
+        return mIsIdle;
+    }
+
+    bool isInError() {
+        std::lock_guard<std::mutex> lock(mMutex);
+        return mInError;
+    }
+
+    int onClosedCount()  {
+        std::lock_guard<std::mutex> lock(mMutex);
+        return mOnClosed;
+    }
+
+    int onReadyCount()  {
+        std::lock_guard<std::mutex> lock(mMutex);
+        return mOnReady;
+    }
+
+    int onActiveCount()  {
+        std::lock_guard<std::mutex> lock(mMutex);
+        return mOnActive;
+    }
+
+    void reset() {
+        std::lock_guard<std::mutex> lock(mMutex);
+        mIsClosed = false;
+        mIsIdle = true;
+        mInError = false;
+        mOnClosed = 0;
+        mOnReady = 0;
+        mOnActive = 0;
+    }
+
+  private:
+    std::mutex mMutex;
+    bool mIsClosed = false;
+    bool mIsIdle = true;
+    bool mInError = false; // should always stay false
+    int mOnClosed = 0;
+    int mOnReady = 0;
+    int mOnActive = 0;
+};
+
+class ImageReaderListener {
+  public:
+    static void onImageAvailable(void* obj, AImageReader* reader) {
+        ALOGV("%s", __FUNCTION__);
+        if (obj == nullptr) {
+            return;
+        }
+        ImageReaderListener* thiz = reinterpret_cast<ImageReaderListener*>(obj);
+        std::lock_guard<std::mutex> lock(thiz->mMutex);
+        thiz->mOnImageAvailableCount++;
+
+        AImage* img = nullptr;
+        media_status_t ret = AImageReader_acquireNextImage(reader, &img);
+        if (ret != AMEDIA_OK || img == nullptr) {
+            ALOGE("%s: acquire image from reader %p failed! ret: %d, img %p",
+                    __FUNCTION__, reader, ret, img);
+            return;
+        }
+
+        // TODO: validate image content
+        int32_t format = -1;
+        ret = AImage_getFormat(img, &format);
+        if (ret != AMEDIA_OK || format == -1) {
+            ALOGE("%s: get format for image %p failed! ret: %d, format %d",
+                    __FUNCTION__, img, ret, format);
+        }
+
+        // Save jpeg to SD card
+        if (thiz->mDumpFilePathBase && format == AIMAGE_FORMAT_JPEG) {
+            int32_t numPlanes = 0;
+            ret = AImage_getNumberOfPlanes(img, &numPlanes);
+            if (ret != AMEDIA_OK || numPlanes != 1) {
+                ALOGE("%s: get numPlanes for image %p failed! ret: %d, numPlanes %d",
+                        __FUNCTION__, img, ret, numPlanes);
+                AImage_delete(img);
+                return;
+            }
+
+            int32_t width = -1, height = -1;
+            ret = AImage_getWidth(img, &width);
+            if (ret != AMEDIA_OK || width <= 0) {
+                ALOGE("%s: get width for image %p failed! ret: %d, width %d",
+                        __FUNCTION__, img, ret, width);
+                AImage_delete(img);
+                return;
+            }
+
+            ret = AImage_getHeight(img, &height);
+            if (ret != AMEDIA_OK || height <= 0) {
+                ALOGE("%s: get height for image %p failed! ret: %d, height %d",
+                        __FUNCTION__, img, ret, height);
+                AImage_delete(img);
+                return;
+            }
+
+            uint8_t* data = nullptr;
+            int dataLength = 0;
+            ret =  AImage_getPlaneData(img, /*planeIdx*/0, &data, &dataLength);
+            if (ret != AMEDIA_OK || data == nullptr || dataLength <= 0) {
+                ALOGE("%s: get jpeg data for image %p failed! ret: %d, data %p, len %d",
+                        __FUNCTION__, img, ret, data, dataLength);
+                AImage_delete(img);
+                return;
+            }
+
+#if 0
+            char dumpFilePath[512];
+            sprintf(dumpFilePath, "%s/%dx%d.jpg", thiz->mDumpFilePathBase, width, height);
+            ALOGI("Writing jpeg file to %s", dumpFilePath);
+            FILE* file = fopen(dumpFilePath,"w+");
+
+            if (file != nullptr) {
+                fwrite(data, 1, dataLength, file);
+                fflush(file);
+                fclose(file);
+            }
+#endif
+        }
+
+        AImage_delete(img);
+    }
+
+    int onImageAvailableCount() {
+        std::lock_guard<std::mutex> lock(mMutex);
+        return mOnImageAvailableCount;
+    }
+
+    void setDumpFilePathBase(const char* path) {
+        std::lock_guard<std::mutex> lock(mMutex);
+        mDumpFilePathBase = path;
+    }
+
+    void reset() {
+        std::lock_guard<std::mutex> lock(mMutex);
+        mOnImageAvailableCount = 0;
+        mDumpFilePathBase = nullptr;
+    }
+
+  private:
+    // TODO: add mReader to make sure each listener is associated to one reader?
+    std::mutex mMutex;
+    int mOnImageAvailableCount = 0;
+    const char* mDumpFilePathBase = nullptr;
+};
+
+class StaticInfo {
+  public:
+    StaticInfo(ACameraMetadata* chars) : mChars(chars) {}
+
+    bool isColorOutputSupported() {
+        return isCapabilitySupported(ACAMERA_REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE);
+    }
+
+    bool isCapabilitySupported(acamera_metadata_enum_android_request_available_capabilities_t cap) {
+        ACameraMetadata_const_entry entry;
+        ACameraMetadata_getConstEntry(mChars, ACAMERA_REQUEST_AVAILABLE_CAPABILITIES, &entry);
+        for (uint32_t i = 0; i < entry.count; i++) {
+            if (entry.data.u8[i] == cap) {
+                return true;
+            }
+        }
+        return false;
+    }
+  private:
+    const ACameraMetadata* mChars;
+};
+
+class PreviewTestCase {
+  public:
+    ~PreviewTestCase() {
+        resetCamera();
+        deInit();
+        if (mCameraManager) {
+            ACameraManager_delete(mCameraManager);
+            mCameraManager = nullptr;
+        }
+    }
+
+    PreviewTestCase() {
+        // create is guaranteed to succeed;
+        createManager();
+    }
+
+    // Free all resources except camera manager
+    void resetCamera() {
+        mReaderListener.reset();
+        mSessionListener.reset();
+        if (mSession) {
+            ACameraCaptureSession_close(mSession);
+            mSession = nullptr;
+        }
+        if (mDevice) {
+            ACameraDevice_close(mDevice);
+            mDevice = nullptr;
+        }
+        if (mImgReader) {
+            AImageReader_delete(mImgReader);
+            // No need to call ANativeWindow_release on imageReaderAnw
+            mImgReaderAnw = nullptr;
+            mImgReader = nullptr;
+        }
+        if (mPreviewAnw) {
+            ANativeWindow_release(mPreviewAnw);
+            mPreviewAnw = nullptr;
+        }
+        if (mOutputs) {
+            ACaptureSessionOutputContainer_free(mOutputs);
+            mOutputs = nullptr;
+        }
+        if (mPreviewOutput) {
+            ACaptureSessionOutput_free(mPreviewOutput);
+            mPreviewOutput = nullptr;
+        }
+        if (mImgReaderOutput) {
+            ACaptureSessionOutput_free(mImgReaderOutput);
+            mImgReaderOutput = nullptr;
+        }
+        if (mPreviewRequest) {
+            ACaptureRequest_free(mPreviewRequest);
+            mPreviewRequest = nullptr;
+        }
+        if (mStillRequest) {
+            ACaptureRequest_free(mStillRequest);
+            mStillRequest = nullptr;
+        }
+        if (mReqPreviewOutput) {
+            ACameraOutputTarget_free(mReqPreviewOutput);
+            mReqPreviewOutput = nullptr;
+        }
+        if (mReqImgReaderOutput) {
+            ACameraOutputTarget_free(mReqImgReaderOutput);
+            mReqImgReaderOutput = nullptr;
+        }
+
+        mImgReaderInited = false;
+        mPreviewInited = false;
+    }
+
+    camera_status_t initWithErrorLog() {
+        camera_status_t ret = ACameraManager_getCameraIdList(
+                mCameraManager, &mCameraIdList);
+        if (ret != ACAMERA_OK) {
+            LOG_ERROR(errorString, "Get camera id list failed: ret %d", ret);
+            return ret;
+        }
+        ret = ACameraManager_registerAvailabilityCallback(mCameraManager, &mServiceCb);
+        if (ret != ACAMERA_OK) {
+            LOG_ERROR(errorString, "Register availability callback failed: ret %d", ret);
+            return ret;
+        }
+        mMgrInited = true;
+        return ACAMERA_OK;
+    }
+
+    camera_status_t deInit () {
+        if (!mMgrInited) {
+            return ACAMERA_OK;
+        }
+
+        camera_status_t ret = ACameraManager_unregisterAvailabilityCallback(
+                mCameraManager, &mServiceCb);
+        if (ret != ACAMERA_OK) {
+            ALOGE("Unregister availability callback failed: ret %d", ret);
+            return ret;
+        }
+
+        if (mCameraIdList) {
+            ACameraManager_deleteCameraIdList(mCameraIdList);
+            mCameraIdList = nullptr;
+        }
+        mMgrInited = false;
+        return ACAMERA_OK;
+    }
+
+    int getNumCameras() {
+        if (!mMgrInited || !mCameraIdList) {
+            return -1;
+        }
+        return mCameraIdList->numCameras;
+    }
+
+    const char* getCameraId(int idx) {
+        if (!mMgrInited || !mCameraIdList || idx < 0 || idx >= mCameraIdList->numCameras) {
+            return nullptr;
+        }
+        return mCameraIdList->cameraIds[idx];
+    }
+
+    camera_status_t openCamera(const char* cameraId) {
+        if (mDevice) {
+            ALOGE("Cannot open camera before closing previously open one");
+            return ACAMERA_ERROR_INVALID_PARAMETER;
+        }
+        mCameraId = cameraId;
+        return ACameraManager_openCamera(mCameraManager, cameraId, &mDeviceCb, &mDevice);
+    }
+
+    camera_status_t closeCamera() {
+        camera_status_t ret = ACameraDevice_close(mDevice);
+        mDevice = nullptr;
+        return ret;
+    }
+
+    bool isCameraAvailable(const char* cameraId) {
+        if (!mMgrInited) {
+            ALOGE("Camera service listener has not been registered!");
+        }
+        return mServiceListener.isAvailable(cameraId);
+    }
+
+    media_status_t initImageReaderWithErrorLog(
+            int32_t width, int32_t height, int32_t format, int32_t maxImages) {
+        if (mImgReader || mImgReaderAnw) {
+            LOG_ERROR(errorString, "Cannot init image reader before closing existing one");
+            return AMEDIA_ERROR_UNKNOWN;
+        }
+
+        media_status_t ret = AImageReader_new(
+                width, height, format,
+                maxImages, &mImgReader);
+        if (ret != AMEDIA_OK) {
+            LOG_ERROR(errorString, "Create image reader. ret %d", ret);
+            return ret;
+        }
+        if (mImgReader == nullptr) {
+            LOG_ERROR(errorString, "null image reader created");
+            return AMEDIA_ERROR_UNKNOWN;
+        }
+
+        ret = AImageReader_setImageListener(
+                mImgReader, &mReaderCb);
+        if (ret != AMEDIA_OK) {
+            LOG_ERROR(errorString, "Set AImageReader listener failed. ret %d", ret);
+            return ret;
+        }
+
+        ret = AImageReader_getWindow(mImgReader, &mImgReaderAnw);
+        if (ret != AMEDIA_OK) {
+            LOG_ERROR(errorString, "AImageReader_getWindow failed. ret %d", ret);
+            return ret;
+        }
+        if (mImgReaderAnw == nullptr) {
+            LOG_ERROR(errorString, "Null ANW from AImageReader!");
+            return AMEDIA_ERROR_UNKNOWN;
+        }
+        mImgReaderInited = true;
+        return AMEDIA_OK;
+    }
+
+    ANativeWindow* initPreviewAnw(JNIEnv* env, jobject jSurface) {
+        if (mPreviewAnw) {
+            ALOGE("Cannot init preview twice!");
+            return nullptr;
+        }
+        mPreviewAnw =  ANativeWindow_fromSurface(env, jSurface);
+        mPreviewInited = true;
+        return mPreviewAnw;
+    }
+
+    camera_status_t createCaptureSessionWithLog() {
+        if (mSession) {
+            LOG_ERROR(errorString, "Cannot create session before closing existing one");
+            return ACAMERA_ERROR_UNKNOWN;
+        }
+
+        if (!mMgrInited || (!mImgReaderInited && !mPreviewInited)) {
+            LOG_ERROR(errorString, "Cannot create session. mgrInit %d readerInit %d previewInit %d",
+                    mMgrInited, mImgReaderInited, mPreviewInited);
+            return ACAMERA_ERROR_UNKNOWN;
+        }
+
+        camera_status_t ret = ACaptureSessionOutputContainer_create(&mOutputs);
+        if (ret != ACAMERA_OK) {
+            LOG_ERROR(errorString, "Create capture session output container failed. ret %d", ret);
+            return ret;
+        }
+
+        if (mImgReaderInited) {
+            ret = ACaptureSessionOutput_create(mImgReaderAnw, &mImgReaderOutput);
+            if (ret != ACAMERA_OK || mImgReaderOutput == nullptr) {
+                LOG_ERROR(errorString,
+                        "Sesssion image reader output create fail! ret %d output %p",
+                        ret, mImgReaderOutput);
+                if (ret == ACAMERA_OK) {
+                    ret = ACAMERA_ERROR_UNKNOWN; // ret OK but output is null
+                }
+                return ret;
+            }
+
+            ret = ACaptureSessionOutputContainer_add(mOutputs, mImgReaderOutput);
+            if (ret != ACAMERA_OK) {
+                LOG_ERROR(errorString, "Sesssion image reader output add failed! ret %d", ret);
+                return ret;
+            }
+        }
+
+        if (mPreviewInited) {
+            ret = ACaptureSessionOutput_create(mPreviewAnw, &mPreviewOutput);
+            if (ret != ACAMERA_OK || mPreviewOutput == nullptr) {
+                LOG_ERROR(errorString,
+                        "Sesssion preview output create fail! ret %d output %p",
+                        ret, mPreviewOutput);
+                if (ret == ACAMERA_OK) {
+                    ret = ACAMERA_ERROR_UNKNOWN; // ret OK but output is null
+                }
+                return ret;
+            }
+
+            ret = ACaptureSessionOutputContainer_add(mOutputs, mPreviewOutput);
+            if (ret != ACAMERA_OK) {
+                LOG_ERROR(errorString, "Sesssion preview output add failed! ret %d", ret);
+                return ret;
+            }
+        }
+
+        ret = ACameraDevice_createCaptureSession(
+                mDevice, mOutputs, &mSessionCb, &mSession);
+        if (ret != ACAMERA_OK || mSession == nullptr) {
+            LOG_ERROR(errorString, "Create session for camera %s failed. ret %d session %p",
+                    mCameraId, ret, mSession);
+            if (ret == ACAMERA_OK) {
+                ret = ACAMERA_ERROR_UNKNOWN; // ret OK but session is null
+            }
+            return ret;
+        }
+
+        return ACAMERA_OK;
+    }
+
+    void closeSession() {
+        if (mSession != nullptr) {
+            ACameraCaptureSession_close(mSession);
+        }
+        if (mOutputs) {
+            ACaptureSessionOutputContainer_free(mOutputs);
+            mOutputs = nullptr;
+        }
+        if (mPreviewOutput) {
+            ACaptureSessionOutput_free(mPreviewOutput);
+            mPreviewOutput = nullptr;
+        }
+        if (mImgReaderOutput) {
+            ACaptureSessionOutput_free(mImgReaderOutput);
+            mImgReaderOutput = nullptr;
+        }
+        mSession = nullptr;
+    }
+
+    camera_status_t createRequestsWithErrorLog() {
+        if (mPreviewRequest || mStillRequest) {
+            LOG_ERROR(errorString, "Cannot create requests before deleteing existing one");
+            return ACAMERA_ERROR_UNKNOWN;
+        }
+
+        if (mDevice == nullptr || (!mPreviewInited && !mImgReaderInited)) {
+            LOG_ERROR(errorString,
+                    "Cannot create request. device %p previewInit %d readeInit %d",
+                    mDevice, mPreviewInited, mImgReaderInited);
+            return ACAMERA_ERROR_UNKNOWN;
+        }
+
+        camera_status_t ret;
+        if (mPreviewInited) {
+            ret = ACameraDevice_createCaptureRequest(
+                    mDevice, TEMPLATE_PREVIEW, &mPreviewRequest);
+            if (ret != ACAMERA_OK) {
+                LOG_ERROR(errorString, "Camera %s create preview request failed. ret %d",
+                        mCameraId, ret);
+                return ret;
+            }
+
+            ret = ACameraOutputTarget_create(mPreviewAnw, &mReqPreviewOutput);
+            if (ret != ACAMERA_OK) {
+                LOG_ERROR(errorString,
+                        "Camera %s create request preview output target failed. ret %d",
+                        mCameraId, ret);
+                return ret;
+            }
+
+            ret = ACaptureRequest_addTarget(mPreviewRequest, mReqPreviewOutput);
+            if (ret != ACAMERA_OK) {
+                LOG_ERROR(errorString, "Camera %s add preview request output failed. ret %d",
+                        mCameraId, ret);
+                return ret;
+            }
+        } else {
+            ALOGI("Preview not inited. Will not create preview request!");
+        }
+
+        if (mImgReaderInited) {
+            ret = ACameraDevice_createCaptureRequest(
+                    mDevice, TEMPLATE_STILL_CAPTURE, &mStillRequest);
+            if (ret != ACAMERA_OK) {
+                LOG_ERROR(errorString, "Camera %s create still request failed. ret %d",
+                        mCameraId, ret);
+                return ret;
+            }
+
+            ret = ACameraOutputTarget_create(mImgReaderAnw, &mReqImgReaderOutput);
+            if (ret != ACAMERA_OK) {
+                LOG_ERROR(errorString,
+                        "Camera %s create request reader output target failed. ret %d",
+                        mCameraId, ret);
+                return ret;
+            }
+
+            ret = ACaptureRequest_addTarget(mStillRequest, mReqImgReaderOutput);
+            if (ret != ACAMERA_OK) {
+                LOG_ERROR(errorString, "Camera %s add still request output failed. ret %d",
+                        mCameraId, ret);
+                return ret;
+            }
+
+            if (mPreviewInited) {
+                ret = ACaptureRequest_addTarget(mStillRequest, mReqPreviewOutput);
+                if (ret != ACAMERA_OK) {
+                    LOG_ERROR(errorString,
+                            "Camera %s add still request preview output failed. ret %d",
+                            mCameraId, ret);
+                    return ret;
+                }
+            }
+        } else {
+            ALOGI("AImageReader not inited. Will not create still request!");
+        }
+
+        return ACAMERA_OK;
+    }
+
+    camera_status_t startPreview() {
+        if (mSession == nullptr || mPreviewRequest == nullptr) {
+            ALOGE("Testcase cannot start preview: session %p, preview request %p",
+                    mSession, mPreviewRequest);
+            return ACAMERA_ERROR_UNKNOWN;
+        }
+        int previewSeqId;
+        return ACameraCaptureSession_setRepeatingRequest(
+                mSession, nullptr, 1, &mPreviewRequest, &previewSeqId);
+    }
+
+    camera_status_t takePicture() {
+        if (mSession == nullptr || mStillRequest == nullptr) {
+            ALOGE("Testcase cannot take picture: session %p, still request %p",
+                    mSession, mStillRequest);
+            return ACAMERA_ERROR_UNKNOWN;
+        }
+        int seqId;
+        return ACameraCaptureSession_capture(
+                mSession, nullptr, 1, &mStillRequest, &seqId);
+    }
+
+    int getReaderImageCount() {
+        return mReaderListener.onImageAvailableCount();
+    }
+
+    camera_status_t resetWithErrorLog() {
+        camera_status_t ret;
+
+        mReaderListener.reset();
+        closeSession();
+
+        for (int i = 0; i < 50; i++) {
+            usleep(100000); // sleep 100ms
+            if (mSessionListener.isClosed()) {
+                ALOGI("Session take ~%d ms to close", i*100);
+                break;
+            }
+        }
+
+        if (!mSessionListener.isClosed() || mSessionListener.onClosedCount() != 1) {
+            LOG_ERROR(errorString,
+                    "Session for camera %s close error. isClosde %d close count %d",
+                    mCameraId, mSessionListener.isClosed(), mSessionListener.onClosedCount());
+            return ACAMERA_ERROR_UNKNOWN;
+        }
+        mSessionListener.reset();
+
+        ret = closeCamera();
+        if (ret != ACAMERA_OK) {
+            LOG_ERROR(errorString, "Close camera device %s failure. ret %d", mCameraId, ret);
+            return ret;
+        }
+
+        resetCamera();
+        return ACAMERA_OK;
+    }
+
+    void setDumpFilePathBase(const char* path) {
+        mReaderListener.setDumpFilePathBase(path);
+    }
+
+    CaptureSessionListener* getSessionListener() {
+        return &mSessionListener;
+    }
+
+  private:
+    ACameraManager* createManager() {
+        if (!mCameraManager) {
+            mCameraManager = ACameraManager_create();
+        }
+        return mCameraManager;
+    }
+
+    CameraServiceListener mServiceListener;
+    ACameraManager_AvailabilityCallbacks mServiceCb {
+        &mServiceListener,
+        CameraServiceListener::onAvailable,
+        CameraServiceListener::onUnavailable
+    };
+    CameraDeviceListener mDeviceListener;
+    ACameraDevice_StateCallbacks mDeviceCb {
+        &mDeviceListener,
+        CameraDeviceListener::onDisconnected,
+        CameraDeviceListener::onError
+    };
+    CaptureSessionListener mSessionListener;
+    ACameraCaptureSession_stateCallbacks mSessionCb {
+        &mSessionListener,
+        CaptureSessionListener::onClosed,
+        CaptureSessionListener::onReady,
+        CaptureSessionListener::onActive
+    };
+
+    // TODO: capture listeners
+    ImageReaderListener mReaderListener;
+    AImageReader_ImageListener mReaderCb {
+        &mReaderListener,
+        ImageReaderListener::onImageAvailable
+    };
+
+    ACameraIdList* mCameraIdList = nullptr;
+    ACameraDevice* mDevice = nullptr;
+    AImageReader* mImgReader = nullptr;
+    ANativeWindow* mImgReaderAnw = nullptr;
+    ANativeWindow* mPreviewAnw = nullptr;
+    ACameraManager* mCameraManager = nullptr;
+    ACaptureSessionOutputContainer* mOutputs = nullptr;
+    ACaptureSessionOutput* mPreviewOutput = nullptr;
+    ACaptureSessionOutput* mImgReaderOutput = nullptr;
+    ACameraCaptureSession* mSession = nullptr;
+    ACaptureRequest* mPreviewRequest = nullptr;
+    ACaptureRequest* mStillRequest = nullptr;
+    ACameraOutputTarget* mReqPreviewOutput = nullptr;
+    ACameraOutputTarget* mReqImgReaderOutput = nullptr;
+    const char* mCameraId;
+
+    bool mMgrInited = false; // cameraId, serviceListener
+    bool mImgReaderInited = false;
+    bool mPreviewInited = false;
+};
+
+jint throwAssertionError(JNIEnv* env, const char* message)
+{
+    jclass assertionClass;
+    const char* className = "junit/framework/AssertionFailedError";
+
+    assertionClass = env->FindClass(className);
+    if (assertionClass == nullptr) {
+        ALOGE("Native throw error: cannot find class %s", className);
+        return -1;
+    }
+    return env->ThrowNew(assertionClass, message);
+}
+
+extern "C" jboolean
+Java_android_hardware_camera2_cts_NativeCameraManagerTest_\
+testCameraManagerGetAndCloseNative(
+        JNIEnv* env, jclass /*clazz*/) {
+    bool pass = false;
+    ALOGV("%s", __FUNCTION__);
+    ACameraManager* cameraManager2 = nullptr;
+    ACameraManager* cameraManager3 = nullptr;
+    ACameraManager* cameraManager4 = nullptr;
+    camera_status_t ret = ACAMERA_OK;
+    ACameraManager* cameraManager = ACameraManager_create();
+    if (cameraManager == nullptr) {
+        LOG_ERROR(errorString, "ACameraManager_create returns nullptr");
+        goto cleanup;
+    }
+    ACameraManager_delete(cameraManager);
+    cameraManager = nullptr;
+
+    // Test get/close multiple instances
+    cameraManager = ACameraManager_create();
+    cameraManager2 = ACameraManager_create();
+    if (cameraManager2 == nullptr) {
+        LOG_ERROR(errorString, "ACameraManager_create 2 returns nullptr");
+        goto cleanup;
+    }
+    ACameraManager_delete(cameraManager);
+    cameraManager = nullptr;
+    cameraManager3 = ACameraManager_create();
+    if (cameraManager3 == nullptr) {
+        LOG_ERROR(errorString, "ACameraManager_create 3 returns nullptr");
+        goto cleanup;
+    }
+    cameraManager4 = ACameraManager_create();
+        if (cameraManager4 == nullptr) {
+        LOG_ERROR(errorString, "ACameraManager_create 4 returns nullptr");
+        goto cleanup;
+    }
+    ACameraManager_delete(cameraManager3);
+    ACameraManager_delete(cameraManager2);
+    ACameraManager_delete(cameraManager4);
+
+    pass = true;
+cleanup:
+    if (cameraManager) {
+        ACameraManager_delete(cameraManager);
+    }
+    ALOGI("%s %s", __FUNCTION__, pass ? "pass" : "fail");
+    if (!pass) {
+        throwAssertionError(env, errorString);
+    }
+    return pass;
+}
+
+extern "C" jboolean
+Java_android_hardware_camera2_cts_NativeCameraManagerTest_\
+testCameraManagerGetCameraIdsNative(
+        JNIEnv* env, jclass /*clazz*/) {
+    ALOGV("%s", __FUNCTION__);
+    bool pass = false;
+    ACameraManager* mgr = ACameraManager_create();
+    ACameraIdList *cameraIdList = nullptr;
+    camera_status_t ret = ACameraManager_getCameraIdList(mgr, &cameraIdList);
+    if (ret != ACAMERA_OK || cameraIdList == nullptr) {
+        LOG_ERROR(errorString, "Get camera id list failed: ret %d, cameraIdList %p",
+                ret, cameraIdList);
+        goto cleanup;
+    }
+    ALOGI("Number of cameras: %d", cameraIdList->numCameras);
+    for (int i = 0; i < cameraIdList->numCameras; i++) {
+        ALOGI("Camera ID: %s", cameraIdList->cameraIds[i]);
+    }
+    ACameraManager_deleteCameraIdList(cameraIdList);
+    cameraIdList = nullptr;
+
+    pass = true;
+cleanup:
+    if (mgr) {
+        ACameraManager_delete(mgr);
+    }
+    if (cameraIdList) {
+        ACameraManager_deleteCameraIdList(cameraIdList);
+    }
+    ALOGI("%s %s", __FUNCTION__, pass ? "pass" : "fail");
+    if (!pass) {
+        throwAssertionError(env, errorString);
+    }
+    return pass;
+}
+
+extern "C" jboolean
+Java_android_hardware_camera2_cts_NativeCameraManagerTest_\
+testCameraManagerAvailabilityCallbackNative(
+        JNIEnv* env, jclass /*clazz*/) {
+    ALOGV("%s", __FUNCTION__);
+    bool pass = false;
+    ACameraManager* mgr = ACameraManager_create();
+    ACameraIdList *cameraIdList = nullptr;
+    camera_status_t ret = ACameraManager_getCameraIdList(mgr, &cameraIdList);
+    int numCameras = cameraIdList->numCameras;
+    CameraServiceListener listener;
+    ACameraManager_AvailabilityCallbacks cbs {
+            &listener,
+            CameraServiceListener::onAvailable,
+            CameraServiceListener::onUnavailable};
+    ret = ACameraManager_registerAvailabilityCallback(mgr, &cbs);
+    if (ret != ACAMERA_OK) {
+        LOG_ERROR(errorString, "Register availability callback failed: ret %d", ret);
+        goto cleanup;
+    }
+    sleep(1); // sleep a second to give some time for callbacks to happen
+
+    // Should at least get onAvailable for each camera once
+    if (listener.getAvailableCount() < numCameras) {
+        LOG_ERROR(errorString, "Expect at least %d available callback but only got %d",
+                numCameras, listener.getAvailableCount());
+        goto cleanup;
+    }
+
+    ret = ACameraManager_unregisterAvailabilityCallback(mgr, &cbs);
+    if (ret != ACAMERA_OK) {
+        LOG_ERROR(errorString, "Unregister availability callback failed: ret %d", ret);
+        goto cleanup;
+    }
+    pass = true;
+cleanup:
+    if (cameraIdList) {
+        ACameraManager_deleteCameraIdList(cameraIdList);
+    }
+    if (mgr) {
+        ACameraManager_delete(mgr);
+    }
+    ALOGI("%s %s", __FUNCTION__, pass ? "pass" : "failed");
+    if (!pass) {
+        throwAssertionError(env, errorString);
+    }
+    return pass;
+}
+
+extern "C" jboolean
+Java_android_hardware_camera2_cts_NativeCameraManagerTest_\
+testCameraManagerCharacteristicsNative(
+        JNIEnv* env, jclass /*clazz*/) {
+    ALOGV("%s", __FUNCTION__);
+    bool pass = false;
+    ACameraManager* mgr = ACameraManager_create();
+    ACameraIdList *cameraIdList = nullptr;
+    ACameraMetadata* chars = nullptr;
+    int numCameras = 0;
+    camera_status_t ret = ACameraManager_getCameraIdList(mgr, &cameraIdList);
+    if (ret != ACAMERA_OK || cameraIdList == nullptr) {
+        LOG_ERROR(errorString, "Get camera id list failed: ret %d, cameraIdList %p",
+                ret, cameraIdList);
+        goto cleanup;
+    }
+    numCameras = cameraIdList->numCameras;
+
+    for (int i = 0; i < numCameras; i++) {
+        ret = ACameraManager_getCameraCharacteristics(
+                mgr, cameraIdList->cameraIds[i], &chars);
+        if (ret != ACAMERA_OK) {
+            LOG_ERROR(errorString, "Get camera characteristics failed: ret %d", ret);
+            goto cleanup;
+        }
+
+        ACameraMetadata_const_entry entry;
+        ret = ACameraMetadata_getConstEntry(chars, ACAMERA_REQUEST_AVAILABLE_CAPABILITIES, &entry);
+        if (ret != ACAMERA_OK) {
+            LOG_ERROR(errorString, "Get const available capabilities key failed. ret %d", ret);
+            goto cleanup;
+        }
+
+        // Check the entry is actually legit
+        if (entry.tag != ACAMERA_REQUEST_AVAILABLE_CAPABILITIES ||
+                entry.count == 0 || entry.type != ACAMERA_TYPE_BYTE || entry.data.i32 == nullptr) {
+            LOG_ERROR(errorString,
+                    "Bad available capabilities key: tag: %d (expected %d), count %u (expect > 0), "
+                    "type %d (expected %d), data %p (expected not null)",
+                    entry.tag, ACAMERA_REQUEST_AVAILABLE_CAPABILITIES, entry.count,
+                    entry.type, ACAMERA_TYPE_BYTE, entry.data.i32);
+            goto cleanup;
+        }
+        // All camera supports BC except depth only cameras
+        bool supportBC = false, supportDepth = false;
+        for (uint32_t i = 0; i < entry.count; i++) {
+            if (entry.data.u8[i] == ACAMERA_REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE) {
+                supportBC = true;
+            }
+            if (entry.data.u8[i] == ACAMERA_REQUEST_AVAILABLE_CAPABILITIES_DEPTH_OUTPUT) {
+                supportDepth = true;
+            }
+        }
+        if (!(supportBC || supportDepth)) {
+            LOG_ERROR(errorString, "Error: camera device %s does not support either BC or DEPTH",
+                    cameraIdList->cameraIds[i]);
+            goto cleanup;
+        }
+
+        // Check get unknown value fails
+        uint32_t badTag = (uint32_t) ACAMERA_VENDOR_START - 1;
+        ret = ACameraMetadata_getConstEntry(chars, ACAMERA_VENDOR_START, &entry);
+        if (ret == ACAMERA_OK) {
+            LOG_ERROR(errorString, "Error: get unknown tag should fail!");
+            goto cleanup;
+        }
+
+        ACameraMetadata_free(chars);
+        chars = nullptr;
+    }
+
+    pass = true;
+cleanup:
+    if (chars) {
+        ACameraMetadata_free(chars);
+    }
+    ACameraManager_deleteCameraIdList(cameraIdList);
+    ACameraManager_delete(mgr);
+    ALOGI("%s %s", __FUNCTION__, pass ? "pass" : "failed");
+    if (!pass) {
+        throwAssertionError(env, errorString);
+    }
+    return pass;
+}
+
+extern "C" jboolean
+Java_android_hardware_camera2_cts_NativeCameraDeviceTest_\
+testCameraDeviceOpenAndCloseNative(
+        JNIEnv* env, jclass /*clazz*/) {
+    ALOGV("%s", __FUNCTION__);
+    int numCameras = 0;
+    bool pass = false;
+    PreviewTestCase testCase;
+
+    camera_status_t ret = testCase.initWithErrorLog();
+    if (ret != ACAMERA_OK) {
+        // Don't log error here. testcase did it
+        goto cleanup;
+    }
+
+    numCameras = testCase.getNumCameras();
+    if (numCameras < 0) {
+        LOG_ERROR(errorString, "Testcase returned negavtive number of cameras: %d", numCameras);
+        goto cleanup;
+    }
+
+    for (int i = 0; i < numCameras; i++) {
+        const char* cameraId = testCase.getCameraId(i);
+        if (cameraId == nullptr) {
+            LOG_ERROR(errorString, "Testcase returned null camera id for camera %d", i);
+            goto cleanup;
+        }
+
+        ret = testCase.openCamera(cameraId);
+        if (ret != ACAMERA_OK) {
+            LOG_ERROR(errorString, "Open camera device %s failure. ret %d", cameraId, ret);
+            goto cleanup;
+        }
+
+        usleep(100000); // sleep to give some time for callbacks to happen
+
+        if (testCase.isCameraAvailable(cameraId)) {
+            LOG_ERROR(errorString, "Camera %s should be unavailable now", cameraId);
+            goto cleanup;
+        }
+
+        ret = testCase.closeCamera();
+        if (ret != ACAMERA_OK) {
+            LOG_ERROR(errorString, "Close camera device %s failure. ret %d", cameraId, ret);
+            goto cleanup;
+        }
+
+        usleep(100000); // sleep to give some time for callbacks to happen
+
+        if (!testCase.isCameraAvailable(cameraId)) {
+            LOG_ERROR(errorString, "Camera %s should be available now", cameraId);
+            goto cleanup;
+        }
+    }
+
+    ret = testCase.deInit();
+    if (ret != ACAMERA_OK) {
+        LOG_ERROR(errorString, "Testcase deInit failed: ret %d", ret);
+        goto cleanup;
+    }
+
+    pass = true;
+cleanup:
+    ALOGI("%s %s", __FUNCTION__, pass ? "pass" : "failed");
+    if (!pass) {
+        throwAssertionError(env, errorString);
+    }
+    return pass;
+}
+
+extern "C" jboolean
+Java_android_hardware_camera2_cts_NativeCameraDeviceTest_\
+testCameraDeviceCreateCaptureRequestNative(
+        JNIEnv* env, jclass /*clazz*/) {
+    ALOGV("%s", __FUNCTION__);
+    bool pass = false;
+    ACameraManager* mgr = ACameraManager_create();
+    ACameraIdList* cameraIdList = nullptr;
+    ACameraDevice* device = nullptr;
+    ACaptureRequest* request = nullptr;
+    ACameraMetadata* chars = nullptr;
+    camera_status_t ret = ACameraManager_getCameraIdList(mgr, &cameraIdList);
+
+    int numCameras = cameraIdList->numCameras;
+    for (int i = 0; i < numCameras; i++) {
+        CameraDeviceListener deviceListener;
+        const char* cameraId = cameraIdList->cameraIds[i];
+        ACameraDevice_StateCallbacks deviceCb {
+            &deviceListener,
+            CameraDeviceListener::onDisconnected,
+            CameraDeviceListener::onError
+        };
+        ret = ACameraManager_openCamera(mgr, cameraId, &deviceCb, &device);
+        if (ret != ACAMERA_OK) {
+            LOG_ERROR(errorString, "Open camera device %s failure. ret %d", cameraId, ret);
+            goto cleanup;
+        }
+
+        ret = ACameraManager_getCameraCharacteristics(mgr, cameraId, &chars);
+        if (ret != ACAMERA_OK || chars == nullptr) {
+            LOG_ERROR(errorString, "Get camera %s characteristics failure. ret %d, chars %p",
+                    cameraId, ret, chars);
+            goto cleanup;
+        }
+        StaticInfo staticInfo(chars);
+
+        for (int t = TEMPLATE_PREVIEW; t <= TEMPLATE_MANUAL; t++) {
+            ACameraDevice_request_template templateId =
+                    static_cast<ACameraDevice_request_template>(t);
+            ret = ACameraDevice_createCaptureRequest(device, templateId, &request);
+            if (ret == ACAMERA_ERROR_UNSUPPORTED) {
+                // template not supported. skip
+                continue;
+            }
+
+            if (ret != ACAMERA_OK) {
+                LOG_ERROR(errorString, "Create capture request failed!: ret %d", ret);
+                goto cleanup;
+            }
+
+            // try get/set capture request fields
+            ACameraMetadata_const_entry entry;
+            ret = ACaptureRequest_getConstEntry(request, ACAMERA_CONTROL_AE_MODE, &entry);
+            if (ret != ACAMERA_OK) {
+                LOG_ERROR(errorString, "Get AE mode key failed. ret %d", ret);
+                goto cleanup;
+            }
+
+            if (entry.tag != ACAMERA_CONTROL_AE_MODE || entry.type != ACAMERA_TYPE_BYTE ||\
+                    entry.count != 1) {
+                LOG_ERROR(errorString,
+                        "Bad AE mode key. tag 0x%x (expect 0x%x), type %d (expect %d), "
+                        "count %d (expect %d)",
+                        entry.tag, ACAMERA_CONTROL_AE_MODE, entry.type, ACAMERA_TYPE_BYTE,
+                        entry.count, 1);
+                goto cleanup;
+            }
+            if (t == TEMPLATE_MANUAL) {
+                if (entry.data.u8[0] != ACAMERA_CONTROL_AE_MODE_OFF) {
+                    LOG_ERROR(errorString, "Error: MANUAL template AE mode %d (expect %d)",
+                            entry.data.u8[0], ACAMERA_CONTROL_AE_MODE_OFF);
+                    goto cleanup;
+                }
+                // try set AE_MODE_ON
+                uint8_t aeMode = ACAMERA_CONTROL_AE_MODE_ON;
+                ret = ACaptureRequest_setEntry_u8(
+                        request, ACAMERA_CONTROL_AE_MODE, /*count*/ 1, &aeMode);
+                if (ret != ACAMERA_OK) {
+                    LOG_ERROR(errorString,
+                            "Error: Camera %s template %d: update AE mode key fail. ret %d",
+                            cameraId, t, ret);
+                    goto cleanup;
+                }
+                ret = ACaptureRequest_getConstEntry(
+                        request, ACAMERA_CONTROL_AE_MODE, &entry);
+                if (ret != ACAMERA_OK) {
+                    LOG_ERROR(errorString, "Get AE mode key failed. ret %d", ret);
+                    goto cleanup;
+                }
+                if (entry.data.u8[0] != aeMode) {
+                    LOG_ERROR(errorString,
+                            "Error: AE mode key is not updated. expect %d but get %d",
+                            aeMode, entry.data.u8[0]);
+                    goto cleanup;
+                }
+            } else {
+                if (staticInfo.isColorOutputSupported()) {
+                    if (entry.data.u8[0] != ACAMERA_CONTROL_AE_MODE_ON) {
+                        LOG_ERROR(errorString,
+                                "Error: Template %d has wrong AE mode %d (expect %d)",
+                                t, entry.data.u8[0], ACAMERA_CONTROL_AE_MODE_ON);
+                        goto cleanup;
+                    }
+                    // try set AE_MODE_OFF
+                    if (staticInfo.isCapabilitySupported(
+                            ACAMERA_REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR)) {
+                        uint8_t aeMode = ACAMERA_CONTROL_AE_MODE_OFF;
+                        ret = ACaptureRequest_setEntry_u8(
+                                request, ACAMERA_CONTROL_AE_MODE, /*count*/ 1, &aeMode);
+                        if (ret != ACAMERA_OK) {
+                            LOG_ERROR(errorString,
+                                    "Error: Camera %s template %d: update AE mode key fail. ret %d",
+                                    cameraId, t, ret);
+                            goto cleanup;
+                        }
+                        ret = ACaptureRequest_getConstEntry(
+                                request, ACAMERA_CONTROL_AE_MODE, &entry);
+                        if (ret != ACAMERA_OK) {
+                            LOG_ERROR(errorString, "Get AE mode key failed. ret %d", ret);
+                            goto cleanup;
+                        }
+                        if (entry.data.u8[0] != aeMode) {
+                            LOG_ERROR(errorString,
+                                    "Error: AE mode key is not updated. expect %d but get %d",
+                                    aeMode, entry.data.u8[0]);
+                            goto cleanup;
+                        }
+                    }
+                }
+            }
+            ACaptureRequest_free(request);
+            request = nullptr;
+        }
+
+        ACameraMetadata_free(chars);
+        chars = nullptr;
+        ACameraDevice_close(device);
+        device = nullptr;
+    }
+
+    pass = true;
+cleanup:
+    if (cameraIdList) {
+        ACameraManager_deleteCameraIdList(cameraIdList);
+    }
+    if (request) {
+        ACaptureRequest_free(request);
+    }
+    if (chars) {
+        ACameraMetadata_free(chars);
+    }
+    if (device) {
+        ACameraDevice_close(device);
+    }
+    if (mgr) {
+        ACameraManager_delete(mgr);
+    }
+    ALOGI("%s %s", __FUNCTION__, pass ? "pass" : "failed");
+    if (!pass) {
+        throwAssertionError(env, errorString);
+    }
+    return pass;
+}
+
+extern "C" jboolean
+Java_android_hardware_camera2_cts_NativeCameraDeviceTest_\
+testCameraDeviceSessionOpenAndCloseNative(
+        JNIEnv* env, jclass /*clazz*/, jobject jPreviewSurface) {
+    ALOGV("%s", __FUNCTION__);
+    int numCameras = 0;
+    bool pass = false;
+    PreviewTestCase testCase;
+
+    camera_status_t ret = testCase.initWithErrorLog();
+    if (ret != ACAMERA_OK) {
+        // Don't log error here. testcase did it
+        goto cleanup;
+    }
+
+    numCameras = testCase.getNumCameras();
+    if (numCameras < 0) {
+        LOG_ERROR(errorString, "Testcase returned negavtive number of cameras: %d", numCameras);
+        goto cleanup;
+    }
+
+    for (int i = 0; i < numCameras; i++) {
+        const char* cameraId = testCase.getCameraId(i);
+        if (cameraId == nullptr) {
+            LOG_ERROR(errorString, "Testcase returned null camera id for camera %d", i);
+            goto cleanup;
+        }
+
+        ret = testCase.openCamera(cameraId);
+        if (ret != ACAMERA_OK) {
+            LOG_ERROR(errorString, "Open camera device %s failure. ret %d", cameraId, ret);
+            goto cleanup;
+        }
+
+        usleep(100000); // sleep to give some time for callbacks to happen
+
+        if (testCase.isCameraAvailable(cameraId)) {
+            LOG_ERROR(errorString, "Camera %s should be unavailable now", cameraId);
+            goto cleanup;
+        }
+
+        ANativeWindow* previewAnw = testCase.initPreviewAnw(env, jPreviewSurface);
+        if (previewAnw == nullptr) {
+            LOG_ERROR(errorString, "Null ANW from preview surface!");
+            goto cleanup;
+        }
+
+        CaptureSessionListener* sessionListener = testCase.getSessionListener();
+        if (sessionListener == nullptr) {
+            LOG_ERROR(errorString, "Session listener camera %s is null", cameraId);
+            goto cleanup;
+        }
+
+        // Try open/close session multiple times
+        for (int j = 0; j < 5; j++) {
+            ret = testCase.createCaptureSessionWithLog();
+            if (ret != ACAMERA_OK) {
+                // Don't log error here. testcase did it
+                goto cleanup;
+            }
+
+            usleep(100000); // sleep to give some time for callbacks to happen
+
+            if (!sessionListener->isIdle()) {
+                LOG_ERROR(errorString, "Session for camera %s should be idle right after creation",
+                        cameraId);
+                goto cleanup;
+            }
+
+            testCase.closeSession();
+
+            usleep(100000); // sleep to give some time for callbacks to happen
+            if (!sessionListener->isClosed() || sessionListener->onClosedCount() != 1) {
+                LOG_ERROR(errorString,
+                        "Session for camera %s close error. isClosde %d close count %d",
+                        cameraId, sessionListener->isClosed(), sessionListener->onClosedCount());
+                goto cleanup;
+            }
+            sessionListener->reset();
+        }
+
+        // Try open/close really fast
+        ret = testCase.createCaptureSessionWithLog();
+        if (ret != ACAMERA_OK) {
+            LOG_ERROR(errorString, "Create session for camera %s failed. ret %d",
+                    cameraId, ret);
+            goto cleanup;
+        }
+        testCase.closeSession();
+        usleep(100000); // sleep to give some time for callbacks to happen
+        if (!sessionListener->isClosed() || sessionListener->onClosedCount() != 1) {
+            LOG_ERROR(errorString,
+                    "Session for camera %s close error. isClosde %d close count %d",
+                    cameraId, sessionListener->isClosed(), sessionListener->onClosedCount());
+            goto cleanup;
+        }
+
+        ret = testCase.resetWithErrorLog();
+        if (ret != ACAMERA_OK) {
+            // Don't log error here. testcase did it
+            goto cleanup;
+        }
+
+        usleep(100000); // sleep to give some time for callbacks to happen
+
+        if (!testCase.isCameraAvailable(cameraId)) {
+            LOG_ERROR(errorString, "Camera %s should be available now", cameraId);
+            goto cleanup;
+        }
+    }
+
+    ret = testCase.deInit();
+    if (ret != ACAMERA_OK) {
+        LOG_ERROR(errorString, "Testcase deInit failed: ret %d", ret);
+        goto cleanup;
+    }
+
+    pass = true;
+cleanup:
+    ALOGI("%s %s", __FUNCTION__, pass ? "pass" : "failed");
+    if (!pass) {
+        throwAssertionError(env, errorString);
+    }
+    return pass;
+}
+
+extern "C" jboolean
+Java_android_hardware_camera2_cts_NativeCameraDeviceTest_\
+testCameraDeviceSimplePreviewNative(
+        JNIEnv* env, jclass /*clazz*/, jobject jPreviewSurface) {
+    ALOGV("%s", __FUNCTION__);
+    int numCameras = 0;
+    bool pass = false;
+    PreviewTestCase testCase;
+
+    camera_status_t ret = testCase.initWithErrorLog();
+    if (ret != ACAMERA_OK) {
+        // Don't log error here. testcase did it
+        goto cleanup;
+    }
+
+    numCameras = testCase.getNumCameras();
+    if (numCameras < 0) {
+        LOG_ERROR(errorString, "Testcase returned negavtive number of cameras: %d", numCameras);
+        goto cleanup;
+    }
+
+    for (int i = 0; i < numCameras; i++) {
+        const char* cameraId = testCase.getCameraId(i);
+        if (cameraId == nullptr) {
+            LOG_ERROR(errorString, "Testcase returned null camera id for camera %d", i);
+            goto cleanup;
+        }
+
+        ret = testCase.openCamera(cameraId);
+        if (ret != ACAMERA_OK) {
+            LOG_ERROR(errorString, "Open camera device %s failure. ret %d", cameraId, ret);
+            goto cleanup;
+        }
+
+        usleep(100000); // sleep to give some time for callbacks to happen
+
+        if (testCase.isCameraAvailable(cameraId)) {
+            LOG_ERROR(errorString, "Camera %s should be unavailable now", cameraId);
+            goto cleanup;
+        }
+
+        ANativeWindow* previewAnw = testCase.initPreviewAnw(env, jPreviewSurface);
+        if (previewAnw == nullptr) {
+            LOG_ERROR(errorString, "Null ANW from preview surface!");
+            goto cleanup;
+        }
+
+        ret = testCase.createCaptureSessionWithLog();
+        if (ret != ACAMERA_OK) {
+            // Don't log error here. testcase did it
+            goto cleanup;
+        }
+
+        ret = testCase.createRequestsWithErrorLog();
+        if (ret != ACAMERA_OK) {
+            // Don't log error here. testcase did it
+            goto cleanup;
+        }
+
+        ret = testCase.startPreview();
+        if (ret != ACAMERA_OK) {
+            LOG_ERROR(errorString, "Start preview failed!");
+            goto cleanup;
+        }
+
+        sleep(3);
+
+        ret = testCase.resetWithErrorLog();
+        if (ret != ACAMERA_OK) {
+            // Don't log error here. testcase did it
+            goto cleanup;
+        }
+
+        usleep(100000); // sleep to give some time for callbacks to happen
+
+        if (!testCase.isCameraAvailable(cameraId)) {
+            LOG_ERROR(errorString, "Camera %s should be available now", cameraId);
+            goto cleanup;
+        }
+    }
+
+    ret = testCase.deInit();
+    if (ret != ACAMERA_OK) {
+        LOG_ERROR(errorString, "Testcase deInit failed: ret %d", ret);
+        goto cleanup;
+    }
+
+    pass = true;
+cleanup:
+    ALOGI("%s %s", __FUNCTION__, pass ? "pass" : "failed");
+    if (!pass) {
+        throwAssertionError(env, errorString);
+    }
+    return pass;
+}
+
+extern "C" jboolean
+Java_android_hardware_camera2_cts_NativeImageReaderTest_\
+testJpegNative(
+        JNIEnv* env, jclass /*clazz*/, jstring jOutPath) {
+    ALOGV("%s", __FUNCTION__);
+    const int NUM_TEST_IMAGES = 10;
+    const int TEST_WIDTH  = 640;
+    const int TEST_HEIGHT = 480;
+    media_status_t mediaRet = AMEDIA_ERROR_UNKNOWN;
+    int numCameras = 0;
+    bool pass = false;
+    PreviewTestCase testCase;
+
+    const char* outPath = env->GetStringUTFChars(jOutPath, nullptr);
+    testCase.setDumpFilePathBase(outPath);
+    ALOGI("%s: out path is %s", __FUNCTION__, outPath);
+
+    camera_status_t ret = testCase.initWithErrorLog();
+    if (ret != ACAMERA_OK) {
+        // Don't log error here. testcase did it
+        goto cleanup;
+    }
+
+    numCameras = testCase.getNumCameras();
+    if (numCameras < 0) {
+        LOG_ERROR(errorString, "Testcase returned negavtive number of cameras: %d", numCameras);
+        goto cleanup;
+    }
+
+    for (int i = 0; i < numCameras; i++) {
+        const char* cameraId = testCase.getCameraId(i);
+        if (cameraId == nullptr) {
+            LOG_ERROR(errorString, "Testcase returned null camera id for camera %d", i);
+            goto cleanup;
+        }
+
+        ret = testCase.openCamera(cameraId);
+        if (ret != ACAMERA_OK) {
+            LOG_ERROR(errorString, "Open camera device %s failure. ret %d", cameraId, ret);
+            goto cleanup;
+        }
+
+        usleep(100000); // sleep to give some time for callbacks to happen
+
+        if (testCase.isCameraAvailable(cameraId)) {
+            LOG_ERROR(errorString, "Camera %s should be unavailable now", cameraId);
+            goto cleanup;
+        }
+
+        mediaRet = testCase.initImageReaderWithErrorLog(
+                TEST_WIDTH, TEST_HEIGHT, AIMAGE_FORMAT_JPEG, NUM_TEST_IMAGES);
+        if (mediaRet != AMEDIA_OK) {
+            // Don't log error here. testcase did it
+            goto cleanup;
+        }
+
+        ret = testCase.createCaptureSessionWithLog();
+        if (ret != ACAMERA_OK) {
+            // Don't log error here. testcase did it
+            goto cleanup;
+        }
+
+        ret = testCase.createRequestsWithErrorLog();
+        if (ret != ACAMERA_OK) {
+            // Don't log error here. testcase did it
+            goto cleanup;
+        }
+
+        // Do some still capture
+        for (int capture = 0; capture < NUM_TEST_IMAGES; capture++) {
+            ret = testCase.takePicture();
+            if (ret != ACAMERA_OK) {
+                LOG_ERROR(errorString, "Camera %s capture(%d) failed. ret %d",
+                        cameraId, capture, ret);
+                goto cleanup;
+            }
+        }
+
+        // wait until all capture finished
+        for (int i = 0; i < 50; i++) {
+            usleep(100000); // sleep 100ms
+            if (testCase.getReaderImageCount() == NUM_TEST_IMAGES) {
+                ALOGI("Session take ~%d ms to capture %d images",
+                        i*100, NUM_TEST_IMAGES);
+                break;
+            }
+        }
+
+        if (testCase.getReaderImageCount() != NUM_TEST_IMAGES) {
+            LOG_ERROR(errorString, "Camera %s timeout capturing %d images. Got %d",
+                    cameraId, NUM_TEST_IMAGES, testCase.getReaderImageCount());
+            goto cleanup;
+        }
+
+        ret = testCase.resetWithErrorLog();
+        if (ret != ACAMERA_OK) {
+            // Don't log error here. testcase did it
+            goto cleanup;
+        }
+
+        usleep(100000); // sleep to give some time for callbacks to happen
+
+        if (!testCase.isCameraAvailable(cameraId)) {
+            LOG_ERROR(errorString, "Camera %s should be available now", cameraId);
+            goto cleanup;
+        }
+    }
+
+    ret = testCase.deInit();
+    if (ret != ACAMERA_OK) {
+        LOG_ERROR(errorString, "Testcase deInit failed: ret %d", ret);
+        goto cleanup;
+    }
+
+    pass = true;
+
+cleanup:
+    env->ReleaseStringUTFChars(jOutPath, outPath);
+    ALOGI("%s %s", __FUNCTION__, pass ? "pass" : "failed");
+    if (!pass) {
+        throwAssertionError(env, errorString);
+    }
+    return pass;
+}
+
+
+extern "C" jboolean
+Java_android_hardware_camera2_cts_NativeStillCaptureTest_\
+testStillCaptureNative(
+        JNIEnv* env, jclass /*clazz*/, jstring jOutPath, jobject jPreviewSurface) {
+    ALOGV("%s", __FUNCTION__);
+    const int NUM_TEST_IMAGES = 10;
+    const int TEST_WIDTH  = 640;
+    const int TEST_HEIGHT = 480;
+    media_status_t mediaRet = AMEDIA_ERROR_UNKNOWN;
+    int numCameras = 0;
+    bool pass = false;
+    PreviewTestCase testCase;
+
+    const char* outPath = env->GetStringUTFChars(jOutPath, nullptr);
+    testCase.setDumpFilePathBase(outPath);
+    ALOGI("%s: out path is %s", __FUNCTION__, outPath);
+
+    camera_status_t ret = testCase.initWithErrorLog();
+    if (ret != ACAMERA_OK) {
+        // Don't log error here. testcase did it
+        goto cleanup;
+    }
+
+    numCameras = testCase.getNumCameras();
+    if (numCameras < 0) {
+        LOG_ERROR(errorString, "Testcase returned negavtive number of cameras: %d", numCameras);
+        goto cleanup;
+    }
+
+    for (int i = 0; i < numCameras; i++) {
+        const char* cameraId = testCase.getCameraId(i);
+        if (cameraId == nullptr) {
+            LOG_ERROR(errorString, "Testcase returned null camera id for camera %d", i);
+            goto cleanup;
+        }
+
+        ret = testCase.openCamera(cameraId);
+        if (ret != ACAMERA_OK) {
+            LOG_ERROR(errorString, "Open camera device %s failure. ret %d", cameraId, ret);
+            goto cleanup;
+        }
+
+        usleep(100000); // sleep to give some time for callbacks to happen
+
+        if (testCase.isCameraAvailable(cameraId)) {
+            LOG_ERROR(errorString, "Camera %s should be unavailable now", cameraId);
+            goto cleanup;
+        }
+
+        mediaRet = testCase.initImageReaderWithErrorLog(
+                TEST_WIDTH, TEST_HEIGHT, AIMAGE_FORMAT_JPEG, NUM_TEST_IMAGES);
+        if (mediaRet != AMEDIA_OK) {
+            // Don't log error here. testcase did it
+            goto cleanup;
+        }
+
+        ANativeWindow* previewAnw = testCase.initPreviewAnw(env, jPreviewSurface);
+        if (previewAnw == nullptr) {
+            LOG_ERROR(errorString, "Null ANW from preview surface!");
+            goto cleanup;
+        }
+
+        ret = testCase.createCaptureSessionWithLog();
+        if (ret != ACAMERA_OK) {
+            // Don't log error here. testcase did it
+            goto cleanup;
+        }
+
+        ret = testCase.createRequestsWithErrorLog();
+        if (ret != ACAMERA_OK) {
+            // Don't log error here. testcase did it
+            goto cleanup;
+        }
+
+        ret = testCase.startPreview();
+        if (ret != ACAMERA_OK) {
+            LOG_ERROR(errorString, "Start preview failed!");
+            goto cleanup;
+        }
+
+        // Let preview run some time
+        sleep(3);
+
+        // Do some still capture
+        for (int capture = 0; capture < NUM_TEST_IMAGES; capture++) {
+            ret = testCase.takePicture();
+            if (ret != ACAMERA_OK) {
+                LOG_ERROR(errorString, "Camera %s capture(%d) failed. ret %d",
+                        cameraId, capture, ret);
+                goto cleanup;
+            }
+        }
+
+        // wait until all capture finished
+        for (int i = 0; i < 50; i++) {
+            usleep(100000); // sleep 100ms
+            if (testCase.getReaderImageCount() == NUM_TEST_IMAGES) {
+                ALOGI("Session take ~%d ms to capture %d images",
+                        i*100, NUM_TEST_IMAGES);
+                break;
+            }
+        }
+
+        if (testCase.getReaderImageCount() != NUM_TEST_IMAGES) {
+            LOG_ERROR(errorString, "Camera %s timeout capturing %d images. Got %d",
+                    cameraId, NUM_TEST_IMAGES, testCase.getReaderImageCount());
+            goto cleanup;
+        }
+
+        ret = testCase.resetWithErrorLog();
+        if (ret != ACAMERA_OK) {
+            // Don't log error here. testcase did it
+            goto cleanup;
+        }
+
+        usleep(100000); // sleep to give some time for callbacks to happen
+
+        if (!testCase.isCameraAvailable(cameraId)) {
+            LOG_ERROR(errorString, "Camera %s should be available now", cameraId);
+            goto cleanup;
+        }
+    }
+
+    ret = testCase.deInit();
+    if (ret != ACAMERA_OK) {
+        LOG_ERROR(errorString, "Testcase deInit failed: ret %d", ret);
+        goto cleanup;
+    }
+
+    pass = true;
+cleanup:
+    env->ReleaseStringUTFChars(jOutPath, outPath);
+    ALOGI("%s %s", __FUNCTION__, pass ? "pass" : "failed");
+    if (!pass) {
+        throwAssertionError(env, errorString);
+    }
+    return pass;
+}
+
diff --git a/tests/camera/src/android/hardware/camera2/cts/AllocationTest.java b/tests/camera/src/android/hardware/camera2/cts/AllocationTest.java
index 0476477..e6a13ff 100644
--- a/tests/camera/src/android/hardware/camera2/cts/AllocationTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/AllocationTest.java
@@ -717,7 +717,7 @@
                 CameraCharacteristics properties =
                         mCameraManager.getCameraCharacteristics(mCameraIds[i]);
                 StaticMetadata staticInfo = new StaticMetadata(properties);
-                if (fullHwLevel && !staticInfo.isHardwareLevelFull()) {
+                if (fullHwLevel && !staticInfo.isHardwareLevelAtLeastFull()) {
                     Log.i(TAG, String.format(
                             "Skipping this test for camera %s, needs FULL hw level",
                             mCameraIds[i]));
diff --git a/tests/camera/src/android/hardware/camera2/cts/BurstCaptureRawTest.java b/tests/camera/src/android/hardware/camera2/cts/BurstCaptureRawTest.java
index 41e20459..72a5f99 100644
--- a/tests/camera/src/android/hardware/camera2/cts/BurstCaptureRawTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/BurstCaptureRawTest.java
@@ -437,8 +437,7 @@
      */
     private boolean checkCapability(ArrayList<Integer> supportedRawList, int[] testedFormats) {
         // make sure the sensor has manual support
-        if (!mStaticInfo.isCapabilitySupported(
-                CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL)) {
+        if (!mStaticInfo.isHardwareLevelAtLeastFull()) {
             Log.w(TAG, "Full hardware level is not supported");
             return false;
         }
diff --git a/tests/camera/src/android/hardware/camera2/cts/CameraDeviceTest.java b/tests/camera/src/android/hardware/camera2/cts/CameraDeviceTest.java
index 3778b95..cf36b05 100644
--- a/tests/camera/src/android/hardware/camera2/cts/CameraDeviceTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/CameraDeviceTest.java
@@ -758,7 +758,7 @@
         try {
             mSession.prepare(output3Surface);
             // Legacy camera prepare always succeed
-            if (mStaticInfo.isHardwareLevelLimitedOrBetter()) {
+            if (mStaticInfo.isHardwareLevelAtLeastLimited()) {
                 fail("Preparing surface not part of session must throw IllegalArgumentException");
             }
         } catch (IllegalArgumentException e) {
@@ -782,7 +782,7 @@
         try {
             mSession.prepare(output1Surface);
             // Legacy camera prepare always succeed
-            if (mStaticInfo.isHardwareLevelLimitedOrBetter()) {
+            if (mStaticInfo.isHardwareLevelAtLeastLimited()) {
                 fail("Preparing already-used surface must throw IllegalArgumentException");
             }
         } catch (IllegalArgumentException e) {
@@ -803,7 +803,7 @@
         try {
             mSession.prepare(output1Surface);
             // Legacy camera prepare always succeed
-            if (mStaticInfo.isHardwareLevelLimitedOrBetter()) {
+            if (mStaticInfo.isHardwareLevelAtLeastLimited()) {
                 fail("Preparing surface used in previous session must throw " +
                         "IllegalArgumentException");
             }
@@ -824,7 +824,7 @@
         try {
             mSession.prepare(output3Surface);
             // Legacy camera prepare always succeed
-            if (mStaticInfo.isHardwareLevelLimitedOrBetter()) {
+            if (mStaticInfo.isHardwareLevelAtLeastLimited()) {
                 fail("Preparing already-used surface must throw IllegalArgumentException");
             }
         } catch (IllegalArgumentException e) {
@@ -849,7 +849,7 @@
         try {
             mSession.prepare(output1Surface);
             // Legacy camera prepare always succeed
-            if (mStaticInfo.isHardwareLevelLimitedOrBetter()) {
+            if (mStaticInfo.isHardwareLevelAtLeastLimited()) {
                 fail("Preparing surface used in previous session must throw " +
                         "IllegalArgumentException");
             }
@@ -1260,7 +1260,7 @@
             }
 
             // Relax framerate constraints on legacy mode
-            if (mStaticInfo.isHardwareLevelLimitedOrBetter()) {
+            if (mStaticInfo.isHardwareLevelAtLeastLimited()) {
                 // Need give fixed frame rate for video recording template.
                 if (template == CameraDevice.TEMPLATE_RECORD) {
                     if (maxFps != minFps) {
@@ -1597,14 +1597,16 @@
                     CaptureRequest.NOISE_REDUCTION_MODE_ZERO_SHUTTER_LAG);
         } else if (template == CameraDevice.TEMPLATE_PREVIEW ||
                 template == CameraDevice.TEMPLATE_RECORD){
-            List<Integer> availableEdgeModes =
-                    Arrays.asList(toObject(mStaticInfo.getAvailableEdgeModesChecked()));
-            if (availableEdgeModes.contains(CaptureRequest.EDGE_MODE_FAST)) {
-                mCollector.expectKeyValueEquals(request, EDGE_MODE,
-                        CaptureRequest.EDGE_MODE_FAST);
-            } else {
-                mCollector.expectKeyValueEquals(request, EDGE_MODE,
-                        CaptureRequest.EDGE_MODE_OFF);
+            if (mStaticInfo.areKeysAvailable(EDGE_MODE)) {
+                List<Integer> availableEdgeModes =
+                        Arrays.asList(toObject(mStaticInfo.getAvailableEdgeModesChecked()));
+                if (availableEdgeModes.contains(CaptureRequest.EDGE_MODE_FAST)) {
+                    mCollector.expectKeyValueEquals(request, EDGE_MODE,
+                            CaptureRequest.EDGE_MODE_FAST);
+                } else {
+                    mCollector.expectKeyValueEquals(request, EDGE_MODE,
+                            CaptureRequest.EDGE_MODE_OFF);
+                }
             }
 
             if (mStaticInfo.areKeysAvailable(NOISE_REDUCTION_MODE)) {
diff --git a/tests/camera/src/android/hardware/camera2/cts/CameraTestUtils.java b/tests/camera/src/android/hardware/camera2/cts/CameraTestUtils.java
index 85eeb0b..0da92a6 100644
--- a/tests/camera/src/android/hardware/camera2/cts/CameraTestUtils.java
+++ b/tests/camera/src/android/hardware/camera2/cts/CameraTestUtils.java
@@ -36,6 +36,7 @@
 import android.hardware.camera2.TotalCaptureResult;
 import android.hardware.cts.helpers.CameraUtils;
 import android.hardware.camera2.params.MeteringRectangle;
+import android.hardware.camera2.params.OutputConfiguration;
 import android.hardware.camera2.params.StreamConfigurationMap;
 import android.location.Location;
 import android.location.LocationManager;
@@ -721,6 +722,26 @@
     }
 
     /**
+     * Configure a new camera session with output configurations.
+     *
+     * @param camera The CameraDevice to be configured.
+     * @param outputs The OutputConfiguration list that is used for camera output.
+     * @param listener The callback CameraDevice will notify when capture results are available.
+     */
+    public static CameraCaptureSession configureCameraSessionWithConfig(CameraDevice camera,
+            List<OutputConfiguration> outputs,
+            CameraCaptureSession.StateCallback listener, Handler handler)
+            throws CameraAccessException {
+        BlockingSessionCallback sessionListener = new BlockingSessionCallback(listener);
+        camera.createCaptureSessionByOutputConfiguration(outputs, sessionListener, handler);
+        CameraCaptureSession session =
+                sessionListener.waitAndGetSession(SESSION_CONFIGURE_TIMEOUT_MS);
+        assertFalse("Camera session should not be a reprocessable session",
+                session.isReprocessable());
+        return session;
+    }
+
+    /**
      * Configure a new camera session with output surfaces.
      *
      * @param camera The CameraDevice to be configured.
@@ -759,6 +780,40 @@
         return session;
     }
 
+    /**
+     * Create a reprocessable camera session with input and output configurations.
+     *
+     * @param camera The CameraDevice to be configured.
+     * @param inputConfiguration The input configuration used to create this session.
+     * @param outputs The output configurations used to create this session.
+     * @param listener The callback CameraDevice will notify when capture results are available.
+     * @param handler The handler used to notify callbacks.
+     * @return The session ready to use.
+     * @throws CameraAccessException
+     */
+    public static CameraCaptureSession configureReprocCameraSessionWithConfig(CameraDevice camera,
+            InputConfiguration inputConfiguration, List<OutputConfiguration> outputs,
+            CameraCaptureSession.StateCallback listener, Handler handler)
+            throws CameraAccessException {
+        BlockingSessionCallback sessionListener = new BlockingSessionCallback(listener);
+        camera.createReprocessableCaptureSessionWithConfigurations(inputConfiguration, outputs,
+                sessionListener, handler);
+
+        Integer[] sessionStates = {BlockingSessionCallback.SESSION_READY,
+                                   BlockingSessionCallback.SESSION_CONFIGURE_FAILED};
+        int state = sessionListener.getStateWaiter().waitForAnyOfStates(
+                Arrays.asList(sessionStates), SESSION_CONFIGURE_TIMEOUT_MS);
+
+        assertTrue("Creating a reprocessable session failed.",
+                state == BlockingSessionCallback.SESSION_READY);
+
+        CameraCaptureSession session =
+                sessionListener.waitAndGetSession(SESSION_CONFIGURE_TIMEOUT_MS);
+        assertTrue("Camera session should be a reprocessable session", session.isReprocessable());
+
+        return session;
+    }
+
     public static <T> void assertArrayNotEmpty(T arr, String message) {
         assertTrue(message, arr != null && Array.getLength(arr) > 0);
     }
@@ -1797,7 +1852,7 @@
         int orientationTested = expectedExifData.jpegOrientation;
         // Legacy shim always doesn't rotate thumbnail size
         if ((orientationTested == 90 || orientationTested == 270) &&
-                staticInfo.isHardwareLevelLimitedOrBetter()) {
+                staticInfo.isHardwareLevelAtLeastLimited()) {
             int exifOrientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION,
                     /*defaultValue*/-1);
             if (exifOrientation == ExifInterface.ORIENTATION_UNDEFINED) {
diff --git a/tests/camera/src/android/hardware/camera2/cts/CaptureRequestTest.java b/tests/camera/src/android/hardware/camera2/cts/CaptureRequestTest.java
index a4e1917..81af410 100644
--- a/tests/camera/src/android/hardware/camera2/cts/CaptureRequestTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/CaptureRequestTest.java
@@ -1136,7 +1136,7 @@
         }
 
         // Test flash SINGLE mode control. Wait for flash state to be READY first.
-        if (mStaticInfo.isHardwareLevelLimitedOrBetter()) {
+        if (mStaticInfo.isHardwareLevelAtLeastLimited()) {
             waitForResultValue(listener, CaptureResult.FLASH_STATE, CaptureResult.FLASH_STATE_READY,
                     NUM_RESULTS_WAIT_TIMEOUT);
         } // else the settings were already waited on earlier
@@ -1886,7 +1886,7 @@
             // In LEGACY mode, a transition to one of the continuous AF modes does not necessarily
             // result in a passive AF call if the camera has already been focused, and the scene has
             // not changed enough to trigger an AF pass.  Skip this constraint for LEGACY.
-            if (mStaticInfo.isHardwareLevelLimitedOrBetter() &&
+            if (mStaticInfo.isHardwareLevelAtLeastLimited() &&
                     (mode == CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE ||
                     mode == CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_VIDEO)) {
                 List<Integer> afStateList = new ArrayList<Integer>();
@@ -2054,7 +2054,7 @@
                                  previousCrop.height() > currentCrop.height()));
                 }
 
-                if (mStaticInfo.isHardwareLevelLimitedOrBetter()) {
+                if (mStaticInfo.isHardwareLevelAtLeastLimited()) {
                     mCollector.expectRectsAreSimilar(
                             "Request and result crop region should be similar",
                             cropRegions[i], cropRegion, CROP_REGION_ERROR_PERCENT_DELTA);
diff --git a/tests/camera/src/android/hardware/camera2/cts/CaptureResultTest.java b/tests/camera/src/android/hardware/camera2/cts/CaptureResultTest.java
index 8510016..89e3891 100644
--- a/tests/camera/src/android/hardware/camera2/cts/CaptureResultTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/CaptureResultTest.java
@@ -518,7 +518,7 @@
             waiverKeys.add(CaptureResult.SENSOR_DYNAMIC_WHITE_LEVEL);
         }
 
-        if (mStaticInfo.isHardwareLevelFull()) {
+        if (mStaticInfo.isHardwareLevelAtLeastFull()) {
             return waiverKeys;
         }
 
diff --git a/tests/camera/src/android/hardware/camera2/cts/DngCreatorTest.java b/tests/camera/src/android/hardware/camera2/cts/DngCreatorTest.java
index bddbd52..4259882 100644
--- a/tests/camera/src/android/hardware/camera2/cts/DngCreatorTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/DngCreatorTest.java
@@ -46,13 +46,16 @@
 import android.view.Surface;
 
 import java.io.ByteArrayOutputStream;
+import java.io.File;
 import java.io.FileOutputStream;
 import java.nio.ByteBuffer;
 import java.nio.channels.FileChannel;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Calendar;
 import java.util.Collections;
 import java.util.List;
+import java.util.TimeZone;
 
 import static android.hardware.camera2.cts.helpers.AssertHelpers.*;
 
@@ -68,6 +71,19 @@
     private static final int DEFAULT_PATCH_DIMEN = 512;
     private static final int AE_TIMEOUT_MS = 2000;
 
+    // Constants used for GPS testing.
+    private static final double GPS_DIFFERENCE_TOLERANCE = 0.0001;
+    private static final double GPS_LATITUDE = 37.420016;
+    private static final double GPS_LONGITUDE = -122.081987;
+    private static final String GPS_DATESTAMP = "2015:01:27";
+    private static final String GPS_TIMESTAMP = "02:12:01";
+    private static final Calendar GPS_CALENDAR =
+            Calendar.getInstance(TimeZone.getTimeZone("GMT+0"));
+
+    static {
+        GPS_CALENDAR.set(2015, 0, 27, 2, 12, 01);
+    }
+
     @Override
     protected void setUp() throws Exception {
         super.setUp();
@@ -165,8 +181,8 @@
      *
      * <p>
      * For each camera, capture a single RAW16 image at the first capture size reported for
-     * the raw format on that device, and save that image as a DNG file.  No further validation
-     * is done.
+     * the raw format on that device, and save that image as a DNG file. GPS information validation
+     * is done via ExifInterface.
      * </p>
      *
      * <p>
@@ -222,9 +238,9 @@
                 DngCreator dngCreator = new DngCreator(characteristics, resultPair.second);
                 Location l = new Location("test");
                 l.reset();
-                l.setLatitude(37.420016);
-                l.setLongitude(-122.081987);
-                l.setTime(System.currentTimeMillis());
+                l.setLatitude(GPS_LATITUDE);
+                l.setLongitude(GPS_LONGITUDE);
+                l.setTime(GPS_CALENDAR.getTimeInMillis());
                 dngCreator.setLocation(l);
 
                 dngCreator.setDescription("helloworld");
@@ -233,16 +249,38 @@
                 outputStream = new ByteArrayOutputStream();
                 dngCreator.writeImage(outputStream, resultPair.first.get(0));
 
+                String filePath = DEBUG_FILE_NAME_BASE + "/camera_thumb_" + deviceId + "_" +
+                        DEBUG_DNG_FILE;
+                // Write out captured DNG file for the first camera device if setprop is enabled
+                fileStream = new FileOutputStream(filePath);
+                fileStream.write(outputStream.toByteArray());
+                fileStream.flush();
+                fileStream.close();
                 if (VERBOSE) {
-                    String filePath = DEBUG_FILE_NAME_BASE + "/camera_thumb_" + deviceId + "_" +
-                            DEBUG_DNG_FILE;
-                    // Write out captured DNG file for the first camera device if setprop is enabled
-                    fileStream = new FileOutputStream(filePath);
-                    fileStream.write(outputStream.toByteArray());
-                    fileStream.flush();
-                    fileStream.close();
                     Log.v(TAG, "Test DNG file for camera " + deviceId + " saved to " + filePath);
                 }
+
+                ExifInterface exifInterface = new ExifInterface(filePath);
+                // Verify GPS data.
+                float[] latLong = new float[2];
+                assertTrue(exifInterface.getLatLong(latLong));
+                assertEquals(GPS_LATITUDE, latLong[0], GPS_DIFFERENCE_TOLERANCE);
+                assertEquals(GPS_LONGITUDE, latLong[1], GPS_DIFFERENCE_TOLERANCE);
+                assertEquals(GPS_DATESTAMP,
+                        exifInterface.getAttribute(ExifInterface.TAG_GPS_DATESTAMP));
+                assertEquals(GPS_TIMESTAMP,
+                        exifInterface.getAttribute(ExifInterface.TAG_GPS_TIMESTAMP));
+
+                // Verify the orientation.
+                assertEquals(ExifInterface.ORIENTATION_FLIP_VERTICAL,
+                        exifInterface.getAttributeInt(ExifInterface.TAG_ORIENTATION,
+                                ExifInterface.ORIENTATION_UNDEFINED));
+
+                if (!VERBOSE) {
+                    // Delete the captured DNG file.
+                    File dngFile = new File(filePath);
+                    assertTrue(dngFile.delete());
+                }
             } finally {
                 closeDevice(deviceId);
                 for (ImageReader r : captureReaders) {
diff --git a/tests/camera/src/android/hardware/camera2/cts/ExtendedCameraCharacteristicsTest.java b/tests/camera/src/android/hardware/camera2/cts/ExtendedCameraCharacteristicsTest.java
index 5b21e4d..1c56e98 100644
--- a/tests/camera/src/android/hardware/camera2/cts/ExtendedCameraCharacteristicsTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/ExtendedCameraCharacteristicsTest.java
@@ -77,6 +77,7 @@
     private static final int LEGACY = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY;
     private static final int LIMITED = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED;
     private static final int FULL = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL;
+    private static final int LEVEL_3 = CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_3;
     private static final int OPT = Integer.MAX_VALUE;  // For keys that are optional on all hardware levels.
 
     /*
@@ -210,13 +211,15 @@
             ArrayList<Size> yuvSizesList = new ArrayList<>(Arrays.asList(yuvSizes));
             ArrayList<Size> privateSizesList = new ArrayList<>(Arrays.asList(privateSizes));
 
-            CamcorderProfile maxVideoProfile = CamcorderProfile.get(CamcorderProfile.QUALITY_HIGH);
+            int cameraId = Integer.valueOf(mIds[counter]);
+            CamcorderProfile maxVideoProfile = CamcorderProfile.get(
+                    cameraId, CamcorderProfile.QUALITY_HIGH);
             Size maxVideoSize = new Size(
                     maxVideoProfile.videoFrameWidth, maxVideoProfile.videoFrameHeight);
 
             // Handle FullHD special case first
             if (jpegSizesList.contains(FULLHD)) {
-                if (hwLevel == FULL || (hwLevel == LIMITED &&
+                if (hwLevel >= LEVEL_3 || hwLevel == FULL || (hwLevel == LIMITED &&
                         maxVideoSize.getWidth() >= FULLHD.getWidth() &&
                         maxVideoSize.getHeight() >= FULLHD.getHeight())) {
                     boolean yuvSupportFullHD = yuvSizesList.contains(FULLHD) ||
@@ -244,7 +247,7 @@
                 jpegSizesList.removeAll(toBeRemoved);
             }
 
-            if (hwLevel == FULL || hwLevel == LIMITED) {
+            if (hwLevel >= LEVEL_3 || hwLevel == FULL || hwLevel == LIMITED) {
                 if (!yuvSizesList.containsAll(jpegSizesList)) {
                     for (Size s : jpegSizesList) {
                         if (!yuvSizesList.contains(s)) {
@@ -1419,7 +1422,7 @@
         return remapHardwareLevel(left) - remapHardwareLevel(right);
     }
 
-    /** Remap HW levels worst<->best, 0 = worst, 2 = best */
+    /** Remap HW levels worst<->best, 0 = LEGACY, 1 = LIMITED, 2 = FULL, ..., N = LEVEL_N */
     private static int remapHardwareLevel(int level) {
         switch (level) {
             case OPT:
@@ -1429,7 +1432,11 @@
             case LIMITED:
                 return 1; // second lowest
             case FULL:
-                return 2; // best
+                return 2; // good
+            default:
+                if (level >= LEVEL_3) {
+                    return level; // higher levels map directly
+                }
         }
 
         fail("Unknown HW level: " + level);
@@ -1444,6 +1451,10 @@
                 return "LIMITED";
             case FULL:
                 return "FULL";
+            default:
+                if (level >= LEVEL_3) {
+                    return String.format("LEVEL_%d", level);
+                }
         }
 
         // unknown
diff --git a/tests/camera/src/android/hardware/camera2/cts/NativeCameraDeviceTest.java b/tests/camera/src/android/hardware/camera2/cts/NativeCameraDeviceTest.java
new file mode 100644
index 0000000..ad460c8
--- /dev/null
+++ b/tests/camera/src/android/hardware/camera2/cts/NativeCameraDeviceTest.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.camera2.cts;
+
+import android.hardware.camera2.cts.testcases.Camera2SurfaceViewTestCase;
+import android.util.Log;
+import android.util.Size;
+import android.view.Surface;
+
+/**
+ * <p>Basic test for CameraManager class.</p>
+ */
+public class NativeCameraDeviceTest extends Camera2SurfaceViewTestCase {
+    private static final String TAG = "NativeCameraDeviceTest";
+    private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
+
+    /** Load jni on initialization */
+    static {
+        Log.i("NativeCameraDeviceTest", "before loadlibrary");
+        System.loadLibrary("ctscamera2ndk_jni");
+        Log.i("NativeCameraDeviceTest", "after loadlibrary");
+    }
+
+    public void testCameraDeviceOpenAndClose() {
+        assertTrue("testCameraDeviceOpenAndClose fail, see log for details",
+                testCameraDeviceOpenAndCloseNative());
+    }
+
+    public void testCameraDeviceCreateCaptureRequest() {
+        assertTrue("testCameraDeviceCreateCaptureRequest fail, see log for details",
+                testCameraDeviceCreateCaptureRequestNative());
+    }
+
+    public void testCameraDeviceSessionOpenAndClose() {
+        // Init preview surface to a guaranteed working size
+        updatePreviewSurface(new Size(640, 480));
+        assertTrue("testCameraDeviceSessionOpenAndClose fail, see log for details",
+                testCameraDeviceSessionOpenAndCloseNative(mPreviewSurface));
+    }
+
+    public void testCameraDeviceSimplePreview() {
+        // Init preview surface to a guaranteed working size
+        updatePreviewSurface(new Size(640, 480));
+        assertTrue("testCameraDeviceSimplePreview fail, see log for details",
+                testCameraDeviceSimplePreviewNative(mPreviewSurface));
+    }
+
+    private static native boolean testCameraDeviceOpenAndCloseNative();
+    private static native boolean testCameraDeviceCreateCaptureRequestNative();
+    private static native boolean testCameraDeviceSessionOpenAndCloseNative(Surface preview);
+    private static native boolean testCameraDeviceSimplePreviewNative(Surface preview);
+}
diff --git a/tests/camera/src/android/hardware/camera2/cts/NativeCameraManagerTest.java b/tests/camera/src/android/hardware/camera2/cts/NativeCameraManagerTest.java
new file mode 100644
index 0000000..6d0d73c
--- /dev/null
+++ b/tests/camera/src/android/hardware/camera2/cts/NativeCameraManagerTest.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright 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.hardware.camera2.cts;
+
+import android.test.AndroidTestCase;
+import android.util.Log;
+
+/**
+ * <p>Basic test for CameraManager class.</p>
+ */
+public class NativeCameraManagerTest extends AndroidTestCase {
+    private static final String TAG = "NativeCameraManagerTest";
+    private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
+
+    /** Load jni on initialization */
+    static {
+        Log.i("NativeCameraManagerTest", "before loadlibrary");
+        System.loadLibrary("ctscamera2ndk_jni");
+        Log.i("NativeCameraManagerTest", "after loadlibrary");
+    }
+
+    public void testCameraManagerGetAndClose() {
+        assertTrue("testCameraManagerGetAndClose fail, see log for details",
+                testCameraManagerGetAndCloseNative());
+    }
+
+    public void testCameraManagerGetCameraIds() {
+        assertTrue("testCameraManagerGetCameraIds fail, see log for details",
+                testCameraManagerGetCameraIdsNative());
+    }
+
+    public void testCameraManagerAvailabilityCallback() {
+        assertTrue("testCameraManagerAvailabilityCallback fail, see log for details",
+                testCameraManagerAvailabilityCallbackNative());
+    }
+
+    public void testCameraManagerCameraCharacteristics() {
+        assertTrue("testCameraManagerCameraCharacteristics fail, see log for details",
+                testCameraManagerCharacteristicsNative());
+    }
+
+    private static native boolean testCameraManagerGetAndCloseNative();
+    private static native boolean testCameraManagerGetCameraIdsNative();
+    private static native boolean testCameraManagerAvailabilityCallbackNative();
+    private static native boolean testCameraManagerCharacteristicsNative();
+}
diff --git a/tests/camera/src/android/hardware/camera2/cts/NativeImageReaderTest.java b/tests/camera/src/android/hardware/camera2/cts/NativeImageReaderTest.java
new file mode 100644
index 0000000..24fc04f
--- /dev/null
+++ b/tests/camera/src/android/hardware/camera2/cts/NativeImageReaderTest.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.camera2.cts;
+
+import android.hardware.camera2.cts.testcases.Camera2AndroidTestCase;
+import android.util.Log;
+
+/**
+ * <p>Basic test for CameraManager class.</p>
+ */
+public class NativeImageReaderTest extends Camera2AndroidTestCase {
+    private static final String TAG = "NativeImageReaderTest";
+    private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
+
+    /** Load jni on initialization */
+    static {
+        Log.i("NativeImageReaderTest", "before loadlibrary");
+        System.loadLibrary("ctscamera2ndk_jni");
+        Log.i("NativeImageReaderTest", "after loadlibrary");
+    }
+
+    public void testJpeg() {
+        assertTrue("testJpeg fail, see log for details",
+                testJpegNative(DEBUG_FILE_NAME_BASE));
+    }
+
+    private static native boolean testJpegNative(String filePath);
+}
diff --git a/tests/camera/src/android/hardware/camera2/cts/NativeStillCaptureTest.java b/tests/camera/src/android/hardware/camera2/cts/NativeStillCaptureTest.java
new file mode 100644
index 0000000..50fa715
--- /dev/null
+++ b/tests/camera/src/android/hardware/camera2/cts/NativeStillCaptureTest.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.camera2.cts;
+
+import android.hardware.camera2.cts.testcases.Camera2SurfaceViewTestCase;
+import android.util.Log;
+import android.util.Size;
+import android.view.Surface;
+
+/**
+ * <p>Basic test for CameraManager class.</p>
+ */
+public class NativeStillCaptureTest extends Camera2SurfaceViewTestCase {
+    private static final String TAG = "NativeStillCaptureTest";
+    private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
+
+    /** Load jni on initialization */
+    static {
+        Log.i("NativeStillCaptureTest", "before loadlibrary");
+        System.loadLibrary("ctscamera2ndk_jni");
+        Log.i("NativeStillCaptureTest", "after loadlibrary");
+    }
+
+    public void testStillCapture() {
+        // Init preview surface to a guaranteed working size
+        updatePreviewSurface(new Size(640, 480));
+        assertTrue("testStillCapture fail, see log for details",
+                testStillCaptureNative(DEBUG_FILE_NAME_BASE, mPreviewSurface));
+    }
+
+    private static native boolean testStillCaptureNative(
+            String filePath, Surface previewSurface);
+}
diff --git a/tests/camera/src/android/hardware/camera2/cts/PerformanceTest.java b/tests/camera/src/android/hardware/camera2/cts/PerformanceTest.java
index 666292c..f0f8f66 100644
--- a/tests/camera/src/android/hardware/camera2/cts/PerformanceTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/PerformanceTest.java
@@ -652,7 +652,7 @@
         // 2. Create camera output ImageReaders.
         // YUV/Opaque output, camera should support output with input size/format
         mCameraZslImageListener = new SimpleImageReaderListener(
-                /*asyncMode*/true, MAX_ZSL_IMAGES / 2);
+                /*asyncMode*/true, MAX_ZSL_IMAGES - MAX_REPROCESS_IMAGES);
         mCameraZslReader = CameraTestUtils.makeImageReader(
                 maxInputSize, inputFormat, MAX_ZSL_IMAGES, mCameraZslImageListener, mHandler);
         // Jpeg reprocess output
diff --git a/tests/camera/src/android/hardware/camera2/cts/RecordingTest.java b/tests/camera/src/android/hardware/camera2/cts/RecordingTest.java
index 5e32509..33b0c50 100644
--- a/tests/camera/src/android/hardware/camera2/cts/RecordingTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/RecordingTest.java
@@ -887,7 +887,7 @@
              *    When full res jpeg stream cannot keep up to video stream speed, search
              *    the largest jpeg size that can susptain video speed instead.
              */
-            if (mStaticInfo.isHardwareLevelFull() &&
+            if (mStaticInfo.isHardwareLevelAtLeastFull() &&
                     videoSz.getWidth() <= maxPreviewSize.getWidth() &&
                     videoSz.getHeight() <= maxPreviewSize.getHeight()) {
                 for (Size jpegSize : mOrderedStillSizes) {
diff --git a/tests/camera/src/android/hardware/camera2/cts/RobustnessTest.java b/tests/camera/src/android/hardware/camera2/cts/RobustnessTest.java
index ddae8eb..bd5c404 100644
--- a/tests/camera/src/android/hardware/camera2/cts/RobustnessTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/RobustnessTest.java
@@ -168,50 +168,86 @@
 
         // Enum values are defined in MaxStreamSizes
         final int[][] LEGACY_COMBINATIONS = {
-            {PRIV, MAXIMUM}, // Simple preview, GPU video processing, or no-preview video recording
-            {JPEG, MAXIMUM}, // No-viewfinder still image capture
-            {YUV,  MAXIMUM}, // In-application video/image processing
-            {PRIV, PREVIEW,  JPEG, MAXIMUM}, // Standard still imaging.
-            {YUV,  PREVIEW,  JPEG, MAXIMUM}, // In-app processing plus still capture.
-            {PRIV, PREVIEW,  PRIV, PREVIEW}, // Standard recording.
-            {PRIV, PREVIEW,  YUV,  PREVIEW}, // Preview plus in-app processing.
-            {PRIV, PREVIEW,  YUV,  PREVIEW,  JPEG, MAXIMUM} // Still capture plus in-app processing.
+            // Simple preview, GPU video processing, or no-preview video recording
+            {PRIV, MAXIMUM},
+            // No-viewfinder still image capture
+            {JPEG, MAXIMUM},
+            // In-application video/image processing
+            {YUV,  MAXIMUM},
+            // Standard still imaging.
+            {PRIV, PREVIEW,  JPEG, MAXIMUM},
+            // In-app processing plus still capture.
+            {YUV,  PREVIEW,  JPEG, MAXIMUM},
+            // Standard recording.
+            {PRIV, PREVIEW,  PRIV, PREVIEW},
+            // Preview plus in-app processing.
+            {PRIV, PREVIEW,  YUV,  PREVIEW},
+            // Still capture plus in-app processing.
+            {PRIV, PREVIEW,  YUV,  PREVIEW,  JPEG, MAXIMUM}
         };
 
         final int[][] LIMITED_COMBINATIONS = {
-            {PRIV, PREVIEW,  PRIV, RECORD }, // High-resolution video recording with preview.
-            {PRIV, PREVIEW,  YUV , RECORD }, // High-resolution in-app video processing with preview.
-            {YUV , PREVIEW,  YUV , RECORD }, // Two-input in-app video processing.
-            {PRIV, PREVIEW,  PRIV, RECORD,   JPEG, RECORD  }, // High-resolution recording with video snapshot.
-            {PRIV, PREVIEW,  YUV,  RECORD,   JPEG, RECORD  }, // High-resolution in-app processing with video snapshot.
-            {YUV , PREVIEW,  YUV,  PREVIEW,  JPEG, MAXIMUM }  // Two-input in-app processing with still capture.
+            // High-resolution video recording with preview.
+            {PRIV, PREVIEW,  PRIV, RECORD },
+            // High-resolution in-app video processing with preview.
+            {PRIV, PREVIEW,  YUV , RECORD },
+            // Two-input in-app video processing.
+            {YUV , PREVIEW,  YUV , RECORD },
+            // High-resolution recording with video snapshot.
+            {PRIV, PREVIEW,  PRIV, RECORD,   JPEG, RECORD  },
+            // High-resolution in-app processing with video snapshot.
+            {PRIV, PREVIEW,  YUV,  RECORD,   JPEG, RECORD  },
+            // Two-input in-app processing with still capture.
+            {YUV , PREVIEW,  YUV,  PREVIEW,  JPEG, MAXIMUM }
         };
 
         final int[][] BURST_COMBINATIONS = {
-            {PRIV, PREVIEW,  PRIV, MAXIMUM }, // Maximum-resolution GPU processing with preview.
-            {PRIV, PREVIEW,  YUV,  MAXIMUM }, // Maximum-resolution in-app processing with preview.
-            {YUV,  PREVIEW,  YUV,  MAXIMUM }, // Maximum-resolution two-input in-app processsing.
+            // Maximum-resolution GPU processing with preview.
+            {PRIV, PREVIEW,  PRIV, MAXIMUM },
+            // Maximum-resolution in-app processing with preview.
+            {PRIV, PREVIEW,  YUV,  MAXIMUM },
+            // Maximum-resolution two-input in-app processsing.
+            {YUV,  PREVIEW,  YUV,  MAXIMUM },
         };
 
         final int[][] FULL_COMBINATIONS = {
-            {PRIV, PREVIEW,  PRIV, PREVIEW,  JPEG, MAXIMUM }, //Video recording with maximum-size video snapshot.
-            {YUV,  VGA,      PRIV, PREVIEW,  YUV,  MAXIMUM }, // Standard video recording plus maximum-resolution in-app processing.
-            {YUV,  VGA,      YUV,  PREVIEW,  YUV,  MAXIMUM } // Preview plus two-input maximum-resolution in-app processing.
+            // Video recording with maximum-size video snapshot.
+            {PRIV, PREVIEW,  PRIV, PREVIEW,  JPEG, MAXIMUM },
+            // Standard video recording plus maximum-resolution in-app processing.
+            {YUV,  VGA,      PRIV, PREVIEW,  YUV,  MAXIMUM },
+            // Preview plus two-input maximum-resolution in-app processing.
+            {YUV,  VGA,      YUV,  PREVIEW,  YUV,  MAXIMUM }
         };
 
         final int[][] RAW_COMBINATIONS = {
-            {RAW,  MAXIMUM }, // No-preview DNG capture.
-            {PRIV, PREVIEW,  RAW,  MAXIMUM }, // Standard DNG capture.
-            {YUV,  PREVIEW,  RAW,  MAXIMUM }, // In-app processing plus DNG capture.
-            {PRIV, PREVIEW,  PRIV, PREVIEW,  RAW, MAXIMUM}, // Video recording with DNG capture.
-            {PRIV, PREVIEW,  YUV,  PREVIEW,  RAW, MAXIMUM}, // Preview with in-app processing and DNG capture.
-            {YUV,  PREVIEW,  YUV,  PREVIEW,  RAW, MAXIMUM}, // Two-input in-app processing plus DNG capture.
-            {PRIV, PREVIEW,  JPEG, MAXIMUM,  RAW, MAXIMUM}, // Still capture with simultaneous JPEG and DNG.
-            {YUV,  PREVIEW,  JPEG, MAXIMUM,  RAW, MAXIMUM}  // In-app processing with simultaneous JPEG and DNG.
+            // No-preview DNG capture.
+            {RAW,  MAXIMUM },
+            // Standard DNG capture.
+            {PRIV, PREVIEW,  RAW,  MAXIMUM },
+            // In-app processing plus DNG capture.
+            {YUV,  PREVIEW,  RAW,  MAXIMUM },
+            // Video recording with DNG capture.
+            {PRIV, PREVIEW,  PRIV, PREVIEW,  RAW, MAXIMUM},
+            // Preview with in-app processing and DNG capture.
+            {PRIV, PREVIEW,  YUV,  PREVIEW,  RAW, MAXIMUM},
+            // Two-input in-app processing plus DNG capture.
+            {YUV,  PREVIEW,  YUV,  PREVIEW,  RAW, MAXIMUM},
+            // Still capture with simultaneous JPEG and DNG.
+            {PRIV, PREVIEW,  JPEG, MAXIMUM,  RAW, MAXIMUM},
+            // In-app processing with simultaneous JPEG and DNG.
+            {YUV,  PREVIEW,  JPEG, MAXIMUM,  RAW, MAXIMUM}
+        };
+
+        final int[][] LEVEL_3_COMBINATIONS = {
+            // In-app viewfinder analysis with dynamic selection of output format
+            {PRIV, PREVIEW, PRIV, VGA, YUV, MAXIMUM, RAW, MAXIMUM},
+            // In-app viewfinder analysis with dynamic selection of output format
+            {PRIV, PREVIEW, PRIV, VGA, JPEG, MAXIMUM, RAW, MAXIMUM}
         };
 
         final int[][][] TABLES =
-            { LEGACY_COMBINATIONS, LIMITED_COMBINATIONS, BURST_COMBINATIONS, FULL_COMBINATIONS, RAW_COMBINATIONS };
+                { LEGACY_COMBINATIONS, LIMITED_COMBINATIONS, BURST_COMBINATIONS, FULL_COMBINATIONS,
+                  RAW_COMBINATIONS, LEVEL_3_COMBINATIONS };
 
         sanityCheckConfigurationTables(TABLES);
 
@@ -257,7 +293,7 @@
                     }
                 }
 
-                if (mStaticInfo.isHardwareLevelFull()) {
+                if (mStaticInfo.isHardwareLevelAtLeastFull()) {
                     for (int[] config : FULL_COMBINATIONS) {
                         testOutputCombination(id, config, maxSizes);
                     }
@@ -269,6 +305,13 @@
                         testOutputCombination(id, config, maxSizes);
                     }
                 }
+
+                if (mStaticInfo.isHardwareLevelAtLeast(
+                        CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_3)) {
+                    for (int[] config: LEVEL_3_COMBINATIONS) {
+                        testOutputCombination(id, config, maxSizes);
+                    }
+                }
             }
 
             closeDevice(id);
@@ -287,45 +330,55 @@
          *    2. Reprocess capture requests targeting YUV and JPEG outputs are successful.
          */
         final int[][] LIMITED_COMBINATIONS = {
-            // Input        Outputs
-            {PRIV, MAXIMUM, JPEG, MAXIMUM},
-            {YUV , MAXIMUM, JPEG, MAXIMUM},
-            {PRIV, MAXIMUM, PRIV, PREVIEW, JPEG, MAXIMUM},
-            {YUV , MAXIMUM, PRIV, PREVIEW, JPEG, MAXIMUM},
-            {PRIV, MAXIMUM, YUV , PREVIEW, JPEG, MAXIMUM},
-            {YUV , MAXIMUM, YUV , PREVIEW, JPEG, MAXIMUM},
-            {PRIV, MAXIMUM, YUV , PREVIEW, YUV , PREVIEW, JPEG, MAXIMUM},
-            {YUV,  MAXIMUM, YUV , PREVIEW, YUV , PREVIEW, JPEG, MAXIMUM},
+            // Input           Outputs
+            {PRIV, MAXIMUM,    JPEG, MAXIMUM},
+            {YUV , MAXIMUM,    JPEG, MAXIMUM},
+            {PRIV, MAXIMUM,    PRIV, PREVIEW, JPEG, MAXIMUM},
+            {YUV , MAXIMUM,    PRIV, PREVIEW, JPEG, MAXIMUM},
+            {PRIV, MAXIMUM,    YUV , PREVIEW, JPEG, MAXIMUM},
+            {YUV , MAXIMUM,    YUV , PREVIEW, JPEG, MAXIMUM},
+            {PRIV, MAXIMUM,    YUV , PREVIEW, YUV , PREVIEW, JPEG, MAXIMUM},
+            {YUV,  MAXIMUM,    YUV , PREVIEW, YUV , PREVIEW, JPEG, MAXIMUM},
         };
 
         final int[][] FULL_COMBINATIONS = {
-            // Input        Outputs
-            {YUV , MAXIMUM, PRIV, PREVIEW},
-            {YUV , MAXIMUM, YUV , PREVIEW},
-            {PRIV, MAXIMUM, PRIV, PREVIEW, YUV , RECORD},
-            {YUV , MAXIMUM, PRIV, PREVIEW, YUV , RECORD},
-            {PRIV, MAXIMUM, PRIV, PREVIEW, YUV , MAXIMUM},
-            {PRIV, MAXIMUM, YUV , PREVIEW, YUV , MAXIMUM},
-            {PRIV, MAXIMUM, PRIV, PREVIEW, YUV , PREVIEW, JPEG, MAXIMUM},
-            {YUV , MAXIMUM, PRIV, PREVIEW, YUV , PREVIEW, JPEG, MAXIMUM},
+            // Input           Outputs
+            {YUV , MAXIMUM,    PRIV, PREVIEW},
+            {YUV , MAXIMUM,    YUV , PREVIEW},
+            {PRIV, MAXIMUM,    PRIV, PREVIEW, YUV , RECORD},
+            {YUV , MAXIMUM,    PRIV, PREVIEW, YUV , RECORD},
+            {PRIV, MAXIMUM,    PRIV, PREVIEW, YUV , MAXIMUM},
+            {PRIV, MAXIMUM,    YUV , PREVIEW, YUV , MAXIMUM},
+            {PRIV, MAXIMUM,    PRIV, PREVIEW, YUV , PREVIEW, JPEG, MAXIMUM},
+            {YUV , MAXIMUM,    PRIV, PREVIEW, YUV , PREVIEW, JPEG, MAXIMUM},
         };
 
         final int[][] RAW_COMBINATIONS = {
-            // Input        Outputs
-            {PRIV, MAXIMUM, YUV , PREVIEW, RAW , MAXIMUM},
-            {YUV , MAXIMUM, YUV , PREVIEW, RAW , MAXIMUM},
-            {PRIV, MAXIMUM, PRIV, PREVIEW, YUV , PREVIEW, RAW , MAXIMUM},
-            {YUV , MAXIMUM, PRIV, PREVIEW, YUV , PREVIEW, RAW , MAXIMUM},
-            {PRIV, MAXIMUM, YUV , PREVIEW, YUV , PREVIEW, RAW , MAXIMUM},
-            {YUV , MAXIMUM, YUV , PREVIEW, YUV , PREVIEW, RAW , MAXIMUM},
-            {PRIV, MAXIMUM, PRIV, PREVIEW, JPEG, MAXIMUM, RAW , MAXIMUM},
-            {YUV , MAXIMUM, PRIV, PREVIEW, JPEG, MAXIMUM, RAW , MAXIMUM},
-            {PRIV, MAXIMUM, YUV , PREVIEW, JPEG, MAXIMUM, RAW , MAXIMUM},
-            {YUV , MAXIMUM, YUV , PREVIEW, JPEG, MAXIMUM, RAW , MAXIMUM},
+            // Input           Outputs
+            {PRIV, MAXIMUM,    YUV , PREVIEW, RAW , MAXIMUM},
+            {YUV , MAXIMUM,    YUV , PREVIEW, RAW , MAXIMUM},
+            {PRIV, MAXIMUM,    PRIV, PREVIEW, YUV , PREVIEW, RAW , MAXIMUM},
+            {YUV , MAXIMUM,    PRIV, PREVIEW, YUV , PREVIEW, RAW , MAXIMUM},
+            {PRIV, MAXIMUM,    YUV , PREVIEW, YUV , PREVIEW, RAW , MAXIMUM},
+            {YUV , MAXIMUM,    YUV , PREVIEW, YUV , PREVIEW, RAW , MAXIMUM},
+            {PRIV, MAXIMUM,    PRIV, PREVIEW, JPEG, MAXIMUM, RAW , MAXIMUM},
+            {YUV , MAXIMUM,    PRIV, PREVIEW, JPEG, MAXIMUM, RAW , MAXIMUM},
+            {PRIV, MAXIMUM,    YUV , PREVIEW, JPEG, MAXIMUM, RAW , MAXIMUM},
+            {YUV , MAXIMUM,    YUV , PREVIEW, JPEG, MAXIMUM, RAW , MAXIMUM},
+        };
+
+        final int[][] LEVEL_3_COMBINATIONS = {
+            // Input          Outputs
+            // In-app viewfinder analysis with YUV->YUV ZSL and RAW
+            {YUV , MAXIMUM,   PRIV, PREVIEW, PRIV, VGA, RAW, MAXIMUM},
+            // In-app viewfinder analysis with PRIV->JPEG ZSL and RAW
+            {PRIV, MAXIMUM,   PRIV, PREVIEW, PRIV, VGA, RAW, MAXIMUM, JPEG, MAXIMUM},
+            // In-app viewfinder analysis with YUV->JPEG ZSL and RAW
+            {YUV , MAXIMUM,   PRIV, PREVIEW, PRIV, VGA, RAW, MAXIMUM, JPEG, MAXIMUM},
         };
 
         final int[][][] TABLES =
-                { LIMITED_COMBINATIONS, FULL_COMBINATIONS, RAW_COMBINATIONS };
+                { LIMITED_COMBINATIONS, FULL_COMBINATIONS, RAW_COMBINATIONS, LEVEL_3_COMBINATIONS };
 
         sanityCheckConfigurationTables(TABLES);
 
@@ -347,7 +400,7 @@
                 }
 
                 // Check FULL devices
-                if (staticInfo.isHardwareLevelFull()) {
+                if (staticInfo.isHardwareLevelAtLeastFull()) {
                     for (int[] config : FULL_COMBINATIONS) {
                         testReprocessStreamCombination(id, config, maxSizes, staticInfo);
                     }
@@ -360,6 +413,13 @@
                         testReprocessStreamCombination(id, config, maxSizes, staticInfo);
                     }
                 }
+
+                if (mStaticInfo.isHardwareLevelAtLeast(
+                        CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_3)) {
+                    for (int[] config: LEVEL_3_COMBINATIONS) {
+                        testReprocessStreamCombination(id, config, maxSizes, staticInfo);
+                    }
+                }
             } finally {
                 closeDevice(id);
             }
diff --git a/tests/camera/src/android/hardware/camera2/cts/StaticMetadataTest.java b/tests/camera/src/android/hardware/camera2/cts/StaticMetadataTest.java
index 3e3e81e..3f2d886 100644
--- a/tests/camera/src/android/hardware/camera2/cts/StaticMetadataTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/StaticMetadataTest.java
@@ -78,7 +78,15 @@
                     availableCaps.contains(REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE) ||
                     availableCaps.contains(REQUEST_AVAILABLE_CAPABILITIES_DEPTH_OUTPUT) );
 
-            if (mStaticInfo.isHardwareLevelFull()) {
+            if (mStaticInfo.isHardwareLevelAtLeast(
+                    CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_3)) {
+                mCollector.expectTrue("Level 3 device must contain YUV_REPROCESSING capability",
+                        availableCaps.contains(REQUEST_AVAILABLE_CAPABILITIES_YUV_REPROCESSING));
+                mCollector.expectTrue("Level 3 device must contain RAW capability",
+                        availableCaps.contains(REQUEST_AVAILABLE_CAPABILITIES_RAW));
+            }
+
+            if (mStaticInfo.isHardwareLevelAtLeastFull()) {
                 // Capability advertisement must be right.
                 mCollector.expectTrue("Full device must contain MANUAL_SENSOR capability",
                         availableCaps.contains(REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR));
@@ -132,7 +140,7 @@
             mCollector.expectTrue("max number of processed (stalling) output streams must be >= 1",
                     maxNumStreamsProcStall >= 1);
 
-            if (mStaticInfo.isHardwareLevelFull()) {
+            if (mStaticInfo.isHardwareLevelAtLeastFull()) {
                 mCollector.expectTrue("max number of processed (non-stalling) output streams" +
                         "must be >= 3 for FULL device",
                         maxNumStreamsProc >= 3);
@@ -328,7 +336,7 @@
                 Boolean contrastCurveModeSupported = false;
                 Boolean gammaAndPresetModeSupported = false;
                 Boolean offColorAberrationModeSupported = false;
-                if (mStaticInfo.isHardwareLevelLimitedOrBetter() && mStaticInfo.isColorOutputSupported()) {
+                if (mStaticInfo.isHardwareLevelAtLeastLimited() && mStaticInfo.isColorOutputSupported()) {
                     int[] tonemapModes = mStaticInfo.getAvailableToneMapModesChecked();
                     List<Integer> modeList = (tonemapModes.length == 0) ?
                             new ArrayList<Integer>() :
diff --git a/tests/camera/src/android/hardware/camera2/cts/StillCaptureTest.java b/tests/camera/src/android/hardware/camera2/cts/StillCaptureTest.java
index e7978c6..de9b189 100644
--- a/tests/camera/src/android/hardware/camera2/cts/StillCaptureTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/StillCaptureTest.java
@@ -527,7 +527,7 @@
             stillRequest.set(CaptureRequest.CONTROL_AWB_REGIONS, awbRegions);
         }
         mSession.setRepeatingRequest(previewRequest.build(), resultListener, mHandler);
-        if (mStaticInfo.isHardwareLevelLimitedOrBetter()) {
+        if (mStaticInfo.isHardwareLevelAtLeastLimited()) {
             waitForResultValue(resultListener, CaptureResult.CONTROL_AWB_STATE,
                     CaptureResult.CONTROL_AWB_STATE_CONVERGED, NUM_RESULTS_WAIT_TIMEOUT);
         } else {
diff --git a/tests/camera/src/android/hardware/camera2/cts/SurfaceViewPreviewTest.java b/tests/camera/src/android/hardware/camera2/cts/SurfaceViewPreviewTest.java
index 06daa51..9dd02ce 100644
--- a/tests/camera/src/android/hardware/camera2/cts/SurfaceViewPreviewTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/SurfaceViewPreviewTest.java
@@ -20,6 +20,9 @@
 
 import android.graphics.ImageFormat;
 import android.view.Surface;
+
+import com.android.ex.camera2.blocking.BlockingSessionCallback;
+
 import android.hardware.camera2.CameraCaptureSession;
 import android.hardware.camera2.CameraCaptureSession.CaptureCallback;
 import android.hardware.camera2.CameraDevice;
@@ -30,6 +33,7 @@
 import android.util.Size;
 import android.hardware.camera2.cts.CameraTestUtils.SimpleCaptureCallback;
 import android.hardware.camera2.cts.testcases.Camera2SurfaceViewTestCase;
+import android.hardware.camera2.params.OutputConfiguration;
 import android.util.Log;
 import android.util.Pair;
 import android.util.Range;
@@ -53,6 +57,7 @@
     private static final int NUM_FRAMES_VERIFIED = 30;
     private static final int NUM_TEST_PATTERN_FRAMES_VERIFIED = 60;
     private static final float FRAME_DURATION_ERROR_MARGIN = 0.005f; // 0.5 percent error margin.
+    private static final int PREPARE_TIMEOUT_MS = 10000; // 10 s
 
     @Override
     protected void setUp() throws Exception {
@@ -133,6 +138,31 @@
     }
 
     /**
+     * Test surface set streaming use cases.
+     *
+     * <p>
+     * The test sets output configuration with increasing surface set IDs for preview and YUV
+     * streams. The max supported preview size is selected for preview stream, and the max
+     * supported YUV size (depending on hw supported level) is selected for YUV stream. This test
+     * also exercises the prepare API.
+     * </p>
+     */
+    public void testSurfaceSet() throws Exception {
+        for (String id : mCameraIds) {
+            try {
+                openDevice(id);
+                if (!mStaticInfo.isColorOutputSupported()) {
+                    Log.i(TAG, "Camera " + id + " does not support color outputs, skipping");
+                    continue;
+                }
+                surfaceSetTestByCamera(id);
+            } finally {
+                closeDevice();
+            }
+        }
+    }
+
+    /**
      * Test to verify the {@link CameraCaptureSession#prepare} method works correctly, and has the
      * expected effects on performance.
      *
@@ -163,7 +193,6 @@
         final int UNKNOWN_LATENCY_RESULT_WAIT = 5;
         final int MAX_RESULTS_TO_WAIT = 10;
         final int FRAMES_FOR_AVERAGING = 100;
-        final int PREPARE_TIMEOUT_MS = 10000; // 10 s
         final float PREPARE_FRAME_RATE_BOUNDS = 0.05f; // fraction allowed difference
         final float PREPARE_PEAK_RATE_BOUNDS = 0.5f; // fraction allowed difference
 
@@ -206,7 +235,7 @@
             // Lock AE if possible to improve stability
             previewRequest.set(CaptureRequest.CONTROL_AE_LOCK, true);
             mSession.setRepeatingRequest(previewRequest.build(), resultListener, mHandler);
-            if (mStaticInfo.isHardwareLevelLimitedOrBetter()) {
+            if (mStaticInfo.isHardwareLevelAtLeastLimited()) {
                 // Legacy mode doesn't output AE state
                 waitForResultValue(resultListener, CaptureResult.CONTROL_AE_STATE,
                         CaptureResult.CONTROL_AE_STATE_LOCKED, MAX_RESULTS_TO_WAIT);
@@ -241,7 +270,7 @@
                             whilePreparingFrameDurationStats.first / 1e6,
                             whilePreparingFrameDurationStats.second / 1e6));
 
-            if (mStaticInfo.isHardwareLevelLimitedOrBetter()) {
+            if (mStaticInfo.isHardwareLevelAtLeastLimited()) {
                 mCollector.expectTrue(
                     String.format("Camera %s: Preview peak frame interval affected by prepare " +
                             "call: preview avg frame duration: %f ms, peak during prepare: %f ms",
@@ -279,7 +308,7 @@
                         preparedFrameDurationStats.first / 1e6,
                         preparedFrameDurationStats.second / 1e6));
 
-        if (mStaticInfo.isHardwareLevelLimitedOrBetter()) {
+        if (mStaticInfo.isHardwareLevelAtLeastLimited()) {
             mCollector.expectTrue(
                 String.format("Camera %s: Preview peak frame interval affected by use of new " +
                         " stream: preview avg frame duration: %f ms, peak with new stream: %f ms",
@@ -330,7 +359,6 @@
      * validated.
      */
     private void previewFpsRangeTestByCamera() throws Exception {
-        final int FPS_RANGE_SIZE = 2;
         Size maxPreviewSz = mOrderedPreviewSizes.get(0);
         Range<Integer>[] fpsRanges = mStaticInfo.getAeAvailableTargetFpsRangesChecked();
         boolean antiBandingOffIsSupported = mStaticInfo.isAntiBandingOffModeSupported();
@@ -450,6 +478,72 @@
         stopPreview();
     }
 
+    private void surfaceSetTestByCamera(String cameraId) throws Exception {
+        final int MAX_SURFACE_SET_ID = 10;
+        Size maxPreviewSz = mOrderedPreviewSizes.get(0);
+        Size yuvSizeBound = maxPreviewSz; // Default case: legacy device
+        if (mStaticInfo.isHardwareLevelLimited()) {
+            yuvSizeBound = mOrderedVideoSizes.get(0);
+        } else if (mStaticInfo.isHardwareLevelAtLeastFull()) {
+            yuvSizeBound = null;
+        }
+        Size maxYuvSize = getSupportedPreviewSizes(cameraId, mCameraManager, yuvSizeBound).get(0);
+
+        CaptureRequest.Builder requestBuilder =
+                mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
+        ImageDropperListener imageListener = new ImageDropperListener();
+
+        updatePreviewSurface(maxPreviewSz);
+        createImageReader(maxYuvSize, ImageFormat.YUV_420_888, MAX_READER_IMAGES, imageListener);
+        List<OutputConfiguration> outputConfigs = new ArrayList<OutputConfiguration>();
+        OutputConfiguration previewConfig = new OutputConfiguration(mPreviewSurface);
+        OutputConfiguration yuvConfig = new OutputConfiguration(mReaderSurface);
+        assertEquals(OutputConfiguration.SURFACE_SET_ID_INVALID, previewConfig.getSurfaceSetId());
+        assertEquals(OutputConfiguration.SURFACE_SET_ID_INVALID, yuvConfig.getSurfaceSetId());
+        assertEquals(mPreviewSurface, previewConfig.getSurface());
+        assertEquals(mReaderSurface, yuvConfig.getSurface());
+        outputConfigs.add(previewConfig);
+        outputConfigs.add(yuvConfig);
+        requestBuilder.addTarget(mPreviewSurface);
+        requestBuilder.addTarget(mReaderSurface);
+
+        // Test different stream set ID.
+        for (int surfaceSetId = OutputConfiguration.SURFACE_SET_ID_INVALID;
+                surfaceSetId < MAX_SURFACE_SET_ID; surfaceSetId++) {
+            if (VERBOSE) {
+                Log.v(TAG, "test preview with surface set id: ");
+            }
+            for (OutputConfiguration config : outputConfigs) {
+                config.setSurfaceSetId(surfaceSetId);
+                assertEquals(surfaceSetId, config.getSurfaceSetId());
+            }
+
+            CameraCaptureSession.StateCallback mockSessionListener =
+                    mock(CameraCaptureSession.StateCallback.class);
+
+            mSession = configureCameraSessionWithConfig(mCamera, outputConfigs,
+                    mockSessionListener, mHandler);
+
+
+            mSession.prepare(mPreviewSurface);
+            verify(mockSessionListener,
+                    timeout(PREPARE_TIMEOUT_MS).times(1)).
+                    onSurfacePrepared(eq(mSession), eq(mPreviewSurface));
+
+            mSession.prepare(mReaderSurface);
+            verify(mockSessionListener,
+                    timeout(PREPARE_TIMEOUT_MS).times(1)).
+                    onSurfacePrepared(eq(mSession), eq(mReaderSurface));
+
+            CaptureRequest request = requestBuilder.build();
+            CaptureCallback mockCaptureCallback =
+                    mock(CameraCaptureSession.CaptureCallback.class);
+            mSession.setRepeatingRequest(request, mockCaptureCallback, mHandler);
+            verifyCaptureResults(mSession, mockCaptureCallback, NUM_FRAMES_VERIFIED,
+                    NUM_FRAMES_VERIFIED * FRAME_TIMEOUT_MS);
+        }
+    }
+
     private class IsCaptureResultValid extends ArgumentMatcher<TotalCaptureResult> {
         @Override
         public boolean matches(Object obj) {
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 cb6b04c..5736ebd 100644
--- a/tests/camera/src/android/hardware/camera2/cts/helpers/StaticMetadata.java
+++ b/tests/camera/src/android/hardware/camera2/cts/helpers/StaticMetadata.java
@@ -191,15 +191,28 @@
 
     /**
      * Whether or not the hardware level reported by android.info.supportedHardwareLevel
-     * is {@value CameraMetadata#INFO_SUPPORTED_HARDWARE_LEVEL_FULL}.
+     * is at least {@value CameraMetadata#INFO_SUPPORTED_HARDWARE_LEVEL_FULL}.
      *
      * <p>If the camera device is not reporting the hardwareLevel, this
      * will cause the test to fail.</p>
      *
      * @return {@code true} if the device is {@code FULL}, {@code false} otherwise.
      */
-    public boolean isHardwareLevelFull() {
-        return getHardwareLevelChecked() == CameraMetadata.INFO_SUPPORTED_HARDWARE_LEVEL_FULL;
+    public boolean isHardwareLevelAtLeastFull() {
+        return isHardwareLevelAtLeast(CameraMetadata.INFO_SUPPORTED_HARDWARE_LEVEL_FULL);
+    }
+
+    /**
+     * Whether or not the hardware level reported by android.info.supportedHardwareLevel is
+     * at least the desired one (but could be higher)
+     */
+    public boolean isHardwareLevelAtLeast(int level) {
+        int deviceLevel = getHardwareLevelChecked();
+        if (deviceLevel == CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY) {
+            return level == deviceLevel;
+        }
+        // deviceLevel is not LEGACY, can use numerical sort
+        return level <= deviceLevel;
     }
 
     /**
@@ -279,18 +292,8 @@
      *          {@code true} if the device is {@code LIMITED} or {@code FULL},
      *          {@code false} otherwise (i.e. LEGACY).
      */
-    public boolean isHardwareLevelLimitedOrBetter() {
-        Integer hwLevel = getValueFromKeyNonNull(
-                CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL);
-
-        if (hwLevel == null) {
-            return false;
-        }
-
-        // Normal. Device could be limited.
-        int hwLevelInt = hwLevel;
-        return hwLevelInt == CameraMetadata.INFO_SUPPORTED_HARDWARE_LEVEL_FULL ||
-                hwLevelInt == CameraMetadata.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED;
+    public boolean isHardwareLevelAtLeastLimited() {
+        return isHardwareLevelAtLeast(CameraMetadata.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED);
     }
 
     /**
@@ -391,7 +394,7 @@
          * android.lens.info.minimumFocusDistance - required for FULL and MANUAL_SENSOR-capable
          *   devices; optional for all other devices.
          */
-        if (isHardwareLevelFull() || isCapabilitySupported(
+        if (isHardwareLevelAtLeastFull() || isCapabilitySupported(
                 CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR)) {
             minFocusDistance = getValueFromKeyNonNull(key);
         } else {
@@ -625,12 +628,12 @@
         }
 
         List<Integer> modeList = Arrays.asList(CameraTestUtils.toObject(modes));
-        if (isHardwareLevelFull()) {
+        if (isHardwareLevelAtLeastFull()) {
             checkTrueForKey(key, "Full-capability camera devices must support FAST mode",
                     modeList.contains(CameraMetadata.HOT_PIXEL_MODE_FAST));
         }
 
-        if (isHardwareLevelLimitedOrBetter()) {
+        if (isHardwareLevelAtLeastLimited()) {
             // FAST and HIGH_QUALITY mode must be both present or both not present
             List<Integer> coupledModes = Arrays.asList(new Integer[] {
                     CameraMetadata.HOT_PIXEL_MODE_FAST,
@@ -723,7 +726,7 @@
         // Qualification check for MANUAL_POSTPROCESSING capability is in
         // StaticMetadataTest#testCapabilities
 
-        if (isHardwareLevelLimitedOrBetter()) {
+        if (isHardwareLevelAtLeastLimited()) {
             // FAST and HIGH_QUALITY mode must be both present or both not present
             List<Integer> coupledModes = Arrays.asList(new Integer[] {
                     CameraMetadata.TONEMAP_MODE_FAST,
@@ -887,7 +890,7 @@
         Key<Integer> key = CameraCharacteristics.SENSOR_MAX_ANALOG_SENSITIVITY;
         Integer maxAnalogsensitivity = mCharacteristics.get(key);
         if (maxAnalogsensitivity == null) {
-            if (isHardwareLevelFull()) {
+            if (isHardwareLevelAtLeastFull()) {
                 Assert.fail("Full device should report max analog sensitivity");
             }
             return 0;
@@ -1170,7 +1173,7 @@
 
         // FULL mode camera devices always support OFF mode.
         boolean condition =
-                !isHardwareLevelFull() || modeList.contains(CameraMetadata.CONTROL_AE_MODE_OFF);
+                !isHardwareLevelAtLeastFull() || modeList.contains(CameraMetadata.CONTROL_AE_MODE_OFF);
         checkTrueForKey(modesKey, "Full capability device must have OFF mode", condition);
 
         // Boundary check.
@@ -1201,7 +1204,7 @@
         List<Integer> modesList = Arrays.asList(CameraTestUtils.toObject(awbModes));
         checkTrueForKey(key, " All camera devices must support AUTO mode",
                 modesList.contains(CameraMetadata.CONTROL_AWB_MODE_AUTO));
-        if (isHardwareLevelFull()) {
+        if (isHardwareLevelAtLeastFull()) {
             checkTrueForKey(key, " Full capability camera devices must support OFF mode",
                     modesList.contains(CameraMetadata.CONTROL_AWB_MODE_OFF));
         }
@@ -1225,7 +1228,7 @@
         }
 
         List<Integer> modesList = Arrays.asList(CameraTestUtils.toObject(afModes));
-        if (isHardwareLevelLimitedOrBetter()) {
+        if (isHardwareLevelAtLeastLimited()) {
             // Some LEGACY mode devices do not support AF OFF
             checkTrueForKey(key, " All camera devices must support OFF mode",
                     modesList.contains(CameraMetadata.CONTROL_AF_MODE_OFF));
@@ -1496,13 +1499,13 @@
 
         List<Integer> modeList = Arrays.asList(CameraTestUtils.toObject(edgeModes));
         // Full device should always include OFF and FAST
-        if (isHardwareLevelFull()) {
+        if (isHardwareLevelAtLeastFull()) {
             checkTrueForKey(key, "Full device must contain OFF and FAST edge modes",
                     modeList.contains(CameraMetadata.EDGE_MODE_OFF) &&
                     modeList.contains(CameraMetadata.EDGE_MODE_FAST));
         }
 
-        if (isHardwareLevelLimitedOrBetter()) {
+        if (isHardwareLevelAtLeastLimited()) {
             // FAST and HIGH_QUALITY mode must be both present or both not present
             List<Integer> coupledModes = Arrays.asList(new Integer[] {
                     CameraMetadata.EDGE_MODE_FAST,
@@ -1527,14 +1530,14 @@
 
         List<Integer> modeList = Arrays.asList(CameraTestUtils.toObject(noiseReductionModes));
         // Full device should always include OFF and FAST
-        if (isHardwareLevelFull()) {
+        if (isHardwareLevelAtLeastFull()) {
 
             checkTrueForKey(key, "Full device must contain OFF and FAST noise reduction modes",
                     modeList.contains(CameraMetadata.NOISE_REDUCTION_MODE_OFF) &&
                     modeList.contains(CameraMetadata.NOISE_REDUCTION_MODE_FAST));
         }
 
-        if (isHardwareLevelLimitedOrBetter()) {
+        if (isHardwareLevelAtLeastLimited()) {
             // FAST and HIGH_QUALITY mode must be both present or both not present
             List<Integer> coupledModes = Arrays.asList(new Integer[] {
                     CameraMetadata.NOISE_REDUCTION_MODE_FAST,
@@ -1563,7 +1566,7 @@
         }
 
         // Legacy devices don't have a minimum step requirement
-        if (isHardwareLevelLimitedOrBetter()) {
+        if (isHardwareLevelAtLeastLimited()) {
             float compensationStepF =
                     (float) compensationStep.getNumerator() / compensationStep.getDenominator();
             checkTrueForKey(key, " value must be no more than 1/2", compensationStepF <= 0.5f);
@@ -1592,7 +1595,7 @@
         }
 
         // Legacy devices don't have a minimum range requirement
-        if (isHardwareLevelLimitedOrBetter() && !compensationRange.equals(ZERO_RANGE)) {
+        if (isHardwareLevelAtLeastLimited() && !compensationRange.equals(ZERO_RANGE)) {
             checkTrueForKey(key, " range value must be at least " + DEFAULT_RANGE
                     + ", actual " + compensationRange + ", compensation step " + compensationStep,
                    compensationRange.getLower() <= DEFAULT_RANGE.getLower() &&
@@ -1729,7 +1732,7 @@
                 modeList.contains(CameraMetadata.COLOR_CORRECTION_ABERRATION_MODE_OFF) ||
                 modeList.contains(CameraMetadata.COLOR_CORRECTION_ABERRATION_MODE_FAST));
 
-        if (isHardwareLevelLimitedOrBetter()) {
+        if (isHardwareLevelAtLeastLimited()) {
             // FAST and HIGH_QUALITY mode must be both present or both not present
             List<Integer> coupledModes = Arrays.asList(new Integer[] {
                     CameraMetadata.COLOR_CORRECTION_ABERRATION_MODE_FAST,
diff --git a/tests/camera/src/android/hardware/camera2/cts/testcases/Camera2SurfaceViewTestCase.java b/tests/camera/src/android/hardware/camera2/cts/testcases/Camera2SurfaceViewTestCase.java
index b7287ae..539304c 100644
--- a/tests/camera/src/android/hardware/camera2/cts/testcases/Camera2SurfaceViewTestCase.java
+++ b/tests/camera/src/android/hardware/camera2/cts/testcases/Camera2SurfaceViewTestCase.java
@@ -485,7 +485,7 @@
             int numResultWaitForUnknownLatency) {
         waitForSettingsApplied(resultListener, numResultWaitForUnknownLatency);
 
-        if (!mStaticInfo.isHardwareLevelLimitedOrBetter()) {
+        if (!mStaticInfo.isHardwareLevelAtLeastLimited()) {
             // No-op for metadata
             return;
         }
@@ -515,7 +515,7 @@
 
         waitForSettingsApplied(resultListener, numResultWaitForUnknownLatency);
 
-        if (!mStaticInfo.isHardwareLevelLimitedOrBetter()) {
+        if (!mStaticInfo.isHardwareLevelAtLeastLimited()) {
             // No-op for legacy devices
             return;
         }
diff --git a/tests/tests/automotive/src/android/support/car/cts/CarUiProviderTest.java b/tests/tests/automotive/src/android/support/car/cts/CarUiProviderTest.java
new file mode 100644
index 0000000..07e10cb
--- /dev/null
+++ b/tests/tests/automotive/src/android/support/car/cts/CarUiProviderTest.java
@@ -0,0 +1,128 @@
+/*
+ * 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.support.car.cts;
+
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.graphics.Bitmap;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.test.AndroidTestCase;
+import android.util.Log;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Test the existence of compatibility apis in the car ui provider.
+ *
+ * This test will only be run on devices with automotive feature.
+ */
+public class CarUiProviderTest extends AndroidTestCase {
+    private static final String TAG = "CarUiProviderTest";
+    private static final String UI_ENTRY_CLASS_NAME = ".CarUiEntry";
+    private static final String CAR_UI_PROVIDER_PKG = "android.support.car.ui.provider";
+
+    private static final Map<String, Class<?>[]> COMPATIBILITY_APIS =
+            new HashMap<String, Class<?>[]>();
+
+    static {
+        COMPATIBILITY_APIS.put("onStart", new Class<?>[]{});
+        COMPATIBILITY_APIS.put("onResume", new Class<?>[]{});
+        COMPATIBILITY_APIS.put("onPause", new Class<?>[]{});
+        COMPATIBILITY_APIS.put("onStop", new Class<?>[]{});
+        COMPATIBILITY_APIS.put("getContentView", new Class<?>[]{});
+        COMPATIBILITY_APIS.put("setCarMenuBinder", new Class<?>[]{IBinder.class});
+        COMPATIBILITY_APIS.put("getFragmentContainerId", new Class<?>[]{});
+        COMPATIBILITY_APIS.put("setBackground", new Class<?>[]{Bitmap.class});
+        COMPATIBILITY_APIS.put("setBackgroundResource", new Class<?>[]{int.class});
+        COMPATIBILITY_APIS.put("hideMenuButton", new Class<?>[]{});
+        COMPATIBILITY_APIS.put("restoreMenuDrawable", new Class<?>[]{});
+        COMPATIBILITY_APIS.put("setMenuProgress", new Class<?>[]{float.class});
+        COMPATIBILITY_APIS.put("setScrimColor", new Class<?>[]{int.class});
+        COMPATIBILITY_APIS.put("setTitle", new Class<?>[]{CharSequence.class});
+        COMPATIBILITY_APIS.put("setTitleText", new Class<?>[]{CharSequence.class});
+        COMPATIBILITY_APIS.put("closeDrawer", new Class<?>[]{});
+        COMPATIBILITY_APIS.put("openDrawer", new Class<?>[]{});
+        COMPATIBILITY_APIS.put("showMenu", new Class<?>[]{String.class, String.class});
+        COMPATIBILITY_APIS.put("setMenuButtonColor", new Class<?>[]{int.class});
+        COMPATIBILITY_APIS.put("showTitle", new Class<?>[]{});
+        COMPATIBILITY_APIS.put("hideTitle", new Class<?>[]{});
+        COMPATIBILITY_APIS.put("setLightMode", new Class<?>[]{});
+        COMPATIBILITY_APIS.put("setDarkMode", new Class<?>[]{});
+        COMPATIBILITY_APIS.put("setAutoLightDarkMode", new Class<?>[]{});
+        COMPATIBILITY_APIS.put("onRestoreInstanceState", new Class<?>[]{Bundle.class});
+        COMPATIBILITY_APIS.put("onSaveInstanceState", new Class<?>[]{Bundle.class});
+    }
+
+    private boolean mIsCar = false;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mIsCar = getContext().getPackageManager().hasSystemFeature(
+                PackageManager.FEATURE_AUTOMOTIVE
+        );
+    }
+
+    public void testCarUiProvider() throws Exception {
+        if (!mIsCar) {
+            Log.d(TAG, "Bypass CarUiProviderTest on non-automotive devices");
+            return;
+        }
+        checkCompatibilityApi();
+    }
+
+    private void checkCompatibilityApi() {
+        List<String> missingApis = new ArrayList<String>();
+        Class<?> loadedClass = null;
+        try {
+            Context carUiContext = getContext().createPackageContext(
+                    CAR_UI_PROVIDER_PKG,
+                    Context.CONTEXT_INCLUDE_CODE | Context.CONTEXT_IGNORE_SECURITY);
+
+            int flag = getContext().getPackageManager()
+                    .getApplicationInfo(CAR_UI_PROVIDER_PKG, 0).flags;
+            assertEquals(true, (flag & ApplicationInfo.FLAG_SYSTEM) != 0
+                    || (flag & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0);
+
+            ClassLoader classLoader = carUiContext.getClassLoader();
+            loadedClass = classLoader.loadClass(CAR_UI_PROVIDER_PKG + UI_ENTRY_CLASS_NAME);
+        } catch (PackageManager.NameNotFoundException e) {
+            fail("CarUiProvider package does not exsit");
+        } catch (ClassNotFoundException e) {
+            fail("CarUiEntry class not found");
+        }
+
+        if (loadedClass == null) {
+            fail("Fail to load CarUiEntry class");
+        }
+
+        for (Map.Entry<String, Class<?>[]> method : COMPATIBILITY_APIS.entrySet()) {
+            try {
+                loadedClass.getDeclaredMethod(method.getKey(), method.getValue());
+            } catch (NoSuchMethodException e) {
+                missingApis.add(method.getKey());
+            }
+        }
+        assertEquals("Missing the following APIs from CarUiProvider"
+                + Arrays.toString(missingApis.toArray()), 0, missingApis.size());
+    }
+}
diff --git a/tests/tests/automotive/src/android/support/car/cts/ExceptionsTest.java b/tests/tests/automotive/src/android/support/car/cts/ExceptionsTest.java
new file mode 100644
index 0000000..3391883
--- /dev/null
+++ b/tests/tests/automotive/src/android/support/car/cts/ExceptionsTest.java
@@ -0,0 +1,59 @@
+/*
+ * 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.support.car.cts;
+
+import android.support.car.CarNotConnectedException;
+import android.support.car.CarNotSupportedException;
+import android.test.AndroidTestCase;
+
+public class ExceptionsTest extends AndroidTestCase {
+    private static final String MESSAGE = "Oops!";
+    private static final Exception CAUSE = new RuntimeException();
+
+    public void testCarNotConnectedException() {
+        CarNotConnectedException exception = new CarNotConnectedException();
+        assertNull(exception.getMessage());
+        assertNull(exception.getCause());
+
+        exception = new CarNotConnectedException(MESSAGE);
+        assertEquals(MESSAGE, exception.getMessage());
+        assertNull(exception.getCause());
+
+        exception = new CarNotConnectedException(MESSAGE, CAUSE);
+        assertEquals(MESSAGE, exception.getMessage());
+        assertEquals(CAUSE, exception.getCause());
+
+        exception = new CarNotConnectedException(CAUSE);
+        assertEquals(CAUSE, exception.getCause());
+    }
+
+    public void testCarNotSupportedException() {
+        CarNotSupportedException exception = new CarNotSupportedException();
+        assertNull(exception.getMessage());
+        assertNull(exception.getCause());
+
+        exception = new CarNotSupportedException(MESSAGE);
+        assertEquals(MESSAGE, exception.getMessage());
+        assertNull(exception.getCause());
+
+        exception = new CarNotSupportedException(MESSAGE, CAUSE);
+        assertEquals(MESSAGE, exception.getMessage());
+        assertEquals(CAUSE, exception.getCause());
+
+        exception = new CarNotSupportedException(CAUSE);
+        assertEquals(CAUSE, exception.getCause());
+    }
+}
diff --git a/tests/tests/carrierapi/Android.mk b/tests/tests/carrierapi/Android.mk
new file mode 100644
index 0000000..890fcdd
--- /dev/null
+++ b/tests/tests/carrierapi/Android.mk
@@ -0,0 +1,36 @@
+# 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.
+
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+# don't include this package in any target
+LOCAL_MODULE_TAGS := optional
+# and when built explicitly put it in the data partition
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+
+LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner ctsdeviceutil
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_PACKAGE_NAME := CtsCarrierApiTestCases
+
+# Tag this module as a cts_v2 test artifact
+LOCAL_COMPATIBILITY_SUITE := cts_v2
+
+LOCAL_JAVA_LIBRARIES += android.test.runner telephony-common
+
+include $(BUILD_CTS_PACKAGE)
diff --git a/tests/tests/carrierapi/AndroidManifest.xml b/tests/tests/carrierapi/AndroidManifest.xml
new file mode 100644
index 0000000..0338d99
--- /dev/null
+++ b/tests/tests/carrierapi/AndroidManifest.xml
@@ -0,0 +1,35 @@
+<?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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="android.carrierapi.cts">
+
+    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
+    <uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
+    <uses-permission android:name="android.permission.READ_PHONE_STATE" />
+
+    <application>
+        <uses-library android:name="android.test.runner" />
+    </application>
+
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+                     android:targetPackage="android.carrierapi.cts">
+        <meta-data android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
+
+</manifest>
+
diff --git a/tests/tests/carrierapi/AndroidTest.xml b/tests/tests/carrierapi/AndroidTest.xml
new file mode 100644
index 0000000..2091d97
--- /dev/null
+++ b/tests/tests/carrierapi/AndroidTest.xml
@@ -0,0 +1,27 @@
+<?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.
+-->
+<configuration description="Config for CTS Carrier APIs test cases">
+    <target_preparer class="com.android.compatibility.common.tradefed.targetprep.TokenRequirement">
+        <option name="token" value="sim-card" />
+    </target_preparer>
+    <target_preparer class="com.android.compatibility.common.tradefed.targetprep.ApkInstaller">
+        <option name="cleanup-apks" value="true" />
+        <option name="test-file-name" value="CtsCarrierApiTestCases.apk" />
+    </target_preparer>
+    <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+        <option name="package" value="android.carrierapi.cts" />
+    </test>
+</configuration>
diff --git a/tests/tests/carrierapi/src/android/carrierapi/cts/CarrierApiTest.java b/tests/tests/carrierapi/src/android/carrierapi/cts/CarrierApiTest.java
new file mode 100644
index 0000000..be6a759
--- /dev/null
+++ b/tests/tests/carrierapi/src/android/carrierapi/cts/CarrierApiTest.java
@@ -0,0 +1,110 @@
+/*
+ * 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.carrierapi.cts;
+
+import android.content.Context;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.Signature;
+import android.net.ConnectivityManager;
+import android.os.PersistableBundle;
+import android.telephony.CarrierConfigManager;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
+import android.test.AndroidTestCase;
+import android.util.Log;
+
+import com.android.internal.telephony.uicc.IccUtils;
+
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+
+public class CarrierApiTest extends AndroidTestCase {
+    private static final String TAG = "CarrierApiTest";
+    private TelephonyManager mTelephonyManager;
+    private PackageManager mPackageManager;
+    private boolean hasCellular;
+    private String selfPackageName;
+    private String selfCertHash;
+
+    private static final String FiDevCert = "24EB92CBB156B280FA4E1429A6ECEEB6E5C1BFE4";
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mTelephonyManager = (TelephonyManager)
+                getContext().getSystemService(Context.TELEPHONY_SERVICE);
+        mPackageManager = getContext().getPackageManager();
+        selfPackageName = getContext().getPackageName();
+        selfCertHash = getCertHash(selfPackageName);
+        hasCellular = hasCellular();
+        if (!hasCellular) {
+            Log.e(TAG, "No cellular support, all tests will be skipped.");
+        }
+    }
+
+    /**
+     * Checks whether the cellular stack should be running on this device.
+     */
+    private boolean hasCellular() {
+        ConnectivityManager mgr =
+                (ConnectivityManager) getContext().getSystemService(Context.CONNECTIVITY_SERVICE);
+        return mgr.isNetworkSupported(ConnectivityManager.TYPE_MOBILE);
+    }
+
+    private boolean isSimCardPresent() {
+        return mTelephonyManager.getPhoneType() != TelephonyManager.PHONE_TYPE_NONE &&
+                mTelephonyManager.getSimState() != TelephonyManager.SIM_STATE_ABSENT;
+    }
+
+    private String getCertHash(String pkgName) {
+        try {
+            PackageInfo pInfo = mPackageManager.getPackageInfo(pkgName,
+                    PackageManager.GET_SIGNATURES | PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS);
+            MessageDigest md = MessageDigest.getInstance("SHA-1");
+            return IccUtils.bytesToHexString(md.digest(pInfo.signatures[0].toByteArray()));
+        } catch (PackageManager.NameNotFoundException ex) {
+            Log.e(TAG, pkgName + " not found", ex);
+        } catch (NoSuchAlgorithmException ex) {
+            Log.e(TAG, "Algorithm SHA1 is not found.");
+        }
+        return "";
+    }
+
+    private void failMessage() {
+        if (FiDevCert.equalsIgnoreCase(selfCertHash)) {
+            fail("This test requires a Project Fi SIM card.");
+        } else {
+            fail("This test requires a SIM card with carrier privilege rule on it.\n" +
+                 "Cert hash: " + selfCertHash + "\n" +
+                 "Visit https://source.android.com/devices/tech/config/uicc.html");
+        }
+    }
+
+    public void testSimCardPresent() {
+        if (!hasCellular) return;
+        assertTrue("This test requires SIM card.", isSimCardPresent());
+    }
+
+    public void testHasCarrierPrivileges() {
+        if (!hasCellular) return;
+        if (!mTelephonyManager.hasCarrierPrivileges()) {
+            failMessage();
+        }
+    }
+
+}
diff --git a/tests/tests/content/Android.mk b/tests/tests/content/Android.mk
index 1c8fbfc..924c639 100644
--- a/tests/tests/content/Android.mk
+++ b/tests/tests/content/Android.mk
@@ -25,13 +25,10 @@
 
 LOCAL_STATIC_JAVA_LIBRARIES := android-support-v4 ctsdeviceutil ctstestrunner
 
-# Resource unit tests use various locales (including a private locale) and some densities
-LOCAL_AAPT_FLAGS = -c small -c normal -c large -c xlarge \
+# Resource unit tests use a private locale and some densities
+LOCAL_AAPT_FLAGS = -c xx_YY -c cs -c small -c normal -c large -c xlarge \
         -c 320dpi -c 240dpi -c 160dpi -c 32dpi \
-        -c cs,fa_IR \
-        -c kok,kok_IN,kok_419,kok_419_VARIANT \
-        -c kok_Knda_419,kok_Knda_419_VARIANT,kok_VARIANT,kok_Knda \
-        -c tgl,tgl_PH,xx_YY
+        -c kok,kok_IN,kok_419,kok_419_VARIANT,kok_Knda_419,kok_Knda_419_VARIANT,kok_VARIANT,kok_Knda,tgl,tgl_PH
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
diff --git a/tests/tests/content/src/android/content/cts/ContextTest.java b/tests/tests/content/src/android/content/cts/ContextTest.java
index cf27bda..c988f77 100644
--- a/tests/tests/content/src/android/content/cts/ContextTest.java
+++ b/tests/tests/content/src/android/content/cts/ContextTest.java
@@ -27,11 +27,14 @@
 import android.content.res.Resources.Theme;
 import android.content.res.TypedArray;
 import android.content.res.XmlResourceParser;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
 import android.test.AndroidTestCase;
 import android.util.AttributeSet;
 import android.util.Xml;
 import android.view.WindowManager;
 
+import java.io.File;
 import java.io.IOException;
 
 public class ContextTest extends AndroidTestCase {
@@ -81,6 +84,108 @@
         }
     }
 
+    /**
+     * Ensure that default and device encrypted storage areas are stored
+     * separately on disk. All devices must support these storage areas, even if
+     * they don't have file-based encryption, so that apps can go through a
+     * backup/restore cycle between FBE and non-FBE devices.
+     */
+    public void testCreateDeviceEncryptedStorageContext() throws Exception {
+        final Context deviceContext = mContext.createDeviceEncryptedStorageContext();
+
+        assertFalse(mContext.isDeviceEncryptedStorage());
+        assertTrue(deviceContext.isDeviceEncryptedStorage());
+
+        final File defaultFile = new File(mContext.getFilesDir(), "test");
+        final File deviceFile = new File(deviceContext.getFilesDir(), "test");
+
+        assertFalse(deviceFile.equals(defaultFile));
+
+        deviceFile.createNewFile();
+
+        // Make sure storage areas are mutually exclusive
+        assertFalse(defaultFile.exists());
+        assertTrue(deviceFile.exists());
+    }
+
+    public void testMigrateSharedPreferencesFrom() throws Exception {
+        final Context deviceContext = mContext.createDeviceEncryptedStorageContext();
+
+        mContext.getSharedPreferences("test", Context.MODE_PRIVATE).edit().putInt("answer", 42)
+                .commit();
+
+        // Verify that we can migrate
+        assertTrue(deviceContext.migrateSharedPreferencesFrom(mContext, "test"));
+        assertEquals(0, mContext.getSharedPreferences("test", Context.MODE_PRIVATE)
+                .getInt("answer", 0));
+        assertEquals(42, deviceContext.getSharedPreferences("test", Context.MODE_PRIVATE)
+                .getInt("answer", 0));
+
+        // Trying to migrate again when already done is a no-op
+        assertTrue(deviceContext.migrateSharedPreferencesFrom(mContext, "test"));
+        assertEquals(0, mContext.getSharedPreferences("test", Context.MODE_PRIVATE)
+                .getInt("answer", 0));
+        assertEquals(42, deviceContext.getSharedPreferences("test", Context.MODE_PRIVATE)
+                .getInt("answer", 0));
+
+        // Add a new value and verify that we can migrate back
+        deviceContext.getSharedPreferences("test", Context.MODE_PRIVATE).edit()
+                .putInt("question", 24).commit();
+
+        assertTrue(mContext.migrateSharedPreferencesFrom(deviceContext, "test"));
+        assertEquals(42, mContext.getSharedPreferences("test", Context.MODE_PRIVATE)
+                .getInt("answer", 0));
+        assertEquals(24, mContext.getSharedPreferences("test", Context.MODE_PRIVATE)
+                .getInt("question", 0));
+        assertEquals(0, deviceContext.getSharedPreferences("test", Context.MODE_PRIVATE)
+                .getInt("answer", 0));
+        assertEquals(0, deviceContext.getSharedPreferences("test", Context.MODE_PRIVATE)
+                .getInt("question", 0));
+    }
+
+    public void testMigrateDatabaseFrom() throws Exception {
+        final Context deviceContext = mContext.createDeviceEncryptedStorageContext();
+
+        SQLiteDatabase db = mContext.openOrCreateDatabase("test.db",
+                Context.MODE_PRIVATE | Context.MODE_ENABLE_WRITE_AHEAD_LOGGING, null);
+        db.execSQL("CREATE TABLE list(item TEXT);");
+        db.execSQL("INSERT INTO list VALUES ('cat')");
+        db.execSQL("INSERT INTO list VALUES ('dog')");
+        db.close();
+
+        // Verify that we can migrate
+        assertTrue(deviceContext.migrateDatabaseFrom(mContext, "test.db"));
+        db = deviceContext.openOrCreateDatabase("test.db",
+                Context.MODE_PRIVATE | Context.MODE_ENABLE_WRITE_AHEAD_LOGGING, null);
+        Cursor c = db.query("list", null, null, null, null, null, null);
+        assertEquals(2, c.getCount());
+        assertTrue(c.moveToFirst());
+        assertEquals("cat", c.getString(0));
+        assertTrue(c.moveToNext());
+        assertEquals("dog", c.getString(0));
+        c.close();
+        db.execSQL("INSERT INTO list VALUES ('mouse')");
+        db.close();
+
+        // Trying to migrate again when already done is a no-op
+        assertTrue(deviceContext.migrateDatabaseFrom(mContext, "test.db"));
+
+        // Verify that we can migrate back
+        assertTrue(mContext.migrateDatabaseFrom(deviceContext, "test.db"));
+        db = mContext.openOrCreateDatabase("test.db",
+                Context.MODE_PRIVATE | Context.MODE_ENABLE_WRITE_AHEAD_LOGGING, null);
+        c = db.query("list", null, null, null, null, null, null);
+        assertEquals(3, c.getCount());
+        assertTrue(c.moveToFirst());
+        assertEquals("cat", c.getString(0));
+        assertTrue(c.moveToNext());
+        assertEquals("dog", c.getString(0));
+        assertTrue(c.moveToNext());
+        assertEquals("mouse", c.getString(0));
+        c.close();
+        db.close();
+    }
+
     public void testAccessTheme() {
         mContext.setTheme(R.style.Test_Theme);
         final Theme testTheme = mContext.getTheme();
diff --git a/tests/tests/content/src/android/content/res/cts/ResourcesTest.java b/tests/tests/content/src/android/content/res/cts/ResourcesTest.java
index 1304b26..f264f1c 100644
--- a/tests/tests/content/src/android/content/res/cts/ResourcesTest.java
+++ b/tests/tests/content/src/android/content/res/cts/ResourcesTest.java
@@ -272,67 +272,6 @@
         assertEquals(LocaleList.getDefault(), res.getConfiguration().getLocales());
     }
 
-    public void testUpdateConfiguration_ResolvedLocaleIsRecalculated() {
-        final DisplayMetrics dm = new DisplayMetrics();
-        dm.setToDefaults();
-        final Configuration cfg = new Configuration();
-        cfg.setToDefaults();
-
-        // Avestan has no assets, but Czech does
-        cfg.setLocales(LocaleList.forLanguageTags("ae"));
-        Resources res = new Resources(mResources.getAssets(), dm, cfg);
-        cfg.setLocales(LocaleList.forLanguageTags("ae,cs"));
-        res.updateConfiguration(cfg, null);
-        assertEquals("cs", res.getResolvedLocale().toLanguageTag());
-    }
-
-    public void testGetResolvedLocale_unsupportedLocale() {
-        final DisplayMetrics dm = new DisplayMetrics();
-        dm.setToDefaults();
-        final Configuration cfg = new Configuration();
-        cfg.setToDefaults();
-        cfg.setLocales(LocaleList.forLanguageTags("ae"));  // Avestan has no assets
-
-        Resources res = new Resources(mResources.getAssets(), dm, cfg);
-        assertEquals("ae", res.getResolvedLocale().toLanguageTag());
-    }
-
-    public void testGetResolvedLocale_secondaryLocaleIsSupported() {
-        final DisplayMetrics dm = new DisplayMetrics();
-        dm.setToDefaults();
-        final Configuration cfg = new Configuration();
-        cfg.setToDefaults();
-        // Avestan has no assets, but Czech does
-        cfg.setLocales(LocaleList.forLanguageTags("ae,cs"));
-
-        Resources res = new Resources(mResources.getAssets(), dm, cfg);
-        assertEquals("cs", res.getResolvedLocale().toLanguageTag());
-    }
-
-    public void testGetResolvedLocale_secondaryLocaleIsPartiallySupported() {
-        final DisplayMetrics dm = new DisplayMetrics();
-        dm.setToDefaults();
-        final Configuration cfg = new Configuration();
-        cfg.setToDefaults();
-        // Avestan has no assets;
-        // Persian has assets for Iran, but not Afghanistan (partial match is accepted);
-        // Czech has assets (but we don't get to it)
-        cfg.setLocales(LocaleList.forLanguageTags("ae,fa-AF,cs"));
-
-        Resources res = new Resources(mResources.getAssets(), dm, cfg);
-        assertEquals("fa-AF", res.getResolvedLocale().toLanguageTag());
-    }
-
-    public void testGetResolvedLocale_SystemResourcesLocaleNonNull() {
-        Resources res = Resources.getSystem();
-        assertNotNull(res.getResolvedLocale());
-    }
-
-    public void testGetResolvedLocale_NonNull() {
-        Resources res = createNewResources();
-        assertNotNull(res.getResolvedLocale());
-    }
-
     public void testGetDimensionPixelSize() {
         try {
             mResources.getDimensionPixelSize(-1);
diff --git a/tests/tests/draganddrop/dragsource/res/layout/main_activity.xml b/tests/tests/draganddrop/dragsource/res/layout/main_activity.xml
index af79072..cac6a082 100644
--- a/tests/tests/draganddrop/dragsource/res/layout/main_activity.xml
+++ b/tests/tests/draganddrop/dragsource/res/layout/main_activity.xml
@@ -21,25 +21,91 @@
         android:orientation="vertical">
 
     <TextView
-            android:id="@+id/do_grant"
+            android:id="@+id/dont_grant"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
             android:gravity="center"
-            android:layout_margin="10dp"
-            android:padding="20dp"
+            android:layout_margin="@dimen/item_margin"
+            android:padding="@dimen/item_padding"
             android:background="#ddd"
-            android:text="@string/do_grant_label">
+            android:text="@string/dont_grant_label">
     </TextView>
 
     <TextView
-        android:id="@+id/dont_grant"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:gravity="center"
-        android:layout_margin="10dp"
-        android:padding="20dp"
-        android:background="#aaa"
-        android:text="@string/dont_grant_label">
+            android:id="@+id/grant_read"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:gravity="center"
+            android:layout_margin="@dimen/item_margin"
+            android:padding="@dimen/item_padding"
+            android:background="#ddd"
+            android:text="@string/grant_read_label">
+    </TextView>
+
+    <TextView
+            android:id="@+id/grant_write"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:gravity="center"
+            android:layout_margin="@dimen/item_margin"
+            android:padding="@dimen/item_padding"
+            android:background="#ddd"
+            android:text="@string/grant_write_label">
+    </TextView>
+
+    <TextView
+            android:id="@+id/grant_read_prefix"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:gravity="center"
+            android:layout_margin="@dimen/item_margin"
+            android:padding="@dimen/item_padding"
+            android:background="#ddd"
+            android:text="@string/grant_read_prefix_label">
+    </TextView>
+
+    <TextView
+            android:id="@+id/grant_read_noprefix"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:gravity="center"
+            android:layout_margin="@dimen/item_margin"
+            android:padding="@dimen/item_padding"
+            android:background="#ddd"
+            android:text="@string/grant_read_noprefix_label">
+    </TextView>
+
+    <TextView
+            android:id="@+id/grant_read_persistable"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:gravity="center"
+            android:layout_margin="@dimen/item_margin"
+            android:padding="@dimen/item_padding"
+            android:background="#ddd"
+            android:text="@string/grant_read_persistable_label">
+    </TextView>
+
+    <TextView
+            android:id="@+id/disallow_global"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:gravity="center"
+            android:layout_margin="@dimen/item_margin"
+            android:padding="@dimen/item_padding"
+            android:background="#aaa"
+            android:text="@string/disallow_global_label">
+    </TextView>
+
+    <TextView
+            android:id="@+id/cancel_soon"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:gravity="center"
+            android:layout_margin="@dimen/item_margin"
+            android:padding="@dimen/item_padding"
+            android:background="#aaa"
+            android:text="@string/cancel_soon_label">
     </TextView>
 
 </LinearLayout>
\ No newline at end of file
diff --git a/tests/tests/content/res/values-fa-rIR/strings.xml b/tests/tests/draganddrop/dragsource/res/values/dimen.xml
similarity index 79%
rename from tests/tests/content/res/values-fa-rIR/strings.xml
rename to tests/tests/draganddrop/dragsource/res/values/dimen.xml
index 25fd20c..4923a32 100644
--- a/tests/tests/content/res/values-fa-rIR/strings.xml
+++ b/tests/tests/draganddrop/dragsource/res/values/dimen.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2015 The Android Open Source Project
+<!-- 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.
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="am">صبح</string>
+<resources>
+    <dimen name="item_margin">6dp</dimen>
+    <dimen name="item_padding">6dp</dimen>
 </resources>
diff --git a/tests/tests/draganddrop/dragsource/res/values/strings.xml b/tests/tests/draganddrop/dragsource/res/values/strings.xml
index 1cb4f5d..28cd792 100644
--- a/tests/tests/draganddrop/dragsource/res/values/strings.xml
+++ b/tests/tests/draganddrop/dragsource/res/values/strings.xml
@@ -16,7 +16,13 @@
 
 <resources>
 
-     <string name="do_grant_label">Grant permissions</string>
      <string name="dont_grant_label">Do not grant permissions</string>
+     <string name="disallow_global_label">Disallow global drag</string>
+     <string name="cancel_soon_label">Cancel soon after start</string>
+     <string name="grant_read_label">Grant read permissions</string>
+     <string name="grant_write_label">Grant write permissions</string>
+     <string name="grant_read_prefix_label">Grant read+prefix permissions</string>
+     <string name="grant_read_noprefix_label">Grant read, no prefix permissions</string>
+     <string name="grant_read_persistable_label">Grant read+persistable</string>
 
 </resources>
diff --git a/tests/tests/draganddrop/dragsource/src/android/dnd/cts/dragsource/DragSource.java b/tests/tests/draganddrop/dragsource/src/android/dnd/cts/dragsource/DragSource.java
index e6356f6..bbba70d 100644
--- a/tests/tests/draganddrop/dragsource/src/android/dnd/cts/dragsource/DragSource.java
+++ b/tests/tests/draganddrop/dragsource/src/android/dnd/cts/dragsource/DragSource.java
@@ -18,15 +18,19 @@
 
 import android.app.Activity;
 import android.content.ClipData;
+import android.content.ClipDescription;
 import android.net.Uri;
 import android.os.Bundle;
+import android.os.Handler;
+import android.os.PersistableBundle;
 import android.view.View;
 
 public class DragSource extends Activity{
     private static final String URI_PREFIX =
-            "content://" + DragSourceContentProvider.AUTHORITY + "/data/";
+            "content://" + DragSourceContentProvider.AUTHORITY + "/data";
 
     private static final String MAGIC_VALUE = "42";
+    public static final long TIMEOUT_CANCEL = 150;
 
     @Override
     public void onCreate(Bundle savedInstanceState) {
@@ -35,24 +39,53 @@
         View view = getLayoutInflater().inflate(R.layout.main_activity, null);
         setContentView(view);
 
-        setUpDragSource(R.id.dont_grant, false);
-        setUpDragSource(R.id.do_grant, true);
+        final Uri plainUri = Uri.parse(URI_PREFIX + "/" + MAGIC_VALUE);
+
+        setUpDragSource(R.id.disallow_global, plainUri, 0);
+        setUpDragSource(R.id.cancel_soon, plainUri, View.DRAG_FLAG_GLOBAL);
+
+        setUpDragSource(R.id.dont_grant, plainUri, View.DRAG_FLAG_GLOBAL);
+        setUpDragSource(R.id.grant_read, plainUri,
+                View.DRAG_FLAG_GLOBAL | View.DRAG_FLAG_GLOBAL_URI_READ);
+        setUpDragSource(R.id.grant_write, plainUri,
+                View.DRAG_FLAG_GLOBAL | View.DRAG_FLAG_GLOBAL_URI_WRITE);
+        setUpDragSource(R.id.grant_read_persistable, plainUri,
+                View.DRAG_FLAG_GLOBAL | View.DRAG_FLAG_GLOBAL_URI_READ |
+                        View.DRAG_FLAG_GLOBAL_PERSISTABLE_URI_PERMISSION);
+
+        final Uri prefixUri = Uri.parse(URI_PREFIX);
+
+        setUpDragSource(R.id.grant_read_prefix, prefixUri,
+                View.DRAG_FLAG_GLOBAL | View.DRAG_FLAG_GLOBAL_URI_READ |
+                        View.DRAG_FLAG_GLOBAL_PREFIX_URI_PERMISSION);
+        setUpDragSource(R.id.grant_read_noprefix, prefixUri,
+                View.DRAG_FLAG_GLOBAL | View.DRAG_FLAG_GLOBAL_URI_READ);
+
     }
 
-    private void setUpDragSource(final int resourceId, final boolean grantPermissions) {
+    private void setUpDragSource(final int resourceId, final Uri uri, final int flags) {
         findViewById(resourceId).setOnLongClickListener(new View.OnLongClickListener() {
             @Override
-            public boolean onLongClick(View v) {
-                int flags = View.DRAG_FLAG_GLOBAL;
-                if (grantPermissions) {
-                    flags |= View.DRAG_FLAG_GLOBAL_URI_READ | View.DRAG_FLAG_GLOBAL_URI_WRITE;
-                }
-                final Uri uri = Uri.parse(URI_PREFIX + MAGIC_VALUE);
+            public boolean onLongClick(final View v) {
+                final ClipDescription clipDescription = new ClipDescription("", new String[] {
+                        ClipDescription.MIMETYPE_TEXT_URILIST });
+                PersistableBundle extras = new PersistableBundle(1);
+                extras.putString("extraKey", "extraValue");
+                clipDescription.setExtras(extras);
+                final ClipData clipData = new ClipData(clipDescription, new ClipData.Item(uri));
                 v.startDragAndDrop(
-                        ClipData.newUri(getContentResolver(), "", uri),
+                        clipData,
                         new View.DragShadowBuilder(v),
                         null,
                         flags);
+                if (resourceId == R.id.cancel_soon) {
+                    new Handler().postDelayed(new Runnable() {
+                        @Override
+                        public void run() {
+                            v.cancelDragAndDrop();
+                        }
+                    }, TIMEOUT_CANCEL);
+                }
                 return false;
             }
         });
diff --git a/tests/tests/draganddrop/droptarget/res/layout/main_activity.xml b/tests/tests/draganddrop/droptarget/res/layout/main_activity.xml
index f473c75..4a5a5b1 100644
--- a/tests/tests/draganddrop/droptarget/res/layout/main_activity.xml
+++ b/tests/tests/draganddrop/droptarget/res/layout/main_activity.xml
@@ -21,39 +21,89 @@
         android:orientation="vertical">
 
     <TextView
-            android:id="@+id/do_request"
+            android:id="@+id/dont_request"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
             android:gravity="center"
-            android:layout_margin="10dp"
-            android:padding="20dp"
-            android:background="#ccc"
-            android:text="@string/do_request_label">
+            android:layout_margin="@dimen/item_margin"
+            android:padding="@dimen/item_padding"
+            android:background="#ddd"
+            android:text="@string/dont_request_label">
     </TextView>
 
     <TextView
-        android:id="@+id/dont_request"
+        android:id="@+id/request_read"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:gravity="center"
-        android:layout_margin="10dp"
-        android:padding="20dp"
-        android:background="#aaa"
-        android:text="@string/dont_request_label">
+        android:layout_margin="@dimen/item_margin"
+        android:padding="@dimen/item_padding"
+        android:background="#ddd"
+        android:text="@string/request_read_label">
+    </TextView>
+
+    <TextView
+        android:id="@+id/request_write"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:gravity="center"
+        android:layout_margin="@dimen/item_margin"
+        android:padding="@dimen/item_padding"
+        android:background="#ddd"
+        android:text="@string/request_write_label">
+    </TextView>
+
+    <TextView
+        android:id="@+id/request_read_nested"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:gravity="center"
+        android:layout_margin="@dimen/item_margin"
+        android:padding="@dimen/item_padding"
+        android:background="#ddd"
+        android:text="@string/request_read_nested_label">
+    </TextView>
+
+    <TextView
+        android:id="@+id/request_take_persistable"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:gravity="center"
+        android:layout_margin="@dimen/item_margin"
+        android:padding="@dimen/item_padding"
+        android:background="#ddd"
+        android:text="@string/request_take_persistable_label">
     </TextView>
 
     <TextView
             android:id="@+id/result"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
-            android:layout_margin="10dp">
+            android:layout_marginLeft="10dp"
+            android:text="@string/not_available_label">
+    </TextView>
+
+    <TextView
+            android:id="@+id/drag_started"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginLeft="10dp"
+            android:text="@string/not_available_label">
+    </TextView>
+
+    <TextView
+            android:id="@+id/extra_value"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginLeft="10dp"
+            android:text="@string/not_available_label">
     </TextView>
 
     <TextView
             android:id="@+id/details"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
-            android:layout_margin="10dp">
+            android:layout_marginLeft="10dp">
     </TextView>
 
 </LinearLayout>
\ No newline at end of file
diff --git a/tests/tests/content/res/values-fa-rIR/strings.xml b/tests/tests/draganddrop/droptarget/res/values/dimen.xml
similarity index 79%
copy from tests/tests/content/res/values-fa-rIR/strings.xml
copy to tests/tests/draganddrop/droptarget/res/values/dimen.xml
index 25fd20c..4923a32 100644
--- a/tests/tests/content/res/values-fa-rIR/strings.xml
+++ b/tests/tests/draganddrop/droptarget/res/values/dimen.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2015 The Android Open Source Project
+<!-- 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.
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="am">صبح</string>
+<resources>
+    <dimen name="item_margin">6dp</dimen>
+    <dimen name="item_padding">6dp</dimen>
 </resources>
diff --git a/tests/tests/draganddrop/droptarget/res/values/strings.xml b/tests/tests/draganddrop/droptarget/res/values/strings.xml
index ba30235..9fa4619 100644
--- a/tests/tests/draganddrop/droptarget/res/values/strings.xml
+++ b/tests/tests/draganddrop/droptarget/res/values/strings.xml
@@ -16,7 +16,11 @@
 
 <resources>
 
-     <string name="do_request_label">Request permissions</string>
-     <string name="dont_request_label">Do not request permissions</string>
+     <string name="dont_request_label">Do not request permissions, read</string>
+     <string name="request_read_label">Request permissions, read</string>
+     <string name="request_write_label">Request permissions, write</string>
+     <string name="request_read_nested_label">Request permissions, read nested</string>
+     <string name="request_take_persistable_label">Request permissions, take persistable</string>
+     <string name="not_available_label">N/A</string>
 
 </resources>
diff --git a/tests/tests/draganddrop/droptarget/src/android/dnd/cts/droptarget/DropTarget.java b/tests/tests/draganddrop/droptarget/src/android/dnd/cts/droptarget/DropTarget.java
index 0740619..2be57d4 100644
--- a/tests/tests/draganddrop/droptarget/src/android/dnd/cts/droptarget/DropTarget.java
+++ b/tests/tests/draganddrop/droptarget/src/android/dnd/cts/droptarget/DropTarget.java
@@ -18,9 +18,11 @@
 
 import android.app.Activity;
 import android.content.ClipData;
+import android.content.ContentValues;
 import android.database.Cursor;
 import android.net.Uri;
 import android.os.Bundle;
+import android.os.PersistableBundle;
 import android.view.DragEvent;
 import android.view.DropPermissions;
 import android.view.View;
@@ -29,6 +31,7 @@
 public class DropTarget extends Activity {
 
     private static final String MAGIC_VALUE = "42";
+    public static final String RESULT_OK = "OK";
 
     @Override
     public void onCreate(Bundle savedInstanceState) {
@@ -37,93 +40,189 @@
         View view = getLayoutInflater().inflate(R.layout.main_activity, null);
         setContentView(view);
 
-        setUpDropTarget(R.id.dont_request, false);
-        setUpDropTarget(R.id.do_request, true);
+        setUpDropTarget(R.id.dont_request, new OnDragUriReadListener(false));
+        setUpDropTarget(R.id.request_read, new OnDragUriReadListener());
+        setUpDropTarget(R.id.request_write, new OnDragUriWriteListener());
+        setUpDropTarget(R.id.request_read_nested, new OnDragUriReadPrefixListener());
+        setUpDropTarget(R.id.request_take_persistable, new OnDragUriTakePersistableListener());
     }
 
-    private void setUpDropTarget(final int targetResourceId,
-                                 final boolean requestPermissions) {
-        findViewById(targetResourceId).setOnDragListener(new View.OnDragListener() {
-            @Override
-            public boolean onDrag(View v, DragEvent event) {
-                switch (event.getAction()) {
-                    case DragEvent.ACTION_DRAG_STARTED:
-                        return true;
+    private void setUpDropTarget(final int targetResourceId, OnDragUriListener listener) {
+        findViewById(targetResourceId).setOnDragListener(listener);
+    }
 
-                    case DragEvent.ACTION_DRAG_ENTERED:
-                        return true;
+    private String checkExtraValue(DragEvent event) {
+        PersistableBundle extras = event.getClipDescription().getExtras();
+        if (extras == null) {
+            return "Null";
+        }
 
-                    case DragEvent.ACTION_DRAG_LOCATION:
-                        return true;
+        final String value = extras.getString("extraKey");
+        if ("extraValue".equals(value)) {
+            return RESULT_OK;
+        }
+        return value;
+    }
 
-                    case DragEvent.ACTION_DRAG_EXITED:
-                        return true;
+    private abstract class OnDragUriListener implements View.OnDragListener {
+        private final boolean requestPermissions;
 
-                    case DragEvent.ACTION_DROP:
-                        String result;
-                        try {
-                            result = processDrop(event, requestPermissions);
-                        } catch (Exception e) {
-                            result = "Exception";
-                            ((TextView) findViewById(R.id.details)).setText(e.getMessage());
-                        }
-                        ((TextView) findViewById(R.id.result)).setText(result);
-                        return true;
+        public OnDragUriListener(boolean requestPermissions) {
+            this.requestPermissions = requestPermissions;
+        }
 
-                    case DragEvent.ACTION_DRAG_ENDED:
-                        return true;
+        @Override
+        public boolean onDrag(View v, DragEvent event) {
+            switch (event.getAction()) {
+                case DragEvent.ACTION_DRAG_STARTED:
+                    ((TextView) findViewById(R.id.drag_started)).setText("DRAG_STARTED");
+                    ((TextView) findViewById(R.id.extra_value)).setText(checkExtraValue(event));
+                    return true;
 
-                    default:
-                        return false;
+                case DragEvent.ACTION_DRAG_ENTERED:
+                    return true;
+
+                case DragEvent.ACTION_DRAG_LOCATION:
+                    return true;
+
+                case DragEvent.ACTION_DRAG_EXITED:
+                    return true;
+
+                case DragEvent.ACTION_DROP:
+                    String result;
+                    try {
+                        result = processDrop(event, requestPermissions);
+                    } catch (Exception e) {
+                        result = "Exception";
+                        ((TextView) findViewById(R.id.details)).setText(e.getMessage());
+                    }
+                    ((TextView) findViewById(R.id.result)).setText(result);
+                    return true;
+
+                case DragEvent.ACTION_DRAG_ENDED:
+                    return true;
+
+                default:
+                    return false;
+            }
+        }
+
+        private String processDrop(DragEvent event, boolean requestPermissions) {
+            final ClipData clipData = event.getClipData();
+            if (clipData == null) {
+                return "Null ClipData";
+            }
+            if (clipData.getItemCount() == 0) {
+                return "Empty ClipData";
+            }
+            ClipData.Item item = clipData.getItemAt(0);
+            if (item == null) {
+                return "Null ClipData.Item";
+            }
+            Uri uri = item.getUri();
+            if (uri == null) {
+                return "Null Uri";
+            }
+
+            DropPermissions dp = null;
+            if (requestPermissions) {
+                dp = requestDropPermissions(event);
+                if (dp == null) {
+                    return "Null DropPermissions";
                 }
             }
-        });
+
+            try {
+                return processUri(uri);
+            } finally {
+                if (dp != null) {
+                    dp.release();
+                }
+            }
+        }
+
+        abstract protected String processUri(Uri uri);
     }
 
-    private String processDrop(DragEvent event, boolean requestPermissions) {
-        final ClipData clipData = event.getClipData();
-        if (clipData == null) {
-           return "Null ClipData";
-        }
-        if (clipData.getItemCount() == 0) {
-           return "Empty ClipData";
-        }
-        ClipData.Item item = clipData.getItemAt(0);
-        if (item == null) {
-            return "Null ClipData.Item";
-        }
-        Uri uri = item.getUri();
-        if (uri == null) {
-            return "Null Uri";
+    private class OnDragUriReadListener extends OnDragUriListener {
+        public OnDragUriReadListener(boolean requestPermissions) {
+            super(requestPermissions);
         }
 
-        DropPermissions dp = null;
-        if (requestPermissions) {
-            dp = requestDropPermissions(event);
-            if (dp == null) {
-                return "Null DropPermissions";
-            }
+        public OnDragUriReadListener() {
+            super(true);
         }
 
-        Cursor cursor = null;
-        try {
-            cursor = getContentResolver().query(uri, null, null, null, null);
-            if (cursor == null) {
-                return "Null Cursor";
+        protected String processUri(Uri uri) {
+            return checkQueryResult(uri, MAGIC_VALUE);
+        }
+
+        protected String checkQueryResult(Uri uri, String expectedValue) {
+            Cursor cursor = null;
+            try {
+                cursor = getContentResolver().query(uri, null, null, null, null);
+                if (cursor == null) {
+                    return "Null Cursor";
+                }
+                cursor.moveToPosition(0);
+                String value = cursor.getString(0);
+                if (!expectedValue.equals(value)) {
+                    return "Wrong value: " + value;
+                }
+                return RESULT_OK;
+            } finally {
+                if (cursor != null) {
+                    cursor.close();
+                }
             }
-            cursor.moveToPosition(0);
-            String value = cursor.getString(0);
-            if (!MAGIC_VALUE.equals(value)) {
-                return "Wrong value: " + value;
+        }
+    }
+
+    private class OnDragUriWriteListener extends OnDragUriListener {
+        public OnDragUriWriteListener() {
+            super(true);
+        }
+
+        protected String processUri(Uri uri) {
+            ContentValues values = new ContentValues();
+            values.put("key", 100);
+            getContentResolver().update(uri, values, null, null);
+            return RESULT_OK;
+        }
+    }
+
+    private class OnDragUriReadPrefixListener extends OnDragUriReadListener {
+        @Override
+        protected String processUri(Uri uri) {
+            final String result1 = queryPrefixed(uri, "1");
+            if (!result1.equals(RESULT_OK)) {
+                return result1;
             }
-            return "OK";
-        } finally {
-            if (cursor != null) {
-                cursor.close();
+            final String result2 = queryPrefixed(uri, "2");
+            if (!result2.equals(RESULT_OK)) {
+                return result2;
             }
-            if (dp != null) {
-                dp.release();
-            }
+            return queryPrefixed(uri, "3");
+        }
+
+        private String queryPrefixed(Uri uri, String selector) {
+            final Uri prefixedUri = Uri.parse(uri.toString() + "/" + selector);
+            return checkQueryResult(prefixedUri, selector);
+        }
+    }
+
+    private class OnDragUriTakePersistableListener extends OnDragUriListener {
+        public OnDragUriTakePersistableListener() {
+            super(true);
+        }
+
+        @Override
+        protected String processUri(Uri uri) {
+            getContentResolver().takePersistableUriPermission(
+                    uri, View.DRAG_FLAG_GLOBAL_URI_READ);
+            getContentResolver().releasePersistableUriPermission(
+                    uri, View.DRAG_FLAG_GLOBAL_URI_READ);
+            return RESULT_OK;
         }
     }
 }
diff --git a/tests/tests/draganddrop/src/android/dnd/cts/DragAndDropTest.java b/tests/tests/draganddrop/src/android/dnd/cts/DragAndDropTest.java
index 9df4d17..78b85f5 100644
--- a/tests/tests/draganddrop/src/android/dnd/cts/DragAndDropTest.java
+++ b/tests/tests/draganddrop/src/android/dnd/cts/DragAndDropTest.java
@@ -82,7 +82,7 @@
     }
 
     private void drag(Point srcPosition, Point tgtPosition) {
-        mDevice.drag(srcPosition.x, srcPosition.y, tgtPosition.x, tgtPosition.y, 50);
+        mDevice.drag(srcPosition.x, srcPosition.y, tgtPosition.x, tgtPosition.y, 100);
     }
 
     private void doCrossAppDrag(String sourceViewId, String targetViewId, String expectedResult) {
@@ -102,22 +102,83 @@
         mDevice.click(tgtPosition.x, tgtPosition.y);
 
         UiObject2 result = findObject(DROP_TARGET_PKG, "result");
+        assertNotNull("Result widget not found", result);
         assertEquals(expectedResult, result.getText());
     }
 
+    private void assertDragStarted(String expectedResult) {
+        UiObject2 drag_started = findObject(DROP_TARGET_PKG, "drag_started");
+        assertEquals(expectedResult, drag_started.getText());
+    }
+
+    private void assertExtraValue(String expectedResult) {
+        UiObject2 extra_value = findObject(DROP_TARGET_PKG, "extra_value");
+        assertEquals(expectedResult, extra_value.getText());
+    }
+
+    public void testLocal() {
+        doCrossAppDrag("disallow_global", "dont_request", "N/A");
+        assertDragStarted("N/A");
+        assertExtraValue("N/A");
+    }
+
+    public void testCancel() {
+        doCrossAppDrag("cancel_soon", "dont_request", "N/A");
+        assertDragStarted("DRAG_STARTED");
+        assertExtraValue("OK");
+    }
+
     public void testDontGrantDontRequest() {
         doCrossAppDrag("dont_grant", "dont_request", "Exception");
+        assertDragStarted("DRAG_STARTED");
+        assertExtraValue("OK");
     }
 
-    public void testDoGrantDontRequest() {
-        doCrossAppDrag("do_grant", "dont_request", "Exception");
+    public void testDontGrantRequestRead() {
+        doCrossAppDrag("dont_grant", "request_read", "Null DropPermissions");
     }
 
-    public void testDontGrantDoRequest() {
-        doCrossAppDrag("dont_grant", "do_request", "Null DropPermissions");
+    public void testDontGrantRequestWrite() {
+        doCrossAppDrag("dont_grant", "request_write", "Null DropPermissions");
     }
 
-    public void testDoGrantDoRequest() {
-        doCrossAppDrag("do_grant", "do_request", "OK");
+    public void testGrantReadDontRequest() {
+        doCrossAppDrag("grant_read", "dont_request", "Exception");
+    }
+
+    public void testGrantReadRequestRead() {
+        doCrossAppDrag("grant_read", "request_read", "OK");
+    }
+
+    public void testGrantReadRequestWrite() {
+        doCrossAppDrag("grant_read", "request_write", "Exception");
+    }
+
+    public void testGrantWriteDontRequest() {
+        doCrossAppDrag("grant_write", "dont_request", "Exception");
+    }
+
+    public void testGrantWriteRequestRead() {
+        doCrossAppDrag("grant_write", "request_write", "OK");
+    }
+
+    public void testGrantWriteRequestWrite() {
+        doCrossAppDrag("grant_write", "request_write", "OK");
+    }
+
+    public void testGrantReadPrefixRequestReadNested() {
+        doCrossAppDrag("grant_read_prefix", "request_read_nested", "OK");
+    }
+
+    public void testGrantReadNoPrefixRequestReadNested() {
+        doCrossAppDrag("grant_read_noprefix", "request_read_nested", "Exception");
+    }
+
+    public void testGrantPersistableRequestTakePersistable() {
+        doCrossAppDrag("grant_read_persistable", "request_take_persistable", "OK");
+    }
+
+    public void testGrantReadRequestTakePersistable() {
+        doCrossAppDrag("grant_read", "request_take_persistable", "Exception");
     }
 }
\ No newline at end of file
diff --git a/tests/tests/externalservice/Android.mk b/tests/tests/externalservice/Android.mk
new file mode 100644
index 0000000..4fa4666
--- /dev/null
+++ b/tests/tests/externalservice/Android.mk
@@ -0,0 +1,42 @@
+# 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.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+# Don't include this package in any target
+LOCAL_MODULE_TAGS := tests
+# When built, explicitly put it in the data partition.
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+
+LOCAL_DEX_PREOPT := false
+
+LOCAL_PROGUARD_ENABLED := disabled
+
+LOCAL_STATIC_JAVA_LIBRARIES := CtsExternalServiceCommon ctsdeviceutil ctstestrunner
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+# Tag this module as a cts_v2 test artifact
+LOCAL_COMPATIBILITY_SUITE := cts_v2
+
+LOCAL_CTS_MODULE_CONFIG := $(LOCAL_PATH)/Old$(CTS_MODULE_TEST_CONFIG)
+
+LOCAL_PACKAGE_NAME := CtsExternalServiceTestCases
+
+LOCAL_SDK_VERSION := system_current
+
+include $(BUILD_CTS_PACKAGE)
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/tests/tests/externalservice/AndroidManifest.xml b/tests/tests/externalservice/AndroidManifest.xml
new file mode 100644
index 0000000..0c5ea5d
--- /dev/null
+++ b/tests/tests/externalservice/AndroidManifest.xml
@@ -0,0 +1,31 @@
+<?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.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="android.externalservice.cts">
+
+    <application>
+        <uses-library android:name="android.test.runner" />
+    </application>
+
+    <instrumentation
+        android:name="android.support.test.runner.AndroidJUnitRunner"
+        android:label="CTS external service tests"
+        android:targetPackage="android.externalservice.cts" >
+    </instrumentation>
+</manifest>
+
diff --git a/tests/tests/externalservice/AndroidTest.xml b/tests/tests/externalservice/AndroidTest.xml
new file mode 100644
index 0000000..b07f248
--- /dev/null
+++ b/tests/tests/externalservice/AndroidTest.xml
@@ -0,0 +1,25 @@
+<?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.
+-->
+<configuration description="Config for CTS External Service test cases">
+    <target_preparer class="com.android.compatibility.common.tradefed.targetprep.ApkInstaller">
+        <option name="cleanup-apks" value="true" />
+        <option name="test-file-name" value="CtsExternalServiceService.apk" />
+        <option name="test-file-name" value="CtsExternalServiceTestCases.apk" />
+    </target_preparer>
+    <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+        <option name="package" value="android.externalservice.cts" />
+    </test>
+</configuration>
diff --git a/tests/tests/content/res/values-fa-rIR/strings.xml b/tests/tests/externalservice/OldAndroidTest.xml
similarity index 61%
copy from tests/tests/content/res/values-fa-rIR/strings.xml
copy to tests/tests/externalservice/OldAndroidTest.xml
index 25fd20c..838db60 100644
--- a/tests/tests/content/res/values-fa-rIR/strings.xml
+++ b/tests/tests/externalservice/OldAndroidTest.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2015 The Android Open Source Project
+<!-- 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.
@@ -13,7 +13,8 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string name="am">صبح</string>
-</resources>
+<configuration description="Config for CTS External Service test cases">
+    <include name="common-config" />
+    <option name="cts-apk-installer:test-file-name" value="CtsExternalServiceService.apk" />
+    <option name="cts-apk-installer:test-file-name" value="CtsExternalServiceTestCases.apk" />
+</configuration>
diff --git a/tests/tests/externalservice/common/Android.mk b/tests/tests/externalservice/common/Android.mk
new file mode 100644
index 0000000..1ebf149
--- /dev/null
+++ b/tests/tests/externalservice/common/Android.mk
@@ -0,0 +1,30 @@
+# 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.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+# don't include this package in any target
+LOCAL_MODULE_TAGS := optional
+# and when built explicitly put it in the data partition
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_MODULE := CtsExternalServiceCommon
+
+LOCAL_SDK_VERSION := current
+
+include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/tests/tests/externalservice/common/src/android/externalservice/common/ServiceMessages.java b/tests/tests/externalservice/common/src/android/externalservice/common/ServiceMessages.java
new file mode 100644
index 0000000..8d23bc0
--- /dev/null
+++ b/tests/tests/externalservice/common/src/android/externalservice/common/ServiceMessages.java
@@ -0,0 +1,37 @@
+/*
+ * 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.externalservice.common;
+
+import android.os.IBinder;
+
+public class ServiceMessages {
+    // No arguments. Gets the UID and PID of the service.
+    public static final int MSG_IDENTIFY = IBinder.FIRST_CALL_TRANSACTION + 1;
+    // Response to MSG_IDENTIFY. arg1 is the UID, arg2 is the PID.
+    public static final int MSG_IDENTIFY_RESPONSE = MSG_IDENTIFY + 100;
+
+    // Bundle key in MSG_IDENTIFY_RESPONSE containing the package name.
+    public static final String IDENTIFY_PACKAGE = "packageName";
+
+    // No arguments. Starts an external service.
+    public static final int MSG_CREATE_EXTERNAL_SERVICE = IBinder.FIRST_CALL_TRANSACTION + 2;
+    // Responds to MSG_CREATE_EXTERNAL_SERVICE. obj is the IBinder on success, null on failure.
+    public static final int MSG_CREATE_EXTERNAL_SERVICE_RESPONSE =
+        MSG_CREATE_EXTERNAL_SERVICE + 100;
+
+    private ServiceMessages() {}
+}
diff --git a/tests/tests/externalservice/service/Android.mk b/tests/tests/externalservice/service/Android.mk
new file mode 100644
index 0000000..ca19202
--- /dev/null
+++ b/tests/tests/externalservice/service/Android.mk
@@ -0,0 +1,35 @@
+# 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.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+# don't include this package in any target
+LOCAL_MODULE_TAGS := optional
+# and when built explicitly put it in the data partition
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+
+LOCAL_STATIC_JAVA_LIBRARIES := CtsExternalServiceCommon ctstestrunner ctsdeviceutil
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_PACKAGE_NAME := CtsExternalServiceService
+
+# Tag this module as a cts_v2 test artifact
+LOCAL_COMPATIBILITY_SUITE := cts_v2
+
+LOCAL_SDK_VERSION := system_current
+
+include $(BUILD_CTS_SUPPORT_PACKAGE)
diff --git a/tests/tests/externalservice/service/AndroidManifest.xml b/tests/tests/externalservice/service/AndroidManifest.xml
new file mode 100644
index 0000000..06fa80e
--- /dev/null
+++ b/tests/tests/externalservice/service/AndroidManifest.xml
@@ -0,0 +1,53 @@
+<?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.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="android.externalservice.service">
+
+    <application android:label="External Service Host">
+        <!-- Service used to start .ExternalService from this package. -->
+        <service android:name=".ServiceCreator"
+                 android:isolatedProcess="false"
+                 android:externalService="false"
+                 android:exported="true" />
+
+        <!-- These services only respond to MSG_IDENTIFY for testing bindService(). -->
+        <service android:name=".IsolatedService"
+                 android:isolatedProcess="true"
+                 android:externalService="false"
+                 android:exported="false"/>
+        <service android:name=".ExportedService"
+                 android:isolatedProcess="true"
+                 android:externalService="false"
+                 android:exported="true"/>
+        <service android:name=".ExternalNonExportedService"
+                 android:isolatedProcess="true"
+                 android:externalService="true"
+                 android:exported="false"/>
+        <service android:name=".ExternalNonIsolatedService"
+                 android:isolatedProcess="false"
+                 android:externalService="true"
+                 android:exported="true"/>
+
+        <!-- The only valid, externalService entry. -->
+        <service android:name=".ExternalService"
+                 android:isolatedProcess="true"
+                 android:externalService="true"
+                 android:exported="true"/>
+    </application>
+
+</manifest>
diff --git a/tests/tests/externalservice/service/src/android/externalservice/service/BaseService.java b/tests/tests/externalservice/service/src/android/externalservice/service/BaseService.java
new file mode 100644
index 0000000..a14adbe
--- /dev/null
+++ b/tests/tests/externalservice/service/src/android/externalservice/service/BaseService.java
@@ -0,0 +1,70 @@
+/*
+ * 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.externalservice.service;
+
+import android.app.Service;
+import android.content.Context;
+import android.content.Intent;
+import android.os.IBinder;
+import android.os.Handler;
+import android.os.Message;
+import android.os.Messenger;
+import android.os.Process;
+import android.os.RemoteException;
+import android.util.Log;
+
+import android.externalservice.common.ServiceMessages;
+
+public class BaseService extends Service {
+    private static final String TAG = "ExternalServiceTest.Service";
+
+    private final Handler mHandler = new BaseHandler(this);
+    private final Messenger mMessenger = new Messenger(mHandler);
+
+    @Override
+    public IBinder onBind(Intent intent) {
+        Log.d(TAG, "onBind " + intent);
+        return mMessenger.getBinder();
+    }
+
+    static class BaseHandler extends Handler {
+        private Context mContext;
+
+        public BaseHandler(Context context) {
+            mContext = context;
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            Log.d(TAG, "Received message: " + msg);
+            switch (msg.what) {
+                case ServiceMessages.MSG_IDENTIFY:
+                    Message reply = Message.obtain(null, ServiceMessages.MSG_IDENTIFY_RESPONSE,
+                            Process.myUid(), Process.myPid());
+                    reply.getData().putString(ServiceMessages.IDENTIFY_PACKAGE,
+                            mContext.getPackageName());
+                    try {
+                        msg.replyTo.send(reply);
+                    } catch (RemoteException e) {
+                        Log.e(TAG, "Error sending MSG_IDENTIFY_RESPONSE", e);
+                    }
+                    break;
+            }
+            super.handleMessage(msg);
+        }
+    }
+}
diff --git a/tests/tests/externalservice/service/src/android/externalservice/service/ExportedService.java b/tests/tests/externalservice/service/src/android/externalservice/service/ExportedService.java
new file mode 100644
index 0000000..faee786
--- /dev/null
+++ b/tests/tests/externalservice/service/src/android/externalservice/service/ExportedService.java
@@ -0,0 +1,20 @@
+/*
+ * 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.externalservice.service;
+
+public class ExportedService extends BaseService {
+}
diff --git a/tests/tests/externalservice/service/src/android/externalservice/service/ExternalNonExportedService.java b/tests/tests/externalservice/service/src/android/externalservice/service/ExternalNonExportedService.java
new file mode 100644
index 0000000..91ab426
--- /dev/null
+++ b/tests/tests/externalservice/service/src/android/externalservice/service/ExternalNonExportedService.java
@@ -0,0 +1,20 @@
+/*
+ * 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.externalservice.service;
+
+public class ExternalNonExportedService extends BaseService {
+}
diff --git a/tests/tests/externalservice/service/src/android/externalservice/service/ExternalNonIsolatedService.java b/tests/tests/externalservice/service/src/android/externalservice/service/ExternalNonIsolatedService.java
new file mode 100644
index 0000000..c8f4a50
--- /dev/null
+++ b/tests/tests/externalservice/service/src/android/externalservice/service/ExternalNonIsolatedService.java
@@ -0,0 +1,20 @@
+/*
+ * 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.externalservice.service;
+
+public class ExternalNonIsolatedService extends BaseService {
+}
diff --git a/tests/tests/externalservice/service/src/android/externalservice/service/ExternalService.java b/tests/tests/externalservice/service/src/android/externalservice/service/ExternalService.java
new file mode 100644
index 0000000..50b475a
--- /dev/null
+++ b/tests/tests/externalservice/service/src/android/externalservice/service/ExternalService.java
@@ -0,0 +1,20 @@
+/*
+ * 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.externalservice.service;
+
+public class ExternalService extends BaseService {
+}
diff --git a/tests/tests/externalservice/service/src/android/externalservice/service/IsolatedService.java b/tests/tests/externalservice/service/src/android/externalservice/service/IsolatedService.java
new file mode 100644
index 0000000..94db76f
--- /dev/null
+++ b/tests/tests/externalservice/service/src/android/externalservice/service/IsolatedService.java
@@ -0,0 +1,20 @@
+/*
+ * 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.externalservice.service;
+
+public class IsolatedService extends BaseService {
+}
diff --git a/tests/tests/externalservice/service/src/android/externalservice/service/ServiceCreator.java b/tests/tests/externalservice/service/src/android/externalservice/service/ServiceCreator.java
new file mode 100644
index 0000000..3af5c98
--- /dev/null
+++ b/tests/tests/externalservice/service/src/android/externalservice/service/ServiceCreator.java
@@ -0,0 +1,114 @@
+/*
+ * 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.externalservice.service;
+
+import android.app.Service;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Message;
+import android.os.Messenger;
+import android.os.Process;
+import android.os.RemoteException;
+import android.util.Log;
+
+import java.util.ArrayList;
+
+import android.externalservice.common.ServiceMessages;
+
+public class ServiceCreator extends Service {
+    private static final String TAG = "ExternalServiceTest.ServiceCreator";
+
+    private final ArrayList<CreatorConnection> mConnections = new ArrayList<>();
+
+    private final Handler mHandler = new BaseService.BaseHandler(this) {
+        @Override
+        public void handleMessage(Message msg) {
+            Log.d(TAG, "Received message: " + msg);
+            switch (msg.what) {
+                case ServiceMessages.MSG_CREATE_EXTERNAL_SERVICE:
+                    final Context context = ServiceCreator.this;
+                    final String pkgName = context.getPackageName();
+                    Intent intent = new Intent();
+                    intent.setComponent(new ComponentName(pkgName, pkgName+".ExternalService"));
+                    CreatorConnection conn = new CreatorConnection(msg.replyTo);
+                    boolean result = context.bindService(intent, conn,
+                            Context.BIND_AUTO_CREATE | Context.BIND_EXTERNAL_SERVICE);
+                    if (result) {
+                        mConnections.add(conn);
+                    } else {
+                        Message reply = Message.obtain();
+                        reply.what = ServiceMessages.MSG_CREATE_EXTERNAL_SERVICE_RESPONSE;
+                        try {
+                            msg.replyTo.send(reply);
+                        } catch (RemoteException e) {
+                            Log.e(TAG, "Failed to send MSG_CREATE_EXTERNAL_SERVICE_RESPONSE", e);
+                        }
+                    }
+            }
+            super.handleMessage(msg);
+        }
+    };
+
+    private final Messenger mMessenger = new Messenger(mHandler);
+
+    @Override
+    public IBinder onBind(Intent intent) {
+        Log.d(TAG, "onBind " + intent);
+        return mMessenger.getBinder();
+    }
+
+    @Override
+    public void onDestroy() {
+        for (final CreatorConnection conn : mConnections) {
+            unbindService(conn);
+        }
+        super.onDestroy();
+    }
+
+    private class CreatorConnection implements ServiceConnection {
+        private IBinder mService = null;
+        private Messenger mReplyTo = null;
+
+        public CreatorConnection(Messenger replyTo) {
+            mReplyTo = replyTo;
+        }
+
+        @Override
+        public void onServiceConnected(ComponentName name, IBinder service) {
+            Log.d(TAG, "onServiceConnected " + name);
+            Message msg =
+                    Message.obtain(null, ServiceMessages.MSG_CREATE_EXTERNAL_SERVICE_RESPONSE);
+            msg.obj = new Messenger(service);
+            try {
+                mReplyTo.send(msg);
+            } catch (RemoteException e) {
+                Log.e(TAG, "Failed to send MSG_CREATE_EXTERNAL_SERVICE_RESPONSE", e);
+            }
+            mReplyTo = null;
+        }
+
+        @Override
+        public void onServiceDisconnected(ComponentName name) {
+            Log.d(TAG, "onServiceDisconnected " + name);
+            mService = null;
+        }
+    }
+}
diff --git a/tests/tests/externalservice/src/android/externalservice/cts/ExternalServiceTest.java b/tests/tests/externalservice/src/android/externalservice/cts/ExternalServiceTest.java
new file mode 100644
index 0000000..b80b2ff
--- /dev/null
+++ b/tests/tests/externalservice/src/android/externalservice/cts/ExternalServiceTest.java
@@ -0,0 +1,358 @@
+/*
+ * 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.externalservice.cts;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.ConditionVariable;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.os.Messenger;
+import android.os.Parcel;
+import android.os.Process;
+import android.os.RemoteException;
+import android.test.AndroidTestCase;
+import android.util.Log;
+import android.util.MutableInt;
+
+import android.externalservice.common.ServiceMessages;
+
+public class ExternalServiceTest extends AndroidTestCase {
+    private static final String TAG = "ExternalServiceTest";
+
+    static final String sServicePackage = "android.externalservice.service";
+
+    private Connection mConnection = new Connection();
+
+    private ConditionVariable mCondition = new ConditionVariable(false);
+
+    static final int CONDITION_TIMEOUT = 10 * 1000 /* 10 seconds */;
+
+    public void tearDown() {
+        if (mConnection.service != null)
+            getContext().unbindService(mConnection);
+    }
+
+    /** Tests that an isolatedProcess service cannot be bound to by an external package. */
+    public void testFailBindIsolated() {
+        Intent intent = new Intent();
+        intent.setComponent(new ComponentName(sServicePackage, sServicePackage+".IsolatedService"));
+        try {
+            getContext().bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
+            fail("Should not be able to bind to non-exported, non-external service");
+        } catch (SecurityException e) {
+        }
+    }
+
+    /** Tests that BIND_EXTERNAL_SERVICE does not work with plain isolatedProcess services. */
+    public void testFailBindExternalIsolated() {
+        Intent intent = new Intent();
+        intent.setComponent(new ComponentName(sServicePackage, sServicePackage+".IsolatedService"));
+        try {
+            getContext().bindService(intent, mConnection,
+                    Context.BIND_AUTO_CREATE | Context.BIND_EXTERNAL_SERVICE);
+            fail("Should not be able to BIND_EXTERNAL_SERVICE to non-exported, non-external service");
+        } catch (SecurityException e) {
+        }
+    }
+
+    /** Tests that BIND_EXTERNAL_SERVICE does not work with exported, isolatedProcess services (
+     * requires externalService as well). */
+    public void testFailBindExternalExported() {
+        Intent intent = new Intent();
+        intent.setComponent(new ComponentName(sServicePackage, sServicePackage+".ExportedService"));
+        try {
+            getContext().bindService(intent, mConnection,
+                    Context.BIND_AUTO_CREATE | Context.BIND_EXTERNAL_SERVICE);
+            fail("Should not be able to BIND_EXTERNAL_SERVICE to non-external service");
+        } catch (SecurityException e) {
+        }
+    }
+
+    /** Tests that BIND_EXTERNAL_SERVICE requires that an externalService be exported. */
+    public void testFailBindExternalNonExported() {
+        Intent intent = new Intent();
+        intent.setComponent(
+                new ComponentName(sServicePackage, sServicePackage+".ExternalNonExportedService"));
+        try {
+            getContext().bindService(intent, mConnection,
+                    Context.BIND_AUTO_CREATE | Context.BIND_EXTERNAL_SERVICE);
+            fail("Should not be able to BIND_EXTERNAL_SERVICE to non-exported service");
+        } catch (SecurityException e) {
+        }
+    }
+
+    /** Tests that BIND_EXTERNAL_SERVICE requires the service be an isolatedProcess. */
+    public void testFailBindExternalNonIsolated() {
+        Intent intent = new Intent();
+        intent.setComponent(
+                new ComponentName(sServicePackage, sServicePackage+".ExternalNonIsolatedService"));
+        try {
+            getContext().bindService(intent, mConnection,
+                    Context.BIND_AUTO_CREATE | Context.BIND_EXTERNAL_SERVICE);
+            fail("Should not be able to BIND_EXTERNAL_SERVICE to non-isolated service");
+        } catch (SecurityException e) {
+        }
+    }
+
+    /** Tests that an externalService can only be bound with BIND_EXTERNAL_SERVICE. */
+    public void testFailBindWithoutBindExternal() {
+        Intent intent = new Intent();
+        intent.setComponent(new ComponentName(sServicePackage, sServicePackage+".ExternalService"));
+        try {
+            getContext().bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
+            fail("Should not be able to bind to an external service without BIND_EXTERNAL_SERVICE");
+        } catch (SecurityException e) {
+        }
+    }
+
+    /** Tests that an external service can be bound, and that it runs as a different principal. */
+    public void testBindExternalService() {
+        // Start the service and wait for connection.
+        Intent intent = new Intent();
+        intent.setComponent(new ComponentName(sServicePackage, sServicePackage+".ExternalService"));
+
+        mCondition.close();
+        assertTrue(getContext().bindService(intent, mConnection,
+                    Context.BIND_AUTO_CREATE | Context.BIND_EXTERNAL_SERVICE));
+
+        assertTrue(mCondition.block(CONDITION_TIMEOUT));
+        assertEquals(getContext().getPackageName(), mConnection.name.getPackageName());
+        assertNotSame(sServicePackage, mConnection.name.getPackageName());
+
+        // Check the identity of the service.
+        Messenger remote = new Messenger(mConnection.service);
+        MutableInt uid = new MutableInt(0);
+        MutableInt pid = new MutableInt(0);
+        StringBuilder pkg = new StringBuilder();
+        assertTrue(identifyService(remote, uid, pid, pkg));
+
+        assertFalse(uid.value == 0 || pid.value == 0);
+        assertNotEquals(Process.myUid(), uid.value);
+        assertNotEquals(Process.myPid(), pid.value);
+        assertEquals(getContext().getPackageName(), pkg.toString());
+    }
+
+    /** Tests that the APK providing the externalService can bind the service itself, and that
+     * other APKs bind to a different instance of it. */
+    public void testBindExternalServiceWithRunningOwn() {
+        // Start the service that will create the externalService.
+        final Connection creatorConnection = new Connection();
+        Intent intent = new Intent();
+        intent.setComponent(new ComponentName(sServicePackage, sServicePackage+".ServiceCreator"));
+
+        mCondition.close();
+        assertTrue(getContext().bindService(intent, creatorConnection, Context.BIND_AUTO_CREATE));
+        assertTrue(mCondition.block(CONDITION_TIMEOUT));
+
+        // Get the identity of the creator.
+        Messenger remoteCreator = new Messenger(creatorConnection.service);
+        MutableInt creatorUid = new MutableInt(0);
+        MutableInt creatorPid = new MutableInt(0);
+        StringBuilder creatorPkg = new StringBuilder();
+        assertTrue(identifyService(remoteCreator, creatorUid, creatorPid, creatorPkg));
+        assertFalse(creatorUid.value == 0 || creatorPid.value == 0);
+
+        // Have the creator actually start its service.
+        final Message creatorMsg =
+                Message.obtain(null, ServiceMessages.MSG_CREATE_EXTERNAL_SERVICE);
+        Handler creatorHandler = new Handler(Looper.getMainLooper()) {
+            @Override
+            public void handleMessage(Message msg) {
+                Log.d(TAG, "Received message: " + msg);
+                switch (msg.what) {
+                    case ServiceMessages.MSG_CREATE_EXTERNAL_SERVICE_RESPONSE:
+                        creatorMsg.copyFrom(msg);
+                        mCondition.open();
+                        break;
+                }
+                super.handleMessage(msg);
+            }
+        };
+        Messenger localCreator = new Messenger(creatorHandler);
+        creatorMsg.replyTo = localCreator;
+        try {
+            mCondition.close();
+            remoteCreator.send(creatorMsg);
+        } catch (RemoteException e) {
+            fail("Unexpected remote exception" + e);
+            return;
+        }
+        assertTrue(mCondition.block(CONDITION_TIMEOUT));
+
+        // Get the connection to the creator's service.
+        assertNotNull(creatorMsg.obj);
+        Messenger remoteCreatorService = (Messenger) creatorMsg.obj;
+        MutableInt creatorServiceUid = new MutableInt(0);
+        MutableInt creatorServicePid = new MutableInt(0);
+        StringBuilder creatorServicePkg = new StringBuilder();
+        assertTrue(identifyService(remoteCreatorService, creatorServiceUid, creatorServicePid,
+                creatorServicePkg));
+        assertFalse(creatorServiceUid.value == 0 || creatorPid.value == 0);
+
+        // Create an external service from this (the test) process.
+        intent = new Intent();
+        intent.setComponent(new ComponentName(sServicePackage, sServicePackage+".ExternalService"));
+
+        mCondition.close();
+        assertTrue(getContext().bindService(intent, mConnection,
+                    Context.BIND_AUTO_CREATE | Context.BIND_EXTERNAL_SERVICE));
+        assertTrue(mCondition.block(CONDITION_TIMEOUT));
+        MutableInt serviceUid = new MutableInt(0);
+        MutableInt servicePid = new MutableInt(0);
+        StringBuilder servicePkg = new StringBuilder();
+        assertTrue(identifyService(new Messenger(mConnection.service), serviceUid, servicePid,
+                servicePkg));
+        assertFalse(serviceUid.value == 0 || servicePid.value == 0);
+
+        // Make sure that all the processes are unique.
+        int myUid = Process.myUid();
+        int myPid = Process.myPid();
+        String myPkg = getContext().getPackageName();
+
+        assertNotEquals(myUid, creatorUid.value);
+        assertNotEquals(myUid, creatorServiceUid.value);
+        assertNotEquals(myUid, serviceUid.value);
+        assertNotEquals(myPid, creatorPid.value);
+        assertNotEquals(myPid, creatorServicePid.value);
+        assertNotEquals(myPid, servicePid.value);
+
+        assertNotEquals(creatorUid.value, creatorServiceUid.value);
+        assertNotEquals(creatorUid.value, serviceUid.value);
+        assertNotEquals(creatorPid.value, creatorServicePid.value);
+        assertNotEquals(creatorPid.value, servicePid.value);
+
+        assertNotEquals(creatorServiceUid.value, serviceUid.value);
+        assertNotEquals(creatorServicePid.value, servicePid.value);
+
+        assertNotEquals(myPkg, creatorPkg.toString());
+        assertNotEquals(myPkg, creatorServicePkg.toString());
+        assertEquals(creatorPkg.toString(), creatorServicePkg.toString());
+        assertEquals(myPkg, servicePkg.toString());
+
+        getContext().unbindService(creatorConnection);
+    }
+
+    /** Tests that the binding to an externalService can be changed. */
+    public void testBindExternalAboveClient() {
+        // Start the service and wait for connection.
+        Intent intent = new Intent();
+        intent.setComponent(new ComponentName(sServicePackage, sServicePackage+".ExternalService"));
+
+        mCondition.close();
+        Connection initialConn = new Connection();
+        assertTrue(getContext().bindService(intent, initialConn,
+                    Context.BIND_AUTO_CREATE | Context.BIND_EXTERNAL_SERVICE));
+
+        assertTrue(mCondition.block(CONDITION_TIMEOUT));
+
+        MutableInt uidOne = new MutableInt(0);
+        MutableInt pidOne = new MutableInt(0);
+        StringBuilder pkgOne = new StringBuilder();
+        assertTrue(identifyService(new Messenger(initialConn.service), uidOne, pidOne, pkgOne));
+        assertFalse(uidOne.value == 0 || pidOne.value == 0);
+
+        // Bind the service with a different priority.
+        mCondition.close();
+        Connection prioConn = new Connection();
+        assertTrue(getContext().bindService(intent, prioConn,
+                    Context.BIND_AUTO_CREATE | Context.BIND_EXTERNAL_SERVICE |
+                            Context.BIND_ABOVE_CLIENT));
+
+        assertTrue(mCondition.block(CONDITION_TIMEOUT));
+
+        MutableInt uidTwo = new MutableInt(0);
+        MutableInt pidTwo = new MutableInt(0);
+        StringBuilder pkgTwo = new StringBuilder();
+        Messenger prioMessenger = new Messenger(prioConn.service);
+        assertTrue(identifyService(prioMessenger, uidTwo, pidTwo, pkgTwo));
+        assertFalse(uidTwo.value == 0 || pidTwo.value == 0);
+
+        assertEquals(uidOne.value, uidTwo.value);
+        assertEquals(pidOne.value, pidTwo.value);
+        assertEquals(pkgOne.toString(), pkgTwo.toString());
+        assertNotEquals(Process.myUid(), uidOne.value);
+        assertNotEquals(Process.myPid(), pidOne.value);
+        assertEquals(getContext().getPackageName(), pkgOne.toString());
+
+        getContext().unbindService(prioConn);
+        getContext().unbindService(initialConn);
+    }
+
+    /** Given a Messenger, this will message the service to retrieve its UID, PID, and package name,
+     * storing the results in the mutable parameters. */
+    private boolean identifyService(Messenger service, final MutableInt uid, final MutableInt pid,
+            final StringBuilder packageName) {
+        Handler handler = new Handler(Looper.getMainLooper()) {
+            @Override
+            public void handleMessage(Message msg) {
+                Log.d(TAG, "Received message: " + msg);
+                switch (msg.what) {
+                    case ServiceMessages.MSG_IDENTIFY_RESPONSE:
+                        uid.value = msg.arg1;
+                        pid.value = msg.arg2;
+                        packageName.append(
+                                msg.getData().getString(ServiceMessages.IDENTIFY_PACKAGE));
+                        mCondition.open();
+                        break;
+                }
+                super.handleMessage(msg);
+            }
+        };
+        Messenger local = new Messenger(handler);
+
+        Message msg = Message.obtain(null, ServiceMessages.MSG_IDENTIFY);
+        msg.replyTo = local;
+        try {
+            mCondition.close();
+            service.send(msg);
+        } catch (RemoteException e) {
+            fail("Unexpected remote exception: " + e);
+            return false;
+        }
+
+        return mCondition.block(CONDITION_TIMEOUT);
+    }
+
+    private class Connection implements ServiceConnection {
+        IBinder service = null;
+        ComponentName name = null;
+        boolean disconnected = false;
+
+        @Override
+        public void onServiceConnected(ComponentName name, IBinder service) {
+            Log.d(TAG, "onServiceConnected " + name);
+            this.service = service;
+            this.name = name;
+            mCondition.open();
+        }
+
+        @Override
+        public void onServiceDisconnected(ComponentName name) {
+            Log.d(TAG, "onServiceDisconnected " + name);
+        }
+    }
+
+    private <T> void assertNotEquals(T expected, T actual) {
+        assertFalse("Expected <" + expected + "> should not be equal to actual <" + actual + ">",
+                expected.equals(actual));
+    }
+}
diff --git a/tests/tests/graphics/res/color/fill_gradient_linear.xml b/tests/tests/graphics/res/color/fill_gradient_linear.xml
new file mode 100644
index 0000000..e0e3f03
--- /dev/null
+++ b/tests/tests/graphics/res/color/fill_gradient_linear.xml
@@ -0,0 +1,29 @@
+<?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.
+ */
+-->
+<gradient xmlns:android="http://schemas.android.com/apk/res/android"
+          android:angle="90"
+          android:startColor="?android:attr/colorPrimary"
+          android:endColor="?android:attr/colorControlActivated"
+          android:centerColor="#00ff0000"
+          android:startX="0"
+          android:startY="0"
+          android:endX="100"
+          android:endY="100"
+          android:type="linear">
+</gradient>
\ No newline at end of file
diff --git a/tests/tests/graphics/res/color/fill_gradient_linear_item.xml b/tests/tests/graphics/res/color/fill_gradient_linear_item.xml
new file mode 100644
index 0000000..cfb1236
--- /dev/null
+++ b/tests/tests/graphics/res/color/fill_gradient_linear_item.xml
@@ -0,0 +1,32 @@
+<?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.
+ */
+-->
+<gradient xmlns:android="http://schemas.android.com/apk/res/android"
+          android:angle="90"
+          android:startColor="?android:attr/colorPrimary"
+          android:endColor="?android:attr/colorControlActivated"
+          android:centerColor="#f00"
+          android:startX="0"
+          android:startY="0"
+          android:endX="100"
+          android:endY="100"
+          android:type="linear">
+    <item android:offset="0.1" android:color="?android:attr/colorPrimary"/>
+    <item android:offset="0.4" android:color="#fff"/>
+    <item android:offset="0.9" android:color="?android:attr/colorControlActivated"/>
+</gradient>
\ No newline at end of file
diff --git a/tests/tests/graphics/res/color/fill_gradient_linear_item_overlap.xml b/tests/tests/graphics/res/color/fill_gradient_linear_item_overlap.xml
new file mode 100644
index 0000000..18274b9
--- /dev/null
+++ b/tests/tests/graphics/res/color/fill_gradient_linear_item_overlap.xml
@@ -0,0 +1,33 @@
+<?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.
+ */
+-->
+<gradient xmlns:android="http://schemas.android.com/apk/res/android"
+          android:angle="90"
+          android:startColor="?android:attr/colorPrimary"
+          android:endColor="?android:attr/colorControlActivated"
+          android:centerColor="#f00"
+          android:startX="0"
+          android:startY="0"
+          android:endX="100"
+          android:endY="100"
+          android:type="linear">
+    <item android:offset="0.1" android:color="?android:attr/colorPrimary"/>
+    <item android:offset="0.4" android:color="#f00"/>
+    <item android:offset="0.4" android:color="#fff"/>
+    <item android:offset="0.9" android:color="?android:attr/colorControlActivated"/>
+</gradient>
\ No newline at end of file
diff --git a/tests/tests/graphics/res/color/fill_gradient_radial.xml b/tests/tests/graphics/res/color/fill_gradient_radial.xml
new file mode 100644
index 0000000..ef6fd70
--- /dev/null
+++ b/tests/tests/graphics/res/color/fill_gradient_radial.xml
@@ -0,0 +1,27 @@
+<?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.
+ */
+-->
+<gradient xmlns:android="http://schemas.android.com/apk/res/android"
+          android:centerColor="#ff0000"
+          android:endColor="?android:attr/colorControlActivated"
+          android:centerX="300"
+          android:centerY="300"
+          android:gradientRadius="100"
+          android:startColor="?android:attr/colorPrimary"
+          android:type="radial">
+</gradient>
\ No newline at end of file
diff --git a/tests/tests/graphics/res/color/fill_gradient_radial_item.xml b/tests/tests/graphics/res/color/fill_gradient_radial_item.xml
new file mode 100644
index 0000000..c6cea7c
--- /dev/null
+++ b/tests/tests/graphics/res/color/fill_gradient_radial_item.xml
@@ -0,0 +1,30 @@
+<?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.
+ */
+-->
+<gradient xmlns:android="http://schemas.android.com/apk/res/android"
+          android:centerColor="#ff0000"
+          android:endColor="#ff0000ff"
+          android:centerX="300"
+          android:centerY="300"
+          android:gradientRadius="100"
+          android:startColor="#ffffffff"
+          android:type="radial">
+    <item android:offset="0.1" android:color="?android:attr/colorPrimary"/>
+    <item android:offset="0.4" android:color="#fff"/>
+    <item android:offset="0.9" android:color="?android:attr/colorControlActivated"/>
+</gradient>
\ No newline at end of file
diff --git a/tests/tests/graphics/res/color/fill_gradient_radial_item_short.xml b/tests/tests/graphics/res/color/fill_gradient_radial_item_short.xml
new file mode 100644
index 0000000..fefbe9f
--- /dev/null
+++ b/tests/tests/graphics/res/color/fill_gradient_radial_item_short.xml
@@ -0,0 +1,26 @@
+<?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.
+ */
+-->
+<gradient xmlns:android="http://schemas.android.com/apk/res/android"
+          android:centerX="300"
+          android:centerY="300"
+          android:gradientRadius="100"
+          android:type="radial">
+    <item android:offset="0.1" android:color="?android:attr/colorPrimary"/>
+    <item android:offset="0.9" android:color="?android:attr/colorControlActivated"/>
+</gradient>
\ No newline at end of file
diff --git a/tests/tests/graphics/res/color/fill_gradient_sweep.xml b/tests/tests/graphics/res/color/fill_gradient_sweep.xml
new file mode 100644
index 0000000..e1fbd10
--- /dev/null
+++ b/tests/tests/graphics/res/color/fill_gradient_sweep.xml
@@ -0,0 +1,27 @@
+<?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.
+ */
+-->
+<gradient xmlns:android="http://schemas.android.com/apk/res/android"
+          android:centerColor="#ff0000"
+          android:endColor="#ff0000ff"
+          android:centerX="500"
+          android:centerY="500"
+          android:gradientRadius="10"
+          android:startColor="#ffffffff"
+          android:type="sweep">
+</gradient>
diff --git a/tests/tests/graphics/res/color/fill_gradient_sweep_item.xml b/tests/tests/graphics/res/color/fill_gradient_sweep_item.xml
new file mode 100644
index 0000000..332b9389
--- /dev/null
+++ b/tests/tests/graphics/res/color/fill_gradient_sweep_item.xml
@@ -0,0 +1,30 @@
+<?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.
+ */
+-->
+<gradient xmlns:android="http://schemas.android.com/apk/res/android"
+          android:centerColor="#ff0000"
+          android:endColor="#ff0000ff"
+          android:centerX="500"
+          android:centerY="500"
+          android:gradientRadius="10"
+          android:startColor="#ffffffff"
+          android:type="sweep">
+    <item android:offset="0.1" android:color="?android:attr/colorPrimary"/>
+    <item android:offset="0.4" android:color="#fff"/>
+    <item android:offset="0.9" android:color="?android:attr/colorControlActivated"/>
+</gradient>
diff --git a/tests/tests/graphics/res/color/fill_gradient_sweep_item_long.xml b/tests/tests/graphics/res/color/fill_gradient_sweep_item_long.xml
new file mode 100644
index 0000000..3931288
--- /dev/null
+++ b/tests/tests/graphics/res/color/fill_gradient_sweep_item_long.xml
@@ -0,0 +1,30 @@
+<?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.
+ */
+-->
+<gradient xmlns:android="http://schemas.android.com/apk/res/android"
+          android:centerX="500"
+          android:centerY="500"
+          android:gradientRadius="10"
+          android:type="sweep">
+    <item android:offset="-0.3" android:color="#f00"/>
+    <item android:offset="0.1" android:color="?android:attr/colorPrimary"/>
+    <item android:offset="0.4" android:color="#0f0"/>
+    <item android:offset="0.6" android:color="#00f"/>
+    <item android:offset="0.7" android:color="?android:attr/colorControlActivated"/>
+    <item android:offset="1.5" android:color="#00f"/>
+</gradient>
diff --git a/tests/tests/graphics/res/color/stroke_gradient.xml b/tests/tests/graphics/res/color/stroke_gradient.xml
new file mode 100644
index 0000000..cb324c9
--- /dev/null
+++ b/tests/tests/graphics/res/color/stroke_gradient.xml
@@ -0,0 +1,29 @@
+<?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.
+ */
+-->
+<gradient xmlns:android="http://schemas.android.com/apk/res/android"
+          android:angle="90"
+          android:centerColor="#7f7f7f"
+          android:endColor="#ffffff"
+          android:startColor="#000000"
+          android:startX="0"
+          android:endX="100"
+          android:startY="0"
+          android:endY="0"
+          android:type="linear">
+</gradient>
\ No newline at end of file
diff --git a/tests/tests/graphics/res/color/stroke_gradient_item.xml b/tests/tests/graphics/res/color/stroke_gradient_item.xml
new file mode 100644
index 0000000..15d948c
--- /dev/null
+++ b/tests/tests/graphics/res/color/stroke_gradient_item.xml
@@ -0,0 +1,32 @@
+<?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.
+ */
+-->
+<gradient xmlns:android="http://schemas.android.com/apk/res/android"
+          android:angle="90"
+          android:centerColor="#7f7f7f"
+          android:endColor="#ffffff"
+          android:startColor="#000000"
+          android:startX="0"
+          android:endX="100"
+          android:startY="0"
+          android:endY="0"
+          android:type="linear">
+    <item android:offset="0.1" android:color="#f00"/>
+    <item android:offset="0.2" android:color="#f0f"/>
+    <item android:offset="0.9" android:color="#f00f"/>
+</gradient>
\ No newline at end of file
diff --git a/tests/tests/graphics/res/color/stroke_gradient_item_alpha.xml b/tests/tests/graphics/res/color/stroke_gradient_item_alpha.xml
new file mode 100644
index 0000000..fda2b88b
--- /dev/null
+++ b/tests/tests/graphics/res/color/stroke_gradient_item_alpha.xml
@@ -0,0 +1,28 @@
+<?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.
+ */
+-->
+<gradient xmlns:android="http://schemas.android.com/apk/res/android"
+          android:startX="0"
+          android:endX="100"
+          android:startY="0"
+          android:endY="0"
+          android:type="linear">
+    <item android:offset="0.1" android:color="#f00"/>
+    <item android:offset="0.2" android:color="#2f0f"/>
+    <item android:offset="0.9" android:color="#f00f"/>
+</gradient>
\ No newline at end of file
diff --git a/tests/tests/graphics/res/drawable-nodpi/vector_icon_gradient_1_golden.png b/tests/tests/graphics/res/drawable-nodpi/vector_icon_gradient_1_golden.png
new file mode 100644
index 0000000..638802f
--- /dev/null
+++ b/tests/tests/graphics/res/drawable-nodpi/vector_icon_gradient_1_golden.png
Binary files differ
diff --git a/tests/tests/graphics/res/drawable-nodpi/vector_icon_gradient_2_golden.png b/tests/tests/graphics/res/drawable-nodpi/vector_icon_gradient_2_golden.png
new file mode 100644
index 0000000..1f248e3
--- /dev/null
+++ b/tests/tests/graphics/res/drawable-nodpi/vector_icon_gradient_2_golden.png
Binary files differ
diff --git a/tests/tests/graphics/res/drawable-nodpi/vector_icon_gradient_3_golden.png b/tests/tests/graphics/res/drawable-nodpi/vector_icon_gradient_3_golden.png
new file mode 100644
index 0000000..085e72a
--- /dev/null
+++ b/tests/tests/graphics/res/drawable-nodpi/vector_icon_gradient_3_golden.png
Binary files differ
diff --git a/tests/tests/graphics/res/drawable/vector_icon_gradient_1.xml b/tests/tests/graphics/res/drawable/vector_icon_gradient_1.xml
new file mode 100644
index 0000000..d67aca7
--- /dev/null
+++ b/tests/tests/graphics/res/drawable/vector_icon_gradient_1.xml
@@ -0,0 +1,91 @@
+<?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.
+ */
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:height="64dp"
+        android:width="64dp"
+        android:viewportHeight="400"
+        android:viewportWidth="400" >
+
+<group android:name="backgroundGroup"
+       android:scaleX="0.5"
+       android:scaleY="0.5">
+    <path
+            android:name="background1"
+            android:fillColor="@color/fill_gradient_linear"
+            android:pathData="M 0,0 l 200,0 l 0, 200 l -200, 0 z" />
+    <path
+            android:name="background2"
+            android:fillColor="@color/fill_gradient_radial"
+            android:pathData="M 200,200 l 200,0 l 0, 200 l -200, 0 z" />
+    <path
+            android:name="background3"
+            android:fillColor="@color/fill_gradient_sweep"
+            android:pathData="M 400,400 l 200,0 l 0, 200 l -200, 0 z" />
+</group>
+<group
+        android:name="translateToCenterGroup"
+        android:translateX="50.0"
+        android:translateY="90.0" >
+    <path
+            android:name="twoLines"
+            android:pathData="@string/twoLinePathData"
+            android:strokeColor="@color/stroke_gradient"
+            android:strokeWidth="20" />
+
+    <group
+            android:name="rotationGroup"
+            android:pivotX="0.0"
+            android:pivotY="0.0"
+            android:rotation="-45.0">
+        <path
+                android:name="twoLines1"
+                android:pathData="@string/twoLinePathData"
+                android:strokeColor="@color/stroke_gradient"
+                android:strokeWidth="20" />
+
+        <group
+                android:name="translateGroup"
+                android:translateX="130.0"
+                android:translateY="160.0">
+            <group android:name="scaleGroup" >
+                <path
+                        android:name="twoLines3"
+                        android:pathData="@string/twoLinePathData"
+                        android:strokeColor="@color/stroke_gradient"
+                        android:strokeWidth="20" />
+            </group>
+        </group>
+
+        <group
+                android:name="translateGroupHalf"
+                android:translateX="65.0"
+                android:translateY="80.0">
+            <group android:name="scaleGroup" >
+                <path
+                        android:name="twoLines2"
+                        android:pathData="@string/twoLinePathData"
+                        android:fillColor="@color/fill_gradient_linear"
+                        android:strokeColor="@color/stroke_gradient"
+                        android:strokeWidth="20" />
+            </group>
+        </group>
+    </group>
+</group>
+
+</vector>
\ No newline at end of file
diff --git a/tests/tests/graphics/res/drawable/vector_icon_gradient_2.xml b/tests/tests/graphics/res/drawable/vector_icon_gradient_2.xml
new file mode 100644
index 0000000..abf3c7a
--- /dev/null
+++ b/tests/tests/graphics/res/drawable/vector_icon_gradient_2.xml
@@ -0,0 +1,90 @@
+<?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.
+ */
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:height="64dp"
+        android:width="64dp"
+        android:viewportHeight="400"
+        android:viewportWidth="400" >
+
+<group android:name="backgroundGroup"
+       android:scaleX="0.5"
+       android:scaleY="0.5">
+    <path
+            android:name="background1"
+            android:fillColor="@color/fill_gradient_linear_item"
+            android:pathData="M 0,0 l 200,0 l 0, 200 l -200, 0 z" />
+    <path
+            android:name="background2"
+            android:fillColor="@color/fill_gradient_radial_item"
+            android:pathData="M 200,200 l 200,0 l 0, 200 l -200, 0 z" />
+    <path
+            android:name="background3"
+            android:fillColor="@color/fill_gradient_sweep_item"
+            android:pathData="M 400,400 l 200,0 l 0, 200 l -200, 0 z" />
+</group>
+<group
+        android:name="translateToCenterGroup"
+        android:translateX="50.0"
+        android:translateY="90.0" >
+    <path
+            android:name="twoLines"
+            android:pathData="@string/twoLinePathData"
+            android:strokeColor="@color/stroke_gradient_item"
+            android:strokeWidth="20" />
+
+    <group
+            android:name="rotationGroup"
+            android:pivotX="0.0"
+            android:pivotY="0.0"
+            android:rotation="-45.0">
+        <path
+                android:name="twoLines1"
+                android:pathData="@string/twoLinePathData"
+                android:strokeColor="@color/stroke_gradient_item"
+                android:strokeWidth="20" />
+
+        <group
+                android:name="translateGroup"
+                android:translateX="130.0"
+                android:translateY="160.0">
+            <group android:name="scaleGroup" >
+                <path
+                        android:name="twoLines3"
+                        android:pathData="@string/twoLinePathData"
+                        android:strokeColor="@color/stroke_gradient_item"
+                        android:strokeWidth="20" />
+            </group>
+        </group>
+
+        <group
+                android:name="translateGroupHalf"
+                android:translateX="65.0"
+                android:translateY="80.0">
+            <group android:name="scaleGroup" >
+                <path
+                        android:name="twoLines2"
+                        android:pathData="@string/twoLinePathData"
+                        android:strokeColor="@color/stroke_gradient_item"
+                        android:strokeWidth="20" />
+            </group>
+        </group>
+    </group>
+</group>
+
+</vector>
\ No newline at end of file
diff --git a/tests/tests/graphics/res/drawable/vector_icon_gradient_3.xml b/tests/tests/graphics/res/drawable/vector_icon_gradient_3.xml
new file mode 100644
index 0000000..5f9726f
--- /dev/null
+++ b/tests/tests/graphics/res/drawable/vector_icon_gradient_3.xml
@@ -0,0 +1,90 @@
+<?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.
+ */
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:height="64dp"
+        android:width="64dp"
+        android:viewportHeight="400"
+        android:viewportWidth="400" >
+
+<group android:name="backgroundGroup"
+       android:scaleX="0.5"
+       android:scaleY="0.5">
+    <path
+            android:name="background1"
+            android:fillColor="@color/fill_gradient_linear_item_overlap"
+            android:pathData="M 0,0 l 200,0 l 0, 200 l -200, 0 z" />
+    <path
+            android:name="background2"
+            android:fillColor="@color/fill_gradient_radial_item_short"
+            android:pathData="M 200,200 l 200,0 l 0, 200 l -200, 0 z" />
+    <path
+            android:name="background3"
+            android:fillColor="@color/fill_gradient_sweep_item_long"
+            android:pathData="M 400,400 l 200,0 l 0, 200 l -200, 0 z" />
+</group>
+<group
+        android:name="translateToCenterGroup"
+        android:translateX="50.0"
+        android:translateY="90.0" >
+    <path
+            android:name="twoLines"
+            android:pathData="@string/twoLinePathData"
+            android:strokeColor="@color/stroke_gradient_item_alpha"
+            android:strokeWidth="20" />
+
+    <group
+            android:name="rotationGroup"
+            android:pivotX="0.0"
+            android:pivotY="0.0"
+            android:rotation="-45.0">
+        <path
+                android:name="twoLines1"
+                android:pathData="@string/twoLinePathData"
+                android:strokeColor="@color/stroke_gradient_item_alpha"
+                android:strokeWidth="20" />
+
+        <group
+                android:name="translateGroup"
+                android:translateX="130.0"
+                android:translateY="160.0">
+            <group android:name="scaleGroup" >
+                <path
+                        android:name="twoLines3"
+                        android:pathData="@string/twoLinePathData"
+                        android:strokeColor="@color/stroke_gradient_item_alpha"
+                        android:strokeWidth="20" />
+            </group>
+        </group>
+
+        <group
+                android:name="translateGroupHalf"
+                android:translateX="65.0"
+                android:translateY="80.0">
+            <group android:name="scaleGroup" >
+                <path
+                        android:name="twoLines2"
+                        android:pathData="@string/twoLinePathData"
+                        android:strokeColor="@color/stroke_gradient_item_alpha"
+                        android:strokeWidth="20" />
+            </group>
+        </group>
+    </group>
+</group>
+
+</vector>
\ No newline at end of file
diff --git a/tests/tests/graphics/res/values/strings.xml b/tests/tests/graphics/res/values/strings.xml
index c167278..8208b19 100644
--- a/tests/tests/graphics/res/values/strings.xml
+++ b/tests/tests/graphics/res/values/strings.xml
@@ -176,4 +176,5 @@
 text, I would love to see the kind of devices you guys now use! Guys, maybe some devices need longer string!
 I think so, so how about double this string, like copy and paste! </string>
     <string name="rectangle200">"M 0,0 l 200,0 l 0, 200 l -200, 0 z"</string>
+    <string name="twoLinePathData">"M 0,0 v 100 M 0,0 h 100"</string>
 </resources>
diff --git a/tests/tests/graphics/src/android/graphics/cts/VulkanReservedTest.java b/tests/tests/graphics/src/android/graphics/cts/VulkanReservedTest.java
deleted file mode 100644
index bfd520e..0000000
--- a/tests/tests/graphics/src/android/graphics/cts/VulkanReservedTest.java
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.graphics.cts;
-
-import android.cts.util.FileUtils;
-
-import junit.framework.TestCase;
-
-import java.io.File;
-
-public class VulkanReservedTest extends TestCase {
-
-    /**
-     * Assert that file with given path does not exist.
-     */
-    private static void assertNoFile(String filename) {
-        assertFalse(filename + " must not exist", new File(filename).exists());
-    }
-
-    /**
-     * Test that no vendor ships libvulkan.so before ratification and
-     * appropriate CTS coverage.
-     */
-    public void testNoVulkan() {
-        assertNoFile("/system/lib/libvulkan.so");
-        assertNoFile("/system/lib64/libvulkan.so");
-        assertNoFile("/vendor/lib/libvulkan.so");
-        assertNoFile("/vendor/lib64/libvulkan.so");
-    }
-}
diff --git a/tests/tests/graphics/src/android/graphics/drawable/cts/VectorDrawableTest.java b/tests/tests/graphics/src/android/graphics/drawable/cts/VectorDrawableTest.java
index 8944d63..e5903d0 100644
--- a/tests/tests/graphics/src/android/graphics/drawable/cts/VectorDrawableTest.java
+++ b/tests/tests/graphics/src/android/graphics/drawable/cts/VectorDrawableTest.java
@@ -103,6 +103,18 @@
             R.drawable.vector_icon_arcto_golden,
     };
 
+    private static final int[] GRADIENT_ICON_RES_IDS = new int[] {
+            R.drawable.vector_icon_gradient_1,
+            R.drawable.vector_icon_gradient_2,
+            R.drawable.vector_icon_gradient_3,
+    };
+
+    private static final int[] GRADIENT_GOLDEN_IMAGES = new int[] {
+            R.drawable.vector_icon_gradient_1_golden,
+            R.drawable.vector_icon_gradient_2_golden,
+            R.drawable.vector_icon_gradient_3_golden,
+    };
+
     private static final int[] STATEFUL_RES_IDS = new int[] {
             R.drawable.vector_icon_state_list
     };
@@ -151,6 +163,10 @@
         verifyVectorDrawables(ICON_RES_IDS, GOLDEN_IMAGES, null);
     }
 
+    public void testVectorDrawableGradient() throws XmlPullParserException, IOException {
+        verifyVectorDrawables(GRADIENT_ICON_RES_IDS, GRADIENT_GOLDEN_IMAGES, null);
+    }
+
     public void testColorStateList() throws XmlPullParserException, IOException {
         for (int i = 0; i < STATEFUL_STATE_SETS.length; i++) {
             verifyVectorDrawables(
@@ -175,7 +191,7 @@
                 throw new XmlPullParserException("No start tag found");
             }
 
-            mVectorDrawable.inflate(mResources, parser, attrs);
+            mVectorDrawable.inflate(mResources, parser, attrs, mContext.getTheme());
 
             if (stateSet != null) {
                 mVectorDrawable.setState(stateSet);
diff --git a/tests/tests/location/src/android/location/cts/LocationManagerTest.java b/tests/tests/location/src/android/location/cts/LocationManagerTest.java
index 8ac56ae..d9a871d 100644
--- a/tests/tests/location/src/android/location/cts/LocationManagerTest.java
+++ b/tests/tests/location/src/android/location/cts/LocationManagerTest.java
@@ -897,8 +897,8 @@
         mManager.addNmeaListener(listener);
         mManager.removeNmeaListener(listener);
 
-        mManager.addNmeaListener(null);
-        mManager.removeNmeaListener(null);
+        mManager.addNmeaListener((NmeaListener) null);
+        mManager.removeNmeaListener((NmeaListener) null);
     }
 
     public void testIsProviderEnabled() {
diff --git a/tests/tests/location2/src/android/location2/cts/LocationManagerTest.java b/tests/tests/location2/src/android/location2/cts/LocationManagerTest.java
index 6764223..2e84264 100644
--- a/tests/tests/location2/src/android/location2/cts/LocationManagerTest.java
+++ b/tests/tests/location2/src/android/location2/cts/LocationManagerTest.java
@@ -253,7 +253,7 @@
         }
 
         try {
-            mManager.addNmeaListener(null);
+            mManager.addNmeaListener((NmeaListener) null);
             fail("Should have failed to add null as a gps status nmea listener");
         } catch (SecurityException e) {
             // expected
diff --git a/tests/tests/media/DynamicConfig.xml b/tests/tests/media/DynamicConfig.xml
index 702157d..1299440 100644
--- a/tests/tests/media/DynamicConfig.xml
+++ b/tests/tests/media/DynamicConfig.xml
@@ -13,17 +13,41 @@
      limitations under the License.
 -->
 
-<DynamicConfig>
-    <Config key="DecoderTest-VIDEO_URL">http://redirector.gvt1.com/videoplayback?id=c80658495af60617&amp;itag=18&amp;source=youtube&amp;ip=0.0.0.0&amp;ipbits=0&amp;expire=19000000000&amp;sparams=ip,ipbits,expire,id,itag,source&amp;signature=46A04ED550CA83B79B60060BA80C79FDA5853D26.49582D382B4A9AFAA163DED38D2AE531D85603C0&amp;key=ik0&amp;user=android-device-test</Config>
-    <Config key="StreamingMediaPlayerTest-testHTTP_H264Base_AAC_Video1">http://redirector.gvt1.com/videoplayback?id=271de9756065677e&amp;itag=18&amp;source=youtube&amp;ip=0.0.0.0&amp;ipbits=0&amp;expire=19000000000&amp;sparams=ip,ipbits,expire,id,itag,source&amp;signature=667AEEF54639926662CE62361400B8F8C1753B3F.15F46C382C68A9F121BA17BF1F56BEDEB4B06091&amp;key=ik0&amp;user=android-device-test</Config>
-    <Config key="StreamingMediaPlayerTest-testHTTP_H264Base_AAC_Video2">http://www.youtube.com/api/manifest/hls_variant/id/0168724d02bd9945/itag/5/source/youtube/playlist_type/DVR/ip/0.0.0.0/ipbits/0/expire/19000000000/sparams/ip,ipbits,expire,id,itag,source,playlist_type/signature/773AB8ACC68A96E5AA481996AD6A1BBCB70DCB87.95733B544ACC5F01A1223A837D2CF04DF85A3360/key/ik0/file/m3u8</Config>
-    <Config key="MediaCodecCapabilitiesTest-testAvcHigh31">http://redirector.gvt1.com/videoplayback?id=271de9756065677e&amp;itag=22&amp;source=youtube&amp;user=android-device-test&amp;sparams=ip,ipbits,expire,id,itag,source,user&amp;ip=0.0.0.0&amp;ipbits=0&amp;expire=19000000000&amp;signature=179525311196616BD8E1381759B0E5F81A9E91B5.C4A50E44059FEBCC6BBC78E3B3A4E0E0065777&amp;key=ik0</Config>
-    <Config key="StreamingMediaPlayerTest-testHTTP_MPEG4SP_AAC_Video2">http://redirector.gvt1.com/videoplayback?id=c80658495af60617&amp;itag=17&amp;source=youtube&amp;ip=0.0.0.0&amp;ipbits=0&amp;expire=19000000000&amp;sparams=ip,ipbits,expire,id,itag,source&amp;signature=70E979A621001201BC18622BDBF914FA870BDA40.6E78890B80F4A33A18835F775B1FF64F0A4D0003&amp;key=ik0&amp;user=android-device-test</Config>
-    <Config key="StreamingMediaPlayerTest-testHTTP_MPEG4SP_AAC_Video1">http://redirector.gvt1.com/videoplayback?id=271de9756065677e&amp;itag=17&amp;source=youtube&amp;ip=0.0.0.0&amp;ipbits=0&amp;expire=19000000000&amp;sparams=ip,ipbits,expire,id,itag,source&amp;signature=837198AAADF6F36BA6B2D324F690A7C5B7AFE3FF.7138CE5E36D718220726C1FC305497FF2D082249&amp;key=ik0&amp;user=android-device-test</Config>
-    <Config key="MediaCodecCapabilitiesTest-testAvcBaseline12">http://redirector.gvt1.com/videoplayback?id=271de9756065677e&amp;itag=160&amp;source=youtube&amp;user=android-device-test&amp;sparams=ip,ipbits,expire,id,itag,source,user&amp;ip=0.0.0.0&amp;ipbits=0&amp;expire=19000000000&amp;signature=9EDCA0B395B8A949C511FD5E59B9F805CFF797FD.702DE9BA7AF96785FD6930AD2DD693A0486C880E&amp;key=ik0</Config>
-    <Config key="DecoderTest-AUDIO_URL">http://redirector.gvt1.com/videoplayback?id=c80658495af60617&amp;itag=18&amp;source=youtube&amp;ip=0.0.0.0&amp;ipbits=0&amp;expire=19000000000&amp;sparams=ip,ipbits,expire,id,itag,source&amp;signature=46A04ED550CA83B79B60060BA80C79FDA5853D26.49582D382B4A9AFAA163DED38D2AE531D85603C0&amp;key=ik0&amp;user=android-device-test</Config>
-    <Config key="MediaCodecCapabilitiesTest-testAvcBaseline30">http://redirector.gvt1.com/videoplayback?id=271de9756065677e&amp;itag=18&amp;source=youtube&amp;user=android-device-test&amp;sparams=ip,ipbits,expire,id,itag,source,user&amp;ip=0.0.0.0&amp;ipbits=0&amp;expire=19000000000&amp;signature=7DCDE3A6594D0B91A27676A3CDC3A87B149F82EA.7A83031734CB1EDCE06766B6228842F954927960&amp;key=ik0</Config>
-    <Config key="MediaCodecCapabilitiesTest-testAvcHigh40">http://redirector.gvt1.com/videoplayback?id=271de9756065677e&amp;itag=137&amp;source=youtube&amp;user=android-device-test&amp;sparams=ip,ipbits,expire,id,itag,source,user&amp;ip=0.0.0.0&amp;ipbits=0&amp;expire=19000000000&amp;signature=B0976085596DD42DEA3F08307F76587241CB132B.043B719C039E8B92F45391ADC0BE3665E2332930&amp;key=ik0</Config>
-    <Config key="StreamingMediaPlayerTest-testHTTP_H263_AMR_Video2">http://redirector.gvt1.com/videoplayback?id=c80658495af60617&amp;itag=13&amp;source=youtube&amp;ip=0.0.0.0&amp;ipbits=0&amp;expire=19000000000&amp;sparams=ip,ipbits,expire,id,itag,source&amp;signature=508D82AB36939345BF6B8D0623CB6CABDD9C64C3.9B3336A96846DF38E5343C46AA57F6CF2956E427&amp;key=ik0&amp;user=android-device-test</Config>
-    <Config key="StreamingMediaPlayerTest-testHTTP_H263_AMR_Video1">http://redirector.gvt1.com/videoplayback?id=271de9756065677e&amp;itag=13&amp;source=youtube&amp;ip=0.0.0.0&amp;ipbits=0&amp;expire=19000000000&amp;sparams=ip,ipbits,expire,id,itag,source&amp;signature=5729247E22691EBB3E804DDD523EC42DC17DD8CE.443B81C1E8E6D64E4E1555F568BA46C206507D78&amp;key=ik0&amp;user=android-device-test</Config>
-</DynamicConfig>
+<dynamicConfig>
+    <entry key="DecoderTest-VIDEO_URL">
+        <value>http://redirector.gvt1.com/videoplayback?id=c80658495af60617&amp;itag=18&amp;source=youtube&amp;ip=0.0.0.0&amp;ipbits=0&amp;expire=19000000000&amp;sparams=ip,ipbits,expire,id,itag,source&amp;signature=46A04ED550CA83B79B60060BA80C79FDA5853D26.49582D382B4A9AFAA163DED38D2AE531D85603C0&amp;key=ik0&amp;user=android-device-test</value>
+    </entry>
+    <entry key="StreamingMediaPlayerTest-testHTTP_H264Base_AAC_Video1">
+        <value>http://redirector.gvt1.com/videoplayback?id=271de9756065677e&amp;itag=18&amp;source=youtube&amp;ip=0.0.0.0&amp;ipbits=0&amp;expire=19000000000&amp;sparams=ip,ipbits,expire,id,itag,source&amp;signature=667AEEF54639926662CE62361400B8F8C1753B3F.15F46C382C68A9F121BA17BF1F56BEDEB4B06091&amp;key=ik0&amp;user=android-device-test</value>
+    </entry>
+    <entry key="StreamingMediaPlayerTest-testHTTP_H264Base_AAC_Video2">
+        <value>http://www.youtube.com/api/manifest/hls_variant/id/0168724d02bd9945/itag/5/source/youtube/playlist_type/DVR/ip/0.0.0.0/ipbits/0/expire/19000000000/sparams/ip,ipbits,expire,id,itag,source,playlist_type/signature/773AB8ACC68A96E5AA481996AD6A1BBCB70DCB87.95733B544ACC5F01A1223A837D2CF04DF85A3360/key/ik0/file/m3u8</value>
+    </entry>
+    <entry key="MediaCodecCapabilitiesTest-testAvcHigh31">
+        <value>http://redirector.gvt1.com/videoplayback?id=271de9756065677e&amp;itag=22&amp;source=youtube&amp;user=android-device-test&amp;sparams=ip,ipbits,expire,id,itag,source,user&amp;ip=0.0.0.0&amp;ipbits=0&amp;expire=19000000000&amp;signature=179525311196616BD8E1381759B0E5F81A9E91B5.C4A50E44059FEBCC6BBC78E3B3A4E0E0065777&amp;key=ik0</value>
+    </entry>
+    <entry key="StreamingMediaPlayerTest-testHTTP_MPEG4SP_AAC_Video2">
+        <value>http://redirector.gvt1.com/videoplayback?id=c80658495af60617&amp;itag=17&amp;source=youtube&amp;ip=0.0.0.0&amp;ipbits=0&amp;expire=19000000000&amp;sparams=ip,ipbits,expire,id,itag,source&amp;signature=70E979A621001201BC18622BDBF914FA870BDA40.6E78890B80F4A33A18835F775B1FF64F0A4D0003&amp;key=ik0&amp;user=android-device-test</value>
+    </entry>
+    <entry key="StreamingMediaPlayerTest-testHTTP_MPEG4SP_AAC_Video1">
+        <value>http://redirector.gvt1.com/videoplayback?id=271de9756065677e&amp;itag=17&amp;source=youtube&amp;ip=0.0.0.0&amp;ipbits=0&amp;expire=19000000000&amp;sparams=ip,ipbits,expire,id,itag,source&amp;signature=837198AAADF6F36BA6B2D324F690A7C5B7AFE3FF.7138CE5E36D718220726C1FC305497FF2D082249&amp;key=ik0&amp;user=android-device-test</value>
+    </entry>
+    <entry key="MediaCodecCapabilitiesTest-testAvcBaseline12">
+        <value>http://redirector.gvt1.com/videoplayback?id=271de9756065677e&amp;itag=160&amp;source=youtube&amp;user=android-device-test&amp;sparams=ip,ipbits,expire,id,itag,source,user&amp;ip=0.0.0.0&amp;ipbits=0&amp;expire=19000000000&amp;signature=9EDCA0B395B8A949C511FD5E59B9F805CFF797FD.702DE9BA7AF96785FD6930AD2DD693A0486C880E&amp;key=ik0</value>
+    </entry>
+    <entry key="DecoderTest-AUDIO_URL">
+        <value>http://redirector.gvt1.com/videoplayback?id=c80658495af60617&amp;itag=18&amp;source=youtube&amp;ip=0.0.0.0&amp;ipbits=0&amp;expire=19000000000&amp;sparams=ip,ipbits,expire,id,itag,source&amp;signature=46A04ED550CA83B79B60060BA80C79FDA5853D26.49582D382B4A9AFAA163DED38D2AE531D85603C0&amp;key=ik0&amp;user=android-device-test</value>
+    </entry>
+    <entry key="MediaCodecCapabilitiesTest-testAvcBaseline30">
+        <value>http://redirector.gvt1.com/videoplayback?id=271de9756065677e&amp;itag=18&amp;source=youtube&amp;user=android-device-test&amp;sparams=ip,ipbits,expire,id,itag,source,user&amp;ip=0.0.0.0&amp;ipbits=0&amp;expire=19000000000&amp;signature=7DCDE3A6594D0B91A27676A3CDC3A87B149F82EA.7A83031734CB1EDCE06766B6228842F954927960&amp;key=ik0</value>
+    </entry>
+    <entry key="MediaCodecCapabilitiesTest-testAvcHigh40">
+        <value>http://redirector.gvt1.com/videoplayback?id=271de9756065677e&amp;itag=137&amp;source=youtube&amp;user=android-device-test&amp;sparams=ip,ipbits,expire,id,itag,source,user&amp;ip=0.0.0.0&amp;ipbits=0&amp;expire=19000000000&amp;signature=B0976085596DD42DEA3F08307F76587241CB132B.043B719C039E8B92F45391ADC0BE3665E2332930&amp;key=ik0</value>
+    </entry>
+    <entry key="StreamingMediaPlayerTest-testHTTP_H263_AMR_Video2">
+        <value>http://redirector.gvt1.com/videoplayback?id=c80658495af60617&amp;itag=13&amp;source=youtube&amp;ip=0.0.0.0&amp;ipbits=0&amp;expire=19000000000&amp;sparams=ip,ipbits,expire,id,itag,source&amp;signature=508D82AB36939345BF6B8D0623CB6CABDD9C64C3.9B3336A96846DF38E5343C46AA57F6CF2956E427&amp;key=ik0&amp;user=android-device-test</value>
+    </entry>
+    <entry key="StreamingMediaPlayerTest-testHTTP_H263_AMR_Video1">
+        <value>http://redirector.gvt1.com/videoplayback?id=271de9756065677e&amp;itag=13&amp;source=youtube&amp;ip=0.0.0.0&amp;ipbits=0&amp;expire=19000000000&amp;sparams=ip,ipbits,expire,id,itag,source&amp;signature=5729247E22691EBB3E804DDD523EC42DC17DD8CE.443B81C1E8E6D64E4E1555F568BA46C206507D78&amp;key=ik0&amp;user=android-device-test</value>
+    </entry>
+</dynamicConfig>
diff --git a/tests/tests/media/res/raw/image_exif_byte_order_ii.jpg b/tests/tests/media/res/raw/image_exif_byte_order_ii.jpg
new file mode 100644
index 0000000..477cd3a
--- /dev/null
+++ b/tests/tests/media/res/raw/image_exif_byte_order_ii.jpg
Binary files differ
diff --git a/tests/tests/media/res/raw/image_exif_byte_order_mm.jpg b/tests/tests/media/res/raw/image_exif_byte_order_mm.jpg
new file mode 100644
index 0000000..78ac703
--- /dev/null
+++ b/tests/tests/media/res/raw/image_exif_byte_order_mm.jpg
Binary files differ
diff --git a/tests/tests/media/res/raw/lg_g4_iso_800.dng b/tests/tests/media/res/raw/lg_g4_iso_800.dng
new file mode 100644
index 0000000..5fcc720
--- /dev/null
+++ b/tests/tests/media/res/raw/lg_g4_iso_800.dng
Binary files differ
diff --git a/tests/tests/media/res/raw/okgoogle123_good.wav b/tests/tests/media/res/raw/okgoogle123_good.wav
new file mode 100644
index 0000000..ffd5a7f8
--- /dev/null
+++ b/tests/tests/media/res/raw/okgoogle123_good.wav
Binary files differ
diff --git a/tests/tests/media/res/values/exifinterface.xml b/tests/tests/media/res/values/exifinterface.xml
new file mode 100644
index 0000000..8fc6adc
--- /dev/null
+++ b/tests/tests/media/res/values/exifinterface.xml
@@ -0,0 +1,108 @@
+<?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.
+-->
+
+<resources>
+    <array name="exifbyteorderii_jpg">
+        <item>true</item>
+        <item>512</item>
+        <item>288</item>
+        <item>false</item>
+        <item>0.0</item>
+        <item>0.0</item>
+        <item>0.0</item>
+        <item>SAMSUNG</item>
+        <item>SM-N900S</item>
+        <item>2.200</item>
+        <item>2016:01:29 18:32:27</item>
+        <item>0.033</item>
+        <item>0</item>
+        <item>413/100</item>
+        <item />
+        <item />
+        <item />
+        <item />
+        <item />
+        <item />
+        <item />
+        <item />
+        <item />
+        <item>480</item>
+        <item>640</item>
+        <item>50</item>
+        <item>6</item>
+        <item>0</item>
+    </array>
+    <array name="exifbyteordermm_jpg">
+        <item>false</item>
+        <item>0</item>
+        <item>0</item>
+        <item>true</item>
+        <item>0.0</item>
+        <item>0.0</item>
+        <item>0.0</item>
+        <item>LGE</item>
+        <item>Nexus 5</item>
+        <item>2.400</item>
+        <item>2016:01:29 15:44:58</item>
+        <item>0.017</item>
+        <item>0</item>
+        <item>3970/1000</item>
+        <item>0/1000</item>
+        <item>0</item>
+        <item>1970:01:01</item>
+        <item>0/1,0/1,0/10000</item>
+        <item>N</item>
+        <item>0/1,0/1,0/10000</item>
+        <item>E</item>
+        <item>GPS</item>
+        <item>00:00:00</item>
+        <item>176</item>
+        <item>144</item>
+        <item>146</item>
+        <item>0</item>
+        <item>0</item>
+    </array>
+    <array name="lg_g4_iso_800_dng">
+        <item>false</item>
+        <item>0</item>
+        <item>0</item>
+        <item>true</item>
+        <item>53.834507</item>
+        <item>10.69585</item>
+        <item>0.0</item>
+        <item>LGE</item>
+        <item>LG-H815</item>
+        <item>1.800</item>
+        <item>2015:11:12 16:46:18</item>
+        <item>0.0040</item>
+        <item>0.0</item>
+        <item>442/100</item>
+        <item>0/1</item>
+        <item>0</item>
+        <item>1970:01:17</item>
+        <item>53/1,50/1,423/100</item>
+        <item>N</item>
+        <item>10/1,41/1,4506/100</item>
+        <item>E</item>
+        <item />
+        <item>18:08:10</item>
+        <item>337</item>
+        <item>600</item>
+        <item>800</item>
+        <item>1</item>
+        <item />
+    </array>
+</resources>
diff --git a/tests/tests/media/src/android/media/cts/EncoderTest.java b/tests/tests/media/src/android/media/cts/EncoderTest.java
index c8cea22..da81879 100644
--- a/tests/tests/media/src/android/media/cts/EncoderTest.java
+++ b/tests/tests/media/src/android/media/cts/EncoderTest.java
@@ -23,9 +23,12 @@
 import android.media.MediaCodecInfo;
 import android.media.MediaCodecList;
 import android.media.MediaFormat;
+import android.media.MediaMuxer;
 import android.test.AndroidTestCase;
 import android.util.Log;
 
+import java.io.File;
+import java.io.InputStream;
 import java.nio.BufferOverflowException;
 import java.nio.ByteBuffer;
 import java.util.LinkedList;
@@ -42,6 +45,20 @@
     private static final int kNumInputBytes = 512 * 1024;
     private static final long kTimeoutUs = 100;
 
+    // not all combinations are valid
+    private static final int MODE_SILENT = 0;
+    private static final int MODE_RANDOM = 1;
+    private static final int MODE_RESOURCE = 2;
+    private static final int MODE_QUIET = 4;
+    private static final int MODE_SILENTLEAD = 8;
+
+    /*
+     * Set this to true to save the encoding results to /data/local/tmp
+     * You will need to make /data/local/tmp writeable, run "setenforce 0",
+     * and remove files left from a previous run.
+     */
+    private static boolean sSaveResults = false;
+
     @Override
     public void setContext(Context context) {
         super.setContext(context);
@@ -136,8 +153,10 @@
         }
         try {
             pool.shutdown();
-            pool.awaitTermination(5, TimeUnit.MINUTES);
+            assertTrue("timed out waiting for encoder threads",
+                    pool.awaitTermination(5, TimeUnit.MINUTES));
         } catch (InterruptedException e) {
+            fail("interrupted while waiting for encoder threads");
         }
     }
 
@@ -172,13 +191,26 @@
 
     private int queueInputBuffer(
             MediaCodec codec, ByteBuffer[] inputBuffers, int index,
-            boolean random, boolean zeroLead) {
+            InputStream istream, int mode) {
         ByteBuffer buffer = inputBuffers[index];
         buffer.rewind();
         int size = buffer.limit();
 
-        if (random) {
-            if (zeroLead) {
+        if ((mode & MODE_RESOURCE) != 0 && istream != null) {
+            while (buffer.hasRemaining()) {
+                try {
+                    int next = istream.read();
+                    if (next < 0) {
+                        break;
+                    }
+                    buffer.put((byte) next);
+                } catch (Exception ex) {
+                    Log.i(TAG, "caught exception writing: " + ex);
+                    break;
+                }
+            }
+        } else if ((mode & MODE_RANDOM) != 0) {
+            if ((mode & MODE_SILENTLEAD) != 0) {
                 buffer.putInt(0);
                 buffer.putInt(0);
                 buffer.putInt(0);
@@ -186,6 +218,7 @@
             }
             while (true) {
                 try {
+                    int next = mRandom.nextInt();
                     buffer.putInt(mRandom.nextInt());
                 } catch (BufferOverflowException ex) {
                     break;
@@ -196,6 +229,15 @@
             buffer.put(zeroes);
         }
 
+        if ((mode & MODE_QUIET) != 0) {
+            int n = buffer.limit();
+            for (int i = 0; i < n; i += 2) {
+                short s = buffer.getShort(i);
+                s /= 8;
+                buffer.putShort(i, s);
+            }
+        }
+
         codec.queueInputBuffer(index, 0 /* offset */, size, 0 /* timeUs */, 0);
 
         return size;
@@ -224,17 +266,47 @@
     private void testEncoder(String componentName, MediaFormat format) {
         Log.i(TAG, "testEncoder " + componentName + "/" + format);
         // test with all zeroes/silence
-        testEncoder(componentName, format, false, 0, false);
+        testEncoder(componentName, format, 0, -1, MODE_SILENT);
+
+        // test with pcm input file
+        testEncoder(componentName, format, 0, R.raw.okgoogle123_good, MODE_RESOURCE);
+        testEncoder(componentName, format, 0, R.raw.okgoogle123_good, MODE_RESOURCE | MODE_QUIET);
 
         // test with random data, with and without a few leading zeroes
         for (int i = 0; i < mBadSeeds.length; i++) {
-            testEncoder(componentName, format, true, mBadSeeds[i], false);
-            testEncoder(componentName, format, true, mBadSeeds[i], true);
+            testEncoder(componentName, format, mBadSeeds[i], -1, MODE_RANDOM);
+            testEncoder(componentName, format, mBadSeeds[i], -1, MODE_RANDOM | MODE_SILENTLEAD);
         }
     }
 
-    private void testEncoder(String componentName, MediaFormat format, boolean random,
-            long startSeed, boolean zeroLead) {
+    private void testEncoder(String componentName, MediaFormat format,
+            long startSeed, int resid, int mode) {
+
+        Log.i(TAG, "testEncoder " + componentName + "/" + mode + "/" + format);
+        int sampleRate = format.getInteger(MediaFormat.KEY_SAMPLE_RATE);
+        int channelCount = format.getInteger(MediaFormat.KEY_CHANNEL_COUNT);
+        int inBitrate = sampleRate * channelCount * 16;  // bit/sec
+        int outBitrate = format.getInteger(MediaFormat.KEY_BIT_RATE);
+
+        MediaMuxer muxer = null;
+        int muxidx = -1;
+        if (sSaveResults) {
+            try {
+                String outFile = "/data/local/tmp/transcoded-" + componentName + "-" + outBitrate +
+                        "-" + mode + "-" + startSeed + ".mp4";
+                new File("outFile").delete();
+                muxer = new MediaMuxer(outFile, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
+                // The track can't be added until we have the codec specific data
+            } catch (Exception e) {
+                Log.i(TAG, "couldn't create muxer: " + e);
+            }
+        }
+
+        InputStream istream = null;
+        if ((mode & MODE_RESOURCE) != 0) {
+            istream = mContext.getResources().openRawResource(resid);
+        }
+
         mRandom.setSeed(startSeed);
         MediaCodec codec;
         try {
@@ -283,7 +355,7 @@
                         doneSubmittingInput = true;
                     } else {
                         int size = queueInputBuffer(
-                                codec, codecInputBuffers, index, random, zeroLead);
+                                codec, codecInputBuffers, index, istream, mode);
 
                         numBytesSubmitted += size;
 
@@ -302,6 +374,16 @@
             } else if (index == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
                 codecOutputBuffers = codec.getOutputBuffers();
             } else {
+                if (muxer != null) {
+                    ByteBuffer buffer = codec.getOutputBuffer(index);
+                    if (muxidx < 0) {
+                        MediaFormat trackFormat = codec.getOutputFormat();
+                        muxidx = muxer.addTrack(trackFormat);
+                        muxer.start();
+                    }
+                    muxer.writeSampleData(muxidx, buffer, info);
+                }
+
                 dequeueOutputBuffer(codec, codecOutputBuffers, index, info);
 
                 numBytesDequeued += info.size;
@@ -324,11 +406,6 @@
                     + "dequeued " + numBytesDequeued + " bytes.");
         }
 
-        int sampleRate = format.getInteger(MediaFormat.KEY_SAMPLE_RATE);
-        int channelCount = format.getInteger(MediaFormat.KEY_CHANNEL_COUNT);
-        int inBitrate = sampleRate * channelCount * 16;  // bit/sec
-        int outBitrate = format.getInteger(MediaFormat.KEY_BIT_RATE);
-
         float desiredRatio = (float)outBitrate / (float)inBitrate;
         float actualRatio = (float)numBytesDequeued / (float)numBytesSubmitted;
 
@@ -339,6 +416,11 @@
 
         codec.release();
         codec = null;
+        if (muxer != null) {
+            muxer.stop();
+            muxer.release();
+            muxer = null;
+        }
     }
 }
 
diff --git a/tests/tests/media/src/android/media/cts/ExifInterfaceTest.java b/tests/tests/media/src/android/media/cts/ExifInterfaceTest.java
new file mode 100644
index 0000000..a51dd3d
--- /dev/null
+++ b/tests/tests/media/src/android/media/cts/ExifInterfaceTest.java
@@ -0,0 +1,333 @@
+/*
+ * 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.media.cts;
+
+import android.content.res.TypedArray;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.media.ExifInterface;
+import android.os.Environment;
+import android.test.AndroidTestCase;
+import android.util.Log;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.InputStream;
+import java.io.IOException;
+import java.lang.reflect.Type;
+
+public class ExifInterfaceTest extends AndroidTestCase {
+    private static final String TAG = ExifInterface.class.getSimpleName();
+    private static final boolean VERBOSE = false;  // lots of logging
+
+    private static final double DIFFERENCE_TOLERANCE = .0001;
+    private static final int BUFFER_SIZE = 32768;
+
+    // List of files.
+    private static final String EXIF_BYTE_ORDER_II_JPEG = "ExifByteOrderII.jpg";
+    private static final String EXIF_BYTE_ORDER_MM_JPEG = "ExifByteOrderMM.jpg";
+    private static final String LG_G4_ISO_800_DNG = "lg_g4_iso_800.dng";
+    private static final int[] IMAGE_RESOURCES = new int[] {
+            R.raw.image_exif_byte_order_ii,  R.raw.image_exif_byte_order_mm, R.raw.lg_g4_iso_800 };
+    private static final String[] IMAGE_FILENAMES = new String[] {
+            EXIF_BYTE_ORDER_II_JPEG, EXIF_BYTE_ORDER_MM_JPEG, LG_G4_ISO_800_DNG };
+
+    private static final String[] EXIF_TAGS = {
+            ExifInterface.TAG_MAKE,
+            ExifInterface.TAG_MODEL,
+            ExifInterface.TAG_APERTURE,
+            ExifInterface.TAG_DATETIME,
+            ExifInterface.TAG_EXPOSURE_TIME,
+            ExifInterface.TAG_FLASH,
+            ExifInterface.TAG_FOCAL_LENGTH,
+            ExifInterface.TAG_GPS_ALTITUDE,
+            ExifInterface.TAG_GPS_ALTITUDE_REF,
+            ExifInterface.TAG_GPS_DATESTAMP,
+            ExifInterface.TAG_GPS_LATITUDE,
+            ExifInterface.TAG_GPS_LATITUDE_REF,
+            ExifInterface.TAG_GPS_LONGITUDE,
+            ExifInterface.TAG_GPS_LONGITUDE_REF,
+            ExifInterface.TAG_GPS_PROCESSING_METHOD,
+            ExifInterface.TAG_GPS_TIMESTAMP,
+            ExifInterface.TAG_IMAGE_LENGTH,
+            ExifInterface.TAG_IMAGE_WIDTH,
+            ExifInterface.TAG_ISO,
+            ExifInterface.TAG_ORIENTATION,
+            ExifInterface.TAG_WHITE_BALANCE
+    };
+
+    private static class ExpectedValue {
+        // Thumbnail information.
+        public final boolean hasThumbnail;
+        public final int thumbnailWidth;
+        public final int thumbnailHeight;
+
+        // GPS information.
+        public final boolean hasLatLong;
+        public final float latitude;
+        public final float longitude;
+        public final float altitude;
+
+        // Values.
+        public final String make;
+        public final String model;
+        public final float aperture;
+        public final String datetime;
+        public final float exposureTime;
+        public final float flash;
+        public final String focalLength;
+        public final String gpsAltitude;
+        public final String gpsAltitudeRef;
+        public final String gpsDatestamp;
+        public final String gpsLatitude;
+        public final String gpsLatitudeRef;
+        public final String gpsLongitude;
+        public final String gpsLongitudeRef;
+        public final String gpsProcessingMethod;
+        public final String gpsTimestamp;
+        public final String imageLength;
+        public final String imageWidth;
+        public final String iso;
+        public final String whiteBalance;
+        public final String orientation;
+
+        private static String getString(TypedArray typedArray, int index) {
+            String stringValue = typedArray.getString(index);
+            if (stringValue == null || stringValue.equals("")) {
+                return null;
+            }
+            return stringValue.trim();
+        }
+
+        public ExpectedValue(TypedArray typedArray) {
+            // Reads thumbnail information.
+            hasThumbnail = typedArray.getBoolean(0, false);
+            thumbnailWidth = typedArray.getInt(1, 0);
+            thumbnailHeight = typedArray.getInt(2, 0);
+
+            // Reads GPS information.
+            hasLatLong = typedArray.getBoolean(3, false);
+            latitude = typedArray.getFloat(4, 0f);
+            longitude = typedArray.getFloat(5, 0f);
+            altitude = typedArray.getFloat(6, 0f);
+
+            // Read values.
+            make = getString(typedArray, 7);
+            model = getString(typedArray, 8);
+            aperture = typedArray.getFloat(9, 0f);
+            datetime = getString(typedArray, 10);
+            exposureTime = typedArray.getFloat(11, 0f);
+            flash = typedArray.getFloat(12, 0f);
+            focalLength = getString(typedArray, 13);
+            gpsAltitude = getString(typedArray, 14);
+            gpsAltitudeRef = getString(typedArray, 15);
+            gpsDatestamp = getString(typedArray, 16);
+            gpsLatitude = getString(typedArray, 17);
+            gpsLatitudeRef = getString(typedArray, 18);
+            gpsLongitude = getString(typedArray, 19);
+            gpsLongitudeRef = getString(typedArray, 20);
+            gpsProcessingMethod = getString(typedArray, 21);
+            gpsTimestamp = getString(typedArray, 22);
+            imageLength = getString(typedArray, 23);
+            imageWidth = getString(typedArray, 24);
+            iso = getString(typedArray, 25);
+            orientation = getString(typedArray, 26);
+            whiteBalance = getString(typedArray, 27);
+
+            typedArray.recycle();
+        }
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        byte[] buffer = new byte[BUFFER_SIZE];
+
+        for (int i = 0; i < IMAGE_RESOURCES.length; ++i) {
+            String outputPath = new File(Environment.getExternalStorageDirectory(),
+                    IMAGE_FILENAMES[i]).getAbsolutePath();
+            try (InputStream inputStream = getContext().getResources().openRawResource(
+                    IMAGE_RESOURCES[i])) {
+                try (FileOutputStream outputStream = new FileOutputStream(outputPath)) {
+                    int amount;
+                    while ((amount = inputStream.read(buffer)) > 0) {
+                        outputStream.write(buffer, 0, amount);
+                    }
+                }
+            }
+        }
+        super.setUp();
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        for (int i = 0; i < IMAGE_RESOURCES.length; ++i) {
+            String imageFilePath = new File(Environment.getExternalStorageDirectory(),
+                    IMAGE_FILENAMES[i]).getAbsolutePath();
+            File imageFile = new File(imageFilePath);
+            if (imageFile.exists()) {
+                imageFile.delete();
+            }
+        }
+
+        super.tearDown();
+    }
+
+    private void printExifTagsAndValues(String fileName, ExifInterface exifInterface) {
+        // Prints thumbnail information.
+        if (exifInterface.hasThumbnail()) {
+            byte[] thumbnailBytes = exifInterface.getThumbnail();
+            if (thumbnailBytes != null) {
+                Log.v(TAG, fileName + " Thumbnail size = " + thumbnailBytes.length);
+                Bitmap bitmap = BitmapFactory.decodeByteArray(
+                        thumbnailBytes, 0, thumbnailBytes.length);
+                if (bitmap == null) {
+                    Log.e(TAG, fileName + " Corrupted thumbnail!");
+                } else {
+                    Log.v(TAG, fileName + " Thumbnail size: " + bitmap.getWidth() + ", "
+                            + bitmap.getHeight());
+                }
+            } else {
+                Log.e(TAG, fileName + " Corrupted image (no thumbnail)");
+            }
+        } else {
+            if (exifInterface.getThumbnail() != null) {
+                Log.e(TAG, fileName + " Corrupted image (a thumbnail exists)");
+            } else {
+                Log.v(TAG, fileName + " No thumbnail");
+            }
+        }
+
+        // Prints GPS information.
+        Log.v(TAG, fileName + " Altitude = " + exifInterface.getAltitude(.0));
+
+        float[] latLong = new float[2];
+        if (exifInterface.getLatLong(latLong)) {
+            Log.v(TAG, fileName + " Latitude = " + latLong[0]);
+            Log.v(TAG, fileName + " Longitude = " + latLong[1]);
+        } else {
+            Log.v(TAG, fileName + "No latlong data");
+        }
+
+        // Prints values.
+        for (String tagKey : EXIF_TAGS) {
+            String tagValue = exifInterface.getAttribute(tagKey);
+            Log.v(TAG, fileName + "Key{" + tagKey + "} = '" + tagValue + "'");
+        }
+    }
+
+    private void compareFloatTag(ExifInterface exifInterface, String tag, float expectedValue) {
+        String stringValue = exifInterface.getAttribute(tag);
+        float floatValue = 0f;
+
+        if (stringValue != null) {
+            floatValue = Float.parseFloat(stringValue);
+        }
+
+        assertEquals(expectedValue, floatValue, DIFFERENCE_TOLERANCE);
+    }
+
+    private void compareStringTag(ExifInterface exifInterface, String tag, String expectedValue) {
+        String stringValue = exifInterface.getAttribute(tag);
+        if (stringValue != null) {
+            stringValue = stringValue.trim();
+        }
+
+        assertEquals(expectedValue, stringValue);
+    }
+
+    private void compareWithExpectedValue(ExifInterface exifInterface,
+            ExpectedValue expectedValue) {
+        // Checks a thumbnail image.
+        assertEquals(expectedValue.hasThumbnail, exifInterface.hasThumbnail());
+        if (expectedValue.hasThumbnail) {
+            byte[] thumbnailBytes = exifInterface.getThumbnail();
+            assertNotNull(thumbnailBytes);
+            Bitmap thumbnailBitmap =
+                    BitmapFactory.decodeByteArray(thumbnailBytes, 0, thumbnailBytes.length);
+            assertNotNull(thumbnailBitmap);
+            assertEquals(expectedValue.thumbnailWidth, thumbnailBitmap.getWidth());
+            assertEquals(expectedValue.thumbnailHeight, thumbnailBitmap.getHeight());
+        } else {
+            assertNull(exifInterface.getThumbnail());
+        }
+
+        // Checks GPS information.
+        float[] latLong = new float[2];
+        assertEquals(expectedValue.hasLatLong, exifInterface.getLatLong(latLong));
+        if (expectedValue.hasLatLong) {
+            assertEquals(expectedValue.latitude, latLong[0], DIFFERENCE_TOLERANCE);
+            assertEquals(expectedValue.longitude, latLong[1], DIFFERENCE_TOLERANCE);
+        }
+        assertEquals(expectedValue.altitude, exifInterface.getAltitude(.0), DIFFERENCE_TOLERANCE);
+
+        // Checks values.
+        compareStringTag(exifInterface, ExifInterface.TAG_MAKE, expectedValue.make);
+        compareStringTag(exifInterface, ExifInterface.TAG_MODEL, expectedValue.model);
+        compareFloatTag(exifInterface, ExifInterface.TAG_APERTURE, expectedValue.aperture);
+        compareStringTag(exifInterface, ExifInterface.TAG_DATETIME, expectedValue.datetime);
+        compareFloatTag(exifInterface, ExifInterface.TAG_EXPOSURE_TIME, expectedValue.exposureTime);
+        compareFloatTag(exifInterface, ExifInterface.TAG_FLASH, expectedValue.flash);
+        compareStringTag(exifInterface, ExifInterface.TAG_FOCAL_LENGTH, expectedValue.focalLength);
+        compareStringTag(exifInterface, ExifInterface.TAG_GPS_ALTITUDE, expectedValue.gpsAltitude);
+        compareStringTag(exifInterface, ExifInterface.TAG_GPS_ALTITUDE_REF,
+                expectedValue.gpsAltitudeRef);
+        compareStringTag(exifInterface, ExifInterface.TAG_GPS_DATESTAMP,
+                expectedValue.gpsDatestamp);
+        compareStringTag(exifInterface, ExifInterface.TAG_GPS_LATITUDE, expectedValue.gpsLatitude);
+        compareStringTag(exifInterface, ExifInterface.TAG_GPS_LATITUDE_REF,
+                expectedValue.gpsLatitudeRef);
+        compareStringTag(exifInterface, ExifInterface.TAG_GPS_LONGITUDE,
+                expectedValue.gpsLongitude);
+        compareStringTag(exifInterface, ExifInterface.TAG_GPS_LONGITUDE_REF,
+                expectedValue.gpsLongitudeRef);
+        compareStringTag(exifInterface, ExifInterface.TAG_GPS_PROCESSING_METHOD,
+                expectedValue.gpsProcessingMethod);
+        compareStringTag(exifInterface, ExifInterface.TAG_GPS_TIMESTAMP,
+                expectedValue.gpsTimestamp);
+        compareStringTag(exifInterface, ExifInterface.TAG_IMAGE_LENGTH, expectedValue.imageLength);
+        compareStringTag(exifInterface, ExifInterface.TAG_IMAGE_WIDTH, expectedValue.imageWidth);
+        compareStringTag(exifInterface, ExifInterface.TAG_ISO, expectedValue.iso);
+        compareStringTag(exifInterface, ExifInterface.TAG_ORIENTATION, expectedValue.orientation);
+        compareStringTag(exifInterface, ExifInterface.TAG_WHITE_BALANCE,
+                expectedValue.whiteBalance);
+    }
+
+    private void testExifInterface(String fileName, int typedArrayResourceId) throws IOException {
+        ExifInterface exifInterface = new ExifInterface(new File(Environment
+                .getExternalStorageDirectory(), fileName).getAbsolutePath());
+
+        assertNotNull(exifInterface);
+        if (VERBOSE) {
+            printExifTagsAndValues(fileName, exifInterface);
+        }
+
+        compareWithExpectedValue(exifInterface, new ExpectedValue(
+                getContext().getResources().obtainTypedArray(typedArrayResourceId)));
+    }
+
+    public void testReadExifDataFromExifByteOrderIIJpeg() throws Throwable {
+        testExifInterface(EXIF_BYTE_ORDER_II_JPEG, R.array.exifbyteorderii_jpg);
+    }
+
+    public void testReadExifDataFromExifByteOrderMMJpeg() throws Throwable {
+        testExifInterface(EXIF_BYTE_ORDER_MM_JPEG, R.array.exifbyteordermm_jpg);
+    }
+
+    public void testReadExifDataFromLgG4Iso800Dng() throws Throwable {
+        testExifInterface(LG_G4_ISO_800_DNG, R.array.lg_g4_iso_800_dng);
+    }
+}
diff --git a/tests/tests/media/src/android/media/cts/ExtractDecodeEditEncodeMuxTest.java b/tests/tests/media/src/android/media/cts/ExtractDecodeEditEncodeMuxTest.java
index 1af825c..72a74c1 100644
--- a/tests/tests/media/src/android/media/cts/ExtractDecodeEditEncodeMuxTest.java
+++ b/tests/tests/media/src/android/media/cts/ExtractDecodeEditEncodeMuxTest.java
@@ -18,6 +18,7 @@
 
 import android.annotation.TargetApi;
 import android.content.res.AssetFileDescriptor;
+import android.content.Context;
 import android.media.MediaCodec;
 import android.media.MediaCodecInfo;
 import android.media.MediaCodecInfo.CodecCapabilities;
@@ -26,8 +27,9 @@
 import android.media.MediaExtractor;
 import android.media.MediaFormat;
 import android.media.MediaMuxer;
+import android.media.MediaPlayer;
 import android.os.Environment;
-import android.test.AndroidTestCase;
+import android.test.ActivityInstrumentationTestCase2;
 import android.util.Log;
 import android.view.Surface;
 
@@ -41,6 +43,7 @@
 import java.io.IOException;
 import java.nio.ByteBuffer;
 import java.util.concurrent.atomic.AtomicReference;
+import java.util.concurrent.CountDownLatch;
 
 /**
  * Test for the integration of MediaMuxer and MediaCodec's encoder.
@@ -56,7 +59,8 @@
  * MediaMuxer.
  */
 @TargetApi(18)
-public class ExtractDecodeEditEncodeMuxTest extends AndroidTestCase {
+public class ExtractDecodeEditEncodeMuxTest
+        extends ActivityInstrumentationTestCase2<MediaStubActivity> {
 
     private static final String TAG = ExtractDecodeEditEncodeMuxTest.class.getSimpleName();
     private static final boolean VERBOSE = false; // lots of logging
@@ -68,8 +72,6 @@
     private static final File OUTPUT_FILENAME_DIR = Environment.getExternalStorageDirectory();
 
     // parameters for the video encoder
-                                                                // H.264 Advanced Video Coding
-    private static final String OUTPUT_VIDEO_MIME_TYPE = MediaFormat.MIMETYPE_VIDEO_AVC;
     private static final int OUTPUT_VIDEO_BIT_RATE = 2000000;   // 2Mbps
     private static final int OUTPUT_VIDEO_FRAME_RATE = 15;      // 15fps
     private static final int OUTPUT_VIDEO_IFRAME_INTERVAL = 10; // 10 seconds between I-frames
@@ -116,10 +118,17 @@
     /** The destination file for the encoded output. */
     private String mOutputFile;
 
+    private String mOutputVideoMimeType;
+
+    public ExtractDecodeEditEncodeMuxTest() {
+        super(MediaStubActivity.class);
+    }
+
     public void testExtractDecodeEditEncodeMuxQCIF() throws Throwable {
         if(!setSize(176, 144)) return;
         setSource(R.raw.video_480x360_mp4_h264_500kbps_30fps_aac_stereo_128kbps_44100hz);
         setCopyVideo();
+        setVideoMimeType(MediaFormat.MIMETYPE_VIDEO_AVC);
         TestWrapper.runTest(this);
     }
 
@@ -127,6 +136,7 @@
         if(!setSize(320, 240)) return;
         setSource(R.raw.video_480x360_mp4_h264_500kbps_30fps_aac_stereo_128kbps_44100hz);
         setCopyVideo();
+        setVideoMimeType(MediaFormat.MIMETYPE_VIDEO_AVC);
         TestWrapper.runTest(this);
     }
 
@@ -134,6 +144,15 @@
         if(!setSize(1280, 720)) return;
         setSource(R.raw.video_480x360_mp4_h264_500kbps_30fps_aac_stereo_128kbps_44100hz);
         setCopyVideo();
+        setVideoMimeType(MediaFormat.MIMETYPE_VIDEO_AVC);
+        TestWrapper.runTest(this);
+    }
+
+    public void testExtractDecodeEditEncodeMux2160pHevc() throws Throwable {
+        if(!setSize(3840, 2160)) return;
+        setSource(R.raw.video_480x360_mp4_h264_500kbps_30fps_aac_stereo_128kbps_44100hz);
+        setCopyVideo();
+        setVideoMimeType(MediaFormat.MIMETYPE_VIDEO_HEVC);
         TestWrapper.runTest(this);
     }
 
@@ -222,7 +241,7 @@
         mWidth = width;
         mHeight = height;
 
-	// TODO: remove this logic in setSize as it is now handled when configuring codecs
+        // TODO: remove this logic in setSize as it is now handled when configuring codecs
         return true;
     }
 
@@ -264,6 +283,10 @@
         mOutputFile = sb.toString();
     }
 
+    private void setVideoMimeType(String mimeType) {
+        mOutputVideoMimeType = mimeType;
+    }
+
     /**
      * Tests encoding and subsequently decoding video from frames generated into a buffer.
      * <p>
@@ -279,7 +302,7 @@
         // We avoid the device-specific limitations on width and height by using values
         // that are multiples of 16, which all tested devices seem to be able to handle.
         MediaFormat outputVideoFormat =
-                MediaFormat.createVideoFormat(OUTPUT_VIDEO_MIME_TYPE, mWidth, mHeight);
+                MediaFormat.createVideoFormat(mOutputVideoMimeType, mWidth, mHeight);
 
         // Set some properties. Failing to specify some of these can cause the MediaCodec
         // configure() call to throw an unhelpful exception.
@@ -304,7 +327,8 @@
                         OUTPUT_AUDIO_MIME_TYPE, OUTPUT_AUDIO_SAMPLE_RATE_HZ,
                         OUTPUT_AUDIO_CHANNEL_COUNT);
         outputAudioFormat.setInteger(MediaFormat.KEY_BIT_RATE, OUTPUT_AUDIO_BIT_RATE);
-        outputAudioFormat.setInteger(MediaFormat.KEY_AAC_PROFILE, OUTPUT_AUDIO_AAC_PROFILE);
+        // TODO: Bug workaround --- uncomment once fixed.
+        // outputAudioFormat.setInteger(MediaFormat.KEY_AAC_PROFILE, OUTPUT_AUDIO_AAC_PROFILE);
 
         String audioEncoderName = mcl.findEncoderForFormat(outputAudioFormat);
         if (audioEncoderName == null) {
@@ -514,6 +538,42 @@
         }
 
         // TODO: Check the generated output file's video format and sample data.
+
+        MediaStubActivity activity = getActivity();
+        final MediaPlayer mp = new MediaPlayer();
+        final Exception[] exceptionHolder = { null };
+        final CountDownLatch playbackEndSignal = new CountDownLatch(1);
+        mp.setOnErrorListener(new MediaPlayer.OnErrorListener() {
+            @Override
+            public boolean onError(MediaPlayer origin, int what, int extra) {
+                exceptionHolder[0] = new RuntimeException("error playing output file: what=" + what
+                        + " extra=" + extra);
+                // Returning false would trigger onCompletion() so that
+                // playbackEndSignal.await() can stop waiting.
+                return false;
+            }
+        });
+        mp.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
+            @Override
+            public void onCompletion(MediaPlayer origin) {
+                playbackEndSignal.countDown();
+            }
+        });
+        try {
+            mp.setDataSource(mOutputFile);
+            mp.setDisplay(activity.getSurfaceHolder());
+            mp.prepare();
+            mp.start();
+            playbackEndSignal.await();
+        } catch (Exception e) {
+            exceptionHolder[0] = e;
+        } finally {
+            mp.release();
+        }
+
+        if (exceptionHolder[0] != null) {
+            throw exceptionHolder[0];
+        }
     }
 
     /**
@@ -521,7 +581,8 @@
      */
     private MediaExtractor createExtractor() throws IOException {
         MediaExtractor extractor;
-        AssetFileDescriptor srcFd = getContext().getResources().openRawResourceFd(mSourceResId);
+        Context context = getInstrumentation().getTargetContext();
+        AssetFileDescriptor srcFd = context.getResources().openRawResourceFd(mSourceResId);
         extractor = new MediaExtractor();
         extractor.setDataSource(srcFd.getFileDescriptor(), srcFd.getStartOffset(),
                 srcFd.getLength());
@@ -1166,64 +1227,4 @@
         }
         return null;
     }
-
-  /**
-   * Checks whether the given resolution is supported by the AVC codec.
-   */
-    private static boolean isAvcSupportedSize(int width, int height) {
-        MediaCodecInfo mediaCodecInfo = selectCodec(OUTPUT_VIDEO_MIME_TYPE);
-        CodecCapabilities cap = mediaCodecInfo.getCapabilitiesForType(OUTPUT_VIDEO_MIME_TYPE);
-        if (cap == null) { // not supported
-            return false;
-        }
-        int highestLevel = 0;
-        for (CodecProfileLevel lvl : cap.profileLevels) {
-            if (lvl.level > highestLevel) {
-                highestLevel = lvl.level;
-            }
-        }
-        int maxW = 0;
-        int maxH = 0;
-        int bitRate = 0;
-        int fps = 0; // frame rate for the max resolution
-        switch(highestLevel) {
-            // Do not support Level 1 to 2.
-            case CodecProfileLevel.AVCLevel1:
-            case CodecProfileLevel.AVCLevel11:
-            case CodecProfileLevel.AVCLevel12:
-            case CodecProfileLevel.AVCLevel13:
-            case CodecProfileLevel.AVCLevel1b:
-            case CodecProfileLevel.AVCLevel2:
-                return false;
-            case CodecProfileLevel.AVCLevel21:
-                maxW = 352;
-                maxH = 576;
-                break;
-            case CodecProfileLevel.AVCLevel22:
-                maxW = 720;
-                maxH = 480;
-                break;
-            case CodecProfileLevel.AVCLevel3:
-                maxW = 720;
-                maxH = 480;
-                break;
-            case CodecProfileLevel.AVCLevel31:
-                maxW = 1280;
-                maxH = 720;
-                break;
-            case CodecProfileLevel.AVCLevel32:
-                maxW = 1280;
-                maxH = 720;
-                break;
-            case CodecProfileLevel.AVCLevel4: // only try up to 1080p
-            default:
-                maxW = 1920;
-                maxH = 1080;
-                break;
-        }
-        if(maxW*maxH < width*height)
-            return false;
-        else
-            return true;
-    }
 }
diff --git a/tests/tests/media/src/android/media/cts/MediaRecorderTest.java b/tests/tests/media/src/android/media/cts/MediaRecorderTest.java
index e7ae935..8d94a25 100644
--- a/tests/tests/media/src/android/media/cts/MediaRecorderTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaRecorderTest.java
@@ -15,7 +15,6 @@
  */
 package android.media.cts;
 
-
 import android.content.pm.PackageManager;
 import android.cts.util.MediaUtils;
 import android.graphics.Canvas;
@@ -209,20 +208,28 @@
 
     @UiThreadTest
     public void testSetCamera() throws Exception {
-        recordVideoUsingCamera(false);
+        recordVideoUsingCamera(false, false);
     }
 
     public void testRecorderTimelapsedVideo() throws Exception {
-        recordVideoUsingCamera(true);
+        recordVideoUsingCamera(true, false);
     }
 
-    private void recordVideoUsingCamera(boolean timelapse) throws Exception {
+    public void testRecorderPauseResume() throws Exception {
+        recordVideoUsingCamera(false, true);
+    }
+
+    public void testRecorderPauseResumeOnTimeLapse() throws Exception {
+        recordVideoUsingCamera(true, true);
+    }
+
+    private void recordVideoUsingCamera(boolean timelapse, boolean pause) throws Exception {
         int nCamera = Camera.getNumberOfCameras();
         int durMs = timelapse? RECORD_TIME_LAPSE_MS: RECORD_TIME_MS;
         for (int cameraId = 0; cameraId < nCamera; cameraId++) {
             mCamera = Camera.open(cameraId);
             setSupportedResolution(mCamera);
-            recordVideoUsingCamera(mCamera, OUTPUT_PATH, durMs, timelapse);
+            recordVideoUsingCamera(mCamera, OUTPUT_PATH, durMs, timelapse, pause);
             mCamera.release();
             mCamera = null;
             assertTrue(checkLocationInFile(OUTPUT_PATH));
@@ -245,7 +252,8 @@
     }
 
     private void recordVideoUsingCamera(
-            Camera camera, String fileName, int durMs, boolean timelapse) throws Exception {
+            Camera camera, String fileName, int durMs, boolean timelapse, boolean pause)
+        throws Exception {
         // FIXME:
         // We should add some test case to use Camera.Parameters.getPreviewFpsRange()
         // to get the supported video frame rate range.
@@ -271,7 +279,15 @@
 
         mMediaRecorder.prepare();
         mMediaRecorder.start();
-        Thread.sleep(durMs);
+        if (pause) {
+            Thread.sleep(durMs / 2);
+            mMediaRecorder.pause();
+            Thread.sleep(durMs / 2);
+            mMediaRecorder.resume();
+            Thread.sleep(durMs / 2);
+        } else {
+            Thread.sleep(durMs);
+        }
         mMediaRecorder.stop();
         assertTrue(mOutFile.exists());
 
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 ab5b65d..b996e4a 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
@@ -229,7 +229,7 @@
         URL url;
         try {
             DynamicConfigHostSide config = new DynamicConfigHostSide(DYNAMIC_CONFIG_MODULE);
-            String mediaUrlString = config.getConfig(MEDIA_FILES_URL_KEY);
+            String mediaUrlString = config.getValue(MEDIA_FILES_URL_KEY);
             url = new URL(mediaUrlString);
         } catch (IOException | XmlPullParserException e) {
             throw new TargetSetupError("Trouble finding media file download location with " +
diff --git a/tests/tests/net/src/android/net/wifi/cts/ConcurrencyTest.java b/tests/tests/net/src/android/net/wifi/cts/ConcurrencyTest.java
index 343c1e6..a066ba8 100644
--- a/tests/tests/net/src/android/net/wifi/cts/ConcurrencyTest.java
+++ b/tests/tests/net/src/android/net/wifi/cts/ConcurrencyTest.java
@@ -20,12 +20,20 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.net.ConnectivityManager;
+import android.net.ConnectivityManager.NetworkCallback;
+import android.net.Network;
+import android.net.NetworkCapabilities;
+import android.net.NetworkRequest;
 import android.net.wifi.WifiManager;
 import android.net.wifi.p2p.WifiP2pManager;
 import static android.net.wifi.p2p.WifiP2pManager.WIFI_P2P_STATE_DISABLED;
 import static android.net.wifi.p2p.WifiP2pManager.WIFI_P2P_STATE_ENABLED;
 import android.test.AndroidTestCase;
 
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
 public class ConcurrencyTest extends AndroidTestCase {
     private class MySync {
         int expectedWifiState;
@@ -94,10 +102,7 @@
         }
         mContext.unregisterReceiver(mReceiver);
 
-        if (!mWifiManager.isWifiEnabled()) {
-            assertTrue(mWifiManager.setWifiEnabled(true));
-            Thread.sleep(DURATION);
-        }
+        enableWifi();
         super.tearDown();
     }
 
@@ -114,6 +119,33 @@
         }
     }
 
+    /*
+     * Enables Wifi and block until connection is established.
+     */
+    private void enableWifi() throws InterruptedException {
+        if (!mWifiManager.isWifiEnabled()) {
+            assertTrue(mWifiManager.setWifiEnabled(true));
+        }
+
+        ConnectivityManager cm =
+            (ConnectivityManager) getContext().getSystemService(Context.CONNECTIVITY_SERVICE);
+        NetworkRequest request =
+            new NetworkRequest.Builder().addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
+                                        .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
+                                        .build();
+        final CountDownLatch latch = new CountDownLatch(1);
+        NetworkCallback networkCallback = new NetworkCallback() {
+            @Override
+            public void onAvailable(Network network) {
+                latch.countDown();
+            }
+        };
+        cm.registerNetworkCallback(request, networkCallback);
+        latch.await(DURATION, TimeUnit.MILLISECONDS);
+
+        cm.unregisterNetworkCallback(networkCallback);
+    }
+
     public void testConcurrency() {
         // Cannot support p2p alone
         if (!WifiFeature.isWifiSupported(getContext())) {
diff --git a/tests/tests/net/src/android/net/wifi/cts/WifiEnterpriseConfigTest.java b/tests/tests/net/src/android/net/wifi/cts/WifiEnterpriseConfigTest.java
index 2cc5951..f3eb4e9 100644
--- a/tests/tests/net/src/android/net/wifi/cts/WifiEnterpriseConfigTest.java
+++ b/tests/tests/net/src/android/net/wifi/cts/WifiEnterpriseConfigTest.java
@@ -25,6 +25,10 @@
 import android.net.wifi.WifiManager;
 import android.test.AndroidTestCase;
 
+import java.io.ByteArrayInputStream;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
+
 public class WifiEnterpriseConfigTest extends AndroidTestCase {
     private  WifiManager mWifiManager;
 
@@ -39,6 +43,396 @@
     private static final String ANON_IDENTITY = "anonidentity";
     private static final int ENABLE_DELAY = 10000;
 
+    /*
+     * The keys and certificates below are generated with:
+     *
+     * openssl req -new -x509 -days 3650 -extensions v3_ca -keyout cakey.pem -out cacert.pem
+     * openssl ecparam -name prime256v1 -out ecparam.pem
+     * openssl req -newkey ec:ecparam.pem -keyout userkey.pem -nodes -days 3650 -out userkey.req
+     * mkdir -p demoCA/newcerts
+     * touch demoCA/index.txt
+     * echo "01" > demoCA/serial
+     * openssl ca -out usercert.pem -in userkey.req -cert cacert.pem -keyfile cakey.pem -days 3650
+     */
+
+    /**
+     * Generated from above and converted with:
+     *
+     * openssl x509 -outform d -in cacert.pem | xxd -i | sed 's/0x/(byte) 0x/g'
+     */
+
+    private static final byte[] FAKE_EC_1 = {
+        (byte) 0x30, (byte) 0x82, (byte) 0x04, (byte) 0x2f, (byte) 0x30, (byte) 0x82,
+        (byte) 0x03, (byte) 0x17, (byte) 0xa0, (byte) 0x03, (byte) 0x02, (byte) 0x01,
+        (byte) 0x02, (byte) 0x02, (byte) 0x09, (byte) 0x00, (byte) 0xa7, (byte) 0xe4,
+        (byte) 0x70, (byte) 0x50, (byte) 0x9b, (byte) 0xd2, (byte) 0x68, (byte) 0x68,
+        (byte) 0x30, (byte) 0x0d, (byte) 0x06, (byte) 0x09, (byte) 0x2a, (byte) 0x86,
+        (byte) 0x48, (byte) 0x86, (byte) 0xf7, (byte) 0x0d, (byte) 0x01, (byte) 0x01,
+        (byte) 0x0b, (byte) 0x05, (byte) 0x00, (byte) 0x30, (byte) 0x81, (byte) 0xad,
+        (byte) 0x31, (byte) 0x0b, (byte) 0x30, (byte) 0x09, (byte) 0x06, (byte) 0x03,
+        (byte) 0x55, (byte) 0x04, (byte) 0x06, (byte) 0x13, (byte) 0x02, (byte) 0x41,
+        (byte) 0x55, (byte) 0x31, (byte) 0x13, (byte) 0x30, (byte) 0x11, (byte) 0x06,
+        (byte) 0x03, (byte) 0x55, (byte) 0x04, (byte) 0x08, (byte) 0x0c, (byte) 0x0a,
+        (byte) 0x53, (byte) 0x6f, (byte) 0x6d, (byte) 0x65, (byte) 0x2d, (byte) 0x53,
+        (byte) 0x74, (byte) 0x61, (byte) 0x74, (byte) 0x65, (byte) 0x31, (byte) 0x12,
+        (byte) 0x30, (byte) 0x10, (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x04,
+        (byte) 0x07, (byte) 0x0c, (byte) 0x09, (byte) 0x53, (byte) 0x6f, (byte) 0x6d,
+        (byte) 0x65, (byte) 0x2d, (byte) 0x43, (byte) 0x69, (byte) 0x74, (byte) 0x79,
+        (byte) 0x31, (byte) 0x15, (byte) 0x30, (byte) 0x13, (byte) 0x06, (byte) 0x03,
+        (byte) 0x55, (byte) 0x04, (byte) 0x0a, (byte) 0x0c, (byte) 0x0c, (byte) 0x53,
+        (byte) 0x6f, (byte) 0x6d, (byte) 0x65, (byte) 0x2d, (byte) 0x43, (byte) 0x6f,
+        (byte) 0x6d, (byte) 0x70, (byte) 0x61, (byte) 0x6e, (byte) 0x79, (byte) 0x31,
+        (byte) 0x10, (byte) 0x30, (byte) 0x0e, (byte) 0x06, (byte) 0x03, (byte) 0x55,
+        (byte) 0x04, (byte) 0x0b, (byte) 0x0c, (byte) 0x07, (byte) 0x53, (byte) 0x65,
+        (byte) 0x63, (byte) 0x74, (byte) 0x69, (byte) 0x6f, (byte) 0x6e, (byte) 0x31,
+        (byte) 0x21, (byte) 0x30, (byte) 0x1f, (byte) 0x06, (byte) 0x03, (byte) 0x55,
+        (byte) 0x04, (byte) 0x03, (byte) 0x0c, (byte) 0x18, (byte) 0x57, (byte) 0x69,
+        (byte) 0x66, (byte) 0x69, (byte) 0x45, (byte) 0x6e, (byte) 0x74, (byte) 0x65,
+        (byte) 0x72, (byte) 0x70, (byte) 0x72, (byte) 0x69, (byte) 0x73, (byte) 0x65,
+        (byte) 0x43, (byte) 0x6f, (byte) 0x6e, (byte) 0x66, (byte) 0x69, (byte) 0x67,
+        (byte) 0x54, (byte) 0x65, (byte) 0x73, (byte) 0x74, (byte) 0x31, (byte) 0x29,
+        (byte) 0x30, (byte) 0x27, (byte) 0x06, (byte) 0x09, (byte) 0x2a, (byte) 0x86,
+        (byte) 0x48, (byte) 0x86, (byte) 0xf7, (byte) 0x0d, (byte) 0x01, (byte) 0x09,
+        (byte) 0x01, (byte) 0x16, (byte) 0x1a, (byte) 0x61, (byte) 0x6e, (byte) 0x2d,
+        (byte) 0x65, (byte) 0x6d, (byte) 0x61, (byte) 0x69, (byte) 0x6c, (byte) 0x2d,
+        (byte) 0x61, (byte) 0x64, (byte) 0x72, (byte) 0x65, (byte) 0x73, (byte) 0x73,
+        (byte) 0x40, (byte) 0x64, (byte) 0x6f, (byte) 0x6d, (byte) 0x61, (byte) 0x69,
+        (byte) 0x6e, (byte) 0x2e, (byte) 0x63, (byte) 0x6f, (byte) 0x6d, (byte) 0x30,
+        (byte) 0x1e, (byte) 0x17, (byte) 0x0d, (byte) 0x31, (byte) 0x36, (byte) 0x30,
+        (byte) 0x31, (byte) 0x31, (byte) 0x35, (byte) 0x31, (byte) 0x31, (byte) 0x31,
+        (byte) 0x38, (byte) 0x35, (byte) 0x31, (byte) 0x5a, (byte) 0x17, (byte) 0x0d,
+        (byte) 0x32, (byte) 0x36, (byte) 0x30, (byte) 0x31, (byte) 0x31, (byte) 0x32,
+        (byte) 0x31, (byte) 0x31, (byte) 0x31, (byte) 0x38, (byte) 0x35, (byte) 0x31,
+        (byte) 0x5a, (byte) 0x30, (byte) 0x81, (byte) 0xad, (byte) 0x31, (byte) 0x0b,
+        (byte) 0x30, (byte) 0x09, (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x04,
+        (byte) 0x06, (byte) 0x13, (byte) 0x02, (byte) 0x41, (byte) 0x55, (byte) 0x31,
+        (byte) 0x13, (byte) 0x30, (byte) 0x11, (byte) 0x06, (byte) 0x03, (byte) 0x55,
+        (byte) 0x04, (byte) 0x08, (byte) 0x0c, (byte) 0x0a, (byte) 0x53, (byte) 0x6f,
+        (byte) 0x6d, (byte) 0x65, (byte) 0x2d, (byte) 0x53, (byte) 0x74, (byte) 0x61,
+        (byte) 0x74, (byte) 0x65, (byte) 0x31, (byte) 0x12, (byte) 0x30, (byte) 0x10,
+        (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x04, (byte) 0x07, (byte) 0x0c,
+        (byte) 0x09, (byte) 0x53, (byte) 0x6f, (byte) 0x6d, (byte) 0x65, (byte) 0x2d,
+        (byte) 0x43, (byte) 0x69, (byte) 0x74, (byte) 0x79, (byte) 0x31, (byte) 0x15,
+        (byte) 0x30, (byte) 0x13, (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x04,
+        (byte) 0x0a, (byte) 0x0c, (byte) 0x0c, (byte) 0x53, (byte) 0x6f, (byte) 0x6d,
+        (byte) 0x65, (byte) 0x2d, (byte) 0x43, (byte) 0x6f, (byte) 0x6d, (byte) 0x70,
+        (byte) 0x61, (byte) 0x6e, (byte) 0x79, (byte) 0x31, (byte) 0x10, (byte) 0x30,
+        (byte) 0x0e, (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x04, (byte) 0x0b,
+        (byte) 0x0c, (byte) 0x07, (byte) 0x53, (byte) 0x65, (byte) 0x63, (byte) 0x74,
+        (byte) 0x69, (byte) 0x6f, (byte) 0x6e, (byte) 0x31, (byte) 0x21, (byte) 0x30,
+        (byte) 0x1f, (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x04, (byte) 0x03,
+        (byte) 0x0c, (byte) 0x18, (byte) 0x57, (byte) 0x69, (byte) 0x66, (byte) 0x69,
+        (byte) 0x45, (byte) 0x6e, (byte) 0x74, (byte) 0x65, (byte) 0x72, (byte) 0x70,
+        (byte) 0x72, (byte) 0x69, (byte) 0x73, (byte) 0x65, (byte) 0x43, (byte) 0x6f,
+        (byte) 0x6e, (byte) 0x66, (byte) 0x69, (byte) 0x67, (byte) 0x54, (byte) 0x65,
+        (byte) 0x73, (byte) 0x74, (byte) 0x31, (byte) 0x29, (byte) 0x30, (byte) 0x27,
+        (byte) 0x06, (byte) 0x09, (byte) 0x2a, (byte) 0x86, (byte) 0x48, (byte) 0x86,
+        (byte) 0xf7, (byte) 0x0d, (byte) 0x01, (byte) 0x09, (byte) 0x01, (byte) 0x16,
+        (byte) 0x1a, (byte) 0x61, (byte) 0x6e, (byte) 0x2d, (byte) 0x65, (byte) 0x6d,
+        (byte) 0x61, (byte) 0x69, (byte) 0x6c, (byte) 0x2d, (byte) 0x61, (byte) 0x64,
+        (byte) 0x72, (byte) 0x65, (byte) 0x73, (byte) 0x73, (byte) 0x40, (byte) 0x64,
+        (byte) 0x6f, (byte) 0x6d, (byte) 0x61, (byte) 0x69, (byte) 0x6e, (byte) 0x2e,
+        (byte) 0x63, (byte) 0x6f, (byte) 0x6d, (byte) 0x30, (byte) 0x82, (byte) 0x01,
+        (byte) 0x22, (byte) 0x30, (byte) 0x0d, (byte) 0x06, (byte) 0x09, (byte) 0x2a,
+        (byte) 0x86, (byte) 0x48, (byte) 0x86, (byte) 0xf7, (byte) 0x0d, (byte) 0x01,
+        (byte) 0x01, (byte) 0x01, (byte) 0x05, (byte) 0x00, (byte) 0x03, (byte) 0x82,
+        (byte) 0x01, (byte) 0x0f, (byte) 0x00, (byte) 0x30, (byte) 0x82, (byte) 0x01,
+        (byte) 0x0a, (byte) 0x02, (byte) 0x82, (byte) 0x01, (byte) 0x01, (byte) 0x00,
+        (byte) 0xb4, (byte) 0x6e, (byte) 0x66, (byte) 0x24, (byte) 0xe7, (byte) 0x5c,
+        (byte) 0xd8, (byte) 0x6f, (byte) 0x08, (byte) 0xd3, (byte) 0x80, (byte) 0xa3,
+        (byte) 0xb9, (byte) 0xaf, (byte) 0x90, (byte) 0xef, (byte) 0x1c, (byte) 0x2a,
+        (byte) 0x5f, (byte) 0x39, (byte) 0x0b, (byte) 0xbd, (byte) 0x75, (byte) 0x0d,
+        (byte) 0x3e, (byte) 0x19, (byte) 0x2e, (byte) 0x47, (byte) 0x1e, (byte) 0x14,
+        (byte) 0xc2, (byte) 0x1a, (byte) 0x59, (byte) 0xcc, (byte) 0x1b, (byte) 0xb6,
+        (byte) 0x9b, (byte) 0x46, (byte) 0x1f, (byte) 0x7f, (byte) 0x71, (byte) 0xdd,
+        (byte) 0x38, (byte) 0xbe, (byte) 0x89, (byte) 0x30, (byte) 0xba, (byte) 0x88,
+        (byte) 0xfb, (byte) 0x3f, (byte) 0x57, (byte) 0x35, (byte) 0xe7, (byte) 0xa7,
+        (byte) 0x2f, (byte) 0x2c, (byte) 0x8d, (byte) 0x7c, (byte) 0xe2, (byte) 0xd8,
+        (byte) 0x0c, (byte) 0x0a, (byte) 0xe6, (byte) 0x62, (byte) 0x46, (byte) 0x8c,
+        (byte) 0xf4, (byte) 0x51, (byte) 0xfc, (byte) 0x6a, (byte) 0x79, (byte) 0xdd,
+        (byte) 0x0a, (byte) 0x41, (byte) 0x23, (byte) 0xd3, (byte) 0xe9, (byte) 0x5e,
+        (byte) 0x91, (byte) 0xcd, (byte) 0xbd, (byte) 0x55, (byte) 0x28, (byte) 0x71,
+        (byte) 0xec, (byte) 0x52, (byte) 0x19, (byte) 0x85, (byte) 0x0c, (byte) 0x1b,
+        (byte) 0xfa, (byte) 0xbf, (byte) 0xfe, (byte) 0xae, (byte) 0x5c, (byte) 0x3b,
+        (byte) 0x99, (byte) 0x42, (byte) 0xd4, (byte) 0xe7, (byte) 0x17, (byte) 0xec,
+        (byte) 0x41, (byte) 0x22, (byte) 0x2c, (byte) 0x1e, (byte) 0x7b, (byte) 0x53,
+        (byte) 0xad, (byte) 0x02, (byte) 0xfd, (byte) 0xf6, (byte) 0x4a, (byte) 0xb1,
+        (byte) 0x6e, (byte) 0x6c, (byte) 0x87, (byte) 0xf5, (byte) 0x7d, (byte) 0x9b,
+        (byte) 0x34, (byte) 0x0e, (byte) 0x3b, (byte) 0x0e, (byte) 0xaa, (byte) 0xc5,
+        (byte) 0xc4, (byte) 0xef, (byte) 0xf2, (byte) 0x5a, (byte) 0xa9, (byte) 0xac,
+        (byte) 0x19, (byte) 0xce, (byte) 0x5f, (byte) 0xc5, (byte) 0xcc, (byte) 0x0d,
+        (byte) 0xee, (byte) 0x7f, (byte) 0x32, (byte) 0xb4, (byte) 0xfe, (byte) 0xc1,
+        (byte) 0xca, (byte) 0x9b, (byte) 0x3f, (byte) 0xad, (byte) 0x2c, (byte) 0x7a,
+        (byte) 0xc5, (byte) 0x8d, (byte) 0x48, (byte) 0xa1, (byte) 0xc9, (byte) 0x74,
+        (byte) 0xfe, (byte) 0x8a, (byte) 0xe3, (byte) 0xb0, (byte) 0x92, (byte) 0xee,
+        (byte) 0x73, (byte) 0x09, (byte) 0x0a, (byte) 0xbc, (byte) 0xc8, (byte) 0x63,
+        (byte) 0xba, (byte) 0x0e, (byte) 0x26, (byte) 0xab, (byte) 0x1e, (byte) 0xff,
+        (byte) 0xbc, (byte) 0x24, (byte) 0x12, (byte) 0x26, (byte) 0x11, (byte) 0xe0,
+        (byte) 0x04, (byte) 0xcb, (byte) 0x96, (byte) 0x7d, (byte) 0x41, (byte) 0xf7,
+        (byte) 0x79, (byte) 0x32, (byte) 0x05, (byte) 0x33, (byte) 0x19, (byte) 0x6e,
+        (byte) 0xb9, (byte) 0x75, (byte) 0xf3, (byte) 0x50, (byte) 0xa4, (byte) 0xc3,
+        (byte) 0x55, (byte) 0x9d, (byte) 0x8f, (byte) 0xb6, (byte) 0xab, (byte) 0x97,
+        (byte) 0xe7, (byte) 0xe2, (byte) 0xe8, (byte) 0x15, (byte) 0xfc, (byte) 0x35,
+        (byte) 0xbd, (byte) 0xce, (byte) 0x17, (byte) 0xbe, (byte) 0xe3, (byte) 0x73,
+        (byte) 0xd4, (byte) 0x88, (byte) 0x39, (byte) 0x27, (byte) 0x7e, (byte) 0x6d,
+        (byte) 0xa2, (byte) 0x27, (byte) 0xfa, (byte) 0x96, (byte) 0xe3, (byte) 0x38,
+        (byte) 0xc0, (byte) 0xa1, (byte) 0x55, (byte) 0xc6, (byte) 0xf3, (byte) 0x20,
+        (byte) 0xea, (byte) 0x50, (byte) 0x8d, (byte) 0x6c, (byte) 0x94, (byte) 0x9a,
+        (byte) 0x43, (byte) 0x74, (byte) 0xc0, (byte) 0xfa, (byte) 0xef, (byte) 0xe0,
+        (byte) 0xb1, (byte) 0x1c, (byte) 0x6d, (byte) 0x5e, (byte) 0x44, (byte) 0x08,
+        (byte) 0xef, (byte) 0xd5, (byte) 0x80, (byte) 0xad, (byte) 0x02, (byte) 0x03,
+        (byte) 0x01, (byte) 0x00, (byte) 0x01, (byte) 0xa3, (byte) 0x50, (byte) 0x30,
+        (byte) 0x4e, (byte) 0x30, (byte) 0x1d, (byte) 0x06, (byte) 0x03, (byte) 0x55,
+        (byte) 0x1d, (byte) 0x0e, (byte) 0x04, (byte) 0x16, (byte) 0x04, (byte) 0x14,
+        (byte) 0xe9, (byte) 0xd0, (byte) 0x9e, (byte) 0x0e, (byte) 0x62, (byte) 0x31,
+        (byte) 0x02, (byte) 0x9a, (byte) 0x33, (byte) 0xd7, (byte) 0x4a, (byte) 0x93,
+        (byte) 0x0d, (byte) 0xf3, (byte) 0xd6, (byte) 0x74, (byte) 0xce, (byte) 0x69,
+        (byte) 0xe1, (byte) 0xef, (byte) 0x30, (byte) 0x1f, (byte) 0x06, (byte) 0x03,
+        (byte) 0x55, (byte) 0x1d, (byte) 0x23, (byte) 0x04, (byte) 0x18, (byte) 0x30,
+        (byte) 0x16, (byte) 0x80, (byte) 0x14, (byte) 0xe9, (byte) 0xd0, (byte) 0x9e,
+        (byte) 0x0e, (byte) 0x62, (byte) 0x31, (byte) 0x02, (byte) 0x9a, (byte) 0x33,
+        (byte) 0xd7, (byte) 0x4a, (byte) 0x93, (byte) 0x0d, (byte) 0xf3, (byte) 0xd6,
+        (byte) 0x74, (byte) 0xce, (byte) 0x69, (byte) 0xe1, (byte) 0xef, (byte) 0x30,
+        (byte) 0x0c, (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x1d, (byte) 0x13,
+        (byte) 0x04, (byte) 0x05, (byte) 0x30, (byte) 0x03, (byte) 0x01, (byte) 0x01,
+        (byte) 0xff, (byte) 0x30, (byte) 0x0d, (byte) 0x06, (byte) 0x09, (byte) 0x2a,
+        (byte) 0x86, (byte) 0x48, (byte) 0x86, (byte) 0xf7, (byte) 0x0d, (byte) 0x01,
+        (byte) 0x01, (byte) 0x0b, (byte) 0x05, (byte) 0x00, (byte) 0x03, (byte) 0x82,
+        (byte) 0x01, (byte) 0x01, (byte) 0x00, (byte) 0x52, (byte) 0x70, (byte) 0xb6,
+        (byte) 0x10, (byte) 0x7f, (byte) 0xaa, (byte) 0x86, (byte) 0x8f, (byte) 0x02,
+        (byte) 0xb0, (byte) 0x97, (byte) 0x89, (byte) 0xb9, (byte) 0x04, (byte) 0x1d,
+        (byte) 0x79, (byte) 0xa3, (byte) 0x74, (byte) 0x7c, (byte) 0xdf, (byte) 0xad,
+        (byte) 0x87, (byte) 0xe4, (byte) 0x00, (byte) 0xd3, (byte) 0x3a, (byte) 0x5c,
+        (byte) 0x48, (byte) 0x3b, (byte) 0xfe, (byte) 0x77, (byte) 0xfd, (byte) 0xbe,
+        (byte) 0xce, (byte) 0x5b, (byte) 0xd2, (byte) 0xea, (byte) 0x3e, (byte) 0x7f,
+        (byte) 0xef, (byte) 0x20, (byte) 0x0d, (byte) 0x0b, (byte) 0xc7, (byte) 0xc4,
+        (byte) 0x25, (byte) 0x20, (byte) 0xe1, (byte) 0x8f, (byte) 0xc5, (byte) 0x19,
+        (byte) 0x37, (byte) 0x9c, (byte) 0xa0, (byte) 0x9d, (byte) 0x02, (byte) 0x30,
+        (byte) 0x5f, (byte) 0x49, (byte) 0x4e, (byte) 0x56, (byte) 0xc4, (byte) 0xab,
+        (byte) 0xcb, (byte) 0x5c, (byte) 0xe6, (byte) 0x40, (byte) 0x93, (byte) 0x92,
+        (byte) 0xee, (byte) 0xa1, (byte) 0x69, (byte) 0x7d, (byte) 0x10, (byte) 0x6b,
+        (byte) 0xd4, (byte) 0xf7, (byte) 0xec, (byte) 0xd9, (byte) 0xa5, (byte) 0x29,
+        (byte) 0x63, (byte) 0x29, (byte) 0xd9, (byte) 0x27, (byte) 0x2d, (byte) 0x5e,
+        (byte) 0x34, (byte) 0x37, (byte) 0xa9, (byte) 0xba, (byte) 0x0a, (byte) 0x7b,
+        (byte) 0x99, (byte) 0x1a, (byte) 0x7d, (byte) 0xa7, (byte) 0xa7, (byte) 0xf0,
+        (byte) 0xbf, (byte) 0x40, (byte) 0x29, (byte) 0x5d, (byte) 0x2f, (byte) 0x2e,
+        (byte) 0x0f, (byte) 0x35, (byte) 0x90, (byte) 0xb5, (byte) 0xc3, (byte) 0xfd,
+        (byte) 0x1e, (byte) 0xe2, (byte) 0xb3, (byte) 0xae, (byte) 0xf9, (byte) 0xde,
+        (byte) 0x9d, (byte) 0x76, (byte) 0xe1, (byte) 0x20, (byte) 0xf5, (byte) 0x1c,
+        (byte) 0x30, (byte) 0x42, (byte) 0x80, (byte) 0x2a, (byte) 0x4f, (byte) 0x85,
+        (byte) 0x5c, (byte) 0xb4, (byte) 0x49, (byte) 0x68, (byte) 0x6c, (byte) 0x7c,
+        (byte) 0x2a, (byte) 0xc8, (byte) 0xbc, (byte) 0x15, (byte) 0xed, (byte) 0x88,
+        (byte) 0xfd, (byte) 0x8a, (byte) 0x63, (byte) 0xe0, (byte) 0x93, (byte) 0xfd,
+        (byte) 0x86, (byte) 0xab, (byte) 0xa9, (byte) 0xf6, (byte) 0x63, (byte) 0xa5,
+        (byte) 0x29, (byte) 0xaf, (byte) 0xdc, (byte) 0x8f, (byte) 0xca, (byte) 0xc2,
+        (byte) 0x28, (byte) 0xe7, (byte) 0x26, (byte) 0x89, (byte) 0x75, (byte) 0xf1,
+        (byte) 0x3e, (byte) 0x2e, (byte) 0x86, (byte) 0x11, (byte) 0x8b, (byte) 0xfa,
+        (byte) 0xf5, (byte) 0xb4, (byte) 0xb4, (byte) 0x04, (byte) 0x02, (byte) 0xa3,
+        (byte) 0x85, (byte) 0x81, (byte) 0xad, (byte) 0xb3, (byte) 0xec, (byte) 0x2d,
+        (byte) 0x4b, (byte) 0x40, (byte) 0x59, (byte) 0x61, (byte) 0x0d, (byte) 0x59,
+        (byte) 0x09, (byte) 0x09, (byte) 0xee, (byte) 0xc7, (byte) 0x51, (byte) 0xef,
+        (byte) 0x6f, (byte) 0xd6, (byte) 0x9a, (byte) 0xa5, (byte) 0x45, (byte) 0xa2,
+        (byte) 0x89, (byte) 0xc2, (byte) 0x97, (byte) 0x93, (byte) 0xbc, (byte) 0x5b,
+        (byte) 0x37, (byte) 0x55, (byte) 0x73, (byte) 0x55, (byte) 0x0c, (byte) 0x9c,
+        (byte) 0xcb, (byte) 0x10, (byte) 0xec, (byte) 0x76, (byte) 0xfe, (byte) 0xa7,
+        (byte) 0x70, (byte) 0x4e, (byte) 0x9a, (byte) 0xa2, (byte) 0xf9, (byte) 0x40,
+        (byte) 0xdd, (byte) 0x96, (byte) 0x7d, (byte) 0x67, (byte) 0x5c, (byte) 0x8e,
+        (byte) 0x43, (byte) 0x1a, (byte) 0x26, (byte) 0xaa, (byte) 0xee, (byte) 0x38,
+        (byte) 0x11, (byte) 0x26, (byte) 0x3d, (byte) 0x69, (byte) 0xc7, (byte) 0x6a,
+        (byte) 0xe7, (byte) 0xbd, (byte) 0x67, (byte) 0x70, (byte) 0x35, (byte) 0xff,
+        (byte) 0x72, (byte) 0x2c, (byte) 0x87, (byte) 0x82, (byte) 0x68, (byte) 0x3f,
+        (byte) 0x8d
+    };
+
+    private static final byte[] FAKE_EC_2 = {
+        (byte) 0x30, (byte) 0x82, (byte) 0x04, (byte) 0x4f, (byte) 0x30, (byte) 0x82,
+        (byte) 0x03, (byte) 0x37, (byte) 0xa0, (byte) 0x03, (byte) 0x02, (byte) 0x01,
+        (byte) 0x02, (byte) 0x02, (byte) 0x09, (byte) 0x00, (byte) 0xd9, (byte) 0xc4,
+        (byte) 0xe1, (byte) 0xfc, (byte) 0x3d, (byte) 0x02, (byte) 0x21, (byte) 0x1f,
+        (byte) 0x30, (byte) 0x0d, (byte) 0x06, (byte) 0x09, (byte) 0x2a, (byte) 0x86,
+        (byte) 0x48, (byte) 0x86, (byte) 0xf7, (byte) 0x0d, (byte) 0x01, (byte) 0x01,
+        (byte) 0x0b, (byte) 0x05, (byte) 0x00, (byte) 0x30, (byte) 0x81, (byte) 0xbd,
+        (byte) 0x31, (byte) 0x0b, (byte) 0x30, (byte) 0x09, (byte) 0x06, (byte) 0x03,
+        (byte) 0x55, (byte) 0x04, (byte) 0x06, (byte) 0x13, (byte) 0x02, (byte) 0x41,
+        (byte) 0x55, (byte) 0x31, (byte) 0x13, (byte) 0x30, (byte) 0x11, (byte) 0x06,
+        (byte) 0x03, (byte) 0x55, (byte) 0x04, (byte) 0x08, (byte) 0x0c, (byte) 0x0a,
+        (byte) 0x53, (byte) 0x6f, (byte) 0x6d, (byte) 0x65, (byte) 0x2d, (byte) 0x53,
+        (byte) 0x74, (byte) 0x61, (byte) 0x74, (byte) 0x65, (byte) 0x31, (byte) 0x12,
+        (byte) 0x30, (byte) 0x10, (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x04,
+        (byte) 0x07, (byte) 0x0c, (byte) 0x09, (byte) 0x53, (byte) 0x6f, (byte) 0x6d,
+        (byte) 0x65, (byte) 0x2d, (byte) 0x43, (byte) 0x69, (byte) 0x74, (byte) 0x79,
+        (byte) 0x31, (byte) 0x1b, (byte) 0x30, (byte) 0x19, (byte) 0x06, (byte) 0x03,
+        (byte) 0x55, (byte) 0x04, (byte) 0x0a, (byte) 0x0c, (byte) 0x12, (byte) 0x53,
+        (byte) 0x6f, (byte) 0x6d, (byte) 0x65, (byte) 0x2d, (byte) 0x4f, (byte) 0x74,
+        (byte) 0x68, (byte) 0x65, (byte) 0x72, (byte) 0x2d, (byte) 0x43, (byte) 0x6f,
+        (byte) 0x6d, (byte) 0x70, (byte) 0x61, (byte) 0x6e, (byte) 0x79, (byte) 0x31,
+        (byte) 0x15, (byte) 0x30, (byte) 0x13, (byte) 0x06, (byte) 0x03, (byte) 0x55,
+        (byte) 0x04, (byte) 0x0b, (byte) 0x0c, (byte) 0x0c, (byte) 0x53, (byte) 0x6f,
+        (byte) 0x6d, (byte) 0x65, (byte) 0x2d, (byte) 0x53, (byte) 0x65, (byte) 0x63,
+        (byte) 0x74, (byte) 0x69, (byte) 0x6f, (byte) 0x6e, (byte) 0x31, (byte) 0x21,
+        (byte) 0x30, (byte) 0x1f, (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x04,
+        (byte) 0x03, (byte) 0x0c, (byte) 0x18, (byte) 0x57, (byte) 0x69, (byte) 0x66,
+        (byte) 0x69, (byte) 0x45, (byte) 0x6e, (byte) 0x74, (byte) 0x65, (byte) 0x72,
+        (byte) 0x70, (byte) 0x72, (byte) 0x69, (byte) 0x73, (byte) 0x65, (byte) 0x43,
+        (byte) 0x6f, (byte) 0x6e, (byte) 0x66, (byte) 0x69, (byte) 0x67, (byte) 0x54,
+        (byte) 0x65, (byte) 0x73, (byte) 0x74, (byte) 0x31, (byte) 0x2e, (byte) 0x30,
+        (byte) 0x2c, (byte) 0x06, (byte) 0x09, (byte) 0x2a, (byte) 0x86, (byte) 0x48,
+        (byte) 0x86, (byte) 0xf7, (byte) 0x0d, (byte) 0x01, (byte) 0x09, (byte) 0x01,
+        (byte) 0x16, (byte) 0x1f, (byte) 0x61, (byte) 0x6e, (byte) 0x2d, (byte) 0x65,
+        (byte) 0x6d, (byte) 0x61, (byte) 0x69, (byte) 0x6c, (byte) 0x2d, (byte) 0x61,
+        (byte) 0x64, (byte) 0x72, (byte) 0x65, (byte) 0x73, (byte) 0x73, (byte) 0x40,
+        (byte) 0x73, (byte) 0x6f, (byte) 0x6d, (byte) 0x65, (byte) 0x2d, (byte) 0x64,
+        (byte) 0x6f, (byte) 0x6d, (byte) 0x61, (byte) 0x69, (byte) 0x6e, (byte) 0x2e,
+        (byte) 0x63, (byte) 0x6f, (byte) 0x6d, (byte) 0x30, (byte) 0x1e, (byte) 0x17,
+        (byte) 0x0d, (byte) 0x31, (byte) 0x36, (byte) 0x30, (byte) 0x31, (byte) 0x31,
+        (byte) 0x35, (byte) 0x31, (byte) 0x31, (byte) 0x33, (byte) 0x32, (byte) 0x34,
+        (byte) 0x36, (byte) 0x5a, (byte) 0x17, (byte) 0x0d, (byte) 0x32, (byte) 0x36,
+        (byte) 0x30, (byte) 0x31, (byte) 0x31, (byte) 0x32, (byte) 0x31, (byte) 0x31,
+        (byte) 0x33, (byte) 0x32, (byte) 0x34, (byte) 0x36, (byte) 0x5a, (byte) 0x30,
+        (byte) 0x81, (byte) 0xbd, (byte) 0x31, (byte) 0x0b, (byte) 0x30, (byte) 0x09,
+        (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x04, (byte) 0x06, (byte) 0x13,
+        (byte) 0x02, (byte) 0x41, (byte) 0x55, (byte) 0x31, (byte) 0x13, (byte) 0x30,
+        (byte) 0x11, (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x04, (byte) 0x08,
+        (byte) 0x0c, (byte) 0x0a, (byte) 0x53, (byte) 0x6f, (byte) 0x6d, (byte) 0x65,
+        (byte) 0x2d, (byte) 0x53, (byte) 0x74, (byte) 0x61, (byte) 0x74, (byte) 0x65,
+        (byte) 0x31, (byte) 0x12, (byte) 0x30, (byte) 0x10, (byte) 0x06, (byte) 0x03,
+        (byte) 0x55, (byte) 0x04, (byte) 0x07, (byte) 0x0c, (byte) 0x09, (byte) 0x53,
+        (byte) 0x6f, (byte) 0x6d, (byte) 0x65, (byte) 0x2d, (byte) 0x43, (byte) 0x69,
+        (byte) 0x74, (byte) 0x79, (byte) 0x31, (byte) 0x1b, (byte) 0x30, (byte) 0x19,
+        (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x04, (byte) 0x0a, (byte) 0x0c,
+        (byte) 0x12, (byte) 0x53, (byte) 0x6f, (byte) 0x6d, (byte) 0x65, (byte) 0x2d,
+        (byte) 0x4f, (byte) 0x74, (byte) 0x68, (byte) 0x65, (byte) 0x72, (byte) 0x2d,
+        (byte) 0x43, (byte) 0x6f, (byte) 0x6d, (byte) 0x70, (byte) 0x61, (byte) 0x6e,
+        (byte) 0x79, (byte) 0x31, (byte) 0x15, (byte) 0x30, (byte) 0x13, (byte) 0x06,
+        (byte) 0x03, (byte) 0x55, (byte) 0x04, (byte) 0x0b, (byte) 0x0c, (byte) 0x0c,
+        (byte) 0x53, (byte) 0x6f, (byte) 0x6d, (byte) 0x65, (byte) 0x2d, (byte) 0x53,
+        (byte) 0x65, (byte) 0x63, (byte) 0x74, (byte) 0x69, (byte) 0x6f, (byte) 0x6e,
+        (byte) 0x31, (byte) 0x21, (byte) 0x30, (byte) 0x1f, (byte) 0x06, (byte) 0x03,
+        (byte) 0x55, (byte) 0x04, (byte) 0x03, (byte) 0x0c, (byte) 0x18, (byte) 0x57,
+        (byte) 0x69, (byte) 0x66, (byte) 0x69, (byte) 0x45, (byte) 0x6e, (byte) 0x74,
+        (byte) 0x65, (byte) 0x72, (byte) 0x70, (byte) 0x72, (byte) 0x69, (byte) 0x73,
+        (byte) 0x65, (byte) 0x43, (byte) 0x6f, (byte) 0x6e, (byte) 0x66, (byte) 0x69,
+        (byte) 0x67, (byte) 0x54, (byte) 0x65, (byte) 0x73, (byte) 0x74, (byte) 0x31,
+        (byte) 0x2e, (byte) 0x30, (byte) 0x2c, (byte) 0x06, (byte) 0x09, (byte) 0x2a,
+        (byte) 0x86, (byte) 0x48, (byte) 0x86, (byte) 0xf7, (byte) 0x0d, (byte) 0x01,
+        (byte) 0x09, (byte) 0x01, (byte) 0x16, (byte) 0x1f, (byte) 0x61, (byte) 0x6e,
+        (byte) 0x2d, (byte) 0x65, (byte) 0x6d, (byte) 0x61, (byte) 0x69, (byte) 0x6c,
+        (byte) 0x2d, (byte) 0x61, (byte) 0x64, (byte) 0x72, (byte) 0x65, (byte) 0x73,
+        (byte) 0x73, (byte) 0x40, (byte) 0x73, (byte) 0x6f, (byte) 0x6d, (byte) 0x65,
+        (byte) 0x2d, (byte) 0x64, (byte) 0x6f, (byte) 0x6d, (byte) 0x61, (byte) 0x69,
+        (byte) 0x6e, (byte) 0x2e, (byte) 0x63, (byte) 0x6f, (byte) 0x6d, (byte) 0x30,
+        (byte) 0x82, (byte) 0x01, (byte) 0x22, (byte) 0x30, (byte) 0x0d, (byte) 0x06,
+        (byte) 0x09, (byte) 0x2a, (byte) 0x86, (byte) 0x48, (byte) 0x86, (byte) 0xf7,
+        (byte) 0x0d, (byte) 0x01, (byte) 0x01, (byte) 0x01, (byte) 0x05, (byte) 0x00,
+        (byte) 0x03, (byte) 0x82, (byte) 0x01, (byte) 0x0f, (byte) 0x00, (byte) 0x30,
+        (byte) 0x82, (byte) 0x01, (byte) 0x0a, (byte) 0x02, (byte) 0x82, (byte) 0x01,
+        (byte) 0x01, (byte) 0x00, (byte) 0xa9, (byte) 0xa3, (byte) 0x21, (byte) 0xfd,
+        (byte) 0xa6, (byte) 0xc1, (byte) 0x04, (byte) 0x48, (byte) 0xc2, (byte) 0xc8,
+        (byte) 0x44, (byte) 0x50, (byte) 0xc4, (byte) 0x6d, (byte) 0x35, (byte) 0x24,
+        (byte) 0xf0, (byte) 0x6d, (byte) 0x69, (byte) 0xfb, (byte) 0xd1, (byte) 0xfc,
+        (byte) 0xde, (byte) 0xe9, (byte) 0xdb, (byte) 0xca, (byte) 0xee, (byte) 0x24,
+        (byte) 0x3d, (byte) 0x85, (byte) 0x8d, (byte) 0x84, (byte) 0xb4, (byte) 0x73,
+        (byte) 0xd1, (byte) 0x09, (byte) 0x37, (byte) 0x16, (byte) 0x80, (byte) 0x70,
+        (byte) 0x6b, (byte) 0x61, (byte) 0xcc, (byte) 0xf2, (byte) 0x98, (byte) 0xbd,
+        (byte) 0x53, (byte) 0x3a, (byte) 0x68, (byte) 0x60, (byte) 0x02, (byte) 0xba,
+        (byte) 0x0c, (byte) 0x53, (byte) 0x96, (byte) 0xfb, (byte) 0x80, (byte) 0xd1,
+        (byte) 0x5b, (byte) 0xc3, (byte) 0xcb, (byte) 0x7a, (byte) 0x81, (byte) 0x00,
+        (byte) 0x5d, (byte) 0x20, (byte) 0x72, (byte) 0xc0, (byte) 0xe4, (byte) 0x48,
+        (byte) 0x0e, (byte) 0xa2, (byte) 0xcd, (byte) 0xa2, (byte) 0x63, (byte) 0x8c,
+        (byte) 0x05, (byte) 0x7c, (byte) 0x63, (byte) 0x5b, (byte) 0xda, (byte) 0x0e,
+        (byte) 0xa7, (byte) 0x05, (byte) 0x09, (byte) 0x6d, (byte) 0xd5, (byte) 0xe4,
+        (byte) 0x3a, (byte) 0x4e, (byte) 0xa1, (byte) 0xf5, (byte) 0xfd, (byte) 0x47,
+        (byte) 0xee, (byte) 0x7b, (byte) 0xa3, (byte) 0x4c, (byte) 0x8c, (byte) 0xd3,
+        (byte) 0xbb, (byte) 0x58, (byte) 0x0f, (byte) 0x1c, (byte) 0x56, (byte) 0x80,
+        (byte) 0x80, (byte) 0xb5, (byte) 0xf9, (byte) 0x80, (byte) 0xc2, (byte) 0xd1,
+        (byte) 0x1d, (byte) 0x3f, (byte) 0xe8, (byte) 0x2a, (byte) 0x63, (byte) 0x0b,
+        (byte) 0x54, (byte) 0x5f, (byte) 0xd4, (byte) 0xcb, (byte) 0xb7, (byte) 0x94,
+        (byte) 0xe2, (byte) 0x35, (byte) 0x65, (byte) 0x59, (byte) 0xd1, (byte) 0x72,
+        (byte) 0xa4, (byte) 0xb8, (byte) 0xee, (byte) 0x82, (byte) 0x11, (byte) 0x7a,
+        (byte) 0x4c, (byte) 0x26, (byte) 0x66, (byte) 0x9b, (byte) 0x27, (byte) 0x3d,
+        (byte) 0x14, (byte) 0x4b, (byte) 0x4b, (byte) 0xc8, (byte) 0xf0, (byte) 0x6e,
+        (byte) 0x43, (byte) 0x8f, (byte) 0xee, (byte) 0x1f, (byte) 0xeb, (byte) 0x20,
+        (byte) 0xe2, (byte) 0x4c, (byte) 0x79, (byte) 0xbf, (byte) 0x21, (byte) 0x0d,
+        (byte) 0x36, (byte) 0xed, (byte) 0x5f, (byte) 0xcc, (byte) 0x70, (byte) 0x68,
+        (byte) 0x8a, (byte) 0x05, (byte) 0x7c, (byte) 0x2f, (byte) 0x1b, (byte) 0xe9,
+        (byte) 0xec, (byte) 0x83, (byte) 0x6e, (byte) 0x9a, (byte) 0x78, (byte) 0x31,
+        (byte) 0x3d, (byte) 0xf4, (byte) 0xde, (byte) 0x1b, (byte) 0xd2, (byte) 0x76,
+        (byte) 0x32, (byte) 0x6c, (byte) 0x1e, (byte) 0xc9, (byte) 0x90, (byte) 0x7f,
+        (byte) 0xc4, (byte) 0x30, (byte) 0xc0, (byte) 0xae, (byte) 0xab, (byte) 0x70,
+        (byte) 0x08, (byte) 0x78, (byte) 0xbf, (byte) 0x2e, (byte) 0x8b, (byte) 0x07,
+        (byte) 0xab, (byte) 0x8f, (byte) 0x03, (byte) 0xc5, (byte) 0xd3, (byte) 0xeb,
+        (byte) 0x98, (byte) 0x19, (byte) 0x50, (byte) 0x83, (byte) 0x52, (byte) 0xf7,
+        (byte) 0xff, (byte) 0xf5, (byte) 0x89, (byte) 0xe6, (byte) 0xe7, (byte) 0xa7,
+        (byte) 0xcb, (byte) 0xdf, (byte) 0x96, (byte) 0x9d, (byte) 0x14, (byte) 0x04,
+        (byte) 0x5e, (byte) 0x45, (byte) 0x82, (byte) 0xf7, (byte) 0x23, (byte) 0x1a,
+        (byte) 0xb6, (byte) 0x64, (byte) 0x57, (byte) 0xe8, (byte) 0x7e, (byte) 0xa1,
+        (byte) 0xaf, (byte) 0x58, (byte) 0x68, (byte) 0x70, (byte) 0xc5, (byte) 0x0f,
+        (byte) 0x8d, (byte) 0x54, (byte) 0xf3, (byte) 0x49, (byte) 0xa3, (byte) 0x97,
+        (byte) 0x32, (byte) 0xa7, (byte) 0x2a, (byte) 0x79, (byte) 0xbe, (byte) 0xcd,
+        (byte) 0x02, (byte) 0x03, (byte) 0x01, (byte) 0x00, (byte) 0x01, (byte) 0xa3,
+        (byte) 0x50, (byte) 0x30, (byte) 0x4e, (byte) 0x30, (byte) 0x1d, (byte) 0x06,
+        (byte) 0x03, (byte) 0x55, (byte) 0x1d, (byte) 0x0e, (byte) 0x04, (byte) 0x16,
+        (byte) 0x04, (byte) 0x14, (byte) 0xac, (byte) 0xf3, (byte) 0x73, (byte) 0x9a,
+        (byte) 0x25, (byte) 0x08, (byte) 0x01, (byte) 0x07, (byte) 0x86, (byte) 0x8b,
+        (byte) 0xc4, (byte) 0xed, (byte) 0xb1, (byte) 0x6b, (byte) 0x53, (byte) 0xa3,
+        (byte) 0x21, (byte) 0xb4, (byte) 0xb4, (byte) 0x46, (byte) 0x30, (byte) 0x1f,
+        (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x1d, (byte) 0x23, (byte) 0x04,
+        (byte) 0x18, (byte) 0x30, (byte) 0x16, (byte) 0x80, (byte) 0x14, (byte) 0xac,
+        (byte) 0xf3, (byte) 0x73, (byte) 0x9a, (byte) 0x25, (byte) 0x08, (byte) 0x01,
+        (byte) 0x07, (byte) 0x86, (byte) 0x8b, (byte) 0xc4, (byte) 0xed, (byte) 0xb1,
+        (byte) 0x6b, (byte) 0x53, (byte) 0xa3, (byte) 0x21, (byte) 0xb4, (byte) 0xb4,
+        (byte) 0x46, (byte) 0x30, (byte) 0x0c, (byte) 0x06, (byte) 0x03, (byte) 0x55,
+        (byte) 0x1d, (byte) 0x13, (byte) 0x04, (byte) 0x05, (byte) 0x30, (byte) 0x03,
+        (byte) 0x01, (byte) 0x01, (byte) 0xff, (byte) 0x30, (byte) 0x0d, (byte) 0x06,
+        (byte) 0x09, (byte) 0x2a, (byte) 0x86, (byte) 0x48, (byte) 0x86, (byte) 0xf7,
+        (byte) 0x0d, (byte) 0x01, (byte) 0x01, (byte) 0x0b, (byte) 0x05, (byte) 0x00,
+        (byte) 0x03, (byte) 0x82, (byte) 0x01, (byte) 0x01, (byte) 0x00, (byte) 0x16,
+        (byte) 0xf6, (byte) 0xd0, (byte) 0xe1, (byte) 0x14, (byte) 0x2d, (byte) 0x52,
+        (byte) 0x47, (byte) 0xa2, (byte) 0x89, (byte) 0xe6, (byte) 0x7f, (byte) 0xac,
+        (byte) 0x88, (byte) 0x04, (byte) 0x15, (byte) 0x21, (byte) 0x00, (byte) 0x72,
+        (byte) 0xf9, (byte) 0xee, (byte) 0xb2, (byte) 0x1b, (byte) 0x8e, (byte) 0x46,
+        (byte) 0x8b, (byte) 0x90, (byte) 0x20, (byte) 0x4f, (byte) 0xa7, (byte) 0xae,
+        (byte) 0x30, (byte) 0xb6, (byte) 0x24, (byte) 0xc5, (byte) 0x54, (byte) 0xaf,
+        (byte) 0x6c, (byte) 0x1e, (byte) 0xd6, (byte) 0x73, (byte) 0x22, (byte) 0x48,
+        (byte) 0x07, (byte) 0xb5, (byte) 0x13, (byte) 0x35, (byte) 0xbb, (byte) 0x9e,
+        (byte) 0xd9, (byte) 0x19, (byte) 0x79, (byte) 0xda, (byte) 0x76, (byte) 0x7f,
+        (byte) 0xf7, (byte) 0x87, (byte) 0xc9, (byte) 0xc3, (byte) 0x0b, (byte) 0x38,
+        (byte) 0x20, (byte) 0x26, (byte) 0xfc, (byte) 0x7f, (byte) 0x32, (byte) 0x2a,
+        (byte) 0xd5, (byte) 0x09, (byte) 0x87, (byte) 0xda, (byte) 0x23, (byte) 0x1f,
+        (byte) 0x71, (byte) 0x83, (byte) 0x00, (byte) 0x17, (byte) 0xf6, (byte) 0xb9,
+        (byte) 0x57, (byte) 0x21, (byte) 0xdf, (byte) 0x29, (byte) 0xcc, (byte) 0xdb,
+        (byte) 0xe9, (byte) 0x2c, (byte) 0xba, (byte) 0x86, (byte) 0x34, (byte) 0x53,
+        (byte) 0x29, (byte) 0x09, (byte) 0xc7, (byte) 0x3c, (byte) 0x8e, (byte) 0xa3,
+        (byte) 0x86, (byte) 0x81, (byte) 0x26, (byte) 0x7b, (byte) 0xa1, (byte) 0xbe,
+        (byte) 0xbc, (byte) 0xc9, (byte) 0x83, (byte) 0xb5, (byte) 0x36, (byte) 0x65,
+        (byte) 0x51, (byte) 0xb4, (byte) 0x41, (byte) 0xf0, (byte) 0x05, (byte) 0x78,
+        (byte) 0x3a, (byte) 0xa6, (byte) 0xad, (byte) 0x4b, (byte) 0x08, (byte) 0xd1,
+        (byte) 0xe4, (byte) 0xf1, (byte) 0x2e, (byte) 0xc7, (byte) 0x23, (byte) 0x6d,
+        (byte) 0xf0, (byte) 0x9d, (byte) 0x60, (byte) 0x6d, (byte) 0xe7, (byte) 0x11,
+        (byte) 0xaf, (byte) 0x41, (byte) 0x68, (byte) 0xee, (byte) 0x06, (byte) 0x76,
+        (byte) 0x82, (byte) 0x48, (byte) 0xee, (byte) 0x41, (byte) 0xc4, (byte) 0xf8,
+        (byte) 0xe1, (byte) 0x83, (byte) 0xbc, (byte) 0xa8, (byte) 0xbd, (byte) 0x9c,
+        (byte) 0x17, (byte) 0x45, (byte) 0xf4, (byte) 0x36, (byte) 0x67, (byte) 0x47,
+        (byte) 0x0e, (byte) 0x32, (byte) 0x13, (byte) 0x6e, (byte) 0xc1, (byte) 0x1e,
+        (byte) 0x08, (byte) 0xef, (byte) 0x10, (byte) 0xdf, (byte) 0x45, (byte) 0xbf,
+        (byte) 0x5a, (byte) 0xc4, (byte) 0x44, (byte) 0x4c, (byte) 0xd0, (byte) 0xd5,
+        (byte) 0x23, (byte) 0xde, (byte) 0xd7, (byte) 0x83, (byte) 0x1e, (byte) 0xb0,
+        (byte) 0x27, (byte) 0x4d, (byte) 0x57, (byte) 0xa3, (byte) 0xe8, (byte) 0x36,
+        (byte) 0x52, (byte) 0x1c, (byte) 0x48, (byte) 0x0a, (byte) 0xc4, (byte) 0xd8,
+        (byte) 0x32, (byte) 0xfc, (byte) 0xd0, (byte) 0x26, (byte) 0x6f, (byte) 0xa4,
+        (byte) 0x61, (byte) 0x2c, (byte) 0x3a, (byte) 0xa9, (byte) 0xfe, (byte) 0xa4,
+        (byte) 0x7a, (byte) 0x58, (byte) 0x54, (byte) 0x58, (byte) 0x96, (byte) 0x2b,
+        (byte) 0x6e, (byte) 0x9c, (byte) 0xc9, (byte) 0x00, (byte) 0xda, (byte) 0xc6,
+        (byte) 0xbb, (byte) 0x97, (byte) 0xc4, (byte) 0x95, (byte) 0x32, (byte) 0x6b,
+        (byte) 0x03, (byte) 0x6f, (byte) 0x33, (byte) 0x59, (byte) 0xd4, (byte) 0xa4,
+        (byte) 0x4a, (byte) 0x29, (byte) 0x29, (byte) 0x9a, (byte) 0xf4, (byte) 0x87,
+        (byte) 0x26, (byte) 0xe6, (byte) 0xee, (byte) 0x5c, (byte) 0x0b, (byte) 0xe9,
+        (byte) 0x98, (byte) 0x5d, (byte) 0xab, (byte) 0x31, (byte) 0xa1, (byte) 0x63,
+        (byte) 0xaa, (byte) 0x1a, (byte) 0xea, (byte) 0x61, (byte) 0x27, (byte) 0x5e,
+        (byte) 0x9e, (byte) 0x34, (byte) 0x73
+    };
+
+
     private boolean hasWifi() {
         return getContext().getPackageManager().hasSystemFeature(
                 PackageManager.FEATURE_WIFI);
@@ -57,7 +451,7 @@
         }
     }
 
-    public void testSettersAndGetters() {
+    public void testSettersAndGetters() throws Exception {
         if (!hasWifi()) {
             return;
         }
@@ -87,7 +481,17 @@
         assertTrue(config.getAnonymousIdentity().equals(ANON_IDENTITY));
         config.setPassword(PASSWORD);
         assertTrue(config.getPassword().equals(PASSWORD));
-        config.setCaCertificate(null);
+        CertificateFactory factory = CertificateFactory.getInstance("X.509");
+        X509Certificate cert1 = (X509Certificate) factory.generateCertificate(
+                new ByteArrayInputStream(FAKE_EC_1));
+        X509Certificate cert2 = (X509Certificate) factory.generateCertificate(
+                new ByteArrayInputStream(FAKE_EC_2));
+        config.setCaCertificate(cert1);
+        assertTrue(config.getCaCertificate().getSerialNumber().equals(cert1.getSerialNumber()));
+        config.setCaCertificates(new X509Certificate[]{cert1, cert2});
+        X509Certificate[] certs = config.getCaCertificates();
+        assertTrue(cert1.getSerialNumber().equals(certs[0].getSerialNumber()));
+        assertTrue(cert2.getSerialNumber().equals(certs[1].getSerialNumber()));
         config.setClientKeyEntry(null, null);
         config.setSubjectMatch(SUBJECT_MATCH);
         assertTrue(config.getSubjectMatch().equals(SUBJECT_MATCH));
diff --git a/tests/tests/net/src/org/apache/http/conn/ssl/cts/AbstractVerifierTest.java b/tests/tests/net/src/org/apache/http/conn/ssl/cts/AbstractVerifierTest.java
index 16d3ac4..5e2a55e 100644
--- a/tests/tests/net/src/org/apache/http/conn/ssl/cts/AbstractVerifierTest.java
+++ b/tests/tests/net/src/org/apache/http/conn/ssl/cts/AbstractVerifierTest.java
@@ -107,7 +107,7 @@
     public void testGetCns_whitespace() {
         assertCns("cn= p", "p");
         assertCns("cn=\np", "p");
-        assertCns("cn=\tp", "p");
+        assertCns("cn=\tp", "\tp");
     }
 
     public void testGetCnsWithOid() {
diff --git a/tests/tests/os/src/android/os/cts/HardwarePropertiesManagerTest.java b/tests/tests/os/src/android/os/cts/HardwarePropertiesManagerTest.java
new file mode 100644
index 0000000..4239fa2
--- /dev/null
+++ b/tests/tests/os/src/android/os/cts/HardwarePropertiesManagerTest.java
@@ -0,0 +1,145 @@
+/*
+ * 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.os.cts;
+
+import android.content.Context;
+import android.os.CpuUsageInfo;
+import android.os.HardwarePropertiesManager;
+import android.os.SystemClock;
+import android.test.AndroidTestCase;
+
+import java.lang.Math;
+
+public class HardwarePropertiesManagerTest extends AndroidTestCase {
+    public static final int MAX_FAN_SPEED = 20000;
+    public static final int MAX_DEVICE_TEMP = 200;
+    public static final int MONITORING_ITERATION_NUMBER = 10;
+
+    // Time between checks in milliseconds.
+    public static final long SLEEP_TIME = 10;
+
+    private void checkFanSpeed(float speed) {
+        assertTrue(speed >= 0 && speed < MAX_FAN_SPEED);
+    }
+
+    private void checkDeviceTemp(float temp) {
+        assertTrue(Math.abs(temp) < MAX_DEVICE_TEMP);
+    }
+
+    private void checkCpuUsageInfo(CpuUsageInfo info) {
+        assertTrue(info.getActive() >= 0 && info.getTotal() >= 0 && info.getTotal() >= info.getActive());
+    }
+
+    private void checkFanSpeeds(float[] fanSpeeds) {
+        for (float speed : fanSpeeds) {
+            checkFanSpeed(speed);
+        }
+    }
+
+    private void checkTemps(float[] temps) {
+        for (float temp : temps) {
+            checkDeviceTemp(temp);
+        }
+    }
+
+    private void checkCpuUsages(CpuUsageInfo[] cpuUsages) {
+        for (CpuUsageInfo info : cpuUsages) {
+            checkCpuUsageInfo(info);
+        }
+    }
+
+    // Check validity of new array of fan speeds:
+    // the number of fans should be the same.
+    private void checkFanSpeeds(float[] speeds, float[] oldSpeeds) {
+        assertEquals(speeds.length, oldSpeeds.length);
+    }
+
+    // Check validity of new array of device temperatures:
+    // the number of entries should be the same.
+    private void checkDeviceTemps(float[] temps, float[] oldTemps) {
+        assertEquals(temps.length, oldTemps.length);
+    }
+
+    // Check validity of new array of cpu usages:
+    // The number of CPUs should be the same and total/active time should not decrease.
+    private void checkCpuUsages(CpuUsageInfo[] infos,
+                                CpuUsageInfo[] oldInfos) {
+        assertEquals(infos.length, oldInfos.length);
+        for (int i = 0; i < infos.length; ++i) {
+            assertTrue(oldInfos[i].getActive() <= infos[i].getActive() &&
+                    oldInfos[i].getTotal() <= infos[i].getTotal());
+        }
+    }
+
+    /**
+     * test points:
+     * 1. Get fan speeds, device temperatures and CPU usage information.
+     * 2. Check for validity.
+     * 3. Sleep.
+     * 4. Do it 10 times and compare with old ones.
+     */
+    public void testHardwarePropertiesManager() throws InterruptedException {
+        HardwarePropertiesManager hm = (HardwarePropertiesManager) getContext().getSystemService(
+            Context.HARDWARE_PROPERTIES_SERVICE);
+
+        float[] oldFanSpeeds = hm.getFanSpeeds();
+        float[] oldCpuTemps = hm.getDeviceTemperatures(
+            HardwarePropertiesManager.DEVICE_TEMPERATURE_CPU);
+        float[] oldGpuTemps = hm.getDeviceTemperatures(
+            HardwarePropertiesManager.DEVICE_TEMPERATURE_GPU);
+        float[] oldBatteryTemps = hm.getDeviceTemperatures(
+            HardwarePropertiesManager.DEVICE_TEMPERATURE_BATTERY);
+        CpuUsageInfo[] oldCpuUsages = hm.getCpuUsages();
+
+        checkFanSpeeds(oldFanSpeeds);
+        checkTemps(oldCpuTemps);
+        checkTemps(oldGpuTemps);
+        checkTemps(oldBatteryTemps);
+        checkCpuUsages(oldCpuUsages);
+
+        for (int i = 0; i < MONITORING_ITERATION_NUMBER; i++) {
+            Thread.sleep(SLEEP_TIME);
+
+            float[] fanSpeeds = hm.getFanSpeeds();
+            float[] cpuTemps = hm.getDeviceTemperatures(
+                HardwarePropertiesManager.DEVICE_TEMPERATURE_CPU);
+            float[] gpuTemps = hm.getDeviceTemperatures(
+                HardwarePropertiesManager.DEVICE_TEMPERATURE_GPU);
+            float[] batteryTemps = hm.getDeviceTemperatures(
+                HardwarePropertiesManager.DEVICE_TEMPERATURE_BATTERY);
+            CpuUsageInfo[] cpuUsages = hm.getCpuUsages();
+
+            checkFanSpeeds(fanSpeeds);
+            checkTemps(cpuTemps);
+            checkTemps(gpuTemps);
+            checkTemps(batteryTemps);
+            checkCpuUsages(cpuUsages);
+
+            checkFanSpeeds(fanSpeeds, oldFanSpeeds);
+            checkDeviceTemps(cpuTemps, oldCpuTemps);
+            checkDeviceTemps(gpuTemps, oldGpuTemps);
+            checkDeviceTemps(batteryTemps, oldBatteryTemps);
+            checkCpuUsages(cpuUsages, oldCpuUsages);
+
+            oldFanSpeeds = fanSpeeds;
+            oldCpuTemps = cpuTemps;
+            oldGpuTemps = gpuTemps;
+            oldBatteryTemps = batteryTemps;
+            oldCpuUsages = cpuUsages;
+        }
+    }
+}
diff --git a/tests/tests/os/src/android/os/cts/StrictModeTest.java b/tests/tests/os/src/android/os/cts/StrictModeTest.java
index 2407309..a345256 100644
--- a/tests/tests/os/src/android/os/cts/StrictModeTest.java
+++ b/tests/tests/os/src/android/os/cts/StrictModeTest.java
@@ -108,9 +108,9 @@
     }
 
     private boolean hasInternetConnection() {
-        // TODO: expand this to include devices with ethernet
         final PackageManager pm = getContext().getPackageManager();
         return pm.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)
-                || pm.hasSystemFeature(PackageManager.FEATURE_WIFI);
+                || pm.hasSystemFeature(PackageManager.FEATURE_WIFI)
+                || pm.hasSystemFeature(PackageManager.FEATURE_ETHERNET);
     }
 }
diff --git a/tests/tests/permission/src/android/permission/cts/FileSystemPermissionTest.java b/tests/tests/permission/src/android/permission/cts/FileSystemPermissionTest.java
index d20e550..14d5343 100644
--- a/tests/tests/permission/src/android/permission/cts/FileSystemPermissionTest.java
+++ b/tests/tests/permission/src/android/permission/cts/FileSystemPermissionTest.java
@@ -541,6 +541,7 @@
                     "/data/misc/bluetooth",
                     "/data/misc/dhcp",
                     "/data/misc/lockscreen",
+                    "/data/misc/sensor",
                     "/data/misc/webwidgets",
                     "/data/misc/webwidgets/chess",
                     "/data/misc/widgets",
diff --git a/tests/tests/permission2/res/raw/android_manifest.xml b/tests/tests/permission2/res/raw/android_manifest.xml
new file mode 100644
index 0000000..08bccc1
--- /dev/null
+++ b/tests/tests/permission2/res/raw/android_manifest.xml
@@ -0,0 +1,2911 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* //device/apps/common/AndroidManifest.xml
+**
+** Copyright 2006, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT 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" coreApp="true" android:sharedUserId="android.uid.system"
+    android:sharedUserLabel="@string/android_system_label">
+
+    <!-- ================================================ -->
+    <!-- Special broadcasts that only the system can send -->
+    <!-- ================================================ -->
+    <eat-comment />
+
+    <protected-broadcast android:name="android.intent.action.SCREEN_OFF" />
+    <protected-broadcast android:name="android.intent.action.SCREEN_ON" />
+    <protected-broadcast android:name="android.intent.action.USER_PRESENT" />
+    <protected-broadcast android:name="android.intent.action.TIME_SET" />
+    <protected-broadcast android:name="android.intent.action.TIME_TICK" />
+    <protected-broadcast android:name="android.intent.action.TIMEZONE_CHANGED" />
+    <protected-broadcast android:name="android.intent.action.DATE_CHANGED" />
+    <protected-broadcast android:name="android.intent.action.BOOT_COMPLETED" />
+    <protected-broadcast android:name="android.intent.action.PACKAGE_INSTALL" />
+    <protected-broadcast android:name="android.intent.action.PACKAGE_ADDED" />
+    <protected-broadcast android:name="android.intent.action.PACKAGE_REPLACED" />
+    <protected-broadcast android:name="android.intent.action.MY_PACKAGE_REPLACED" />
+    <protected-broadcast android:name="android.intent.action.PACKAGE_REMOVED" />
+    <protected-broadcast android:name="android.intent.action.PACKAGE_FULLY_REMOVED" />
+    <protected-broadcast android:name="android.intent.action.PACKAGE_CHANGED" />
+    <protected-broadcast android:name="android.intent.action.PACKAGE_RESTARTED" />
+    <protected-broadcast android:name="android.intent.action.PACKAGE_DATA_CLEARED" />
+    <protected-broadcast android:name="android.intent.action.PACKAGE_FIRST_LAUNCH" />
+    <protected-broadcast android:name="android.intent.action.PACKAGE_NEEDS_VERIFICATION" />
+    <protected-broadcast android:name="android.intent.action.PACKAGE_VERIFIED" />
+    <protected-broadcast android:name="android.intent.action.UID_REMOVED" />
+    <protected-broadcast android:name="android.intent.action.QUERY_PACKAGE_RESTART" />
+    <protected-broadcast android:name="android.intent.action.CONFIGURATION_CHANGED" />
+    <protected-broadcast android:name="android.intent.action.LOCALE_CHANGED" />
+    <protected-broadcast android:name="android.intent.action.BATTERY_CHANGED" />
+    <protected-broadcast android:name="android.intent.action.BATTERY_LOW" />
+    <protected-broadcast android:name="android.intent.action.BATTERY_OKAY" />
+    <protected-broadcast android:name="android.intent.action.ACTION_POWER_CONNECTED" />
+    <protected-broadcast android:name="android.intent.action.ACTION_POWER_DISCONNECTED" />
+    <protected-broadcast android:name="android.intent.action.ACTION_SHUTDOWN" />
+    <protected-broadcast android:name="android.intent.action.CHARGING" />
+    <protected-broadcast android:name="android.intent.action.DISCHARGING" />
+    <protected-broadcast android:name="android.intent.action.DEVICE_STORAGE_LOW" />
+    <protected-broadcast android:name="android.intent.action.DEVICE_STORAGE_OK" />
+    <protected-broadcast android:name="android.intent.action.DEVICE_STORAGE_FULL" />
+    <protected-broadcast android:name="android.intent.action.DEVICE_STORAGE_NOT_FULL" />
+    <protected-broadcast android:name="android.intent.action.NEW_OUTGOING_CALL" />
+    <protected-broadcast android:name="android.intent.action.REBOOT" />
+    <protected-broadcast android:name="android.intent.action.DOCK_EVENT" />
+    <protected-broadcast android:name="android.intent.action.MASTER_CLEAR_NOTIFICATION" />
+    <protected-broadcast android:name="android.intent.action.USER_ADDED" />
+    <protected-broadcast android:name="android.intent.action.USER_REMOVED" />
+    <protected-broadcast android:name="android.intent.action.USER_STARTING" />
+    <protected-broadcast android:name="android.intent.action.USER_STARTED" />
+    <protected-broadcast android:name="android.intent.action.USER_STOPPING" />
+    <protected-broadcast android:name="android.intent.action.USER_STOPPED" />
+    <protected-broadcast android:name="android.intent.action.USER_BACKGROUND" />
+    <protected-broadcast android:name="android.intent.action.USER_FOREGROUND" />
+    <protected-broadcast android:name="android.intent.action.USER_SWITCHED" />
+    <protected-broadcast android:name="android.intent.action.INTENT_FILTER_NEEDS_VERIFICATION" />
+
+    <protected-broadcast android:name="android.os.action.POWER_SAVE_MODE_CHANGED" />
+    <protected-broadcast android:name="android.os.action.POWER_SAVE_MODE_CHANGING" />
+    <protected-broadcast android:name="android.os.action.DEVICE_IDLE_MODE_CHANGED" />
+    <protected-broadcast android:name="android.os.action.POWER_SAVE_WHITELIST_CHANGED" />
+    <protected-broadcast android:name="android.os.action.POWER_SAVE_TEMP_WHITELIST_CHANGED" />
+
+    <protected-broadcast android:name="android.os.action.SCREEN_BRIGHTNESS_BOOST_CHANGED" />
+
+    <protected-broadcast android:name="android.app.action.ENTER_CAR_MODE" />
+    <protected-broadcast android:name="android.app.action.EXIT_CAR_MODE" />
+    <protected-broadcast android:name="android.app.action.ENTER_DESK_MODE" />
+    <protected-broadcast android:name="android.app.action.EXIT_DESK_MODE" />
+    <protected-broadcast android:name="android.app.action.NEXT_ALARM_CLOCK_CHANGED" />
+
+    <protected-broadcast android:name="android.appwidget.action.APPWIDGET_UPDATE_OPTIONS" />
+    <protected-broadcast android:name="android.appwidget.action.APPWIDGET_DELETED" />
+    <protected-broadcast android:name="android.appwidget.action.APPWIDGET_DISABLED" />
+    <protected-broadcast android:name="android.appwidget.action.APPWIDGET_ENABLED" />
+    <protected-broadcast android:name="android.appwidget.action.APPWIDGET_HOST_RESTORED" />
+    <protected-broadcast android:name="android.appwidget.action.APPWIDGET_RESTORED" />
+
+    <protected-broadcast android:name="android.os.action.SETTING_RESTORED" />
+
+    <protected-broadcast android:name="android.backup.intent.RUN" />
+    <protected-broadcast android:name="android.backup.intent.CLEAR" />
+    <protected-broadcast android:name="android.backup.intent.INIT" />
+
+    <protected-broadcast android:name="android.bluetooth.adapter.action.STATE_CHANGED" />
+    <protected-broadcast android:name="android.bluetooth.adapter.action.SCAN_MODE_CHANGED" />
+    <protected-broadcast android:name="android.bluetooth.adapter.action.DISCOVERY_STARTED" />
+    <protected-broadcast android:name="android.bluetooth.adapter.action.DISCOVERY_FINISHED" />
+    <protected-broadcast android:name="android.bluetooth.adapter.action.LOCAL_NAME_CHANGED" />
+    <protected-broadcast android:name="android.bluetooth.adapter.action.CONNECTION_STATE_CHANGED" />
+    <protected-broadcast android:name="android.bluetooth.device.action.UUID" />
+    <protected-broadcast android:name="android.bluetooth.device.action.MAS_INSTANCE" />
+    <protected-broadcast android:name="android.bluetooth.device.action.ALIAS_CHANGED" />
+    <protected-broadcast android:name="android.bluetooth.device.action.FOUND" />
+    <protected-broadcast android:name="android.bluetooth.device.action.DISAPPEARED" />
+    <protected-broadcast android:name="android.bluetooth.device.action.CLASS_CHANGED" />
+    <protected-broadcast android:name="android.bluetooth.device.action.ACL_CONNECTED" />
+    <protected-broadcast android:name="android.bluetooth.device.action.ACL_DISCONNECT_REQUESTED" />
+    <protected-broadcast android:name="android.bluetooth.device.action.ACL_DISCONNECTED" />
+    <protected-broadcast android:name="android.bluetooth.device.action.NAME_CHANGED" />
+    <protected-broadcast android:name="android.bluetooth.device.action.BOND_STATE_CHANGED" />
+    <protected-broadcast android:name="android.bluetooth.device.action.NAME_FAILED" />
+    <protected-broadcast android:name="android.bluetooth.device.action.PAIRING_REQUEST" />
+    <protected-broadcast android:name="android.bluetooth.device.action.PAIRING_CANCEL" />
+    <protected-broadcast android:name="android.bluetooth.device.action.CONNECTION_ACCESS_REPLY" />
+    <protected-broadcast android:name="android.bluetooth.device.action.CONNECTION_ACCESS_CANCEL" />
+    <protected-broadcast android:name="android.bluetooth.device.action.CONNECTION_ACCESS_REQUEST" />
+    <protected-broadcast android:name="android.bluetooth.devicepicker.action.LAUNCH" />
+    <protected-broadcast android:name="android.bluetooth.devicepicker.action.DEVICE_SELECTED" />
+    <protected-broadcast
+        android:name="android.bluetooth.headset.profile.action.CONNECTION_STATE_CHANGED" />
+    <protected-broadcast
+        android:name="android.bluetooth.headset.profile.action.AUDIO_STATE_CHANGED" />
+    <protected-broadcast
+        android:name="android.bluetooth.headset.action.VENDOR_SPECIFIC_HEADSET_EVENT" />
+    <protected-broadcast
+        android:name="android.bluetooth.headsetclient.profile.action.CONNECTION_STATE_CHANGED" />
+    <protected-broadcast
+        android:name="android.bluetooth.headsetclient.profile.action.AUDIO_STATE_CHANGED" />
+    <protected-broadcast
+        android:name="android.bluetooth.headsetclient.profile.action.AG_EVENT" />
+    <protected-broadcast
+        android:name="android.bluetooth.headsetclient.profile.action.AG_CALL_CHANGED" />
+    <protected-broadcast
+        android:name="android.bluetooth.headsetclient.profile.action.RESULT" />
+    <protected-broadcast
+        android:name="android.bluetooth.headsetclient.profile.action.LAST_VTAG" />
+    <protected-broadcast
+        android:name="android.bluetooth.a2dp.profile.action.CONNECTION_STATE_CHANGED" />
+    <protected-broadcast
+        android:name="android.bluetooth.a2dp.profile.action.PLAYING_STATE_CHANGED" />
+    <protected-broadcast
+        android:name="android.bluetooth.a2dp-sink.profile.action.CONNECTION_STATE_CHANGED" />
+    <protected-broadcast
+        android:name="android.bluetooth.a2dp-sink.profile.action.PLAYING_STATE_CHANGED" />
+    <protected-broadcast
+        android:name="android.bluetooth.a2dp-sink.profile.action.AUDIO_CONFIG_CHANGED" />
+   <protected-broadcast
+        android:name="android.bluetooth.avrcp-controller.profile.action.CONNECTION_STATE_CHANGED" />
+    <protected-broadcast
+        android:name="android.bluetooth.input.profile.action.CONNECTION_STATE_CHANGED" />
+    <protected-broadcast
+        android:name="android.bluetooth.input.profile.action.PROTOCOL_MODE_CHANGED" />
+    <protected-broadcast
+        android:name="android.bluetooth.input.profile.action.VIRTUAL_UNPLUG_STATUS" />
+    <protected-broadcast
+        android:name="android.bluetooth.pan.profile.action.CONNECTION_STATE_CHANGED" />
+    <protected-broadcast android:name="android.bluetooth.pbap.intent.action.PBAP_STATE_CHANGED" />
+    <protected-broadcast android:name="android.btopp.intent.action.INCOMING_FILE_NOTIFICATION" />
+    <protected-broadcast android:name="android.btopp.intent.action.USER_CONFIRMATION_TIMEOUT" />
+    <protected-broadcast android:name="android.btopp.intent.action.LIST" />
+    <protected-broadcast android:name="android.btopp.intent.action.OPEN_OUTBOUND" />
+    <protected-broadcast android:name="android.btopp.intent.action.HIDE_COMPLETE" />
+    <protected-broadcast android:name="android.btopp.intent.action.CONFIRM" />
+    <protected-broadcast android:name="android.btopp.intent.action.HIDE" />
+    <protected-broadcast android:name="android.btopp.intent.action.RETRY" />
+    <protected-broadcast android:name="android.btopp.intent.action.OPEN" />
+    <protected-broadcast android:name="android.btopp.intent.action.OPEN_INBOUND" />
+    <protected-broadcast android:name="com.android.bluetooth.pbap.authchall" />
+    <protected-broadcast android:name="com.android.bluetooth.pbap.userconfirmtimeout" />
+    <protected-broadcast android:name="com.android.bluetooth.pbap.authresponse" />
+    <protected-broadcast android:name="com.android.bluetooth.pbap.authcancelled" />
+
+    <protected-broadcast android:name="android.hardware.display.action.WIFI_DISPLAY_STATUS_CHANGED" />
+
+    <protected-broadcast android:name="android.hardware.usb.action.USB_STATE" />
+    <protected-broadcast android:name="android.hardware.usb.action.USB_PORT_CHANGED" />
+    <protected-broadcast android:name="android.hardware.usb.action.USB_ACCESSORY_ATTACHED" />
+    <protected-broadcast android:name="android.hardware.usb.action.USB_ACCESSORY_ATTACHED" />
+    <protected-broadcast android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" />
+    <protected-broadcast android:name="android.hardware.usb.action.USB_DEVICE_DETACHED" />
+
+    <protected-broadcast android:name="android.intent.action.HEADSET_PLUG" />
+    <protected-broadcast android:name="android.media.action.HDMI_AUDIO_PLUG" />
+
+    <protected-broadcast android:name="android.media.AUDIO_BECOMING_NOISY" />
+    <protected-broadcast android:name="android.media.RINGER_MODE_CHANGED" />
+    <protected-broadcast android:name="android.media.VIBRATE_SETTING_CHANGED" />
+    <protected-broadcast android:name="android.media.VOLUME_CHANGED_ACTION" />
+    <protected-broadcast android:name="android.media.MASTER_VOLUME_CHANGED_ACTION" />
+    <protected-broadcast android:name="android.media.MASTER_MUTE_CHANGED_ACTION" />
+    <protected-broadcast android:name="android.media.SCO_AUDIO_STATE_CHANGED" />
+    <protected-broadcast android:name="android.media.ACTION_SCO_AUDIO_STATE_UPDATED" />
+
+    <protected-broadcast android:name="android.intent.action.MEDIA_REMOVED" />
+    <protected-broadcast android:name="android.intent.action.MEDIA_UNMOUNTED" />
+    <protected-broadcast android:name="android.intent.action.MEDIA_CHECKING" />
+    <protected-broadcast android:name="android.intent.action.MEDIA_NOFS" />
+    <protected-broadcast android:name="android.intent.action.MEDIA_MOUNTED" />
+    <protected-broadcast android:name="android.intent.action.MEDIA_SHARED" />
+    <protected-broadcast android:name="android.intent.action.MEDIA_UNSHARED" />
+    <protected-broadcast android:name="android.intent.action.MEDIA_BAD_REMOVAL" />
+    <protected-broadcast android:name="android.intent.action.MEDIA_UNMOUNTABLE" />
+    <protected-broadcast android:name="android.intent.action.MEDIA_EJECT" />
+
+    <protected-broadcast android:name="android.net.conn.CAPTIVE_PORTAL" />
+    <protected-broadcast android:name="android.net.conn.CONNECTIVITY_CHANGE" />
+    <!-- @deprecated.  Only {@link android.net.ConnectivityManager.CONNECTIVITY_ACTION} is sent. -->
+    <protected-broadcast android:name="android.net.conn.CONNECTIVITY_CHANGE_IMMEDIATE" />
+    <protected-broadcast android:name="android.net.conn.DATA_ACTIVITY_CHANGE" />
+    <protected-broadcast android:name="android.net.conn.BACKGROUND_DATA_SETTING_CHANGED" />
+    <protected-broadcast android:name="android.net.conn.CAPTIVE_PORTAL_TEST_COMPLETED" />
+
+    <protected-broadcast android:name="android.net.nsd.STATE_CHANGED" />
+
+    <protected-broadcast android:name="android.nfc.action.LLCP_LINK_STATE_CHANGED" />
+    <protected-broadcast android:name="com.android.nfc_extras.action.RF_FIELD_ON_DETECTED" />
+    <protected-broadcast android:name="com.android.nfc_extras.action.RF_FIELD_OFF_DETECTED" />
+    <protected-broadcast android:name="com.android.nfc_extras.action.AID_SELECTED" />
+
+    <protected-broadcast android:name="android.nfc.action.TRANSACTION_DETECTED" />
+
+    <protected-broadcast android:name="android.intent.action.CLEAR_DNS_CACHE" />
+    <protected-broadcast android:name="android.intent.action.PROXY_CHANGE" />
+
+    <protected-broadcast android:name="android.os.UpdateLock.UPDATE_LOCK_CHANGED" />
+
+    <protected-broadcast android:name="android.intent.action.DREAMING_STARTED" />
+    <protected-broadcast android:name="android.intent.action.DREAMING_STOPPED" />
+    <protected-broadcast android:name="android.intent.action.ANY_DATA_STATE" />
+    <protected-broadcast android:name="android.intent.action.DATA_CONNECTION_CONNECTED_TO_PROVISIONING_APN" />
+
+    <protected-broadcast android:name="com.android.server.WifiManager.action.START_SCAN" />
+    <protected-broadcast android:name="com.android.server.WifiManager.action.START_PNO" />
+    <protected-broadcast android:name="com.android.server.WifiManager.action.DELAYED_DRIVER_STOP" />
+    <protected-broadcast android:name="android.net.wifi.WIFI_STATE_CHANGED" />
+    <protected-broadcast android:name="android.net.wifi.WIFI_AP_STATE_CHANGED" />
+    <protected-broadcast android:name="android.net.wifi.WIFI_CREDENTIAL_CHANGED" />
+    <protected-broadcast android:name="android.net.wifi.WIFI_SCAN_AVAILABLE" />
+    <protected-broadcast android:name="android.net.wifi.SCAN_RESULTS" />
+    <protected-broadcast android:name="android.net.wifi.RSSI_CHANGED" />
+    <protected-broadcast android:name="android.net.wifi.STATE_CHANGE" />
+    <protected-broadcast android:name="android.net.wifi.LINK_CONFIGURATION_CHANGED" />
+    <protected-broadcast android:name="android.net.wifi.CONFIGURED_NETWORKS_CHANGE" />
+    <protected-broadcast android:name="android.net.wifi.supplicant.CONNECTION_CHANGE" />
+    <protected-broadcast android:name="android.net.wifi.supplicant.STATE_CHANGE" />
+    <protected-broadcast android:name="android.net.wifi.p2p.STATE_CHANGED" />
+    <protected-broadcast android:name="android.net.wifi.p2p.DISCOVERY_STATE_CHANGE" />
+    <protected-broadcast android:name="android.net.wifi.p2p.THIS_DEVICE_CHANGED" />
+    <protected-broadcast android:name="android.net.wifi.p2p.PEERS_CHANGED" />
+    <protected-broadcast android:name="android.net.wifi.p2p.CONNECTION_STATE_CHANGE" />
+    <protected-broadcast android:name="android.net.wifi.p2p.PERSISTENT_GROUPS_CHANGED" />
+    <protected-broadcast android:name="android.net.conn.TETHER_STATE_CHANGED" />
+    <protected-broadcast android:name="android.net.conn.INET_CONDITION_ACTION" />
+    <protected-broadcast android:name="android.net.conn.NETWORK_CONDITIONS_MEASURED" />
+    <protected-broadcast
+            android:name="android.net.ConnectivityService.action.PKT_CNT_SAMPLE_INTERVAL_ELAPSED" />
+    <protected-broadcast android:name="android.net.scoring.SCORE_NETWORKS" />
+    <protected-broadcast android:name="android.net.scoring.SCORER_CHANGED" />
+    <protected-broadcast android:name="android.intent.action.EXTERNAL_APPLICATIONS_AVAILABLE" />
+    <protected-broadcast android:name="android.intent.action.EXTERNAL_APPLICATIONS_UNAVAILABLE" />
+    <protected-broadcast android:name="android.intent.action.AIRPLANE_MODE" />
+    <protected-broadcast android:name="android.intent.action.ADVANCED_SETTINGS" />
+    <protected-broadcast android:name="android.intent.action.APPLICATION_RESTRICTIONS_CHANGED" />
+    <protected-broadcast android:name="android.intent.action.BUGREPORT_FINISHED" />
+
+    <protected-broadcast android:name="android.intent.action.ACTION_IDLE_MAINTENANCE_START" />
+    <protected-broadcast android:name="android.intent.action.ACTION_IDLE_MAINTENANCE_END" />
+
+    <protected-broadcast android:name="android.intent.action.HDMI_PLUGGED" />
+
+    <protected-broadcast android:name="android.intent.action.PHONE_STATE" />
+
+    <protected-broadcast android:name="android.intent.action.SUB_DEFAULT_CHANGED" />
+
+    <protected-broadcast android:name="android.location.GPS_ENABLED_CHANGE" />
+    <protected-broadcast android:name="android.location.PROVIDERS_CHANGED" />
+    <protected-broadcast android:name="android.location.MODE_CHANGED" />
+    <protected-broadcast android:name="android.location.GPS_FIX_CHANGE" />
+    <protected-broadcast android:name="android.net.proxy.PAC_REFRESH" />
+
+    <protected-broadcast android:name="android.telecom.action.DEFAULT_DIALER_CHANGED" />
+
+    <protected-broadcast
+        android:name="com.android.server.connectivityservice.CONNECTED_TO_PROVISIONING_NETWORK_ACTION" />
+
+    <!-- Defined in RestrictionsManager -->
+    <protected-broadcast
+        android:name="android.intent.action.PERMISSION_RESPONSE_RECEIVED" />
+    <!-- Defined in RestrictionsManager -->
+
+    <protected-broadcast android:name="android.intent.action.REQUEST_PERMISSION" />
+    <protected-broadcast android:name="android.nfc.handover.intent.action.HANDOVER_STARTED" />
+    <protected-broadcast android:name="android.nfc.handover.intent.action.TRANSFER_DONE" />
+    <protected-broadcast android:name="android.nfc.handover.intent.action.TRANSFER_PROGRESS" />
+    <protected-broadcast android:name="android.nfc.handover.intent.action.TRANSFER_DONE" />
+
+    <protected-broadcast android:name="android.intent.action.ACTION_DEFAULT_SUBSCRIPTION_CHANGED" />
+    <protected-broadcast android:name="android.intent.action.ACTION_DEFAULT_SMS_SUBSCRIPTION_CHANGED" />
+    <protected-broadcast android:name="android.intent.action.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED" />
+    <protected-broadcast android:name="android.intent.action.ACTION_DEFAULT_VOICE_SUBSCRIPTION_CHANGED" />
+    <protected-broadcast android:name="android.intent.action.ACTION_SUBINFO_CONTENT_CHANGE" />
+    <protected-broadcast android:name="android.intent.action.ACTION_SUBINFO_RECORD_UPDATED" />
+
+    <protected-broadcast android:name="android.intent.action.ACTION_SET_RADIO_CAPABILITY_DONE" />
+    <protected-broadcast android:name="android.intent.action.ACTION_SET_RADIO_CAPABILITY_FAILED" />
+
+    <protected-broadcast android:name="android.internal.policy.action.BURN_IN_PROTECTION" />
+    <protected-broadcast android:name="android.app.action.SYSTEM_UPDATE_POLICY_CHANGED" />
+    <protected-broadcast android:name="android.app.action.DEVICE_OWNER_CHANGED" />
+    <!-- ====================================================================== -->
+    <!--                          RUNTIME PERMISSIONS                           -->
+    <!-- ====================================================================== -->
+    <eat-comment />
+
+    <!-- ====================================================================== -->
+    <!-- Permissions for accessing user's contacts including personal profile   -->
+    <!-- ====================================================================== -->
+    <eat-comment />
+
+    <!-- Used for runtime permissions related to user's contacts and profile. -->
+    <permission-group android:name="android.permission-group.CONTACTS"
+        android:icon="@drawable/perm_group_contacts"
+        android:label="@string/permgrouplab_contacts"
+        android:description="@string/permgroupdesc_contacts"
+        android:priority="100" />
+
+    <!-- Allows an application to read the user's contacts data.
+        <p>Protection level: dangerous
+    -->
+    <permission android:name="android.permission.READ_CONTACTS"
+        android:permissionGroup="android.permission-group.CONTACTS"
+        android:label="@string/permlab_readContacts"
+        android:description="@string/permdesc_readContacts"
+        android:protectionLevel="dangerous" />
+
+    <!-- Allows an application to write the user's contacts data.
+         <p>Protection level: dangerous
+    -->
+    <permission android:name="android.permission.WRITE_CONTACTS"
+        android:permissionGroup="android.permission-group.CONTACTS"
+        android:label="@string/permlab_writeContacts"
+        android:description="@string/permdesc_writeContacts"
+        android:protectionLevel="dangerous" />
+
+    <!-- ====================================================================== -->
+    <!-- Permissions for accessing user's calendar                              -->
+    <!-- ====================================================================== -->
+    <eat-comment />
+
+    <!-- Used for runtime permissions related to user's calendar. -->
+    <permission-group android:name="android.permission-group.CALENDAR"
+        android:icon="@drawable/perm_group_calendar"
+        android:label="@string/permgrouplab_calendar"
+        android:description="@string/permgroupdesc_calendar"
+        android:priority="200" />
+
+    <!-- Allows an application to read the user's calendar data.
+         <p>Protection level: dangerous
+    -->
+    <permission android:name="android.permission.READ_CALENDAR"
+        android:permissionGroup="android.permission-group.CALENDAR"
+        android:label="@string/permlab_readCalendar"
+        android:description="@string/permdesc_readCalendar"
+        android:protectionLevel="dangerous" />
+
+    <!-- Allows an application to write the user's calendar data.
+         <p>Protection level: dangerous
+    -->
+    <permission android:name="android.permission.WRITE_CALENDAR"
+        android:permissionGroup="android.permission-group.CALENDAR"
+        android:label="@string/permlab_writeCalendar"
+        android:description="@string/permdesc_writeCalendar"
+        android:protectionLevel="dangerous" />
+
+    <!-- ====================================================================== -->
+    <!-- Permissions for accessing and modifying user's SMS messages            -->
+    <!-- ====================================================================== -->
+    <eat-comment />
+
+    <!-- Used for runtime permissions related to user's SMS messages. -->
+    <permission-group android:name="android.permission-group.SMS"
+        android:icon="@drawable/perm_group_sms"
+        android:label="@string/permgrouplab_sms"
+        android:description="@string/permgroupdesc_sms"
+        android:priority="300" />
+
+    <!-- Allows an application to send SMS messages.
+         <p>Protection level: dangerous
+    -->
+    <permission android:name="android.permission.SEND_SMS"
+        android:permissionGroup="android.permission-group.SMS"
+        android:label="@string/permlab_sendSms"
+        android:description="@string/permdesc_sendSms"
+        android:permissionFlags="costsMoney"
+        android:protectionLevel="dangerous" />
+
+    <!-- Allows an application to receive SMS messages.
+         <p>Protection level: dangerous
+    -->
+    <permission android:name="android.permission.RECEIVE_SMS"
+        android:permissionGroup="android.permission-group.SMS"
+        android:label="@string/permlab_receiveSms"
+        android:description="@string/permdesc_receiveSms"
+        android:protectionLevel="dangerous"/>
+
+    <!-- Allows an application to read SMS messages.
+         <p>Protection level: dangerous
+    -->
+    <permission android:name="android.permission.READ_SMS"
+        android:permissionGroup="android.permission-group.SMS"
+        android:label="@string/permlab_readSms"
+        android:description="@string/permdesc_readSms"
+        android:protectionLevel="dangerous" />
+
+    <!-- Allows an application to receive WAP push messages.
+         <p>Protection level: dangerous
+    -->
+    <permission android:name="android.permission.RECEIVE_WAP_PUSH"
+        android:permissionGroup="android.permission-group.SMS"
+        android:label="@string/permlab_receiveWapPush"
+        android:description="@string/permdesc_receiveWapPush"
+        android:protectionLevel="dangerous" />
+
+    <!-- Allows an application to monitor incoming MMS messages.
+        <p>Protection level: dangerous
+    -->
+    <permission android:name="android.permission.RECEIVE_MMS"
+        android:permissionGroup="android.permission-group.SMS"
+        android:label="@string/permlab_receiveMms"
+        android:description="@string/permdesc_receiveMms"
+        android:protectionLevel="dangerous" />
+
+    <!-- Allows an application to read previously received cell broadcast
+         messages and to register a content observer to get notifications when
+         a cell broadcast has been received and added to the database. For
+         emergency alerts, the database is updated immediately after the
+         alert dialog and notification sound/vibration/speech are presented.
+         The "read" column is then updated after the user dismisses the alert.
+         This enables supplementary emergency assistance apps to start loading
+         additional emergency information (if Internet access is available)
+         when the alert is first received, and to delay presenting the info
+         to the user until after the initial alert dialog is dismissed.
+         <p>Protection level: dangerous
+         @hide Pending API council approval -->
+    <permission android:name="android.permission.READ_CELL_BROADCASTS"
+        android:permissionGroup="android.permission-group.SMS"
+        android:label="@string/permlab_readCellBroadcasts"
+        android:description="@string/permdesc_readCellBroadcasts"
+        android:protectionLevel="dangerous" />
+
+    <!-- ====================================================================== -->
+    <!-- Permissions for accessing external storage                             -->
+    <!-- ====================================================================== -->
+    <eat-comment />
+
+    <!-- Used for runtime permissions related to the shared external storage. -->
+    <permission-group android:name="android.permission-group.STORAGE"
+        android:icon="@drawable/perm_group_storage"
+        android:label="@string/permgrouplab_storage"
+        android:description="@string/permgroupdesc_storage"
+        android:priority="900" />
+
+    <!-- Allows an application to read from external storage.
+     <p>Any app that declares the {@link #WRITE_EXTERNAL_STORAGE} permission is implicitly
+     granted this permission.</p>
+     <p>This permission is enforced starting in API level 19.  Before API level 19, this
+     permission is not enforced and all apps still have access to read from external storage.
+     You can test your app with the permission enforced by enabling <em>Protect USB
+     storage</em> under Developer options in the Settings app on a device running Android 4.1 or
+     higher.</p>
+     <p>Also starting in API level 19, this permission is <em>not</em> required to
+     read/write files in your application-specific directories returned by
+     {@link android.content.Context#getExternalFilesDir} and
+     {@link android.content.Context#getExternalCacheDir}.
+     <p class="note"><strong>Note:</strong> If <em>both</em> your <a
+     href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#min">{@code
+     minSdkVersion}</a> and <a
+     href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#target">{@code
+     targetSdkVersion}</a> values are set to 3 or lower, the system implicitly
+     grants your app this permission. If you don't need this permission, be sure your <a
+     href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#target">{@code
+     targetSdkVersion}</a> is 4 or higher.
+     <p>Protection level: dangerous
+     -->
+    <permission android:name="android.permission.READ_EXTERNAL_STORAGE"
+        android:permissionGroup="android.permission-group.STORAGE"
+        android:label="@string/permlab_sdcardRead"
+        android:description="@string/permdesc_sdcardRead"
+        android:protectionLevel="dangerous" />
+
+    <!-- Allows an application to write to external storage.
+         <p class="note"><strong>Note:</strong> If <em>both</em> your <a
+         href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#min">{@code
+         minSdkVersion}</a> and <a
+         href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#target">{@code
+         targetSdkVersion}</a> values are set to 3 or lower, the system implicitly
+         grants your app this permission. If you don't need this permission, be sure your <a
+         href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#target">{@code
+         targetSdkVersion}</a> is 4 or higher.
+         <p>Starting in API level 19, this permission is <em>not</em> required to
+         read/write files in your application-specific directories returned by
+         {@link android.content.Context#getExternalFilesDir} and
+         {@link android.content.Context#getExternalCacheDir}.
+         <p>Protection level: dangerous
+    -->
+    <permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
+        android:permissionGroup="android.permission-group.STORAGE"
+        android:label="@string/permlab_sdcardWrite"
+        android:description="@string/permdesc_sdcardWrite"
+        android:protectionLevel="dangerous" />
+
+    <!-- ====================================================================== -->
+    <!-- Permissions for accessing the device location                          -->
+    <!-- ====================================================================== -->
+    <eat-comment />
+
+    <!-- Used for permissions that allow accessing the device location. -->
+    <permission-group android:name="android.permission-group.LOCATION"
+        android:icon="@drawable/perm_group_location"
+        android:label="@string/permgrouplab_location"
+        android:description="@string/permgroupdesc_location"
+        android:priority="400" />
+
+    <!-- Allows an app to access precise location.
+         <p>Protection level: dangerous
+    -->
+    <permission android:name="android.permission.ACCESS_FINE_LOCATION"
+        android:permissionGroup="android.permission-group.LOCATION"
+        android:label="@string/permlab_accessFineLocation"
+        android:description="@string/permdesc_accessFineLocation"
+        android:protectionLevel="dangerous" />
+
+    <!-- Allows an app to access approximate location.
+         <p>Protection level: dangerous
+    -->
+    <permission android:name="android.permission.ACCESS_COARSE_LOCATION"
+        android:permissionGroup="android.permission-group.LOCATION"
+        android:label="@string/permlab_accessCoarseLocation"
+        android:description="@string/permdesc_accessCoarseLocation"
+        android:protectionLevel="dangerous" />
+
+    <!-- ====================================================================== -->
+    <!-- Permissions for accessing the device telephony                         -->
+    <!-- ====================================================================== -->
+    <eat-comment />
+
+    <!-- Used for permissions that are associated telephony features. -->
+    <permission-group android:name="android.permission-group.PHONE"
+        android:icon="@drawable/perm_group_phone_calls"
+        android:label="@string/permgrouplab_phone"
+        android:description="@string/permgroupdesc_phone"
+        android:priority="500" />
+
+    <!-- Allows read only access to phone state.
+         <p class="note"><strong>Note:</strong> If <em>both</em> your <a
+         href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#min">{@code
+         minSdkVersion}</a> and <a
+         href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#target">{@code
+         targetSdkVersion}</a> values are set to 3 or lower, the system implicitly
+         grants your app this permission. If you don't need this permission, be sure your <a
+         href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#target">{@code
+         targetSdkVersion}</a> is 4 or higher.
+         <p>Protection level: dangerous
+    -->
+    <permission android:name="android.permission.READ_PHONE_STATE"
+        android:permissionGroup="android.permission-group.PHONE"
+        android:label="@string/permlab_readPhoneState"
+        android:description="@string/permdesc_readPhoneState"
+        android:protectionLevel="dangerous" />
+
+    <!-- Allows an application to initiate a phone call without going through
+        the Dialer user interface for the user to confirm the call.
+        <p>Protection level: dangerous
+    -->
+    <permission android:name="android.permission.CALL_PHONE"
+        android:permissionGroup="android.permission-group.PHONE"
+        android:permissionFlags="costsMoney"
+        android:label="@string/permlab_callPhone"
+        android:description="@string/permdesc_callPhone"
+        android:protectionLevel="dangerous" />
+
+    <!-- Allows an application to access the IMS call service: making and
+         modifying a call
+        <p>Protection level: signature|system
+        @hide
+    -->
+    <permission android:name="android.permission.ACCESS_IMS_CALL_SERVICE"
+        android:permissionGroup="android.permission-group.PHONE"
+        android:label="@string/permlab_accessImsCallService"
+        android:description="@string/permdesc_accessImsCallService"
+        android:protectionLevel="signature|system" />
+
+    <!-- Allows an application to read the user's call log.
+         <p class="note"><strong>Note:</strong> If your app uses the
+         {@link #READ_CONTACTS} permission and <em>both</em> your <a
+         href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#min">{@code
+         minSdkVersion}</a> and <a
+         href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#target">{@code
+         targetSdkVersion}</a> values are set to 15 or lower, the system implicitly
+         grants your app this permission. If you don't need this permission, be sure your <a
+         href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#target">{@code
+         targetSdkVersion}</a> is 16 or higher.</p>
+         <p>Protection level: dangerous
+    -->
+    <permission android:name="android.permission.READ_CALL_LOG"
+        android:permissionGroup="android.permission-group.PHONE"
+        android:label="@string/permlab_readCallLog"
+        android:description="@string/permdesc_readCallLog"
+        android:protectionLevel="dangerous" />
+
+    <!-- Allows an application to write (but not read) the user's
+         contacts data.
+         <p class="note"><strong>Note:</strong> If your app uses the
+         {@link #WRITE_CONTACTS} permission and <em>both</em> your <a
+         href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#min">{@code
+         minSdkVersion}</a> and <a
+         href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#target">{@code
+         targetSdkVersion}</a> values are set to 15 or lower, the system implicitly
+         grants your app this permission. If you don't need this permission, be sure your <a
+         href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#target">{@code
+         targetSdkVersion}</a> is 16 or higher.</p>
+         <p>Protection level: dangerous
+    -->
+    <permission android:name="android.permission.WRITE_CALL_LOG"
+        android:permissionGroup="android.permission-group.PHONE"
+        android:label="@string/permlab_writeCallLog"
+        android:description="@string/permdesc_writeCallLog"
+        android:protectionLevel="dangerous" />
+
+    <!-- Allows an application to add voicemails into the system.
+         <p>Protection level: dangerous
+    -->
+    <permission android:name="com.android.voicemail.permission.ADD_VOICEMAIL"
+        android:permissionGroup="android.permission-group.PHONE"
+        android:label="@string/permlab_addVoicemail"
+        android:description="@string/permdesc_addVoicemail"
+        android:protectionLevel="dangerous" />
+
+    <!-- Allows an application to use SIP service.
+         <p>Protection level: dangerous
+    -->
+    <permission android:name="android.permission.USE_SIP"
+        android:permissionGroup="android.permission-group.PHONE"
+        android:description="@string/permdesc_use_sip"
+        android:label="@string/permlab_use_sip"
+        android:protectionLevel="dangerous"/>
+
+    <!-- Allows an application to see the number being dialed during an outgoing
+         call with the option to redirect the call to a different number or
+         abort the call altogether.
+         <p>Protection level: dangerous
+    -->
+    <permission android:name="android.permission.PROCESS_OUTGOING_CALLS"
+        android:permissionGroup="android.permission-group.PHONE"
+        android:label="@string/permlab_processOutgoingCalls"
+        android:description="@string/permdesc_processOutgoingCalls"
+        android:protectionLevel="dangerous" />
+
+    <!-- ====================================================================== -->
+    <!-- Permissions for accessing the device microphone                        -->
+    <!-- ====================================================================== -->
+    <eat-comment />
+
+    <!-- Used for permissions that are associated with accessing
+         microphone audio from the device. Note that phone calls also capture audio
+         but are in a separate (more visible) permission group. -->
+    <permission-group android:name="android.permission-group.MICROPHONE"
+        android:icon="@drawable/perm_group_microphone"
+        android:label="@string/permgrouplab_microphone"
+        android:description="@string/permgroupdesc_microphone"
+        android:priority="600" />
+
+    <!-- Allows an application to record audio.
+         <p>Protection level: dangerous
+    -->
+    <permission android:name="android.permission.RECORD_AUDIO"
+        android:permissionGroup="android.permission-group.MICROPHONE"
+        android:label="@string/permlab_recordAudio"
+        android:description="@string/permdesc_recordAudio"
+        android:protectionLevel="dangerous"/>
+
+    <!-- ====================================================================== -->
+    <!-- Permissions for accessing the device camera                            -->
+    <!-- ====================================================================== -->
+    <eat-comment />
+
+    <!-- Used for permissions that are associated with accessing
+     camera or capturing images/video from the device. -->
+    <permission-group android:name="android.permission-group.CAMERA"
+        android:icon="@drawable/perm_group_camera"
+        android:label="@string/permgrouplab_camera"
+        android:description="@string/permgroupdesc_camera"
+        android:priority="700" />
+
+    <!-- Required to be able to access the camera device.
+         <p>This will automatically enforce the <a
+         href="{@docRoot}guide/topics/manifest/uses-feature-element.html">{@code
+         &lt;uses-feature&gt;}</a> manifest element for <em>all</em> camera features.
+         If you do not require all camera features or can properly operate if a camera
+         is not available, then you must modify your manifest as appropriate in order to
+         install on devices that don't support all camera features.</p>
+         <p>Protection level: dangerous
+    -->
+    <permission android:name="android.permission.CAMERA"
+        android:permissionGroup="android.permission-group.CAMERA"
+        android:label="@string/permlab_camera"
+        android:description="@string/permdesc_camera"
+        android:protectionLevel="dangerous" />
+
+
+    <!-- ====================================================================== -->
+    <!-- Permissions for accessing the device sensors                           -->
+    <!-- ====================================================================== -->
+    <eat-comment />
+
+    <!-- Used for permissions that are associated with accessing
+         camera or capturing images/video from the device. -->
+    <permission-group android:name="android.permission-group.SENSORS"
+        android:icon="@drawable/perm_group_sensors"
+        android:label="@string/permgrouplab_sensors"
+        android:description="@string/permgroupdesc_sensors"
+        android:priority="800" />
+
+    <!-- Allows an application to access data from sensors that the user uses to
+         measure what is happening inside his/her body, such as heart rate.
+         <p>Protection level: dangerous -->
+    <permission android:name="android.permission.BODY_SENSORS"
+        android:permissionGroup="android.permission-group.SENSORS"
+        android:label="@string/permlab_bodySensors"
+        android:description="@string/permdesc_bodySensors"
+        android:protectionLevel="dangerous" />
+
+    <!-- Allows an app to use fingerprint hardware.
+         <p>Protection level: normal
+    -->
+    <permission android:name="android.permission.USE_FINGERPRINT"
+        android:permissionGroup="android.permission-group.SENSORS"
+        android:label="@string/permlab_useFingerprint"
+        android:description="@string/permdesc_useFingerprint"
+        android:protectionLevel="normal" />
+
+    <!-- ====================================================================== -->
+    <!-- REMOVED PERMISSIONS                                                    -->
+    <!-- ====================================================================== -->
+
+    <!-- @hide We need to keep this around for backwards compatibility -->
+    <permission android:name="android.permission.READ_PROFILE"
+        android:protectionLevel="normal"
+        android:permissionFlags="hidden"/>
+
+    <!-- @hide We need to keep this around for backwards compatibility -->
+    <permission android:name="android.permission.WRITE_PROFILE"
+        android:protectionLevel="normal"
+        android:permissionFlags="hidden"/>
+
+    <!-- @hide We need to keep this around for backwards compatibility -->
+    <permission android:name="android.permission.READ_SOCIAL_STREAM"
+        android:protectionLevel="normal"
+        android:permissionFlags="hidden"/>
+
+    <!-- @hide We need to keep this around for backwards compatibility -->
+    <permission android:name="android.permission.WRITE_SOCIAL_STREAM"
+        android:protectionLevel="normal"
+        android:permissionFlags="hidden"/>
+
+    <!-- @hide We need to keep this around for backwards compatibility -->
+    <permission android:name="android.permission.READ_USER_DICTIONARY"
+        android:protectionLevel="normal"
+        android:permissionFlags="hidden"/>
+
+    <!-- @hide We need to keep this around for backwards compatibility -->
+    <permission android:name="android.permission.WRITE_USER_DICTIONARY"
+        android:protectionLevel="normal"
+        android:permissionFlags="hidden"/>
+
+    <!-- @hide We need to keep this around for backwards compatibility -->
+    <permission android:name="android.permission.WRITE_SMS"
+        android:protectionLevel="normal"
+        android:permissionFlags="hidden"/>
+
+    <!-- @hide We need to keep this around for backwards compatibility -->
+    <permission android:name="com.android.browser.permission.READ_HISTORY_BOOKMARKS"
+        android:protectionLevel="normal"
+        android:permissionFlags="hidden"/>
+
+    <!-- @hide We need to keep this around for backwards compatibility -->
+    <permission android:name="com.android.browser.permission.WRITE_HISTORY_BOOKMARKS"
+        android:protectionLevel="normal"
+        android:permissionFlags="hidden"/>
+
+    <!-- @hide We need to keep this around for backwards compatibility -->
+    <permission android:name="android.permission.AUTHENTICATE_ACCOUNTS"
+        android:protectionLevel="normal"
+        android:permissionFlags="hidden"/>
+
+    <!-- @hide We need to keep this around for backwards compatibility -->
+    <permission android:name="android.permission.MANAGE_ACCOUNTS"
+        android:protectionLevel="normal"
+        android:permissionFlags="hidden"/>
+
+    <!-- @hide We need to keep this around for backwards compatibility -->
+    <permission android:name="android.permission.USE_CREDENTIALS"
+        android:protectionLevel="normal"
+        android:permissionFlags="hidden"/>
+
+    <!-- @hide We need to keep this around for backwards compatibility -->
+    <permission android:name="android.permission.SUBSCRIBED_FEEDS_READ"
+        android:protectionLevel="normal"
+        android:permissionFlags="hidden"/>
+
+    <!-- @hide We need to keep this around for backwards compatibility -->
+    <permission android:name="android.permission.SUBSCRIBED_FEEDS_WRITE"
+        android:protectionLevel="normal"
+        android:permissionFlags="hidden"/>
+
+    <!-- ====================================================================== -->
+    <!-- INSTALL PERMISSIONS                                                    -->
+    <!-- ====================================================================== -->
+
+    <!-- ================================== -->
+    <!-- Permissions for accessing messages -->
+    <!-- ================================== -->
+    <eat-comment />
+
+    <!-- @SystemApi Allows an application (Phone) to send a request to other applications
+         to handle the respond-via-message action during incoming calls.
+         <p>Not for use by third-party applications. -->
+    <permission android:name="android.permission.SEND_RESPOND_VIA_MESSAGE"
+        android:protectionLevel="signature|privileged" />
+
+    <!-- Allows an application to filter carrier specific sms.
+         @hide -->
+    <permission android:name="android.permission.CARRIER_FILTER_SMS"
+        android:protectionLevel="signature|privileged" />
+
+    <!-- @SystemApi Allows an application to receive emergency cell broadcast messages,
+         to record or display them to the user.
+         <p>Not for use by third-party applications.
+         @hide Pending API council approval -->
+    <permission android:name="android.permission.RECEIVE_EMERGENCY_BROADCAST"
+        android:protectionLevel="signature|privileged" />
+
+    <!-- Allows an application to monitor incoming Bluetooth MAP messages, to record
+         or perform processing on them. -->
+    <!-- @hide -->
+    <permission android:name="android.permission.RECEIVE_BLUETOOTH_MAP"
+        android:protectionLevel="signature|privileged" />
+
+    <!-- @SystemApi @hide Allows an application to execute contacts directory search.
+         This should only be used by ContactsProvider.
+         <p>Not for use by third-party applications. -->
+    <permission android:name="android.permission.BIND_DIRECTORY_SEARCH"
+        android:protectionLevel="signature|privileged" />
+
+    <!-- =============================================================== -->
+    <!-- Permissions for setting the device alarm                        -->
+    <!-- =============================================================== -->
+    <eat-comment />
+
+    <!-- Allows an application to broadcast an Intent to set an alarm for the user.
+         <p>Protection level: normal
+    -->
+    <permission android:name="com.android.alarm.permission.SET_ALARM"
+        android:label="@string/permlab_setAlarm"
+        android:description="@string/permdesc_setAlarm"
+        android:protectionLevel="normal" />
+
+    <!-- =============================================================== -->
+    <!-- Permissions for accessing the user voicemail                    -->
+    <!-- =============================================================== -->
+    <eat-comment />
+
+    <!-- Allows an application to modify and remove existing voicemails in the system
+        <p>Protection level: system|signature
+    -->
+    <permission android:name="com.android.voicemail.permission.WRITE_VOICEMAIL"
+        android:protectionLevel="system|signature" />
+
+    <!-- Allows an application to read voicemails in the system.
+         <p>Protection level: system|signature
+    -->
+    <permission android:name="com.android.voicemail.permission.READ_VOICEMAIL"
+        android:protectionLevel="system|signature" />
+
+    <!-- ======================================= -->
+    <!-- Permissions for accessing location info -->
+    <!-- ======================================= -->
+    <eat-comment />
+
+    <!-- Allows an application to access extra location provider commands
+         <p>Protection level: normal
+    -->
+    <permission android:name="android.permission.ACCESS_LOCATION_EXTRA_COMMANDS"
+        android:label="@string/permlab_accessLocationExtraCommands"
+        android:description="@string/permdesc_accessLocationExtraCommands"
+        android:protectionLevel="normal" />
+
+    <!-- @SystemApi Allows an application to install a location provider into the Location Manager.
+    <p>Not for use by third-party applications. -->
+    <permission android:name="android.permission.INSTALL_LOCATION_PROVIDER"
+        android:protectionLevel="signature|privileged" />
+
+    <!-- @SystemApi @hide Allows HDMI-CEC service to access device and configuration files.
+         This should only be used by HDMI-CEC service.
+    -->
+    <permission android:name="android.permission.HDMI_CEC"
+        android:protectionLevel="signature|privileged" />
+
+    <!-- @SystemApi Allows an application to use location features in hardware,
+         such as the geofencing api.
+         <p>Not for use by third-party applications. -->
+    <permission android:name="android.permission.LOCATION_HARDWARE"
+        android:protectionLevel="signature|privileged" />
+    <uses-permission android:name="android.permission.LOCATION_HARDWARE"/>
+
+    <!-- @SystemApi Allows an application to create mock location providers for testing.
+         <p>Protection level: signature
+         @hide
+    -->
+    <permission android:name="android.permission.ACCESS_MOCK_LOCATION"
+        android:protectionLevel="signature" />
+
+    <!-- ======================================= -->
+    <!-- Permissions for accessing networks -->
+    <!-- ======================================= -->
+    <eat-comment />
+
+    <!-- Allows applications to open network sockets.
+         <p>Protection level: normal
+    -->
+    <permission android:name="android.permission.INTERNET"
+        android:description="@string/permdesc_createNetworkSockets"
+        android:label="@string/permlab_createNetworkSockets"
+        android:protectionLevel="normal" />
+
+    <!-- Allows applications to access information about networks
+         <p>Protection level: normal
+    -->
+    <permission android:name="android.permission.ACCESS_NETWORK_STATE"
+        android:description="@string/permdesc_accessNetworkState"
+        android:label="@string/permlab_accessNetworkState"
+        android:protectionLevel="normal" />
+
+    <!-- Allows applications to access information about Wi-Fi networks.
+         <p>Protection level: normal
+    -->
+    <permission android:name="android.permission.ACCESS_WIFI_STATE"
+        android:description="@string/permdesc_accessWifiState"
+        android:label="@string/permlab_accessWifiState"
+        android:protectionLevel="normal" />
+
+    <!-- Allows applications to change Wi-Fi connectivity state.
+         <p>Protection level: normal
+    -->
+    <permission android:name="android.permission.CHANGE_WIFI_STATE"
+        android:description="@string/permdesc_changeWifiState"
+        android:label="@string/permlab_changeWifiState"
+        android:protectionLevel="normal" />
+
+    <!-- @SystemApi @hide Allows applications to read Wi-Fi credential.
+         <p>Not for use by third-party applications. -->
+    <permission android:name="android.permission.READ_WIFI_CREDENTIAL"
+        android:protectionLevel="signature|privileged" />
+
+    <!-- @SystemApi @hide Allow system apps to receive broadcast
+         when a wifi network credential is changed.
+         <p>Not for use by third-party applications. -->
+    <permission android:name="android.permission.RECEIVE_WIFI_CREDENTIAL_CHANGE"
+        android:protectionLevel="signature|privileged" />
+
+    <!-- @SystemApi @hide Allows an application to modify any wifi configuration, even if created
+     by another application. Once reconfigured the original creator cannot make any further
+     modifications.
+     <p>Not for use by third-party applications. -->
+    <permission android:name="android.permission.OVERRIDE_WIFI_CONFIG"
+        android:protectionLevel="signature|privileged" />
+
+    <!-- @hide -->
+    <permission android:name="android.permission.ACCESS_WIMAX_STATE"
+        android:description="@string/permdesc_accessWimaxState"
+        android:label="@string/permlab_accessWimaxState"
+        android:protectionLevel="normal" />
+
+    <!-- @hide -->
+    <permission android:name="android.permission.CHANGE_WIMAX_STATE"
+        android:description="@string/permdesc_changeWimaxState"
+        android:label="@string/permlab_changeWimaxState"
+        android:protectionLevel="normal" />
+
+    <!-- Allows applications to act as network scorers. @hide @SystemApi-->
+    <permission android:name="android.permission.SCORE_NETWORKS"
+        android:protectionLevel="signature|privileged" />
+
+    <!-- ======================================= -->
+    <!-- Permissions for short range, peripheral networks -->
+    <!-- ======================================= -->
+    <eat-comment />
+
+    <!-- Allows applications to connect to paired bluetooth devices.
+         <p>Protection level: normal
+    -->
+    <permission android:name="android.permission.BLUETOOTH"
+        android:description="@string/permdesc_bluetooth"
+        android:label="@string/permlab_bluetooth"
+        android:protectionLevel="normal" />
+
+    <!-- Allows applications to discover and pair bluetooth devices.
+         <p>Protection level: normal
+    -->
+    <permission android:name="android.permission.BLUETOOTH_ADMIN"
+        android:description="@string/permdesc_bluetoothAdmin"
+        android:label="@string/permlab_bluetoothAdmin"
+        android:protectionLevel="normal" />
+
+    <!-- @SystemApi Allows applications to pair bluetooth devices without user interaction, and to
+         allow or disallow phonebook access or message access.
+         This is not available to third party applications. -->
+    <permission android:name="android.permission.BLUETOOTH_PRIVILEGED"
+        android:protectionLevel="system|signature" />
+
+    <!-- Control access to email providers exclusively for Bluetooth
+         @hide
+    -->
+    <permission android:name="android.permission.BLUETOOTH_MAP"
+        android:protectionLevel="signature" />
+
+    <!-- Allows bluetooth stack to access files
+         @hide This should only be used by Bluetooth apk.
+    -->
+    <permission android:name="android.permission.BLUETOOTH_STACK"
+        android:protectionLevel="signature" />
+
+    <!-- Allows applications to perform I/O operations over NFC.
+         <p>Protection level: normal
+    -->
+    <permission android:name="android.permission.NFC"
+        android:description="@string/permdesc_nfc"
+        android:label="@string/permlab_nfc"
+        android:protectionLevel="normal" />
+
+    <!-- @SystemApi Allows an internal user to use privileged ConnectivityManager APIs.
+         @hide -->
+    <permission android:name="android.permission.CONNECTIVITY_INTERNAL"
+        android:protectionLevel="signature|privileged" />
+
+    <!-- Allows a system application to access hardware packet offload capabilities.
+         @hide -->
+    <permission android:name="android.permission.PACKET_KEEPALIVE_OFFLOAD"
+        android:protectionLevel="signature|privileged" />
+
+    <!-- @SystemApi
+         @hide -->
+    <permission android:name="android.permission.RECEIVE_DATA_ACTIVITY_CHANGE"
+        android:protectionLevel="signature|privileged" />
+
+    <!-- @SystemApi Allows access to the loop radio (Android@Home mesh network) device.
+         @hide -->
+    <permission android:name="android.permission.LOOP_RADIO"
+        android:protectionLevel="signature|privileged" />
+
+    <!-- Allows sending and receiving handover transfer status from Wifi and Bluetooth
+         @hide -->
+    <permission android:name="android.permission.NFC_HANDOVER_STATUS"
+        android:protectionLevel="signature|privileged" />
+
+    <!-- ================================== -->
+    <!-- Permissions for accessing accounts -->
+    <!-- ================================== -->
+    <eat-comment />
+
+    <!-- Allows access to the list of accounts in the Accounts Service.
+        <p>Protection level: normal
+    -->
+    <permission android:name="android.permission.GET_ACCOUNTS"
+        android:permissionGroup="android.permission-group.CONTACTS"
+        android:protectionLevel="dangerous"
+        android:description="@string/permdesc_getAccounts"
+        android:label="@string/permlab_getAccounts" />
+
+    <!-- @SystemApi Allows applications to call into AccountAuthenticators.
+    <p>Not for use by third-party applications. -->
+    <permission android:name="android.permission.ACCOUNT_MANAGER"
+        android:protectionLevel="signature" />
+
+    <!-- ================================== -->
+    <!-- Permissions for accessing hardware that may effect battery life-->
+    <!-- ================================== -->
+    <eat-comment />
+
+    <!-- Allows applications to enter Wi-Fi Multicast mode.
+         <p>Protection level: normal
+    -->
+    <permission android:name="android.permission.CHANGE_WIFI_MULTICAST_STATE"
+        android:description="@string/permdesc_changeWifiMulticastState"
+        android:label="@string/permlab_changeWifiMulticastState"
+        android:protectionLevel="normal" />
+
+    <!-- Allows access to the vibrator.
+         <p>Protection level: normal
+    -->
+    <permission android:name="android.permission.VIBRATE"
+        android:label="@string/permlab_vibrate"
+        android:description="@string/permdesc_vibrate"
+        android:protectionLevel="normal" />
+
+    <!-- Allows access to the flashlight.
+         <p>Protection level: normal
+    -->
+    <permission android:name="android.permission.FLASHLIGHT"
+        android:label="@string/permlab_flashlight"
+        android:description="@string/permdesc_flashlight"
+        android:protectionLevel="normal" />
+
+    <!-- Allows using PowerManager WakeLocks to keep processor from sleeping or screen
+         from dimming.
+         <p>Protection level: normal
+    -->
+    <permission android:name="android.permission.WAKE_LOCK"
+        android:label="@string/permlab_wakeLock"
+        android:description="@string/permdesc_wakeLock"
+        android:protectionLevel="normal" />
+
+    <!-- Allows using the device's IR transmitter, if available.
+         <p>Protection level: normal
+    -->
+    <permission android:name="android.permission.TRANSMIT_IR"
+        android:label="@string/permlab_transmitIr"
+        android:description="@string/permdesc_transmitIr"
+        android:protectionLevel="normal" />
+
+    <!-- ==================================================== -->
+    <!-- Permissions related to changing audio settings   -->
+    <!-- ==================================================== -->
+    <eat-comment />
+
+    <!-- Allows an application to modify global audio settings.
+         <p>Protection level: normal
+    -->
+    <permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"
+        android:label="@string/permlab_modifyAudioSettings"
+        android:description="@string/permdesc_modifyAudioSettings"
+        android:protectionLevel="normal" />
+
+    <!-- ================================== -->
+    <!-- Permissions for accessing hardware -->
+    <!-- ================================== -->
+    <eat-comment />
+
+    <!-- @SystemApi Allows an application to manage preferences and permissions for USB devices
+         @hide -->
+    <permission android:name="android.permission.MANAGE_USB"
+        android:protectionLevel="signature|privileged" />
+
+    <!-- @SystemApi Allows an application to access the MTP USB kernel driver.
+         For use only by the device side MTP implementation.
+         @hide -->
+    <permission android:name="android.permission.ACCESS_MTP"
+        android:protectionLevel="signature|privileged" />
+
+    <!-- @SystemApi Allows access to hardware peripherals.  Intended only for hardware testing.
+         <p>Not for use by third-party applications.
+         @hide
+    -->
+    <permission android:name="android.permission.HARDWARE_TEST"
+        android:protectionLevel="signature" />
+
+    <!-- @SystemApi Allows access to FM
+         @hide This is not a third-party API (intended for system apps).-->
+    <permission android:name="android.permission.ACCESS_FM_RADIO"
+        android:protectionLevel="signature|privileged" />
+
+    <!-- Allows access to configure network interfaces, configure/use IPSec, etc.
+         @hide -->
+    <permission android:name="android.permission.NET_ADMIN"
+        android:protectionLevel="signature" />
+
+    <!-- Allows registration for remote audio playback. @hide -->
+    <permission android:name="android.permission.REMOTE_AUDIO_PLAYBACK"
+        android:protectionLevel="signature" />
+
+    <!-- @SystemApi Allows TvInputService to access underlying TV input hardware such as
+         built-in tuners and HDMI-in's.
+         @hide This should only be used by OEM's TvInputService's.
+    -->
+    <permission android:name="android.permission.TV_INPUT_HARDWARE"
+        android:protectionLevel="signature|privileged" />
+
+    <!-- @SystemApi Allows to capture a frame of TV input hardware such as
+         built-in tuners and HDMI-in's.
+         @hide <p>Not for use by third-party applications.
+    -->
+    <permission android:name="android.permission.CAPTURE_TV_INPUT"
+        android:protectionLevel="signature|privileged" />
+
+    <!-- @hide Allows TvInputService to access DVB device.
+   <p>Not for use by third-party applications. -->
+    <permission android:name="android.permission.DVB_DEVICE"
+        android:protectionLevel="signature|privileged" />
+
+    <!-- @hide Allows enabling/disabling OEM unlock
+   <p>Not for use by third-party applications. -->
+    <permission android:name="android.permission.OEM_UNLOCK_STATE"
+        android:protectionLevel="signature" />
+
+    <!-- @hide Allows querying state of PersistentDataBlock
+   <p>Not for use by third-party applications. -->
+    <permission android:name="android.permission.ACCESS_PDB_STATE"
+        android:protectionLevel="signature" />
+
+    <!-- @hide Allows system update service to notify device owner about pending updates.
+   <p>Not for use by third-party applications. -->
+    <permission android:name="android.permission.NOTIFY_PENDING_SYSTEM_UPDATE"
+        android:protectionLevel="signature|privileged" />
+
+    <!-- =========================================== -->
+    <!-- Permissions associated with camera and image capture -->
+    <!-- =========================================== -->
+    <eat-comment />
+
+    <!-- @SystemApi Allows disabling the transmit-indicator LED that is normally on when
+         a camera is in use by an application.
+         @hide -->
+    <permission android:name="android.permission.CAMERA_DISABLE_TRANSMIT_LED"
+        android:protectionLevel="signature|privileged" />
+
+    <!-- Allows sending the camera service notifications about system-wide events.
+        @hide -->
+    <permission android:name="android.permission.CAMERA_SEND_SYSTEM_EVENTS"
+        android:protectionLevel="signature|privileged" />
+
+    <!-- =========================================== -->
+    <!-- Permissions associated with telephony state -->
+    <!-- =========================================== -->
+    <eat-comment />
+
+    <!-- @SystemApi Allows modification of the telephony state - power on, mmi, etc.
+         Does not include placing calls.
+         <p>Not for use by third-party applications. -->
+    <permission android:name="android.permission.MODIFY_PHONE_STATE"
+        android:protectionLevel="signature|privileged" />
+
+    <!-- Allows read only access to precise phone state.
+         @hide Pending API council approval -->
+    <permission android:name="android.permission.READ_PRECISE_PHONE_STATE"
+        android:protectionLevel="signature|privileged" />
+
+    <!-- @SystemApi Allows read access to privileged phone state.
+         @hide Used internally. -->
+    <permission android:name="android.permission.READ_PRIVILEGED_PHONE_STATE"
+        android:protectionLevel="signature|privileged" />
+
+    <!-- @SystemApi Protects the ability to register any PhoneAccount with
+         PhoneAccount#CAPABILITY_SIM_SUBSCRIPTION. This capability indicates that the PhoneAccount
+         corresponds to a device SIM.
+         @hide -->
+    <permission android:name="android.permission.REGISTER_SIM_SUBSCRIPTION"
+        android:protectionLevel="system|signature" />
+
+    <!-- @SystemApi Protects the ability to register any PhoneAccount with
+         PhoneAccount#CAPABILITY_CALL_PROVIDER.
+         @hide -->
+    <permission android:name="android.permission.REGISTER_CALL_PROVIDER"
+        android:protectionLevel="system|signature" />
+
+    <!-- @SystemApi Protects the ability to register any PhoneAccount with
+         PhoneAccount#CAPABILITY_CONNECTION_MANAGER
+         @hide -->
+    <permission android:name="android.permission.REGISTER_CONNECTION_MANAGER"
+        android:protectionLevel="system|signature" />
+
+    <!-- Must be required by a {@link android.telecom.InCallService},
+         to ensure that only the system can bind to it.
+         <p>Protection level: system|signature
+    -->
+    <permission android:name="android.permission.BIND_INCALL_SERVICE"
+        android:protectionLevel="system|signature" />
+
+    <!-- Must be required by a {@link android.telecom.ConnectionService},
+         to ensure that only the system can bind to it.
+         @deprecated {@link android.telecom.ConnectionService}s should require
+                 android.permission.BIND_TELECOM_CONNECTION_SERVICE instead.
+         @SystemApi
+         @hide -->
+    <permission android:name="android.permission.BIND_CONNECTION_SERVICE"
+        android:protectionLevel="system|signature" />
+
+    <!-- Must be required by a {@link android.telecom.ConnectionService},
+         to ensure that only the system can bind to it.
+         <p>Protection level: system|signature
+    -->
+    <permission android:name="android.permission.BIND_TELECOM_CONNECTION_SERVICE"
+        android:protectionLevel="system|signature" />
+
+    <!-- @SystemApi Allows an application to control the in-call experience.
+         @hide -->
+    <permission android:name="android.permission.CONTROL_INCALL_EXPERIENCE"
+        android:protectionLevel="system|signature" />
+
+    <!-- Allows an application to receive STK related commands.
+         @hide -->
+    <permission android:name="android.permission.RECEIVE_STK_COMMANDS"
+        android:protectionLevel="system|signature" />
+
+    <!-- ================================== -->
+    <!-- Permissions for sdcard interaction -->
+    <!-- ================================== -->
+    <eat-comment />
+
+    <!-- @SystemApi Allows an application to write to internal media storage
+         @hide  -->
+    <permission android:name="android.permission.WRITE_MEDIA_STORAGE"
+        android:protectionLevel="signature|privileged" />
+
+    <!-- Allows an application to manage access to documents, usually as part
+         of a document picker.
+         <p>Protection level: signature
+    -->
+    <permission android:name="android.permission.MANAGE_DOCUMENTS"
+        android:protectionLevel="signature" />
+
+    <!-- ================================== -->
+    <!-- Permissions for screenlock         -->
+    <!-- ================================== -->
+    <eat-comment />
+
+    <!-- Allows applications to disable the keyguard if it is not secure.
+         <p>Protection level: normal
+    -->
+    <permission android:name="android.permission.DISABLE_KEYGUARD"
+        android:description="@string/permdesc_disableKeyguard"
+        android:label="@string/permlab_disableKeyguard"
+        android:protectionLevel="normal" />
+
+    <!-- ================================== -->
+    <!-- Permissions to access other installed applications  -->
+    <!-- ================================== -->
+    <eat-comment />
+
+    <!-- @deprecated No longer enforced. -->
+    <permission android:name="android.permission.GET_TASKS"
+        android:label="@string/permlab_getTasks"
+        android:description="@string/permdesc_getTasks"
+        android:protectionLevel="normal" />
+
+    <!-- New version of GET_TASKS that apps can request, since GET_TASKS doesn't really
+         give access to task information.  We need this new one because there are
+         many existing apps that use add libraries and such that have validation
+         code to ensure the app has requested the GET_TASKS permission by seeing
+         if it has been granted the permission...  if it hasn't, it kills the app
+         with a message about being upset.  So we need to have it continue to look
+         like the app is getting that permission, even though it will never be
+         checked, and new privileged apps can now request this one for real access.
+         @hide
+         @SystemApi -->
+    <permission android:name="android.permission.REAL_GET_TASKS"
+        android:protectionLevel="signature|privileged" />
+
+    <!-- Allows an application to start a task from a ActivityManager#RecentTaskInfo.
+         @hide -->
+    <permission android:name="android.permission.START_TASKS_FROM_RECENTS"
+        android:protectionLevel="signature|privileged" />
+
+    <!-- @SystemApi @hide Allows an application to call APIs that allow it to do interactions
+         across the users on the device, using singleton services and
+         user-targeted broadcasts.  This permission is not available to
+         third party applications. -->
+    <permission android:name="android.permission.INTERACT_ACROSS_USERS"
+        android:protectionLevel="signature|privileged|development" />
+
+    <!-- @hide Fuller form of {@link android.Manifest.permission#INTERACT_ACROSS_USERS}
+         that removes restrictions on where broadcasts can be sent and allows other
+         types of interactions. -->
+    <permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL"
+        android:protectionLevel="signature|installer" />
+
+    <!-- @SystemApi @hide Allows an application to call APIs that allow it to query and manage
+         users on the device. This permission is not available to
+         third party applications. -->
+    <permission android:name="android.permission.MANAGE_USERS"
+        android:protectionLevel="signature|privileged" />
+
+    <!-- @hide Allows an application to set the profile owners and the device owner.
+         This permission is not available to third party applications.-->
+    <permission android:name="android.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS"
+        android:protectionLevel="signature"
+        android:label="@string/permlab_manageProfileAndDeviceOwners"
+        android:description="@string/permdesc_manageProfileAndDeviceOwners" />
+
+    <!-- Allows an application to get full detailed information about
+         recently running tasks, with full fidelity to the real state.
+         @hide -->
+    <permission android:name="android.permission.GET_DETAILED_TASKS"
+        android:protectionLevel="signature" />
+
+    <!-- Allows an application to change the Z-order of tasks.
+         <p>Protection level: normal
+    -->
+    <permission android:name="android.permission.REORDER_TASKS"
+        android:label="@string/permlab_reorderTasks"
+        android:description="@string/permdesc_reorderTasks"
+        android:protectionLevel="normal" />
+
+    <!-- @hide Allows an application to change to remove/kill tasks -->
+    <permission android:name="android.permission.REMOVE_TASKS"
+        android:protectionLevel="signature" />
+
+    <!-- @SystemApi @hide Allows an application to create/manage/remove stacks -->
+    <permission android:name="android.permission.MANAGE_ACTIVITY_STACKS"
+        android:protectionLevel="signature|privileged" />
+
+    <!-- Allows an application to start any activity, regardless of permission
+         protection or exported state.
+         @hide -->
+    <permission android:name="android.permission.START_ANY_ACTIVITY"
+        android:protectionLevel="signature" />
+
+    <!-- @deprecated The {@link android.app.ActivityManager#restartPackage}
+        API is no longer supported. -->
+    <permission android:name="android.permission.RESTART_PACKAGES"
+        android:label="@string/permlab_killBackgroundProcesses"
+        android:description="@string/permdesc_killBackgroundProcesses"
+        android:protectionLevel="normal" />
+
+    <!-- Allows an application to call
+        {@link android.app.ActivityManager#killBackgroundProcesses}.
+         <p>Protection level: normal
+    -->
+    <permission android:name="android.permission.KILL_BACKGROUND_PROCESSES"
+        android:label="@string/permlab_killBackgroundProcesses"
+        android:description="@string/permdesc_killBackgroundProcesses"
+        android:protectionLevel="normal" />
+
+    <!-- @SystemApi @hide Allows an application to retrieve a package's importance.
+         This permission is not available to third party applications. -->
+    <permission android:name="android.permission.GET_PACKAGE_IMPORTANCE"
+        android:protectionLevel="signature|privileged" />
+
+    <!-- ================================== -->
+    <!-- Permissions affecting the display of other applications  -->
+    <!-- ================================== -->
+    <eat-comment />
+
+    <!-- Allows an application to open windows using the type
+         {@link android.view.WindowManager.LayoutParams#TYPE_SYSTEM_ALERT},
+         shown on top of all other applications.  Very few applications
+         should use this permission; these windows are intended for
+         system-level interaction with the user. -->
+    <permission android:name="android.permission.SYSTEM_ALERT_WINDOW"
+        android:label="@string/permlab_systemAlertWindow"
+        android:description="@string/permdesc_systemAlertWindow"
+        android:protectionLevel="signature|preinstalled|appop|pre23|development" />
+
+    <!-- ================================== -->
+    <!-- Permissions affecting the system wallpaper -->
+    <!-- ================================== -->
+    <eat-comment />
+
+    <!-- Allows applications to set the wallpaper.
+         <p>Protection level: normal
+     -->
+    <permission android:name="android.permission.SET_WALLPAPER"
+        android:label="@string/permlab_setWallpaper"
+        android:description="@string/permdesc_setWallpaper"
+        android:protectionLevel="normal" />
+
+    <!-- Allows applications to set the wallpaper hints.
+         <p>Protection level: normal
+    -->
+    <permission android:name="android.permission.SET_WALLPAPER_HINTS"
+        android:label="@string/permlab_setWallpaperHints"
+        android:description="@string/permdesc_setWallpaperHints"
+        android:protectionLevel="normal" />
+
+    <!-- ============================================ -->
+    <!-- Permissions for changing the system clock -->
+    <!-- ============================================ -->
+    <eat-comment />
+
+    <!-- @SystemApi Allows applications to set the system time.
+    <p>Not for use by third-party applications. -->
+    <permission android:name="android.permission.SET_TIME"
+        android:protectionLevel="signature|privileged" />
+
+    <!-- Allows applications to set the system time zone.
+         <p>Protection level: normal
+    -->
+    <permission android:name="android.permission.SET_TIME_ZONE"
+        android:label="@string/permlab_setTimeZone"
+        android:description="@string/permdesc_setTimeZone"
+        android:protectionLevel="normal" />
+
+    <!-- ==================================================== -->
+    <!-- Permissions related to changing status bar   -->
+    <!-- ==================================================== -->
+    <eat-comment />
+
+    <!-- Allows an application to expand or collapse the status bar.
+         <p>Protection level: normal
+    -->
+    <permission android:name="android.permission.EXPAND_STATUS_BAR"
+        android:label="@string/permlab_expandStatusBar"
+        android:description="@string/permdesc_expandStatusBar"
+        android:protectionLevel="normal" />
+
+    <!-- ============================================================== -->
+    <!-- Permissions related to adding/removing shortcuts from Launcher -->
+    <!-- ============================================================== -->
+    <eat-comment />
+
+    <!-- Allows an application to install a shortcut in Launcher.
+         <p>Protection level: normal
+    -->
+    <permission android:name="com.android.launcher.permission.INSTALL_SHORTCUT"
+        android:label="@string/permlab_install_shortcut"
+        android:description="@string/permdesc_install_shortcut"
+        android:protectionLevel="normal"/>
+
+    <!-- Allows an application to uninstall a shortcut in Launcher.
+         <p>Protection level: normal
+    -->
+    <permission android:name="com.android.launcher.permission.UNINSTALL_SHORTCUT"
+        android:label="@string/permlab_uninstall_shortcut"
+        android:description="@string/permdesc_uninstall_shortcut"
+        android:protectionLevel="normal"/>
+
+    <!-- ==================================================== -->
+    <!-- Permissions related to accessing sync settings   -->
+    <!-- ==================================================== -->
+    <eat-comment />
+
+    <!-- Allows applications to read the sync settings.
+         <p>Protection level: normal
+    -->
+    <permission android:name="android.permission.READ_SYNC_SETTINGS"
+        android:description="@string/permdesc_readSyncSettings"
+        android:label="@string/permlab_readSyncSettings"
+        android:protectionLevel="normal" />
+
+    <!-- Allows applications to write the sync settings.
+         <p>Protection level: normal
+    -->
+    <permission android:name="android.permission.WRITE_SYNC_SETTINGS"
+        android:description="@string/permdesc_writeSyncSettings"
+        android:label="@string/permlab_writeSyncSettings"
+        android:protectionLevel="normal" />
+
+    <!-- Allows applications to read the sync stats.
+         <p>Protection level: normal
+    -->
+    <permission android:name="android.permission.READ_SYNC_STATS"
+        android:description="@string/permdesc_readSyncStats"
+        android:label="@string/permlab_readSyncStats"
+        android:protectionLevel="normal" />
+
+    <!-- ============================================ -->
+    <!-- Permissions for low-level system interaction -->
+    <!-- ============================================ -->
+    <eat-comment />
+
+    <!-- @SystemApi @hide Change the screen compatibility mode of applications -->
+    <permission android:name="android.permission.SET_SCREEN_COMPATIBILITY"
+        android:protectionLevel="signature" />
+
+    <!-- @SystemApi Allows an application to modify the current configuration, such
+         as locale. -->
+    <permission android:name="android.permission.CHANGE_CONFIGURATION"
+        android:protectionLevel="signature|privileged|development" />
+
+    <!-- Allows an application to read or write the system settings.
+         <p>Protection level: signature
+    -->
+    <permission android:name="android.permission.WRITE_SETTINGS"
+        android:label="@string/permlab_writeSettings"
+        android:description="@string/permdesc_writeSettings"
+        android:protectionLevel="signature|preinstalled|appop|pre23" />
+
+    <!-- @SystemApi Allows an application to modify the Google service map.
+    <p>Not for use by third-party applications. -->
+    <permission android:name="android.permission.WRITE_GSERVICES"
+        android:protectionLevel="signature|privileged" />
+
+    <!-- @SystemApi Allows an application to call
+        {@link android.app.ActivityManager#forceStopPackage}.
+        @hide -->
+    <permission android:name="android.permission.FORCE_STOP_PACKAGES"
+        android:protectionLevel="signature|privileged" />
+
+    <!-- @SystemApi @hide Allows an application to retrieve the content of the active window
+         An active window is the window that has fired an accessibility event. -->
+    <permission android:name="android.permission.RETRIEVE_WINDOW_CONTENT"
+        android:protectionLevel="signature|privileged" />
+
+    <!-- @SystemApi Modify the global animation scaling factor.
+    <p>Not for use by third-party applications. -->
+    <permission android:name="android.permission.SET_ANIMATION_SCALE"
+        android:protectionLevel="signature|privileged|development" />
+
+    <!-- @deprecated This functionality will be removed in the future; please do
+         not use. Allow an application to make its activities persistent. -->
+    <permission android:name="android.permission.PERSISTENT_ACTIVITY"
+        android:label="@string/permlab_persistentActivity"
+        android:description="@string/permdesc_persistentActivity"
+        android:protectionLevel="normal" />
+
+    <!-- Allows an application to find out the space used by any package.
+         <p>Protection level: normal
+    -->
+    <permission android:name="android.permission.GET_PACKAGE_SIZE"
+        android:label="@string/permlab_getPackageSize"
+        android:description="@string/permdesc_getPackageSize"
+        android:protectionLevel="normal" />
+
+    <!-- @deprecated No longer useful, see
+         {@link android.content.pm.PackageManager#addPackageToPreferred}
+         for details. -->
+    <permission android:name="android.permission.SET_PREFERRED_APPLICATIONS"
+        android:protectionLevel="signature" />
+
+    <!-- Allows an application to receive the
+         {@link android.content.Intent#ACTION_BOOT_COMPLETED} that is
+         broadcast after the system finishes booting.  If you don't
+         request this permission, you will not receive the broadcast at
+         that time.  Though holding this permission does not have any
+         security implications, it can have a negative impact on the
+         user experience by increasing the amount of time it takes the
+         system to start and allowing applications to have themselves
+         running without the user being aware of them.  As such, you must
+         explicitly declare your use of this facility to make that visible
+         to the user.
+         <p>Protection level: normal
+    -->
+    <permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"
+        android:label="@string/permlab_receiveBootCompleted"
+        android:description="@string/permdesc_receiveBootCompleted"
+        android:protectionLevel="normal" />
+
+    <!-- Allows an application to broadcast sticky intents.  These are
+         broadcasts whose data is held by the system after being finished,
+         so that clients can quickly retrieve that data without having
+         to wait for the next broadcast.
+         <p>Protection level: normal
+    -->
+    <permission android:name="android.permission.BROADCAST_STICKY"
+        android:label="@string/permlab_broadcastSticky"
+        android:description="@string/permdesc_broadcastSticky"
+        android:protectionLevel="normal" />
+
+    <!-- @SystemApi Allows mounting and unmounting file systems for removable storage.
+    <p>Not for use by third-party applications.-->
+    <permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"
+        android:protectionLevel="system|signature" />
+
+    <!-- @SystemApi Allows formatting file systems for removable storage.
+    <p>Not for use by third-party applications. -->
+    <permission android:name="android.permission.MOUNT_FORMAT_FILESYSTEMS"
+        android:protectionLevel="system|signature" />
+
+    <!-- Allows access to ASEC non-destructive API calls
+         @hide  -->
+    <permission android:name="android.permission.ASEC_ACCESS"
+        android:protectionLevel="signature" />
+
+    <!-- Allows creation of ASEC volumes
+         @hide  -->
+    <permission android:name="android.permission.ASEC_CREATE"
+        android:protectionLevel="signature" />
+
+    <!-- Allows destruction of ASEC volumes
+         @hide  -->
+    <permission android:name="android.permission.ASEC_DESTROY"
+        android:protectionLevel="signature" />
+
+    <!-- Allows mount / unmount of ASEC volumes
+         @hide  -->
+    <permission android:name="android.permission.ASEC_MOUNT_UNMOUNT"
+        android:protectionLevel="signature" />
+
+    <!-- Allows rename of ASEC volumes
+         @hide  -->
+    <permission android:name="android.permission.ASEC_RENAME"
+        android:protectionLevel="signature" />
+
+    <!-- @SystemApi Allows applications to write the apn settings.
+    <p>Not for use by third-party applications. -->
+    <permission android:name="android.permission.WRITE_APN_SETTINGS"
+        android:protectionLevel="signature|privileged" />
+
+    <!-- Allows applications to change network connectivity state.
+         <p>Protection level: normal
+    -->
+    <permission android:name="android.permission.CHANGE_NETWORK_STATE"
+        android:description="@string/permdesc_changeNetworkState"
+        android:label="@string/permlab_changeNetworkState"
+        android:protectionLevel="normal" />
+
+    <!-- Allows an application to clear the caches of all installed
+         applications on the device.
+         <p>Protection level: system|signature
+    -->
+    <permission android:name="android.permission.CLEAR_APP_CACHE"
+        android:protectionLevel="signature|privileged" />
+
+    <!-- @SystemApi Allows an application to use any media decoder when decoding for playback
+         @hide -->
+    <permission android:name="android.permission.ALLOW_ANY_CODEC_FOR_PLAYBACK"
+        android:protectionLevel="signature|privileged" />
+
+    <!-- @SystemApi Allows an application to install and/or uninstall CA certificates on
+         behalf of the user.
+         @hide -->
+    <permission android:name="android.permission.MANAGE_CA_CERTIFICATES"
+        android:protectionLevel="signature|privileged" />
+
+    <!-- @SystemApi Allows an application to do certain operations needed for
+         interacting with the recovery (system update) system.
+         @hide -->
+    <permission android:name="android.permission.RECOVERY"
+        android:protectionLevel="signature|privileged" />
+
+    <!-- Allows the system to bind to an application's task services
+         @hide -->
+    <permission android:name="android.permission.BIND_JOB_SERVICE"
+        android:protectionLevel="signature" />
+    <uses-permission android:name="android.permission.BIND_JOB_SERVICE"/>
+
+    <!-- Allows an application to initiate configuration updates
+         <p>An application requesting this permission is responsible for
+         verifying the source and integrity of any update before passing
+         it off to the various individual installer components
+         @hide -->
+    <permission android:name="android.permission.UPDATE_CONFIG"
+        android:protectionLevel="signature|privileged" />
+
+    <!-- ========================================= -->
+    <!-- Permissions for special development tools -->
+    <!-- ========================================= -->
+    <eat-comment />
+
+    <!-- @SystemApi Allows an application to read or write the secure system settings.
+    <p>Not for use by third-party applications. -->
+    <permission android:name="android.permission.WRITE_SECURE_SETTINGS"
+        android:protectionLevel="signature|privileged|development" />
+
+    <!-- @SystemApi Allows an application to retrieve state dump information from system services.
+    <p>Not for use by third-party applications. -->
+    <permission android:name="android.permission.DUMP"
+        android:protectionLevel="signature|privileged|development" />
+
+    <!-- @SystemApi Allows an application to read the low-level system log files.
+    <p>Not for use by third-party applications, because
+    Log entries can contain the user's private information. -->
+    <permission android:name="android.permission.READ_LOGS"
+        android:protectionLevel="signature|privileged|development" />
+
+    <!-- @SystemApi Configure an application for debugging.
+    <p>Not for use by third-party applications. -->
+    <permission android:name="android.permission.SET_DEBUG_APP"
+        android:protectionLevel="signature|privileged|development" />
+
+    <!-- @SystemApi Allows an application to set the maximum number of (not needed)
+         application processes that can be running.
+         <p>Not for use by third-party applications. -->
+    <permission android:name="android.permission.SET_PROCESS_LIMIT"
+        android:protectionLevel="signature|privileged|development" />
+
+    <!-- @SystemApi Allows an application to control whether activities are immediately
+         finished when put in the background.
+         <p>Not for use by third-party applications. -->
+    <permission android:name="android.permission.SET_ALWAYS_FINISH"
+        android:protectionLevel="signature|privileged|development" />
+
+    <!-- @SystemApi Allow an application to request that a signal be sent to all persistent processes.
+    <p>Not for use by third-party applications. -->
+    <permission android:name="android.permission.SIGNAL_PERSISTENT_PROCESSES"
+        android:protectionLevel="signature|privileged|development" />
+
+    <!-- ==================================== -->
+    <!-- Private permissions                  -->
+    <!-- ==================================== -->
+    <eat-comment />
+
+    <!-- @SystemApi Allows access to the list of accounts in the Accounts Service. -->
+    <permission android:name="android.permission.GET_ACCOUNTS_PRIVILEGED"
+        android:protectionLevel="signature|privileged" />
+
+    <!-- @SystemApi Allows applications to RW to diagnostic resources.
+    <p>Not for use by third-party applications. -->
+    <permission android:name="android.permission.DIAGNOSTIC"
+        android:protectionLevel="signature" />
+
+    <!-- @SystemApi Allows an application to open, close, or disable the status bar
+         and its icons.
+         <p>Not for use by third-party applications. -->
+    <permission android:name="android.permission.STATUS_BAR"
+        android:protectionLevel="signature|privileged" />
+
+    <!-- Allows an application to be the status bar.  Currently used only by SystemUI.apk
+    @hide -->
+    <permission android:name="android.permission.STATUS_BAR_SERVICE"
+        android:protectionLevel="signature" />
+
+    <!-- @SystemApi Allows an application to force a BACK operation on whatever is the
+         top activity.
+         <p>Not for use by third-party applications.
+         @hide
+    -->
+    <permission android:name="android.permission.FORCE_BACK"
+        android:protectionLevel="signature" />
+
+    <!-- @SystemApi Allows an application to update device statistics.
+    <p>Not for use by third-party applications. -->
+    <permission android:name="android.permission.UPDATE_DEVICE_STATS"
+        android:protectionLevel="signature|privileged" />
+
+    <!-- @SystemApi @hide Allows an application to collect battery statistics -->
+    <permission android:name="android.permission.GET_APP_OPS_STATS"
+        android:protectionLevel="signature|privileged|development" />
+
+    <!-- @SystemApi Allows an application to update application operation statistics. Not for
+         use by third party apps. @hide -->
+    <permission android:name="android.permission.UPDATE_APP_OPS_STATS"
+        android:protectionLevel="signature|privileged|installer" />
+
+    <!-- @SystemApi Allows an application to open windows that are for use by parts
+         of the system user interface.
+         <p>Not for use by third-party applications.
+         @hide
+    -->
+    <permission android:name="android.permission.INTERNAL_SYSTEM_WINDOW"
+        android:protectionLevel="signature" />
+
+    <!-- @SystemApi Allows an application to manage (create, destroy,
+         Z-order) application tokens in the window manager.
+         <p>Not for use by third-party applications.
+         @hide
+    -->
+    <permission android:name="android.permission.MANAGE_APP_TOKENS"
+        android:protectionLevel="signature" />
+
+    <!-- @hide Allows the application to temporarily freeze the screen for a
+         full-screen transition. -->
+    <permission android:name="android.permission.FREEZE_SCREEN"
+        android:protectionLevel="signature" />
+
+    <!-- @SystemApi Allows an application to inject user events (keys, touch, trackball)
+         into the event stream and deliver them to ANY window.  Without this
+         permission, you can only deliver events to windows in your own process.
+         <p>Not for use by third-party applications.
+         @hide
+    -->
+    <permission android:name="android.permission.INJECT_EVENTS"
+        android:protectionLevel="signature" />
+
+    <!-- @hide Allows an application to register an input filter which filters the stream
+         of user events (keys, touch, trackball) before they are dispatched to any window. -->
+    <permission android:name="android.permission.FILTER_EVENTS"
+        android:protectionLevel="signature" />
+
+    <!-- @hide Allows an application to retrieve the window token from the accessibility manager. -->
+    <permission android:name="android.permission.RETRIEVE_WINDOW_TOKEN"
+        android:protectionLevel="signature" />
+
+    <!-- @hide Allows an application to collect frame statistics -->
+    <permission android:name="android.permission.FRAME_STATS"
+         android:protectionLevel="signature" />
+
+    <!-- @hide Allows an application to temporary enable accessibility on the device. -->
+    <permission android:name="android.permission.TEMPORARY_ENABLE_ACCESSIBILITY"
+        android:protectionLevel="signature" />
+
+    <!-- @SystemApi Allows an application to watch and control how activities are
+         started globally in the system.  Only for is in debugging
+         (usually the monkey command).
+         <p>Not for use by third-party applications.
+         @hide
+    -->
+    <permission android:name="android.permission.SET_ACTIVITY_WATCHER"
+        android:protectionLevel="signature" />
+
+    <!-- @SystemApi Allows an application to call the activity manager shutdown() API
+         to put the higher-level system there into a shutdown state.
+         @hide -->
+    <permission android:name="android.permission.SHUTDOWN"
+        android:protectionLevel="signature|privileged" />
+
+    <!-- @SystemApi Allows an application to tell the activity manager to temporarily
+         stop application switches, putting it into a special mode that
+         prevents applications from immediately switching away from some
+         critical UI such as the home screen.
+         @hide -->
+    <permission android:name="android.permission.STOP_APP_SWITCHES"
+        android:protectionLevel="signature|privileged" />
+
+    <!-- @SystemApi Allows an application to retrieve private information about
+         the current top activity, such as any assist context it can provide.
+         <p>Not for use by third-party applications.
+         @hide
+    -->
+    <permission android:name="android.permission.GET_TOP_ACTIVITY_INFO"
+        android:protectionLevel="signature" />
+
+    <!-- Allows an application to retrieve the current state of keys and
+         switches.
+         <p>Not for use by third-party applications.
+         @deprecated The API that used this permission has been removed. -->
+    <permission android:name="android.permission.READ_INPUT_STATE"
+        android:protectionLevel="signature" />
+
+    <!-- Must be required by an {@link android.inputmethodservice.InputMethodService},
+         to ensure that only the system can bind to it.
+         <p>Protection level: signature
+    -->
+    <permission android:name="android.permission.BIND_INPUT_METHOD"
+        android:protectionLevel="signature" />
+
+    <!-- Must be required by an {@link android.media.midi.MidiDeviceService},
+         to ensure that only the system can bind to it.
+         <p>Protection level: signature
+    -->
+    <permission android:name="android.permission.BIND_MIDI_DEVICE_SERVICE"
+        android:protectionLevel="signature" />
+
+    <!-- Must be required by an {@link android.accessibilityservice.AccessibilityService},
+         to ensure that only the system can bind to it.
+         <p>Protection level: signature
+    -->
+    <permission android:name="android.permission.BIND_ACCESSIBILITY_SERVICE"
+        android:protectionLevel="signature" />
+
+    <!-- Must be required by a {@link android.printservice.PrintService},
+         to ensure that only the system can bind to it.
+         <p>Protection level: signature
+    -->
+    <permission android:name="android.permission.BIND_PRINT_SERVICE"
+        android:protectionLevel="signature" />
+
+    <!-- Must be required by a {@link android.nfc.cardemulation.HostApduService}
+         or {@link android.nfc.cardemulation.OffHostApduService} to ensure that only
+         the system can bind to it.
+         <p>Protection level: signature
+    -->
+    <permission android:name="android.permission.BIND_NFC_SERVICE"
+        android:protectionLevel="signature" />
+
+    <!-- Must be required by the PrintSpooler to ensure that only the system can bind to it.
+         @hide -->
+    <permission android:name="android.permission.BIND_PRINT_SPOOLER_SERVICE"
+        android:protectionLevel="signature" />
+
+    <!-- Must be required by a TextService (e.g. SpellCheckerService)
+         to ensure that only the system can bind to it.
+         <p>Protection level: signature
+    -->
+    <permission android:name="android.permission.BIND_TEXT_SERVICE"
+        android:protectionLevel="signature" />
+
+    <!-- Must be required by a {@link android.net.VpnService},
+         to ensure that only the system can bind to it.
+         <p>Protection level: signature
+    -->
+    <permission android:name="android.permission.BIND_VPN_SERVICE"
+        android:protectionLevel="signature" />
+
+    <!-- Must be required by a {@link android.service.wallpaper.WallpaperService},
+         to ensure that only the system can bind to it.
+         <p>Protection level: system|signature
+    -->
+    <permission android:name="android.permission.BIND_WALLPAPER"
+        android:protectionLevel="signature|privileged" />
+
+    <!-- Must be required by a {@link android.service.voice.VoiceInteractionService},
+         to ensure that only the system can bind to it.
+         <p>Protection level: signature
+    -->
+    <permission android:name="android.permission.BIND_VOICE_INTERACTION"
+        android:protectionLevel="signature" />
+
+    <!-- Must be required by hotword enrollment application,
+         to ensure that only the system can interact with it.
+         @hide <p>Not for use by third-party applications.</p> -->
+    <permission android:name="android.permission.MANAGE_VOICE_KEYPHRASES"
+        android:protectionLevel="signature|privileged" />
+
+    <!-- Must be required by a {@link com.android.media.remotedisplay.RemoteDisplayProvider},
+         to ensure that only the system can bind to it.
+         @hide -->
+    <permission android:name="android.permission.BIND_REMOTE_DISPLAY"
+        android:protectionLevel="signature" />
+
+    <!-- Must be required by a {@link android.media.tv.TvInputService}
+         to ensure that only the system can bind to it.
+         <p>Protection level: signature
+    -->
+    <permission android:name="android.permission.BIND_TV_INPUT"
+        android:protectionLevel="signature|privileged" />
+
+    <!-- @SystemApi Allows an application to modify parental controls
+         <p>Not for use by third-party applications.
+         @hide -->
+    <permission android:name="android.permission.MODIFY_PARENTAL_CONTROLS"
+        android:protectionLevel="signature|privileged" />
+
+    <!-- Must be required by a {@link android.media.routing.MediaRouteService}
+         to ensure that only the system can interact with it.
+         @hide -->
+    <permission android:name="android.permission.BIND_ROUTE_PROVIDER"
+        android:protectionLevel="signature" />
+
+    <!-- Must be required by device administration receiver, to ensure that only the
+         system can interact with it.
+         <p>Protection level: signature
+    -->
+    <permission android:name="android.permission.BIND_DEVICE_ADMIN"
+        android:protectionLevel="signature" />
+
+    <!-- @SystemApi Required to add or remove another application as a device admin.
+         <p>Not for use by third-party applications.
+         @hide -->
+    <permission android:name="android.permission.MANAGE_DEVICE_ADMINS"
+        android:protectionLevel="signature|privileged" />
+
+    <!-- @SystemApi Allows low-level access to setting the orientation (actually
+         rotation) of the screen.
+         <p>Not for use by third-party applications.
+         @hide
+    -->
+    <permission android:name="android.permission.SET_ORIENTATION"
+        android:protectionLevel="signature" />
+
+    <!-- @SystemApi Allows low-level access to setting the pointer speed.
+         <p>Not for use by third-party applications.
+         @hide
+    -->
+    <permission android:name="android.permission.SET_POINTER_SPEED"
+        android:protectionLevel="signature" />
+
+    <!-- Allows low-level access to setting input device calibration.
+         <p>Not for use by normal applications.
+         @hide -->
+    <permission android:name="android.permission.SET_INPUT_CALIBRATION"
+        android:protectionLevel="signature" />
+
+    <!-- Allows low-level access to setting the keyboard layout.
+         <p>Not for use by third-party applications.
+         @hide -->
+    <permission android:name="android.permission.SET_KEYBOARD_LAYOUT"
+        android:protectionLevel="signature" />
+
+    <!-- Allows an application to query tablet mode state and monitor changes
+         in it.
+         <p>Not for use by third-party applications.
+         @hide -->
+    <permission android:name="android.permission.TABLET_MODE"
+        android:protectionLevel="signature" />
+
+    <!-- Allows an application to request installing packages. Apps
+         targeting APIs greater than 22 must hold this permission in
+         order to use {@link android.content.Intent#ACTION_INSTALL_PACKAGE}.
+         <p>Protection level: normal
+    -->
+    <permission android:name="android.permission.REQUEST_INSTALL_PACKAGES"
+        android:label="@string/permlab_requestInstallPackages"
+        android:description="@string/permdesc_requestInstallPackages"
+        android:protectionLevel="normal" />
+
+    <!-- @SystemApi Allows an application to install packages.
+    <p>Not for use by third-party applications. -->
+    <permission android:name="android.permission.INSTALL_PACKAGES"
+        android:protectionLevel="signature|privileged" />
+
+    <!-- @SystemApi Allows an application to clear user data.
+         <p>Not for use by third-party applications
+         @hide
+    -->
+    <permission android:name="android.permission.CLEAR_APP_USER_DATA"
+        android:protectionLevel="signature|installer" />
+
+    <!-- @SystemApi Allows an application to delete cache files.
+    <p>Not for use by third-party applications. -->
+    <permission android:name="android.permission.DELETE_CACHE_FILES"
+        android:protectionLevel="signature|privileged" />
+
+    <!-- @SystemApi Allows an application to delete packages.
+    <p>Not for use by third-party applications. -->
+    <permission android:name="android.permission.DELETE_PACKAGES"
+        android:protectionLevel="signature|privileged" />
+
+    <!-- @SystemApi Allows an application to move location of installed package.
+         @hide -->
+    <permission android:name="android.permission.MOVE_PACKAGE"
+        android:protectionLevel="signature|privileged" />
+
+    <!-- @SystemApi Allows an application to change whether an application component (other than its own) is
+         enabled or not.
+         <p>Not for use by third-party applications. -->
+    <permission android:name="android.permission.CHANGE_COMPONENT_ENABLED_STATE"
+        android:protectionLevel="signature|privileged" />
+
+    <!-- Allows an application to grant specific permissions.
+         @hide -->
+    <permission android:name="android.permission.GRANT_RUNTIME_PERMISSIONS"
+        android:protectionLevel="signature|installer|verifier" />
+
+    <!-- Allows an app that has this permission and the permissions to install packages
+         to request certain runtime permissions to be granted at installation.
+         @hide
+         @SystemApi -->
+    <permission android:name="android.permission.INSTALL_GRANT_RUNTIME_PERMISSIONS"
+        android:protectionLevel="signature|installer|verifier" />
+
+    <!-- Allows an application to revoke specific permissions.
+        @hide
+        @SystemApi -->
+    <permission android:name="android.permission.REVOKE_RUNTIME_PERMISSIONS"
+         android:protectionLevel="signature|installer|verifier" />
+
+    <!-- @hide Allows an application to observe permission changes. -->
+    <permission android:name="android.permission.OBSERVE_GRANT_REVOKE_PERMISSIONS"
+        android:protectionLevel="signature|privileged" />
+
+    <!-- @SystemApi Allows an application to use SurfaceFlinger's low level features.
+         <p>Not for use by third-party applications.
+         @hide
+    -->
+    <permission android:name="android.permission.ACCESS_SURFACE_FLINGER"
+        android:protectionLevel="signature" />
+
+    <!-- @SystemApi Allows an application to take screen shots and more generally
+         get access to the frame buffer data.
+         <p>Not for use by third-party applications. -->
+    <permission android:name="android.permission.READ_FRAME_BUFFER"
+        android:protectionLevel="signature|privileged" />
+
+    <!-- Allows an application to use InputFlinger's low level features.
+         @hide -->
+    <permission android:name="android.permission.ACCESS_INPUT_FLINGER"
+        android:protectionLevel="signature" />
+
+    <!-- Allows an application to configure and connect to Wifi displays
+         @hide -->
+    <permission android:name="android.permission.CONFIGURE_WIFI_DISPLAY"
+        android:protectionLevel="signature" />
+
+    <!-- Allows an application to control low-level features of Wifi displays
+         such as opening an RTSP socket.  This permission should only be used
+         by the display manager.
+         @hide -->
+    <permission android:name="android.permission.CONTROL_WIFI_DISPLAY"
+        android:protectionLevel="signature" />
+
+    <!-- Allows an application to control the color transforms applied to
+         displays system-wide.
+         <p>Not for use by third-party applications.</p>
+         @hide -->
+    <permission android:name="android.permission.CONFIGURE_DISPLAY_COLOR_TRANSFORM"
+        android:protectionLevel="signature" />
+
+    <!-- @SystemApi Allows an application to control VPN.
+         <p>Not for use by third-party applications.</p>
+         @hide -->
+    <permission android:name="android.permission.CONTROL_VPN"
+        android:protectionLevel="signature|privileged" />
+    <uses-permission android:name="android.permission.CONTROL_VPN" />
+
+    <!-- @SystemApi Allows an application to capture audio output.
+         <p>Not for use by third-party applications.</p> -->
+    <permission android:name="android.permission.CAPTURE_AUDIO_OUTPUT"
+        android:protectionLevel="signature|privileged" />
+
+    <!-- @SystemApi Allows an application to capture audio for hotword detection.
+         <p>Not for use by third-party applications.</p>
+         @hide -->
+    <permission android:name="android.permission.CAPTURE_AUDIO_HOTWORD"
+        android:protectionLevel="signature|privileged" />
+
+    <!-- @SystemApi Allows an application to modify audio routing and override policy decisions.
+         <p>Not for use by third-party applications.</p>
+         @hide -->
+    <permission android:name="android.permission.MODIFY_AUDIO_ROUTING"
+        android:protectionLevel="signature|privileged" />
+
+    <!-- @SystemApi Allows an application to capture video output.
+         <p>Not for use by third-party applications.</p> -->
+    <permission android:name="android.permission.CAPTURE_VIDEO_OUTPUT"
+        android:protectionLevel="signature|privileged" />
+
+    <!-- @SystemApi Allows an application to capture secure video output.
+         <p>Not for use by third-party applications.</p> -->
+    <permission android:name="android.permission.CAPTURE_SECURE_VIDEO_OUTPUT"
+        android:protectionLevel="signature|privileged" />
+
+    <!-- @SystemApi Allows an application to know what content is playing and control its playback.
+         <p>Not for use by third-party applications due to privacy of media consumption</p>  -->
+    <permission android:name="android.permission.MEDIA_CONTENT_CONTROL"
+        android:protectionLevel="signature|privileged" />
+
+    <!-- @SystemApi Required to be able to disable the device (very dangerous!).
+         <p>Not for use by third-party applications.
+         @hide
+    -->
+    <permission android:name="android.permission.BRICK"
+        android:protectionLevel="signature" />
+
+    <!-- @SystemApi Required to be able to reboot the device.
+    <p>Not for use by third-party applications. -->
+    <permission android:name="android.permission.REBOOT"
+        android:protectionLevel="signature|privileged" />
+
+   <!-- @SystemApi Allows low-level access to power management.
+        <p>Not for use by third-party applications.
+        @hide
+    -->
+   <permission android:name="android.permission.DEVICE_POWER"
+        android:protectionLevel="signature" />
+
+   <!-- Allows access to the PowerManager.userActivity function.
+   <p>Not for use by third-party applications. @hide @SystemApi -->
+    <permission android:name="android.permission.USER_ACTIVITY"
+        android:protectionLevel="signature|privileged" />
+
+   <!-- @hide Allows low-level access to tun tap driver -->
+    <permission android:name="android.permission.NET_TUNNELING"
+        android:protectionLevel="signature" />
+
+    <!-- Run as a manufacturer test application, running as the root user.
+         Only available when the device is running in manufacturer test mode.
+         <p>Not for use by third-party applications.
+    -->
+    <permission android:name="android.permission.FACTORY_TEST"
+        android:protectionLevel="signature" />
+
+    <!-- Allows an application to broadcast a notification that an application
+         package has been removed.
+         <p>Not for use by third-party applications.
+    -->
+    <permission android:name="android.permission.BROADCAST_PACKAGE_REMOVED"
+        android:protectionLevel="signature" />
+
+    <!-- Allows an application to broadcast an SMS receipt notification.
+         <p>Not for use by third-party applications.
+    -->
+    <permission android:name="android.permission.BROADCAST_SMS"
+        android:protectionLevel="signature" />
+
+    <!-- Allows an application to broadcast a WAP PUSH receipt notification.
+         <p>Not for use by third-party applications.
+    -->
+    <permission android:name="android.permission.BROADCAST_WAP_PUSH"
+        android:protectionLevel="signature" />
+
+    <!-- @SystemApi Allows an application to broadcast privileged networking requests.
+         <p>Not for use by third-party applications. @hide -->
+    <permission android:name="android.permission.BROADCAST_NETWORK_PRIVILEGED"
+        android:protectionLevel="signature|privileged" />
+
+    <!-- @SystemApi Not for use by third-party applications. -->
+    <permission android:name="android.permission.MASTER_CLEAR"
+        android:protectionLevel="signature|privileged" />
+
+    <!-- @SystemApi Allows an application to call any phone number, including emergency
+         numbers, without going through the Dialer user interface for the user
+         to confirm the call being placed.
+         <p>Not for use by third-party applications. -->
+    <permission android:name="android.permission.CALL_PRIVILEGED"
+        android:protectionLevel="signature|privileged" />
+
+    <!-- @SystemApi Allows an application to perform CDMA OTA provisioning @hide -->
+    <permission android:name="android.permission.PERFORM_CDMA_PROVISIONING"
+        android:protectionLevel="signature|privileged" />
+
+    <!-- @SystemApi Allows an application to perform SIM Activation @hide -->
+    <permission android:name="android.permission.PERFORM_SIM_ACTIVATION"
+        android:protectionLevel="signature|privileged" />
+
+    <!-- @SystemApi Allows enabling/disabling location update notifications from
+         the radio.
+         <p>Not for use by third-party applications. -->
+    <permission android:name="android.permission.CONTROL_LOCATION_UPDATES"
+        android:protectionLevel="signature|privileged" />
+
+    <!-- @SystemApi Allows read/write access to the "properties" table in the checkin
+         database, to change values that get uploaded.
+         <p>Not for use by third-party applications. -->
+    <permission android:name="android.permission.ACCESS_CHECKIN_PROPERTIES"
+        android:protectionLevel="signature|privileged" />
+
+    <!-- @SystemApi Allows an application to collect component usage
+         statistics
+         <p>Declaring the permission implies intention to use the API and the user of the
+         device can grant permission through the Settings application. -->
+    <permission android:name="android.permission.PACKAGE_USAGE_STATS"
+        android:protectionLevel="signature|privileged|development|appop" />
+    <uses-permission android:name="android.permission.PACKAGE_USAGE_STATS" />
+
+    <!-- @hide Allows an application to change the app idle state of an app.
+         <p>Not for use by third-party applications. -->
+    <permission android:name="android.permission.CHANGE_APP_IDLE_STATE"
+        android:protectionLevel="signature" />
+
+    <!-- @hide @SystemApi Allows an application to temporarily whitelist an inactive app to
+         access the network and acquire wakelocks.
+         <p>Not for use by third-party applications. -->
+    <permission android:name="android.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST"
+        android:protectionLevel="system|signature" />
+
+    <!-- Permission an application must hold in order to use
+         {@link android.provider.Settings#ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS}.
+         This is a normal permission: an app requesting it will always be granted the
+         permission, without the user needing to approve or see it. -->
+    <permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS"
+        android:protectionLevel="normal" />
+
+    <!-- @SystemApi Allows an application to collect battery statistics -->
+    <permission android:name="android.permission.BATTERY_STATS"
+        android:protectionLevel="signature|privileged|development" />
+
+    <!-- @SystemApi Allows an application to control the backup and restore process.
+    <p>Not for use by third-party applications.
+         @hide pending API council -->
+    <permission android:name="android.permission.BACKUP"
+        android:protectionLevel="signature|privileged" />
+
+    <!-- Allows a package to launch the secure full-backup confirmation UI.
+         ONLY the system process may hold this permission.
+         @hide -->
+    <permission android:name="android.permission.CONFIRM_FULL_BACKUP"
+        android:protectionLevel="signature" />
+
+    <!-- @SystemApi Must be required by a {@link android.widget.RemoteViewsService},
+         to ensure that only the system can bind to it. -->
+    <permission android:name="android.permission.BIND_REMOTEVIEWS"
+        android:protectionLevel="signature|privileged" />
+
+    <!-- @SystemApi Allows an application to tell the AppWidget service which application
+         can access AppWidget's data.  The normal user flow is that a user
+         picks an AppWidget to go into a particular host, thereby giving that
+         host application access to the private data from the AppWidget app.
+         An application that has this permission should honor that contract.
+         <p>Not for use by third-party applications. -->
+    <permission android:name="android.permission.BIND_APPWIDGET"
+        android:protectionLevel="signature|privileged" />
+
+    <!-- @SystemApi Private permission, to restrict who can bring up a dialog to add a new
+         keyguard widget
+         @hide -->
+    <permission android:name="android.permission.BIND_KEYGUARD_APPWIDGET"
+        android:protectionLevel="signature|privileged" />
+
+    <!-- @SystemApi Internal permission allowing an application to query/set which
+         applications can bind AppWidgets.
+         @hide -->
+    <permission android:name="android.permission.MODIFY_APPWIDGET_BIND_PERMISSIONS"
+        android:protectionLevel="signature|privileged" />
+
+    <!-- Allows applications to change the background data setting.
+    <p>Not for use by third-party applications.
+         @hide pending API council -->
+    <permission android:name="android.permission.CHANGE_BACKGROUND_DATA_SETTING"
+        android:protectionLevel="signature" />
+
+    <!-- @SystemApi This permission can be used on content providers to allow the global
+         search system to access their data.  Typically it used when the
+         provider has some permissions protecting it (which global search
+         would not be expected to hold), and added as a read-only permission
+         to the path in the provider where global search queries are
+         performed.  This permission can not be held by regular applications;
+         it is used by applications to protect themselves from everyone else
+         besides global search. -->
+    <permission android:name="android.permission.GLOBAL_SEARCH"
+        android:protectionLevel="signature|privileged" />
+
+    <!-- Internal permission protecting access to the global search
+         system: ensures that only the system can access the provider
+         to perform queries (since this otherwise provides unrestricted
+         access to a variety of content providers), and to write the
+         search statistics (to keep applications from gaming the source
+         ranking).
+         @hide -->
+    <permission android:name="android.permission.GLOBAL_SEARCH_CONTROL"
+        android:protectionLevel="signature" />
+
+    <!-- @SystemApi Internal permission to allows an application to read indexable data.
+        @hide -->
+    <permission android:name="android.permission.READ_SEARCH_INDEXABLES"
+        android:protectionLevel="signature|privileged" />
+
+    <!-- @SystemApi Allows applications to set a live wallpaper.
+         @hide XXX Change to signature once the picker is moved to its
+         own apk as Ghod Intended. -->
+    <permission android:name="android.permission.SET_WALLPAPER_COMPONENT"
+        android:protectionLevel="signature|privileged" />
+
+    <!-- @SystemApi Allows applications to read dream settings and dream state.
+         @hide -->
+    <permission android:name="android.permission.READ_DREAM_STATE"
+        android:protectionLevel="signature|privileged" />
+
+    <!-- @SystemApi Allows applications to write dream settings, and start or stop dreaming.
+         @hide -->
+    <permission android:name="android.permission.WRITE_DREAM_STATE"
+        android:protectionLevel="signature|privileged" />
+
+    <!-- @SystemApi Allow an application to read and write the cache partition.
+         @hide -->
+    <permission android:name="android.permission.ACCESS_CACHE_FILESYSTEM"
+        android:protectionLevel="signature|privileged" />
+
+    <!-- Must be required by default container service so that only
+         the system can bind to it and use it to copy
+         protected data to secure containers or files
+         accessible to the system.
+         @hide -->
+    <permission android:name="android.permission.COPY_PROTECTED_DATA"
+        android:protectionLevel="signature" />
+
+    <!-- @SystemApi Internal permission protecting access to the encryption methods
+        @hide
+    -->
+    <permission android:name="android.permission.CRYPT_KEEPER"
+        android:protectionLevel="signature|privileged" />
+
+    <!-- @SystemApi Allows an application to read historical network usage for
+         specific networks and applications. @hide -->
+    <permission android:name="android.permission.READ_NETWORK_USAGE_HISTORY"
+        android:protectionLevel="signature|privileged" />
+
+    <!-- Allows an application to manage network policies (such as warning and disable
+         limits) and to define application-specific rules. @hide -->
+    <permission android:name="android.permission.MANAGE_NETWORK_POLICY"
+        android:protectionLevel="signature" />
+
+    <!-- @SystemApi Allows an application to account its network traffic against other UIDs. Used
+         by system services like download manager and media server. Not for use by
+         third party apps. @hide -->
+    <permission android:name="android.permission.MODIFY_NETWORK_ACCOUNTING"
+        android:protectionLevel="signature|privileged" />
+
+    <!-- C2DM permission.
+         @hide Used internally.
+     -->
+    <permission android:name="android.intent.category.MASTER_CLEAR.permission.C2D_MESSAGE"
+          android:protectionLevel="signature" />
+    <uses-permission android:name="android.intent.category.MASTER_CLEAR.permission.C2D_MESSAGE"/>
+
+    <!-- @SystemApi @hide Package verifier needs to have this permission before the PackageManager will
+         trust it to verify packages.
+    -->
+    <permission android:name="android.permission.PACKAGE_VERIFICATION_AGENT"
+        android:protectionLevel="signature|privileged" />
+
+    <!-- Must be required by package verifier receiver, to ensure that only the
+         system can interact with it.
+         @hide
+    -->
+    <permission android:name="android.permission.BIND_PACKAGE_VERIFIER"
+        android:protectionLevel="signature" />
+
+    <!-- @SystemApi @hide Intent filter verifier needs to have this permission before the
+         PackageManager will trust it to verify intent filters.
+    -->
+    <permission android:name="android.permission.INTENT_FILTER_VERIFICATION_AGENT"
+        android:protectionLevel="signature|privileged" />
+
+    <!-- Must be required by intent filter verifier receiver, to ensure that only the
+         system can interact with it.
+         @hide
+    -->
+    <permission android:name="android.permission.BIND_INTENT_FILTER_VERIFIER"
+        android:protectionLevel="signature" />
+
+    <!-- @SystemApi Allows applications to access serial ports via the SerialManager.
+         @hide -->
+    <permission android:name="android.permission.SERIAL_PORT"
+        android:protectionLevel="signature|privileged" />
+
+    <!-- Allows the holder to access content providers from outside an ApplicationThread.
+         This permission is enforced by the ActivityManagerService on the corresponding APIs,
+         in particular ActivityManagerService#getContentProviderExternal(String) and
+         ActivityManagerService#removeContentProviderExternal(String).
+         @hide
+    -->
+    <permission android:name="android.permission.ACCESS_CONTENT_PROVIDERS_EXTERNALLY"
+        android:protectionLevel="signature" />
+
+    <!-- @SystemApi Allows an application to hold an UpdateLock, recommending that a headless
+         OTA reboot *not* occur while the lock is held.
+         @hide -->
+    <permission android:name="android.permission.UPDATE_LOCK"
+        android:protectionLevel="signature|privileged" />
+
+    <!-- @SystemApi Allows an application to read the current set of notifications, including
+         any metadata and intents attached.
+         @hide -->
+    <permission android:name="android.permission.ACCESS_NOTIFICATIONS"
+        android:protectionLevel="signature|privileged" />
+
+    <!-- Marker permission for applications that wish to access notification policy.
+         <p>Protection level: normal
+    -->
+    <permission android:name="android.permission.ACCESS_NOTIFICATION_POLICY"
+        android:description="@string/permdesc_access_notification_policy"
+        android:label="@string/permlab_access_notification_policy"
+        android:protectionLevel="normal" />
+
+    <!-- Allows access to keyguard secure storage.  Only allowed for system processes.
+        @hide -->
+    <permission android:name="android.permission.ACCESS_KEYGUARD_SECURE_STORAGE"
+        android:protectionLevel="signature" />
+
+    <!-- Allows managing (adding, removing) fingerprint templates. Reserved for the system. @hide -->
+    <permission android:name="android.permission.MANAGE_FINGERPRINT"
+        android:protectionLevel="system|signature" />
+
+    <!-- Allows an app to reset fingerprint attempt counter. Reserved for the system. @hide -->
+    <permission android:name="android.permission.RESET_FINGERPRINT_LOCKOUT"
+        android:protectionLevel="signature" />
+
+    <!-- Allows an application to control keyguard.  Only allowed for system processes.
+        @hide -->
+    <permission android:name="android.permission.CONTROL_KEYGUARD"
+        android:protectionLevel="signature" />
+
+    <!-- Allows an application to listen to trust changes.  Only allowed for system processes.
+        @hide -->
+    <permission android:name="android.permission.TRUST_LISTENER"
+        android:protectionLevel="signature" />
+
+    <!-- @SystemApi Allows an application to provide a trust agent.
+         @hide For security reasons, this is a platform-only permission. -->
+    <permission android:name="android.permission.PROVIDE_TRUST_AGENT"
+        android:protectionLevel="signature|privileged" />
+
+    <!-- Allows an application to launch the trust agent settings activity.
+        @hide -->
+    <permission android:name="android.permission.LAUNCH_TRUST_AGENT_SETTINGS"
+        android:protectionLevel="signature|privileged" />
+
+    <!-- @SystemApi Must be required by an {@link
+        android.service.trust.TrustAgentService},
+        to ensure that only the system can bind to it.
+        @hide -->
+    <permission android:name="android.permission.BIND_TRUST_AGENT"
+        android:protectionLevel="signature" />
+
+    <!-- Must be required by an {@link
+         android.service.notification.NotificationListenerService},
+         to ensure that only the system can bind to it.
+         <p>Protection level: signature
+    -->
+    <permission android:name="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE"
+        android:protectionLevel="signature" />
+
+    <!-- Must be required by a {@link
+         android.service.chooser.ChooserTargetService}, to ensure that
+         only the system can bind to it.
+         <p>Protection level: signature
+    -->
+    <permission android:name="android.permission.BIND_CHOOSER_TARGET_SERVICE"
+        android:protectionLevel="signature" />
+
+    <!-- @SystemApi Must be required by a {@link
+         android.service.notification.ConditionProviderService},
+         to ensure that only the system can bind to it.
+         @hide -->
+    <permission android:name="android.permission.BIND_CONDITION_PROVIDER_SERVICE"
+        android:protectionLevel="signature" />
+
+    <!-- Must be required by an {@link android.service.dreams.DreamService},
+         to ensure that only the system can bind to it.
+         <p>Protection level: signature
+    -->
+    <permission android:name="android.permission.BIND_DREAM_SERVICE"
+        android:protectionLevel="signature" />
+
+    <!-- @SystemApi Allows an application to call into a carrier setup flow. It is up to the
+         carrier setup application to enforce that this permission is required
+         @hide This is not a third-party API (intended for OEMs and system apps). -->
+    <permission android:name="android.permission.INVOKE_CARRIER_SETUP"
+        android:protectionLevel="signature|privileged" />
+
+    <!-- @SystemApi Allows an application to listen for network condition observations.
+         @hide This is not a third-party API (intended for system apps). -->
+    <permission android:name="android.permission.ACCESS_NETWORK_CONDITIONS"
+        android:protectionLevel="signature|privileged" />
+
+    <!-- @SystemApi Allows an application to provision and access DRM certificates
+         @hide This is not a third-party API (intended for system apps). -->
+    <permission android:name="android.permission.ACCESS_DRM_CERTIFICATES"
+        android:protectionLevel="signature|privileged" />
+
+    <!-- Api Allows an application to manage media projection sessions.
+         @hide This is not a third-party API (intended for system apps). -->
+    <permission android:name="android.permission.MANAGE_MEDIA_PROJECTION"
+        android:protectionLevel="signature" />
+
+    <!-- @SystemApi Allows an application to read install sessions
+         @hide This is not a third-party API (intended for system apps). -->
+    <permission android:name="android.permission.READ_INSTALL_SESSIONS"
+        android:label="@string/permlab_readInstallSessions"
+        android:description="@string/permdesc_readInstallSessions"
+        android:protectionLevel="normal"/>
+
+    <!-- @SystemApi Allows an application to remove DRM certificates
+         @hide This is not a third-party API (intended for system apps). -->
+    <permission android:name="android.permission.REMOVE_DRM_CERTIFICATES"
+        android:protectionLevel="signature|privileged" />
+
+    <!-- @deprecated Use {@link android.Manifest.permission#BIND_CARRIER_SERVICES} instead -->
+    <permission android:name="android.permission.BIND_CARRIER_MESSAGING_SERVICE"
+        android:protectionLevel="signature|privileged" />
+
+    <!-- Allows an application to interact with the currently active
+         {@link android.service.voice.VoiceInteractionService}.
+         @hide -->
+    <permission android:name="android.permission.ACCESS_VOICE_INTERACTION_SERVICE"
+        android:protectionLevel="signature" />
+
+    <!-- The system process that is allowed to bind to services in carrier apps will
+         have this permission. Carrier apps should use this permission to protect
+         their services that only the system is allowed to bind to.
+         <p>Protection level: system|signature
+    -->
+    <permission android:name="android.permission.BIND_CARRIER_SERVICES"
+        android:label="@string/permlab_bindCarrierServices"
+        android:description="@string/permdesc_bindCarrierServices"
+        android:protectionLevel="signature|privileged" />
+
+    <!-- Allows an application to query whether DO_NOT_ASK_CREDENTIALS_ON_BOOT
+         flag is set.
+         @hide -->
+    <permission android:name="android.permission.QUERY_DO_NOT_ASK_CREDENTIALS_ON_BOOT"
+                android:protectionLevel="signature" />
+
+    <!-- Allows applications to kill UIDs.
+        <p>Not for use by third-party applications.
+         @hide -->
+    <permission android:name="android.permission.KILL_UID"
+                android:protectionLevel="signature|installer" />
+
+    <!-- @SystemApi Allows applications to read the local WiFi and Bluetooth MAC address.
+        @hide -->
+    <permission android:name="android.permission.LOCAL_MAC_ADDRESS"
+                android:protectionLevel="signature|privileged" />
+
+    <!-- @SystemApi Allows access to MAC addresses of WiFi and Bluetooth peer devices.
+        @hide -->
+    <permission android:name="android.permission.PEERS_MAC_ADDRESS"
+                android:protectionLevel="signature" />
+
+    <!-- Allows the Nfc stack to dispatch Nfc messages to applications. Applications
+        can use this permission to ensure incoming Nfc messages are from the Nfc stack
+        and not simulated by another application.
+        @hide -->
+    <permission android:name="android.permission.DISPATCH_NFC_MESSAGE"
+                android:protectionLevel="signature|privileged" />
+
+    <!-- The system process is explicitly the only one allowed to launch the
+         confirmation UI for full backup/restore -->
+    <uses-permission android:name="android.permission.CONFIRM_FULL_BACKUP"/>
+
+    <application android:process="system"
+                 android:persistent="true"
+                 android:hasCode="false"
+                 android:label="@string/android_system_label"
+                 android:allowClearUserData="false"
+                 android:backupAgent="com.android.server.backup.SystemBackupAgent"
+                 android:killAfterRestore="false"
+                 android:icon="@drawable/ic_launcher_android"
+                 android:supportsRtl="true">
+        <activity android:name="com.android.internal.app.ChooserActivity"
+                android:theme="@style/Theme.DeviceDefault.Resolver"
+                android:finishOnCloseSystemDialogs="true"
+                android:excludeFromRecents="true"
+                android:documentLaunchMode="never"
+                android:relinquishTaskIdentity="true"
+                android:process=":ui">
+            <intent-filter>
+                <action android:name="android.intent.action.CHOOSER" />
+                <category android:name="android.intent.category.DEFAULT" />
+                <category android:name="android.intent.category.VOICE" />
+            </intent-filter>
+        </activity>
+        <activity android:name="com.android.internal.app.IntentForwarderActivity"
+                android:finishOnCloseSystemDialogs="true"
+                android:theme="@style/Theme.NoDisplay"
+                android:excludeFromRecents="true"
+                android:label="@string/user_owner_label"
+                android:exported="true"
+                >
+        </activity>
+        <activity-alias android:name="com.android.internal.app.ForwardIntentToUserOwner"
+                android:targetActivity="com.android.internal.app.IntentForwarderActivity"
+                android:exported="true"
+                android:label="@string/user_owner_label">
+        </activity-alias>
+        <activity-alias android:name="com.android.internal.app.ForwardIntentToManagedProfile"
+                android:targetActivity="com.android.internal.app.IntentForwarderActivity"
+                android:icon="@drawable/ic_corp_icon"
+                android:exported="true"
+                android:label="@string/managed_profile_label">
+        </activity-alias>
+        <activity android:name="com.android.internal.app.HeavyWeightSwitcherActivity"
+                android:theme="@style/Theme.Material.Light.Dialog"
+                android:label="@string/heavy_weight_switcher_title"
+                android:finishOnCloseSystemDialogs="true"
+                android:excludeFromRecents="true"
+                android:process=":ui">
+        </activity>
+        <activity android:name="com.android.internal.app.PlatLogoActivity"
+                android:theme="@style/Theme.Wallpaper.NoTitleBar.Fullscreen"
+                android:configChanges="orientation|keyboardHidden"
+                android:process=":ui">
+        </activity>
+        <activity android:name="com.android.internal.app.DisableCarModeActivity"
+                android:theme="@style/Theme.NoDisplay"
+                android:excludeFromRecents="true"
+                android:process=":ui">
+        </activity>
+        <activity android:name="com.android.internal.app.DumpHeapActivity"
+                android:theme="@style/Theme.Translucent.NoTitleBar"
+                android:label="@string/dump_heap_title"
+                android:finishOnCloseSystemDialogs="true"
+                android:noHistory="true"
+                android:excludeFromRecents="true"
+                android:process=":ui">
+        </activity>
+        <provider android:name="com.android.server.am.DumpHeapProvider"
+                android:authorities="com.android.server.heapdump"
+                android:grantUriPermissions="true"
+                android:multiprocess="false"
+                android:singleUser="true" />
+
+        <activity android:name="android.accounts.ChooseAccountActivity"
+                android:excludeFromRecents="true"
+                android:exported="true"
+                android:theme="@style/Theme.Material.Light.Dialog"
+                android:label="@string/choose_account_label"
+                android:process=":ui">
+        </activity>
+
+        <activity android:name="android.accounts.ChooseTypeAndAccountActivity"
+                android:excludeFromRecents="true"
+                android:exported="true"
+                android:theme="@style/Theme.Material.Light.Dialog"
+                android:label="@string/choose_account_label"
+                android:process=":ui">
+        </activity>
+
+        <activity android:name="android.accounts.ChooseAccountTypeActivity"
+                android:excludeFromRecents="true"
+                android:theme="@style/Theme.Material.Light.Dialog"
+                android:label="@string/choose_account_label"
+                android:process=":ui">
+        </activity>
+
+        <activity android:name="android.accounts.CantAddAccountActivity"
+                android:excludeFromRecents="true"
+                android:exported="true"
+                android:theme="@style/Theme.Material.Light.Dialog.NoActionBar"
+                android:process=":ui">
+        </activity>
+
+        <activity android:name="android.accounts.GrantCredentialsPermissionActivity"
+                android:excludeFromRecents="true"
+                android:exported="true"
+                android:theme="@style/Theme.Material.Light.DialogWhenLarge"
+                android:process=":ui">
+        </activity>
+
+        <activity android:name="android.content.SyncActivityTooManyDeletes"
+               android:theme="@style/Theme.Material.Light.Dialog"
+               android:label="@string/sync_too_many_deletes"
+               android:process=":ui">
+        </activity>
+
+        <activity android:name="com.android.internal.app.ShutdownActivity"
+            android:permission="android.permission.SHUTDOWN"
+            android:theme="@style/Theme.NoDisplay"
+            android:excludeFromRecents="true">
+            <intent-filter>
+                <action android:name="android.intent.action.ACTION_REQUEST_SHUTDOWN" />
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
+            <intent-filter>
+                <action android:name="android.intent.action.REBOOT" />
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
+        </activity>
+
+        <activity android:name="com.android.internal.app.NetInitiatedActivity"
+                android:theme="@style/Theme.Material.Light.Dialog.Alert"
+                android:excludeFromRecents="true"
+                android:process=":ui">
+        </activity>
+
+        <receiver android:name="com.android.server.BootReceiver"
+                android:primaryUserOnly="true">
+            <intent-filter android:priority="1000">
+                <action android:name="android.intent.action.BOOT_COMPLETED" />
+            </intent-filter>
+        </receiver>
+
+        <receiver android:name="com.android.server.updates.CertPinInstallReceiver"
+                android:permission="android.permission.UPDATE_CONFIG">
+            <intent-filter>
+                <action android:name="android.intent.action.UPDATE_PINS" />
+                <data android:scheme="content" android:host="*" android:mimeType="*/*" />
+            </intent-filter>
+        </receiver>
+
+        <receiver android:name="com.android.server.updates.IntentFirewallInstallReceiver"
+                android:permission="android.permission.UPDATE_CONFIG">
+            <intent-filter>
+                <action android:name="android.intent.action.UPDATE_INTENT_FIREWALL" />
+                <data android:scheme="content" android:host="*" android:mimeType="*/*" />
+            </intent-filter>
+        </receiver>
+
+        <receiver android:name="com.android.server.updates.SmsShortCodesInstallReceiver"
+                android:permission="android.permission.UPDATE_CONFIG">
+            <intent-filter>
+                <action android:name="android.intent.action.UPDATE_SMS_SHORT_CODES" />
+                <data android:scheme="content" android:host="*" android:mimeType="*/*" />
+            </intent-filter>
+        </receiver>
+
+        <receiver android:name="com.android.server.updates.CarrierProvisioningUrlsInstallReceiver"
+                android:permission="android.permission.UPDATE_CONFIG">
+            <intent-filter>
+                <action android:name="android.intent.action.UPDATE_CARRIER_PROVISIONING_URLS" />
+                <data android:scheme="content" android:host="*" android:mimeType="*/*" />
+            </intent-filter>
+        </receiver>
+
+        <receiver android:name="com.android.server.updates.TzDataInstallReceiver"
+                android:permission="android.permission.UPDATE_CONFIG">
+            <intent-filter>
+                <action android:name="android.intent.action.UPDATE_TZDATA" />
+                <data android:scheme="content" android:host="*" android:mimeType="*/*" />
+            </intent-filter>
+        </receiver>
+
+        <receiver android:name="com.android.server.updates.SELinuxPolicyInstallReceiver"
+                android:permission="android.permission.UPDATE_CONFIG">
+            <intent-filter>
+                <action android:name="android.intent.action.UPDATE_SEPOLICY" />
+                <data android:scheme="content" android:host="*" android:mimeType="*/*" />
+            </intent-filter>
+        </receiver>
+
+        <receiver android:name="com.android.server.MasterClearReceiver"
+            android:permission="android.permission.MASTER_CLEAR">
+            <intent-filter
+                    android:priority="100" >
+                <!-- For Checkin, Settings, etc.: action=MASTER_CLEAR -->
+                <action android:name="android.intent.action.MASTER_CLEAR" />
+
+                <!-- MCS always uses REMOTE_INTENT: category=MASTER_CLEAR -->
+                <action android:name="com.google.android.c2dm.intent.RECEIVE" />
+                <category android:name="android.intent.category.MASTER_CLEAR" />
+            </intent-filter>
+        </receiver>
+
+        <service android:name="android.hardware.location.GeofenceHardwareService"
+            android:permission="android.permission.LOCATION_HARDWARE"
+            android:exported="false" />
+
+        <service android:name="com.android.internal.backup.LocalTransportService"
+                android:permission="android.permission.CONFIRM_FULL_BACKUP"
+                android:exported="false">
+            <intent-filter>
+                <action android:name="android.backup.TRANSPORT_HOST" />
+            </intent-filter>
+        </service>
+
+        <service android:name="com.android.server.MountServiceIdler"
+                 android:exported="true"
+                 android:permission="android.permission.BIND_JOB_SERVICE" >
+        </service>
+
+        <service android:name="com.android.server.backup.FullBackupJob"
+                 android:exported="true"
+                 android:permission="android.permission.BIND_JOB_SERVICE" >
+        </service>
+
+        <service android:name="com.android.server.backup.KeyValueBackupJob"
+                 android:permission="android.permission.BIND_JOB_SERVICE" >
+        </service>
+
+        <service
+            android:name="com.android.server.pm.BackgroundDexOptService"
+            android:exported="true"
+            android:permission="android.permission.BIND_JOB_SERVICE">
+        </service>
+
+    </application>
+
+</manifest>
diff --git a/tests/tests/permission2/src/android/permission2/cts/PermissionPolicyTest.java b/tests/tests/permission2/src/android/permission2/cts/PermissionPolicyTest.java
index a4d68cb..d1a102f 100644
--- a/tests/tests/permission2/src/android/permission2/cts/PermissionPolicyTest.java
+++ b/tests/tests/permission2/src/android/permission2/cts/PermissionPolicyTest.java
@@ -16,49 +16,185 @@
 
 package android.permission2.cts;
 
-import android.Manifest;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
+import android.content.pm.PermissionGroupInfo;
 import android.content.pm.PermissionInfo;
 import android.test.AndroidTestCase;
-import android.text.TextUtils;
+import android.util.ArrayMap;
 import android.util.ArraySet;
+import android.util.Log;
+import android.util.Xml;
+import org.xmlpull.v1.XmlPullParser;
 
+import java.io.InputStream;
+import java.lang.String;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
 import java.util.Set;
 
 /**
  * Tests for permission policy on the platform.
  */
 public class PermissionPolicyTest extends AndroidTestCase {
+    private static final String LOG_TAG = "PermissionProtectionTest";
+
     private static final String PLATFORM_PACKAGE_NAME = "android";
 
-    private static final Set<String> PERMISSION_GROUPS = new ArraySet<>();
-    static {
-        PERMISSION_GROUPS.add(Manifest.permission_group.CALENDAR);
-        PERMISSION_GROUPS.add(Manifest.permission_group.CAMERA);
-        PERMISSION_GROUPS.add(Manifest.permission_group.CONTACTS);
-        PERMISSION_GROUPS.add(Manifest.permission_group.LOCATION);
-        PERMISSION_GROUPS.add(Manifest.permission_group.MICROPHONE);
-        PERMISSION_GROUPS.add(Manifest.permission_group.PHONE);
-        PERMISSION_GROUPS.add(Manifest.permission_group.SENSORS);
-        PERMISSION_GROUPS.add(Manifest.permission_group.SMS);
-        PERMISSION_GROUPS.add(Manifest.permission_group.STORAGE);
+    private static final String PLATFORM_ROOT_NAMESPACE = "android.";
+
+    private static final String TAG_PERMISSION = "permission";
+
+    private static final String ATTR_NAME = "name";
+    private static final String ATTR_PERMISSION_GROUP = "permissionGroup";
+    private static final String ATTR_PROTECTION_LEVEL = "protectionLevel";
+
+    public void testPlatformPermissionPolicyUnaltered() throws Exception {
+        PackageInfo platformPackage = getContext().getPackageManager()
+                .getPackageInfo(PLATFORM_PACKAGE_NAME, PackageManager.GET_PERMISSIONS);
+        Map<String, PermissionInfo> declaredPermissionsMap = new ArrayMap<>();
+        for (PermissionInfo declaredPermission : platformPackage.permissions) {
+            declaredPermissionsMap.put(declaredPermission.name, declaredPermission);
+        }
+
+        List<PermissionGroupInfo> declaredGroups = getContext().getPackageManager()
+                .getAllPermissionGroups(0);
+        Set<String> declaredGroupsSet = new ArraySet<>();
+        for (PermissionGroupInfo declaredGroup : declaredGroups) {
+            declaredGroupsSet.add(declaredGroup.name);
+        }
+
+        Set<String> expectedPermissionGroups = new ArraySet<String>();
+
+        for (PermissionInfo expectedPermission : loadExpectedPermissions()) {
+            // OEMs cannot remove permissions
+            String expectedPermissionName = expectedPermission.name;
+            PermissionInfo declaredPermission = declaredPermissionsMap.get(expectedPermissionName);
+            assertNotNull("Permission " + expectedPermissionName
+                    + " must be declared", declaredPermission);
+
+            // We want to end up with OEM defined permissions and groups to check their namespace
+            declaredPermissionsMap.remove(expectedPermissionName);
+            // Collect expected groups to check if OEM defined groups aren't in platform namespace
+            expectedPermissionGroups.add(expectedPermission.group);
+
+            // OEMs cannot change permission protection
+            final int expectedProtection = expectedPermission.protectionLevel
+                    & PermissionInfo.PROTECTION_MASK_BASE;
+            final int declaredProtection = declaredPermission.protectionLevel
+                    & PermissionInfo.PROTECTION_MASK_BASE;
+            assertEquals("Permission " + expectedPermissionName + " invalid protection level",
+                    expectedProtection, declaredProtection);
+
+            // OEMs cannot change permission protection flags
+            final int expectedProtectionFlags = expectedPermission.protectionLevel
+                    & PermissionInfo.PROTECTION_MASK_FLAGS;
+            final int declaredProtectionFlags = declaredPermission.protectionLevel
+                    & PermissionInfo.PROTECTION_MASK_FLAGS;
+            assertEquals("Permission " + expectedPermissionName + " invalid enforced protection"
+                    + " level flags", expectedProtectionFlags, declaredProtectionFlags);
+
+            // OEMs cannot change permission grouping
+            if ((declaredPermission.protectionLevel & PermissionInfo.PROTECTION_DANGEROUS) != 0) {
+                assertEquals("Permission " + expectedPermissionName + " not in correct group",
+                        expectedPermission.group, declaredPermission.group);
+                assertTrue("Permission group " + expectedPermission.group + "must be defined",
+                        declaredGroupsSet.contains(declaredPermission.group));
+            }
+        }
+
+        // OEMs cannot define permissions in the platform namespace
+        for (String permission : declaredPermissionsMap.keySet()) {
+            assertFalse("Cannot define permission in android namespace",
+                    permission.startsWith(PLATFORM_ROOT_NAMESPACE));
+        }
+
+        // OEMs cannot define groups in the platform namespace
+        for (PermissionGroupInfo declaredGroup : declaredGroups) {
+            if (!expectedPermissionGroups.contains(declaredGroup.name)) {
+                assertFalse("Cannot define groups in android namespace",
+                        declaredGroup.name != null
+                        && declaredGroup.name.startsWith(PLATFORM_ROOT_NAMESPACE));
+            }
+        }
     }
 
-    public void testPlatformDefinedRuntimePermissionValid() throws Exception {
-        PackageManager packageManager = getContext().getPackageManager();
-        PackageInfo packageInfo = packageManager.getPackageInfo(PLATFORM_PACKAGE_NAME,
-                PackageManager.GET_PERMISSIONS);
-        for (PermissionInfo permission : packageInfo.permissions) {
-            if ((permission.protectionLevel & PermissionInfo.PROTECTION_DANGEROUS) == 0) {
-                continue;
+    private List<PermissionInfo> loadExpectedPermissions() throws Exception {
+        List<PermissionInfo> permissions = new ArrayList<>();
+        try (
+                InputStream in = getContext().getResources()
+                        .openRawResource(android.permission2.cts.R.raw.android_manifest)
+        ) {
+            XmlPullParser parser = Xml.newPullParser();
+            parser.setInput(in, null);
+
+            final int outerDepth = parser.getDepth();
+            int type;
+            while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+                    && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
+                if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+                    continue;
+                }
+                if (TAG_PERMISSION.equals(parser.getName())) {
+                    PermissionInfo permissionInfo = new PermissionInfo();
+                    permissionInfo.name = parser.getAttributeValue(null, ATTR_NAME);
+                    permissionInfo.group = parser.getAttributeValue(null, ATTR_PERMISSION_GROUP);
+                    permissionInfo.protectionLevel = parseProtectionLevel(
+                            parser.getAttributeValue(null, ATTR_PROTECTION_LEVEL));
+                    permissions.add(permissionInfo);
+                } else {
+                    Log.e(LOG_TAG, "Unknown tag " + parser.getName());
+                }
             }
-            assertTrue(permission.name + " must be in one of these groups: " + PERMISSION_GROUPS,
-                    PERMISSION_GROUPS.contains(permission.group));
-            assertFalse(permission.name + " must have non-empty label",
-                    TextUtils.isEmpty(permission.loadLabel(packageManager)));
-            assertFalse(permission.name + " must have non-empty description",
-                    TextUtils.isEmpty(permission.loadDescription(packageManager)));
         }
+        return permissions;
+    }
+
+    private static int parseProtectionLevel(String protectionLevelString) {
+        int protectionLevel = 0;
+        String[] fragments = protectionLevelString.split("\\|");
+        for (String fragment : fragments) {
+            switch (fragment.trim()) {
+                case "normal": {
+                    protectionLevel |= PermissionInfo.PROTECTION_NORMAL;
+                } break;
+                case "dangerous": {
+                    protectionLevel |= PermissionInfo.PROTECTION_DANGEROUS;
+                } break;
+                case "signature": {
+                    protectionLevel |= PermissionInfo.PROTECTION_SIGNATURE;
+                } break;
+                case "signatureOrSystem": {
+                    protectionLevel |= PermissionInfo.PROTECTION_SIGNATURE;
+                    protectionLevel |= PermissionInfo.PROTECTION_FLAG_SYSTEM;
+                } break;
+                case "system": {
+                    protectionLevel |= PermissionInfo.PROTECTION_FLAG_SYSTEM;
+                } break;
+                case "installer": {
+                    protectionLevel |= PermissionInfo.PROTECTION_FLAG_INSTALLER;
+                } break;
+                case "verifier": {
+                    protectionLevel |= PermissionInfo.PROTECTION_FLAG_VERIFIER;
+                } break;
+                case "preinstalled": {
+                    protectionLevel |= PermissionInfo.PROTECTION_FLAG_PREINSTALLED;
+                } break;
+                case "pre23": {
+                    protectionLevel |= PermissionInfo.PROTECTION_FLAG_PRE23;
+                } break;
+                case "appop": {
+                    protectionLevel |= PermissionInfo.PROTECTION_FLAG_APPOP;
+                } break;
+                case "development": {
+                    protectionLevel |= PermissionInfo.PROTECTION_FLAG_DEVELOPMENT;
+                } break;
+                case "privileged": {
+                    protectionLevel |= PermissionInfo.PROTECTION_FLAG_PRIVILEGED;
+                } break;
+            }
+        }
+        return protectionLevel;
     }
 }
diff --git a/tests/tests/provider/src/android/provider/cts/MediaStoreIntentsTest.java b/tests/tests/provider/src/android/provider/cts/MediaStoreIntentsTest.java
index a94e7f4..529b176 100644
--- a/tests/tests/provider/src/android/provider/cts/MediaStoreIntentsTest.java
+++ b/tests/tests/provider/src/android/provider/cts/MediaStoreIntentsTest.java
@@ -71,7 +71,10 @@
         final String[] schemes = new String[] {
                 "file", "http", "https", "content" };
         final String[] mimes = new String[] {
-                "image/bmp", "image/jpeg", "image/png", "image/gif", "image/webp" };
+                "image/bmp", "image/jpeg", "image/png", "image/gif", "image/webp",
+                "image/x-adobe-dng", "image/x-canon-cr2", "image/x-nikon-nef", "image/x-nikon-nrw",
+                "image/x-sony-arw", "image/x-panasonic-rw2", "image/x-olympus-orf",
+                "image/x-fuji-raf", "image/x-pentax-pef", "image/x-samsung-srw" };
 
         for (String scheme : schemes) {
             for (String mime : mimes) {
diff --git a/tests/tests/provider/src/android/provider/cts/VoicemailContractTest.java b/tests/tests/provider/src/android/provider/cts/VoicemailContractTest.java
index 34a546e..8e0a6cc 100644
--- a/tests/tests/provider/src/android/provider/cts/VoicemailContractTest.java
+++ b/tests/tests/provider/src/android/provider/cts/VoicemailContractTest.java
@@ -205,7 +205,8 @@
         final String[] STATUS_PROJECTION = new String[] {
                 Status._ID, Status.SOURCE_PACKAGE, Status.CONFIGURATION_STATE,
                 Status.DATA_CHANNEL_STATE, Status.NOTIFICATION_CHANNEL_STATE,
-                Status.SETTINGS_URI, Status.VOICEMAIL_ACCESS_URI};
+                Status.SETTINGS_URI, Status.VOICEMAIL_ACCESS_URI,
+                Status.QUOTA_OCCUPIED, Status.QUOTA_TOTAL};
         final int ID_INDEX = 0;
         final int SOURCE_PACKAGE_INDEX = 1;
         final int CONFIGURATION_STATE_INDEX = 2;
@@ -213,16 +214,22 @@
         final int NOTIFICATION_CHANNEL_STATE_INDEX = 4;
         final int SETTINGS_URI_INDEX = 5;
         final int VOICEMAIL_ACCESS_URI_INDEX = 6;
+        final int QUOTA_OCCUPIED_INDEX = 7;
+        final int QUOTA_TOTAL_INDEX = 8;
 
         int insertConfigurationState = Status.CONFIGURATION_STATE_OK;
         int insertDataChannelState = Status.DATA_CHANNEL_STATE_OK;
         int insertNotificationChannelState = Status.NOTIFICATION_CHANNEL_STATE_OK;
         String insertSettingsUri = "settings_uri";
         String insertVoicemailAccessUri = "tel:901";
+        int quotaOccupied = 7;
+        int quotaTotal = 42;
 
         int updateDataChannelState = Status.DATA_CHANNEL_STATE_NO_CONNECTION;
         int updateNotificationChannelState = Status.NOTIFICATION_CHANNEL_STATE_MESSAGE_WAITING;
         String updateSettingsUri = "settings_uri_2";
+        int updateQuotaOccupied = 1337;
+        int updateQuotaTotal = 2187;
 
         // Test: insert
         ContentValues value = new ContentValues();
@@ -231,6 +238,8 @@
         value.put(Status.NOTIFICATION_CHANNEL_STATE, insertNotificationChannelState);
         value.put(Status.SETTINGS_URI, insertSettingsUri);
         value.put(Status.VOICEMAIL_ACCESS_URI, insertVoicemailAccessUri);
+        value.put(Status.QUOTA_OCCUPIED, quotaOccupied);
+        value.put(Status.QUOTA_TOTAL, quotaTotal);
 
         Uri uri = mStatusProvider.insert(mStatusContentUri, value);
         Cursor cursor = mStatusProvider.query(
@@ -243,6 +252,8 @@
                 cursor.getInt(NOTIFICATION_CHANNEL_STATE_INDEX));
         assertEquals(insertSettingsUri, cursor.getString(SETTINGS_URI_INDEX));
         assertEquals(insertVoicemailAccessUri, cursor.getString(VOICEMAIL_ACCESS_URI_INDEX));
+        assertEquals(quotaOccupied, cursor.getInt(QUOTA_OCCUPIED_INDEX));
+        assertEquals(quotaTotal, cursor.getInt(QUOTA_TOTAL_INDEX));
         int id = cursor.getInt(ID_INDEX);
         assertEquals(id, Integer.parseInt(uri.getLastPathSegment()));
         cursor.close();
@@ -252,6 +263,8 @@
         value.put(Status.DATA_CHANNEL_STATE, updateDataChannelState);
         value.put(Status.NOTIFICATION_CHANNEL_STATE, updateNotificationChannelState);
         value.put(Status.SETTINGS_URI, updateSettingsUri);
+        value.put(Status.QUOTA_OCCUPIED, updateQuotaOccupied);
+        value.put(Status.QUOTA_TOTAL, updateQuotaTotal);
 
         mStatusProvider.update(uri, value, null, null);
         cursor = mStatusProvider.query(mStatusContentUri, STATUS_PROJECTION,
@@ -263,6 +276,8 @@
         assertEquals(updateNotificationChannelState,
                 cursor.getInt(NOTIFICATION_CHANNEL_STATE_INDEX));
         assertEquals(updateSettingsUri, cursor.getString(SETTINGS_URI_INDEX));
+        assertEquals(updateQuotaOccupied, cursor.getInt(QUOTA_OCCUPIED_INDEX));
+        assertEquals(updateQuotaTotal, cursor.getInt(QUOTA_TOTAL_INDEX));
         cursor.close();
 
         // Test: delete
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/AllocationByteBufferTest.java b/tests/tests/renderscript/src/android/renderscript/cts/AllocationByteBufferTest.java
new file mode 100644
index 0000000..760a8ed
--- /dev/null
+++ b/tests/tests/renderscript/src/android/renderscript/cts/AllocationByteBufferTest.java
@@ -0,0 +1,205 @@
+/*
+ * 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.renderscript.cts;
+
+import java.nio.ByteBuffer;
+import java.util.Random;
+
+import android.renderscript.Allocation;
+import android.renderscript.AllocationAdapter;
+import android.renderscript.Element;
+import android.renderscript.Element.DataType;
+import android.renderscript.RSIllegalArgumentException;
+import android.renderscript.Type;
+
+import android.util.Log;
+
+public class AllocationByteBufferTest extends RSBaseCompute {
+
+    protected int MAX_DIM = 128;
+    protected int RAND_SEED = 2016;
+
+    Allocation createTypedAllocation(DataType dt, int size, int dimX, int dimY) {
+        Element e = getElement(mRS, dt, size);
+        Type t;
+        if (dimY <= 1) {
+            t = Type.createX(mRS, e, dimX);
+        } else {
+            t = Type.createXY(mRS, e, dimX, dimY);
+        }
+
+        return Allocation.createTyped(mRS, t);
+    }
+
+    void testByteBufferHelper(DataType dt, int byteSize, int dimX, int dimY) {
+        Random r = new Random(RAND_SEED);
+
+        for (int size = 1; size <= 4; size++) {
+            int vecWidth = (size == 3) ? 4 : size;
+            byte[] data = new byte[dimX * dimY * vecWidth * byteSize];
+            RSUtils.genRandomBytes(RAND_SEED, data, true, 8);
+
+            Allocation alloc = createTypedAllocation(dt, size, dimX, dimY);
+            alloc.copyFromUnchecked(data);
+
+            ByteBuffer bb = alloc.getByteBuffer();
+            int stride = (int)alloc.getStride();
+            for (int i=0; i < 10; i++) {
+                int posX = r.nextInt(dimX);
+                int posY = r.nextInt(dimY);
+                byte byteInData = data[(posY * dimX + posX) * vecWidth * byteSize];
+                byte byteInBuffer = bb.get(posY * stride + posX * vecWidth * byteSize);
+                assertEquals(byteInData, byteInBuffer);
+            }
+        }
+    }
+
+    void testByteBufferHelper1D(DataType dt, int byteSize) {
+        Random r = new Random(RAND_SEED);
+        int dimX = r.nextInt(MAX_DIM) + 1;
+        testByteBufferHelper(dt, byteSize, dimX, 1);
+    }
+
+    void testByteBufferHelper2D(DataType dt, int byteSize) {
+        Random r = new Random(RAND_SEED);
+        int dimX = r.nextInt(MAX_DIM) + 1;
+        int dimY = r.nextInt(MAX_DIM) + 2; //Make sure dimY is larger than 1;
+        testByteBufferHelper(dt, byteSize, dimX, dimY);
+    }
+
+    public void test1DWrite() {
+        Random r = new Random(RAND_SEED);
+        int vecWidth = 4;
+        int dimX = r.nextInt(MAX_DIM) + 1;
+
+        Type t = Type.createX(mRS, Element.U8_4(mRS), dimX);
+        Allocation alloc = Allocation.createTyped(mRS, t);
+        ByteBuffer bb = alloc.getByteBuffer();
+
+        byte[] dataIn = new byte[dimX * vecWidth];
+        byte[] dataOut = new byte[dimX * vecWidth];
+        RSUtils.genRandomBytes(RAND_SEED, dataIn, true, 8);
+        bb.put(dataIn);
+        alloc.copyTo(dataOut);
+        for (int i = 0; i < dimX * vecWidth; i++) {
+            assertEquals(dataIn[i], dataOut[i]);
+        }
+    }
+
+    public void test2DWrite() {
+        Random r = new Random(RAND_SEED);
+        int vecWidth = 4;
+        int dimX = r.nextInt(MAX_DIM) + 1;
+        int dimY = r.nextInt(MAX_DIM) + 2; //Make sure dimY is larger than 1;
+
+        Type t = Type.createXY(mRS, Element.U8_4(mRS), dimX, dimY);
+        Allocation alloc = Allocation.createTyped(mRS, t);
+        ByteBuffer bb = alloc.getByteBuffer();
+
+        int stride = (int)alloc.getStride();
+        byte[] dataIn = new byte[stride * dimY];
+        byte[] dataOut = new byte[dimX * dimY * vecWidth];
+        RSUtils.genRandomBytes(RAND_SEED, dataIn, true, 8);
+        bb.put(dataIn);
+        alloc.copyTo(dataOut);
+        for (int i = 0; i < dimX*vecWidth; i++) {
+            for (int j = 0; j < dimY; j++) {
+                assertEquals(dataIn[j*stride + i], dataOut[j*dimX*vecWidth + i]);
+            }
+        }
+    }
+
+    public void testByteBufferU8_1D() {
+        testByteBufferHelper1D(DataType.UNSIGNED_8, 1);
+    }
+
+    public void testByteBufferU8_2D() {
+        testByteBufferHelper2D(DataType.UNSIGNED_8, 1);
+    }
+
+    public void testByteBufferU16_1D() {
+        testByteBufferHelper1D(DataType.UNSIGNED_16, 2);
+    }
+
+    public void testByteBufferU16_2D() {
+        testByteBufferHelper2D(DataType.UNSIGNED_16, 2);
+    }
+
+    public void testByteBufferU32_1D() {
+        testByteBufferHelper1D(DataType.UNSIGNED_32, 4);
+    }
+
+    public void testByteBufferU32_2D() {
+        testByteBufferHelper2D(DataType.UNSIGNED_32, 4);
+    }
+
+    public void testByteBufferU64_1D() {
+        testByteBufferHelper1D(DataType.UNSIGNED_64, 8);
+    }
+
+    public void testByteBufferU64_2D() {
+        testByteBufferHelper2D(DataType.UNSIGNED_64, 8);
+    }
+
+    public void testByteBufferS8_1D() {
+        testByteBufferHelper1D(DataType.SIGNED_8, 1);
+    }
+
+    public void testByteBufferS8_2D() {
+        testByteBufferHelper2D(DataType.SIGNED_8, 1);
+    }
+
+    public void testByteBufferS16_1D() {
+        testByteBufferHelper1D(DataType.SIGNED_16, 2);
+    }
+
+    public void testByteBufferS16_2D() {
+        testByteBufferHelper2D(DataType.SIGNED_16, 2);
+    }
+
+    public void testByteBufferS32_1D() {
+        testByteBufferHelper1D(DataType.SIGNED_32, 4);
+    }
+
+    public void testByteBufferS32_2D() {
+        testByteBufferHelper2D(DataType.SIGNED_32, 4);
+    }
+
+    public void testByteBufferS64_1D() {
+        testByteBufferHelper1D(DataType.SIGNED_64, 8);
+    }
+
+    public void testByteBufferS64_2D() {
+        testByteBufferHelper2D(DataType.UNSIGNED_64, 8);
+    }
+
+    public void testByteBufferF32_1D() {
+        testByteBufferHelper1D(DataType.FLOAT_32, 4);
+    }
+
+    public void testByteBufferF32_2D() {
+        testByteBufferHelper2D(DataType.FLOAT_32, 4);
+    }
+
+    public void testByteBufferF64_1D() {
+        testByteBufferHelper1D(DataType.FLOAT_64, 8);
+    }
+
+    public void testByteBufferF64_2D() {
+        testByteBufferHelper2D(DataType.FLOAT_64, 8);
+    }
+}
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/AllocationCreateAllocationsTest.java b/tests/tests/renderscript/src/android/renderscript/cts/AllocationCreateAllocationsTest.java
new file mode 100644
index 0000000..986704e
--- /dev/null
+++ b/tests/tests/renderscript/src/android/renderscript/cts/AllocationCreateAllocationsTest.java
@@ -0,0 +1,116 @@
+/*
+ * 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.renderscript.cts;
+
+import java.nio.ByteBuffer;
+import java.util.Random;
+
+import android.renderscript.Allocation;
+import android.renderscript.Element;
+import android.renderscript.Element.DataType;
+import android.renderscript.RSIllegalArgumentException;
+import android.renderscript.Type;
+
+import android.util.Log;
+import android.view.Surface;
+
+public class AllocationCreateAllocationsTest extends RSBaseCompute {
+    private int dimX = 1920;
+    private int dimY = 1080;
+    private final int MAX_NUM_IO_ALLOC = 16;
+
+    Allocation[] createAllocationsHelper(int usage, int numAlloc) {
+      Element e = Element.U8_4(mRS);
+      Type t = Type.createXY(mRS, e, dimX, dimY);
+      return Allocation.createAllocations(mRS, t, usage, numAlloc);
+    }
+
+    public void testCreateAllocations() {
+        int usage = Allocation.USAGE_SCRIPT;
+
+        int numAlloc = MAX_NUM_IO_ALLOC + 1;
+        Allocation[] allocArray;
+        allocArray = createAllocationsHelper(usage, numAlloc);
+        assertTrue("failed to create AllocationQueue", allocArray != null);
+    }
+
+    public void testCreateAllocations_USAGE_IO_INPUT() {
+        int usage = Allocation.USAGE_IO_INPUT;
+
+        int numAlloc = MAX_NUM_IO_ALLOC + 1;
+        Allocation[] allocArray;
+        try {
+            allocArray = createAllocationsHelper(usage, MAX_NUM_IO_ALLOC + 1);
+            fail("should throw RSIllegalArgumentException");
+        } catch (RSIllegalArgumentException e) {
+        }
+        numAlloc = 10;
+        allocArray = createAllocationsHelper(usage, numAlloc);
+        assertTrue("failed to create AllocationQueue", allocArray != null);
+    }
+
+    public void testGetProperties() {
+        int numAlloc = 10;
+        int usage = Allocation.USAGE_SCRIPT | Allocation.USAGE_IO_INPUT;
+        Allocation[] allocArray = createAllocationsHelper(usage, numAlloc);
+
+        Element eRef = allocArray[0].getElement();
+        Type tRef = allocArray[0].getType();
+        int uRef = allocArray[0].getUsage();
+        Surface sRef = allocArray[0].getSurface();
+        for (int i=1; i<numAlloc; i++) {
+            Element e = allocArray[i].getElement();
+            assertTrue("Element mismatch between AllocationQueue and Allocation",
+                       e.equals(eRef));
+            Type t = allocArray[i].getType();
+            assertTrue("Type mismatch between AllocationQueue and Allocation",
+                       t.equals(tRef));
+            int u = allocArray[i].getUsage();
+            assertTrue("Usage mismatch between AllocationQueue and Allocation",
+                       u == uRef);
+            Surface s = allocArray[i].getSurface();
+            assertTrue("Surface mismatch between AllocationQueue and Allocation",
+                       s.equals(sRef));
+        }
+    }
+
+    public void testMultipleIoReceive_USAGE_IO_INPUT() {
+        int usage = Allocation.USAGE_SCRIPT | Allocation.USAGE_IO_INPUT;
+        int dX = 64, dY = 64, numAlloc = 5;
+        Type t = Type.createXY(mRS, Element.U8_4(mRS), dX, dY);
+
+        Allocation[] allocArray = Allocation.createAllocations(mRS, t, usage, numAlloc);
+        Allocation inputAlloc = Allocation.createTyped(mRS, t,
+                                    Allocation.USAGE_SCRIPT | Allocation.USAGE_IO_OUTPUT);
+        inputAlloc.setSurface(allocArray[0].getSurface());
+
+        for (int i=0; i<numAlloc; i++) {
+            Random r = new Random();
+            byte[] dataIn = new byte[dX * dY * 4];
+            byte[] dataOut = new byte[dX * dY * 4];
+
+            r.nextBytes(dataIn);
+            inputAlloc.copyFromUnchecked(dataIn);
+            inputAlloc.ioSend();
+            allocArray[i].ioReceive();
+            allocArray[i].copyTo(dataOut);
+            for (int j=0; j<dX*dY*4; j++) {
+                assertTrue("IoReceive Failed, Frame: " + i, dataIn[j] == dataOut[j]);
+            }
+        }
+    }
+}
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/ReduceTest.java b/tests/tests/renderscript/src/android/renderscript/cts/ReduceTest.java
new file mode 100644
index 0000000..2977ce1
--- /dev/null
+++ b/tests/tests/renderscript/src/android/renderscript/cts/ReduceTest.java
@@ -0,0 +1,247 @@
+/*
+ * 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.renderscript.cts;
+
+import android.renderscript.*;
+import java.lang.Float;
+import java.util.Random;
+
+public class ReduceTest extends RSBaseCompute {
+    private ScriptC_reduce mScript;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mScript = new ScriptC_reduce(mRS);
+        mScript.set_negInf(Float.NEGATIVE_INFINITY);
+        mScript.set_posInf(Float.POSITIVE_INFINITY);
+    }
+
+    ///////////////////////////////////////////////////////////////////
+
+    private void assertEquals(Int2 javaRslt, Int2 rsRslt) {
+        assertEquals("x", javaRslt.x, rsRslt.x);
+        assertEquals("y", javaRslt.y, rsRslt.y);
+    }
+
+    private byte[] createInputArrayByte(int len, int seed) {
+        byte[] array = new byte[len];
+        (new Random(seed)).nextBytes(array);
+        return array;
+    }
+
+    private int[] createInputArrayInt(int len, int seed) {
+        Random rand = new Random(seed);
+        int[] array = new int[len];
+        for (int i = 0; i < len; ++i)
+            array[i] = rand.nextInt();
+        return array;
+    }
+
+    private int[] createInputArrayInt(int len, int seed, int eltRange) {
+        Random rand = new Random(seed);
+        int[] array = new int[len];
+        for (int i = 0; i < len; ++i)
+            array[i] = rand.nextInt(eltRange);
+        return array;
+    }
+
+    private float[] createInputArrayFloat(int len, int seed) {
+        Random rand = new Random(seed);
+        float[] array = new float[len];
+        for (int i = 0; i < len; ++i)
+            array[i] = rand.nextFloat();
+        return array;
+    }
+
+    ///////////////////////////////////////////////////////////////////
+
+    private int addint(int[] input) {
+        int rslt = 0;
+        for (int idx = 0; idx < input.length; ++idx)
+            rslt += input[idx];
+        return rslt;
+    }
+
+    public void testAddInt1D() {
+        final int[] input = createInputArrayInt(100000, 0, 1 << 13);
+
+        final int javaRslt = addint(input);
+        final int rsRslt = mScript.reduce_addint(input).get();
+
+        assertEquals(javaRslt, rsRslt);
+    }
+
+    public void testAddInt2D() {
+        final int dimX = 450, dimY = 225;
+
+        final int[] inputArray = createInputArrayInt(dimX * dimY, 1, 1 << 13);
+        Type.Builder typeBuilder = new Type.Builder(mRS, Element.I32(mRS));
+        typeBuilder.setX(dimX).setY(dimY);
+        Allocation inputAllocation = Allocation.createTyped(mRS, typeBuilder.create());
+        inputAllocation.copy2DRangeFrom(0, 0, dimX, dimY, inputArray);
+
+        final int javaRslt = addint(inputArray);
+        final int rsRslt = mScript.reduce_addint(inputAllocation).get();
+
+        assertEquals(javaRslt, rsRslt);
+    }
+
+    ///////////////////////////////////////////////////////////////////
+
+    private Int2 findMinAndMax(float[] input) {
+        float minVal = Float.POSITIVE_INFINITY;
+        int minIdx = -1;
+        float maxVal = Float.NEGATIVE_INFINITY;
+        int maxIdx = -1;
+
+        for (int idx = 0; idx < input.length; ++idx) {
+            if (input[idx] < minVal) {
+                minVal = input[idx];
+                minIdx = idx;
+            }
+            if (input[idx] > maxVal) {
+                maxVal = input[idx];
+                maxIdx = idx;
+            }
+        }
+
+        return new Int2(minIdx, maxIdx);
+    }
+
+    public void testFindMinAndMax() {
+        final float[] input = createInputArrayFloat(100000, 4);
+
+        final Int2 javaRslt = findMinAndMax(input);
+        final Int2 rsRslt = mScript.reduce_findMinAndMax(input).get();
+
+        assertEquals(javaRslt, rsRslt);
+    }
+
+    ///////////////////////////////////////////////////////////////////
+
+    public void testFz() {
+        final int inputLen = 100000;
+        int[] input = createInputArrayInt(inputLen, 5);
+        // just in case we got unlucky
+        input[(new Random(6)).nextInt(inputLen)] = 0;
+
+        final int rsRslt = mScript.reduce_fz(input).get();
+
+        assertEquals("input[" + rsRslt + "]", 0, input[rsRslt]);
+    }
+
+    ///////////////////////////////////////////////////////////////////
+
+    public void testFz2() {
+        final int dimX = 225, dimY = 450;
+        final int inputLen = dimX * dimY;
+
+        int[] inputArray = createInputArrayInt(inputLen, 7);
+        // just in case we got unlucky
+        inputArray[(new Random(8)).nextInt(inputLen)] = 0;
+
+        Type.Builder typeBuilder = new Type.Builder(mRS, Element.I32(mRS));
+        typeBuilder.setX(dimX).setY(dimY);
+        Allocation inputAllocation = Allocation.createTyped(mRS, typeBuilder.create());
+        inputAllocation.copy2DRangeFrom(0, 0, dimX, dimY, inputArray);
+
+        final Int2 rsRslt = mScript.reduce_fz2(inputAllocation).get();
+
+        final int cellVal = inputArray[rsRslt.x + dimX * rsRslt.y];
+
+        assertEquals("input[" + rsRslt.x + ", " + rsRslt.y + "]", 0, cellVal);
+    }
+
+    ///////////////////////////////////////////////////////////////////
+
+    public void testFz3() {
+        final int dimX = 59, dimY = 48, dimZ = 37;
+        final int inputLen = dimX * dimY * dimZ;
+
+        int[] inputArray = createInputArrayInt(inputLen, 9);
+        // just in case we got unlucky
+        inputArray[(new Random(10)).nextInt(inputLen)] = 0;
+
+        Type.Builder typeBuilder = new Type.Builder(mRS, Element.I32(mRS));
+        typeBuilder.setX(dimX).setY(dimY).setZ(dimZ);
+        Allocation inputAllocation = Allocation.createTyped(mRS, typeBuilder.create());
+        inputAllocation.copy3DRangeFrom(0, 0, 0, dimX, dimY, dimZ, inputArray);
+
+        final Int3 rsRslt = mScript.reduce_fz3(inputAllocation).get();
+
+        final int cellVal = inputArray[rsRslt.x + dimX * rsRslt.y + dimX * dimY * rsRslt.z];
+
+        assertEquals("input[" + rsRslt.x + ", " + rsRslt.y + ", " + rsRslt.z + "]", 0, cellVal);
+    }
+
+    ///////////////////////////////////////////////////////////////////
+
+    private static final int histogramBucketCount = 256;
+
+    private long[] histogram(final byte[] inputArray) {
+        Allocation inputAllocation = Allocation.createSized(mRS, Element.U8(mRS), inputArray.length);
+        inputAllocation.copyFrom(inputArray);
+
+        Allocation outputAllocation = Allocation.createSized(mRS, Element.U32(mRS), histogramBucketCount);
+
+        ScriptIntrinsicHistogram scriptHsg = ScriptIntrinsicHistogram.create(mRS, Element.U8(mRS));
+        scriptHsg.setOutput(outputAllocation);
+        scriptHsg.forEach(inputAllocation);
+
+        int[] outputArrayMistyped = new int[histogramBucketCount];
+        outputAllocation.copyTo(outputArrayMistyped);
+
+        long[] outputArray = new long[histogramBucketCount];
+        for (int i = 0; i < histogramBucketCount; ++i)
+            outputArray[i] = outputArrayMistyped[i] & (long)0xffffffff;
+        return outputArray;
+    }
+
+    public void testHistogram() {
+        final byte[] inputArray = createInputArrayByte(100000, 11);
+
+        final long[] javaRslt = histogram(inputArray);
+        assertEquals("javaRslt unexpected length", histogramBucketCount, javaRslt.length);
+        final long[] rsRslt = mScript.reduce_histogram(inputArray).get();
+        assertEquals("rsRslt unexpected length", histogramBucketCount, rsRslt.length);
+
+        for (int i = 0; i < histogramBucketCount; ++i) {
+            assertEquals("histogram[" + i + "]", javaRslt[i], rsRslt[i]);
+        }
+    }
+
+    //-----------------------------------------------------------------
+
+    private Int2 mode(final byte[] inputArray) {
+        long[] hsg = histogram(inputArray);
+
+        int modeIdx = 0;
+        for (int i = 1; i < hsg.length; ++i)
+            if (hsg[i] > hsg[modeIdx]) modeIdx =i;
+        return new Int2(modeIdx, (int)hsg[modeIdx]);
+    }
+
+    public void testMode() {
+        final byte[] inputArray = createInputArrayByte(100000, 12);
+
+        final Int2 javaRslt = mode(inputArray);
+        final Int2 rsRslt = mScript.reduce_mode(inputArray).get();
+
+        assertEquals(javaRslt, rsRslt);
+    }
+}
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/reduce.rs b/tests/tests/renderscript/src/android/renderscript/cts/reduce.rs
new file mode 100644
index 0000000..c9b169c
--- /dev/null
+++ b/tests/tests/renderscript/src/android/renderscript/cts/reduce.rs
@@ -0,0 +1,173 @@
+/*
+ * 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.
+ */
+
+#include "shared.rsh"
+
+float negInf, posInf;
+
+/////////////////////////////////////////////////////////////////////////
+
+#pragma rs reduce(addint) \
+  accumulator(aiAccum)
+
+static void aiAccum(int *accum, int val) { *accum += val; }
+
+/////////////////////////////////////////////////////////////////////////
+
+#pragma rs reduce(dp) \
+  accumulator(dpAccum) combiner(dpSum)
+
+static void dpAccum(float *accum, float in1, float in2) {
+  *accum += in1*in2;
+}
+
+// combiner function
+static void dpSum(float *accum, const float *val) { *accum += *val; }
+
+/////////////////////////////////////////////////////////////////////////
+
+#pragma rs reduce(findMinAndMax) \
+  initializer(fMMInit) accumulator(fMMAccumulator) \
+  combiner(fMMCombiner) outconverter(fMMOutConverter)
+
+typedef struct {
+  float val;
+  int idx;
+} IndexedVal;
+
+typedef struct {
+  IndexedVal min, max;
+} MinAndMax;
+
+static void fMMInit(MinAndMax *accum) {
+  accum->min.val = posInf;
+  accum->min.idx = -1;
+  accum->max.val = negInf;
+  accum->max.idx = -1;
+}
+
+static void fMMAccumulator(MinAndMax *accum, float in, int x) {
+  IndexedVal me;
+  me.val = in;
+  me.idx = x;
+
+  if (me.val < accum->min.val)
+    accum->min = me;
+  if (me.val > accum->max.val)
+    accum->max = me;
+}
+
+static void fMMCombiner(MinAndMax *accum,
+                        const MinAndMax *val) {
+  fMMAccumulator(accum, val->min.val, val->min.idx);
+  fMMAccumulator(accum, val->max.val, val->max.idx);
+}
+
+static void fMMOutConverter(int2 *result,
+                            const MinAndMax *val) {
+  result->x = val->min.idx;
+  result->y = val->max.idx;
+}
+
+/////////////////////////////////////////////////////////////////////////
+
+#pragma rs reduce(fz) \
+  initializer(fzInit) \
+  accumulator(fzAccum) combiner(fzCombine)
+
+static void fzInit(int *accumIdx) { *accumIdx = -1; }
+
+static void fzAccum(int *accumIdx,
+                    int inVal, int x /* special arg */) {
+  if (inVal==0) *accumIdx = x;
+}
+
+static void fzCombine(int *accumIdx, const int *accumIdx2) {
+  if (*accumIdx2 >= 0) *accumIdx = *accumIdx2;
+}
+
+/////////////////////////////////////////////////////////////////////////
+
+#pragma rs reduce(fz2) \
+  initializer(fz2Init) \
+  accumulator(fz2Accum) combiner(fz2Combine)
+
+static void fz2Init(int2 *accum) { accum->x = accum->y = -1; }
+
+static void fz2Accum(int2 *accum,
+                     int inVal,
+                     int x /* special arg */,
+                     int y /* special arg */) {
+  if (inVal==0) {
+    accum->x = x;
+    accum->y = y;
+  }
+}
+
+static void fz2Combine(int2 *accum, const int2 *accum2) {
+  if (accum2->x >= 0) *accum = *accum2;
+}
+
+/////////////////////////////////////////////////////////////////////////
+
+#pragma rs reduce(fz3) \
+  initializer(fz3Init) \
+  accumulator(fz3Accum) combiner(fz3Combine)
+
+static void fz3Init(int3 *accum) { accum->x = accum->y = accum->z = -1; }
+
+static void fz3Accum(int3 *accum,
+                     int inVal,
+                     int x /* special arg */,
+                     int y /* special arg */,
+                     int z /* special arg */) {
+  if (inVal==0) {
+    accum->x = x;
+    accum->y = y;
+    accum->z = z;
+  }
+}
+
+static void fz3Combine(int3 *accum, const int3 *accum2) {
+  if (accum->x >= 0) *accum = *accum2;
+}
+
+/////////////////////////////////////////////////////////////////////////
+
+#pragma rs reduce(histogram) \
+  accumulator(hsgAccum) combiner(hsgCombine)
+
+#define BUCKETS 256
+typedef uint32_t Histogram[BUCKETS];
+
+static void hsgAccum(Histogram *h, uchar in) { ++(*h)[in]; }
+
+static void hsgCombine(Histogram *accum, const Histogram *addend) {
+  for (int i = 0; i < BUCKETS; ++i)
+    (*accum)[i] += (*addend)[i];
+}
+
+#pragma rs reduce(mode) \
+  accumulator(hsgAccum) combiner(hsgCombine) \
+  outconverter(modeOutConvert)
+
+static void modeOutConvert(int2 *result, const Histogram *h) {
+  uint32_t mode = 0;
+  for (int i = 1; i < BUCKETS; ++i)
+    if ((*h)[i] > (*h)[mode]) mode = i;
+  result->x = mode;
+  result->y = (*h)[mode];
+}
diff --git a/tests/tests/security/Android.mk b/tests/tests/security/Android.mk
index a6e7a45..c9fa9f4 100644
--- a/tests/tests/security/Android.mk
+++ b/tests/tests/security/Android.mk
@@ -81,7 +81,7 @@
 
 LOCAL_PACKAGE_NAME := CtsSecurityTestCases
 
-LOCAL_SDK_VERSION := current
+LOCAL_SDK_VERSION := 23
 
 # Tag this module as a cts_v2 test artifact
 LOCAL_COMPATIBILITY_SUITE := cts_v2
diff --git a/tests/tests/security/assets/path_building/a.pem b/tests/tests/security/assets/path_building/a.pem
new file mode 100644
index 0000000..c3a38c8
--- /dev/null
+++ b/tests/tests/security/assets/path_building/a.pem
@@ -0,0 +1,17 @@
+-----BEGIN CERTIFICATE-----
+MIICvjCCAaagAwIBAgIDALo5MA0GCSqGSIb3DQEBCwUAMBExDzANBgNVBAMTBlJv
+b3QgQTAqGBMyMDE1MDEwMTAwMDAwMCswMDAwGBMyMDMwMDEwMTAwMDAwMCswMDAw
+MBExDzANBgNVBAMTBlJvb3QgQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC
+ggEBAPjsieSXdpmZv/YxiIbZpuicCFcteqSoWncsGAsZNqHjL7jgIsDZJK8W0c3g
+cj0Ij8WmuK6zntN39kszXWAoul9WM+KeJvRBd96dXBo3WrwmqMeLtNKLERmG7pW9
+VekJ0rOF3q6pPbYNujkwI+WBmlLsVmUYG5Jz9mD5JfVHdtnX+R99XNisfoGuMI7D
+ADVxPuzDs6+KWsoDU55xytmik5scF/CsTjftPNeVjCNBwhm7XRsDT+SMztZL2jsP
+WatTYPecZ+JkelGDlskv++k0QzkmrDLB4lBOrqdz7KMUyf3t1JcQDmmyFLCDiZOF
+Kx3UKR7qmrb++eNQ/D6cSPbdTq0CAwEAAaMTMBEwDwYDVR0TAQH/BAUwAwEB/zAN
+BgkqhkiG9w0BAQsFAAOCAQEAxLy+Qf3Mv/qy4R77zhVccVSQyh2pbHY4L3RryoJF
+k565iJRZ7f6I/1LrsL40RpYy057+bIKom34BRQQlFXTJj5ghWqmZ4eZVloWYcT0s
+NeQJW3TVUPzB1Fb968oAKt7I7/L+/wZTBgNiwQlbIQhAmQgQiJRWGSWqOmrvrfRJ
+hG6W3ueErMxElyOPtMj3xYGlCW0k9PyhBXoCt3QohDqzWibX4sI8DLBKyKSWKTA1
+6MIWLvwm0Hf3QAS7IjPCxAUUtt+arXgZ28lEbq49itcKfuKfdFTdLtQfPdIU5iEV
+WJ1sMKGehnz3giChXKvKeyt3xVCIaOAkbAjHPyeW28mpwg==
+-----END CERTIFICATE-----
diff --git a/tests/tests/security/assets/path_building/a_sha1.pem b/tests/tests/security/assets/path_building/a_sha1.pem
new file mode 100644
index 0000000..8f27783
--- /dev/null
+++ b/tests/tests/security/assets/path_building/a_sha1.pem
@@ -0,0 +1,17 @@
+-----BEGIN CERTIFICATE-----
+MIICvjCCAaagAwIBAgIDBs9kMA0GCSqGSIb3DQEBBQUAMBExDzANBgNVBAMTBlJv
+b3QgQTAqGBMyMDE1MDEwMTAwMDAwMCswMDAwGBMyMDMwMDEwMTAwMDAwMCswMDAw
+MBExDzANBgNVBAMTBlJvb3QgQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC
+ggEBAPjsieSXdpmZv/YxiIbZpuicCFcteqSoWncsGAsZNqHjL7jgIsDZJK8W0c3g
+cj0Ij8WmuK6zntN39kszXWAoul9WM+KeJvRBd96dXBo3WrwmqMeLtNKLERmG7pW9
+VekJ0rOF3q6pPbYNujkwI+WBmlLsVmUYG5Jz9mD5JfVHdtnX+R99XNisfoGuMI7D
+ADVxPuzDs6+KWsoDU55xytmik5scF/CsTjftPNeVjCNBwhm7XRsDT+SMztZL2jsP
+WatTYPecZ+JkelGDlskv++k0QzkmrDLB4lBOrqdz7KMUyf3t1JcQDmmyFLCDiZOF
+Kx3UKR7qmrb++eNQ/D6cSPbdTq0CAwEAAaMTMBEwDwYDVR0TAQH/BAUwAwEB/zAN
+BgkqhkiG9w0BAQUFAAOCAQEAPkTGyMCOYPohWrdrxPtUOao4fPUdhgH2n5MQOtXK
+HJhfH1ro0URt9eGBbvKU/jb5nK7tJpZrfQTJBa0LKkNzWQ4CuKqJU5CJY1tfHxVP
+2toM4v/pP99OsfnJVBVJYd2IPg3WoOoehpmohdJHznlmkVb62Azg9nU5X22Z+dYt
+fNtvBZLneug0AfIFOde0W4yHahD+pQyVbUgLTsBXayTRnmaHeFbtVjmy2CiHvxLc
+KDcoNiW6gK4R+T7o7r6cjyPXcTN2W4TA0SszR0tnIFaDEnNewO/L3tVQKx4ltfOG
+fEfvl5DqnJs1b6sc8VgLKXSMXzp9NXWxHWKrlCyIJEcHZg==
+-----END CERTIFICATE-----
diff --git a/tests/tests/security/assets/path_building/a_to_b.pem b/tests/tests/security/assets/path_building/a_to_b.pem
new file mode 100644
index 0000000..5cd16d3
--- /dev/null
+++ b/tests/tests/security/assets/path_building/a_to_b.pem
@@ -0,0 +1,17 @@
+-----BEGIN CERTIFICATE-----
+MIICvjCCAaagAwIBAgIDCx+QMA0GCSqGSIb3DQEBCwUAMBExDzANBgNVBAMTBlJv
+b3QgQjAqGBMyMDE1MDEwMTAwMDAwMCswMDAwGBMyMDMwMDEwMTAwMDAwMCswMDAw
+MBExDzANBgNVBAMTBlJvb3QgQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC
+ggEBAPjsieSXdpmZv/YxiIbZpuicCFcteqSoWncsGAsZNqHjL7jgIsDZJK8W0c3g
+cj0Ij8WmuK6zntN39kszXWAoul9WM+KeJvRBd96dXBo3WrwmqMeLtNKLERmG7pW9
+VekJ0rOF3q6pPbYNujkwI+WBmlLsVmUYG5Jz9mD5JfVHdtnX+R99XNisfoGuMI7D
+ADVxPuzDs6+KWsoDU55xytmik5scF/CsTjftPNeVjCNBwhm7XRsDT+SMztZL2jsP
+WatTYPecZ+JkelGDlskv++k0QzkmrDLB4lBOrqdz7KMUyf3t1JcQDmmyFLCDiZOF
+Kx3UKR7qmrb++eNQ/D6cSPbdTq0CAwEAAaMTMBEwDwYDVR0TAQH/BAUwAwEB/zAN
+BgkqhkiG9w0BAQsFAAOCAQEAA73yZZ5TJ5ieRq6y7MjHQL0yJZTPqr7LIIRop0kh
+aJcfYffDdffERCQuVRgay+r6Su06aZ8MopBSubRWERyL/ZgSeEdS49HnISWehSCx
+Md0uvCPQUrxxS6GDaorLpBQ/1WK9rZc7vs3pgzLNN3zpdJ2o3GaJ9BYtuDUU+Kh5
+cv8gHD5LThxFbPLpnhDRyhiJ4CuJAZht681ItNhcL7Cfb8pNNXNLq2RhBOkZ6Upo
+xd9Fr9zv0FjeuZ9WzQLxyhHU8gXqnWmJIO+W7nKPJP9x3YDg0EMc3BtdvyS4QWIj
+PTnjOoT7Z3CGBd/Qn4z+PoAfGpg5aAo0J6bs6kelF6+6Yw==
+-----END CERTIFICATE-----
diff --git a/tests/tests/security/assets/path_building/b.pem b/tests/tests/security/assets/path_building/b.pem
new file mode 100644
index 0000000..b6adfc8
--- /dev/null
+++ b/tests/tests/security/assets/path_building/b.pem
@@ -0,0 +1,17 @@
+-----BEGIN CERTIFICATE-----
+MIICvjCCAaagAwIBAgIDDwzUMA0GCSqGSIb3DQEBCwUAMBExDzANBgNVBAMTBlJv
+b3QgQjAqGBMyMDE1MDEwMTAwMDAwMCswMDAwGBMyMDMwMDEwMTAwMDAwMCswMDAw
+MBExDzANBgNVBAMTBlJvb3QgQjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC
+ggEBAKpKiUbK1DhAWyLuRKik1fPNvtN5DyRr3Qa5K83OmNyBpsR57qfpenksp5Cs
+By5zOYwLMJxGfA2z6phY1Crvd6ogU5GcCWIUtMzNJEJAg1O6sNjhb9AN82yZUuvn
++oPL1Qpxsj91AZ0qi8qBAT7JuM67KOoTrls43BbASDc3/eUfNeoDANdz7AO21obj
+oL9P6B0Edy4aN0OeTVUxsix3vOeW/CL3RSklHymrR24iL5AfVq2gQFGui10esXV2
+R4K8kzmwfp4rODsEKABLB7/gljkvy1bmOrg7oW32FSf0Rw4PCKz0xdD34KaGYYhW
+7N98TnkQ6XAYGsaiSR4r3VFrHRECAwEAAaMTMBEwDwYDVR0TAQH/BAUwAwEB/zAN
+BgkqhkiG9w0BAQsFAAOCAQEAJSCbpsDjfyKpGp2a7d4WonzfAf+/sC0Z6M8UhVCD
+WK/0cZmrYgjm80uflxmwTMCIqvn0XcV3rt/lpV0UTxEqhHQ/4lrdzYVz97mbXdei
+eqtKTC4rG24/4LDBL1PuGfIABwBW7QroF9I9OrRGyuKVs17MDuM2n8jhKCs0Rjtj
+kwA2ZbmfvDPzLipTQd7FzL73eos9Lug2CtoW5IzFWlmuhc1V1E3M36nelDCJfUij
+0gUuE1XovVIHl5P4M9ktS1S8GlWrsdHcW3uWFxTiXBW9e8/UKn6vk49kORCzj3S1
+/9CrVNLTCFaxGH2TmGBCOW9Z90+PJS/KHg4JIJ/mEbrc3w==
+-----END CERTIFICATE-----
diff --git a/tests/tests/security/assets/path_building/b_to_a.pem b/tests/tests/security/assets/path_building/b_to_a.pem
new file mode 100644
index 0000000..0aff5b9
--- /dev/null
+++ b/tests/tests/security/assets/path_building/b_to_a.pem
@@ -0,0 +1,17 @@
+-----BEGIN CERTIFICATE-----
+MIICvjCCAaagAwIBAgIDD3FMMA0GCSqGSIb3DQEBCwUAMBExDzANBgNVBAMTBlJv
+b3QgQTAqGBMyMDE1MDEwMTAwMDAwMCswMDAwGBMyMDMwMDEwMTAwMDAwMCswMDAw
+MBExDzANBgNVBAMTBlJvb3QgQjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC
+ggEBAKpKiUbK1DhAWyLuRKik1fPNvtN5DyRr3Qa5K83OmNyBpsR57qfpenksp5Cs
+By5zOYwLMJxGfA2z6phY1Crvd6ogU5GcCWIUtMzNJEJAg1O6sNjhb9AN82yZUuvn
++oPL1Qpxsj91AZ0qi8qBAT7JuM67KOoTrls43BbASDc3/eUfNeoDANdz7AO21obj
+oL9P6B0Edy4aN0OeTVUxsix3vOeW/CL3RSklHymrR24iL5AfVq2gQFGui10esXV2
+R4K8kzmwfp4rODsEKABLB7/gljkvy1bmOrg7oW32FSf0Rw4PCKz0xdD34KaGYYhW
+7N98TnkQ6XAYGsaiSR4r3VFrHRECAwEAAaMTMBEwDwYDVR0TAQH/BAUwAwEB/zAN
+BgkqhkiG9w0BAQsFAAOCAQEAfYxfbh+dksrQK2KguFnd33gTibH1mYJ0zVS6Bcio
+D8S9Vxn7Zn4dSW+Wj+ec1LdMrU8zbqvXxPGJze5m9mxYTGVSq/wnG1ebOD/IO0g4
+Vr6H2iRaditUjeoBdvHgDATo4LJhsUloljDQIR8v4gpIuV55RtC2ppd0A9C1cDGf
+Ia5+bkcmPrNLTdYitwHs2TUqCq7zTApmtWz814R6ryk/M3NIu4c4hbS1m3aTWRln
+wPy9oaRcpHiNO7QnKrJ6nqfL1UE3STar2s7dTU4Teo0lEVRGFXRnmjckltZv2iSz
+GEwUxlbhaq1nHlOetZS+W6FD2Ve/1RPDPzeP4cMQB98J2w==
+-----END CERTIFICATE-----
diff --git a/tests/tests/security/assets/path_building/intermediate_a.pem b/tests/tests/security/assets/path_building/intermediate_a.pem
new file mode 100644
index 0000000..bf6eba3
--- /dev/null
+++ b/tests/tests/security/assets/path_building/intermediate_a.pem
@@ -0,0 +1,17 @@
+-----BEGIN CERTIFICATE-----
+MIICxDCCAaygAwIBAgIDBKqTMA0GCSqGSIb3DQEBCwUAMBExDzANBgNVBAMTBlJv
+b3QgQTAqGBMyMDE1MDEwMTAwMDAwMCswMDAwGBMyMDMwMDEwMTAwMDAwMCswMDAw
+MBcxFTATBgNVBAMTDGludGVybWVkaWF0ZTCCASIwDQYJKoZIhvcNAQEBBQADggEP
+ADCCAQoCggEBAOOqsYw6pzmD1/TQMncmc25lvOtzocU8CNOQaAw02yZrkGuA1Ivb
+3ga5wMeQFfwm2/tqdEqNm9zEzIHvoxPx6ZDT5FyjQK6cSbaB002sMJODip0ldgWW
+ds+Zq3XqSJsIo/em7H7MKAxXbbPulfBKaWHCWUbwj7pGOdANY3sAKksljB+5KFI0
+gAdRn72glUMOnq5nY2ZD9ZCR+eRZ2xIabGUvg50rk35GAaZSZsbAVl2QqOZ1cKZr
+MlkYhFA4cuODY2mbs81MS/VTZdQIEa5a8QJbxPltdQE683lfDqR25Y7q31kNn4yV
+WB3EVbMSB/lq/csM5X+XBKrI2i9rbORUcqECAwEAAaMTMBEwDwYDVR0TAQH/BAUw
+AwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAQyeRijHRzBH5UetO3Kudzro5g3J+5Txu
+Jw446RnPkpbab6A++6FB57D1Y4qYWBeIrNH1xH1bIsWvYIhi86vjOS2Eyu8MM3u9
+lzPQNrHMazbQzOALtOE9do3SajgxaJeM2/WrzygGVErrrzKrt9X3/7bbqOmqyZLH
+Px82azPWCXtgzZy/DErQBcKjxpFyvpwqQTDKke1+opW0P5KXB7fKhtbOygfeeHCG
++dpokE4TtUhznc2yvsNxWzuKXmMsr5/sQtnACJOlm/uT5hkyFBv/5zoug81C9RhV
+Z7BV2mZ+Wlf6ueTbVp70+iJm/0Rpv0z4puGB8TQb2HpYrNtYfSWoMw==
+-----END CERTIFICATE-----
diff --git a/tests/tests/security/assets/path_building/intermediate_b.pem b/tests/tests/security/assets/path_building/intermediate_b.pem
new file mode 100644
index 0000000..41066fc
--- /dev/null
+++ b/tests/tests/security/assets/path_building/intermediate_b.pem
@@ -0,0 +1,17 @@
+-----BEGIN CERTIFICATE-----
+MIICxDCCAaygAwIBAgIDDac7MA0GCSqGSIb3DQEBCwUAMBExDzANBgNVBAMTBlJv
+b3QgQjAqGBMyMDE1MDEwMTAwMDAwMCswMDAwGBMyMDMwMDEwMTAwMDAwMCswMDAw
+MBcxFTATBgNVBAMTDGludGVybWVkaWF0ZTCCASIwDQYJKoZIhvcNAQEBBQADggEP
+ADCCAQoCggEBAOOqsYw6pzmD1/TQMncmc25lvOtzocU8CNOQaAw02yZrkGuA1Ivb
+3ga5wMeQFfwm2/tqdEqNm9zEzIHvoxPx6ZDT5FyjQK6cSbaB002sMJODip0ldgWW
+ds+Zq3XqSJsIo/em7H7MKAxXbbPulfBKaWHCWUbwj7pGOdANY3sAKksljB+5KFI0
+gAdRn72glUMOnq5nY2ZD9ZCR+eRZ2xIabGUvg50rk35GAaZSZsbAVl2QqOZ1cKZr
+MlkYhFA4cuODY2mbs81MS/VTZdQIEa5a8QJbxPltdQE683lfDqR25Y7q31kNn4yV
+WB3EVbMSB/lq/csM5X+XBKrI2i9rbORUcqECAwEAAaMTMBEwDwYDVR0TAQH/BAUw
+AwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAhZjhJYoV+h1vekKHcbQsMmYECNqmaeHa
+lgJ54LLfzuk2cyTIp+PX/I6hGU+QR1OVR3gUwwI4wyFxANv/RH0EDEjDTUcd/FYk
+vAfs3W9OHnCMRF1D+bxw8InRvgzFIfXMorwVvxwC5XCrgSYAM3OkhgvNxmf+nXgg
+dtGKZmva5rRtCEZj4djV9JUwLTWW5x3SN2rhGur6+vRFA7jScAm3YkCD+ii4x9Mf
+5RisFtsYzmhIhCWMGXmLrTZmghPkgQVlES/YGst8XptmHVqwY9qAOKayIYOgf/d6
+BvLsVGdSw9iRrPASWlFXvCyNWuO6zOM97IcYF9HIUWcUVNYSiXXceg==
+-----END CERTIFICATE-----
diff --git a/tests/tests/security/assets/path_building/leaf1.pem b/tests/tests/security/assets/path_building/leaf1.pem
new file mode 100644
index 0000000..28fdc6f
--- /dev/null
+++ b/tests/tests/security/assets/path_building/leaf1.pem
@@ -0,0 +1,17 @@
+-----BEGIN CERTIFICATE-----
+MIICvDCCAaSgAwIBAgIDCS0BMA0GCSqGSIb3DQEBCwUAMBExDzANBgNVBAMTBlJv
+b3QgQTAqGBMyMDE1MDEwMTAwMDAwMCswMDAwGBMyMDMwMDEwMTAwMDAwMCswMDAw
+MA8xDTALBgNVBAMTBExlYWYwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB
+AQDZYuX5LRdHetGr0nVbzb0tDC68PEjJKqKepZcRAsob0r+eLig0PVcNz/F0EF/X
++Nm72rBQUu9zkaZ8iqHbwMpEKX7JdAhnTCa5x0eU6umQ66v6MKWRapYjkBgfSpBp
++lBuXxl6SbIj1PST8XkkMC1Ow+D7O71dJyCQrNjlaGANOODqvsI2eX3oxLpbhdyi
+yTMR4z+w+QYqpR5gRFU8Ynx+Wy6IQXZtTcc/3uvZ4PewYVe8mjDNgqSOj+7sxHEY
+duV/yc6+oLcSIu8scVVXxUp53QhbtfiaUi7dbpe/ba4a/tCNkDNFD6lRAk7lh9io
+i0NCleXlyzqQKbdKqOrcCCJDAgMBAAGjEzARMA8GA1UdEwEB/wQFMAMBAf8wDQYJ
+KoZIhvcNAQELBQADggEBAA5ETybm67kpTWwwdVfJRb8vMAePbeOx6Kx05UsHhiBp
+BzyB7IK+xSDC72PDY/6EG/0rwiQYRCfUujWXY6D+l9uVNU8Eak33hPplhdMDf6/k
+F7BOM/Jvey4MsHbbZqiYrYaE/ucR04lEb2XmMlwrSIDjdZ5UCanWiH0FwHlE+NsR
+fONJIfCy5W+wVg78nFtCcRYd1/OY0aiUQbUuXzfgEB7J0bWZEUms9XrLVrl9Mo0S
+PRyR8vi8Kmn+EM0Ic0spooX5YSsl/FUqBPSGhMBR1LGM7QxKM+Olkf1j9xoNLN+x
+vzCJl3UuzWzwbUqg1IF1/lzFEyIrqj+pD8rpENYa5WI=
+-----END CERTIFICATE-----
diff --git a/tests/tests/security/assets/path_building/leaf2.pem b/tests/tests/security/assets/path_building/leaf2.pem
new file mode 100644
index 0000000..4e5b9f0
--- /dev/null
+++ b/tests/tests/security/assets/path_building/leaf2.pem
@@ -0,0 +1,17 @@
+-----BEGIN CERTIFICATE-----
+MIICxDCCAaygAwIBAgIDAyZ6MA0GCSqGSIb3DQEBCwUAMBcxFTATBgNVBAMTDGlu
+dGVybWVkaWF0ZTAqGBMyMDE1MDEwMTAwMDAwMCswMDAwGBMyMDMwMDEwMTAwMDAw
+MCswMDAwMBExDzANBgNVBAMTBkxlYWYgMjCCASIwDQYJKoZIhvcNAQEBBQADggEP
+ADCCAQoCggEBAK5cJ9ZkIxJx49TsqdEBW4GNXkO2lRWIG8Y71fXGYBvlrqhfTGhX
+JxdTa+yGHw5IPgsq+D37BHWfvgRys7egRoiOhXHFORJKbPGrr5I5/vWkp1px5k1e
+ILzndhbRrRBf283oqd78bos8szNpWJ8EnbwY8BoVtBlW25Woi/tAQQGuQgYLldIC
+LWmxYi9GqyTwGGZddpHILb54zc4/P9DeU0XQRlL8Zx0XSKj126IlEoWG1zoC3Zu3
++eI2wgMxyL/AKZjjOrfdrqG1cX2kPjoM2RvZG9LllI41vR87CYizttYphQxii88v
+6UmobYt01njjHj2RNLNc6HcUxaFjoxMkxN0CAwEAAaMTMBEwDwYDVR0TAQH/BAUw
+AwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAVtsuF4WB0/z3VM0aIc9BXnZivK+xDtHA
+c5fUVPwVeBpPxiJUHzY1pFfcVvmefgI2hVvUq2MVbMyDe3IpSZ7bp03DPrtLl+i8
+4RaLUlit9OAqsjg55ZXB1miTtGjElJj6h8DdgF9Qyl/WULZqVgBCDmkwAjWViL1i
+5gmj5VWVZydGLDMuX23jFHj2kPmVJfaLiHg6mqP5ktYEy3YbtJiLg+D+gEyvSqrt
+NfcTp6BMt25Hm+YpJ7V5e35SmIl+izKGh+BpZUDM2+iJy2hEhapBpRk0jiuGqMZh
+MNAqKAMArGZAY/wYoEGAyLveNmZV/xtIROXk4LmpeO1RkGq4wsm7iw==
+-----END CERTIFICATE-----
diff --git a/tests/tests/security/jni/Android.mk b/tests/tests/security/jni/Android.mk
index 021abb2..8ea6a96 100644
--- a/tests/tests/security/jni/Android.mk
+++ b/tests/tests/security/jni/Android.mk
@@ -35,15 +35,15 @@
 		android_security_cts_AudioFlingerBinderTest.cpp \
 		android_security_cts_AudioEffectBinderTest.cpp \
 		android_security_cts_MediaPlayerInfoLeakTest.cpp \
-		android_security_cts_StagefrightFoundationTest.cpp \
 		android_security_cts_GraphicBufferInfoLeakTest.cpp
 
 LOCAL_C_INCLUDES := $(JNI_H_INCLUDE) \
 										$(TOP)/frameworks/native/include/media/openmax
 
-LOCAL_SHARED_LIBRARIES := libnativehelper liblog libbinder libutils libmedia libselinux libdl libcutils libcrypto libstagefright_foundation
+LOCAL_SHARED_LIBRARIES := libnativehelper_compat_libc++ liblog libdl libmedia libcrypto
 
 LOCAL_C_INCLUDES += ndk/sources/cpufeatures
-LOCAL_STATIC_LIBRARIES := cpufeatures
+LOCAL_STATIC_LIBRARIES := cpufeatures libbinder libselinux libutils libcutils
+LOCAL_CXX_STL := libc++_static
 
 include $(BUILD_SHARED_LIBRARY)
diff --git a/tests/tests/security/jni/CtsSecurityJniOnLoad.cpp b/tests/tests/security/jni/CtsSecurityJniOnLoad.cpp
index 072f59e..24c87b1 100644
--- a/tests/tests/security/jni/CtsSecurityJniOnLoad.cpp
+++ b/tests/tests/security/jni/CtsSecurityJniOnLoad.cpp
@@ -29,7 +29,6 @@
 extern int register_android_security_cts_EncryptionTest(JNIEnv* env);
 extern int register_android_security_cts_AudioEffectBinderTest(JNIEnv* env);
 extern int register_android_security_cts_MediaPlayerInfoLeakTest(JNIEnv* env);
-extern int register_android_security_cts_StagefrightFoundationTest(JNIEnv* env);
 extern int register_android_security_cts_GraphicBufferInfoLeakTest(JNIEnv* env);
 
 jint JNI_OnLoad(JavaVM *vm, void *reserved) {
@@ -91,10 +90,6 @@
         return JNI_ERR;
     }
 
-    if (register_android_security_cts_StagefrightFoundationTest(env)) {
-        return JNI_ERR;
-    }
-
     if (register_android_security_cts_GraphicBufferInfoLeakTest(env)) {
         return JNI_ERR;
     }
diff --git a/tests/tests/security/jni/android_security_cts_MediaCryptoTest.cpp b/tests/tests/security/jni/android_security_cts_MediaCryptoTest.cpp
deleted file mode 100644
index abb26eb..0000000
--- a/tests/tests/security/jni/android_security_cts_MediaCryptoTest.cpp
+++ /dev/null
@@ -1,103 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-/* Original code copied from NDK Native-media sample code */
-
-//#define LOG_NDEBUG 0
-#define TAG "NativeMediaCrypto"
-#include <log/log.h>
-
-#include <android_media_MediaCrypto.h>
-#include <assert.h>
-#include <binder/MemoryDealer.h>
-#include <jni.h>
-#include <media/ICrypto.h>
-#include <media/stagefright/foundation/AString.h>
-#include <pthread.h>
-#include <stdio.h>
-#include <string.h>
-#include <unistd.h>
-#include <utils/StrongPointer.h>
-#include <semaphore.h>
-
-
-static const size_t kBufferSize = 1024;
-
-using namespace android;
-
-static jboolean testCrypto(sp<ICrypto> icrypto,
-        const CryptoPlugin::SubSample *subSample, CryptoPlugin::Mode mode)
-{
-    // Allocate source buffer
-    sp<MemoryDealer> memDealer = new MemoryDealer(kBufferSize, "MediaCryptoTest");
-    sp<IMemory> srcBuffer = memDealer->allocate(kBufferSize);
-    if (!srcBuffer->pointer()) {
-        ALOGE("Failed to allocate source buffer");
-        return false;
-    }
-    memset(srcBuffer->pointer(), 's', kBufferSize);
-
-    // Invalid dest pointer should fault if mediaserver attempts
-    // to write to it.  Don't use NULL because that's probably
-    // checked for.
-    void *dstPtr = reinterpret_cast<void *>(1);
-
-    // Spoof the device as being secure
-    bool secure = true;
-
-    uint8_t key[16] = {0};
-    uint8_t iv[16] = {0};
-    uint32_t offset = 0;
-    AString errorDetailMsg;
-
-    ssize_t result = icrypto->decrypt(secure, key, iv, mode, srcBuffer, offset,
-            subSample, 1, dstPtr, &errorDetailMsg);
-
-    // call should return an error and shouldn't kill media server
-    return (result != OK && result != DEAD_OBJECT);
-}
-
-// Test for icrypto interface vulnerabilities
-extern "C" jboolean Java_android_security_cts_MediaCryptoTest_validateCryptoNative(JNIEnv *env,
-        jclass /*clazz*/, jobject crypto)
-{
-    bool result = false;
-    sp<ICrypto> icrypto = JCrypto::GetCrypto(env, crypto);
-    if (icrypto != NULL) {
-        if (icrypto->requiresSecureDecoderComponent("video/avc")) {
-            ALOGI("device is secure, bypassing test");
-            return true;
-        }
-
-        CryptoPlugin::Mode unencryptedMode = CryptoPlugin::kMode_Unencrypted;
-        CryptoPlugin::Mode aesCtrMode = CryptoPlugin::kMode_AES_CTR;
-
-        CryptoPlugin::SubSample clrSubSample = {kBufferSize, 0};
-        CryptoPlugin::SubSample encSubSample = {0, kBufferSize};
-
-        result =
-            testCrypto(icrypto, &clrSubSample, unencryptedMode) &&
-            testCrypto(icrypto, &clrSubSample, aesCtrMode) &&
-            testCrypto(icrypto, &encSubSample, unencryptedMode) &&
-            testCrypto(icrypto, &encSubSample, aesCtrMode);
-    } else {
-        ALOGE("Failed to get icrypto interface");
-    }
-    return result;
-}
-
-
-
diff --git a/tests/tests/security/jni/android_security_cts_StagefrightFoundationTest.cpp b/tests/tests/security/jni/android_security_cts_StagefrightFoundationTest.cpp
deleted file mode 100644
index d16bd38..0000000
--- a/tests/tests/security/jni/android_security_cts_StagefrightFoundationTest.cpp
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "AudioEffectBinderTest-JNI"
-
-#include <cstdio>
-#include <jni.h>
-#include <binder/Parcel.h>
-#include <media/stagefright/foundation/AMessage.h>
-
-using namespace android;
-
-/*
- * Native methods used by
- * cts/tests/tests/security/src/android/security/cts/StagefrightFoundationTest.java
- */
-
-static jboolean android_security_cts_StagefrightFoundation_test_aMessageFromParcel(
-        JNIEnv* env __unused, jobject thiz __unused)
-{
-    const int kMaxNumItems = 64;
-    const int kNumItems = kMaxNumItems + 1 + 1000;
-    char name[128];
-
-    Parcel data;
-    data.writeInt32(0);  // what
-    data.writeInt32(kNumItems);  // numItems
-    for (int i = 0; i < kMaxNumItems; ++i) {
-        snprintf(name, sizeof(name), "item-%d", i);
-        data.writeCString(name);  // name
-        data.writeInt32(0);  // kTypeInt32
-        data.writeInt32(i);  // value
-    }
-    data.writeCString("evil");  // name
-    data.writeInt32(0);  // kTypeInt32
-    data.writeInt32(0);  // value
-    // NOTE: This could overwrite mNumItems!
-
-    for (int i = 0; i < 1000; ++i) {
-        snprintf(name, sizeof(name), "evil-%d", i);
-        data.writeCString(name);  // name
-        data.writeInt32(0);  // kTypeInt32
-        data.writeInt32(0);  // value
-    }
-
-    data.setDataPosition(0);
-    sp<AMessage> msg = AMessage::FromParcel(data);
-
-    for (int i = 0; i < kMaxNumItems; ++i) {
-        snprintf(name, sizeof(name), "item-%d", i);
-        int32_t value;
-        if (!msg->findInt32(name, &value)) {
-            ALOGE("cannot find value for %s", name);
-            return JNI_FALSE;
-        }
-        if (value != i) {
-            ALOGE("value is changed: expected %d actual %d", i, value);
-            return JNI_FALSE;
-        }
-    }
-    return JNI_TRUE;
-}
-
-int register_android_security_cts_StagefrightFoundationTest(JNIEnv *env)
-{
-    static JNINativeMethod methods[] = {
-        { "native_test_aMessageFromParcel", "()Z",
-                (void *) android_security_cts_StagefrightFoundation_test_aMessageFromParcel},
-    };
-
-    jclass clazz = env->FindClass("android/security/cts/StagefrightFoundationTest");
-    return env->RegisterNatives(clazz, methods, sizeof(methods) / sizeof(methods[0]));
-}
diff --git a/tests/tests/security/src/android/security/cts/ClonedSecureRandomTest.java b/tests/tests/security/src/android/security/cts/ClonedSecureRandomTest.java
index 19866de..4ee1480 100644
--- a/tests/tests/security/src/android/security/cts/ClonedSecureRandomTest.java
+++ b/tests/tests/security/src/android/security/cts/ClonedSecureRandomTest.java
@@ -21,7 +21,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.ServiceConnection;
-import android.os.DeadObjectException;
+import android.os.RemoteException;
 import android.os.IBinder;
 import android.security.cts.activity.ISecureRandomService;
 import android.security.cts.activity.SecureRandomService;
@@ -145,7 +145,13 @@
                 }
             }, 0);
 
-            pid = mSecureRandomService.getRandomBytesAndPid(output);
+            try {
+                pid = mSecureRandomService.getRandomBytesAndPid(output);
+            } catch (RemoteException e) {
+                // The process died before we could query. Try again.
+                continue;
+            }
+
             getContext().unbindService(mServiceConnection);
 
             /*
diff --git a/tests/tests/security/src/android/security/cts/EncryptionTest.java b/tests/tests/security/src/android/security/cts/EncryptionTest.java
index bd9a458..b2e3991 100644
--- a/tests/tests/security/src/android/security/cts/EncryptionTest.java
+++ b/tests/tests/security/src/android/security/cts/EncryptionTest.java
@@ -76,8 +76,9 @@
     public void testConfig() throws Exception {
         if (cpuHasAes()) {
             // If CPU has AES CE, it must be enabled in kernel
-            assertTrue(crypto + " is missing xts-aes-ce",
-                hasKernelCrypto("xts-aes-ce"));
+            assertTrue(crypto + " is missing xts-aes-ce or xts-aes-aesni",
+                hasKernelCrypto("xts-aes-ce") ||
+                hasKernelCrypto("xts-aes-aesni"));
         } else if (cpuHasNeon()) {
             // Otherwise, if CPU has NEON, it must be enabled
             assertTrue(crypto + " is missing xts-aes-neon (or xts-aes-neonbs)",
diff --git a/tests/tests/security/src/android/security/cts/HttpPost.java b/tests/tests/security/src/android/security/cts/HttpPost.java
deleted file mode 100644
index 7f855d9..0000000
--- a/tests/tests/security/src/android/security/cts/HttpPost.java
+++ /dev/null
@@ -1,100 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.security.cts;
-
-import android.annotation.SuppressLint;
-import android.content.Context;
-import android.content.pm.PackageManager;
-import android.os.Build;
-import android.util.Log;
-import android.util.Pair;
-
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.net.HttpURLConnection;
-import java.net.URL;
-import java.util.Locale;
-import java.util.Map;
-
-/**
- * HttpPost utility functions.
- */
-public final class HttpPost {
-    private static final String TAG = "HttpPost";
-
-    private HttpPost() {}
-
-    /**
-     * Executes a post request using {@link HttpURLConnection}.
-     *
-     * @param url The request URL.
-     * @param data The request body, or null.
-     * @param requestProperties Request properties, or null.
-     * @return The response code and body.
-     * @throws IOException If an error occurred making the request.
-     */
-    public static Pair<Integer, byte[]> execute(String url, byte[] data,
-            Map<String, String> requestProperties) throws IOException {
-        HttpURLConnection urlConnection = null;
-        try {
-            urlConnection = (HttpURLConnection) new URL(url).openConnection();
-            urlConnection.setRequestMethod("POST");
-            urlConnection.setDoOutput(data != null);
-            urlConnection.setDoInput(true);
-            urlConnection.setConnectTimeout(5000);
-            urlConnection.setReadTimeout(5000);
-            if (requestProperties != null) {
-                for (Map.Entry<String, String> requestProperty : requestProperties.entrySet()) {
-                    urlConnection.setRequestProperty(requestProperty.getKey(),
-                            requestProperty.getValue());
-                }
-            }
-            // Write the request body, if there is one.
-            if (data != null) {
-                OutputStream out = urlConnection.getOutputStream();
-                try {
-                    out.write(data);
-                } finally {
-                    out.close();
-                }
-            }
-            // Read the response code.
-            int responseCode = urlConnection.getResponseCode();
-            // Read the response body.
-            InputStream inputStream = urlConnection.getInputStream();
-            try {
-                ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
-                byte scratch[] = new byte[1024];
-                int bytesRead;
-                while ((bytesRead = inputStream.read(scratch)) != -1) {
-                    byteArrayOutputStream.write(scratch, 0, bytesRead);
-                }
-                byte[] responseBody = byteArrayOutputStream.toByteArray();
-                Log.d(TAG, "responseCode=" + responseCode + ", length=" + responseBody.length);
-                return Pair.create(responseCode, responseBody);
-            } finally {
-                inputStream.close();
-            }
-        } finally {
-            if (urlConnection != null) {
-                urlConnection.disconnect();
-            }
-        }
-    }
-
-}
diff --git a/tests/tests/security/src/android/security/cts/KeyRequester.java b/tests/tests/security/src/android/security/cts/KeyRequester.java
deleted file mode 100644
index 26a3337..0000000
--- a/tests/tests/security/src/android/security/cts/KeyRequester.java
+++ /dev/null
@@ -1,202 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.security.cts;
-
-import android.media.DeniedByServerException;
-import android.media.MediaDrm;
-import android.media.NotProvisionedException;
-import android.util.Log;
-import android.util.Pair;
-
-import java.io.UnsupportedEncodingException;
-import java.util.Arrays;
-import java.util.concurrent.CancellationException;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-import java.util.concurrent.Future;
-import java.util.concurrent.RejectedExecutionException;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
-
-public class KeyRequester {
-    private final String TAG = "KeyRequester";
-    private final int MAX_RETRY_COUNT = 3;
-    private final int POOL_SIZE = 1;
-    private final int POOL_TERMINATION_MS_TIMEOUT = 3000;  // in milliseconds
-    private final int KEYREQUEST_MS_TIMEOUT = 5000;  // in milliseconds
-
-    private byte[] mPssh;
-    private ExecutorService mExecutorService;
-    private Future<byte[]> mFuture;
-    private String mDefaultHeartbeatUrl;
-    private String mServerUrl;
-
-    public KeyRequester(byte[] pssh, String url) {
-        mPssh = pssh;
-        mServerUrl = url;
-    }
-
-    public final String getDefaultHeartbeatUrl() {
-        return mDefaultHeartbeatUrl;
-    }
-
-    public byte[] doTransact(final MediaDrm drm, final byte[] sessionId, final int keyType) {
-        boolean retryRequest;
-        boolean retryTransaction;
-        byte[] keySetIdResult;
-        int getKeyRequestRetryCount;
-        int provisioningRetryCount = 0;
-        MediaDrm.KeyRequest drmRequest;
-
-        mExecutorService = Executors.newFixedThreadPool(POOL_SIZE);
-
-        do {
-            drmRequest = null;
-            getKeyRequestRetryCount = 0;
-            keySetIdResult = null;
-            retryTransaction = false;
-
-            do {
-                retryRequest = false;
-
-                try {
-                    drmRequest = drm.getKeyRequest(sessionId, mPssh,
-                        "video/avc", keyType, null);
-                } catch (NotProvisionedException e) {
-                    Log.i(TAG, "Invalid certificate, reprovisioning");
-                    ProvisionRequester provisionRequester = new ProvisionRequester();
-                    provisionRequester.doTransact(drm);
-                    retryRequest = true;
-                }
-            } while (retryRequest && ++getKeyRequestRetryCount < MAX_RETRY_COUNT);
-
-            if (drmRequest == null) {
-                Log.e(TAG, "Failed to get key request");
-                return null;
-            }
-
-            try {
-                mFuture = mExecutorService.submit(new KeyRequesterTask(mServerUrl, drmRequest));
-            } catch (RejectedExecutionException e) {
-                Log.e(TAG, "Failed to submit KeyRequesterTask for execution", e);
-                if (++provisioningRetryCount < MAX_RETRY_COUNT) {
-                    continue;
-                } else {
-                    break;
-                }
-            }
-
-            try {
-                byte[] responseBody = mFuture.get(KEYREQUEST_MS_TIMEOUT, TimeUnit.MILLISECONDS);
-                if (responseBody == null) {
-                    Log.e(TAG, "No response from license server!");
-                    retryTransaction = true;
-                } else {
-                    byte[] drmResponse = parseResponseBody(responseBody);
-                    try {
-                        keySetIdResult = drm.provideKeyResponse(sessionId, drmResponse);
-                    } catch (NotProvisionedException e) {
-                        Log.i(TAG, "Response invalidated the certificate, reprovisioning");
-                        ProvisionRequester provisionRequester = new ProvisionRequester();
-                        provisionRequester.doTransact(drm);
-                        retryTransaction = true;
-                    } catch (DeniedByServerException e) {
-                        // informational, the event handler will take care of provisioning
-                        Log.i(TAG, "Server rejected the key request");
-                    }  catch (IllegalStateException e) {
-                        Log.e(TAG, "provideKeyResponse failed", e);
-                    }
-
-                    try {
-                        // first call to getKeyRequest does not return heartbeat url
-                        drmRequest = drm.getKeyRequest(sessionId, mPssh, "video/avc",
-                                keyType, null);
-                        try {
-                            mDefaultHeartbeatUrl = drmRequest.getDefaultUrl();
-                        } catch (Exception e) {
-                            // ignore
-                        }
-                    } catch (NotProvisionedException e) {
-                        Log.e(TAG, "Fails to get heartbeat url");
-                    }
-                    break;
-                }
-            } catch (ExecutionException | InterruptedException ex) {
-                Log.e(TAG, "Failed to execute KeyRequesterTask", ex);
-                shutdownAndAwaitTermination(mExecutorService);
-                return null;
-            } catch (TimeoutException te) {
-                // The request timed out. The network is possibly too slow.
-                // Cancel the running task.
-                Log.d(TAG, "Request timed out, retry...");
-                mFuture.cancel(true);
-                retryTransaction = true;
-            }
-        } while (retryTransaction && ++provisioningRetryCount < MAX_RETRY_COUNT);
-
-        shutdownAndAwaitTermination(mExecutorService);
-        return keySetIdResult;
-    }
-
-    private void shutdownAndAwaitTermination(ExecutorService pool) {
-        pool.shutdown();  // disable new tasks from being submitted
-        try {
-            // wait for existing tasks to terminate
-            if (!pool.awaitTermination(POOL_TERMINATION_MS_TIMEOUT, TimeUnit.MILLISECONDS)) {
-                pool.shutdownNow();
-                // wait for tasks to respond to being cancelled
-                if (!pool.awaitTermination(POOL_TERMINATION_MS_TIMEOUT, TimeUnit.MILLISECONDS))
-                    Log.e(TAG, "Pool did not terminate");
-            }
-        } catch (InterruptedException ie) {
-            // (Re-)Cancel if current thread also interrupted
-            pool.shutdownNow();
-            // Preserve interrupt status
-            Thread.currentThread().interrupt();
-        }
-    }
-
-    // Validate the response body and return the drmResponse blob.
-    private byte[] parseResponseBody(byte[] responseBody) {
-        String bodyString = null;
-        try {
-            bodyString = new String(responseBody, "UTF-8");
-        } catch (UnsupportedEncodingException e) {
-            e.printStackTrace();
-        }
-
-        if (bodyString == null) {
-            return null;
-        }
-
-        if (bodyString.startsWith("GLS/")) {
-            if (!bodyString.startsWith("GLS/1.")) {
-                Log.e(TAG, "Invalid server version, expected 1.x");
-                return null;
-            }
-            int drmMessageOffset = bodyString.indexOf("\r\n\r\n");
-            if (drmMessageOffset == -1) {
-                Log.e(TAG, "Invalid server response, could not locate drm message");
-                return null;
-            }
-            responseBody = Arrays.copyOfRange(responseBody, drmMessageOffset + 4,
-                    responseBody.length);
-        }
-        return responseBody;
-    }
-}
diff --git a/tests/tests/security/src/android/security/cts/KeyRequesterTask.java b/tests/tests/security/src/android/security/cts/KeyRequesterTask.java
deleted file mode 100644
index c4d96dd..0000000
--- a/tests/tests/security/src/android/security/cts/KeyRequesterTask.java
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.security.cts;
-
-import android.media.MediaDrm;
-import android.util.Log;
-import android.util.Pair;
-
-import java.io.IOException;
-import java.util.concurrent.Callable;
-import java.util.HashMap;
-
-public class KeyRequesterTask implements Callable<byte[]> {
-    private static final String TAG = "KeyRequesterTask";
-    private final MediaDrm.KeyRequest mDrmRequest;
-    private final String mUrl;
-
-    public KeyRequesterTask(String url, MediaDrm.KeyRequest drmRequest) {
-        mDrmRequest = drmRequest;
-        mUrl = url;
-    }
-
-    /**
-     * @return a byte array containing the license response if successful,
-     * {@code null} otherwise.
-     */
-    @Override
-    public byte[] call() throws Exception {
-        byte[] drmRequest = mDrmRequest.getData();
-        Log.d(TAG, "PostRequest:" + mUrl);
-
-        HashMap<String, String> headers = new HashMap<>();
-        headers.put("User-Agent", "Widevine CDM v1.0");
-        headers.put("Connection", "close");
-
-        try {
-            Pair<Integer, byte[]> response = HttpPost.execute(mUrl, drmRequest, headers);
-            int responseCode = response.first;
-            if (responseCode != 200) {
-                Log.d(TAG, "Server returned HTTP error code " + responseCode);
-                return null;
-            }
-            return response.second;
-        } catch (IOException e) {
-            e.printStackTrace();
-            return null;
-        }
-    }
-}
-
diff --git a/tests/tests/security/src/android/security/cts/MediaCryptoTest.java b/tests/tests/security/src/android/security/cts/MediaCryptoTest.java
deleted file mode 100644
index b5639a7..0000000
--- a/tests/tests/security/src/android/security/cts/MediaCryptoTest.java
+++ /dev/null
@@ -1,124 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.security.cts;
-
-import android.media.MediaCrypto;
-import android.media.MediaCryptoException;
-import android.media.MediaDrm;
-import android.media.MediaDrmException;
-import android.media.NotProvisionedException;
-import android.media.ResourceBusyException;
-import android.test.AndroidTestCase;
-import android.util.Log;
-import java.util.UUID;
-
-public class MediaCryptoTest extends AndroidTestCase {
-    private static final String TAG = "MediaCryptoTest";
-
-    private static final UUID CLEARKEY_SCHEME_UUID =
-        new UUID(0x1077efecc0b24d02L, 0xace33c1e52e2fb4bL);
-    private static final UUID WIDEVINE_SCHEME_UUID =
-        new UUID(0xedef8ba979d64aceL, 0xa3c827dcd51d21edL);
-
-    static {
-        System.loadLibrary("ctssecurity_jni");
-    }
-
-    private native boolean validateCryptoNative(MediaCrypto crypto);
-
-    public void testMediaCryptoClearKey() throws Exception {
-        MediaCrypto crypto = null;
-        if (!MediaDrm.isCryptoSchemeSupported(CLEARKEY_SCHEME_UUID)) {
-            Log.i(TAG, "No ClearKey plugin, skipping test");
-            return;
-        }
-        try {
-            byte[] initData = new byte[0];
-            crypto = new MediaCrypto(CLEARKEY_SCHEME_UUID, initData);
-        } catch (MediaCryptoException e) {
-            throw new Error("Failed to create MediaCrypto using ClearKey plugin");
-        }
-
-        assertTrue("MediaCrypto validation failed", validateCryptoNative(crypto));
-    }
-
-    public void testMediaCryptoWidevine() throws Exception {
-        if (!MediaDrm.isCryptoSchemeSupported(WIDEVINE_SCHEME_UUID)) {
-            Log.i(TAG, "No Widevine plugin, skipping test");
-            return;
-        }
-
-        MediaDrm drm = null;
-        byte[] sessionId = null;
-
-        try {
-            drm = new MediaDrm(WIDEVINE_SCHEME_UUID);
-            sessionId = openSession(drm);
-            getWidevineKeys(drm, sessionId);
-            MediaCrypto crypto = new MediaCrypto(WIDEVINE_SCHEME_UUID, sessionId);
-            assertTrue("MediaCrypto validation failed", validateCryptoNative(crypto));
-        } catch (MediaCryptoException | MediaDrmException e) {
-            if (drm != null && sessionId != null) {
-                drm.closeSession(sessionId);
-            }
-            throw e;
-        }
-    }
-
-    private byte[] openSession(MediaDrm drm) throws Exception {
-        byte[] sessionId = null;
-        int retryCount = 3;
-        while (retryCount-- > 0) {
-            try {
-                return drm.openSession();
-            } catch (NotProvisionedException e) {
-                Log.i(TAG, "Missing certificate, provisioning");
-                ProvisionRequester provisionRequester = new ProvisionRequester();
-                provisionRequester.doTransact(drm);
-            } catch (ResourceBusyException e) {
-                Log.w(TAG, "Resource busy in openSession, retrying...");
-                try {
-                    Thread.sleep(1000);
-                } catch (InterruptedException ie) {
-                    // ignore
-                }
-            }
-        }
-        throw new Error("Failed to open session");
-    }
-
-    private void getWidevineKeys(MediaDrm drm, byte[] sessionId) throws Exception {
-        final String kKeyServerUrl = "https://jmt17.google.com/video/license/GetCencLicense";
-        final byte[] kPssh = hex2ba("08011210e02562e04cd55351b14b3d748d36ed8e");
-        final String kClientAuth = "?source=YOUTUBE&video_id=EGHC6OHNbOo&oauth=ya.gtsqawidevine";
-        final String kPort = "80";
-        KeyRequester keyRequester = new KeyRequester(kPssh, kKeyServerUrl + ":" + kPort + kClientAuth);
-        if (keyRequester.doTransact(drm, sessionId, MediaDrm.KEY_TYPE_STREAMING) == null) {
-            throw new Error("Failed to get keys from license server!");
-        }
-    }
-
-    private static byte[] hex2ba(String s) {
-        int len = s.length();
-        byte[] data = new byte[len / 2];
-        for (int i = 0; i < len; i += 2) {
-            data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4)
-                                  + Character.digit(s.charAt(i+1), 16));
-        }
-        return data;
-    }
-}
diff --git a/tests/tests/security/src/android/security/cts/ProvisionRequester.java b/tests/tests/security/src/android/security/cts/ProvisionRequester.java
deleted file mode 100644
index ee1cb9e..0000000
--- a/tests/tests/security/src/android/security/cts/ProvisionRequester.java
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.security.cts;
-
-import android.media.DeniedByServerException;
-import android.media.MediaDrm;
-import android.util.Log;
-import android.util.Pair;
-
-import java.io.IOException;
-import java.util.HashMap;
-
-public class ProvisionRequester {
-    private final String TAG = "ProvisionRequester";
-
-    public ProvisionRequester() {
-    }
-
-    public void doTransact(final MediaDrm drm) {
-        Thread t = new Thread() {
-            @Override
-            public void run() {
-                MediaDrm.ProvisionRequest drmRequest;
-                drmRequest = drm.getProvisionRequest();
-                byte[] responseBody = postRequest(drmRequest.getDefaultUrl(),
-                        drmRequest.getData());
-
-                if (responseBody == null) {
-                    Log.e(TAG, "No response from provisioning server!");
-                } else {
-                    try {
-                        drm.provideProvisionResponse(responseBody);
-                    } catch (DeniedByServerException e) {
-                        Log.e(TAG, "Server denied provisioning request");
-                    }
-                }
-            }
-        };
-        t.start();
-
-        try {
-            t.join();
-        } catch (InterruptedException e) {
-        }
-    }
-
-    // TODO May want to throw exceptions without having try/catch in body.
-    private byte[] postRequest(String url, byte[] drmRequest) {
-        String signedUrl = url + "&signedRequest=" + new String(drmRequest);
-        Log.d(TAG, "PostRequest:" + signedUrl);
-
-        HashMap<String, String> headers = new HashMap<>();
-        headers.put("Accept", "*/*");
-        headers.put("User-Agent", "Widevine CDM v1.0");
-        headers.put("Content-Type", "application/json");
-        headers.put("Connection", "close");
-
-        try {
-            Pair<Integer, byte[]> response = HttpPost.execute(signedUrl, null, headers);
-            int responseCode = response.first;
-            if (responseCode != 200) {
-                Log.e(TAG, "Server returned HTTP error code " + responseCode);
-                return null;
-            }
-            return response.second;
-        } catch (IOException e) {
-            e.printStackTrace();
-            return null;
-        }
-    }
-
-    private void sleep(int msec) {
-        try {
-            Thread.sleep(msec);
-        } catch (InterruptedException e) {
-        }
-    }
-}
diff --git a/tests/tests/security/src/android/security/cts/StagefrightFoundationTest.java b/tests/tests/security/src/android/security/cts/StagefrightFoundationTest.java
deleted file mode 100644
index 9999d88..0000000
--- a/tests/tests/security/src/android/security/cts/StagefrightFoundationTest.java
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.security.cts;
-
-import junit.framework.TestCase;
-
-public class StagefrightFoundationTest extends TestCase {
-
-    static {
-        System.loadLibrary("ctssecurity_jni");
-    }
-
-    /**
-     * Checks that IEffect::command() cannot leak data.
-     */
-    public void test_aMessageFromParcel() throws Exception {
-        assertTrue(native_test_aMessageFromParcel());
-    }
-
-    private static native boolean native_test_aMessageFromParcel();
-}
diff --git a/tests/tests/security/src/android/security/cts/X509CertChainBuildingTest.java b/tests/tests/security/src/android/security/cts/X509CertChainBuildingTest.java
new file mode 100644
index 0000000..70e345d
--- /dev/null
+++ b/tests/tests/security/src/android/security/cts/X509CertChainBuildingTest.java
@@ -0,0 +1,230 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.cts;
+
+import android.content.res.AssetManager;
+import android.net.http.X509TrustManagerExtensions;
+import android.test.AndroidTestCase;
+
+import java.io.File;
+import java.io.InputStream;
+import java.util.Arrays;
+import java.util.List;
+import java.security.KeyStore;
+import java.security.Provider;
+import java.security.Security;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
+
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.TrustManagerFactory;
+import javax.net.ssl.X509TrustManager;
+
+/**
+ * Test that all {@link X509TrustManager} build the correct certificate chain during
+ * {@link X509TrustManagerExtensions#checkServerTrusted(X509Certificate[], String, String)} when
+ * multiple possible certificate paths exist.
+ */
+public class X509CertChainBuildingTest extends AndroidTestCase {
+    private static final String CERT_ASSET_DIR = "path_building";
+
+    /* Certificates for tests. These are initialized in setUp.
+     * All certificates use 2048 bit RSA keys and SHA-256 digests unless otherwise specified.
+     * First certificate graph:
+     *
+     * rootA: A root CA
+     * rootASha1: rootA but with SHA-1 as the digest algorithm.
+     * rootB: Another root CA
+     * leaf1: Certificate issued by rootA
+     * rootAtoB: rootA cross signed by rootB
+     * rootBtoA: rootB cross signed by rootA
+     *
+     *   [A] <-------> [B]
+     *    |
+     *    v
+     * [leaf1]
+     * Second certificate graph:
+     *
+     * intermediateA: Intermediate I issued by rootA
+     * intermediateB: Intermediate I issued by rootB
+     * leaf2: Leaf issued by I
+     *
+     * [A]   [B]
+     *    \ /
+     *    [I]
+     *     |
+     *     v
+     *  [leaf2]
+     *
+     *  These can be generated by running cts/tools/utils/certificates.py
+     */
+    private X509Certificate rootA;
+    private X509Certificate rootASha1;
+    private X509Certificate rootB;
+    private X509Certificate rootAtoB;
+    private X509Certificate rootBtoA;
+    private X509Certificate leaf1;
+    private X509Certificate leaf2;
+    private X509Certificate intermediateA;
+    private X509Certificate intermediateB;
+
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+        rootA = loadCertificate("a.pem");
+        rootASha1 = loadCertificate("a_sha1.pem");
+        rootB = loadCertificate("b.pem");
+        leaf1 = loadCertificate("leaf1.pem");
+        leaf2 = loadCertificate("leaf2.pem");
+        rootAtoB = loadCertificate("a_to_b.pem");
+        rootBtoA = loadCertificate("b_to_a.pem");
+        intermediateA = loadCertificate("intermediate_a.pem");
+        intermediateB = loadCertificate("intermediate_b.pem");
+    }
+
+    public void testBasicChain() throws Exception {
+        assertExactPath(new X509Certificate[] {leaf1, rootA},
+                new X509Certificate[] {leaf1},
+                new X509Certificate[] {rootA});
+    }
+    public void testCrossSign() throws Exception {
+        // First try a path that doesn't have the cross signed A to B certificate.
+        assertNoPath(new X509Certificate[] {leaf1, rootA}, new X509Certificate[] {rootB});
+        // Now try with one valid chain (leaf1 -> rootAtoB -> rootB).
+        assertExactPath(new X509Certificate[] {leaf1, rootAtoB, rootB},
+                new X509Certificate[] {leaf1, rootAtoB},
+                new X509Certificate[] {rootB});
+        // Now try with two possible chains present only one of which chains to a trusted root.
+        assertExactPath(new X509Certificate[] {leaf1, rootAtoB, rootB},
+                new X509Certificate[] {leaf1, rootA, rootAtoB},
+                new X509Certificate[] {rootB});
+    }
+
+    public void testUntrustedLoop() throws Exception {
+        // Verify that providing all the certificates doesn't cause the path building to get stuck
+        // in the loop caused by the cross signed certificates.
+        assertNoPath(new X509Certificate[] {leaf1, rootAtoB, rootBtoA, rootA, rootB},
+                new X509Certificate[] {});
+    }
+
+    public void testAvoidCrossSigned() throws Exception {
+        // Check that leaf1 -> rootA is preferred over using the cross signed cert when both rootA
+        // and rootB are trusted.
+        assertExactPath(new X509Certificate[] {leaf1, rootA},
+                new X509Certificate[] {leaf1, rootAtoB},
+                new X509Certificate[] {rootA, rootB});
+    }
+
+    public void testSelfIssuedPreferred() throws Exception {
+        // Check that when there are multiple possible trusted issuers that we prefer self-issued
+        // certificates over bridge versions of the same certificate.
+        assertExactPath(new X509Certificate[] {leaf1, rootA},
+                new X509Certificate[] {leaf1, rootAtoB},
+                new X509Certificate[] {rootA, rootAtoB, rootB});
+    }
+
+    public void testBridgeCrossing() throws Exception {
+        // Check that when provided with leaf2, intermediateA, intermediateB, rootA that it builds
+        // the leaf2 -> intermediateB -> B path.
+        assertExactPath(new X509Certificate[] {leaf2, intermediateB, rootB},
+                new X509Certificate[] {leaf2, intermediateA, rootA, intermediateB},
+                new X509Certificate[] {rootB});
+    }
+
+    public void testDigestOrdering() throws Exception {
+        // Check that leaf1 -> rootASha1 is valid
+        assertExactPath(new X509Certificate[] {leaf1, rootASha1},
+                new X509Certificate[] {leaf1},
+                new X509Certificate[] {rootASha1});
+        // Check that when a SHA-256 and SHA-1 are available the SHA-256 cert is used
+        assertExactPath(new X509Certificate[] {leaf1, rootA},
+                new X509Certificate[] {leaf1},
+                new X509Certificate[] {rootASha1, rootA});
+    }
+
+    private X509Certificate loadCertificate(String file) throws Exception {
+        CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
+        AssetManager assetManager = getContext().getAssets();
+        InputStream in = null;
+        try {
+            in = assetManager.open(new File(CERT_ASSET_DIR, file).toString());
+            return (X509Certificate) certFactory.generateCertificate(in);
+        } finally {
+            if (in != null) {
+                in.close();
+            }
+        }
+    }
+
+    private static X509TrustManager getTrustManager(KeyStore ks, Provider p) throws Exception {
+        TrustManagerFactory tmf = TrustManagerFactory.getInstance("PKIX", p);
+        tmf.init(ks);
+        for (TrustManager tm : tmf.getTrustManagers()) {
+            if (tm instanceof X509TrustManager) {
+                return (X509TrustManager) tm;
+            }
+        }
+        fail("Unable to find X509TrustManager");
+        return null;
+    }
+
+    /**
+     * Asserts that all PKIX TrustManagerFactory providers build the expected certificate path or
+     * throw a {@code CertificateException} if {@code expected} is {@code null}.
+     */
+    private static void assertExactPath(X509Certificate[] expected, X509Certificate[] bagOfCerts,
+            X509Certificate[] roots) throws Exception {
+        KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
+        ks.load(null);
+        int i = 0;
+        for (X509Certificate root : roots) {
+            ks.setEntry(String.valueOf(i++), new KeyStore.TrustedCertificateEntry(root), null);
+        }
+        Provider[] providers = Security.getProviders("TrustManagerFactory.PKIX");
+        assertNotNull(providers);
+        assertTrue("No providers found", providers.length != 0);
+        for (Provider p : providers) {
+            try {
+                X509TrustManager tm = getTrustManager(ks, p);
+                X509TrustManagerExtensions xtm = new X509TrustManagerExtensions(tm);
+                List<X509Certificate> result;
+                try {
+                    result = xtm.checkServerTrusted(bagOfCerts, "RSA", null);
+                } catch (CertificateException e) {
+                    if (expected == null) {
+                        // Exception expected.
+                        continue;
+                    }
+                    throw e;
+                }
+                if (expected == null) {
+                    fail("checkServerTrusted expected to fail, instead returned: " + result);
+                }
+                assertEquals(Arrays.asList(expected), result);
+            } catch (Exception e) {
+                throw new Exception("Failed with provider " + p, e);
+            }
+        }
+    }
+
+    private static void assertNoPath(X509Certificate[] bagOfCerts, X509Certificate[] roots)
+            throws Exception {
+        assertExactPath(null, bagOfCerts, roots);
+    }
+
+}
diff --git a/tests/tests/telecom/AndroidManifest.xml b/tests/tests/telecom/AndroidManifest.xml
index b4d44db..517b463 100644
--- a/tests/tests/telecom/AndroidManifest.xml
+++ b/tests/tests/telecom/AndroidManifest.xml
@@ -45,6 +45,15 @@
             <intent-filter>
                 <action android:name="android.telecom.InCallService"/>
             </intent-filter>
+            <meta-data android:name="android.telecom.IN_CALL_SERVICE_UI" android:value="true" />
+        </service>
+
+        <service android:name="android.telecom.cts.MockCallScreeningService"
+            android:permission="android.permission.BIND_SCREENING_SERVICE"
+            android:enabled="false" >
+            <intent-filter>
+                <action android:name="android.telecom.CallScreeningService"/>
+            </intent-filter>
         </service>
 
         <activity android:name="android.telecom.cts.MockDialerActivity">
diff --git a/tests/tests/telecom/src/android/telecom/cts/BaseTelecomTestWithMockServices.java b/tests/tests/telecom/src/android/telecom/cts/BaseTelecomTestWithMockServices.java
index 8bb3cc1..1e7377d 100644
--- a/tests/tests/telecom/src/android/telecom/cts/BaseTelecomTestWithMockServices.java
+++ b/tests/tests/telecom/src/android/telecom/cts/BaseTelecomTestWithMockServices.java
@@ -890,7 +890,7 @@
      * was invoked with. This class is prefixed Invoke rather than the more typical Call for
      * disambiguation purposes.
      */
-    protected static final class InvokeCounter {
+    public static final class InvokeCounter {
         private final String mName;
         private final Object mLock = new Object();
         private final ArrayList<Object[]> mInvokeArgs = new ArrayList<>();
diff --git a/tests/tests/telecom/src/android/telecom/cts/CallScreeningServiceTest.java b/tests/tests/telecom/src/android/telecom/cts/CallScreeningServiceTest.java
new file mode 100644
index 0000000..0704a8d
--- /dev/null
+++ b/tests/tests/telecom/src/android/telecom/cts/CallScreeningServiceTest.java
@@ -0,0 +1,187 @@
+/*
+ * 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.telecom.cts;
+
+import static android.telecom.cts.TestUtils.shouldTestTelecom;
+import static org.junit.Assert.assertTrue;
+
+import android.telecom.cts.MockCallScreeningService.CallScreeningServiceCallbacks;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Bundle;
+import android.telecom.Call;
+import android.telecom.CallScreeningService;
+import android.telecom.PhoneAccount;
+import android.telecom.PhoneAccountHandle;
+import android.telecom.TelecomManager;
+import android.test.InstrumentationTestCase;
+import android.text.TextUtils;
+
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Verify that call screening service gets a chance to block calls.
+ */
+public class CallScreeningServiceTest extends InstrumentationTestCase {
+    private static final Uri TEST_NUMBER = Uri.fromParts("tel", "7", null);
+
+    public static final PhoneAccountHandle TEST_PHONE_ACCOUNT_HANDLE = new PhoneAccountHandle(
+            new ComponentName(TestUtils.PACKAGE, TestUtils.COMPONENT),
+            TestUtils.ACCOUNT_ID);
+
+    public static final PhoneAccount TEST_PHONE_ACCOUNT = PhoneAccount.builder(
+            TEST_PHONE_ACCOUNT_HANDLE, TestUtils.ACCOUNT_LABEL)
+            .setAddress(Uri.parse("tel:555-TEST"))
+            .setCapabilities(PhoneAccount.CAPABILITY_CALL_PROVIDER |
+                    PhoneAccount.CAPABILITY_CONNECTION_MANAGER)
+            .addSupportedUriScheme(PhoneAccount.SCHEME_TEL)
+            .build();
+
+    private Context mContext;
+    private TelecomManager mTelecomManager;
+    private String mPreviousDefaultDialer;
+    MockConnectionService mConnectionService;
+    private boolean mCallFound;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mContext = getInstrumentation().getContext();
+        mTelecomManager = (TelecomManager) mContext.getSystemService(Context.TELECOM_SERVICE);
+        if (shouldTestTelecom(mContext)) {
+            mPreviousDefaultDialer = TestUtils.getDefaultDialer(getInstrumentation());
+            TestUtils.setDefaultDialer(getInstrumentation(), TestUtils.PACKAGE);
+            setupConnectionService();
+            MockCallScreeningService.enableService(mContext);
+        }
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        if (!TextUtils.isEmpty(mPreviousDefaultDialer)) {
+            TestUtils.setDefaultDialer(getInstrumentation(), mPreviousDefaultDialer);
+            mTelecomManager.unregisterPhoneAccount(TEST_PHONE_ACCOUNT_HANDLE);
+            CtsConnectionService.tearDown();
+            mConnectionService = null;
+            MockCallScreeningService.disableService(mContext);
+        }
+        super.tearDown();
+    }
+
+    /**
+     * Tests that when sending a CALL intent via the Telecom stack, Telecom binds to the registered
+     * {@link CallScreeningService}s and invokes onScreenCall.
+     */
+    public void testTelephonyCall_bindsToCallScreeningService() {
+        if (!shouldTestTelecom(mContext)) {
+            return;
+        }
+
+        CallScreeningServiceCallbacks callbacks = createCallbacks();
+        MockCallScreeningService.setCallbacks(callbacks);
+        addNewIncomingCall(TEST_NUMBER);
+
+        try {
+            if (callbacks.lock.tryAcquire(TestUtils.WAIT_FOR_CALL_ADDED_TIMEOUT_S,
+                        TimeUnit.SECONDS)) {
+                assertTrue(mCallFound);
+                return;
+            }
+        } catch (InterruptedException e) {
+        }
+
+        fail("No call added to CallScreeningService.");
+    }
+
+    private void addNewIncomingCall(Uri incomingHandle) {
+        Bundle extras = new Bundle();
+        extras.putParcelable(TelecomManager.EXTRA_INCOMING_CALL_ADDRESS, incomingHandle);
+        mTelecomManager.addNewIncomingCall(TEST_PHONE_ACCOUNT_HANDLE, extras);
+    }
+
+    private CallScreeningServiceCallbacks createCallbacks() {
+        return new CallScreeningServiceCallbacks() {
+            @Override
+            public void onScreenCall(Call.Details callDetails) {
+                mCallFound = true;
+                CallScreeningService.CallResponse response =
+                        new CallScreeningService.CallResponse.Builder()
+                        .setDisallowCall(true)
+                        .setRejectCall(true)
+                        .setSkipCallLog(true)
+                        .setSkipNotification(true)
+                        .build();
+                getService().respondToCall(callDetails, response);
+                lock.release();
+            }
+        };
+    }
+
+    private void setupConnectionService() throws Exception {
+        mConnectionService = new MockConnectionService();
+        CtsConnectionService.setUp(TEST_PHONE_ACCOUNT_HANDLE, mConnectionService);
+
+        mTelecomManager.registerPhoneAccount(TEST_PHONE_ACCOUNT);
+        TestUtils.enablePhoneAccount(getInstrumentation(), TEST_PHONE_ACCOUNT_HANDLE);
+        // Wait till the adb commands have executed and account is enabled in Telecom database.
+        assertPhoneAccountEnabled(TEST_PHONE_ACCOUNT_HANDLE);
+    }
+
+    private void assertPhoneAccountEnabled(final PhoneAccountHandle handle) {
+        waitUntilConditionIsTrueOrTimeout(
+                new Condition() {
+                    @Override
+                    public Object expected() {
+                        return true;
+                    }
+
+                    @Override
+                    public Object actual() {
+                        PhoneAccount phoneAccount = mTelecomManager.getPhoneAccount(handle);
+                        return (phoneAccount != null && phoneAccount.isEnabled());
+                    }
+                },
+                TestUtils.WAIT_FOR_STATE_CHANGE_TIMEOUT_MS,
+                "Phone account enable failed for " + handle
+        );
+    }
+
+    private void waitUntilConditionIsTrueOrTimeout(Condition condition, long timeout,
+            String description) {
+        final long start = System.currentTimeMillis();
+        while (!condition.expected().equals(condition.actual())
+                && System.currentTimeMillis() - start < timeout) {
+            sleep(50);
+        }
+        assertEquals(description, condition.expected(), condition.actual());
+    }
+
+    private void sleep(long ms) {
+        try {
+            Thread.sleep(ms);
+        } catch (InterruptedException e) {
+        }
+    }
+
+    protected interface Condition {
+        Object expected();
+        Object actual();
+    }
+}
diff --git a/tests/tests/telecom/src/android/telecom/cts/MockCallScreeningService.java b/tests/tests/telecom/src/android/telecom/cts/MockCallScreeningService.java
new file mode 100644
index 0000000..3c7d2b6
--- /dev/null
+++ b/tests/tests/telecom/src/android/telecom/cts/MockCallScreeningService.java
@@ -0,0 +1,107 @@
+/*
+ * 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.telecom.cts;
+
+import static org.junit.Assert.assertFalse;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.telecom.Call;
+import android.telecom.CallScreeningService;
+import android.util.Log;
+
+import java.util.concurrent.Semaphore;
+
+public class MockCallScreeningService extends CallScreeningService {
+    private static String LOG_TAG = "MockCallScreeningSvc";
+
+    private static boolean mIsServiceUnbound;
+    private static CallScreeningServiceCallbacks sCallbacks;
+
+    public static abstract class CallScreeningServiceCallbacks {
+        public Semaphore lock = new Semaphore(0);
+        private MockCallScreeningService mService;
+
+        public void onScreenCall(Call.Details callDetails) {};
+
+        final public MockCallScreeningService getService() {
+            return mService;
+        }
+
+        final public void setService(MockCallScreeningService service) {
+            mService = service;
+        }
+    }
+
+    @Override
+    public android.os.IBinder onBind(android.content.Intent intent) {
+        Log.i(LOG_TAG, "Service bounded");
+        if (getCallbacks() != null) {
+            getCallbacks().setService(this);
+        }
+        mIsServiceUnbound = false;
+        return super.onBind(intent);
+    }
+
+    @Override
+    public void onScreenCall(Call.Details callDetails) {
+        if (getCallbacks() != null) {
+            getCallbacks().onScreenCall(callDetails);
+        }
+    }
+
+    public static void setCallbacks(CallScreeningServiceCallbacks callbacks) {
+        sCallbacks = callbacks;
+    }
+
+    private CallScreeningServiceCallbacks getCallbacks() {
+        if (sCallbacks != null) {
+            sCallbacks.setService(this);
+        }
+        return sCallbacks;
+    }
+
+    @Override
+    public boolean onUnbind(Intent intent) {
+        Log.i(LOG_TAG, "Service unbounded");
+        assertFalse(mIsServiceUnbound);
+        mIsServiceUnbound = true;
+        return super.onUnbind(intent);
+    }
+
+    public static boolean isServiceUnbound() {
+        return mIsServiceUnbound;
+    }
+
+    public static void enableService(Context context) {
+        context.getPackageManager().setComponentEnabledSetting(getComponentName(context),
+            PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
+            PackageManager.DONT_KILL_APP);
+    }
+
+    public static void disableService(Context context) {
+        context.getPackageManager().setComponentEnabledSetting(getComponentName(context),
+            PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
+            PackageManager.DONT_KILL_APP);
+    }
+
+    private static ComponentName getComponentName(Context context) {
+        return new ComponentName(context, MockCallScreeningService.class);
+    }
+}
diff --git a/tests/tests/telecom/src/android/telecom/cts/MockVideoProvider.java b/tests/tests/telecom/src/android/telecom/cts/MockVideoProvider.java
index b19b5b7..6a0d874 100644
--- a/tests/tests/telecom/src/android/telecom/cts/MockVideoProvider.java
+++ b/tests/tests/telecom/src/android/telecom/cts/MockVideoProvider.java
@@ -17,7 +17,9 @@
 package android.telecom.cts;
 
 import android.net.Uri;
+import android.os.RemoteException;
 import android.telecom.Connection;
+import android.telecom.RemoteConnection;
 import android.telecom.VideoProfile;
 import android.view.Surface;
 
@@ -48,6 +50,7 @@
     private Surface mPreviewSurface = null;
     private Surface mDisplaySurface = null;
     private VideoProfile mSessionModifyResponse = null;
+    private BaseTelecomTestWithMockServices.InvokeCounter mVideoProviderHandlerTracker;
 
     public MockVideoProvider(MockConnection mockConnection) {
         mMockConnection = mockConnection;
@@ -75,6 +78,10 @@
 
     @Override
     public void onSetZoom(float value) {
+        if (mVideoProviderHandlerTracker != null) {
+            mVideoProviderHandlerTracker.invoke();
+            return;
+        }
         mZoom = value;
     }
 
@@ -135,6 +142,17 @@
     }
 
     /**
+     * Waits until all messages in the VideoProvider message handler up to the point of this call
+     * have been cleared and processed. Use this to wait for the callback to actually register.
+     */
+    public void waitForVideoProviderHandler(RemoteConnection.VideoProvider remoteVideoProvider) {
+        mVideoProviderHandlerTracker =
+                new BaseTelecomTestWithMockServices.InvokeCounter("WaitForHandler");
+        remoteVideoProvider.setZoom(0);
+        mVideoProviderHandlerTracker.waitForCount(1);
+        mVideoProviderHandlerTracker = null;
+    }
+    /**
      * Sends a mock video quality value from the provider.
      *
      * @param videoQuality The video quality.
diff --git a/tests/tests/telecom/src/android/telecom/cts/RemoteConnectionTest.java b/tests/tests/telecom/src/android/telecom/cts/RemoteConnectionTest.java
index 5b552a0..f464dbf 100644
--- a/tests/tests/telecom/src/android/telecom/cts/RemoteConnectionTest.java
+++ b/tests/tests/telecom/src/android/telecom/cts/RemoteConnectionTest.java
@@ -619,6 +619,7 @@
         };
         remoteVideoProvider.registerCallback(videoCallback);
         VideoProfile videoProfile = new VideoProfile(VideoProfile.STATE_BIDIRECTIONAL);
+        mockVideoProvider.waitForVideoProviderHandler(remoteVideoProvider);
         mockVideoProvider.sendMockSessionModifyRequest(videoProfile);
         callbackInvoker.waitForCount(1, WAIT_FOR_STATE_CHANGE_TIMEOUT_MS);
         assertEquals(remoteVideoProvider, callbackInvoker.getArgs(0)[0]);
@@ -647,6 +648,7 @@
             }
         };
         remoteVideoProvider.registerCallback(videoCallback);
+        mockVideoProvider.waitForVideoProviderHandler(remoteVideoProvider);
         mockVideoProvider.handleCallSessionEvent(Connection.VideoProvider.SESSION_EVENT_RX_PAUSE);
         callbackInvoker.waitForCount(1, WAIT_FOR_STATE_CHANGE_TIMEOUT_MS);
         assertEquals(remoteVideoProvider, callbackInvoker.getArgs(0)[0]);
@@ -677,6 +679,7 @@
         };
         remoteVideoProvider.registerCallback(videoCallback);
         final int width = 100, heigth = 20;
+        mockVideoProvider.waitForVideoProviderHandler(remoteVideoProvider);
         mockVideoProvider.changePeerDimensions(width, heigth);
         callbackInvoker.waitForCount(1, WAIT_FOR_STATE_CHANGE_TIMEOUT_MS);
         assertEquals(remoteVideoProvider, callbackInvoker.getArgs(0)[0]);
@@ -707,6 +710,7 @@
         };
         remoteVideoProvider.registerCallback(videoCallback);
         long callDataUsage = 10000;
+        mockVideoProvider.waitForVideoProviderHandler(remoteVideoProvider);
         mockVideoProvider.setCallDataUsage(callDataUsage);
         callbackInvoker.waitForCount(1, WAIT_FOR_STATE_CHANGE_TIMEOUT_MS);
         assertEquals(remoteVideoProvider, callbackInvoker.getArgs(0)[0]);
@@ -738,6 +742,7 @@
         };
         remoteVideoProvider.registerCallback(videoCallback);
         VideoProfile.CameraCapabilities capabilities = new VideoProfile.CameraCapabilities(100, 200);
+        mockVideoProvider.waitForVideoProviderHandler(remoteVideoProvider);
         mockVideoProvider.changeCameraCapabilities(capabilities);
         callbackInvoker.waitForCount(1, WAIT_FOR_STATE_CHANGE_TIMEOUT_MS);
         assertEquals(remoteVideoProvider, callbackInvoker.getArgs(0)[0]);
@@ -767,6 +772,7 @@
         };
         remoteVideoProvider.registerCallback(videoCallback);
         final int videoQuality = 10;
+        mockVideoProvider.waitForVideoProviderHandler(remoteVideoProvider);
         mockVideoProvider.changeVideoQuality(videoQuality);
         callbackInvoker.waitForCount(1, WAIT_FOR_STATE_CHANGE_TIMEOUT_MS);
         assertEquals(remoteVideoProvider, callbackInvoker.getArgs(0)[0]);
diff --git a/tests/tests/telecom/src/android/telecom/cts/TestUtils.java b/tests/tests/telecom/src/android/telecom/cts/TestUtils.java
index 05532e3..0ade4aa 100644
--- a/tests/tests/telecom/src/android/telecom/cts/TestUtils.java
+++ b/tests/tests/telecom/src/android/telecom/cts/TestUtils.java
@@ -96,7 +96,7 @@
         final ComponentName component = handle.getComponentName();
         executeShellCommand(instrumentation, COMMAND_REGISTER_SIM
                 + component.getPackageName() + "/" + component.getClassName() + " "
-                + handle.getId() + " " + PRIMARY_USER_SN);
+                + handle.getId() + " " + PRIMARY_USER_SN + " " + label + " " + address);
     }
 
     /**
diff --git a/tests/tests/telephony/src/android/telephony/cts/CarrierConfigManagerTest.java b/tests/tests/telephony/src/android/telephony/cts/CarrierConfigManagerTest.java
index 4ca35b8..d1d739e 100644
--- a/tests/tests/telephony/src/android/telephony/cts/CarrierConfigManagerTest.java
+++ b/tests/tests/telephony/src/android/telephony/cts/CarrierConfigManagerTest.java
@@ -74,7 +74,7 @@
 
     public void testGetConfigForSubId() {
         PersistableBundle config =
-                mConfigManager.getConfigForSubId(SubscriptionManager.getDefaultSubId());
+                mConfigManager.getConfigForSubId(SubscriptionManager.getDefaultSubscriptionId());
         checkConfig(config);
     }
 
@@ -86,7 +86,8 @@
     public void testNotifyConfigChangedForSubId() {
         try {
             if (isSimCardPresent()) {
-                mConfigManager.notifyConfigChangedForSubId(SubscriptionManager.getDefaultSubId());
+                mConfigManager.notifyConfigChangedForSubId(
+                        SubscriptionManager.getDefaultSubscriptionId());
                 fail("Expected SecurityException. App doesn't have carrier privileges.");
             }
         } catch (SecurityException expected) {
diff --git a/tests/tests/telephony/src/android/telephony/cts/SimRestrictedApisTest.java b/tests/tests/telephony/src/android/telephony/cts/SimRestrictedApisTest.java
index 20810a8..0f09314 100644
--- a/tests/tests/telephony/src/android/telephony/cts/SimRestrictedApisTest.java
+++ b/tests/tests/telephony/src/android/telephony/cts/SimRestrictedApisTest.java
@@ -74,7 +74,7 @@
     public void testSetLine1NumberForDisplay2() {
         try {
             if (isSimCardPresent()) {
-                TelephonyManager.getDefault().setLine1NumberForDisplayForSubscriber(0, "", "");
+                TelephonyManager.getDefault().setLine1NumberForDisplay(0, "", "");
                 fail("Expected SecurityException. App doesn't have carrier privileges.");
             }
         } catch (SecurityException expected) {
diff --git a/tests/tests/telephony/src/android/telephony/cts/SmsUsageMonitorShortCodeTest.java b/tests/tests/telephony/src/android/telephony/cts/SmsUsageMonitorShortCodeTest.java
index 145cc84..0527e9a 100644
--- a/tests/tests/telephony/src/android/telephony/cts/SmsUsageMonitorShortCodeTest.java
+++ b/tests/tests/telephony/src/android/telephony/cts/SmsUsageMonitorShortCodeTest.java
@@ -299,7 +299,7 @@
             new ShortCodeTest("it", "112", CATEGORY_NOT_SHORT_CODE),
             new ShortCodeTest("it", "116117", CATEGORY_FREE_SHORT_CODE),
             new ShortCodeTest("it", "4567", CATEGORY_NOT_SHORT_CODE),
-            new ShortCodeTest("it", "48000", CATEGORY_FREE_SHORT_CODE),
+            new ShortCodeTest("it", "48000", CATEGORY_PREMIUM_SHORT_CODE),
             new ShortCodeTest("it", "45678", CATEGORY_PREMIUM_SHORT_CODE),
             new ShortCodeTest("it", "56789", CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE),
             new ShortCodeTest("it", "456789", CATEGORY_NOT_SHORT_CODE),
diff --git a/tests/tests/text/src/android/text/cts/HtmlTest.java b/tests/tests/text/src/android/text/cts/HtmlTest.java
index 0531875..f3ab787 100644
--- a/tests/tests/text/src/android/text/cts/HtmlTest.java
+++ b/tests/tests/text/src/android/text/cts/HtmlTest.java
@@ -24,6 +24,7 @@
 import android.text.Spanned;
 import android.text.Html.ImageGetter;
 import android.text.Html.TagHandler;
+import android.text.style.BackgroundColorSpan;
 import android.text.style.ForegroundColorSpan;
 import android.text.style.QuoteSpan;
 import android.text.style.StrikethroughSpan;
@@ -154,7 +155,6 @@
 
     public void testMarkup() throws Exception {
         final int start = 6;
-
         SpannableString s = new SpannableString("Hello bold world");
         int end = s.length() - start;
         s.setSpan(new StyleSpan(Typeface.BOLD), start, end, SPAN_EXCLUSIVE_INCLUSIVE);
@@ -188,14 +188,30 @@
         s = new SpannableString("Hello struck world");
         end = s.length() - start;
         s.setSpan(new StrikethroughSpan(), start, end, SPAN_EXCLUSIVE_INCLUSIVE);
-        assertEquals("<p dir=\"ltr\">Hello <strike>struck</strike> world</p>\n", Html.toHtml(s));
+        assertEquals("<p dir=\"ltr\">Hello "
+                + "<span style=\"text-decoration:line-through;\">struck</span> world</p>\n",
+                Html.toHtml(s));
 
         s = new SpannableString("Hello linky world");
         end = s.length() - start;
         s.setSpan(new URLSpan("http://www.google.com"), start, end, SPAN_EXCLUSIVE_INCLUSIVE);
-        String ret = Html.toHtml(s);
-        assertEquals("<p dir=\"ltr\">Hello <a href=\"http://www.google.com\">linky</a> world</p>\n",
-                ret);
+        assertEquals("<p dir=\"ltr\">Hello "
+                + "<a href=\"http://www.google.com\">linky</a> world</p>\n",
+                Html.toHtml(s));
+
+        s = new SpannableString("Hello foreground world");
+        end = s.length() - start;
+        s.setSpan(new ForegroundColorSpan(0x00FF00), start, end, SPAN_EXCLUSIVE_INCLUSIVE);
+        assertEquals("<p dir=\"ltr\">Hello "
+                + "<span style=\"color:#00FF00;\">foreground</span> world</p>\n",
+                Html.toHtml(s));
+
+        s = new SpannableString("Hello background world");
+        end = s.length() - start;
+        s.setSpan(new BackgroundColorSpan(0x00FF00), start, end, SPAN_EXCLUSIVE_INCLUSIVE);
+        assertEquals("<p dir=\"ltr\">Hello "
+                + "<span style=\"background-color:#00FF00;\">background</span> world</p>\n",
+                Html.toHtml(s));
     }
 
     public void testMarkupFromHtml() throws Exception {
diff --git a/tests/tests/text/src/android/text/cts/SpannableStringBuilderTest.java b/tests/tests/text/src/android/text/cts/SpannableStringBuilderTest.java
index ace9c52..6dee5e9 100644
--- a/tests/tests/text/src/android/text/cts/SpannableStringBuilderTest.java
+++ b/tests/tests/text/src/android/text/cts/SpannableStringBuilderTest.java
@@ -673,8 +673,12 @@
 
         assertNotNull(spans);
         assertEquals(4, spans.length);
+        // priority spans are first
         assertEquals(fourth, spans[0]);
         assertEquals(third, spans[1]);
+        // other spans should be there
+        assertEquals(second, spans[2]);
+        assertEquals(first, spans[3]);
     }
 
     public void testLength() {
diff --git a/tests/tests/text/src/android/text/style/cts/LocaleSpanTest.java b/tests/tests/text/src/android/text/style/cts/LocaleSpanTest.java
index 93e6cf9..80aa68e 100644
--- a/tests/tests/text/src/android/text/style/cts/LocaleSpanTest.java
+++ b/tests/tests/text/src/android/text/style/cts/LocaleSpanTest.java
@@ -25,7 +25,7 @@
 
 public class LocaleSpanTest extends TestCase {
 
-    public void testGetLocalesMain(@NonNull final LocaleList locales) {
+    private void checkGetLocales(@NonNull final LocaleList locales) {
         final LocaleSpan span = new LocaleSpan(locales);
         assertEquals(locales.getPrimary(), span.getLocale());
         assertEquals(locales, span.getLocales());
@@ -36,10 +36,10 @@
     }
 
     public void testGetLocales() {
-        testGetLocalesMain(LocaleList.getEmptyLocaleList());
-        testGetLocalesMain(LocaleList.forLanguageTags("en"));
-        testGetLocalesMain(LocaleList.forLanguageTags("en-GB,en"));
-        testGetLocalesMain(LocaleList.forLanguageTags("de-DE-u-co-phonebk,en-GB,en"));
+        checkGetLocales(LocaleList.getEmptyLocaleList());
+        checkGetLocales(LocaleList.forLanguageTags("en"));
+        checkGetLocales(LocaleList.forLanguageTags("en-GB,en"));
+        checkGetLocales(LocaleList.forLanguageTags("de-DE-u-co-phonebk,en-GB,en"));
     }
 
     public void testConstructorWithLocaleList() {
diff --git a/tests/tests/text/src/android/text/style/cts/SuggestionSpanTest.java b/tests/tests/text/src/android/text/style/cts/SuggestionSpanTest.java
index ec51216..ff5167c 100644
--- a/tests/tests/text/src/android/text/style/cts/SuggestionSpanTest.java
+++ b/tests/tests/text/src/android/text/style/cts/SuggestionSpanTest.java
@@ -48,7 +48,7 @@
         return original.toString();
     }
 
-    public void testGetLocaleObjectMain(final Locale locale) {
+    private void checkGetLocaleObject(final Locale locale) {
         final SuggestionSpan span = new SuggestionSpan(locale, new String[0],
                 SuggestionSpan.FLAG_AUTO_CORRECTION);
         // In the context of SuggestionSpan#getLocaleObject(), we do care only about subtags that
@@ -63,16 +63,16 @@
     }
 
     public void testGetLocaleObject() {
-        testGetLocaleObjectMain(Locale.forLanguageTag("en"));
-        testGetLocaleObjectMain(Locale.forLanguageTag("en-GB"));
-        testGetLocaleObjectMain(Locale.forLanguageTag("EN-GB"));
-        testGetLocaleObjectMain(Locale.forLanguageTag("en-gb"));
-        testGetLocaleObjectMain(Locale.forLanguageTag("En-gB"));
-        testGetLocaleObjectMain(Locale.forLanguageTag("und"));
-        testGetLocaleObjectMain(Locale.forLanguageTag("de-DE-u-co-phonebk"));
-        testGetLocaleObjectMain(Locale.forLanguageTag(""));
-        testGetLocaleObjectMain(null);
-        testGetLocaleObjectMain(new Locale(" an  ", " i n v a l i d ", "data"));
+        checkGetLocaleObject(Locale.forLanguageTag("en"));
+        checkGetLocaleObject(Locale.forLanguageTag("en-GB"));
+        checkGetLocaleObject(Locale.forLanguageTag("EN-GB"));
+        checkGetLocaleObject(Locale.forLanguageTag("en-gb"));
+        checkGetLocaleObject(Locale.forLanguageTag("En-gB"));
+        checkGetLocaleObject(Locale.forLanguageTag("und"));
+        checkGetLocaleObject(Locale.forLanguageTag("de-DE-u-co-phonebk"));
+        checkGetLocaleObject(Locale.forLanguageTag(""));
+        checkGetLocaleObject(null);
+        checkGetLocaleObject(new Locale(" an  ", " i n v a l i d ", "data"));
     }
 
     @NonNull
diff --git a/tests/tests/tv/src/android/media/tv/cts/TvInputServiceTest.java b/tests/tests/tv/src/android/media/tv/cts/TvInputServiceTest.java
index 2a3a713..4afb851 100644
--- a/tests/tests/tv/src/android/media/tv/cts/TvInputServiceTest.java
+++ b/tests/tests/tv/src/android/media/tv/cts/TvInputServiceTest.java
@@ -25,9 +25,11 @@
 import android.media.tv.TvContract;
 import android.media.tv.TvInputInfo;
 import android.media.tv.TvInputManager;
+import android.media.tv.TvRecordingClient;
 import android.media.tv.TvTrackInfo;
 import android.media.tv.TvView;
 import android.media.tv.cts.TvInputServiceTest.CountingTvInputService.CountingSession;
+import android.media.tv.cts.TvInputServiceTest.CountingTvInputService.CountingRecordingSession;
 import android.net.Uri;
 import android.os.SystemClock;
 import android.test.ActivityInstrumentationTestCase2;
@@ -57,6 +59,7 @@
             .setVideoWidth(1920).setVideoHeight(1080).setLanguage("und").build();
 
     private TvView mTvView;
+    private TvRecordingClient mTvRecordingClient;
     private Activity mActivity;
     private Instrumentation mInstrumentation;
     private TvInputManager mManager;
@@ -64,6 +67,7 @@
     private final StubCallback mCallback = new StubCallback();
     private final StubTimeShiftPositionCallback mTimeShiftPositionCallback =
             new StubTimeShiftPositionCallback();
+    private final StubRecordingCallback mRecordingCallback = new StubRecordingCallback();
 
     private static class StubCallback extends TvView.TvInputCallback {
         private int mChannelRetunedCount;
@@ -166,6 +170,8 @@
         mActivity = getActivity();
         mInstrumentation = getInstrumentation();
         mTvView = (TvView) mActivity.findViewById(R.id.tvview);
+        mTvRecordingClient = new TvRecordingClient(mActivity, "TvInputServiceTest",
+                mRecordingCallback, null);
         mManager = (TvInputManager) mActivity.getSystemService(Context.TV_INPUT_SERVICE);
         for (TvInputInfo info : mManager.getTvInputList()) {
             if (info.getServiceInfo().name.equals(CountingTvInputService.class.getName())) {
@@ -179,7 +185,7 @@
         CountingTvInputService.sSession = null;
     }
 
-    public void testTvInputService() throws Throwable {
+    public void testTvInputServiceSession() throws Throwable {
         if (!Utils.hasTvInputFramework(getActivity())) {
             return;
         }
@@ -218,7 +224,117 @@
         mInstrumentation.waitForIdleSync();
     }
 
+    public void testTvInputServiceRecordingSession() throws Throwable {
+        if (!Utils.hasTvInputFramework(getActivity())) {
+            return;
+        }
+        verifyCommandConnect();
+    }
+
+    public void verifyCommandConnect() {
+        resetCounts();
+        Uri fakeChannelUri = TvContract.buildChannelUri(0);
+        mTvRecordingClient.connect(mStubInfo.getId(), fakeChannelUri);
+        new PollingCheck(TIME_OUT) {
+            @Override
+            protected boolean check() {
+                CountingRecordingSession session = CountingTvInputService.sRecordingSession;
+                return session != null && session.mConnectCount > 0;
+            }
+        }.run();
+    }
+
+    public void verifyCommandDisconnect() {
+        resetCounts();
+        mTvRecordingClient.disconnect();
+        new PollingCheck(TIME_OUT) {
+            @Override
+            protected boolean check() {
+                CountingRecordingSession session = CountingTvInputService.sRecordingSession;
+                return session != null && session.mDisconnectCount > 0;
+            }
+        }.run();
+    }
+
+    public void verifyCommandStartRecording() {
+        resetCounts();
+        mTvRecordingClient.startRecording();
+        new PollingCheck(TIME_OUT) {
+            @Override
+            protected boolean check() {
+                CountingRecordingSession session = CountingTvInputService.sRecordingSession;
+                return session != null && session.mStartRecordingCount> 0;
+            }
+        }.run();
+    }
+
+    public void verifyCommandStopRecording() {
+        resetCounts();
+        mTvRecordingClient.stopRecording();
+        new PollingCheck(TIME_OUT) {
+            @Override
+            protected boolean check() {
+                CountingRecordingSession session = CountingTvInputService.sRecordingSession;
+                return session != null && session.mStopRecordingCount > 0;
+            }
+        }.run();
+    }
+
+    public void verifyCallbackConnected() {
+        resetCounts();
+        CountingRecordingSession session = CountingTvInputService.sRecordingSession;
+        assertNotNull(session);
+        session.notifyConnected();
+        new PollingCheck(TIME_OUT) {
+            @Override
+            protected boolean check() {
+                return mRecordingCallback.mConnectedCount > 0;
+            }
+        }.run();
+    }
+
+    public void verifyCallbackError() {
+        resetCounts();
+        CountingRecordingSession session = CountingTvInputService.sRecordingSession;
+        assertNotNull(session);
+        session.notifyError(TvInputManager.RECORDING_ERROR_UNKNOWN);
+        new PollingCheck(TIME_OUT) {
+            @Override
+            protected boolean check() {
+                return mRecordingCallback.mErrorCount > 0;
+            }
+        }.run();
+    }
+
+    public void verifyCallbackRecordingStarted() {
+        resetCounts();
+        CountingRecordingSession session = CountingTvInputService.sRecordingSession;
+        assertNotNull(session);
+        session.notifyRecordingStarted();
+        new PollingCheck(TIME_OUT) {
+            @Override
+            protected boolean check() {
+                return mRecordingCallback.mRecordingStartedCount > 0;
+            }
+        }.run();
+    }
+
+    public void verifyCallbackRecordingStopped() {
+        resetCounts();
+        CountingRecordingSession session = CountingTvInputService.sRecordingSession;
+        assertNotNull(session);
+        Uri fakeChannelUri = TvContract.buildChannelUri(0);
+        session.notifyRecordingStopped(fakeChannelUri);
+        new PollingCheck(TIME_OUT) {
+            @Override
+            protected boolean check() {
+                return mRecordingCallback.mRecordingStoppedCount > 0;
+            }
+        }.run();
+    }
+
     public void verifyCommandTune() {
+        resetCounts();
         Uri fakeChannelUri = TvContract.buildChannelUri(0);
         mTvView.tune(mStubInfo.getId(), fakeChannelUri);
         mInstrumentation.waitForIdleSync();
@@ -596,12 +712,17 @@
         if (CountingTvInputService.sSession != null) {
             CountingTvInputService.sSession.resetCounts();
         }
+        if (CountingTvInputService.sRecordingSession!= null) {
+            CountingTvInputService.sRecordingSession.resetCounts();
+        }
         mCallback.resetCounts();
         mTimeShiftPositionCallback.resetCounts();
+        mRecordingCallback.resetCounts();
     }
 
     public static class CountingTvInputService extends StubTvInputService {
         static CountingSession sSession;
+        static CountingRecordingSession sRecordingSession;
 
         @Override
         public Session onCreateSession(String inputId) {
@@ -610,6 +731,12 @@
             return sSession;
         }
 
+        @Override
+        public RecordingSession onCreateRecordingSession(String inputId) {
+            sRecordingSession = new CountingRecordingSession(this);
+            return sRecordingSession;
+        }
+
         public static class CountingSession extends Session {
             public volatile int mTuneCount;
             public volatile int mSetStreamVolumeCount;
@@ -771,5 +898,84 @@
                 mOverlayViewSizeChangedCount++;
             }
         }
+
+        public static class CountingRecordingSession extends RecordingSession {
+            public volatile int mConnectCount;
+            public volatile int mDisconnectCount;
+            public volatile int mStartRecordingCount;
+            public volatile int mStopRecordingCount;
+
+            CountingRecordingSession(Context context) {
+                super(context);
+            }
+
+            public void resetCounts() {
+                mConnectCount = 0;
+                mDisconnectCount = 0;
+                mStartRecordingCount = 0;
+                mStopRecordingCount = 0;
+            }
+
+            @Override
+            public void onConnect(Uri recordedProgramUri) {
+                mConnectCount++;
+            }
+
+            @Override
+            public void onDisconnect() {
+                mDisconnectCount++;
+            }
+
+            @Override
+            public void onStartRecording() {
+                mStartRecordingCount++;
+            }
+
+            @Override
+            public void onStopRecording() {
+                mStopRecordingCount++;
+            }
+        }
+    }
+
+    private static class StubRecordingCallback extends TvRecordingClient.RecordingCallback {
+        private int mConnectedCount;
+        private int mDisconnectedCount;
+        private int mRecordingStartedCount;
+        private int mRecordingStoppedCount;
+        private int mErrorCount;
+
+        @Override
+        public void onConnected() {
+            mConnectedCount++;
+        }
+
+        @Override
+        public void onDisconnected() {
+            mDisconnectedCount++;
+        }
+
+        @Override
+        public void onRecordingStarted() {
+            mRecordingStartedCount++;
+        }
+
+        @Override
+        public void onRecordingStopped(Uri recordedProgramUri) {
+            mRecordingStoppedCount++;
+        }
+
+        @Override
+        public void onError(int error) {
+            mErrorCount++;
+        }
+
+        public void resetCounts() {
+            mConnectedCount = 0;
+            mDisconnectedCount = 0;
+            mRecordingStartedCount = 0;
+            mRecordingStoppedCount = 0;
+            mErrorCount = 0;
+        }
     }
 }
diff --git a/tests/tests/view/Android.mk b/tests/tests/view/Android.mk
index e57e10e..1a290ac 100644
--- a/tests/tests/view/Android.mk
+++ b/tests/tests/view/Android.mk
@@ -24,12 +24,18 @@
 # Tag this module as a cts_v2 test artifact
 LOCAL_COMPATIBILITY_SUITE := cts_v2
 
+LOCAL_MULTILIB := both
+
 LOCAL_JAVA_LIBRARIES := android.test.runner
 
 LOCAL_STATIC_JAVA_LIBRARIES := ctsdeviceutil ctstestrunner mockito-target
 
+LOCAL_JNI_SHARED_LIBRARIES := libctsview_jni libnativehelper_compat_libc++
+
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
 LOCAL_PACKAGE_NAME := CtsViewTestCases
 
 include $(BUILD_CTS_PACKAGE)
+
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/tests/tests/view/jni/Android.mk b/tests/tests/view/jni/Android.mk
new file mode 100644
index 0000000..ac7b844
--- /dev/null
+++ b/tests/tests/view/jni/Android.mk
@@ -0,0 +1,35 @@
+# 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.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := libctsview_jni
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_SRC_FILES := \
+		CtsViewJniOnLoad.cpp \
+		android_view_cts_ChoreographerNativeTest.cpp
+
+LOCAL_C_INCLUDES := $(JNI_H_INCLUDE)
+
+LOCAL_SHARED_LIBRARIES := libandroid libnativehelper_compat_libc++ liblog
+
+LOCAL_CXX_STL := libc++_static
+
+LOCAL_CLANG := true
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/tests/tests/view/jni/CtsViewJniOnLoad.cpp b/tests/tests/view/jni/CtsViewJniOnLoad.cpp
new file mode 100644
index 0000000..2c1e643
--- /dev/null
+++ b/tests/tests/view/jni/CtsViewJniOnLoad.cpp
@@ -0,0 +1,33 @@
+/*
+ * 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.
+ *
+ */
+#include <jni.h>
+
+#include <utils/Log.h>
+#define LOG_TAG "CtsViewJniOnLoad"
+
+extern int register_android_view_cts_ChoreographerNativeTest(JNIEnv* env);
+
+jint JNI_OnLoad(JavaVM *vm, void *) {
+    JNIEnv *env = NULL;
+    if (vm->GetEnv((void**)&env, JNI_VERSION_1_4) != JNI_OK) {
+        return JNI_ERR;
+    }
+    if (register_android_view_cts_ChoreographerNativeTest(env)) {
+        return JNI_ERR;
+    }
+    return JNI_VERSION_1_4;
+}
diff --git a/tests/tests/view/jni/android_view_cts_ChoreographerNativeTest.cpp b/tests/tests/view/jni/android_view_cts_ChoreographerNativeTest.cpp
new file mode 100644
index 0000000..45b82d6
--- /dev/null
+++ b/tests/tests/view/jni/android_view_cts_ChoreographerNativeTest.cpp
@@ -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.
+ *
+ */
+
+#include <android/choreographer.h>
+
+#include <jni.h>
+#include <sys/time.h>
+#include <time.h>
+
+#include <chrono>
+#include <cstdlib>
+#include <cstring>
+#include <mutex>
+#include <thread>
+
+#define LOG_TAG "ChoreographerNative"
+
+#define ASSERT(condition, format, args...) \
+        if (!(condition)) { \
+            fail(env, format, ## args); \
+            return; \
+        }
+
+
+using namespace std::chrono_literals;
+
+static constexpr std::chrono::nanoseconds NOMINAL_VSYNC_PERIOD{16ms};
+static constexpr std::chrono::nanoseconds DELAY_PERIOD{NOMINAL_VSYNC_PERIOD * 5};
+
+static std::mutex gLock;
+struct Callback {
+    int count{0};
+    std::chrono::nanoseconds frameTime{0};
+};
+
+static void frameCallback(long frameTimeNanos, void* data) {
+    std::lock_guard<std::mutex> _l(gLock);
+    Callback* cb = static_cast<Callback*>(data);
+    cb->count++;
+    cb->frameTime = std::chrono::nanoseconds{frameTimeNanos};
+}
+
+static std::chrono::nanoseconds now() {
+    return std::chrono::steady_clock::now().time_since_epoch();
+}
+
+static void fail(JNIEnv* env, const char* format, ...) {
+    va_list args;
+
+    va_start(args, format);
+    char *msg;
+    int rc = vasprintf(&msg, format, args);
+    va_end(args);
+
+    jclass exClass;
+    const char *className = "java/lang/AssertionError";
+    exClass = env->FindClass(className);
+    env->ThrowNew(exClass, msg);
+    free(msg);
+}
+
+static jlong android_view_cts_ChoreographerNativeTest_getChoreographer(JNIEnv*, jclass) {
+    std::lock_guard<std::mutex> _l{gLock};
+    return reinterpret_cast<jlong>(AChoreographer_getInstance());
+}
+
+static jboolean android_view_cts_ChoreographerNativeTest_prepareChoreographerTests(JNIEnv*, jclass,
+        jlong choreographerPtr) {
+    std::lock_guard<std::mutex> _l{gLock};
+    AChoreographer* choreographer = reinterpret_cast<AChoreographer*>(choreographerPtr);
+    return choreographer != nullptr;
+}
+
+static void android_view_cts_ChoreographerNativeTest_testPostCallbackWithoutDelayEventuallyRunsCallback(
+        JNIEnv* env, jclass, jlong choreographerPtr) {
+    AChoreographer* choreographer = reinterpret_cast<AChoreographer*>(choreographerPtr);
+    Callback* cb1 = new Callback();
+    Callback* cb2 = new Callback();
+    auto start = now();
+
+    AChoreographer_postFrameCallback(choreographer, frameCallback, cb1);
+    AChoreographer_postFrameCallback(choreographer, frameCallback, cb2);
+    std::this_thread::sleep_for(NOMINAL_VSYNC_PERIOD * 3);
+    {
+        std::lock_guard<std::mutex> _l{gLock};
+        ASSERT(cb1->count == 1, "Choreographer failed to invoke callback 1");
+        ASSERT(cb1->frameTime - start < NOMINAL_VSYNC_PERIOD * 3,
+                "Callback 1 has incorect frame time on first invokation");
+        ASSERT(cb2->count == 1, "Choreographer failed to invoke callback 2");
+        ASSERT(cb2->frameTime - start < NOMINAL_VSYNC_PERIOD * 3,
+                "Callback 2 has incorect frame time on first invokation");
+        auto delta = cb2->frameTime - cb1->frameTime;
+        ASSERT(delta == delta.zero() || delta > delta.zero() && delta < NOMINAL_VSYNC_PERIOD * 2,
+                "Callback 1 and 2 have frame times too large of a delta in frame times");
+    }
+
+    AChoreographer_postFrameCallback(choreographer, frameCallback, cb1);
+    start = now();
+    std::this_thread::sleep_for(NOMINAL_VSYNC_PERIOD * 3);
+    {
+        std::lock_guard<std::mutex> _l{gLock};
+        ASSERT(cb1->count == 2, "Choreographer failed to invoke callback 1 a second time");
+        ASSERT(cb1->frameTime - start < NOMINAL_VSYNC_PERIOD * 3,
+                "Callback 1 has incorect frame time on second invokation");
+        ASSERT(cb2->count == 1, "Choreographer invoked callback 2 when not posted");
+    }
+}
+
+static void android_view_cts_ChoreographerNativeTest_testPostCallbackWithDelayEventuallyRunsCallback(
+        JNIEnv* env, jclass, jlong choreographerPtr) {
+    AChoreographer* choreographer = reinterpret_cast<AChoreographer*>(choreographerPtr);
+    Callback* cb1 = new Callback();
+    auto start = now();
+
+    auto delay = std::chrono::duration_cast<std::chrono::milliseconds>(DELAY_PERIOD).count();
+    AChoreographer_postFrameCallbackDelayed(choreographer, frameCallback, cb1, delay);
+    std::this_thread::sleep_for(NOMINAL_VSYNC_PERIOD * 3);
+    {
+        std::lock_guard<std::mutex> _l{gLock};
+        ASSERT(cb1->count == 0,
+                "Choreographer failed to delay callback for a sufficient period of time");
+    }
+    std::this_thread::sleep_for(DELAY_PERIOD);
+    {
+        std::lock_guard<std::mutex> _l{gLock};
+        ASSERT(cb1->count == 1, "Choreographer failed to invoke delayed callback");
+        ASSERT(cb1->frameTime - start < DELAY_PERIOD + NOMINAL_VSYNC_PERIOD * 3,
+                "Frametime on callback is incorrect")
+    }
+}
+
+static JNINativeMethod gMethods[] = {
+    {  "nativeGetChoreographer", "()J",
+            (void *) android_view_cts_ChoreographerNativeTest_getChoreographer},
+    {  "nativePrepareChoreographerTests", "(J)Z",
+            (void *) android_view_cts_ChoreographerNativeTest_prepareChoreographerTests},
+    {  "nativeTestPostCallbackWithoutDelayEventuallyRunsCallbacks", "(J)V",
+            (void *) android_view_cts_ChoreographerNativeTest_testPostCallbackWithoutDelayEventuallyRunsCallback},
+    {  "nativeTestPostCallbackWithDelayEventuallyRunsCallbacks", "(J)V",
+            (void *) android_view_cts_ChoreographerNativeTest_testPostCallbackWithDelayEventuallyRunsCallback},
+};
+
+int register_android_view_cts_ChoreographerNativeTest(JNIEnv* env)
+{
+    jclass clazz = env->FindClass("android/view/cts/ChoreographerNativeTest");
+    return env->RegisterNatives(clazz, gMethods,
+            sizeof(gMethods) / sizeof(JNINativeMethod));
+}
diff --git a/tests/tests/view/src/android/view/cts/ChoreographerNativeTest.java b/tests/tests/view/src/android/view/cts/ChoreographerNativeTest.java
new file mode 100644
index 0000000..38f351c
--- /dev/null
+++ b/tests/tests/view/src/android/view/cts/ChoreographerNativeTest.java
@@ -0,0 +1,56 @@
+/*
+ * 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.cts;
+
+import android.test.InstrumentationTestCase;
+import android.view.Choreographer;
+
+public class ChoreographerNativeTest extends InstrumentationTestCase {
+    private long mChoreographerPtr;
+    private Choreographer mChoreographer;
+
+    private static native long nativeGetChoreographer();
+    private static native boolean nativePrepareChoreographerTests(long ptr);
+    private static native void nativeTestPostCallbackWithoutDelayEventuallyRunsCallbacks(long ptr);
+    private static native void nativeTestPostCallbackWithDelayEventuallyRunsCallbacks(long ptr);
+
+    static {
+        System.loadLibrary("ctsview_jni");
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        getInstrumentation().runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                mChoreographerPtr = nativeGetChoreographer();
+            }
+        });
+        if (!nativePrepareChoreographerTests(mChoreographerPtr)) {
+            fail("Failed to setup choreographer tests");
+        }
+    }
+
+    public void testPostCallbackWithoutDelayEventuallyRunsCallbacks() {
+        nativeTestPostCallbackWithoutDelayEventuallyRunsCallbacks(mChoreographerPtr);
+    }
+
+    public void testPostCallbackWithDelayEventuallyRunsCallbacks() {
+        nativeTestPostCallbackWithDelayEventuallyRunsCallbacks(mChoreographerPtr);
+    }
+}
diff --git a/tests/tests/view/src/android/view/cts/ViewGroupTest.java b/tests/tests/view/src/android/view/cts/ViewGroupTest.java
index 49979ca..10e743a 100644
--- a/tests/tests/view/src/android/view/cts/ViewGroupTest.java
+++ b/tests/tests/view/src/android/view/cts/ViewGroupTest.java
@@ -27,6 +27,7 @@
 import android.graphics.Rect;
 import android.graphics.Region;
 import android.graphics.drawable.BitmapDrawable;
+import android.os.Build;
 import android.os.Parcelable;
 import android.os.SystemClock;
 import android.test.InstrumentationTestCase;
@@ -1010,6 +1011,18 @@
         assertEquals(2, rect.top);
         assertEquals(1, rect.left);
         assertEquals(1, rect.right);
+
+        if (Build.VERSION.SDK_INT > Build.VERSION_CODES.M || Build.VERSION.CODENAME.equals("N")) {
+            textView.setTranslationX(2);
+            textView.setTranslationY(1);
+
+            rect.setEmpty();
+            vg.offsetDescendantRectToMyCoords(textView, rect);
+            assertEquals(3, rect.bottom);
+            assertEquals(3, rect.top);
+            assertEquals(3, rect.left);
+            assertEquals(3, rect.right);
+        }
     }
 
     public void testOffsetRectIntoDescendantCoords() {
@@ -1033,6 +1046,18 @@
         assertEquals(4, rect.top);
         assertEquals(4, rect.left);
         assertEquals(6, rect.right);
+
+        if (Build.VERSION.SDK_INT > Build.VERSION_CODES.M || Build.VERSION.CODENAME.equals("N")) {
+            textView.setTranslationX(2);
+            textView.setTranslationY(1);
+
+            rect.set(5, 6, 7, 8);
+            vg.offsetRectIntoDescendantCoords(textView, rect);
+            assertEquals(5, rect.bottom);
+            assertEquals(3, rect.top);
+            assertEquals(2, rect.left);
+            assertEquals(4, rect.right);
+        }
     }
 
     public void testOnAnimationEnd() {
diff --git a/tests/tests/view/src/android/view/cts/ViewTest.java b/tests/tests/view/src/android/view/cts/ViewTest.java
index 18868e4..c3fe030 100644
--- a/tests/tests/view/src/android/view/cts/ViewTest.java
+++ b/tests/tests/view/src/android/view/cts/ViewTest.java
@@ -1435,6 +1435,8 @@
 
     public void testPerformLongClickXY_WithListener() {
         OnLongClickListener listener = mock(OnLongClickListener.class);
+        when(listener.onLongClick(any(View.class))).thenReturn(true);
+
         MockViewParent parent = new MockViewParent(mActivity);
         MockView view = new MockView(mActivity);
 
@@ -2843,15 +2845,7 @@
         Rect rectangle = new Rect();
         MockViewGroupParent parent = new MockViewGroupParent(mActivity);
 
-        final Rect requestedRect = new Rect();
-        MockViewGroupParent grandparent = new MockViewGroupParent(mActivity) {
-            @Override
-            public boolean requestChildRectangleOnScreen(View child, Rect rectangle,
-                    boolean immediate) {
-                requestedRect.set(rectangle);
-                return super.requestChildRectangleOnScreen(child, rectangle, immediate);
-            }
-        };
+        MockViewGroupParent grandparent = new MockViewGroupParent(mActivity);
 
         // parent is null
         assertFalse(view.requestRectangleOnScreen(rectangle));
@@ -2873,10 +2867,12 @@
         assertTrue(parent.hasRequestChildRectangleOnScreen());
         assertTrue(grandparent.hasRequestChildRectangleOnScreen());
 
-        assertEquals(-1, requestedRect.left);
-        assertEquals(-2, requestedRect.top);
-        assertEquals(-1, requestedRect.right);
-        assertEquals(-2, requestedRect.bottom);
+        // it is grand parent's responsibility to check parent's scroll offset
+        final Rect requestedRect = grandparent.getLastRequestedChildRectOnScreen();
+        assertEquals(0, requestedRect.left);
+        assertEquals(0, requestedRect.top);
+        assertEquals(0, requestedRect.right);
+        assertEquals(0, requestedRect.bottom);
 
         try {
             view.requestRectangleOnScreen(null);
@@ -2885,6 +2881,70 @@
         }
     }
 
+    public void testRequestRectangleOnScreen3() {
+        requestRectangleOnScreenTest(false);
+    }
+
+    public void testRequestRectangleOnScreen4() {
+        requestRectangleOnScreenTest(true);
+    }
+
+    public void testRequestRectangleOnScreen5() {
+        MockView child = new MockView(mActivity);
+
+        MockViewGroupParent parent = new MockViewGroupParent(mActivity);
+        MockViewGroupParent grandParent = new MockViewGroupParent(mActivity);
+        parent.addView(child);
+        grandParent.addView(parent);
+
+        child.layout(5, 6, 7, 9);
+        child.requestRectangleOnScreen(new Rect(10, 10, 12, 13));
+        assertEquals(new Rect(10, 10, 12, 13), parent.getLastRequestedChildRectOnScreen());
+        assertEquals(new Rect(15, 16, 17, 19), grandParent.getLastRequestedChildRectOnScreen());
+
+        child.scrollBy(1, 2);
+        child.requestRectangleOnScreen(new Rect(10, 10, 12, 13));
+        assertEquals(new Rect(10, 10, 12, 13), parent.getLastRequestedChildRectOnScreen());
+        assertEquals(new Rect(14, 14, 16, 17), grandParent.getLastRequestedChildRectOnScreen());
+    }
+
+    private void requestRectangleOnScreenTest(boolean scrollParent) {
+        MockView child = new MockView(mActivity);
+
+        MockViewGroupParent parent = new MockViewGroupParent(mActivity);
+        MockViewGroupParent grandParent = new MockViewGroupParent(mActivity);
+        parent.addView(child);
+        grandParent.addView(parent);
+
+        child.requestRectangleOnScreen(new Rect(10, 10, 12, 13));
+        assertEquals(new Rect(10, 10, 12, 13), parent.getLastRequestedChildRectOnScreen());
+        assertEquals(new Rect(10, 10, 12, 13), grandParent.getLastRequestedChildRectOnScreen());
+
+        child.scrollBy(1, 2);
+        if (scrollParent) {
+            // should not affect anything
+            parent.scrollBy(25, 30);
+            parent.layout(3, 5, 7, 9);
+        }
+        child.requestRectangleOnScreen(new Rect(10, 10, 12, 13));
+        assertEquals(new Rect(10, 10, 12, 13), parent.getLastRequestedChildRectOnScreen());
+        assertEquals(new Rect(9, 8, 11, 11), grandParent.getLastRequestedChildRectOnScreen());
+    }
+
+    public void testRequestRectangleOnScreenWithScale() {
+        // scale should not affect the rectangle
+        MockView child = new MockView(mActivity);
+        child.setScaleX(2);
+        child.setScaleX(3);
+        MockViewGroupParent parent = new MockViewGroupParent(mActivity);
+        MockViewGroupParent grandParent = new MockViewGroupParent(mActivity);
+        parent.addView(child);
+        grandParent.addView(parent);
+        child.requestRectangleOnScreen(new Rect(10, 10, 12, 13));
+        assertEquals(new Rect(10, 10, 12, 13), parent.getLastRequestedChildRectOnScreen());
+        assertEquals(new Rect(10, 10, 12, 13), grandParent.getLastRequestedChildRectOnScreen());
+    }
+
     /**
      * For the duration of the tap timeout we are in a 'prepressed' state
      * to differentiate between taps and touch scrolls.
@@ -4236,6 +4296,8 @@
 
     private static class MockViewGroupParent extends ViewGroup implements ViewParent {
         private boolean mHasRequestChildRectangleOnScreen = false;
+        private Rect mLastRequestedChildRectOnScreen = new Rect(
+                Integer.MIN_VALUE, Integer.MIN_VALUE, Integer.MIN_VALUE, Integer.MIN_VALUE);
 
         public MockViewGroupParent(Context context) {
             super(context);
@@ -4250,9 +4312,14 @@
         public boolean requestChildRectangleOnScreen(View child,
                 Rect rectangle, boolean immediate) {
             mHasRequestChildRectangleOnScreen = true;
+            mLastRequestedChildRectOnScreen.set(rectangle);
             return super.requestChildRectangleOnScreen(child, rectangle, immediate);
         }
 
+        public Rect getLastRequestedChildRectOnScreen() {
+            return mLastRequestedChildRectOnScreen;
+        }
+
         public boolean hasRequestChildRectangleOnScreen() {
             return mHasRequestChildRectangleOnScreen;
         }
diff --git a/tests/tests/view/src/android/view/cts/WindowTest.java b/tests/tests/view/src/android/view/cts/WindowTest.java
index a897af4..0e67c3e 100644
--- a/tests/tests/view/src/android/view/cts/WindowTest.java
+++ b/tests/tests/view/src/android/view/cts/WindowTest.java
@@ -1046,7 +1046,7 @@
         }
 
         @Override
-        public void onMultiWindowModeChanged() {
+        public void onMultiWindowChanged() {
         }
 
         @Override
diff --git a/tests/tests/voiceinteraction/AndroidManifest.xml b/tests/tests/voiceinteraction/AndroidManifest.xml
index 4c6989f..562f030 100644
--- a/tests/tests/voiceinteraction/AndroidManifest.xml
+++ b/tests/tests/voiceinteraction/AndroidManifest.xml
@@ -32,6 +32,13 @@
               <category android:name="android.intent.category.DEFAULT" />
           </intent-filter>
       </activity>
+      <activity android:name="TestLocalInteractionActivity"
+                android:label="Local Interaction Activity">
+          <intent-filter>
+              <action android:name="android.intent.action.TEST_LOCAL_INTERACTION_ACTIVITY" />
+              <category android:name="android.intent.category.DEFAULT" />
+          </intent-filter>
+      </activity>
     </application>
 
     <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
diff --git a/tests/tests/voiceinteraction/common/src/android/voiceinteraction/common/Utils.java b/tests/tests/voiceinteraction/common/src/android/voiceinteraction/common/Utils.java
index ff3bcfd..fc8916e 100644
--- a/tests/tests/voiceinteraction/common/src/android/voiceinteraction/common/Utils.java
+++ b/tests/tests/voiceinteraction/common/src/android/voiceinteraction/common/Utils.java
@@ -61,6 +61,9 @@
     public static final String COMMANDREQUEST_CANCEL_SUCCESS = "commandrequest cancel ok";
     public static final String TEST_ERROR = "Error In Test:";
 
+    public static final String PRIVATE_OPTIONS_KEY = "private_key";
+    public static final String PRIVATE_OPTIONS_VALUE = "private_value";
+
     public static final String toBundleString(Bundle bundle) {
         if (bundle == null) {
             return "*** Bundle is null ****";
diff --git a/tests/tests/voiceinteraction/service/res/xml/interaction_service.xml b/tests/tests/voiceinteraction/service/res/xml/interaction_service.xml
index dd7e7b8..fa256ef 100644
--- a/tests/tests/voiceinteraction/service/res/xml/interaction_service.xml
+++ b/tests/tests/voiceinteraction/service/res/xml/interaction_service.xml
@@ -18,4 +18,5 @@
     android:sessionService="android.voiceinteraction.service.MainInteractionSessionService"
     android:recognitionService="android.voiceinteraction.service.MainRecognitionService"
     android:settingsActivity="android.voiceinteraction.service.SettingsActivity"
-    android:supportsAssist="false" />
+    android:supportsAssist="false"
+    android:supportsLocalInteraction="true" />
diff --git a/tests/tests/voiceinteraction/service/src/android/voiceinteraction/service/MainInteractionSession.java b/tests/tests/voiceinteraction/service/src/android/voiceinteraction/service/MainInteractionSession.java
index eeb4047..788224e 100644
--- a/tests/tests/voiceinteraction/service/src/android/voiceinteraction/service/MainInteractionSession.java
+++ b/tests/tests/voiceinteraction/service/src/android/voiceinteraction/service/MainInteractionSession.java
@@ -60,7 +60,16 @@
     public void onShow(Bundle args, int showFlags) {
         super.onShow(args, showFlags);
         mStartIntent = args.getParcelable("intent");
-        startVoiceActivity(mStartIntent);
+        if (mStartIntent != null) {
+            startVoiceActivity(mStartIntent);
+        } else if ((showFlags & SHOW_SOURCE_ACTIVITY) == SHOW_SOURCE_ACTIVITY) {
+            // Verify args
+            if (args == null
+                    || !Utils.PRIVATE_OPTIONS_VALUE.equals(
+                            args.getString(Utils.PRIVATE_OPTIONS_KEY))) {
+                throw new IllegalArgumentException("Incorrect arguments for SHOW_SOURCE_ACTIVITY");
+            }
+        }
     }
 
     void assertPromptFromTestApp(CharSequence prompt, Bundle extras) {
diff --git a/tests/tests/voiceinteraction/src/android/voiceinteraction/cts/LocalVoiceInteractionTest.java b/tests/tests/voiceinteraction/src/android/voiceinteraction/cts/LocalVoiceInteractionTest.java
new file mode 100644
index 0000000..55ebebe
--- /dev/null
+++ b/tests/tests/voiceinteraction/src/android/voiceinteraction/cts/LocalVoiceInteractionTest.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.voiceinteraction.cts;
+
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Bundle;
+import android.test.ActivityInstrumentationTestCase2;
+import android.util.Log;
+
+import junit.framework.Assert;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import android.voiceinteraction.common.Utils;
+
+public class LocalVoiceInteractionTest
+        extends ActivityInstrumentationTestCase2<TestLocalInteractionActivity> {
+
+    private static final int TIMEOUT_MS = 20 * 1000;
+
+    private TestLocalInteractionActivity mTestActivity;
+    private Context mContext;
+    private final CountDownLatch mLatchStart = new CountDownLatch(1);
+    private final CountDownLatch mLatchStop = new CountDownLatch(1);
+
+    public LocalVoiceInteractionTest() {
+        super(TestLocalInteractionActivity.class);
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        startTestActivity();
+        mContext = getInstrumentation().getTargetContext();
+    }
+
+    private void startTestActivity() throws Exception {
+        Intent intent = new Intent();
+        intent.setAction("android.intent.action.TEST_LOCAL_INTERACTION_ACTIVITY");
+        intent.setComponent(new ComponentName(getInstrumentation().getContext(),
+                TestLocalInteractionActivity.class));
+        setActivityIntent(intent);
+        mTestActivity = getActivity();
+    }
+
+    public void testLifecycle() throws Exception {
+        assertTrue("Doesn't support LocalVoiceInteraction",
+                mTestActivity.isLocalVoiceInteractionSupported());
+        mTestActivity.startLocalInteraction(mLatchStart);
+        if (!mLatchStart.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
+            fail("Failed to start voice interaction in " + TIMEOUT_MS + "msec");
+            return;
+        }
+        mTestActivity.stopLocalInteraction(mLatchStop);
+        if (!mLatchStop.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
+            fail("Failed to stop voice interaction in " + TIMEOUT_MS + "msec");
+            return;
+        }
+        mTestActivity.finish();
+    }
+}
diff --git a/tests/tests/voiceinteraction/src/android/voiceinteraction/cts/TestLocalInteractionActivity.java b/tests/tests/voiceinteraction/src/android/voiceinteraction/cts/TestLocalInteractionActivity.java
new file mode 100644
index 0000000..4999fbe
--- /dev/null
+++ b/tests/tests/voiceinteraction/src/android/voiceinteraction/cts/TestLocalInteractionActivity.java
@@ -0,0 +1,68 @@
+/*
+ * 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.voiceinteraction.cts;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.content.ComponentName;
+import android.content.Context;
+import android.os.Bundle;
+import android.util.Log;
+import android.voiceinteraction.common.Utils;
+
+import java.util.concurrent.CountDownLatch;
+
+public class TestLocalInteractionActivity extends Activity {
+    static final String TAG = "TestLocalInteractionActivity";
+
+    private CountDownLatch mStarted;
+    private CountDownLatch mStopped;
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        Log.i(TAG, " in onCreate");
+    }
+
+    void startLocalInteraction(CountDownLatch counter) {
+        Bundle privateOptions = new Bundle();
+        privateOptions.putString(Utils.PRIVATE_OPTIONS_KEY, Utils.PRIVATE_OPTIONS_VALUE);
+        mStarted = counter;
+        startLocalVoiceInteraction(privateOptions);
+    }
+
+    @Override
+    public void onLocalVoiceInteractionStarted() {
+        Log.i(TAG, " in onLocalVoiceInteractionStarted");
+        if (mStarted != null) {
+            mStarted.countDown();
+        }
+    }
+
+    void stopLocalInteraction(CountDownLatch counter) {
+        mStopped = counter;
+        stopLocalVoiceInteraction();
+    }
+
+    @Override
+    public void onLocalVoiceInteractionStopped() {
+        Log.i(TAG, " in onLocalVoiceInteractionStarted");
+        if (mStopped != null) {
+            mStopped.countDown();
+        }
+    }
+}
diff --git a/tests/tests/voiceinteraction/src/android/voiceinteraction/cts/VoiceInteractionTest.java b/tests/tests/voiceinteraction/src/android/voiceinteraction/cts/VoiceInteractionTest.java
index 6b47eb4..3f89ad0 100644
--- a/tests/tests/voiceinteraction/src/android/voiceinteraction/cts/VoiceInteractionTest.java
+++ b/tests/tests/voiceinteraction/src/android/voiceinteraction/cts/VoiceInteractionTest.java
@@ -43,6 +43,8 @@
     private TestResultsReceiver mReceiver;
     private Bundle mResults;
     private final CountDownLatch mLatch = new CountDownLatch(1);
+    protected boolean mHasFeature;
+    protected static final String FEATURE_VOICE_RECOGNIZERS = "android.software.voice_recognizers";
 
     public VoiceInteractionTest() {
         super(TestStartActivity.class);
@@ -53,13 +55,16 @@
         super.setUp();
         startTestActivity();
         mContext = getInstrumentation().getTargetContext();
-        mReceiver = new TestResultsReceiver();
-        mContext.registerReceiver(mReceiver, new IntentFilter(Utils.BROADCAST_INTENT));
+        mHasFeature = mContext.getPackageManager().hasSystemFeature(FEATURE_VOICE_RECOGNIZERS);
+        if (mHasFeature) {
+            mReceiver = new TestResultsReceiver();
+            mContext.registerReceiver(mReceiver, new IntentFilter(Utils.BROADCAST_INTENT));
+        }
     }
 
     @Override
     protected void tearDown() throws Exception {
-        if (mReceiver != null) {
+        if (mHasFeature && mReceiver != null) {
             try {
                 mContext.unregisterReceiver(mReceiver);
             } catch (IllegalArgumentException e) {
@@ -82,6 +87,10 @@
     }
 
     public void testAll() throws Exception {
+        if (!mHasFeature) {
+            Log.i(TAG, "The device doesn't support feature: " + FEATURE_VOICE_RECOGNIZERS);
+            return;
+        }
         if (!mLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
             fail("Failed to receive broadcast in " + TIMEOUT_MS + "msec");
             return;
diff --git a/tests/tests/voicesettings/src/android/voicesettings/cts/AirplaneModeTest.java b/tests/tests/voicesettings/src/android/voicesettings/cts/AirplaneModeTest.java
index 9ce743e..adce051 100644
--- a/tests/tests/voicesettings/src/android/voicesettings/cts/AirplaneModeTest.java
+++ b/tests/tests/voicesettings/src/android/voicesettings/cts/AirplaneModeTest.java
@@ -35,6 +35,10 @@
     }
 
     public void testAll() throws Exception {
+        if (!mHasFeature) {
+            Log.i(TAG, "The device doesn't support feature: " + FEATURE_VOICE_RECOGNIZERS);
+            return;
+        }
         if (!isIntentSupported(ACTION_VOICE_CONTROL_AIRPLANE_MODE)) {
             Log.e(TAG, "Voice intent for Airplane Mode NOT supported. existing the test");
             return;
diff --git a/tests/tests/voicesettings/src/android/voicesettings/cts/BatterySaverModeTest.java b/tests/tests/voicesettings/src/android/voicesettings/cts/BatterySaverModeTest.java
index 983e27b..3a778c0 100644
--- a/tests/tests/voicesettings/src/android/voicesettings/cts/BatterySaverModeTest.java
+++ b/tests/tests/voicesettings/src/android/voicesettings/cts/BatterySaverModeTest.java
@@ -32,6 +32,10 @@
     }
 
     public void testAll() throws Exception {
+        if (!mHasFeature) {
+            Log.i(TAG, "The device doesn't support feature: " + FEATURE_VOICE_RECOGNIZERS);
+            return;
+        }
         if (!isIntentSupported(ACTION_VOICE_CONTROL_BATTERY_SAVER_MODE)) {
             Log.e(TAG, "Voice intent for Battery Saver Mode NOT supported. existing the test");
             return;
diff --git a/tests/tests/voicesettings/src/android/voicesettings/cts/VoiceSettingsTestBase.java b/tests/tests/voicesettings/src/android/voicesettings/cts/VoiceSettingsTestBase.java
index 529c160..fe30d90 100644
--- a/tests/tests/voicesettings/src/android/voicesettings/cts/VoiceSettingsTestBase.java
+++ b/tests/tests/voicesettings/src/android/voicesettings/cts/VoiceSettingsTestBase.java
@@ -41,6 +41,8 @@
     private ActivityDoneReceiver mActivityDoneReceiver = null;
     private TestStartActivity mActivity;
     private Utils.TestcaseType mTestCaseType;
+    protected boolean mHasFeature;
+    protected static final String FEATURE_VOICE_RECOGNIZERS = "android.software.voice_recognizers";
 
     public VoiceSettingsTestBase() {
         super(TestStartActivity.class);
@@ -50,11 +52,12 @@
     protected void setUp() throws Exception {
         super.setUp();
         mContext = getInstrumentation().getTargetContext();
+        mHasFeature = mContext.getPackageManager().hasSystemFeature(FEATURE_VOICE_RECOGNIZERS);
     }
 
     @Override
     protected void tearDown() throws Exception {
-        if (mActivityDoneReceiver != null) {
+        if (mHasFeature && mActivityDoneReceiver != null) {
             try {
                 mContext.unregisterReceiver(mActivityDoneReceiver);
             } catch (IllegalArgumentException e) {
diff --git a/tests/tests/voicesettings/src/android/voicesettings/cts/ZenModeTest.java b/tests/tests/voicesettings/src/android/voicesettings/cts/ZenModeTest.java
index 420da8f..70ff437 100644
--- a/tests/tests/voicesettings/src/android/voicesettings/cts/ZenModeTest.java
+++ b/tests/tests/voicesettings/src/android/voicesettings/cts/ZenModeTest.java
@@ -40,6 +40,10 @@
     }
 
     public void testAll() throws Exception {
+        if (!mHasFeature) {
+            Log.i(TAG, "The device doesn't support feature: " + FEATURE_VOICE_RECOGNIZERS);
+            return;
+        }
         if (!isIntentSupported(ACTION_VOICE_CONTROL_DO_NOT_DISTURB_MODE)) {
             Log.e(TAG, "Voice intent for Zen Mode NOT supported. existing the test");
             return;
diff --git a/tests/tests/webkit/src/android/webkit/cts/ServiceWorkerClientTest.java b/tests/tests/webkit/src/android/webkit/cts/ServiceWorkerClientTest.java
new file mode 100644
index 0000000..a77d648
--- /dev/null
+++ b/tests/tests/webkit/src/android/webkit/cts/ServiceWorkerClientTest.java
@@ -0,0 +1,181 @@
+/*
+ * 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.webkit.cts;
+
+import android.cts.util.PollingCheck;
+import android.test.ActivityInstrumentationTestCase2;
+
+import android.webkit.JavascriptInterface;
+import android.webkit.ServiceWorkerController;
+import android.webkit.ServiceWorkerClient;
+import android.webkit.WebResourceResponse;
+import android.webkit.WebResourceRequest;
+import android.webkit.WebView;
+import android.webkit.WebViewClient;
+import android.webkit.cts.WebViewOnUiThread.WaitForLoadedClient;
+
+import java.io.ByteArrayInputStream;
+import java.io.UnsupportedEncodingException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.Callable;
+
+
+public class ServiceWorkerClientTest extends ActivityInstrumentationTestCase2<WebViewCtsActivity> {
+
+    // The BASE_URL does not matter since the tests will intercept the load, but it should be https
+    // for the Service Worker registration to succeed.
+    private static final String BASE_URL = "https://www.example.com/";
+    private static final String INDEX_URL = BASE_URL + "index.html";
+    private static final String SW_URL = BASE_URL + "sw.js";
+    private static final String FETCH_URL = BASE_URL + "fetch.html";
+
+    private static final String JS_INTERFACE_NAME = "Android";
+    private static final int POLLING_TIMEOUT = 60 * 1000;
+
+    // static HTML page always injected instead of the url loaded.
+    private static final String INDEX_RAW_HTML =
+            "<!DOCTYPE html>\n"
+            + "<html>\n"
+            + "  <body>\n"
+            + "    <script>\n"
+            + "      navigator.serviceWorker.register('sw.js').then(function(reg) {\n"
+            + "         " + JS_INTERFACE_NAME + ".registrationSuccess();\n"
+            + "      }).catch(function(err) { \n"
+            + "         console.error(err);\n"
+            + "      });\n"
+            + "    </script>\n"
+            + "  </body>\n"
+            + "</html>\n";
+    private static final String SW_RAW_HTML = "fetch('fetch.html');";
+
+    private JavascriptStatusReceiver mJavascriptStatusReceiver;
+    private WebViewOnUiThread mOnUiThread;
+
+    public ServiceWorkerClientTest() throws Exception {
+        super("android.webkit.cts", WebViewCtsActivity.class);
+    }
+
+    // Both this test and WebViewOnUiThread need to override some of the methods on WebViewClient,
+    // so this test subclasses the WebViewClient from WebViewOnUiThread.
+    private static class InterceptClient extends WaitForLoadedClient {
+
+        public InterceptClient(WebViewOnUiThread webViewOnUiThread) throws Exception {
+            super(webViewOnUiThread);
+        }
+
+        @Override
+        public WebResourceResponse shouldInterceptRequest(WebView view,
+                WebResourceRequest request) {
+            // Only return content for INDEX_URL, deny all other requests.
+            try {
+                if (request.getUrl().toString().equals(INDEX_URL)) {
+                    return new WebResourceResponse("text/html", "utf-8",
+                        new ByteArrayInputStream(INDEX_RAW_HTML.getBytes("UTF-8")));
+                }
+            } catch(java.io.UnsupportedEncodingException e) {}
+            return new WebResourceResponse("text/html", "UTF-8", null);
+        }
+    }
+
+    public static class InterceptServiceWorkerClient extends ServiceWorkerClient {
+        private List<WebResourceRequest> mInterceptedRequests = new ArrayList<WebResourceRequest>();
+
+        @Override
+        public WebResourceResponse shouldInterceptRequest(WebResourceRequest request) {
+            // Records intercepted requests and only return content for SW_URL.
+            mInterceptedRequests.add(request);
+            try {
+                if (request.getUrl().toString().equals(SW_URL)) {
+                    return new WebResourceResponse("application/javascript", "utf-8",
+                        new ByteArrayInputStream(SW_RAW_HTML.getBytes("UTF-8")));
+                }
+            } catch(java.io.UnsupportedEncodingException e) {}
+            return new WebResourceResponse("text/html", "UTF-8", null);
+        }
+
+        List<WebResourceRequest> getInterceptedRequests() {
+            return mInterceptedRequests;
+        }
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        WebView webview = getActivity().getWebView();
+        if (webview == null) return;
+        mOnUiThread = new WebViewOnUiThread(this, webview);
+        mOnUiThread.getSettings().setJavaScriptEnabled(true);
+
+        mJavascriptStatusReceiver = new JavascriptStatusReceiver();
+        mOnUiThread.addJavascriptInterface(mJavascriptStatusReceiver, JS_INTERFACE_NAME);
+        mOnUiThread.setWebViewClient(new InterceptClient(mOnUiThread));
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        if (mOnUiThread != null) {
+            mOnUiThread.cleanUp();
+        }
+        super.tearDown();
+    }
+
+    // Test correct invocation of shouldInterceptRequest for Service Workers.
+    public void testServiceWorkerClientInterceptCallback() throws Exception {
+        final InterceptServiceWorkerClient mInterceptServiceWorkerClient =
+                new InterceptServiceWorkerClient();
+        ServiceWorkerController swController = ServiceWorkerController.getInstance();
+        swController.setServiceWorkerClient(mInterceptServiceWorkerClient);
+
+        mOnUiThread.loadUrlAndWaitForCompletion(INDEX_URL);
+
+        Callable<Boolean> registrationSuccess = new Callable<Boolean>() {
+            @Override
+            public Boolean call() {
+                return mJavascriptStatusReceiver.mRegistrationSuccess;
+            }
+        };
+        PollingCheck.check("JS could not register Service Worker", POLLING_TIMEOUT,
+                registrationSuccess);
+
+        Callable<Boolean> receivedRequest = new Callable<Boolean>() {
+            @Override
+            public Boolean call() {
+                return mInterceptServiceWorkerClient.getInterceptedRequests().size() >= 2;
+            }
+        };
+        PollingCheck.check("Service Worker intercept callbacks not invoked", POLLING_TIMEOUT,
+                receivedRequest);
+
+        List<WebResourceRequest> requests = mInterceptServiceWorkerClient.getInterceptedRequests();
+        assertEquals(2, requests.size());
+        assertEquals(SW_URL, requests.get(0).getUrl().toString());
+        assertEquals(FETCH_URL, requests.get(1).getUrl().toString());
+    }
+
+    // Object added to the page via AddJavascriptInterface() that is used by the test Javascript to
+    // notify back to Java if the Service Worker registration was successful.
+    public final static class JavascriptStatusReceiver {
+        public volatile boolean mRegistrationSuccess = false;
+
+        @JavascriptInterface
+        public void registrationSuccess() {
+            mRegistrationSuccess = true;
+        }
+    }
+}
+
diff --git a/tests/tests/webkit/src/android/webkit/cts/WebViewClientTest.java b/tests/tests/webkit/src/android/webkit/cts/WebViewClientTest.java
index 1267ccb..ff30a2f 100644
--- a/tests/tests/webkit/src/android/webkit/cts/WebViewClientTest.java
+++ b/tests/tests/webkit/src/android/webkit/cts/WebViewClientTest.java
@@ -105,6 +105,10 @@
         mOnUiThread.loadDataAndWaitForCompletion(data, "text/html", null);
         clickOnLinkUsingJs("link", mOnUiThread);
         assertEquals(TEST_URL, webViewClient.getLastShouldOverrideUrl());
+        assertNotNull(webViewClient.getLastShouldOverrideResourceRequest());
+        assertTrue(webViewClient.getLastShouldOverrideResourceRequest().isForMainFrame());
+        assertFalse(webViewClient.getLastShouldOverrideResourceRequest().isRedirect());
+        assertFalse(webViewClient.getLastShouldOverrideResourceRequest().hasGesture());
     }
 
     // Verify shouldoverrideurlloading called on webview called via onCreateWindow
@@ -586,6 +590,7 @@
         private boolean mOnScaleChangedCalled;
         private int mShouldOverrideUrlLoadingCallCount;
         private String mLastShouldOverrideUrl;
+        private WebResourceRequest mLastShouldOverrideResourceRequest;
 
         public MockWebViewClient() {
             super(mOnUiThread);
@@ -647,6 +652,10 @@
             return mLastShouldOverrideUrl;
         }
 
+        public WebResourceRequest getLastShouldOverrideResourceRequest() {
+            return mLastShouldOverrideResourceRequest;
+        }
+
         public String getLoginRequestRealm() {
             return mOnReceivedLoginRealm;
         }
@@ -745,6 +754,15 @@
         @Override
         public boolean shouldOverrideUrlLoading(WebView view, String url) {
             mLastShouldOverrideUrl = url;
+            mLastShouldOverrideResourceRequest = null;
+            mShouldOverrideUrlLoadingCallCount++;
+            return false;
+        }
+
+        @Override
+        public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
+            mLastShouldOverrideUrl = request.getUrl().toString();
+            mLastShouldOverrideResourceRequest = request;
             mShouldOverrideUrlLoadingCallCount++;
             return false;
         }
diff --git a/tests/tests/widget/src/android/widget/cts/AbsListViewTest.java b/tests/tests/widget/src/android/widget/cts/AbsListViewTest.java
index 89a3448..cf8012e 100644
--- a/tests/tests/widget/src/android/widget/cts/AbsListViewTest.java
+++ b/tests/tests/widget/src/android/widget/cts/AbsListViewTest.java
@@ -16,6 +16,7 @@
 
 package android.widget.cts;
 
+import android.test.suitebuilder.annotation.MediumTest;
 import android.widget.cts.R;
 
 import org.xmlpull.v1.XmlPullParser;
@@ -648,6 +649,98 @@
         assertFalse(listView.isInFilterMode());
     }
 
+    @MediumTest
+    public void testSetItemChecked_multipleModeSameValue()
+            throws Throwable {
+        // Calling setItemChecked with the same value in multiple choice mode should not cause
+        // requestLayout
+        mListView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
+        runTestOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                mListView.setItemChecked(0, false);
+            }
+        });
+        mInstrumentation.waitForIdleSync();
+        assertFalse(mListView.isLayoutRequested());
+        runTestOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                mListView.setItemChecked(0, false);
+            }
+        });
+        assertFalse(mListView.isLayoutRequested());
+    }
+
+    @MediumTest
+    public void testSetItemChecked_singleModeSameValue()
+            throws Throwable {
+        // Calling setItemChecked with the same value in single choice mode should not cause
+        // requestLayout
+        mListView.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
+        runTestOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                mListView.setItemChecked(0, false);
+            }
+        });
+        mInstrumentation.waitForIdleSync();
+        assertFalse(mListView.isLayoutRequested());
+        runTestOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                mListView.setItemChecked(0, false);
+            }
+        });
+        assertFalse(mListView.isLayoutRequested());
+    }
+
+    @MediumTest
+    public void testSetItemChecked_multipleModeDifferentValue()
+            throws Throwable {
+        // Calling setItemChecked with a different value in multiple choice mode should cause
+        // requestLayout
+        mListView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
+        runTestOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                mListView.setItemChecked(0, false);
+            }
+        });
+        mInstrumentation.waitForIdleSync();
+        assertFalse(mListView.isLayoutRequested());
+        runTestOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                mListView.setItemChecked(0, true);
+            }
+        });
+        assertTrue(mListView.isLayoutRequested());
+    }
+
+    @MediumTest
+    public void testSetItemChecked_singleModeDifferentValue()
+            throws Throwable {
+        // Calling setItemChecked with a different value in single choice mode should cause
+        // requestLayout
+        mListView.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
+        runTestOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                mListView.setItemChecked(0, false);
+            }
+        });
+        mInstrumentation.waitForIdleSync();
+        assertFalse(mListView.isLayoutRequested());
+        runTestOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                mListView.setItemChecked(0, true);
+            }
+        });
+        assertTrue(mListView.isLayoutRequested());
+    }
+
     public void testLayoutChildren() {
         /**
          * the subclass ListView and GridView override this method, so we can not test
diff --git a/tests/video/src/android/video/cts/VideoEncoderDecoderTest.java b/tests/video/src/android/video/cts/VideoEncoderDecoderTest.java
index d5db375..81a5ce0 100644
--- a/tests/video/src/android/video/cts/VideoEncoderDecoderTest.java
+++ b/tests/video/src/android/video/cts/VideoEncoderDecoderTest.java
@@ -68,6 +68,7 @@
     private static final String VIDEO_VP8 = MediaFormat.MIMETYPE_VIDEO_VP8;
     private static final String VIDEO_H263 = MediaFormat.MIMETYPE_VIDEO_H263;
     private static final String VIDEO_MPEG4 = MediaFormat.MIMETYPE_VIDEO_MPEG4;
+    private static final String VIDEO_HEVC = MediaFormat.MIMETYPE_VIDEO_HEVC;
     private int mCurrentTestRound = 0;
     private double[][] mEncoderFrameTimeDiff = null;
     private double[][] mDecoderFrameTimeDiff = null;
@@ -342,6 +343,53 @@
         doTestGoog(VIDEO_MPEG4, 1280, 720);
     }
 
+    // Hevc tests
+    public void testHevc0320x0240Other() throws Exception {
+        doTestOther(VIDEO_HEVC, 320, 240);
+    }
+
+    public void testHevc0320x0240Goog() throws Exception {
+        doTestGoog(VIDEO_HEVC, 320, 240);
+    }
+
+    public void testHevc0720x0480Other() throws Exception {
+        doTestOther(VIDEO_HEVC, 720, 480);
+    }
+
+    public void testHevc0720x0480Goog() throws Exception {
+        doTestGoog(VIDEO_HEVC, 720, 480);
+    }
+
+    @TimeoutReq(minutes = 10)
+    public void testHevc1280x0720Other() throws Exception {
+        doTestOther(VIDEO_HEVC, 1280, 720);
+    }
+
+    @TimeoutReq(minutes = 10)
+    public void testHevc1280x0720Goog() throws Exception {
+        doTestGoog(VIDEO_HEVC, 1280, 720);
+    }
+
+    @TimeoutReq(minutes = 10)
+    public void testHevc1920x1080Other() throws Exception {
+        doTestOther(VIDEO_HEVC, 1920, 1080);
+    }
+
+    @TimeoutReq(minutes = 10)
+    public void testHevc1920x1080Goog() throws Exception {
+        doTestGoog(VIDEO_HEVC, 1920, 1080);
+    }
+
+    @TimeoutReq(minutes = 10)
+    public void testHevc3840x2160Other() throws Exception {
+        doTestOther(VIDEO_HEVC, 3840, 2160);
+    }
+
+    @TimeoutReq(minutes = 10)
+    public void testHevc3840x2160Goog() throws Exception {
+        doTestGoog(VIDEO_HEVC, 3840, 2160);
+    }
+
     private boolean isSrcSemiPlanar() {
         return mSrcColorFormat == CodecCapabilities.COLOR_FormatYUV420SemiPlanar;
     }
@@ -451,6 +499,14 @@
                    ", dec format " + mDstColorFormat);
 
         initYUVPlane(w + YUV_PLANE_ADDITIONAL_LENGTH, h + YUV_PLANE_ADDITIONAL_LENGTH);
+        // Adjust total number of frames to prevent OOM.
+        Runtime rt = Runtime.getRuntime();
+        long usedMemory = rt.totalMemory() - rt.freeMemory();
+        mTestConfig.mTotalFrames = Math.min(mTestConfig.mTotalFrames,
+                (int) (rt.maxMemory() - usedMemory) /
+                (infoEnc.mBitRate / 8 / infoEnc.mFps + 1));
+        Log.i(TAG, "Total testing frames " + mTestConfig.mTotalFrames);
+
         mEncoderFrameTimeDiff =
                 new double[mTestConfig.mNumberOfRepeat][mTestConfig.mTotalFrames - 1];
         mDecoderFrameTimeDiff =
diff --git a/tools/cts-tradefed/DynamicConfig.xml b/tools/cts-tradefed/DynamicConfig.xml
index 6acffc0..e02d61e 100644
--- a/tools/cts-tradefed/DynamicConfig.xml
+++ b/tools/cts-tradefed/DynamicConfig.xml
@@ -13,6 +13,8 @@
      limitations under the License.
 -->
 
-<DynamicConfig>
-    <Config key="MediaFilesUrl">https://dl.google.com/dl/android/cts/android-cts-media-1.1.zip</Config>
-</DynamicConfig>
+<dynamicConfig>
+    <entry key="MediaFilesUrl">
+         <value>https://dl.google.com/dl/android/cts/android-cts-media-1.1.zip</value>
+    </entry>
+</dynamicConfig>
diff --git a/tools/cts-tradefed/res/config/cts-preconditions.xml b/tools/cts-tradefed/res/config/cts-preconditions.xml
new file mode 100644
index 0000000..9f0a8c9
--- /dev/null
+++ b/tools/cts-tradefed/res/config/cts-preconditions.xml
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2015 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<configuration description="CTS precondition configs">
+
+    <option name="compatibility:plan" value="cts-preconditions" />
+
+    <target_preparer class="com.android.compatibility.common.tradefed.targetprep.DynamicConfigPusher">
+        <option name="target" value="host" />
+        <option name="module-name" value="cts_v2"/>
+        <option name="version-name" value="1.0"/>
+    </target_preparer>
+
+    <target_preparer class="com.android.compatibility.common.tradefed.targetprep.StayAwakePreparer" />
+
+    <target_preparer class="com.android.compatibility.common.tradefed.targetprep.ApkPreconditionCheck">
+        <option name="apk" value="CtsPreconditions.apk"/>
+        <option name="package" value="com.android.preconditions.cts"/>
+    </target_preparer>
+
+    <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
+        <option name="run-command" value="rm -rf /sdcard/device-info-files" />
+    </target_preparer>
+
+    <target_preparer class="com.android.compatibility.common.tradefed.targetprep.DeviceInfoCollector">
+        <option name="apk" value="CtsDeviceInfo.apk"/>
+        <option name="package" value="com.android.compatibility.common.deviceinfo"/>
+        <option name="src-dir" value="/sdcard/device-info-files/"/>
+        <option name="dest-dir" value="device-info-files/"/>
+    </target_preparer>
+
+</configuration>
diff --git a/tools/cts-tradefed/res/config/cts.xml b/tools/cts-tradefed/res/config/cts.xml
index e6a75a0..8e34297 100644
--- a/tools/cts-tradefed/res/config/cts.xml
+++ b/tools/cts-tradefed/res/config/cts.xml
@@ -16,6 +16,7 @@
 <configuration description="Runs CTS from a pre-existing CTS installation">
 
     <include name="everything" />
+    <include name="cts-preconditions" />
 
     <option name="compatibility:plan" value="cts" />
 
@@ -24,17 +25,10 @@
     <!-- Exclude sample test cases -->
     <option name="compatibility:exclude-filter" value="CtsSampleDeviceTestCases" />
     <option name="compatibility:exclude-filter" value="CtsSampleHostTestCases" />
+
     <!-- Exclude automotive only test cases for now -->
     <option name="compatibility:exclude-filter" value="CtsAutomotiveTestCases" />
 
-    <target_preparer class="com.android.compatibility.common.tradefed.targetprep.DynamicConfigPusher">
-        <option name="target" value="host" />
-        <option name="module-name" value="cts_v2"/>
-        <option name="version-name" value="1.0"/>
-    </target_preparer>
-
-    <target_preparer class="com.android.compatibility.common.tradefed.targetprep.StayAwakePreparer" />
-
     <target_preparer class="com.android.compatibility.common.tradefed.targetprep.PropertyCheck">
         <option name="property-name" value="ro.build.type" />
         <option name="expected-value" value="user"/> <!-- Device should have user build -->
@@ -47,20 +41,4 @@
         <option name="throw-error" value="false"/> <!-- Only print warning if not en-US -->
     </target_preparer>
 
-    <target_preparer class="com.android.compatibility.common.tradefed.targetprep.ApkPreconditionCheck">
-        <option name="apk" value="CtsPreconditions.apk"/>
-        <option name="package" value="com.android.preconditions.cts"/>
-    </target_preparer>
-
-    <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
-        <option name="run-command" value="rm -rf /sdcard/device-info-files" />
-    </target_preparer>
-
-    <target_preparer class="com.android.compatibility.common.tradefed.targetprep.DeviceInfoCollector">
-        <option name="apk" value="CtsDeviceInfo.apk"/>
-        <option name="package" value="com.android.compatibility.common.deviceinfo"/>
-        <option name="src-dir" value="/sdcard/device-info-files/"/>
-        <option name="dest-dir" value="device-info-files/"/>
-    </target_preparer>
-
 </configuration>
diff --git a/tools/utils/certificates.py b/tools/utils/certificates.py
new file mode 100644
index 0000000..dc92767
--- /dev/null
+++ b/tools/utils/certificates.py
@@ -0,0 +1,100 @@
+#!/usr/bin/env python
+#
+# 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.
+import random
+
+from OpenSSL import crypto
+from Crypto.PublicKey import RSA
+
+class Certificate(object):
+    cert = None
+    key = None
+    def __init__(self, cert, key):
+        self.cert = cert
+        self.key = key
+
+    def cert_pem(self):
+        return crypto.dump_certificate(crypto.FILETYPE_PEM, self.cert)
+
+    def key_pem(self):
+        return crypto.dump_privatekey(crypto.FILETYPE_PEM, self.key)
+
+    def save_to_file(self, path):
+        with open(path, "w") as f:
+            f.write(self.cert_pem())
+            f.write(self.key_pem())
+
+    def save_cert_to_file(self, path):
+        with open(path, "w") as f:
+            f.write(self.cert_pem())
+
+    @staticmethod
+    def from_file(path):
+        with open(path) as f:
+            data = f.read()
+            cert = crypto.load_certificate(crypto.FILETYPE_PEM, data)
+            key = crypto.load_privatekey(crypto.FILETYPE_PEM, data)
+            return Certificate(cert, key)
+
+    @staticmethod
+    def create(cn, issuer=None, key=None, keysize=2048, digest="sha256",
+            notBefore="20150101000000+0000", notAfter="20300101000000+0000",
+            additional_extensions=None):
+        if key is None:
+            key = crypto.PKey()
+            key.generate_key(crypto.TYPE_RSA, keysize)
+
+        cert = crypto.X509()
+        cert.set_pubkey(key)
+        cert.set_version(2)
+        cert.set_serial_number(random.randint(0, 2**20))
+
+        cert.set_notBefore(notBefore)
+        cert.set_notAfter(notAfter)
+        cert.get_subject().CN = cn
+        cert.set_issuer(cert.get_subject() if issuer is None else issuer.cert.get_subject())
+        # Add the CA=True basic constraint
+        basicContraints = crypto.X509Extension("basicConstraints", True, "CA:TRUE")
+        cert.add_extensions([basicContraints])
+        if additional_extensions is not None:
+            cert.add_extensions(additional_extensions)
+
+        signing_key = key if issuer is None else issuer.key
+        cert.sign(signing_key, digest)
+
+        return Certificate(cert, key)
+
+if __name__ == "__main__":
+    # Generate test certificates.
+    a = Certificate.create("Root A")
+    a_sha1 = Certificate.create("Root A", key=a.key, digest="sha1")
+    b = Certificate.create("Root B")
+    a_to_b = Certificate.create("Root A", b, a.key)
+    b_to_a = Certificate.create("Root B", a, b.key)
+    leaf1 = Certificate.create("Leaf", a)
+    intermediate_a = Certificate.create("intermediate", a)
+    intermediate_b = Certificate.create("intermediate", b, intermediate_a.key)
+    leaf2 = Certificate.create("Leaf 2", intermediate_a)
+
+    # Save test certificates.
+    a.save_cert_to_file("a.pem")
+    a_sha1.save_cert_to_file("a_sha1.pem")
+    b.save_cert_to_file("b.pem")
+    a_to_b.save_cert_to_file("a_to_b.pem")
+    b_to_a.save_cert_to_file("b_to_a.pem")
+    leaf1.save_cert_to_file("leaf1.pem")
+    leaf2.save_cert_to_file("leaf2.pem")
+    intermediate_a.save_cert_to_file("intermediate_a.pem")
+    intermediate_b.save_cert_to_file("intermediate_b.pem")
diff --git a/tools/vm-tests-tf/Android.mk b/tools/vm-tests-tf/Android.mk
index 1e7be8a..7578fa9 100644
--- a/tools/vm-tests-tf/Android.mk
+++ b/tools/vm-tests-tf/Android.mk
@@ -66,6 +66,7 @@
 intermediates := $(call intermediates-dir-for,JAVA_LIBRARIES,vm-tests-tf,HOST)
 vmteststf_jar := $(intermediates)/android.core.vm-tests-tf.jar
 vmteststf_dep_jars := $(addprefix $(HOST_OUT_JAVA_LIBRARIES)/, cts-tf-dalvik-buildutil.jar dasm.jar dx.jar cfassembler.jar junit.jar)
+vmteststf_dep_jars += $(addprefix $(HOST_OUT_JAVA_LIBRARIES)/, jack.jar)
 vmteststf_dep_jars += $(private_jill_jarjar_asm)
 
 $(vmteststf_jar): PRIVATE_JACK_EXTRA_ARGS := $(LOCAL_JACK_EXTRA_ARGS)
@@ -82,7 +83,7 @@
 $(vmteststf_jar): PRIVATE_INTERMEDIATES_HOSTJUNIT_FILES := $(intermediates)/hostjunit_files
 $(vmteststf_jar): PRIVATE_CLASS_PATH := $(subst $(space),:,$(vmteststf_dep_jars)):$(HOST_JDK_TOOLS_JAR)
 ifndef LOCAL_JACK_ENABLED
-$(vmteststf_jar) : $(vmteststf_dep_jars) $(JILL_JAR) $(HOST_OUT_JAVA_LIBRARIES)/tradefed-prebuilt.jar
+$(vmteststf_jar) : $(vmteststf_dep_jars) $(JACK_JAR) $(JILL_JAR) $(HOST_OUT_JAVA_LIBRARIES)/tradefed-prebuilt.jar
 	$(hide) rm -rf $(dir $@) && mkdir -p $(dir $@)
 	$(hide) mkdir -p $(PRIVATE_INTERMEDIATES_HOSTJUNIT_FILES)/dot/junit $(dir $(PRIVATE_INTERMEDIATES_DEXCORE_JAR))
 	# generated and compile the host side junit tests
@@ -101,12 +102,12 @@
 oj_jack := $(call intermediates-dir-for,JAVA_LIBRARIES,core-oj,,COMMON)/classes.jack
 libart_jack := $(call intermediates-dir-for,JAVA_LIBRARIES,core-libart,,COMMON)/classes.jack
 $(vmteststf_jar): PRIVATE_DALVIK_SUITE_CLASSPATH := $(oj_jack):$(libart_jack):$(cts-tf-dalvik-lib.jack):$(HOST_OUT_JAVA_LIBRARIES)/tradefed-prebuilt.jar
-$(vmteststf_jar) : $(vmteststf_dep_jars) $(JACK) $(JILL_JAR) $(oj_jack) $(libart_jack) $(HOST_OUT_JAVA_LIBRARIES)/tradefed-prebuilt.jar | setup-jack-server
+$(vmteststf_jar) : $(vmteststf_dep_jars) $(JACK_JAR) $(JILL_JAR) $(oj_jack) $(libart_jack) $(HOST_OUT_JAVA_LIBRARIES)/tradefed-prebuilt.jar
 	$(hide) rm -rf $(dir $@) && mkdir -p $(dir $@)
 	$(hide) mkdir -p $(PRIVATE_INTERMEDIATES_HOSTJUNIT_FILES)/dot/junit $(dir $(PRIVATE_INTERMEDIATES_DEXCORE_JAR))
 	# generated and compile the host side junit tests
 	@echo "Write generated Main_*.java files to $(PRIVATE_INTERMEDIATES_MAIN_FILES)"
-	$(hide) JACK_VERSION=$(JACK_DEFAULT_VERSION) java -cp $(PRIVATE_CLASS_PATH) util.build.JackBuildDalvikSuite $(PRIVATE_SRC_FOLDER) $(PRIVATE_INTERMEDIATES) \
+	$(hide) java -cp $(PRIVATE_CLASS_PATH) util.build.JackBuildDalvikSuite $(PRIVATE_SRC_FOLDER) $(PRIVATE_INTERMEDIATES) \
 		$(PRIVATE_DALVIK_SUITE_CLASSPATH) \
 		$(PRIVATE_INTERMEDIATES_MAIN_FILES) $(PRIVATE_INTERMEDIATES_CLASSES) $(PRIVATE_INTERMEDIATES_HOSTJUNIT_FILES) $$RUN_VM_TESTS_RTO
 	@echo "Generate $(PRIVATE_INTERMEDIATES_DEXCORE_JAR)"
diff --git a/tools/vm-tests-tf/src/dot/junit/opcodes/invoke_super/Test_invoke_super.java b/tools/vm-tests-tf/src/dot/junit/opcodes/invoke_super/Test_invoke_super.java
index 0995fb0..733c2b7 100644
--- a/tools/vm-tests-tf/src/dot/junit/opcodes/invoke_super/Test_invoke_super.java
+++ b/tools/vm-tests-tf/src/dot/junit/opcodes/invoke_super/Test_invoke_super.java
@@ -236,8 +236,8 @@
      * @title attempt to invoke interface method
      */
     public void testVFE18() {
-        loadAndRun("dot.junit.opcodes.invoke_super.d.T_invoke_super_24", 
-                   IncompatibleClassChangeError.class);
+        loadAndRun("dot.junit.opcodes.invoke_super.d.T_invoke_super_24",
+                   AbstractMethodError.class);
     }
 
     /**
diff --git a/tools/vm-tests-tf/src/dot/junit/opcodes/invoke_super_range/Test_invoke_super_range.java b/tools/vm-tests-tf/src/dot/junit/opcodes/invoke_super_range/Test_invoke_super_range.java
index 9007ef4..566b84f 100644
--- a/tools/vm-tests-tf/src/dot/junit/opcodes/invoke_super_range/Test_invoke_super_range.java
+++ b/tools/vm-tests-tf/src/dot/junit/opcodes/invoke_super_range/Test_invoke_super_range.java
@@ -243,7 +243,7 @@
     public void testVFE18() {
         //@uses dot.junit.opcodes.invoke_super_range.d.T_invoke_super_range_24
         loadAndRun("dot.junit.opcodes.invoke_super_range.d.T_invoke_super_range_24",
-                   IncompatibleClassChangeError.class);
+                   AbstractMethodError.class);
     }
 
     /**
diff --git a/tools/vm-tests-tf/src/util/build/BytesStreamSucker.java b/tools/vm-tests-tf/src/util/build/BytesStreamSucker.java
deleted file mode 100644
index f243c17..0000000
--- a/tools/vm-tests-tf/src/util/build/BytesStreamSucker.java
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package util.build;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-
-import javax.annotation.Nonnull;
-
-/**
- * Class that continuously read an {@link InputStream} and optionally could write the input in a
- * {@link OutputStream}.
- */
-public class BytesStreamSucker {
-
-  private static final int BUFFER_SIZE = 4096;
-
-  @Nonnull
-  private final byte[] buffer = new byte[BUFFER_SIZE];
-
-  @Nonnull
-  private final InputStream is;
-
-  @Nonnull
-  private final OutputStream os;
-
-  private final boolean toBeClose;
-
-  public BytesStreamSucker(
-      @Nonnull InputStream is, @Nonnull OutputStream os, boolean toBeClose) {
-    this.is = is;
-    this.os = os;
-    this.toBeClose = toBeClose;
-  }
-
-  public BytesStreamSucker(@Nonnull InputStream is, @Nonnull OutputStream os) {
-    this(is, os, false);
-  }
-
-  public void suck() throws IOException {
-    try {
-      int bytesRead;
-      while ((bytesRead = is.read(buffer)) >= 0) {
-        os.write(buffer, 0, bytesRead);
-        os.flush();
-      }
-    } finally {
-      if (toBeClose) {
-        os.close();
-      }
-    }
-  }
-}
\ No newline at end of file
diff --git a/tools/vm-tests-tf/src/util/build/CharactersStreamSucker.java b/tools/vm-tests-tf/src/util/build/CharactersStreamSucker.java
deleted file mode 100644
index ce4dfb1..0000000
--- a/tools/vm-tests-tf/src/util/build/CharactersStreamSucker.java
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package util.build;
-
-import java.io.BufferedReader;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.io.PrintStream;
-
-import javax.annotation.Nonnull;
-
-/**
- * Class that continuously read an {@link InputStream} and optionally could print the input in a
- * {@link PrintStream}.
- */
-public class CharactersStreamSucker {
-
-  @Nonnull
-  private final BufferedReader ir;
-
-  @Nonnull
-  private final PrintStream os;
-
-  private final boolean toBeClose;
-
-  public CharactersStreamSucker(
-      @Nonnull InputStream is, @Nonnull PrintStream os, boolean toBeClose) {
-    this.ir = new BufferedReader(new InputStreamReader(is));
-    this.os = os;
-    this.toBeClose = toBeClose;
-  }
-
-  public CharactersStreamSucker(@Nonnull InputStream is, @Nonnull PrintStream os) {
-    this(is, os, false);
-  }
-
-  public void suck() throws IOException {
-    String line;
-
-    try {
-      while ((line = ir.readLine()) != null) {
-        os.println(line);
-      }
-    } finally {
-      if (toBeClose) {
-        os.close();
-      }
-    }
-  }
-}
\ No newline at end of file
diff --git a/tools/vm-tests-tf/src/util/build/ExecuteFile.java b/tools/vm-tests-tf/src/util/build/ExecuteFile.java
deleted file mode 100644
index a0e5ce5..0000000
--- a/tools/vm-tests-tf/src/util/build/ExecuteFile.java
+++ /dev/null
@@ -1,274 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package util.build;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.io.PrintStream;
-import java.io.StreamTokenizer;
-import java.io.StringReader;
-import java.util.ArrayList;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-import javax.annotation.CheckForNull;
-import javax.annotation.Nonnull;
-
-/**
- * Class to handle the execution of an external process
- */
-public class ExecuteFile {
-  @Nonnull
-  private final String[] cmdLine;
-
-  @CheckForNull
-  private File workDir;
-
-  @CheckForNull
-  private InputStream inStream;
-  private boolean inToBeClose;
-
-  @CheckForNull
-  private OutputStream outStream;
-  private boolean outToBeClose;
-
-  @CheckForNull
-  private OutputStream errStream;
-  private boolean errToBeClose;
-  private boolean verbose;
-
-  @Nonnull
-  private final Logger logger = Logger.getLogger(this.getClass().getName());
-
-  public void setErr(@Nonnull File file) throws FileNotFoundException {
-    errStream = new FileOutputStream(file);
-    errToBeClose = true;
-  }
-
-  public void setOut(@Nonnull File file) throws FileNotFoundException {
-    outStream = new FileOutputStream(file);
-    outToBeClose = true;
-  }
-
-  public void setIn(@Nonnull File file) throws FileNotFoundException {
-    inStream = new FileInputStream(file);
-    inToBeClose = true;
-  }
-
-  public void setErr(@Nonnull OutputStream stream) {
-    errStream = stream;
-  }
-
-  public void setOut(@Nonnull OutputStream stream) {
-    outStream = stream;
-  }
-
-  public void setIn(@Nonnull InputStream stream) {
-    inStream = stream;
-  }
-
-  public void setWorkingDir(@Nonnull File dir, boolean create) throws IOException {
-    if (!dir.isDirectory()) {
-      if (create && !dir.exists()) {
-        if (!dir.mkdirs()) {
-          throw new IOException("Directory creation failed");
-        }
-      } else {
-        throw new FileNotFoundException(dir.getPath() + " is not a directory");
-      }
-    }
-
-    workDir = dir;
-  }
-
-  public void setVerbose(boolean verbose) {
-    this.verbose = verbose;
-  }
-
-  public ExecuteFile(@Nonnull File exec, @Nonnull String[] args) {
-    cmdLine = new String[args.length + 1];
-    System.arraycopy(args, 0, cmdLine, 1, args.length);
-
-    cmdLine[0] = exec.getAbsolutePath();
-  }
-
-  public ExecuteFile(@Nonnull String exec, @Nonnull String[] args) {
-    cmdLine = new String[args.length + 1];
-    System.arraycopy(args, 0, cmdLine, 1, args.length);
-
-    cmdLine[0] = exec;
-  }
-
-  public ExecuteFile(@Nonnull File exec) {
-    cmdLine = new String[1];
-    cmdLine[0] = exec.getAbsolutePath();
-  }
-
-  public ExecuteFile(@Nonnull String[] cmdLine) {
-    this.cmdLine = cmdLine.clone();
-  }
-
-  public ExecuteFile(@Nonnull String cmdLine) throws IOException {
-    StringReader reader = new StringReader(cmdLine);
-    StreamTokenizer tokenizer = new StreamTokenizer(reader);
-    tokenizer.resetSyntax();
-    // Only standard spaces are recognized as whitespace chars
-    tokenizer.whitespaceChars(' ', ' ');
-    // Matches alphanumerical and common special symbols like '(' and ')'
-    tokenizer.wordChars('!', 'z');
-    // Quote chars will be ignored when parsing strings
-    tokenizer.quoteChar('\'');
-    tokenizer.quoteChar('\"');
-    ArrayList<String> tokens = new ArrayList<String>();
-    while (tokenizer.nextToken() != StreamTokenizer.TT_EOF) {
-      String token = tokenizer.sval;
-      if (token != null) {
-        tokens.add(token);
-      }
-    }
-    this.cmdLine = tokens.toArray(new String[0]);
-  }
-
-  public boolean run() {
-    int ret;
-    Process proc = null;
-    Thread suckOut = null;
-    Thread suckErr = null;
-    Thread suckIn = null;
-
-    try {
-      StringBuilder cmdLineBuilder = new StringBuilder();
-      for (String arg : cmdLine) {
-        cmdLineBuilder.append(arg).append(' ');
-      }
-      if (verbose) {
-        PrintStream printStream;
-        if (outStream instanceof PrintStream) {
-          printStream = (PrintStream) outStream;
-        } else {
-          printStream = System.out;
-        }
-
-        if (printStream != null) {
-          printStream.println(cmdLineBuilder);
-        }
-      } else {
-        logger.log(Level.FINE, "Execute: {0}", cmdLineBuilder);
-      }
-
-      proc = Runtime.getRuntime().exec(cmdLine, null, workDir);
-
-      InputStream localInStream = inStream;
-      if (localInStream != null) {
-        suckIn = new Thread(
-            new ThreadBytesStreamSucker(localInStream, proc.getOutputStream(), inToBeClose));
-      } else {
-        proc.getOutputStream().close();
-      }
-
-      OutputStream localOutStream = outStream;
-      if (localOutStream != null) {
-        if (localOutStream instanceof PrintStream) {
-          suckOut = new Thread(new ThreadCharactersStreamSucker(proc.getInputStream(),
-              (PrintStream) localOutStream, outToBeClose));
-        } else {
-          suckOut = new Thread(
-              new ThreadBytesStreamSucker(proc.getInputStream(), localOutStream, outToBeClose));
-        }
-      }
-
-      OutputStream localErrStream = errStream;
-      if (localErrStream != null) {
-        if (localErrStream instanceof PrintStream) {
-          suckErr = new Thread(new ThreadCharactersStreamSucker(proc.getErrorStream(),
-              (PrintStream) localErrStream, errToBeClose));
-        } else {
-          suckErr = new Thread(
-              new ThreadBytesStreamSucker(proc.getErrorStream(), localErrStream, errToBeClose));
-        }
-      }
-
-      if (suckIn != null) {
-        suckIn.start();
-      }
-      if (suckOut != null) {
-        suckOut.start();
-      }
-      if (suckErr != null) {
-        suckErr.start();
-      }
-
-      proc.waitFor();
-      if (suckIn != null) {
-        suckIn.join();
-      }
-      if (suckOut != null) {
-        suckOut.join();
-      }
-      if (suckErr != null) {
-        suckErr.join();
-      }
-
-      ret = proc.exitValue();
-      proc.destroy();
-
-      return ret == 0;
-    } catch (Throwable e) {
-      return false;
-    }
-  }
-
-  private static class ThreadBytesStreamSucker extends BytesStreamSucker implements Runnable {
-
-    public ThreadBytesStreamSucker(@Nonnull InputStream is, @Nonnull OutputStream os,
-        boolean toBeClose) {
-      super(is, os, toBeClose);
-    }
-
-    @Override
-    public void run() {
-      try {
-        suck();
-      } catch (IOException e) {
-        // Best effort
-      }
-    }
-  }
-
-  private static class ThreadCharactersStreamSucker extends CharactersStreamSucker implements
-      Runnable {
-
-    public ThreadCharactersStreamSucker(@Nonnull InputStream is, @Nonnull PrintStream ps,
-        boolean toBeClose) {
-      super(is, ps, toBeClose);
-    }
-
-    @Override
-    public void run() {
-      try {
-        suck();
-      } catch (IOException e) {
-        // Best effort
-      }
-    }
-  }
-}
\ No newline at end of file
diff --git a/tools/vm-tests-tf/src/util/build/JackBuildStep.java b/tools/vm-tests-tf/src/util/build/JackBuildStep.java
index 29df8260..061ff07 100644
--- a/tools/vm-tests-tf/src/util/build/JackBuildStep.java
+++ b/tools/vm-tests-tf/src/util/build/JackBuildStep.java
@@ -16,10 +16,16 @@
 
 package util.build;
 
+import com.android.jack.CLILogConfiguration;
+import com.android.jack.CLILogConfiguration.LogConfigurationException;
+
+import util.build.BuildStep.BuildFile;
+
+import com.android.jack.Jack;
+import com.android.jack.Main;
+import com.android.jack.Options;
 import java.io.File;
-import java.io.FileWriter;
 import java.io.IOException;
-import java.io.Writer;
 import java.util.ArrayList;
 import java.util.HashSet;
 import java.util.List;
@@ -27,6 +33,14 @@
 
 public class JackBuildStep extends SourceBuildStep {
 
+    static {
+        try {
+              CLILogConfiguration.setupLogs();
+            } catch (LogConfigurationException e) {
+              throw new Error("Failed to setup logs", e);
+            }
+    }
+
     private final String destPath;
     private final String classPath;
     private final Set<String> sourceFiles = new HashSet<String>();
@@ -64,18 +78,7 @@
             }
             File tmpDex = new File(tmpOutDir, "classes.dex");
 
-            File tmpArgs = new File(outDir, outputFile.fileName.getName() + ".args");
-
-            Writer argsOut = null;
             try {
-                argsOut = new FileWriter(tmpArgs);
-                for (String source : sourceFiles) {
-                    argsOut.append(source);
-                    argsOut.append('\n');
-                }
-                argsOut.close();
-                argsOut = null;
-
                 List<String> commandLine = new ArrayList<String>(6 + sourceFiles.size());
                 commandLine.add("--verbose");
                 commandLine.add("error");
@@ -83,14 +86,10 @@
                 commandLine.add(classPath);
                 commandLine.add("--output-dex");
                 commandLine.add(tmpOutDir.getAbsolutePath());
-                commandLine.add("@" + tmpArgs.getPath());
+                commandLine.addAll(sourceFiles);
 
-                ExecuteFile exec = new ExecuteFile("jack", commandLine.toArray(new String[commandLine.size()]));
-                exec.setErr(System.err);
-                exec.setOut(System.out);
-                if (!exec.run()) {
-                    return false;
-                }
+                Options options = Main.parseCommandLine(commandLine);
+                Jack.checkAndRun(options);
 
                 JarBuildStep jarStep = new JarBuildStep(
                     new BuildFile(tmpDex),
@@ -105,16 +104,8 @@
                 ex.printStackTrace();
                 return false;
             } finally {
-                tmpDex.delete();
-                tmpArgs.delete();
-                tmpOutDir.delete();
-                if (argsOut != null) {
-                    try {
-                        argsOut.close();
-                    } catch (IOException io) {
-                        // Ignore, don't override already thrown exception
-                    }
-                }
+              tmpDex.delete();
+              tmpOutDir.delete();
             }
         }
         return false;
diff --git a/tools/vm-tests-tf/src/util/build/JackDexBuildStep.java b/tools/vm-tests-tf/src/util/build/JackDexBuildStep.java
index 3702973..8734cf0 100644
--- a/tools/vm-tests-tf/src/util/build/JackDexBuildStep.java
+++ b/tools/vm-tests-tf/src/util/build/JackDexBuildStep.java
@@ -16,6 +16,10 @@
 
 package util.build;
 
+import com.android.jack.Jack;
+import com.android.jack.Main;
+import com.android.jack.Options;
+
 import java.io.File;
 import java.io.IOException;
 import java.util.ArrayList;
@@ -65,12 +69,8 @@
                 commandLine.add("--import");
                 commandLine.add(inputFile.fileName.getAbsolutePath());
 
-                ExecuteFile exec = new ExecuteFile("jack", commandLine.toArray(new String[commandLine.size()]));
-                exec.setErr(System.err);
-                exec.setOut(System.out);
-                if (!exec.run()) {
-                  return false;
-                }
+                Options options = Main.parseCommandLine(commandLine);
+                Jack.checkAndRun(options);
 
                 JarBuildStep jarStep = new JarBuildStep(
                     new BuildFile(tmpDex),