Merge "Test TextView autosize and maxline" into oc-dev
diff --git a/apps/CameraITS/pymodules/its/objects.py b/apps/CameraITS/pymodules/its/objects.py
index 0883482..1269b84 100644
--- a/apps/CameraITS/pymodules/its/objects.py
+++ b/apps/CameraITS/pymodules/its/objects.py
@@ -108,7 +108,8 @@
         "android.colorCorrection.gains": [1,1,1,1],
         "android.lens.focusDistance" : f_distance,
         "android.tonemap.mode": 1,
-        "android.shading.mode": 1
+        "android.shading.mode": 1,
+        "android.lens.opticalStabilizationMode": 0
         }
     if linear_tonemap:
         assert(props is not None)
diff --git a/apps/CameraITS/tools/turn_off_screen.py b/apps/CameraITS/tools/turn_off_screen.py
index 207042b..1faef9e 100644
--- a/apps/CameraITS/tools/turn_off_screen.py
+++ b/apps/CameraITS/tools/turn_off_screen.py
@@ -15,6 +15,9 @@
 import re
 import subprocess
 import sys
+import time
+
+TURN_OFF_DELAY = 1  # seconds. Needed for back to back runs
 
 
 def main():
@@ -38,5 +41,7 @@
     else:
         pwrdn = ('adb -s %s shell input keyevent POWER' % screen_id)
         subprocess.Popen(pwrdn.split())
+        time.sleep(TURN_OFF_DELAY)
+
 if __name__ == '__main__':
     main()
diff --git a/apps/CtsVerifier/AndroidManifest.xml b/apps/CtsVerifier/AndroidManifest.xml
index 18a8764..8648aae 100644
--- a/apps/CtsVerifier/AndroidManifest.xml
+++ b/apps/CtsVerifier/AndroidManifest.xml
@@ -118,32 +118,6 @@
                        android:value="android.software.device_admin" />
         </activity>
 
-        <activity android:name=".admin.tapjacking.DeviceAdminTapjackingTestActivity"
-                  android:label="@string/da_tapjacking_test"
-                  android:configChanges="keyboardHidden|orientation|screenSize">
-            <intent-filter>
-                <action android:name="android.intent.action.MAIN" />
-                <category android:name="android.cts.intent.category.MANUAL_TEST" />
-            </intent-filter>
-            <meta-data android:name="test_category" android:value="@string/test_category_device_admin" />
-            <meta-data android:name="test_required_features"
-                       android:value="android.software.device_admin" />
-        </activity>
-
-        <receiver android:name=".admin.tapjacking.EmptyDeviceAdminReceiver"
-                  android:permission="android.permission.BIND_DEVICE_ADMIN">
-            <meta-data android:name="android.app.device_admin"
-                       android:resource="@xml/tapjacking_device_admin" />
-            <intent-filter>
-                <action android:name="android.app.action.DEVICE_ADMIN_ENABLED"/>
-            </intent-filter>
-        </receiver>
-
-        <activity
-            android:name=".admin.tapjacking.OverlayingActivity"
-            android:theme="@style/OverlayTheme"
-            android:label="Overlaying Activity"/>
-
         <activity android:name=".companion.CompanionDeviceTestActivity"
                   android:label="@string/companion_test"
                   android:configChanges="keyboardHidden|orientation|screenSize">
diff --git a/apps/CtsVerifier/res/drawable/border.xml b/apps/CtsVerifier/res/drawable/border.xml
deleted file mode 100644
index b419618..0000000
--- a/apps/CtsVerifier/res/drawable/border.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-Copyright (C) 2017 The Android Open Source Project
-
-   Licensed under the Apache License, Version 2.0 (the "License");
-    you may not use this file except in compliance with the License.
-    You may obtain a copy of the License at
-
-         http://www.apache.org/licenses/LICENSE-2.0
-
-    Unless required by applicable law or agreed to in writing, software
-    distributed under the License is distributed on an "AS IS" BASIS,
-    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-    See the License for the specific language governing permissions and
-    limitations under the License.
--->
-<shape xmlns:android="http://schemas.android.com/apk/res/android"
-       android:shape="rectangle">
-    <corners
-        android:radius="4dp"/>
-    <stroke
-        android:width="4dp"
-        android:color="@android:color/black" />
-</shape>
diff --git a/apps/CtsVerifier/res/layout/da_tapjacking_overlay_activity.xml b/apps/CtsVerifier/res/layout/da_tapjacking_overlay_activity.xml
deleted file mode 100644
index 5583fce..0000000
--- a/apps/CtsVerifier/res/layout/da_tapjacking_overlay_activity.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-Copyright (C) 2017 The Android Open Source Project
-
-   Licensed under the Apache License, Version 2.0 (the "License");
-    you may not use this file except in compliance with the License.
-    You may obtain a copy of the License at
-
-         http://www.apache.org/licenses/LICENSE-2.0
-
-    Unless required by applicable law or agreed to in writing, software
-    distributed under the License is distributed on an "AS IS" BASIS,
-    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-    See the License for the specific language governing permissions and
-    limitations under the License.
--->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-              android:orientation="vertical"
-              android:layout_width="match_parent"
-              android:layout_height="match_parent"
-              android:background="@drawable/border"
-              android:padding="8dp" >
-
-    <TextView android:layout_width="wrap_content"
-              android:layout_height="wrap_content"
-              android:textColor="@android:color/black"
-              android:text="@string/da_tapjacking_overlay_app_description" />
-</LinearLayout>
\ No newline at end of file
diff --git a/apps/CtsVerifier/res/layout/da_tapjacking_test_main.xml b/apps/CtsVerifier/res/layout/da_tapjacking_test_main.xml
deleted file mode 100644
index 2ee9ea9..0000000
--- a/apps/CtsVerifier/res/layout/da_tapjacking_test_main.xml
+++ /dev/null
@@ -1,55 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-     Copyright (C) 2017 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
-    style="@style/RootLayoutPadding"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent">
-
-    <LinearLayout
-        android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        android:orientation="vertical">
-
-        <!-- Enable device admin -->
-        <RelativeLayout
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content" >
-
-            <TextView
-                android:id="@+id/admin_tapjacking_instructions"
-                style="@style/InstructionsSmallFont"
-                android:layout_width="match_parent"
-                android:layout_height="wrap_content"
-                android:layout_alignParentRight="true"
-                android:layout_alignParentTop="true"
-                android:text="@string/da_tapjacking_instructions" />
-
-            <Button
-                android:id="@+id/enable_admin_overlay_button"
-                android:layout_width="match_parent"
-                android:layout_height="wrap_content"
-                android:layout_alignParentRight="true"
-                android:layout_below="@id/admin_tapjacking_instructions"
-                android:layout_marginLeft="20dip"
-                android:layout_marginRight="20dip"
-                android:text="@string/da_tapjacking_button_text" />
-        </RelativeLayout>
-
-        <include layout="@layout/pass_fail_buttons" />
-    </LinearLayout>
-
-</ScrollView>
diff --git a/apps/CtsVerifier/res/values/strings.xml b/apps/CtsVerifier/res/values/strings.xml
index 69b9900..046d426 100755
--- a/apps/CtsVerifier/res/values/strings.xml
+++ b/apps/CtsVerifier/res/values/strings.xml
@@ -79,10 +79,6 @@
         can be easily uninstalled from the application details screen in a way similar to other
         apps.
     </string>
-    <string name="da_tapjacking_test">Device Admin Tapjacking Test</string>
-    <string name="da_tapjacking_test_info">This test checks that an activity cannot tapjack the
-        user by obscuring the device admin details while prompting the user to activate the admin.
-    </string>
     <string name="car_dock_test">Car Dock Test</string>
     <string name="car_dock_test_desc">This test ensures that car mode opens the app associated with
         car dock when going into car mode.\n\n
@@ -138,18 +134,6 @@
     </string>
     <string name="da_uninstall_admin_button_text">Launch settings</string>
 
-    <string name="da_tapjacking_overlay_app_description">This activity attempts to tapjack the activity below.\n
-        Any security sensitive controls below should not respond to taps as long as this activity is visible.</string>
-    <string name="da_tapjacking_instructions">
-        1. Launch the device admin add screen by pressing the button below.\n
-        2. Wait for an overlaying transparent activity to show up obscuring the device admin details window.\n
-        3. Attempt to activate the admin with the transparent activity still obscuring the window.
-        The button should discard any taps while showing a toast warning to the user.\n
-        4. Return to this screen and pass the test if the device admin could not be activated while the details
-        window was being obscured.
-    </string>
-    <string name="da_tapjacking_button_text">Enable device admin</string>
-
     <!-- Strings for lock bound keys test -->
     <string name="sec_lock_bound_key_test">Lock Bound Keys Test</string>
     <string name="sec_lock_bound_key_test_info">
diff --git a/apps/CtsVerifier/res/values/styles.xml b/apps/CtsVerifier/res/values/styles.xml
index 64fd4fe..0e05817 100644
--- a/apps/CtsVerifier/res/values/styles.xml
+++ b/apps/CtsVerifier/res/values/styles.xml
@@ -13,9 +13,4 @@
     <style name="RootLayoutPadding">
         <item name="android:padding">10dip</item>
     </style>
-    <style name="OverlayTheme" parent="android:Theme.Dialog">
-        <item name="android:windowNoTitle">true</item>
-        <item name="android:windowIsTranslucent">true</item>
-        <item name="android:windowBackground">@android:color/transparent</item>
-    </style>
 </resources>
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/admin/tapjacking/DeviceAdminTapjackingTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/admin/tapjacking/DeviceAdminTapjackingTestActivity.java
deleted file mode 100644
index 6a81cba..0000000
--- a/apps/CtsVerifier/src/com/android/cts/verifier/admin/tapjacking/DeviceAdminTapjackingTestActivity.java
+++ /dev/null
@@ -1,129 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.cts.verifier.admin.tapjacking;
-
-import android.app.admin.DevicePolicyManager;
-import android.content.ComponentName;
-import android.content.Intent;
-import android.os.Bundle;
-import android.os.SystemClock;
-import android.util.Log;
-import android.view.View;
-import android.widget.Button;
-
-import com.android.cts.verifier.PassFailButtons;
-import com.android.cts.verifier.R;
-
-/**
- * Test that checks that device admin activate button does not allow taps when another window
- * is obscuring the device admin details
- */
-public class DeviceAdminTapjackingTestActivity extends PassFailButtons.Activity implements
-        View.OnClickListener {
-
-    private static final String TAG = DeviceAdminTapjackingTestActivity.class.getSimpleName();
-    private static final String ADMIN_ACTIVATED_BUNDLE_KEY = "admin_activated";
-    private static final String ACTIVITIES_FINISHED_IN_ORDER_KEY = "activities_finished_in_order";
-    private static final int REQUEST_ENABLE_ADMIN = 0;
-    private static final int REQUEST_OVERLAY_ACTIVITY = 1;
-    private static final long REMOVE_ADMIN_TIMEOUT = 5000;
-
-    private DevicePolicyManager mDevicePolicyManager;
-    private Button mAddDeviceAdminButton;
-    private boolean mAdminActivated;
-    private boolean mActivitiesFinishedInOrder;
-    private boolean mOverlayFinished;
-    private ComponentName mAdmin;
-
-    @Override
-    protected void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        setContentView(R.layout.da_tapjacking_test_main);
-        setInfoResources(R.string.da_tapjacking_test, R.string.da_tapjacking_test_info, -1);
-        setPassFailButtonClickListeners();
-
-        mAdmin = new ComponentName(this, EmptyDeviceAdminReceiver.class);
-        mDevicePolicyManager = (DevicePolicyManager) getSystemService(DEVICE_POLICY_SERVICE);
-
-        if (savedInstanceState != null) {
-            mAdminActivated = savedInstanceState.getBoolean(ADMIN_ACTIVATED_BUNDLE_KEY, false);
-            mActivitiesFinishedInOrder = savedInstanceState.getBoolean(ACTIVITIES_FINISHED_IN_ORDER_KEY, false);
-        } else if (isActiveAdminAfterTimeout()) {
-            Log.e(TAG, "Could not remove active admin. Cannot proceed with test");
-            finish();
-        }
-        mAddDeviceAdminButton = findViewById(R.id.enable_admin_overlay_button);
-        mAddDeviceAdminButton.setOnClickListener(this);
-    }
-
-    private boolean isActiveAdminAfterTimeout() {
-        final long startTime = SystemClock.uptimeMillis();
-        while (mDevicePolicyManager.isAdminActive(mAdmin)
-                && SystemClock.uptimeMillis() < startTime + REMOVE_ADMIN_TIMEOUT) {
-            try {
-                Thread.sleep(1000);
-            } catch(InterruptedException exc) {
-            }
-        }
-        return mDevicePolicyManager.isAdminActive(mAdmin);
-    }
-
-    @Override
-    public void onClick(View v) {
-        if (v == mAddDeviceAdminButton) {
-            Intent securitySettingsIntent = new Intent(DevicePolicyManager.ACTION_ADD_DEVICE_ADMIN);
-            securitySettingsIntent.putExtra(DevicePolicyManager.EXTRA_DEVICE_ADMIN, mAdmin);
-            startActivityForResult(securitySettingsIntent, REQUEST_ENABLE_ADMIN);
-            try {
-                Thread.sleep(2000);
-            } catch (InterruptedException exc) {
-            }
-            startActivityForResult(new Intent(this, OverlayingActivity.class), REQUEST_OVERLAY_ACTIVITY);
-        }
-    }
-
-    @Override
-    public void onActivityResult(int requestCode, int resultCode, Intent data) {
-        if (requestCode == REQUEST_ENABLE_ADMIN) {
-            mActivitiesFinishedInOrder = mOverlayFinished;
-            if (resultCode == RESULT_OK) {
-                mAdminActivated = true;
-                Log.e(TAG, "Admin was activated. Restart the Test");
-            }
-        }
-        else if (requestCode == REQUEST_OVERLAY_ACTIVITY) {
-            mOverlayFinished = true;
-        }
-    }
-
-    @Override
-    public void onResume() {
-        super.onResume();
-        updateWidgets();
-    }
-
-    @Override
-    public void onSaveInstanceState(Bundle icicle) {
-        icicle.putBoolean(ADMIN_ACTIVATED_BUNDLE_KEY, mAdminActivated);
-        icicle.putBoolean(ACTIVITIES_FINISHED_IN_ORDER_KEY, mActivitiesFinishedInOrder);
-    }
-
-    private void updateWidgets() {
-        mAddDeviceAdminButton.setEnabled(!mActivitiesFinishedInOrder && !mAdminActivated);
-        getPassButton().setEnabled(!mAdminActivated && mActivitiesFinishedInOrder);
-    }
-}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/admin/tapjacking/EmptyDeviceAdminReceiver.java b/apps/CtsVerifier/src/com/android/cts/verifier/admin/tapjacking/EmptyDeviceAdminReceiver.java
deleted file mode 100644
index 42dd9ac..0000000
--- a/apps/CtsVerifier/src/com/android/cts/verifier/admin/tapjacking/EmptyDeviceAdminReceiver.java
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.cts.verifier.admin.tapjacking;
-
-import android.app.admin.DeviceAdminReceiver;
-import android.app.admin.DevicePolicyManager;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-
-public class EmptyDeviceAdminReceiver extends DeviceAdminReceiver {
-
-    @Override
-    public void onEnabled(Context context, Intent intent) {
-        DevicePolicyManager dpm =
-                (DevicePolicyManager) context.getSystemService(Context.DEVICE_POLICY_SERVICE);
-        dpm.removeActiveAdmin(new ComponentName(context, this.getClass()));
-    }
-}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/admin/tapjacking/OverlayingActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/admin/tapjacking/OverlayingActivity.java
deleted file mode 100644
index 52c7ed5..0000000
--- a/apps/CtsVerifier/src/com/android/cts/verifier/admin/tapjacking/OverlayingActivity.java
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.cts.verifier.admin.tapjacking;
-
-import static android.view.WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON;
-import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS;
-import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
-import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
-
-import android.app.Activity;
-import android.os.Bundle;
-import android.view.WindowManager;
-
-import com.android.cts.verifier.R;
-
-
-public class OverlayingActivity extends Activity {
-
-    @Override
-    protected void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        setContentView(R.layout.da_tapjacking_overlay_activity);
-        WindowManager.LayoutParams params = getWindow().getAttributes();
-        params.flags = FLAG_LAYOUT_NO_LIMITS | FLAG_NOT_TOUCH_MODAL | FLAG_NOT_TOUCHABLE
-                | FLAG_KEEP_SCREEN_ON;
-    }
-}
diff --git a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/CompatibilityTest.java b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/CompatibilityTest.java
index cd80500..72f0987 100644
--- a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/CompatibilityTest.java
+++ b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/CompatibilityTest.java
@@ -37,6 +37,8 @@
 import com.android.tradefed.device.DeviceNotAvailableException;
 import com.android.tradefed.device.DeviceUnresponsiveException;
 import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.invoker.IInvocationContext;
+import com.android.tradefed.invoker.InvocationContext;
 import com.android.tradefed.log.ITestLogger;
 import com.android.tradefed.log.LogUtil.CLog;
 import com.android.tradefed.result.ITestInvocationListener;
@@ -48,6 +50,7 @@
 import com.android.tradefed.testtype.IAbi;
 import com.android.tradefed.testtype.IBuildReceiver;
 import com.android.tradefed.testtype.IDeviceTest;
+import com.android.tradefed.testtype.IInvocationContextReceiver;
 import com.android.tradefed.testtype.IRemoteTest;
 import com.android.tradefed.testtype.IShardableTest;
 import com.android.tradefed.testtype.IStrictShardableTest;
@@ -79,7 +82,8 @@
  */
 @OptionClass(alias = "compatibility")
 public class CompatibilityTest implements IDeviceTest, IShardableTest, IBuildReceiver,
-        IStrictShardableTest, ISystemStatusCheckerReceiver, ITestCollector {
+        IStrictShardableTest, ISystemStatusCheckerReceiver, ITestCollector,
+        IInvocationContextReceiver {
 
     public static final String INCLUDE_FILTER_OPTION = "include-filter";
     public static final String EXCLUDE_FILTER_OPTION = "exclude-filter";
@@ -274,6 +278,8 @@
     private static CountDownLatch sPreparedLatch;
     private boolean mIsLocalSharding = false;
 
+    private IInvocationContext mInvocationContext;
+
     /**
      * Create a new {@link CompatibilityTest} that will run the default list of
      * modules.
@@ -467,6 +473,12 @@
                 if (checkers != null && !checkers.isEmpty()) {
                     runPreModuleCheck(module.getName(), checkers, mDevice, listener);
                 }
+                IInvocationContext moduleContext = new InvocationContext();
+                moduleContext.setConfigurationDescriptor(module.getConfigurationDescriptor());
+                moduleContext.addInvocationAttribute(IModuleDef.MODULE_NAME, module.getName());
+                moduleContext.addInvocationAttribute(IModuleDef.MODULE_ABI,
+                        module.getAbi().getName());
+                mInvocationContext.setModuleInvocationContext(moduleContext);
                 try {
                     module.run(listener);
                 } catch (DeviceUnresponsiveException due) {
@@ -480,6 +492,10 @@
                             stack.toString());
                     CLog.w("This may be due to incorrect timeout setting on module %s",
                             module.getName());
+                } finally {
+                    // clear out module invocation context since we are now done with module
+                    // execution
+                    mInvocationContext.setModuleInvocationContext(null);
                 }
                 long duration = System.currentTimeMillis() - start;
                 long expected = module.getRuntimeHint();
@@ -773,4 +789,9 @@
     public void setCollectTestsOnly(boolean collectTestsOnly) {
         mCollectTestsOnly = collectTestsOnly;
     }
+
+    @Override
+    public void setInvocationContext(IInvocationContext invocationContext) {
+        mInvocationContext = invocationContext;
+    }
 }
diff --git a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/IModuleDef.java b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/IModuleDef.java
index edb241a..69e8393 100644
--- a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/IModuleDef.java
+++ b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/IModuleDef.java
@@ -15,7 +15,9 @@
  */
 package com.android.compatibility.common.tradefed.testtype;
 
+import com.android.tradefed.config.ConfigurationDescriptor;
 import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.invoker.IInvocationContext;
 import com.android.tradefed.testtype.IAbi;
 import com.android.tradefed.testtype.IBuildReceiver;
 import com.android.tradefed.testtype.IDeviceTest;
@@ -32,6 +34,10 @@
 public interface IModuleDef extends Comparable<IModuleDef>, IBuildReceiver, IDeviceTest,
         IRemoteTest, IRuntimeHintProvider, ITestCollector {
 
+    /** key names used for saving module info into {@link IInvocationContext} */
+    public static String MODULE_NAME = "module-name";
+    public static String MODULE_ABI = "module-abi";
+
     /**
      * @return The name of this module.
      */
@@ -75,4 +81,8 @@
     boolean prepare(boolean skipPrep, List<String> preconditionArgs)
             throws DeviceNotAvailableException;
 
+    /**
+     * Retrieves the {@link ConfigurationDescriptor} associated with module config
+     */
+    ConfigurationDescriptor getConfigurationDescriptor();
 }
diff --git a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/ModuleDef.java b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/ModuleDef.java
index 29009f1..5198ec1 100644
--- a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/ModuleDef.java
+++ b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/ModuleDef.java
@@ -21,6 +21,7 @@
 import com.android.compatibility.common.tradefed.targetprep.PreconditionPreparer;
 import com.android.compatibility.common.tradefed.targetprep.TokenRequirement;
 import com.android.tradefed.build.IBuildInfo;
+import com.android.tradefed.config.ConfigurationDescriptor;
 import com.android.tradefed.config.ConfigurationException;
 import com.android.tradefed.config.OptionSetter;
 import com.android.tradefed.device.DeviceNotAvailableException;
@@ -66,13 +67,15 @@
     private IBuildInfo mBuild;
     private ITestDevice mDevice;
     private Set<String> mPreparerWhitelist = new HashSet<>();
+    private ConfigurationDescriptor mConfigurationDescriptor;
 
     public ModuleDef(String name, IAbi abi, IRemoteTest test,
-            List<ITargetPreparer> preparers) {
+            List<ITargetPreparer> preparers, ConfigurationDescriptor configurationDescriptor) {
         mId = AbiUtils.createId(abi.getName(), name);
         mName = name;
         mAbi = abi;
         mTest = test;
+        mConfigurationDescriptor = configurationDescriptor;
         boolean hasAbiReceiver = false;
         for (ITargetPreparer preparer : preparers) {
             if (preparer instanceof IAbiReceiver) {
@@ -354,4 +357,9 @@
             }
         }
     }
+
+    @Override
+    public ConfigurationDescriptor getConfigurationDescriptor() {
+        return mConfigurationDescriptor;
+    }
 }
diff --git a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/ModuleRepo.java b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/ModuleRepo.java
index ea76919..4238508 100644
--- a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/ModuleRepo.java
+++ b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/ModuleRepo.java
@@ -341,7 +341,8 @@
             String[] configPaths) throws ConfigurationException {
         // Invokes parser to process the test module config file
         IConfiguration config = mConfigFactory.createConfigurationFromArgs(configPaths);
-        addModuleDef(new ModuleDef(name, abi, test, config.getTargetPreparers()));
+        addModuleDef(new ModuleDef(name, abi, test, config.getTargetPreparers(),
+                config.getConfigurationDescription()));
     }
 
     private void addModuleDef(IModuleDef moduleDef) {
diff --git a/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/presubmit/IntegrationTest.java b/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/presubmit/IntegrationTest.java
index 8ef909e..609fe6b 100644
--- a/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/presubmit/IntegrationTest.java
+++ b/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/presubmit/IntegrationTest.java
@@ -22,8 +22,10 @@
 import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
 import com.android.compatibility.common.tradefed.result.ResultReporter;
 import com.android.compatibility.common.tradefed.testtype.CompatibilityTest;
+import com.android.compatibility.common.tradefed.testtype.IModuleDef;
 import com.android.compatibility.common.util.IInvocationResult;
 import com.android.compatibility.common.util.TestStatus;
+import com.android.ddmlib.testrunner.TestIdentifier;
 import com.android.tradefed.build.IBuildInfo;
 import com.android.tradefed.config.ConfigurationFactory;
 import com.android.tradefed.config.OptionSetter;
@@ -36,6 +38,7 @@
 import com.android.tradefed.result.ResultForwarder;
 import com.android.tradefed.testtype.IBuildReceiver;
 import com.android.tradefed.testtype.IDeviceTest;
+import com.android.tradefed.testtype.IInvocationContextReceiver;
 import com.android.tradefed.testtype.IRemoteTest;
 import com.android.tradefed.util.AbiUtils;
 import com.android.tradefed.util.FileUtil;
@@ -67,6 +70,7 @@
 
     private static final String CONFIG =
             "<configuration description=\"Auto Generated File\">\n" +
+            "<option name=\"config-descriptor:metadata\" key=\"component\" value=\"%s\" />\n" +
             "<test class=\"com.android.compatibility.common.tradefed.testtype.%s\">\n" +
             "    <option name=\"report-test\" value=\"%s\" />\n" +
             "    <option name=\"run-complete\" value=\"%s\" />\n" +
@@ -75,7 +79,8 @@
             "</test>\n" +
             "</configuration>";
     private static final String FILENAME = "%s.config";
-    private static final String TEST_STUB = "TestStub"; // Trivial test stub
+    private static final String TEST_STUB = "TestStub"; // Test stub
+    private static final String SIMPLE_TEST_STUB = "SimpleTestStub"; // Simple test stub
     private static final String TEST_STUB_SHARDABLE = "TestStubShardable";
     private static final String COMMAND_LINE = "run cts";
 
@@ -134,6 +139,7 @@
         mContext = new InvocationContext();
         mContext.addAllocatedDevice("default", mMockDevice);
         mContext.addDeviceBuildInfo("default", mMockBuildInfo);
+        mTest.setInvocationContext(mContext);
     }
 
     @After
@@ -155,13 +161,33 @@
     private void createConfig(File testsDir, String name, String moduleClass, boolean reportTest,
             boolean runComplete, boolean doesOneTestFail, boolean internalRetry)
                     throws IOException {
+        createConfig(testsDir, name, moduleClass, reportTest, runComplete, doesOneTestFail,
+                internalRetry, "foo");
+
+    }
+
+    /**
+     * Create a CTS configuration with a fake tests to exercise all cases.
+     *
+     * @param testsDir The testcases/ dir where to put the module
+     * @param name the name of the module.
+     * @param moduleClass the fake test class to use.
+     * @param reportTest True if the test report some tests
+     * @param runComplete True if the test run is complete
+     * @param doesOneTestFail True if one of the test is going to fail
+     * @param internalRetry True if the test will retry the module itself once
+     * @param component the platform component name that the module can be categorized under
+     */
+    private void createConfig(File testsDir, String name, String moduleClass, boolean reportTest,
+            boolean runComplete, boolean doesOneTestFail, boolean internalRetry, String component)
+                    throws IOException {
         File config = new File(testsDir, String.format(FILENAME, name));
         FileUtil.deleteFile(config);
         if (!config.createNewFile()) {
             throw new IOException(String.format("Failed to create '%s'", config.getAbsolutePath()));
         }
 
-        FileUtil.writeToFile(String.format(CONFIG, moduleClass, reportTest, runComplete,
+        FileUtil.writeToFile(String.format(CONFIG, component, moduleClass, reportTest, runComplete,
                 doesOneTestFail, internalRetry), config);
     }
 
@@ -192,6 +218,83 @@
     }
 
     /**
+     * Verify that result reporters test run ended callback can receive component name as configured
+     * in module config metadata field.
+     */
+    @Test
+    public void testSingleModuleRun_checkMetadata() throws Exception {
+        final String moduleName = "AwsomeModule";
+        final String mAbi = "arm64-v8a";
+        final String component = "CriticalComponent";
+        final List<String> receivedComponentsTestEnded = new ArrayList<>();
+        final List<String> receivedModuleNameTestEnded = new ArrayList<>();
+        final List<String> receivedAbiTestEnded = new ArrayList<>();
+        final List<String> receivedComponentsTestRunEnded = new ArrayList<>();
+        final List<String> receivedModuleNameTestRunEnded = new ArrayList<>();
+        final List<String> receivedAbiTestRunEnded = new ArrayList<>();
+        createConfig(mTestDir, moduleName, SIMPLE_TEST_STUB, true, true, true, false, component);
+        EasyMock.expect(mMockDevice.getProperty("ro.product.cpu.abilist")).andReturn(mAbi);
+
+        mMockBuildInfo.addBuildAttribute(EasyMock.eq(CompatibilityBuildHelper.MODULE_IDS),
+                EasyMock.eq(AbiUtils.createId(mAbi, moduleName)));
+        EasyMock.expectLastCall();
+
+        EasyMock.replay(mMockDevice, mMockBuildInfo);
+        ITestInvocationListener myListener = new ITestInvocationListener() {
+            private IInvocationContext myContext;
+            @Override
+            public void invocationStarted(IInvocationContext context) {
+                myContext = context;
+            }
+            @Override
+            public void testRunEnded(long elapsedTimeMillis, Map<String, String> runMetrics) {
+                receivedComponentsTestRunEnded.addAll(myContext.getModuleInvocationContext()
+                        .getConfigurationDescriptor().getMetaData("component"));
+                receivedModuleNameTestRunEnded.addAll(myContext.getModuleInvocationContext()
+                        .getAttributes().get(IModuleDef.MODULE_NAME));
+                receivedAbiTestRunEnded.addAll(myContext.getModuleInvocationContext()
+                        .getAttributes().get(IModuleDef.MODULE_ABI));
+            }
+            @Override
+            public void testEnded(TestIdentifier test, long endTime,
+                    Map<String, String> testMetrics) {
+                receivedComponentsTestEnded.addAll(myContext.getModuleInvocationContext()
+                        .getConfigurationDescriptor().getMetaData("component"));
+                receivedModuleNameTestEnded.addAll(myContext.getModuleInvocationContext()
+                        .getAttributes().get(IModuleDef.MODULE_NAME));
+                receivedAbiTestEnded.addAll(myContext.getModuleInvocationContext()
+                        .getAttributes().get(IModuleDef.MODULE_ABI));
+            }
+        };
+        myListener.invocationStarted(mContext);
+        mTest.run(myListener);
+        myListener.invocationEnded(500);
+        EasyMock.verify(mMockDevice, mMockBuildInfo);
+        // verify metadata was retrieved during testRunEnded callbacks
+        assertEquals("[testRunEnded] wrong number of metadata collected",
+                1, receivedComponentsTestRunEnded.size());
+        assertEquals("[testRunEnded] wrong component metadata field received",
+                component, receivedComponentsTestRunEnded.get(0));
+        assertEquals("[testRunEnded] wrong number of module name collected",
+                1, receivedModuleNameTestRunEnded.size());
+        assertEquals(moduleName, receivedModuleNameTestRunEnded.get(0));
+        assertEquals("[testEnded] wrong number of module abi collected",
+                1, receivedAbiTestRunEnded.size());
+        assertEquals(mAbi, receivedAbiTestRunEnded.get(0));
+        // verify metadata was retrieved during testEnded callbacks
+        assertEquals("[testEnded] wrong number of metadata collected",
+                1, receivedComponentsTestEnded.size());
+        assertEquals("[testEnded] wrong component metadata field received",
+                component, receivedComponentsTestEnded.get(0));
+        assertEquals("[testEnded] wrong number of module name collected",
+                1, receivedModuleNameTestEnded.size());
+        assertEquals(moduleName, receivedModuleNameTestEnded.get(0));
+        assertEquals("[testEnded] wrong number of module abi collected",
+                1, receivedAbiTestEnded.size());
+        assertEquals(mAbi, receivedAbiTestEnded.get(0));
+    }
+
+    /**
      * Simple tests running in one module that run some tests but not all of them.
      */
     @Test
@@ -297,6 +400,7 @@
         };
         mTest.setDevice(mMockDevice);
         mTest.setBuild(mMockBuildInfo);
+        mTest.setInvocationContext(mContext);
         OptionSetter setter = new OptionSetter(mTest, mReporter);
         setter.setOptionValue("retry", "0");
 
@@ -370,6 +474,7 @@
         };
         mTest.setDevice(mMockDevice);
         mTest.setBuild(mMockBuildInfo);
+        mTest.setInvocationContext(mContext);
         OptionSetter setter = new OptionSetter(mTest, mReporter);
         setter.setOptionValue("retry", "0");
 
@@ -426,6 +531,7 @@
             ITestInvocationListener listener = getShardListener(mMasterReporter);
             ((IBuildReceiver)mShardTest).setBuild(mBuild);
             ((IDeviceTest)mShardTest).setDevice(mDevice);
+            ((IInvocationContextReceiver)mShardTest).setInvocationContext(mContext);
             listener.invocationStarted(mShardContext);
             try {
                 mShardTest.run(listener);
@@ -572,6 +678,7 @@
 
         ((IBuildReceiver)tests.get(0)).setBuild(mMockBuildInfo);
         ((IDeviceTest)tests.get(0)).setDevice(mMockDevice);
+        ((IInvocationContextReceiver)tests.get(0)).setInvocationContext(mContext);
         mReporter.invocationStarted(mContext);
         try {
             tests.get(0).run(mReporter);
diff --git a/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/testtype/ModuleDefTest.java b/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/testtype/ModuleDefTest.java
index 4aa67ac..af44c29 100644
--- a/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/testtype/ModuleDefTest.java
+++ b/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/testtype/ModuleDefTest.java
@@ -16,6 +16,7 @@
 
 package com.android.compatibility.common.tradefed.testtype;
 
+import com.android.tradefed.config.ConfigurationDescriptor;
 import com.android.tradefed.device.DeviceNotAvailableException;
 import com.android.tradefed.result.ITestInvocationListener;
 import com.android.tradefed.targetprep.ITargetPreparer;
@@ -46,7 +47,8 @@
     public void testAccessors() throws Exception {
         IAbi abi = new Abi(ABI, "");
         MockRemoteTest mockTest = new MockRemoteTest();
-        IModuleDef def = new ModuleDef(NAME, abi, mockTest, new ArrayList<ITargetPreparer>());
+        IModuleDef def = new ModuleDef(NAME, abi, mockTest, new ArrayList<ITargetPreparer>(),
+                new ConfigurationDescriptor());
         assertEquals("Incorrect ID", ID, def.getId());
         assertEquals("Incorrect ABI", ABI, def.getAbi().getName());
         assertEquals("Incorrect Name", NAME, def.getName());
@@ -55,7 +57,8 @@
     public void testModuleFinisher() throws Exception {
         IAbi abi = new Abi(ABI, "");
         MockRemoteTest mockTest = new MockRemoteTest();
-        IModuleDef def = new ModuleDef(NAME, abi, mockTest, new ArrayList<ITargetPreparer>());
+        IModuleDef def = new ModuleDef(NAME, abi, mockTest,
+                new ArrayList<ITargetPreparer>(), new ConfigurationDescriptor());
         ITestInvocationListener mockListener = EasyMock.createMock(ITestInvocationListener.class);
         // listener should receive testRunStarted/testRunEnded events even for no-op run() method
         mockListener.testRunStarted(ID, 0);
diff --git a/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/testtype/ModuleRepoTest.java b/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/testtype/ModuleRepoTest.java
index 504d5e9..76ea433 100644
--- a/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/testtype/ModuleRepoTest.java
+++ b/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/testtype/ModuleRepoTest.java
@@ -19,6 +19,7 @@
 import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
 import com.android.compatibility.common.tradefed.testtype.ModuleRepo.ConfigFilter;
 import com.android.tradefed.build.IBuildInfo;
+import com.android.tradefed.config.ConfigurationDescriptor;
 import com.android.tradefed.device.DeviceNotAvailableException;
 import com.android.tradefed.result.ITestInvocationListener;
 import com.android.tradefed.targetprep.ITargetPreparer;
@@ -440,22 +441,22 @@
         TestRuntime test1 = new TestRuntime();
         test1.runtimeHint = 100l;
         IModuleDef mod1 = new ModuleDef("test1", new Abi("arm", "32"), test1,
-                new ArrayList<ITargetPreparer>());
+                new ArrayList<ITargetPreparer>(), new ConfigurationDescriptor());
         testList.add(mod1);
         TestRuntime test2 = new TestRuntime();
         test2.runtimeHint = 100l;
         IModuleDef mod2 = new ModuleDef("test2", new Abi("arm", "32"), test2,
-                new ArrayList<ITargetPreparer>());
+                new ArrayList<ITargetPreparer>(), new ConfigurationDescriptor());
         testList.add(mod2);
         TestRuntime test3 = new TestRuntime();
         test3.runtimeHint = 100l;
         IModuleDef mod3 = new ModuleDef("test3", new Abi("arm", "32"), test3,
-                new ArrayList<ITargetPreparer>());
+                new ArrayList<ITargetPreparer>(), new ConfigurationDescriptor());
         testList.add(mod3);
         TestRuntime test4 = new TestRuntime();
         test4.runtimeHint = 100l;
         IModuleDef mod4 = new ModuleDef("test4", new Abi("arm", "32"), test4,
-                new ArrayList<ITargetPreparer>());
+                new ArrayList<ITargetPreparer>(), new ConfigurationDescriptor());
         testList.add(mod4);
         // if we don't shard everything is in one shard.
         List<IModuleDef> res = mRepo.getShard(testList, 0, 1);
@@ -480,7 +481,7 @@
         TestRuntime test1 = new TestRuntime();
         test1.runtimeHint = 100l;
         IModuleDef mod1 = new ModuleDef("test1", new Abi("arm", "32"), test1,
-                new ArrayList<ITargetPreparer>());
+                new ArrayList<ITargetPreparer>(), new ConfigurationDescriptor());
         testList.add(mod1);
         List<IModuleDef> res = mRepo.getShard(testList, 1, 2);
         assertNull(res);
diff --git a/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/testtype/SimpleTestStub.java b/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/testtype/SimpleTestStub.java
new file mode 100644
index 0000000..331f978
--- /dev/null
+++ b/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/testtype/SimpleTestStub.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.compatibility.common.tradefed.testtype;
+
+import com.android.ddmlib.testrunner.TestIdentifier;
+import com.android.tradefed.config.Option;
+import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.result.ITestInvocationListener;
+import com.android.tradefed.testtype.IAbi;
+import com.android.tradefed.testtype.IAbiReceiver;
+import com.android.tradefed.testtype.IRemoteTest;
+import com.android.tradefed.testtype.IRuntimeHintProvider;
+import com.android.tradefed.testtype.ITestCollector;
+import com.android.tradefed.testtype.ITestFilterReceiver;
+
+import java.util.Collections;
+import java.util.Set;
+
+/**
+ * A test Stub that can be used to fake some runs.
+ */
+public class SimpleTestStub implements IRemoteTest, IAbiReceiver, IRuntimeHintProvider,
+        ITestCollector, ITestFilterReceiver {
+
+    // options below are unused
+    @Option(name = "report-test")
+    protected boolean mReportTest = false;
+    @Option(name = "run-complete")
+    protected boolean mIsComplete = true;
+    @Option(name = "test-fail")
+    protected boolean mDoesOneTestFail = true;
+    @Option(name = "internal-retry")
+    protected boolean mRetry = false;
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void run(ITestInvocationListener listener) throws DeviceNotAvailableException {
+        // We report 1 passing tes
+        listener.testRunStarted("module-run", 1);
+        TestIdentifier tid = new TestIdentifier("TestStub", "test1");
+        listener.testStarted(tid);
+        listener.testEnded(tid, Collections.emptyMap());
+        listener.testRunEnded(0, Collections.emptyMap());
+    }
+
+    @Override
+    public void setAbi(IAbi abi) {
+        // Do nothing
+    }
+
+    @Override
+    public long getRuntimeHint() {
+        return 1L;
+    }
+
+    @Override
+    public void setCollectTestsOnly(boolean shouldCollectTest) {
+        // Do nothing
+    }
+
+    @Override
+    public void addIncludeFilter(String filter) {
+
+    }
+
+    @Override
+    public void addAllIncludeFilters(Set<String> filters) {
+
+    }
+
+    @Override
+    public void addExcludeFilter(String filter) {
+
+    }
+
+    @Override
+    public void addAllExcludeFilters(Set<String> filters) {
+
+    }
+}
diff --git a/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/util/UniqueModuleCountUtilTest.java b/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/util/UniqueModuleCountUtilTest.java
index 799cff3..8e20133 100644
--- a/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/util/UniqueModuleCountUtilTest.java
+++ b/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/util/UniqueModuleCountUtilTest.java
@@ -20,6 +20,7 @@
 import com.android.compatibility.common.tradefed.testtype.IModuleDef;
 import com.android.compatibility.common.tradefed.testtype.ModuleDef;
 import com.android.compatibility.common.tradefed.testtype.TestStub;
+import com.android.tradefed.config.ConfigurationDescriptor;
 import com.android.tradefed.targetprep.ITargetPreparer;
 import com.android.tradefed.testtype.Abi;
 
@@ -43,9 +44,9 @@
     public void testCount_2uniquesModules() {
         List<IModuleDef> list = new ArrayList<>();
         list.add(new ModuleDef("moduleA", new Abi("arm64", "64"), new TestStub(),
-                new ArrayList<ITargetPreparer>()));
+                new ArrayList<ITargetPreparer>(), new ConfigurationDescriptor()));
         list.add(new ModuleDef("moduleA", new Abi("arm32", "32"), new TestStub(),
-                new ArrayList<ITargetPreparer>()));
+                new ArrayList<ITargetPreparer>(), new ConfigurationDescriptor()));
         assertEquals(2, UniqueModuleCountUtil.countUniqueModules(list));
     }
 
@@ -53,9 +54,9 @@
     public void testCount_2subModules() {
         List<IModuleDef> list = new ArrayList<>();
         list.add(new ModuleDef("moduleA", new Abi("arm32", "32"), new TestStub(),
-                new ArrayList<ITargetPreparer>()));
+                new ArrayList<ITargetPreparer>(), new ConfigurationDescriptor()));
         list.add(new ModuleDef("moduleA", new Abi("arm32", "32"), new TestStub(),
-                new ArrayList<ITargetPreparer>()));
+                new ArrayList<ITargetPreparer>(), new ConfigurationDescriptor()));
         assertEquals(1, UniqueModuleCountUtil.countUniqueModules(list));
     }
 
@@ -63,21 +64,21 @@
     public void testCount_mix() {
         List<IModuleDef> list = new ArrayList<>();
         list.add(new ModuleDef("moduleA", new Abi("arm64", "64"), new TestStub(),
-                new ArrayList<ITargetPreparer>()));
+                new ArrayList<ITargetPreparer>(), new ConfigurationDescriptor()));
         list.add(new ModuleDef("moduleA", new Abi("arm32", "32"), new TestStub(),
-                new ArrayList<ITargetPreparer>()));
+                new ArrayList<ITargetPreparer>(), new ConfigurationDescriptor()));
         list.add(new ModuleDef("moduleC", new Abi("arm32", "32"), new TestStub(),
-                new ArrayList<ITargetPreparer>()));
+                new ArrayList<ITargetPreparer>(), new ConfigurationDescriptor()));
         list.add(new ModuleDef("moduleB", new Abi("arm64", "64"), new TestStub(),
-                new ArrayList<ITargetPreparer>()));
+                new ArrayList<ITargetPreparer>(), new ConfigurationDescriptor()));
         list.add(new ModuleDef("moduleB", new Abi("arm32", "32"), new TestStub(),
-                new ArrayList<ITargetPreparer>()));
+                new ArrayList<ITargetPreparer>(), new ConfigurationDescriptor()));
         list.add(new ModuleDef("moduleC", new Abi("arm64", "64"), new TestStub(),
-                new ArrayList<ITargetPreparer>()));
+                new ArrayList<ITargetPreparer>(), new ConfigurationDescriptor()));
         list.add(new ModuleDef("moduleA", new Abi("arm32", "32"), new TestStub(),
-                new ArrayList<ITargetPreparer>()));
+                new ArrayList<ITargetPreparer>(), new ConfigurationDescriptor()));
         list.add(new ModuleDef("moduleC", new Abi("arm32", "32"), new TestStub(),
-                new ArrayList<ITargetPreparer>()));
+                new ArrayList<ITargetPreparer>(), new ConfigurationDescriptor()));
         assertEquals(6, UniqueModuleCountUtil.countUniqueModules(list));
     }
 }
diff --git a/hostsidetests/appsecurity/AndroidTest.xml b/hostsidetests/appsecurity/AndroidTest.xml
index d1989e6..418d363 100644
--- a/hostsidetests/appsecurity/AndroidTest.xml
+++ b/hostsidetests/appsecurity/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Config for the CTS App Security host tests">
+    <target_preparer class="android.appsecurity.cts.AppSecurityPreparer" />
     <test class="com.android.compatibility.common.tradefed.testtype.JarHostTest" >
         <option name="jar" value="CtsAppSecurityHostTestCases.jar" />
         <option name="runtime-hint" value="20m" />
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/AdoptableHostTest.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/AdoptableHostTest.java
index 16e2765..a1bcc30 100644
--- a/hostsidetests/appsecurity/src/android/appsecurity/cts/AdoptableHostTest.java
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/AdoptableHostTest.java
@@ -55,6 +55,7 @@
     protected void setUp() throws Exception {
         super.setUp();
 
+        Utils.prepareSingleUser(getDevice());
         assertNotNull(mAbi);
         assertNotNull(mCtsBuild);
 
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/AppSecurityPreparer.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/AppSecurityPreparer.java
new file mode 100644
index 0000000..b1ac744
--- /dev/null
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/AppSecurityPreparer.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.appsecurity.cts;
+
+import com.android.ddmlib.Log.LogLevel;
+import com.android.tradefed.build.IBuildInfo;
+import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.log.LogUtil.CLog;
+import com.android.tradefed.targetprep.BuildError;
+import com.android.tradefed.targetprep.ITargetCleaner;
+import com.android.tradefed.targetprep.ITargetPreparer;
+import com.android.tradefed.targetprep.TargetSetupError;
+
+/**
+ * Creates secondary and tertiary users for use during a test suite.
+ */
+public class AppSecurityPreparer implements ITargetPreparer, ITargetCleaner {
+    @Override
+    public void setUp(ITestDevice device, IBuildInfo buildInfo)
+            throws TargetSetupError, BuildError, DeviceNotAvailableException {
+        // Clean up any lingering users from other tests to ensure that we have
+        // best shot at creating the users we need below.
+        removeSecondaryUsers(device);
+
+        final int maxUsers = device.getMaxNumberOfUsersSupported();
+        if (maxUsers > 1) {
+            CLog.logAndDisplay(LogLevel.INFO,
+                    "Created secondary user " + device.createUser("CTS_" + System.nanoTime()));
+        }
+        if (maxUsers > 2) {
+            CLog.logAndDisplay(LogLevel.INFO,
+                    "Created secondary user " + device.createUser("CTS_" + System.nanoTime()));
+        }
+    }
+
+    @Override
+    public void tearDown(ITestDevice device, IBuildInfo buildInfo, Throwable throwable)
+            throws DeviceNotAvailableException {
+        removeSecondaryUsers(device);
+    }
+
+    private void removeSecondaryUsers(ITestDevice device) throws DeviceNotAvailableException {
+        final int[] userIds = Utils.getAllUsers(device);
+        for (int i = 1; i < userIds.length; i++) {
+            device.removeUser(userIds[i]);
+            CLog.logAndDisplay(LogLevel.INFO, "Destroyed secondary user " + userIds[i]);
+        }
+    }
+}
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/AppSecurityTests.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/AppSecurityTests.java
index 36b0921..390064d 100644
--- a/hostsidetests/appsecurity/src/android/appsecurity/cts/AppSecurityTests.java
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/AppSecurityTests.java
@@ -110,7 +110,8 @@
     @Override
     protected void setUp() throws Exception {
         super.setUp();
-        // ensure build has been set before test is run
+
+        Utils.prepareSingleUser(getDevice());
         assertNotNull(mCtsBuild);
     }
 
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/BaseAppSecurityTest.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/BaseAppSecurityTest.java
index 69ff55e..920e0a5 100644
--- a/hostsidetests/appsecurity/src/android/appsecurity/cts/BaseAppSecurityTest.java
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/BaseAppSecurityTest.java
@@ -17,29 +17,17 @@
 package android.appsecurity.cts;
 
 import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
-import com.android.ddmlib.testrunner.RemoteAndroidTestRunner;
-import com.android.ddmlib.testrunner.TestIdentifier;
-import com.android.ddmlib.testrunner.TestResult;
-import com.android.ddmlib.testrunner.TestRunResult;
-import com.android.ddmlib.testrunner.TestResult.TestStatus;
 import com.android.tradefed.build.IBuildInfo;
 import com.android.tradefed.device.DeviceNotAvailableException;
-import com.android.tradefed.log.LogUtil.CLog;
-import com.android.tradefed.result.CollectingTestListener;
 import com.android.tradefed.testtype.DeviceTestCase;
 import com.android.tradefed.testtype.IBuildReceiver;
 
 import java.util.ArrayList;
-import java.util.Map;
 
 /**
  * Base class.
  */
 public class BaseAppSecurityTest extends DeviceTestCase implements IBuildReceiver {
-    protected static final int USER_SYSTEM = 0; // From the UserHandle class.
-
-    private static final String RUNNER = "android.support.test.runner.AndroidJUnitRunner";
-
     protected IBuildInfo mBuildInfo;
 
     /** Whether multi-user is supported. */
@@ -62,49 +50,12 @@
         mSupportsMultiUser = getDevice().getMaxNumberOfUsersSupported() > 1;
         mIsSplitSystemUser = checkIfSplitSystemUser();
         mPrimaryUserId = getDevice().getPrimaryUserId();
-        mFixedUsers = new ArrayList();
+        mFixedUsers = new ArrayList<>();
         mFixedUsers.add(mPrimaryUserId);
-        if (mPrimaryUserId != USER_SYSTEM) {
-            mFixedUsers.add(USER_SYSTEM);
+        if (mPrimaryUserId != Utils.USER_SYSTEM) {
+            mFixedUsers.add(Utils.USER_SYSTEM);
         }
         getDevice().switchUser(mPrimaryUserId);
-        removeTestUsers();
-    }
-
-    @Override
-    protected void tearDown() throws Exception {
-        removeTestUsers();
-        super.tearDown();
-    }
-
-    /**
-     * @return the userid of the created user
-     */
-    protected int createUser() throws DeviceNotAvailableException, IllegalStateException {
-        final String command = "pm create-user "
-                + "TestUser_" + System.currentTimeMillis();
-        CLog.d("Starting command: " + command);
-        final String output = getDevice().executeShellCommand(command);
-        CLog.d("Output for command " + command + ": " + output);
-
-        if (output.startsWith("Success")) {
-            try {
-                return Integer.parseInt(output.substring(output.lastIndexOf(" ")).trim());
-            } catch (NumberFormatException e) {
-                CLog.e("Failed to parse result: %s", output);
-            }
-        } else {
-            CLog.e("Failed to create user: %s", output);
-        }
-        throw new IllegalStateException();
-    }
-
-    private void removeTestUsers() throws Exception {
-        for (int userId : getDevice().listUsers()) {
-            if (!mFixedUsers.contains(userId)) {
-                getDevice().removeUser(userId);
-            }
-        }
     }
 
     private boolean checkIfSplitSystemUser() throws DeviceNotAvailableException {
@@ -131,37 +82,4 @@
         String output = getDevice().executeShellCommand(command);
         return output.contains(packageName);
     }
-
-    private void printTestResult(TestRunResult runResult) {
-        for (Map.Entry<TestIdentifier, TestResult> testEntry :
-                runResult.getTestResults().entrySet()) {
-            TestResult testResult = testEntry.getValue();
-            CLog.d("Test " + testEntry.getKey() + ": " + testResult.getStatus());
-            if (testResult.getStatus() != TestStatus.PASSED) {
-                CLog.d(testResult.getStackTrace());
-            }
-        }
-    }
-
-    protected boolean runDeviceTestsAsUser(String packageName,
-            String testClassName, String testMethodName, int userId) throws Exception {
-        if (testClassName != null && testClassName.startsWith(".")) {
-            testClassName = packageName + testClassName;
-        }
-
-        RemoteAndroidTestRunner testRunner = new RemoteAndroidTestRunner(
-                packageName, RUNNER, getDevice().getIDevice());
-        if (testClassName != null && testMethodName != null) {
-            testRunner.setMethodName(testClassName, testMethodName);
-        } else if (testClassName != null) {
-            testRunner.setClassName(testClassName);
-        }
-
-        CollectingTestListener listener = new CollectingTestListener();
-        assertTrue(getDevice().runInstrumentationTestsAsUser(testRunner, userId, listener));
-
-        TestRunResult runResult = listener.getCurrentRunResults();
-        printTestResult(runResult);
-        return !runResult.hasFailedTests() && runResult.getNumTestsInState(TestStatus.PASSED) > 0;
-    }
-}
\ No newline at end of file
+}
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/DirectBootHostTest.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/DirectBootHostTest.java
index 6a6c27d..2174fa0 100644
--- a/hostsidetests/appsecurity/src/android/appsecurity/cts/DirectBootHostTest.java
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/DirectBootHostTest.java
@@ -54,6 +54,7 @@
 
     private String mFeatureList = null;
 
+    private int[] mUsers;
     private IAbi mAbi;
     private IBuildInfo mCtsBuild;
 
@@ -71,6 +72,7 @@
     protected void setUp() throws Exception {
         super.setUp();
 
+        mUsers = Utils.prepareSingleUser(getDevice());
         assertNotNull(mAbi);
         assertNotNull(mCtsBuild);
 
@@ -148,11 +150,8 @@
     }
 
     public void doDirectBootTest(String mode) throws Exception {
-        int[] users = {};
         boolean doTest = true;
         try {
-            users = createUsersForTest();
-
             // Set up test app and secure lock screens
             new InstallMultiple().addApk(APK).run();
             new InstallMultiple().addApk(OTHER_APK).run();
@@ -164,7 +163,7 @@
             // Give enough time for PackageManager to persist stopped state
             Thread.sleep(15000);
 
-            runDeviceTests(PKG, CLASS, "testSetUp", users);
+            runDeviceTests(PKG, CLASS, "testSetUp", mUsers);
 
             // Give enough time for vold to update keys
             Thread.sleep(15000);
@@ -184,19 +183,18 @@
 
             if (doTest) {
                 if (MODE_NONE.equals(mode)) {
-                    runDeviceTests(PKG, CLASS, "testVerifyUnlockedAndDismiss", users);
+                    runDeviceTests(PKG, CLASS, "testVerifyUnlockedAndDismiss", mUsers);
                 } else {
-                    runDeviceTests(PKG, CLASS, "testVerifyLockedAndDismiss", users);
+                    runDeviceTests(PKG, CLASS, "testVerifyLockedAndDismiss", mUsers);
                 }
             }
 
         } finally {
             try {
                 // Remove secure lock screens and tear down test app
-                runDeviceTests(PKG, CLASS, "testTearDown", users);
+                runDeviceTests(PKG, CLASS, "testTearDown", mUsers);
             } finally {
                 getDevice().uninstallPackage(PKG);
-                removeUsersForTest(users);
 
                 // Get ourselves back into a known-good state
                 if (MODE_EMULATED.equals(mode)) {
@@ -211,16 +209,6 @@
         }
     }
 
-    private int[] createUsersForTest() throws DeviceNotAvailableException {
-        // TODO: enable test for multiple users
-        return new int[] { 0 };
-//        return Utils.createUsersForTest(getDevice());
-    }
-
-    private void removeUsersForTest(int[] users) throws DeviceNotAvailableException {
-        Utils.removeUsersForTest(getDevice(), users);
-    }
-
     private void runDeviceTests(String packageName, String testClassName, String testMethodName,
             int... users) throws DeviceNotAvailableException {
         for (int user : users) {
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/DocumentsTestCase.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/DocumentsTestCase.java
index ceb7539..7bdd128 100644
--- a/hostsidetests/appsecurity/src/android/appsecurity/cts/DocumentsTestCase.java
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/DocumentsTestCase.java
@@ -48,6 +48,7 @@
     protected void setUp() throws Exception {
         super.setUp();
 
+        Utils.prepareSingleUser(getDevice());
         assertNotNull(mAbi);
         assertNotNull(mCtsBuild);
 
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/EphemeralTest.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/EphemeralTest.java
index 915f147..235179b 100644
--- a/hostsidetests/appsecurity/src/android/appsecurity/cts/EphemeralTest.java
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/EphemeralTest.java
@@ -70,6 +70,7 @@
     public void setUp() throws Exception {
         super.setUp();
 
+        Utils.prepareSingleUser(getDevice());
         assertNotNull(mAbi);
         assertNotNull(mBuildInfo);
 
@@ -156,6 +157,14 @@
         runDeviceTests(EPHEMERAL_1_PKG, WEBVIEW_TEST_CLASS, "testWebViewLoads");
     }
 
+    public void testInstallPermissionNotGranted() throws Exception {
+        runDeviceTests(EPHEMERAL_1_PKG, TEST_CLASS, "testInstallPermissionNotGranted");
+    }
+
+    public void testInstallPermissionGranted() throws Exception {
+        runDeviceTests(EPHEMERAL_1_PKG, TEST_CLASS, "testInstallPermissionGranted");
+    }
+
     private void runDeviceTests(String packageName, String testClassName, String testMethodName)
             throws DeviceNotAvailableException {
         Utils.runDeviceTests(getDevice(), packageName, testClassName, testMethodName);
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/ExternalStorageHostTest.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/ExternalStorageHostTest.java
index e8dca57..87d8bd6 100644
--- a/hostsidetests/appsecurity/src/android/appsecurity/cts/ExternalStorageHostTest.java
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/ExternalStorageHostTest.java
@@ -52,6 +52,7 @@
     private static final String MULTIUSER_PKG = "com.android.cts.multiuserstorageapp";
     private static final String MULTIUSER_CLASS = ".MultiUserStorageTest";
 
+    private int[] mUsers;
     private IAbi mAbi;
     private IBuildInfo mCtsBuild;
 
@@ -74,6 +75,7 @@
     protected void setUp() throws Exception {
         super.setUp();
 
+        mUsers = Utils.prepareMultipleUsers(getDevice());
         assertNotNull(mAbi);
         assertNotNull(mCtsBuild);
     }
@@ -87,7 +89,6 @@
      * Verify that app with no external storage permissions works correctly.
      */
     public void testExternalStorageNone() throws Exception {
-        final int[] users = createUsersForTest();
         try {
             wipePrimaryExternalStorage();
 
@@ -95,13 +96,12 @@
             String[] options = {AbiUtils.createAbiFlag(mAbi.getName())};
             assertNull(getDevice().installPackage(getTestAppFile(NONE_APK), false, options));
 
-            for (int user : users) {
+            for (int user : mUsers) {
                 runDeviceTests(NONE_PKG, COMMON_CLASS, user);
                 runDeviceTests(NONE_PKG, NONE_CLASS, user);
             }
         } finally {
             getDevice().uninstallPackage(NONE_PKG);
-            removeUsersForTest(users);
         }
     }
 
@@ -111,7 +111,6 @@
      * correctly.
      */
     public void testExternalStorageRead() throws Exception {
-        final int[] users = createUsersForTest();
         try {
             wipePrimaryExternalStorage();
 
@@ -119,13 +118,12 @@
             String[] options = {AbiUtils.createAbiFlag(mAbi.getName())};
             assertNull(getDevice().installPackage(getTestAppFile(READ_APK), false, options));
 
-            for (int user : users) {
+            for (int user : mUsers) {
                 runDeviceTests(READ_PKG, COMMON_CLASS, user);
                 runDeviceTests(READ_PKG, READ_CLASS, user);
             }
         } finally {
             getDevice().uninstallPackage(READ_PKG);
-            removeUsersForTest(users);
         }
     }
 
@@ -135,7 +133,6 @@
      * correctly.
      */
     public void testExternalStorageWrite() throws Exception {
-        final int[] users = createUsersForTest();
         try {
             wipePrimaryExternalStorage();
 
@@ -143,13 +140,12 @@
             String[] options = {AbiUtils.createAbiFlag(mAbi.getName())};
             assertNull(getDevice().installPackage(getTestAppFile(WRITE_APK), false, options));
 
-            for (int user : users) {
+            for (int user : mUsers) {
                 runDeviceTests(WRITE_PKG, COMMON_CLASS, user);
                 runDeviceTests(WRITE_PKG, WRITE_CLASS, user);
             }
         } finally {
             getDevice().uninstallPackage(WRITE_PKG);
-            removeUsersForTest(users);
         }
     }
 
@@ -158,7 +154,6 @@
      * directories belonging to other apps, and those apps can read.
      */
     public void testExternalStorageGifts() throws Exception {
-        final int[] users = createUsersForTest();
         try {
             wipePrimaryExternalStorage();
 
@@ -170,13 +165,13 @@
             // We purposefully delay the installation of the reading apps to
             // verify that the daemon correctly invalidates any caches.
             assertNull(getDevice().installPackage(getTestAppFile(WRITE_APK), false, options));
-            for (int user : users) {
+            for (int user : mUsers) {
                 runDeviceTests(WRITE_PKG, ".WriteGiftTest", user);
             }
 
             assertNull(getDevice().installPackage(getTestAppFile(NONE_APK), false, options));
             assertNull(getDevice().installPackage(getTestAppFile(READ_APK), false, options));
-            for (int user : users) {
+            for (int user : mUsers) {
                 runDeviceTests(READ_PKG, ".ReadGiftTest", user);
                 runDeviceTests(NONE_PKG, ".GiftTest", user);
             }
@@ -184,7 +179,6 @@
             getDevice().uninstallPackage(NONE_PKG);
             getDevice().uninstallPackage(READ_PKG);
             getDevice().uninstallPackage(WRITE_PKG);
-            removeUsersForTest(users);
         }
     }
 
@@ -193,15 +187,14 @@
      * isolated storage.
      */
     public void testMultiUserStorageIsolated() throws Exception {
-        final int[] users = createUsersForTest();
         try {
-            if (users.length == 1) {
+            if (mUsers.length == 1) {
                 Log.d(TAG, "Single user device; skipping isolated storage tests");
                 return;
             }
 
-            final int owner = users[0];
-            final int secondary = users[1];
+            final int owner = mUsers[0];
+            final int secondary = mUsers[1];
 
             // Install our test app
             getDevice().uninstallPackage(MULTIUSER_PKG);
@@ -231,7 +224,6 @@
             runDeviceTests(MULTIUSER_PKG, MULTIUSER_CLASS, "testMediaProviderUserIsolation", secondary);
         } finally {
             getDevice().uninstallPackage(MULTIUSER_PKG);
-            removeUsersForTest(users);
         }
     }
 
@@ -240,7 +232,6 @@
      * when apps with r/w permission levels move around their files.
      */
     public void testMultiViewMoveConsistency() throws Exception {
-        final int[] users = createUsersForTest();
         try {
             wipePrimaryExternalStorage();
 
@@ -252,37 +243,35 @@
             assertNull(getDevice().installPackage(getTestAppFile(WRITE_APK), false, options));
             assertNull(getDevice().installPackage(getTestAppFile(READ_APK), false, options));
 
-            for (int user : users) {
+            for (int user : mUsers) {
                 runDeviceTests(READ_PKG, ".ReadMultiViewTest", "testFolderSetup", user);
             }
-            for (int user : users) {
+            for (int user : mUsers) {
                 runDeviceTests(READ_PKG, ".ReadMultiViewTest", "testRWAccess", user);
             }
 
-            for (int user : users) {
+            for (int user : mUsers) {
                 runDeviceTests(WRITE_PKG, ".WriteMultiViewTest", "testMoveAway", user);
             }
-            for (int user : users) {
+            for (int user : mUsers) {
                 runDeviceTests(READ_PKG, ".ReadMultiViewTest", "testROAccess", user);
             }
 
-            for (int user : users) {
+            for (int user : mUsers) {
                 runDeviceTests(WRITE_PKG, ".WriteMultiViewTest", "testMoveBack", user);
             }
-            for (int user : users) {
+            for (int user : mUsers) {
                 runDeviceTests(READ_PKG, ".ReadMultiViewTest", "testRWAccess", user);
             }
         } finally {
             getDevice().uninstallPackage(NONE_PKG);
             getDevice().uninstallPackage(READ_PKG);
             getDevice().uninstallPackage(WRITE_PKG);
-            removeUsersForTest(users);
         }
     }
 
     /** Verify that app without READ_EXTERNAL can play default URIs in external storage. */
     public void testExternalStorageReadDefaultUris() throws Exception {
-        final int[] users = createUsersForTest();
         try {
             wipePrimaryExternalStorage();
 
@@ -293,7 +282,7 @@
             assertNull(getDevice().installPackage(getTestAppFile(WRITE_APK), false, options));
             assertNull(getDevice().installPackage(getTestAppFile(NONE_APK), false, options));
 
-            for (int user : users) {
+            for (int user : mUsers) {
                 enableWriteSettings(WRITE_PKG, user);
                 runDeviceTests(
                         WRITE_PKG, WRITE_PKG + ".ChangeDefaultUris", "testChangeDefaultUris", user);
@@ -303,13 +292,12 @@
             }
         } finally {
             // Make sure the provider and uris are reset on failure.
-            for (int user : users) {
+            for (int user : mUsers) {
                 runDeviceTests(
                         WRITE_PKG, WRITE_PKG + ".ChangeDefaultUris", "testResetDefaultUris", user);
             }
             getDevice().uninstallPackage(NONE_PKG);
             getDevice().uninstallPackage(WRITE_PKG);
-            removeUsersForTest(users);
         }
     }
 
@@ -334,14 +322,6 @@
         getDevice().executeShellCommand("rm -rf /sdcard/MUST_*");
     }
 
-    private int[] createUsersForTest() throws DeviceNotAvailableException {
-        return Utils.createUsersForTest(getDevice());
-    }
-
-    private void removeUsersForTest(int[] users) throws DeviceNotAvailableException {
-        Utils.removeUsersForTest(getDevice(), users);
-    }
-
     private void runDeviceTests(String packageName, String testClassName, int userId)
             throws DeviceNotAvailableException {
         Utils.runDeviceTests(getDevice(), packageName, testClassName, userId);
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/InstantAppUserTest.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/InstantAppUserTest.java
index fb5dffa..4609e8a 100644
--- a/hostsidetests/appsecurity/src/android/appsecurity/cts/InstantAppUserTest.java
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/InstantAppUserTest.java
@@ -19,8 +19,6 @@
 import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
 import com.android.tradefed.build.IBuildInfo;
 import com.android.tradefed.device.DeviceNotAvailableException;
-import com.android.tradefed.device.ITestDevice;
-import com.android.tradefed.log.LogUtil.CLog;
 import com.android.tradefed.testtype.DeviceTestCase;
 import com.android.tradefed.testtype.IAbi;
 import com.android.tradefed.testtype.IAbiReceiver;
@@ -73,8 +71,9 @@
         assertNotNull(mAbi);
         assertNotNull(mBuildInfo);
 
-        mSupportsMultiUser =
-                getDevice().getMaxNumberOfUsersSupported() - getDevice().listUsers().size() >= 2;
+        // This test only runs when we have at least 3 users to work with
+        final int[] users = Utils.prepareMultipleUsers(getDevice(), 3);
+        mSupportsMultiUser = (users.length == 3);
         if (mSupportsMultiUser) {
             mPrimaryUserId = getDevice().getPrimaryUserId();
             mFixedUsers = new ArrayList<>();
@@ -83,8 +82,9 @@
                 mFixedUsers.add(USER_SYSTEM);
             }
             getDevice().switchUser(mPrimaryUserId);
-            removeTestUsers();
-            createTestUsers();
+
+            mTestUser[0] = users[1];
+            mTestUser[1] = users[2];
 
             uninstallTestPackages();
             installTestPackages();
@@ -93,7 +93,6 @@
 
     public void tearDown() throws Exception {
         if (mSupportsMultiUser) {
-            removeTestUsers();
             uninstallTestPackages();
         }
         super.tearDown();
@@ -180,19 +179,6 @@
         getDevice().uninstallPackage(USER_PKG);
     }
 
-    private void createTestUsers() throws Exception {
-        mTestUser[0] = getDevice().createUser("TestUser_" + System.currentTimeMillis());
-        mTestUser[1] = getDevice().createUser("TestUser_" + System.currentTimeMillis());
-    }
-
-    private void removeTestUsers() throws Exception {
-        for (int userId : getDevice().listUsers()) {
-            if (!mFixedUsers.contains(userId)) {
-                getDevice().removeUser(userId);
-            }
-        }
-    }
-
     private void runDeviceTests(String packageName, String testClassName, String testMethodName)
             throws DeviceNotAvailableException {
         Utils.runDeviceTests(getDevice(), packageName, testClassName, testMethodName);
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/InstantCookieHostTest.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/InstantCookieHostTest.java
index e5a483c..fd599ea 100644
--- a/hostsidetests/appsecurity/src/android/appsecurity/cts/InstantCookieHostTest.java
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/InstantCookieHostTest.java
@@ -39,6 +39,8 @@
     @Override
     protected void setUp() throws Exception {
         super.setUp();
+
+        Utils.prepareSingleUser(getDevice());
         uninstallPackage(INSTANT_COOKIE_APP_PKG);
         clearUserData(INSTANT_COOKIE_APP_PKG);
     }
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/IsolatedSplitsTests.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/IsolatedSplitsTests.java
index ed05837..8795fe8 100644
--- a/hostsidetests/appsecurity/src/android/appsecurity/cts/IsolatedSplitsTests.java
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/IsolatedSplitsTests.java
@@ -43,6 +43,8 @@
     @Override
     protected void setUp() throws Exception {
         super.setUp();
+
+        Utils.prepareSingleUser(getDevice());
         getDevice().uninstallPackage(PKG);
     }
 
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/KeySetHostTest.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/KeySetHostTest.java
index 1f7c6be..3574191 100644
--- a/hostsidetests/appsecurity/src/android/appsecurity/cts/KeySetHostTest.java
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/KeySetHostTest.java
@@ -236,10 +236,13 @@
     }
 
     @Override
-        protected void setUp() throws Exception {
+    protected void setUp() throws Exception {
         super.setUp();
-        mDevice = getDevice();
+
+        Utils.prepareSingleUser(getDevice());
         assertNotNull(mCtsBuild);
+
+        mDevice = getDevice();
     }
 
     /**
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/PackageVisibilityTest.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/PackageVisibilityTest.java
index 115ebf4..fde9f10 100644
--- a/hostsidetests/appsecurity/src/android/appsecurity/cts/PackageVisibilityTest.java
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/PackageVisibilityTest.java
@@ -32,10 +32,13 @@
     private static final boolean MATCH_UNINSTALLED = true;
     private static final boolean MATCH_NORMAL = false;
 
+    private int[] mUsers;
     private String mOldVerifierValue;
 
     public void setUp() throws Exception {
         super.setUp();
+
+        mUsers = Utils.prepareMultipleUsers(getDevice());
         mOldVerifierValue =
                 getDevice().executeShellCommand("settings get global package_verifier_enable");
         getDevice().executeShellCommand("settings put global package_verifier_enable 0");
@@ -54,7 +57,7 @@
             return;
         }
 
-        int userId = createUser();
+        int userId = mUsers[1];
         assertTrue(userId > 0);
         getDevice().startUser(userId);
         installTestAppForUser(TEST_APK, userId);
@@ -67,23 +70,23 @@
         assertTrue(isAppVisibleForUser(TINY_PKG, mPrimaryUserId, MATCH_UNINSTALLED));
 
         // Try the same from an app
-        assertTrue(runDeviceTestsAsUser(TEST_PKG,
-                ".PackageAccessTest", "testPackageAccess_inUser", mPrimaryUserId));
-        assertTrue(runDeviceTestsAsUser(TEST_PKG,
-                ".PackageAccessTest", "testPackageAccess_inUserUninstalled", mPrimaryUserId));
+        Utils.runDeviceTests(getDevice(), TEST_PKG,
+                ".PackageAccessTest", "testPackageAccess_inUser", mPrimaryUserId);
+        Utils.runDeviceTests(getDevice(), TEST_PKG,
+                ".PackageAccessTest", "testPackageAccess_inUserUninstalled", mPrimaryUserId);
 
         // It is not visible for the other user using shell commands
         assertFalse(isAppVisibleForUser(TINY_PKG, userId, MATCH_NORMAL));
         assertFalse(isAppVisibleForUser(TINY_PKG, userId, MATCH_UNINSTALLED));
 
         // Try the same from an app
-        assertTrue(runDeviceTestsAsUser(TEST_PKG,
-                ".PackageAccessTest", "testPackageAccess_notInOtherUser", userId));
-        assertTrue(runDeviceTestsAsUser(TEST_PKG,
-                ".PackageAccessTest", "testPackageAccess_notInOtherUserUninstalled", userId));
+        Utils.runDeviceTests(getDevice(), TEST_PKG,
+                ".PackageAccessTest", "testPackageAccess_notInOtherUser", userId);
+        Utils.runDeviceTests(getDevice(), TEST_PKG,
+                ".PackageAccessTest", "testPackageAccess_notInOtherUserUninstalled", userId);
 
-        assertTrue(runDeviceTestsAsUser(TEST_PKG,
-                ".PackageAccessTest", "testPackageAccess_getPackagesCantSeeTiny", userId));
+        Utils.runDeviceTests(getDevice(), TEST_PKG,
+                ".PackageAccessTest", "testPackageAccess_getPackagesCantSeeTiny", userId);
 
         getDevice().uninstallPackage(TINY_PKG);
 
@@ -105,14 +108,14 @@
         assertFalse(isAppVisibleForUser(TINY_PKG, userId, MATCH_NORMAL));
         assertTrue(isAppVisibleForUser(TINY_PKG, userId, MATCH_UNINSTALLED));
 
-        assertTrue(runDeviceTestsAsUser(TEST_PKG,
-                ".PackageAccessTest", "testPackageAccess_getPackagesCanSeeTiny", userId));
+        Utils.runDeviceTests(getDevice(), TEST_PKG,
+                ".PackageAccessTest", "testPackageAccess_getPackagesCanSeeTiny", userId);
 
-        assertTrue(runDeviceTestsAsUser(TEST_PKG,
+        Utils.runDeviceTests(getDevice(), TEST_PKG,
                 ".PackageAccessTest", "testPackageAccess_notInOtherUserUninstalled",
-                mPrimaryUserId));
-        assertTrue(runDeviceTestsAsUser(TEST_PKG,
-                ".PackageAccessTest", "testPackageAccess_getPackagesCantSeeTiny", mPrimaryUserId));
+                mPrimaryUserId);
+        Utils.runDeviceTests(getDevice(), TEST_PKG,
+                ".PackageAccessTest", "testPackageAccess_getPackagesCantSeeTiny", mPrimaryUserId);
 
         getDevice().uninstallPackage(TINY_PKG);
         getDevice().uninstallPackage(TEST_PKG);
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/PermissionsHostTest.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/PermissionsHostTest.java
index b7f7f88..9ba3c82 100644
--- a/hostsidetests/appsecurity/src/android/appsecurity/cts/PermissionsHostTest.java
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/PermissionsHostTest.java
@@ -58,6 +58,7 @@
     protected void setUp() throws Exception {
         super.setUp();
 
+        Utils.prepareSingleUser(getDevice());
         assertNotNull(mAbi);
         assertNotNull(mBuildHelper);
 
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/PkgInstallSignatureVerificationTest.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/PkgInstallSignatureVerificationTest.java
index 93d683f..e87c390 100644
--- a/hostsidetests/appsecurity/src/android/appsecurity/cts/PkgInstallSignatureVerificationTest.java
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/PkgInstallSignatureVerificationTest.java
@@ -16,6 +16,11 @@
 
 package android.appsecurity.cts;
 
+import com.android.tradefed.build.IBuildInfo;
+import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.testtype.DeviceTestCase;
+import com.android.tradefed.testtype.IBuildReceiver;
+
 import java.io.BufferedOutputStream;
 import java.io.File;
 import java.io.FileOutputStream;
@@ -24,12 +29,6 @@
 import java.io.OutputStream;
 import java.util.Locale;
 
-import com.android.tradefed.build.IBuildInfo;
-import com.android.tradefed.device.DeviceNotAvailableException;
-import com.android.tradefed.device.ITestDevice;
-import com.android.tradefed.testtype.DeviceTestCase;
-import com.android.tradefed.testtype.IBuildReceiver;
-
 /**
  * Tests for APK signature verification during installation.
  */
@@ -44,10 +43,6 @@
     private static final String[] RSA_KEY_NAMES_2048_AND_LARGER =
             {"2048", "3072", "4096", "8192", "16384"};
 
-
-    /** Device under test. */
-    private ITestDevice mDevice;
-
     private IBuildInfo mCtsBuild;
 
     @Override
@@ -58,7 +53,8 @@
     @Override
     protected void setUp() throws Exception {
         super.setUp();
-        mDevice = getDevice();
+
+        Utils.prepareSingleUser(getDevice());
         assertNotNull(mCtsBuild);
         uninstallPackage();
     }
@@ -582,9 +578,9 @@
                 }
             }
             if (ephemeral) {
-                return mDevice.installPackage(apkFile, true, "--ephemeral");
+                return getDevice().installPackage(apkFile, true, "--ephemeral");
             } else {
-                return mDevice.installPackage(apkFile, true);
+                return getDevice().installPackage(apkFile, true);
             }
         } finally {
             apkFile.delete();
@@ -602,6 +598,6 @@
     }
 
     private String uninstallPackage() throws DeviceNotAvailableException {
-        return mDevice.uninstallPackage(TEST_PKG);
+        return getDevice().uninstallPackage(TEST_PKG);
     }
 }
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/PrivilegedUpdateTests.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/PrivilegedUpdateTests.java
index ca218ef..630f351 100644
--- a/hostsidetests/appsecurity/src/android/appsecurity/cts/PrivilegedUpdateTests.java
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/PrivilegedUpdateTests.java
@@ -63,6 +63,7 @@
     protected void setUp() throws Exception {
         super.setUp();
 
+        Utils.prepareSingleUser(getDevice());
         assertNotNull(mAbi);
         assertNotNull(mBuildHelper);
 
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/SplitTests.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/SplitTests.java
index 21bfca1..9474ba8 100644
--- a/hostsidetests/appsecurity/src/android/appsecurity/cts/SplitTests.java
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/SplitTests.java
@@ -96,6 +96,7 @@
     protected void setUp() throws Exception {
         super.setUp();
 
+        Utils.prepareSingleUser(getDevice());
         assertNotNull(mAbi);
         assertNotNull(mCtsBuild);
 
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/StorageHostTest.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/StorageHostTest.java
index f679e83..f08e4fe 100644
--- a/hostsidetests/appsecurity/src/android/appsecurity/cts/StorageHostTest.java
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/StorageHostTest.java
@@ -16,18 +16,22 @@
 
 package android.appsecurity.cts;
 
-import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
-import com.android.tradefed.build.IBuildInfo;
+import com.android.compatibility.common.tradefed.testtype.CompatibilityHostTestBase;
 import com.android.tradefed.device.DeviceNotAvailableException;
-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.testtype.DeviceJUnit4ClassRunner;
+
+import junit.framework.AssertionFailedError;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
 
 /**
  * Tests that exercise various storage APIs.
  */
-public class StorageHostTest extends DeviceTestCase implements IAbiReceiver, IBuildReceiver {
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class StorageHostTest extends CompatibilityHostTestBase {
     private static final String PKG_STATS = "com.android.cts.storagestatsapp";
     private static final String PKG_A = "com.android.cts.storageapp_a";
     private static final String PKG_B = "com.android.cts.storageapp_b";
@@ -37,49 +41,15 @@
     private static final String CLASS_STATS = "com.android.cts.storagestatsapp.StorageStatsTest";
     private static final String CLASS = "com.android.cts.storageapp.StorageTest";
 
-    private IAbi mAbi;
-    private IBuildInfo mCtsBuild;
-
     private int[] mUsers;
 
-    @Override
-    public void setAbi(IAbi abi) {
-        mAbi = abi;
-    }
+    @Before
+    public void setUp() throws Exception {
+        mUsers = Utils.prepareMultipleUsers(getDevice());
 
-    @Override
-    public void setBuild(IBuildInfo buildInfo) {
-        mCtsBuild = buildInfo;
-    }
-
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
-
-        mUsers = Utils.createUsersForTest(getDevice());
-    }
-
-    @Override
-    protected void tearDown() throws Exception {
-        super.tearDown();
-
-        getDevice().uninstallPackage(PKG_STATS);
-        getDevice().uninstallPackage(PKG_A);
-        getDevice().uninstallPackage(PKG_B);
-
-        Utils.removeUsersForTest(getDevice(), mUsers);
-        mUsers = null;
-    }
-
-    private void prepareTestApps() throws Exception {
-        getDevice().uninstallPackage(PKG_STATS);
-        getDevice().uninstallPackage(PKG_A);
-        getDevice().uninstallPackage(PKG_B);
-
-        CompatibilityBuildHelper buildHelper = new CompatibilityBuildHelper(mCtsBuild);
-        assertNull(getDevice().installPackage(buildHelper.getTestFile(APK_STATS), false));
-        assertNull(getDevice().installPackage(buildHelper.getTestFile(APK_A), false));
-        assertNull(getDevice().installPackage(buildHelper.getTestFile(APK_B), false));
+        installPackage(APK_STATS);
+        installPackage(APK_A);
+        installPackage(APK_B);
 
         for (int user : mUsers) {
             getDevice().executeShellCommand("appops set --user " + user + " " + PKG_STATS
@@ -87,26 +57,20 @@
         }
     }
 
-    public void testEverything() throws Exception {
-        prepareTestApps(); doVerifyQuota();
-        prepareTestApps(); doVerifyAppStats();
-        prepareTestApps(); doVerifyAppQuota();
-        prepareTestApps(); doVerifyAppAllocate();
-        prepareTestApps(); doVerifySummary();
-        prepareTestApps(); doVerifyStats();
-        prepareTestApps(); doVerifyStatsMultiple();
-        prepareTestApps(); doVerifyStatsExternal();
-        prepareTestApps(); doVerifyStatsExternalConsistent();
-        prepareTestApps(); doVerifyCategory();
-        prepareTestApps(); doCache();
-        prepareTestApps(); doFullDisk();
+    @After
+    public void tearDown() throws Exception {
+        getDevice().uninstallPackage(PKG_STATS);
+        getDevice().uninstallPackage(PKG_A);
+        getDevice().uninstallPackage(PKG_B);
     }
 
-    public void doVerifyQuota() throws Exception {
-        runDeviceTests(PKG_STATS, CLASS_STATS, "testVerifyQuota", Utils.USER_OWNER);
+    @Test
+    public void testVerifyQuota() throws Exception {
+        Utils.runDeviceTests(getDevice(), PKG_STATS, CLASS_STATS, "testVerifyQuota");
     }
 
-    public void doVerifyAppStats() throws Exception {
+    @Test
+    public void testVerifyAppStats() throws Exception {
         for (int user : mUsers) {
             runDeviceTests(PKG_A, CLASS, "testAllocate", user);
         }
@@ -120,31 +84,36 @@
         }
     }
 
-    public void doVerifyAppQuota() throws Exception {
+    @Test
+    public void testVerifyAppQuota() throws Exception {
         for (int user : mUsers) {
             runDeviceTests(PKG_A, CLASS, "testVerifyQuotaApi", user);
         }
     }
 
-    public void doVerifyAppAllocate() throws Exception {
+    @Test
+    public void testVerifyAppAllocate() throws Exception {
         for (int user : mUsers) {
             runDeviceTests(PKG_A, CLASS, "testVerifyAllocateApi", user);
         }
     }
 
-    public void doVerifySummary() throws Exception {
+    @Test
+    public void testVerifySummary() throws Exception {
         for (int user : mUsers) {
             runDeviceTests(PKG_STATS, CLASS_STATS, "testVerifySummary", user);
         }
     }
 
-    public void doVerifyStats() throws Exception {
+    @Test
+    public void testVerifyStats() throws Exception {
         for (int user : mUsers) {
             runDeviceTests(PKG_STATS, CLASS_STATS, "testVerifyStats", user);
         }
     }
 
-    public void doVerifyStatsMultiple() throws Exception {
+    @Test
+    public void testVerifyStatsMultiple() throws Exception {
         for (int user : mUsers) {
             runDeviceTests(PKG_A, CLASS, "testAllocate", user);
             runDeviceTests(PKG_A, CLASS, "testAllocate", user);
@@ -157,25 +126,29 @@
         }
     }
 
-    public void doVerifyStatsExternal() throws Exception {
+    @Test
+    public void testVerifyStatsExternal() throws Exception {
         for (int user : mUsers) {
             runDeviceTests(PKG_STATS, CLASS_STATS, "testVerifyStatsExternal", user);
         }
     }
 
-    public void doVerifyStatsExternalConsistent() throws Exception {
+    @Test
+    public void testVerifyStatsExternalConsistent() throws Exception {
         for (int user : mUsers) {
             runDeviceTests(PKG_STATS, CLASS_STATS, "testVerifyStatsExternalConsistent", user);
         }
     }
 
-    public void doVerifyCategory() throws Exception {
+    @Test
+    public void testVerifyCategory() throws Exception {
         for (int user : mUsers) {
             runDeviceTests(PKG_STATS, CLASS_STATS, "testVerifyCategory", user);
         }
     }
 
-    public void doCache() throws Exception {
+    @Test
+    public void testCache() throws Exception {
         // To make the cache clearing logic easier to verify, ignore any cache
         // and low space reserved space.
         getDevice().executeShellCommand("settings put global sys_storage_threshold_max_bytes 0");
@@ -197,7 +170,8 @@
         }
     }
 
-    public void doFullDisk() throws Exception {
+    @Test
+    public void testFullDisk() throws Exception {
         waitForIdle();
 
         // Clear all other cached and external storage data to give ourselves a
@@ -209,9 +183,18 @@
         final String lastEvent = getDevice().executeShellCommand("logcat -d -b events -t 1");
         final String sinceTime = lastEvent.trim().substring(0, 18);
 
-        // Try our hardest to fill up the entire disk
-        runDeviceTests(PKG_A, CLASS, "testFullDisk", Utils.USER_OWNER);
-        runDeviceTests(PKG_A, CLASS, "testTweakComponent", Utils.USER_OWNER);
+        try {
+            // Try our hardest to fill up the entire disk
+            Utils.runDeviceTests(getDevice(), PKG_A, CLASS, "testFullDisk");
+        } catch (Throwable t) {
+            // If we had trouble filling the disk, don't bother going any
+            // further; we failed because we either don't have quota support, or
+            // because disk was more than 10% full.
+            return;
+        }
+
+        // Tweak something that causes PackageManager to persist data
+        Utils.runDeviceTests(getDevice(), PKG_A, CLASS, "testTweakComponent");
 
         // Try poking around a couple of settings apps
         getDevice().executeShellCommand("input keyevent KEY_HOME");
@@ -234,7 +217,7 @@
         troubleLogs = troubleLogs.trim().replaceAll("\\-+ beginning of [a-z]+", "");
 
         if (troubleLogs.length() > 4) {
-            fail("Unexpected crashes while disk full: " + troubleLogs);
+            throw new AssertionFailedError("Unexpected crashes while disk full: " + troubleLogs);
         }
     }
 
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/UsesLibraryHostTest.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/UsesLibraryHostTest.java
index cf8a354..4afeb9bf 100644
--- a/hostsidetests/appsecurity/src/android/appsecurity/cts/UsesLibraryHostTest.java
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/UsesLibraryHostTest.java
@@ -51,6 +51,7 @@
     protected void setUp() throws Exception {
         super.setUp();
 
+        Utils.prepareSingleUser(getDevice());
         assertNotNull(mAbi);
         assertNotNull(mBuildHelper);
 
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/Utils.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/Utils.java
index 37aaeaf..8e83c87 100644
--- a/hostsidetests/appsecurity/src/android/appsecurity/cts/Utils.java
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/Utils.java
@@ -16,7 +16,6 @@
 
 package android.appsecurity.cts;
 
-import com.android.ddmlib.Log;
 import com.android.ddmlib.testrunner.RemoteAndroidTestRunner;
 import com.android.ddmlib.testrunner.TestIdentifier;
 import com.android.ddmlib.testrunner.TestResult;
@@ -26,16 +25,15 @@
 import com.android.tradefed.device.ITestDevice;
 import com.android.tradefed.result.CollectingTestListener;
 
+import java.util.Arrays;
 import java.util.Map;
 
 public class Utils {
-    private static final String TAG = "AppSecurity";
-
-    public static final int USER_OWNER = 0;
+    public static final int USER_SYSTEM = 0;
 
     public static void runDeviceTests(ITestDevice device, String packageName)
             throws DeviceNotAvailableException {
-        runDeviceTests(device, packageName, null, null, USER_OWNER);
+        runDeviceTests(device, packageName, null, null, USER_SYSTEM);
     }
 
     public static void runDeviceTests(ITestDevice device, String packageName, int userId)
@@ -45,7 +43,7 @@
 
     public static void runDeviceTests(ITestDevice device, String packageName, String testClassName)
             throws DeviceNotAvailableException {
-        runDeviceTests(device, packageName, testClassName, null, USER_OWNER);
+        runDeviceTests(device, packageName, testClassName, null, USER_SYSTEM);
     }
 
     public static void runDeviceTests(ITestDevice device, String packageName, String testClassName,
@@ -55,7 +53,7 @@
 
     public static void runDeviceTests(ITestDevice device, String packageName, String testClassName,
             String testMethodName) throws DeviceNotAvailableException {
-        runDeviceTests(device, packageName, testClassName, testMethodName, USER_OWNER);
+        runDeviceTests(device, packageName, testClassName, testMethodName, USER_SYSTEM);
     }
 
     public static void runDeviceTests(ITestDevice device, String packageName, String testClassName,
@@ -72,13 +70,8 @@
             testRunner.setClassName(testClassName);
         }
 
-        if (userId != USER_OWNER) {
-            // TODO: move this to RemoteAndroidTestRunner once it supports users
-            testRunner.addInstrumentationArg("hack_key", "hack_value --user " + userId);
-        }
-
         final CollectingTestListener listener = new CollectingTestListener();
-        device.runInstrumentationTests(testRunner, listener);
+        device.runInstrumentationTestsAsUser(testRunner, userId, listener);
 
         final TestRunResult result = listener.getCurrentRunResults();
         if (result.isRunFailure()) {
@@ -103,65 +96,55 @@
         }
     }
 
-    private static boolean isMultiUserSupportedOnDevice(ITestDevice device)
+    /**
+     * Prepare and return a single user relevant for testing.
+     */
+    public static int[] prepareSingleUser(ITestDevice device)
             throws DeviceNotAvailableException {
-        // TODO: move this to ITestDevice once it supports users
-        final String output = device.executeShellCommand("pm get-max-users");
-        try {
-            return Integer.parseInt(output.substring(output.lastIndexOf(" ")).trim()) > 1;
-        } catch (NumberFormatException e) {
-            throw new AssertionError("Failed to parse result: " + output);
-        }
+        return prepareMultipleUsers(device, 1);
     }
 
     /**
-     * Return set of users that test should be run for, creating a secondary
-     * user if the device supports it. Always call
-     * {@link #removeUsersForTest(ITestDevice, int[])} when finished.
+     * Prepare and return two users relevant for testing.
      */
-    public static int[] createUsersForTest(ITestDevice device) throws DeviceNotAvailableException {
-        if (isMultiUserSupportedOnDevice(device)) {
-            return new int[] { USER_OWNER, createUserOnDevice(device) };
-        } else {
-            Log.d(TAG, "Single user device; skipping isolated storage tests");
-            return new int[] { USER_OWNER };
-        }
+    public static int[] prepareMultipleUsers(ITestDevice device)
+            throws DeviceNotAvailableException {
+        return prepareMultipleUsers(device, 2);
     }
 
-    public static void removeUsersForTest(ITestDevice device, int[] users)
+    /**
+     * Prepare and return multiple users relevant for testing.
+     */
+    public static int[] prepareMultipleUsers(ITestDevice device, int maxUsers)
             throws DeviceNotAvailableException {
-        for (int user : users) {
-            if (user != USER_OWNER) {
-                removeUserOnDevice(device, user);
+        final int[] userIds = getAllUsers(device);
+        for (int i = 1; i < userIds.length; i++) {
+            if (i < maxUsers) {
+                device.startUser(userIds[i]);
+            } else {
+                device.stopUser(userIds[i]);
             }
         }
-    }
-
-    private static int createUserOnDevice(ITestDevice device) throws DeviceNotAvailableException {
-        // TODO: move this to ITestDevice once it supports users
-        final String name = "CTS_" + System.currentTimeMillis();
-        final String output = device.executeShellCommand("pm create-user " + name);
-        if (output.startsWith("Success")) {
-            try {
-                final int userId = Integer.parseInt(
-                        output.substring(output.lastIndexOf(" ")).trim());
-                device.executeShellCommand("am start-user " + userId);
-                return userId;
-            } catch (NumberFormatException e) {
-                throw new AssertionError("Failed to parse result: " + output);
-            }
+        if (userIds.length > maxUsers) {
+            return Arrays.copyOf(userIds, maxUsers);
         } else {
-            throw new AssertionError("Failed to create user: " + output);
+            return userIds;
         }
     }
 
-    private static void removeUserOnDevice(ITestDevice device, int userId)
+    public static int[] getAllUsers(ITestDevice device)
             throws DeviceNotAvailableException {
-        // TODO: move this to ITestDevice once it supports users
-        final String output = device.executeShellCommand("pm remove-user " + userId);
-        if (output.startsWith("Error")) {
-            throw new AssertionError("Failed to remove user: " + output);
+        Integer primary = device.getPrimaryUserId();
+        if (primary == null) {
+            primary = USER_SYSTEM;
         }
+        int[] users = new int[] { primary };
+        for (Integer user : device.listUsers()) {
+            if ((user != USER_SYSTEM) && (user != primary)) {
+                users = Arrays.copyOf(users, users.length + 1);
+                users[users.length - 1] = user;
+            }
+        }
+        return users;
     }
-
 }
diff --git a/hostsidetests/appsecurity/test-apps/EphemeralTestApp/EphemeralApp1/AndroidManifest.xml b/hostsidetests/appsecurity/test-apps/EphemeralTestApp/EphemeralApp1/AndroidManifest.xml
index b0f53e0..ed3c3cf 100644
--- a/hostsidetests/appsecurity/test-apps/EphemeralTestApp/EphemeralApp1/AndroidManifest.xml
+++ b/hostsidetests/appsecurity/test-apps/EphemeralTestApp/EphemeralApp1/AndroidManifest.xml
@@ -20,6 +20,8 @@
     <uses-sdk
         android:minSdkVersion="25" />
 
+    <uses-permission android:name="com.android.alarm.permission.SET_ALARM" />
+    <uses-permission android:name="android.permission.INTERNET" />
     <application
         android:label="@string/app_name">
         <uses-library android:name="android.test.runner" />
diff --git a/hostsidetests/appsecurity/test-apps/EphemeralTestApp/EphemeralApp1/src/com/android/cts/ephemeralapp1/ClientTest.java b/hostsidetests/appsecurity/test-apps/EphemeralTestApp/EphemeralApp1/src/com/android/cts/ephemeralapp1/ClientTest.java
index c9d0e45..32e0ce7 100644
--- a/hostsidetests/appsecurity/test-apps/EphemeralTestApp/EphemeralApp1/src/com/android/cts/ephemeralapp1/ClientTest.java
+++ b/hostsidetests/appsecurity/test-apps/EphemeralTestApp/EphemeralApp1/src/com/android/cts/ephemeralapp1/ClientTest.java
@@ -22,6 +22,7 @@
 import static org.junit.Assert.assertThat;
 import static org.junit.Assert.fail;
 
+import android.Manifest;
 import android.annotation.Nullable;
 import android.content.ActivityNotFoundException;
 import android.content.BroadcastReceiver;
@@ -86,9 +87,6 @@
      * Intents that we expect the system to expose activities to ephemeral apps to handle.
      */
     private static final Intent[] EXPECTED_EXPOSED_SYSTEM_INTENTS = new Intent[] {
-        // Camera
-        makeIntent(MediaStore.ACTION_IMAGE_CAPTURE, null, null, null),
-        makeIntent(MediaStore.ACTION_VIDEO_CAPTURE, null, null, null),
         // Contacts
         makeIntent(Intent.ACTION_PICK, null, ContactsContract.Contacts.CONTENT_TYPE, null),
         makeIntent(Intent.ACTION_PICK, null,
@@ -109,6 +107,14 @@
         makeIntent(Intent.ACTION_CHOOSER, null, null, null),
     };
 
+    /**
+     * Camera Intents that we expect the system to expose (if the system has FEATURE_CAMERA).
+     */
+    private static final Intent[] EXPECTED_EXPOSED_CAMERA_INTENTS = new Intent[] {
+        makeIntent(MediaStore.ACTION_IMAGE_CAPTURE, null, null, null),
+        makeIntent(MediaStore.ACTION_VIDEO_CAPTURE, null, null, null),
+    };
+
     private BroadcastReceiver mReceiver;
     private final SynchronousQueue<TestResult> mResultQueue = new SynchronousQueue<>();
 
@@ -954,10 +960,30 @@
     }
 
     @Test
+    public void testInstallPermissionNotGranted() throws Exception {
+        assertThat(InstrumentationRegistry.getContext()
+                    .checkCallingOrSelfPermission(Manifest.permission.SET_ALARM),
+                is(PackageManager.PERMISSION_DENIED));
+    }
+
+    @Test
+    public void testInstallPermissionGranted() throws Exception {
+        assertThat(InstrumentationRegistry.getContext()
+                    .checkCallingOrSelfPermission(Manifest.permission.INTERNET),
+                is(PackageManager.PERMISSION_GRANTED));
+    }
+
+    @Test
     public void testExposedSystemActivities() throws Exception {
         for (Intent queryIntent : EXPECTED_EXPOSED_SYSTEM_INTENTS) {
             assertIntentHasExposedActivities(queryIntent);
         }
+        if (InstrumentationRegistry.getContext().getPackageManager()
+                .hasSystemFeature(PackageManager.FEATURE_CAMERA)) {
+            for (Intent queryIntent : EXPECTED_EXPOSED_CAMERA_INTENTS) {
+                assertIntentHasExposedActivities(queryIntent);
+            }
+        }
     }
 
     private void assertIntentHasExposedActivities(Intent queryIntent) throws Exception {
diff --git a/hostsidetests/appsecurity/test-apps/StorageApp/src/com/android/cts/storageapp/StorageTest.java b/hostsidetests/appsecurity/test-apps/StorageApp/src/com/android/cts/storageapp/StorageTest.java
index 4a4eef1..9d0dec6 100644
--- a/hostsidetests/appsecurity/test-apps/StorageApp/src/com/android/cts/storageapp/StorageTest.java
+++ b/hostsidetests/appsecurity/test-apps/StorageApp/src/com/android/cts/storageapp/StorageTest.java
@@ -23,7 +23,6 @@
 import static com.android.cts.storageapp.Utils.DATA_INT;
 import static com.android.cts.storageapp.Utils.MB_IN_BYTES;
 import static com.android.cts.storageapp.Utils.PKG_B;
-import static com.android.cts.storageapp.Utils.TAG;
 import static com.android.cts.storageapp.Utils.assertMostlyEquals;
 import static com.android.cts.storageapp.Utils.getSizeManual;
 import static com.android.cts.storageapp.Utils.makeUniqueFile;
@@ -42,7 +41,6 @@
 import android.os.storage.StorageManager;
 import android.system.Os;
 import android.test.InstrumentationTestCase;
-import android.util.Log;
 
 import java.io.File;
 import java.io.IOException;
@@ -62,9 +60,19 @@
 
     public void testFullDisk() throws Exception {
         if (shouldHaveQuota(Os.uname())) {
-            Hoarder.doBlocks(getContext().getDataDir(), true);
+            final File dataDir = getContext().getDataDir();
+
+            // Pre-flight to see if we have enough disk space to test with
+            final long total = dataDir.getTotalSpace();
+            final long free = dataDir.getFreeSpace();
+            final long required = ((total * 9) / 10) + MB_IN_BYTES;
+            if (free < required) {
+                fail("Skipping full disk test; only found " + free + " free out of " + total);
+            }
+
+            Hoarder.doBlocks(dataDir, true);
         } else {
-            Log.d(TAG, "Skipping full disk test due to missing quota support");
+            fail("Skipping full disk test due to missing quota support");
         }
     }
 
diff --git a/hostsidetests/net/app/src/com/android/cts/net/hostside/AbstractRestrictBackgroundNetworkTestCase.java b/hostsidetests/net/app/src/com/android/cts/net/hostside/AbstractRestrictBackgroundNetworkTestCase.java
index f994169..c5342ba 100644
--- a/hostsidetests/net/app/src/com/android/cts/net/hostside/AbstractRestrictBackgroundNetworkTestCase.java
+++ b/hostsidetests/net/app/src/com/android/cts/net/hostside/AbstractRestrictBackgroundNetworkTestCase.java
@@ -782,7 +782,7 @@
     }
 
     protected void assertAppIdle(boolean enabled) throws Exception {
-        assertDelayedShellCommand("am get-inactive " + TEST_APP2_PKG, 10, 2, "Idle=" + enabled);
+        assertDelayedShellCommand("am get-inactive " + TEST_APP2_PKG, 15, 2, "Idle=" + enabled);
     }
 
     /**
@@ -848,6 +848,8 @@
             return;
         } else if (type == TYPE_COMPONENT_ACTIVTIY) {
             turnScreenOn();
+            // Wait for screen-on state to propagate through the system.
+            SystemClock.sleep(2000);
             final CountDownLatch latch = new CountDownLatch(1);
             final Intent launchIntent = getIntentForComponent(type);
             final Bundle extras = new Bundle();
diff --git a/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerTransitionSelectionTests.java b/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerTransitionSelectionTests.java
index 7b366d4..63aa1ab 100644
--- a/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerTransitionSelectionTests.java
+++ b/hostsidetests/services/activityandwindowmanager/activitymanager/src/android/server/cts/ActivityManagerTransitionSelectionTests.java
@@ -16,6 +16,8 @@
 
 package android.server.cts;
 
+import android.platform.test.annotations.Presubmit;
+
 import static android.server.cts.WindowManagerState.TRANSIT_ACTIVITY_CLOSE;
 import static android.server.cts.WindowManagerState.TRANSIT_ACTIVITY_OPEN;
 import static android.server.cts.WindowManagerState.TRANSIT_TASK_CLOSE;
@@ -38,6 +40,7 @@
  * Build: mmma -j32 cts/hostsidetests/services
  * Run: cts/hostsidetests/services/activityandwindowmanager/util/run-test CtsServicesHostTestCases android.server.cts.ActivityManagerTransitionSelectionTests
  */
+@Presubmit
 public class ActivityManagerTransitionSelectionTests extends ActivityManagerTestBase {
 
     private static final String BOTTOM_ACTIVITY_NAME = "BottomActivity";
diff --git a/tests/acceleration/AndroidTest.xml b/tests/acceleration/AndroidTest.xml
index 9bf8136..60e4bae 100644
--- a/tests/acceleration/AndroidTest.xml
+++ b/tests/acceleration/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Config for CTS Acceleration test cases">
+    <option name="config-descriptor:metadata" key="component" value="framework" />
     <target_preparer class="com.android.compatibility.common.tradefed.targetprep.ApkInstaller">
         <option name="cleanup-apks" value="true" />
         <option name="test-file-name" value="CtsAccelerationTestCases.apk" />
diff --git a/tests/autofillservice/src/android/autofillservice/cts/AuthenticationActivity.java b/tests/autofillservice/src/android/autofillservice/cts/AuthenticationActivity.java
index 3e2f207..56ae652 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/AuthenticationActivity.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/AuthenticationActivity.java
@@ -18,6 +18,7 @@
 
 import static com.google.common.truth.Truth.assertWithMessage;
 
+import android.app.Activity;
 import android.app.PendingIntent;
 import android.app.assist.AssistStructure;
 import android.autofillservice.cts.CannedFillResponse.CannedDataset;
@@ -48,7 +49,13 @@
     private static final SparseArray<CannedFillResponse> sResponses = new SparseArray<>();
     private static final ArrayList<PendingIntent> sPendingIntents = new ArrayList<>();
 
+    private static Object sLock = new Object();
+
+    // Guarded by sLock
+    private static int sResultCode;
+
     static void resetStaticState() {
+        setResultCode(RESULT_OK);
         sDatasets.clear();
         sResponses.clear();
         for (int i = 0; i < sPendingIntents.size(); i++) {
@@ -104,6 +111,16 @@
         return data;
     }
 
+    /**
+     * Sets the value that's passed to {@link Activity#setResult(int, Intent)} when on
+     * {@link Activity#onCreate(Bundle)}.
+     */
+    public static void setResultCode(int resultCode) {
+        synchronized (sLock) {
+            sResultCode = resultCode;
+        }
+    }
+
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
@@ -133,7 +150,12 @@
         // Pass on the auth result
         final Intent intent = new Intent();
         intent.putExtra(AutofillManager.EXTRA_AUTHENTICATION_RESULT, result);
-        setResult(RESULT_OK, intent);
+        final int resultCode;
+        synchronized (sLock) {
+            resultCode = sResultCode;
+        }
+        Log.d(TAG, "Returning code " + resultCode);
+        setResult(resultCode, intent);
 
         // Done
         finish();
diff --git a/tests/autofillservice/src/android/autofillservice/cts/Helper.java b/tests/autofillservice/src/android/autofillservice/cts/Helper.java
index 8029748..2aa8c45 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/Helper.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/Helper.java
@@ -16,12 +16,16 @@
 
 package android.autofillservice.cts;
 
+import static android.autofillservice.cts.Helper.runShellCommand;
+import static android.provider.Settings.Secure.AUTOFILL_SERVICE;
+
 import static com.google.common.truth.Truth.assertThat;
 import static com.google.common.truth.Truth.assertWithMessage;
 
 import android.app.assist.AssistStructure;
 import android.app.assist.AssistStructure.ViewNode;
 import android.app.assist.AssistStructure.WindowNode;
+import android.content.Context;
 import android.content.pm.PackageManager;
 import android.icu.util.Calendar;
 import android.os.UserManager;
@@ -47,6 +51,9 @@
 
     private static final String TAG = "AutoFillCtsHelper";
 
+    // TODO: should static import Settings.Secure instead, but that's not a @TestApi
+    private static String USER_SETUP_COMPLETE = "user_setup_complete";
+
     static final boolean VERBOSE = false;
 
     static final String ID_USERNAME_LABEL = "username_label";
@@ -187,6 +194,38 @@
         runShellCommand("pm set-user-restriction no_autofill %d", restricted ? 1 : 0);
     }
 
+    /**
+     * Sets whether the user completed the initial setup.
+     */
+    static void setUserComplete(Context context, boolean complete) {
+        if (isUserComplete() == complete) return;
+
+        final OneTimeSettingsListener observer = new OneTimeSettingsListener(context,
+                USER_SETUP_COMPLETE);
+        final String newValue = complete ? "1" : null;
+        runShellCommand("settings put secure %s %s default", USER_SETUP_COMPLETE, newValue);
+        observer.assertCalled();
+
+        assertIsUserComplete(complete);
+    }
+
+    /**
+     * Gets whether the user completed the initial setup.
+     */
+    static boolean isUserComplete() {
+        final String isIt = runShellCommand("settings get secure %s", USER_SETUP_COMPLETE);
+        return "1".equals(isIt);
+    }
+
+    /**
+     * Assets that user completed (or not) the initial setup.
+     */
+    static void assertIsUserComplete(boolean expected) {
+        final boolean actual = isUserComplete();
+        assertWithMessage("Invalid value for secure setting %s", USER_SETUP_COMPLETE)
+                .that(actual).isEqualTo(expected);
+    }
+
     private static void dump(StringBuffer buffer, ViewNode node, String prefix, int childId) {
         final int childrenSize = node.getChildCount();
         buffer.append("\n").append(prefix)
diff --git a/tests/autofillservice/src/android/autofillservice/cts/LoginActivityTest.java b/tests/autofillservice/src/android/autofillservice/cts/LoginActivityTest.java
index c157b38..b03966e 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/LoginActivityTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/LoginActivityTest.java
@@ -16,6 +16,8 @@
 
 package android.autofillservice.cts;
 
+import static android.app.Activity.RESULT_CANCELED;
+import static android.app.Activity.RESULT_OK;
 import static android.autofillservice.cts.CannedFillResponse.NO_RESPONSE;
 import static android.autofillservice.cts.Helper.ID_PASSWORD;
 import static android.autofillservice.cts.Helper.ID_PASSWORD_LABEL;
@@ -28,6 +30,7 @@
 import static android.autofillservice.cts.Helper.eventually;
 import static android.autofillservice.cts.Helper.findNodeByResourceId;
 import static android.autofillservice.cts.Helper.runShellCommand;
+import static android.autofillservice.cts.Helper.setUserComplete;
 import static android.autofillservice.cts.Helper.setUserRestrictionForAutofill;
 import static android.autofillservice.cts.InstrumentedAutoFillService.waitUntilConnected;
 import static android.autofillservice.cts.InstrumentedAutoFillService.waitUntilDisconnected;
@@ -64,7 +67,6 @@
 import android.content.IntentFilter;
 import android.content.IntentSender;
 import android.os.Bundle;
-import android.platform.test.annotations.Presubmit;
 import android.service.autofill.FillEventHistory;
 import android.service.autofill.SaveInfo;
 import android.support.test.rule.ActivityTestRule;
@@ -496,7 +498,6 @@
     }
 
     @Test
-    @Presubmit
     public void testAutoFillOneDatasetAndSave() throws Exception {
         // Set service.
         enableService();
@@ -1264,6 +1265,15 @@
 
     @Test
     public void testFillResponseAuthBothFields() throws Exception {
+        fillResponseAuthBothFields(false);
+    }
+
+    @Test
+    public void testFillResponseAuthBothFieldsUserCancelsFirstAttempt() throws Exception {
+        fillResponseAuthBothFields(true);
+    }
+
+    private void fillResponseAuthBothFields(boolean cancelFirstAttempt) throws Exception {
         // Set service.
         enableService();
         final MyAutofillCallback callback = mActivity.registerCallback();
@@ -1298,29 +1308,49 @@
         sReplier.getNextFillRequest();
         final View username = mActivity.getUsername();
         callback.assertUiShownEvent(username);
-        sUiBot.assertShownByText("Tap to auth response");
+        sUiBot.assertDatasets("Tap to auth response");
 
         // Make sure UI is show on 2nd field as well
         final View password = mActivity.getPassword();
         mActivity.onPassword(View::requestFocus);
         callback.assertUiHiddenEvent(username);
         callback.assertUiShownEvent(password);
-        sUiBot.assertShownByText("Tap to auth response");
+        sUiBot.assertDatasets("Tap to auth response");
 
         // Now tap on 1st field to show it again...
         mActivity.onUsername(View::requestFocus);
         callback.assertUiHiddenEvent(password);
         callback.assertUiShownEvent(username);
-        sUiBot.selectByText("Tap to auth response");
-        callback.assertUiHiddenEvent(username);
-        sUiBot.assertNotShownByText("Tap to auth response");
+
+        if (cancelFirstAttempt) {
+            // Trigger the auth dialog, but emulate cancel.
+            AuthenticationActivity.setResultCode(RESULT_CANCELED);
+            sUiBot.selectDataset("Tap to auth response");
+            callback.assertUiHiddenEvent(username);
+            callback.assertUiShownEvent(username);
+            sUiBot.assertDatasets("Tap to auth response");
+
+            // Make sure it's still shown on other fields...
+            mActivity.onPassword(View::requestFocus);
+            callback.assertUiHiddenEvent(username);
+            callback.assertUiShownEvent(password);
+            sUiBot.assertDatasets("Tap to auth response");
+
+            // Tap on 1st field to show it again...
+            mActivity.onUsername(View::requestFocus);
+            callback.assertUiHiddenEvent(password);
+            callback.assertUiShownEvent(username);
+        }
 
         // ...and select it this time
+        AuthenticationActivity.setResultCode(RESULT_OK);
+        sUiBot.selectDataset("Tap to auth response");
+        callback.assertUiHiddenEvent(username);
         callback.assertUiShownEvent(username);
+        sUiBot.assertNotShownByText("Tap to auth response");
         sUiBot.selectDataset("Dataset");
         callback.assertUiHiddenEvent(username);
         sUiBot.assertNoDatasets();
-        sUiBot.assertNotShownByText("Tap to auth response");
 
         // Check the results.
         mActivity.assertAutoFilled();
@@ -1367,18 +1397,18 @@
         sReplier.getNextFillRequest();
         final View username = mActivity.getUsername();
         callback.assertUiShownEvent(username);
-        sUiBot.assertShownByText("Tap to auth response");
+        sUiBot.assertDatasets("Tap to auth response");
 
         // Make sure UI is not show on 2nd field
         mActivity.onPassword(View::requestFocus);
         callback.assertUiHiddenEvent(username);
-        sUiBot.assertNotShownByText("Tap to auth response");
+        sUiBot.assertNoDatasets();
         // Now tap on 1st field to show it again...
         mActivity.onUsername(View::requestFocus);
         callback.assertUiShownEvent(username);
 
         // ...and select it this time
-        sUiBot.selectByText("Tap to auth response");
+        sUiBot.selectDataset("Tap to auth response");
         callback.assertUiHiddenEvent(username);
         sUiBot.assertNotShownByText("Tap to auth response");
 
@@ -1386,7 +1416,6 @@
         sUiBot.selectDataset("Dataset");
         callback.assertUiHiddenEvent(username);
         sUiBot.assertNoDatasets();
-        sUiBot.assertNotShownByText("Tap to auth response");
 
         // Check the results.
         mActivity.assertAutoFilled();
@@ -1423,14 +1452,26 @@
         callback.assertUiShownEvent(username);
 
         // Select the authentication dialog.
-        sUiBot.selectByText("Tap to auth response");
+        sUiBot.selectDataset("Tap to auth response");
         callback.assertUiHiddenEvent(username);
-        sUiBot.assertNotShownByText("Tap to auth response");
         sUiBot.assertNoDatasets();
     }
 
     @Test
     public void testDatasetAuthTwoFields() throws Exception {
+        datasetAuthTwoFields(false);
+    }
+
+    @Test
+    public void testDatasetAuthTwoFieldsUserCancelsFirstAttempt() throws Exception {
+        datasetAuthTwoFields(true);
+    }
+
+    private void datasetAuthTwoFields(boolean cancelFirstAttempt) throws Exception {
+        // TODO: current API requires these fields...
+        final RemoteViews bogusPresentation = createPresentation("Whatever man, I'm not used...");
+        final String bogusValue = "Y U REQUIRE IT?";
+
         // Set service.
         enableService();
         final MyAutofillCallback callback = mActivity.registerCallback();
@@ -1440,14 +1481,14 @@
                 new CannedDataset.Builder()
                         .setField(ID_USERNAME, "dude")
                         .setField(ID_PASSWORD, "sweet")
-                        .setPresentation(createPresentation("Dataset"))
+                        .setPresentation(bogusPresentation)
                         .build());
 
         // Configure the service behavior
         sReplier.addResponse(new CannedFillResponse.Builder()
                 .addDataset(new CannedDataset.Builder()
-                        .setField(ID_USERNAME, "dude")
-                        .setField(ID_PASSWORD, "sweet")
+                        .setField(ID_USERNAME, bogusValue)
+                        .setField(ID_PASSWORD, bogusValue)
                         .setPresentation(createPresentation("Tap to auth dataset"))
                         .setAuthentication(authentication)
                         .build())
@@ -1462,12 +1503,47 @@
         // Wait for onFill() before proceeding.
         sReplier.getNextFillRequest();
         final View username = mActivity.getUsername();
-
-        // Authenticate
         callback.assertUiShownEvent(username);
-        sUiBot.selectByText("Tap to auth dataset");
+        sUiBot.assertDatasets("Tap to auth dataset");
+
+        // Make sure UI is show on 2nd field as well
+        final View password = mActivity.getPassword();
+        mActivity.onPassword(View::requestFocus);
         callback.assertUiHiddenEvent(username);
-        sUiBot.assertNotShownByText("Tap to auth dataset");
+        callback.assertUiShownEvent(password);
+        sUiBot.assertDatasets("Tap to auth dataset");
+
+        // Now tap on 1st field to show it again...
+        mActivity.onUsername(View::requestFocus);
+        callback.assertUiHiddenEvent(password);
+        callback.assertUiShownEvent(username);
+        sUiBot.assertDatasets("Tap to auth dataset");
+
+        if (cancelFirstAttempt) {
+            // Trigger the auth dialog, but emulate cancel.
+            AuthenticationActivity.setResultCode(RESULT_CANCELED);
+            sUiBot.selectDataset("Tap to auth dataset");
+            callback.assertUiHiddenEvent(username);
+            callback.assertUiShownEvent(username);
+            sUiBot.assertDatasets("Tap to auth dataset");
+
+            // Make sure it's still shown on other fields...
+            mActivity.onPassword(View::requestFocus);
+            callback.assertUiHiddenEvent(username);
+            callback.assertUiShownEvent(password);
+            sUiBot.assertDatasets("Tap to auth dataset");
+
+            // Tap on 1st field to show it again...
+            mActivity.onUsername(View::requestFocus);
+            callback.assertUiHiddenEvent(password);
+            callback.assertUiShownEvent(username);
+        }
+
+        // ...and select it this time
+        AuthenticationActivity.setResultCode(RESULT_OK);
+        sUiBot.selectDataset("Tap to auth dataset");
+        callback.assertUiHiddenEvent(username);
+        sUiBot.assertNoDatasets();
 
         // Check the results.
         mActivity.assertAutoFilled();
@@ -1516,14 +1592,14 @@
 
         // Authenticate
         callback.assertUiShownEvent(username);
-        sUiBot.selectByText("Tap to auth dataset");
+        sUiBot.selectDataset("Tap to auth dataset");
         callback.assertUiHiddenEvent(username);
 
         // Select a dataset from the new response
         callback.assertUiShownEvent(username);
-        sUiBot.selectByText("Dataset");
+        sUiBot.selectDataset("Dataset");
         callback.assertUiHiddenEvent(username);
-        sUiBot.assertNotShownByText("Dataset");
+        sUiBot.assertNoDatasets();
 
         // Check the results.
         mActivity.assertAutoFilled();
@@ -1570,9 +1646,9 @@
 
         // Authenticate
         callback.assertUiShownEvent(username);
-        sUiBot.selectByText("Tap to auth dataset");
+        sUiBot.selectDataset("Tap to auth dataset");
         callback.assertUiHiddenEvent(username);
-        sUiBot.assertNotShownByText("Tap to auth dataset");
+        sUiBot.assertNoDatasets();
 
         // Check the results.
         mActivity.assertAutoFilled();
@@ -2495,4 +2571,21 @@
             disableService();
         }
     }
+
+    @Test
+    public void testSetupComplete() throws Exception {
+        enableService();
+
+        // Sanity check.
+        final AutofillManager afm = mActivity.getAutofillManager();
+        assertThat(afm.isEnabled()).isTrue();
+
+        // Now disable user_complete and try again.
+        try {
+            setUserComplete(getContext(), false);
+            assertThat(afm.isEnabled()).isFalse();
+        } finally {
+            setUserComplete(getContext(), true);
+        }
+    }
 }
diff --git a/tests/autofillservice/src/android/autofillservice/cts/PartitionedActivityTest.java b/tests/autofillservice/src/android/autofillservice/cts/PartitionedActivityTest.java
index b60840d..756b6e9 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/PartitionedActivityTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/PartitionedActivityTest.java
@@ -2223,22 +2223,22 @@
 
         // Finally, autofill and check them.
         mActivity.focusCell(2, 1);
-        sUiBot.selectByText("Auth 2");
+        sUiBot.selectDataset("Auth 2");
         sUiBot.selectDataset("Partition 2");
         expectation2.assertAutoFilled();
 
         mActivity.focusCell(4, 1);
-        sUiBot.selectByText("Auth 4");
+        sUiBot.selectDataset("Auth 4");
         sUiBot.selectDataset("Partition 4");
         expectation4.assertAutoFilled();
 
         mActivity.focusCell(3, 1);
-        sUiBot.selectByText("Auth 3");
+        sUiBot.selectDataset("Auth 3");
         sUiBot.selectDataset("Partition 3");
         expectation3.assertAutoFilled();
 
         mActivity.focusCell(1, 1);
-        sUiBot.selectByText("Auth 1");
+        sUiBot.selectDataset("Auth 1");
         sUiBot.selectDataset("Partition 1");
         expectation1.assertAutoFilled();
     }
diff --git a/tests/autofillservice/src/android/autofillservice/cts/UiBot.java b/tests/autofillservice/src/android/autofillservice/cts/UiBot.java
index d1f01e1..987ddbe 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/UiBot.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/UiBot.java
@@ -152,6 +152,9 @@
 
     /**
      * Selects a view by text.
+     *
+     * <p><b>NOTE:</b> when selecting an option in dataset picker is shown, prefer
+     * {@link #selectDataset(String)}.
      */
     void selectByText(String name) {
         Log.v(TAG, "selectByText(): " + name);
@@ -161,7 +164,10 @@
     }
 
     /**
-     * Asserts a text is not shown.
+     * Asserts a text is shown.
+     *
+     * <p><b>NOTE:</b> when asserting the dataset picker is shown, prefer
+     * {@link #assertDatasets(String...)}.
      */
     public void assertShownByText(String text) {
         final UiObject2 object = waitForObject(By.text(text));
@@ -169,7 +175,10 @@
     }
 
     /**
-     * Asserts a text is now shown.
+     * Asserts a text is not shown.
+     *
+     * <p><b>NOTE:</b> when asserting the dataset picker is not shown, prefer
+     * {@link #assertNoDatasets()}.
      */
     public void assertNotShownByText(String text) {
         final UiObject2 uiObject = mDevice.findObject(By.text(text));
diff --git a/tests/camera/AndroidTest.xml b/tests/camera/AndroidTest.xml
index 1358c34..656b96d 100644
--- a/tests/camera/AndroidTest.xml
+++ b/tests/camera/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Config for CTS Camera test cases">
+    <option name="config-descriptor:metadata" key="component" value="camera" />
     <target_preparer class="com.android.compatibility.common.tradefed.targetprep.ApkInstaller">
         <option name="cleanup-apks" value="true" />
         <option name="test-file-name" value="CtsCameraTestCases.apk" />
diff --git a/tests/fragment/src/android/fragment/cts/FragmentLifecycleTest.java b/tests/fragment/src/android/fragment/cts/FragmentLifecycleTest.java
index ed48c82..00ec95b 100644
--- a/tests/fragment/src/android/fragment/cts/FragmentLifecycleTest.java
+++ b/tests/fragment/src/android/fragment/cts/FragmentLifecycleTest.java
@@ -809,6 +809,53 @@
     }
 
     /**
+     * Check that retained fragments in the backstack correctly restored after two "configChanges"
+     */
+    @Test
+    public void retainedFragmentInBackstack() throws Throwable {
+        mActivityRule.runOnUiThread(() -> {
+            FragmentController fc = FragmentTestUtil.createController(mActivityRule);
+            FragmentTestUtil.resume(mActivityRule, fc, null);
+            FragmentManager fm = fc.getFragmentManager();
+
+            Fragment fragment1 = new StrictFragment();
+            fm.beginTransaction()
+                    .add(fragment1, "1")
+                    .addToBackStack(null)
+                    .commit();
+            fm.executePendingTransactions();
+
+            Fragment child = new StrictFragment();
+            child.setRetainInstance(true);
+            fragment1.getChildFragmentManager().beginTransaction()
+                    .add(child, "child").commit();
+            fragment1.getChildFragmentManager().executePendingTransactions();
+
+            Fragment fragment2 = new StrictFragment();
+            fm.beginTransaction()
+                    .remove(fragment1)
+                    .add(fragment2, "2")
+                    .addToBackStack(null)
+                    .commit();
+            fm.executePendingTransactions();
+
+            Pair<Parcelable, FragmentManagerNonConfig> savedState =
+                    FragmentTestUtil.destroy(mActivityRule, fc);
+
+            fc = FragmentTestUtil.createController(mActivityRule);
+            FragmentTestUtil.resume(mActivityRule, fc, savedState);
+            savedState = FragmentTestUtil.destroy(mActivityRule, fc);
+            fc = FragmentTestUtil.createController(mActivityRule);
+            FragmentTestUtil.resume(mActivityRule, fc, savedState);
+            fm = fc.getFragmentManager();
+            fm.popBackStackImmediate();
+            Fragment retainedChild = fm.findFragmentByTag("1")
+                    .getChildFragmentManager().findFragmentByTag("child");
+            assertEquals(child, retainedChild);
+        });
+    }
+
+    /**
      * When a fragment has been optimized out, it state should still be saved during
      * save and restore instance state.
      */
diff --git a/tests/tests/content/Android.mk b/tests/tests/content/Android.mk
index 6821a39..5d5d025 100644
--- a/tests/tests/content/Android.mk
+++ b/tests/tests/content/Android.mk
@@ -26,18 +26,19 @@
 
 LOCAL_JAVA_LIBRARIES := android.test.runner
 
-LOCAL_STATIC_JAVA_LIBRARIES :=  android-support-v4 \
-                                android-support-multidex \
-                                compatibility-device-util \
-                                ctstestrunner \
-                                services.core \
-                                junit \
-                                legacy-android-test \
-                                truth-prebuilt
+LOCAL_STATIC_JAVA_LIBRARIES :=  \
+    compatibility-device-util \
+    ctstestrunner \
+    services.core \
+    junit \
+    legacy-android-test \
+    truth-prebuilt
+
+LOCAL_STATIC_ANDROID_LIBRARIES := android-support-v4
 
 # Use multi-dex as the compatibility-common-util-devicesidelib dependency
 # on compatibility-device-util pushes us beyond 64k methods.
-LOCAL_JACK_FLAGS := --multi-dex legacy
+LOCAL_JACK_FLAGS := --multi-dex native
 LOCAL_DX_FLAGS := --multi-dex
 
 # Resource unit tests use a private locale and some densities
diff --git a/tests/tests/net/src/android/net/wifi/cts/WifiManagerTest.java b/tests/tests/net/src/android/net/wifi/cts/WifiManagerTest.java
index 33c184a..cbf3e93 100644
--- a/tests/tests/net/src/android/net/wifi/cts/WifiManagerTest.java
+++ b/tests/tests/net/src/android/net/wifi/cts/WifiManagerTest.java
@@ -59,6 +59,7 @@
     private static MySync mMySync;
     private List<ScanResult> mScanResults = null;
     private NetworkInfo mNetworkInfo;
+    private Object mLOHSLock = new Object();
 
     // Please refer to WifiManager
     private static final int MIN_RSSI = -100;
@@ -173,14 +174,25 @@
 
     private void setWifiEnabled(boolean enable) throws Exception {
         synchronized (mMySync) {
-            assertTrue(mWifiManager.setWifiEnabled(enable));
             if (mWifiManager.isWifiEnabled() != enable) {
+                // the new state is different, we expect it to change
                 mMySync.expectedState = STATE_WIFI_CHANGING;
-                long timeout = System.currentTimeMillis() + TIMEOUT_MSEC;
-                int expectedState = (enable ? STATE_WIFI_ENABLED : STATE_WIFI_DISABLED);
-                while (System.currentTimeMillis() < timeout
-                        && mMySync.expectedState != expectedState)
-                    mMySync.wait(WAIT_MSEC);
+            } else {
+                mMySync.expectedState = (enable ? STATE_WIFI_ENABLED : STATE_WIFI_DISABLED);
+            }
+            // now trigger the change
+            assertTrue(mWifiManager.setWifiEnabled(enable));
+            waitForExpectedWifiState(enable);
+        }
+    }
+
+    private void waitForExpectedWifiState(boolean enabled) throws InterruptedException {
+        synchronized (mMySync) {
+            long timeout = System.currentTimeMillis() + TIMEOUT_MSEC;
+            int expected = (enabled ? STATE_WIFI_ENABLED : STATE_WIFI_DISABLED);
+            while (System.currentTimeMillis() < timeout
+                    && mMySync.expectedState != expected) {
+                mMySync.wait(WAIT_MSEC);
             }
         }
     }
@@ -517,6 +529,13 @@
         }
         assertTrue(mWifiManager.isWifiEnabled());
 
+        // give the test a chance to autoconnect
+        Thread.sleep(DURATION);
+        if (mNetworkInfo.getState() != NetworkInfo.State.CONNECTED) {
+            // this test requires a connectable network be configured
+            fail("This test requires a wifi network connection.");
+        }
+
         // This will generate a distinct stack trace if the initial connection fails.
         connectWifi();
 
@@ -686,4 +705,148 @@
             // Passpoint build config |config_wifi_hotspot2_enabled| is disabled, so noop.
         }
     }
+
+    public class TestLocalOnlyHotspotCallback extends WifiManager.LocalOnlyHotspotCallback {
+        Object hotspotLock;
+        WifiManager.LocalOnlyHotspotReservation reservation = null;
+        boolean onStartedCalled = false;
+        boolean onStoppedCalled = false;
+        boolean onFailedCalled = false;
+        int failureReason = -1;
+
+        TestLocalOnlyHotspotCallback(Object lock) {
+            hotspotLock = lock;
+        }
+
+        @Override
+        public void onStarted(WifiManager.LocalOnlyHotspotReservation r) {
+            synchronized (hotspotLock) {
+                reservation = r;
+                onStartedCalled = true;
+                hotspotLock.notify();
+            }
+        }
+
+        @Override
+        public void onStopped() {
+            synchronized (hotspotLock) {
+                onStoppedCalled = true;
+                hotspotLock.notify();
+            }
+        }
+
+        @Override
+        public void onFailed(int reason) {
+            synchronized (hotspotLock) {
+                onFailedCalled = true;
+                failureReason = reason;
+                hotspotLock.notify();
+            }
+        }
+    }
+
+    private TestLocalOnlyHotspotCallback startLocalOnlyHotspot() {
+        // Location mode must be enabled for this test
+        if (!isLocationEnabled()) {
+            fail("Please enable location for this test");
+        }
+
+        TestLocalOnlyHotspotCallback callback = new TestLocalOnlyHotspotCallback(mLOHSLock);
+        synchronized (mLOHSLock) {
+            try {
+                mWifiManager.startLocalOnlyHotspot(callback, null);
+                // now wait for callback
+                mLOHSLock.wait(DURATION);
+            } catch (InterruptedException e) {
+            }
+            // check if we got the callback
+            assertTrue(callback.onStartedCalled);
+            assertNotNull(callback.reservation.getWifiConfiguration());
+            assertFalse(callback.onFailedCalled);
+            assertFalse(callback.onStoppedCalled);
+        }
+        return callback;
+    }
+
+    private void stopLocalOnlyHotspot(TestLocalOnlyHotspotCallback callback, boolean wifiEnabled) {
+       synchronized (mMySync) {
+           // we are expecting a new state
+           mMySync.expectedState = STATE_WIFI_CHANGING;
+
+           // now shut down LocalOnlyHotspot
+           callback.reservation.close();
+
+           try {
+               waitForExpectedWifiState(wifiEnabled);
+           } catch (InterruptedException e) {}
+        }
+    }
+
+    /**
+     * Verify that calls to startLocalOnlyHotspot succeed with proper permissions.
+     *
+     * Note: Location mode must be enabled for this test.
+     */
+    public void testStartLocalOnlyHotspotSuccess() {
+        boolean wifiEnabled = mWifiManager.isWifiEnabled();
+
+        TestLocalOnlyHotspotCallback callback = startLocalOnlyHotspot();
+
+        // at this point, wifi should be off
+        assertFalse(mWifiManager.isWifiEnabled());
+
+        stopLocalOnlyHotspot(callback, wifiEnabled);
+        assertEquals(wifiEnabled, mWifiManager.isWifiEnabled());
+    }
+
+    /**
+     * Verify calls to setWifiEnabled from a non-settings app while softap mode is active do not
+     * exit softap mode.
+     *
+     * This test uses the LocalOnlyHotspot API to enter softap mode.  This should also be true when
+     * tethering is started.
+     * Note: Location mode must be enabled for this test.
+     */
+    public void testSetWifiEnabledByAppDoesNotStopHotspot() {
+        boolean wifiEnabled = mWifiManager.isWifiEnabled();
+
+        TestLocalOnlyHotspotCallback callback = startLocalOnlyHotspot();
+        // at this point, wifi should be off
+        assertFalse(mWifiManager.isWifiEnabled());
+
+        // now we should fail to turn on wifi
+        assertFalse(mWifiManager.setWifiEnabled(true));
+
+        stopLocalOnlyHotspot(callback, wifiEnabled);
+        assertEquals(wifiEnabled, mWifiManager.isWifiEnabled());
+    }
+
+    /**
+     * Verify that applications can only have one registered LocalOnlyHotspot request at a time.
+     *
+     * Note: Location mode must be enabled for this test.
+     */
+    public void testStartLocalOnlyHotspotSingleRequestByApps() {
+        boolean caughtException = false;
+
+        boolean wifiEnabled = mWifiManager.isWifiEnabled();
+
+        TestLocalOnlyHotspotCallback callback = startLocalOnlyHotspot();
+
+        // at this point, wifi should be off
+        assertFalse(mWifiManager.isWifiEnabled());
+
+        // now make a second request - this should fail.
+        TestLocalOnlyHotspotCallback callback2 = new TestLocalOnlyHotspotCallback(mLOHSLock);
+        try {
+            mWifiManager.startLocalOnlyHotspot(callback2, null);
+        } catch (IllegalStateException e) {
+            Log.d(TAG, "Caught the IllegalStateException we expected: called startLOHS twice");
+            caughtException = true;
+        }
+        assertTrue(caughtException);
+
+        stopLocalOnlyHotspot(callback, wifiEnabled);
+        assertEquals(wifiEnabled, mWifiManager.isWifiEnabled());
+    }
 }
diff --git a/tests/tests/os/src/android/os/cts/StrictModeTest.java b/tests/tests/os/src/android/os/cts/StrictModeTest.java
index fecc067..21730c6 100644
--- a/tests/tests/os/src/android/os/cts/StrictModeTest.java
+++ b/tests/tests/os/src/android/os/cts/StrictModeTest.java
@@ -22,15 +22,12 @@
 import android.net.TrafficStats;
 import android.net.Uri;
 import android.os.StrictMode;
-import android.os.SystemClock;
+import android.os.StrictMode.ViolationListener;
 import android.system.Os;
 import android.system.OsConstants;
 import android.test.InstrumentationTestCase;
 import android.util.Log;
 
-import libcore.io.Streams;
-
-import java.io.ByteArrayOutputStream;
 import java.io.File;
 import java.io.FileDescriptor;
 import java.io.FileInputStream;
@@ -38,8 +35,8 @@
 import java.net.HttpURLConnection;
 import java.net.Socket;
 import java.net.URL;
-import java.text.SimpleDateFormat;
-import java.util.Date;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.TimeUnit;
 
 /**
  * Tests for {@link StrictMode}
@@ -66,6 +63,10 @@
         StrictMode.setVmPolicy(mVmPolicy);
     }
 
+    public interface ThrowingRunnable {
+        public void run() throws Exception;
+    }
+
     /**
      * Insecure connection should be detected
      */
@@ -80,10 +81,10 @@
                 .penaltyLog()
                 .build());
 
-        long mark = System.currentTimeMillis();
-        ((HttpURLConnection) new URL("http://example.com/").openConnection()).getResponseCode();
-        assertLogged("Detected cleartext network traffic from UID "
-                + android.os.Process.myUid(), mark, 5000);
+        assertViolation("Detected cleartext network traffic from UID", () -> {
+            ((HttpURLConnection) new URL("http://example.com/").openConnection())
+                    .getResponseCode();
+        });
     }
 
     /**
@@ -100,10 +101,10 @@
                 .penaltyLog()
                 .build());
 
-        long mark = System.currentTimeMillis();
-        ((HttpURLConnection) new URL("https://example.com/").openConnection()).getResponseCode();
-        assertNotLogged("Detected cleartext network traffic from UID "
-                + android.os.Process.myUid(), mark, 5000);
+        assertNoViolation(() -> {
+            ((HttpURLConnection) new URL("https://example.com/").openConnection())
+                    .getResponseCode();
+        });
     }
 
     public void testFileUriExposure() throws Exception {
@@ -112,19 +113,21 @@
                 .penaltyLog()
                 .build());
 
-        long mark = System.currentTimeMillis();
-        Uri uri = Uri.fromFile(new File("/sdcard/meow.jpg"));
-        Intent intent = new Intent(Intent.ACTION_VIEW);
-        intent.setDataAndType(uri, "image/jpeg");
-        getContext().startActivity(intent);
-        assertLogged(uri + " exposed beyond app", mark);
+        final Uri badUri = Uri.fromFile(new File("/sdcard/meow.jpg"));
+        assertViolation(badUri + " exposed beyond app", () -> {
+            Intent intent = new Intent(Intent.ACTION_VIEW);
+            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+            intent.setDataAndType(badUri, "image/jpeg");
+            getContext().startActivity(intent);
+        });
 
-        mark = System.currentTimeMillis();
-        uri = Uri.parse("content://com.example/foobar");
-        intent = new Intent(Intent.ACTION_VIEW);
-        intent.setDataAndType(uri, "image/jpeg");
-        getContext().startActivity(intent);
-        assertNotLogged(uri + " exposed beyond app", mark);
+        final Uri goodUri = Uri.parse("content://com.example/foobar");
+        assertNoViolation(() -> {
+            Intent intent = new Intent(Intent.ACTION_VIEW);
+            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+            intent.setDataAndType(goodUri, "image/jpeg");
+            getContext().startActivity(intent);
+        });
     }
 
     public void testContentUriWithoutPermission() throws Exception {
@@ -133,19 +136,21 @@
                 .penaltyLog()
                 .build());
 
-        long mark = System.currentTimeMillis();
         final Uri uri = Uri.parse("content://com.example/foobar");
-        Intent intent = new Intent(Intent.ACTION_VIEW);
-        intent.setDataAndType(uri, "image/jpeg");
-        getContext().startActivity(intent);
-        assertLogged(uri + " exposed beyond app", mark);
+        assertViolation(uri + " exposed beyond app", () -> {
+            Intent intent = new Intent(Intent.ACTION_VIEW);
+            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+            intent.setDataAndType(uri, "image/jpeg");
+            getContext().startActivity(intent);
+        });
 
-        mark = System.currentTimeMillis();
-        intent = new Intent(Intent.ACTION_VIEW);
-        intent.setDataAndType(uri, "image/jpeg");
-        intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
-        getContext().startActivity(intent);
-        assertNotLogged(uri + " exposed beyond app", mark);
+        assertNoViolation(() -> {
+            Intent intent = new Intent(Intent.ACTION_VIEW);
+            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+            intent.setDataAndType(uri, "image/jpeg");
+            intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+            getContext().startActivity(intent);
+        });
     }
 
     public void testUntaggedSocketsHttp() throws Exception {
@@ -159,18 +164,20 @@
                 .penaltyLog()
                 .build());
 
-        long mark = System.currentTimeMillis();
-        ((HttpURLConnection) new URL("http://example.com/").openConnection()).getResponseCode();
-        assertLogged("Untagged socket detected", mark);
+        assertViolation("Untagged socket detected", () -> {
+            ((HttpURLConnection) new URL("http://example.com/").openConnection())
+                    .getResponseCode();
+        });
 
-        mark = System.currentTimeMillis();
-        TrafficStats.setThreadStatsTag(0xDECAFBAD);
-        try {
-            ((HttpURLConnection) new URL("http://example.com/").openConnection()).getResponseCode();
-        } finally {
-            TrafficStats.clearThreadStatsTag();
-        }
-        assertNotLogged("Untagged socket detected", mark);
+        assertNoViolation(() -> {
+            TrafficStats.setThreadStatsTag(0xDECAFBAD);
+            try {
+                ((HttpURLConnection) new URL("http://example.com/").openConnection())
+                        .getResponseCode();
+            } finally {
+                TrafficStats.clearThreadStatsTag();
+            }
+        });
     }
 
     public void testUntaggedSocketsRaw() throws Exception {
@@ -184,99 +191,95 @@
                 .penaltyLog()
                 .build());
 
-        long mark = System.currentTimeMillis();
-        TrafficStats.setThreadStatsTag(0xDECAFBAD);
-        try (Socket socket = new Socket("example.com", 80)) {
-            socket.getOutputStream().close();
-        } finally {
-            TrafficStats.clearThreadStatsTag();
-        }
-        assertNotLogged("Untagged socket detected", mark);
+        assertNoViolation(() -> {
+            TrafficStats.setThreadStatsTag(0xDECAFBAD);
+            try (Socket socket = new Socket("example.com", 80)) {
+                socket.getOutputStream().close();
+            } finally {
+                TrafficStats.clearThreadStatsTag();
+            }
+        });
 
-        mark = System.currentTimeMillis();
-        try (Socket socket = new Socket("example.com", 80)) {
-            socket.getOutputStream().close();
-        }
-        assertLogged("Untagged socket detected", mark);
+        assertViolation("Untagged socket detected", () -> {
+            try (Socket socket = new Socket("example.com", 80)) {
+                socket.getOutputStream().close();
+            }
+        });
     }
 
     public void testRead() throws Exception {
         final File test = File.createTempFile("foo", "bar");
         final File dir = test.getParentFile();
 
-        FileInputStream is = null;
-        FileDescriptor fd = null;
+        final FileInputStream is = new FileInputStream(test);
+        final FileDescriptor fd = Os.open(test.getAbsolutePath(), OsConstants.O_RDONLY, 0600);
 
         StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
                 .detectDiskReads()
                 .penaltyLog()
                 .build());
 
-        SystemClock.sleep(1500);
-
-        try (AssertDiskReadLogged l = new AssertDiskReadLogged()) {
+        assertViolation("StrictModeDiskReadViolation", () -> {
             test.exists();
-        }
-        try (AssertDiskReadLogged l = new AssertDiskReadLogged()) {
+        });
+        assertViolation("StrictModeDiskReadViolation", () -> {
             test.length();
-        }
-        try (AssertDiskReadLogged l = new AssertDiskReadLogged()) {
+        });
+        assertViolation("StrictModeDiskReadViolation", () -> {
             dir.list();
-        }
-        try (AssertDiskReadLogged l = new AssertDiskReadLogged()) {
-            is = new FileInputStream(test);
-        }
-        try (AssertDiskReadLogged l = new AssertDiskReadLogged()) {
+        });
+        assertViolation("StrictModeDiskReadViolation", () -> {
+            new FileInputStream(test);
+        });
+        assertViolation("StrictModeDiskReadViolation", () -> {
             is.read();
-        }
-        try (AssertDiskReadLogged l = new AssertDiskReadLogged()) {
-            fd = Os.open(test.getAbsolutePath(), OsConstants.O_RDONLY, 0600);
-        }
-        try (AssertDiskReadLogged l = new AssertDiskReadLogged()) {
+        });
+        assertViolation("StrictModeDiskReadViolation", () -> {
+            Os.open(test.getAbsolutePath(), OsConstants.O_RDONLY, 0600);
+        });
+        assertViolation("StrictModeDiskReadViolation", () -> {
             Os.read(fd, new byte[10], 0, 1);
-        }
+        });
     }
 
     public void testWrite() throws Exception {
-        File file = null;
+        File file = File.createTempFile("foo", "bar");
 
-        FileOutputStream os = null;
-        FileDescriptor fd = null;
+        final FileOutputStream os = new FileOutputStream(file);
+        final FileDescriptor fd = Os.open(file.getAbsolutePath(), OsConstants.O_RDWR, 0600);
 
         StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
                 .detectDiskWrites()
                 .penaltyLog()
                 .build());
 
-        SystemClock.sleep(1500);
-
-        try (AssertDiskWriteLogged l = new AssertDiskWriteLogged()) {
-            file = File.createTempFile("foo", "bar");
-        }
-        try (AssertDiskWriteLogged l = new AssertDiskWriteLogged()) {
+        assertViolation("StrictModeDiskWriteViolation", () -> {
+            File.createTempFile("foo", "bar");
+        });
+        assertViolation("StrictModeDiskWriteViolation", () -> {
             file.delete();
-        }
-        try (AssertDiskWriteLogged l = new AssertDiskWriteLogged()) {
+        });
+        assertViolation("StrictModeDiskWriteViolation", () -> {
             file.createNewFile();
-        }
-        try (AssertDiskWriteLogged l = new AssertDiskWriteLogged()) {
-            os = new FileOutputStream(file);
-        }
-        try (AssertDiskWriteLogged l = new AssertDiskWriteLogged()) {
+        });
+        assertViolation("StrictModeDiskWriteViolation", () -> {
+            new FileOutputStream(file);
+        });
+        assertViolation("StrictModeDiskWriteViolation", () -> {
             os.write(32);
-        }
-        try (AssertDiskWriteLogged l = new AssertDiskWriteLogged()) {
-            fd = Os.open(file.getAbsolutePath(), OsConstants.O_RDWR, 0600);
-        }
-        try (AssertDiskWriteLogged l = new AssertDiskWriteLogged()) {
+        });
+        assertViolation("StrictModeDiskWriteViolation", () -> {
+            Os.open(file.getAbsolutePath(), OsConstants.O_RDWR, 0600);
+        });
+        assertViolation("StrictModeDiskWriteViolation", () -> {
             Os.write(fd, new byte[10], 0, 1);
-        }
-        try (AssertDiskWriteLogged l = new AssertDiskWriteLogged()) {
+        });
+        assertViolation("StrictModeDiskWriteViolation", () -> {
             Os.fsync(fd);
-        }
-        try (AssertDiskWriteLogged l = new AssertDiskWriteLogged()) {
+        });
+        assertViolation("StrictModeDiskWriteViolation", () -> {
             file.renameTo(new File(file.getParent(), "foobar"));
-        }
+        });
     }
 
     public void testNetwork() throws Exception {
@@ -290,75 +293,66 @@
                 .penaltyLog()
                 .build());
 
-        long mark = System.currentTimeMillis();
-        try (Socket socket = new Socket("example.com", 80)) {
-            socket.getOutputStream().close();
-        }
-        assertLogged("StrictModeNetworkViolation", mark);
+        assertViolation("StrictModeNetworkViolation", () -> {
+            try (Socket socket = new Socket("example.com", 80)) {
+                socket.getOutputStream().close();
+            }
+        });
 
-        mark = System.currentTimeMillis();
-        ((HttpURLConnection) new URL("http://example.com/").openConnection()).getResponseCode();
-        assertLogged("StrictModeNetworkViolation", mark);
+        assertViolation("StrictModeNetworkViolation", () -> {
+            ((HttpURLConnection) new URL("http://example.com/").openConnection())
+                    .getResponseCode();
+        });
     }
 
-    private static class AssertLogged implements AutoCloseable {
-        private final String mMessage;
-        private final long mStart;
+    private static void assertViolation(String expected, ThrowingRunnable r) throws Exception {
+        final LinkedBlockingQueue<String> violations = new LinkedBlockingQueue<>();
+        StrictMode.setViolationListener(new ViolationListener() {
+            @Override
+            public void onViolation(String message) {
+                violations.add(message);
+            }
+        });
 
-        public AssertLogged(String message) {
-            mMessage = message;
-            mStart = System.currentTimeMillis();
-        }
-
-        @Override
-        public void close() throws Exception {
-            assertLogged(mMessage, mStart);
+        try {
+            r.run();
+            while (true) {
+                final String violation = violations.poll(5, TimeUnit.SECONDS);
+                if (violation == null) {
+                    fail("Expected violation not found: " + expected);
+                } else if (violation.contains(expected)) {
+                    return;
+                }
+            }
+        } finally {
+            StrictMode.setViolationListener(null);
         }
     }
 
-    private static class AssertDiskReadLogged extends AssertLogged {
-        public AssertDiskReadLogged() {
-            super("StrictModeDiskReadViolation");
+    private static void assertNoViolation(ThrowingRunnable r) throws Exception {
+        final LinkedBlockingQueue<String> violations = new LinkedBlockingQueue<>();
+        StrictMode.setViolationListener(new ViolationListener() {
+            @Override
+            public void onViolation(String message) {
+                violations.add(message);
+            }
+        });
+
+        try {
+            r.run();
+            while (true) {
+                final String violation = violations.poll(5, TimeUnit.SECONDS);
+                if (violation == null) {
+                    return;
+                } else {
+                    fail("Unexpected violation found: " + violation);
+                }
+            }
+        } finally {
+            StrictMode.setViolationListener(null);
         }
     }
 
-    private static class AssertDiskWriteLogged extends AssertLogged {
-        public AssertDiskWriteLogged() {
-            super("StrictModeDiskWriteViolation");
-        }
-    }
-
-    private static void assertLogged(String msg, long since) throws Exception {
-        assertLogged(msg, since, 1100);
-    }
-
-    private static void assertLogged(String msg, long since, long wait) throws Exception {
-        SystemClock.sleep(wait);
-        assertTrue("Expected message not found: " + msg, readLogSince(since).contains(msg));
-    }
-
-    private static void assertNotLogged(String msg, long since) throws Exception {
-        assertNotLogged(msg, since, 1100);
-    }
-
-    private static void assertNotLogged(String msg, long since, long wait) throws Exception {
-        SystemClock.sleep(wait);
-        assertFalse("Unexpected message found: " + msg, readLogSince(since).contains(msg));
-    }
-
-    private static String readLogSince(long millis) throws Exception {
-        final SimpleDateFormat format = new SimpleDateFormat("MM-dd HH:mm:ss.SSS");
-        final Process proc = new ProcessBuilder("logcat", "-t", format.format(new Date(millis)))
-                .redirectErrorStream(true).start();
-
-        final ByteArrayOutputStream buf = new ByteArrayOutputStream();
-        Streams.copy(proc.getInputStream(), buf);
-        final int res = proc.waitFor();
-
-        Log.d(TAG, "Log output was " + buf.size() + " bytes, exit code " + res);
-        return new String(buf.toByteArray());
-    }
-
     private boolean hasInternetConnection() {
         final PackageManager pm = getContext().getPackageManager();
         return pm.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)
diff --git a/tests/tests/provider/AndroidManifest.xml b/tests/tests/provider/AndroidManifest.xml
index 284841b..380ddd0 100644
--- a/tests/tests/provider/AndroidManifest.xml
+++ b/tests/tests/provider/AndroidManifest.xml
@@ -33,6 +33,8 @@
     <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
     <uses-permission android:name="android.permission.WRITE_SETTINGS" />
     <uses-permission android:name="com.android.voicemail.permission.ADD_VOICEMAIL" />
+    <uses-permission android:name="com.android.voicemail.permission.WRITE_VOICEMAIL" />
+    <uses-permission android:name="com.android.voicemail.permission.READ_VOICEMAIL" />
     <uses-permission android:name="android.permission.WRITE_CALL_LOG" />
     <uses-permission android:name="android.permission.WRITE_CONTACTS" />
     <uses-permission android:name="android.permission.READ_CALL_LOG" />
@@ -94,6 +96,27 @@
                   android:exported="false"
                   android:multiprocess="true" />
 
+        <service
+            android:name="android.provider.cts.contacts.StubInCallService"
+            android:permission="android.permission.BIND_INCALL_SERVICE">
+            <intent-filter>
+                <action android:name="android.telecom.InCallService"/>
+            </intent-filter>
+            <meta-data
+                android:name="android.telecom.IN_CALL_SERVICE_UI"
+                android:value="true"/>
+        </service>
+
+        <activity android:name="android.provider.cts.contacts.StubDialerActivity">
+            <intent-filter>
+                <action android:name="android.intent.action.DIAL"/>
+                <data android:scheme="tel"/>
+            </intent-filter>
+            <intent-filter>
+                <action android:name="android.intent.action.DIAL"/>
+            </intent-filter>
+        </activity>
+
     </application>
 
     <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
diff --git a/tests/tests/provider/src/android/provider/cts/contacts/StubDialerActivity.java b/tests/tests/provider/src/android/provider/cts/contacts/StubDialerActivity.java
new file mode 100644
index 0000000..b87343c
--- /dev/null
+++ b/tests/tests/provider/src/android/provider/cts/contacts/StubDialerActivity.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.provider.cts.contacts;
+
+import android.app.Activity;
+
+/**
+ * Does nothing. Listens to the dial intent to make the test eligible to be set as the default
+ * dialer.
+ */
+public class StubDialerActivity extends Activity {
+
+
+}
diff --git a/tests/tests/provider/src/android/provider/cts/contacts/StubInCallService.java b/tests/tests/provider/src/android/provider/cts/contacts/StubInCallService.java
new file mode 100644
index 0000000..4d84782
--- /dev/null
+++ b/tests/tests/provider/src/android/provider/cts/contacts/StubInCallService.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.provider.cts.contacts;
+
+import android.telecom.InCallService;
+
+/**
+ * An in-call service that does nothing except allowing CTS provider to be set as a default dialer.
+ */
+public class StubInCallService extends InCallService {
+
+}
diff --git a/tests/tests/provider/src/android/provider/cts/contacts/VoicemailContractTest.java b/tests/tests/provider/src/android/provider/cts/contacts/VoicemailContractTest.java
index 4861669..75561f9 100644
--- a/tests/tests/provider/src/android/provider/cts/contacts/VoicemailContractTest.java
+++ b/tests/tests/provider/src/android/provider/cts/contacts/VoicemailContractTest.java
@@ -16,16 +16,25 @@
 
 package android.provider.cts.contacts;
 
+import android.app.Instrumentation;
 import android.content.ContentProviderClient;
 import android.content.ContentResolver;
 import android.content.ContentValues;
 import android.database.Cursor;
 import android.net.Uri;
+import android.os.ParcelFileDescriptor;
 import android.os.RemoteException;
 import android.provider.VoicemailContract;
 import android.provider.VoicemailContract.Status;
 import android.provider.VoicemailContract.Voicemails;
 import android.test.InstrumentationTestCase;
+import android.text.TextUtils;
+
+import java.io.BufferedReader;
+import java.io.FileInputStream;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.nio.charset.StandardCharsets;
 
 /**
  * CTS tests for voicemail provider accessed through {@link VoicemailContract}.
@@ -38,6 +47,15 @@
     private Uri mStatusContentUri;
     private String mSourcePackageName;
 
+    private String mPreviousDefaultDialer;
+
+    private static final String COMMAND_SET_DEFAULT_DIALER = "telecom set-default-dialer ";
+    private static final String COMMAND_GET_DEFAULT_DIALER = "telecom get-default-dialer";
+
+    private static final String PACKAGE = "android.provider.cts";
+
+    private final String FOREIGN_SOURCE = "android.provider.cts.contacts.foreign_source";
+
     @Override
     protected void setUp() throws Exception {
         super.setUp();
@@ -56,6 +74,9 @@
         // entries inserted by this package.
         mStatusProvider.delete(mStatusContentUri, null, null);
         mVoicemailProvider.delete(mVoicemailContentUri, null, null);
+        if (!TextUtils.isEmpty(mPreviousDefaultDialer)) {
+            setDefaultDialer(getInstrumentation(), mPreviousDefaultDialer);
+        }
         super.tearDown();
     }
 
@@ -169,8 +190,7 @@
         assertEquals(updateDate, cursor.getLong(DATE_INDEX));
         assertEquals(updateCallsDuration, cursor.getLong(DURATION_INDEX));
         assertEquals(updateSourceData, cursor.getString(SOURCE_DATA_INDEX));
-        // Self modifying so DIRTY should be overridden to 0
-        assertEquals(0,cursor.getInt(DIRTY_INDEX));
+        assertEquals(1,cursor.getInt(DIRTY_INDEX));
         assertEquals(1,cursor.getInt(DELETED_INDEX));
         assertEquals(1,cursor.getInt(BACKED_UP_INDEX));
         assertEquals(1,cursor.getInt(RESTORED_INDEX));
@@ -186,6 +206,95 @@
         cursor.close();
     }
 
+    public void testForeignUpdate_dirty() throws Exception {
+        // only the default dialer has WRITE_VOICEMAIL permission, which can modify voicemails of
+        // a foreign source package.
+        setTestAsDefaultDialer();
+        ContentValues values = new ContentValues();
+        values.put(Voicemails.SOURCE_PACKAGE, FOREIGN_SOURCE);
+
+        Uri uri = mVoicemailProvider.insert(Voicemails.buildSourceUri(FOREIGN_SOURCE), values);
+
+        mVoicemailProvider.update(uri, new ContentValues(), null, null);
+
+        try (Cursor cursor = mVoicemailProvider
+                .query(uri, new String[] {Voicemails.DIRTY}, null, null, null)) {
+            cursor.moveToFirst();
+            assertEquals(1, cursor.getInt(0));
+        }
+    }
+
+    public void testForeignUpdate_explicitNotDirty() throws Exception {
+        setTestAsDefaultDialer();
+        ContentValues values = new ContentValues();
+        values.put(Voicemails.SOURCE_PACKAGE, FOREIGN_SOURCE);
+
+        Uri uri = mVoicemailProvider.insert(Voicemails.buildSourceUri(FOREIGN_SOURCE), values);
+
+        ContentValues updateValues = new ContentValues();
+        updateValues.put(Voicemails.DIRTY,0);
+        mVoicemailProvider.update(uri, updateValues, null, null);
+
+        try (Cursor cursor = mVoicemailProvider
+                .query(uri, new String[] {Voicemails.DIRTY}, null, null, null)) {
+            cursor.moveToFirst();
+            assertEquals(0, cursor.getInt(0));
+        }
+    }
+
+    public void testForeignUpdate_null_dirty() throws Exception {
+        setTestAsDefaultDialer();
+        ContentValues values = new ContentValues();
+        values.put(Voicemails.SOURCE_PACKAGE, FOREIGN_SOURCE);
+
+        Uri uri = mVoicemailProvider.insert(Voicemails.buildSourceUri(FOREIGN_SOURCE), values);
+
+        ContentValues updateValues = new ContentValues();
+        updateValues.put(Voicemails.DIRTY, (Integer) null);
+        mVoicemailProvider.update(uri, updateValues, null, null);
+
+        try (Cursor cursor = mVoicemailProvider
+                .query(uri, new String[] {Voicemails.DIRTY}, null, null, null)) {
+            cursor.moveToFirst();
+            assertEquals(1, cursor.getInt(0));
+        }
+    }
+
+    public void testForeignUpdate_NotNormalized_normalized() throws Exception {
+        setTestAsDefaultDialer();
+        ContentValues values = new ContentValues();
+        values.put(Voicemails.SOURCE_PACKAGE, FOREIGN_SOURCE);
+
+        Uri uri = mVoicemailProvider.insert(Voicemails.buildSourceUri(FOREIGN_SOURCE), values);
+
+        ContentValues updateValues = new ContentValues();
+        updateValues.put(Voicemails.DIRTY, 2);
+        mVoicemailProvider.update(uri, updateValues, null, null);
+
+        try (Cursor cursor = mVoicemailProvider
+                .query(uri, new String[] {Voicemails.DIRTY}, null, null, null)) {
+            cursor.moveToFirst();
+            assertEquals(1, cursor.getInt(0));
+        }
+    }
+
+    public void testLocalUpdate_notDirty() throws Exception {
+
+        ContentValues values = new ContentValues();
+        values.put(Voicemails.DIRTY,1);
+
+        Uri uri = mVoicemailProvider.insert(Voicemails.buildSourceUri(mSourcePackageName), values);
+
+        mVoicemailProvider.update(uri, new ContentValues(), null, null);
+
+        try (Cursor cursor = mVoicemailProvider
+                .query(uri, new String[] {Voicemails.DIRTY}, null, null, null)) {
+            cursor.moveToFirst();
+            assertEquals(cursor.getInt(0), 0);
+        }
+    }
+
+
     // Data column should be automatically generated during insert.
     public void testInsert_doesNotUpdateDataColumn() throws Exception {
 
@@ -356,4 +465,57 @@
             // Expected result.
         }
     }
+
+    private void setTestAsDefaultDialer() throws Exception{
+        assertTrue(mPreviousDefaultDialer == null);
+        mPreviousDefaultDialer = getDefaultDialer(getInstrumentation());
+        setDefaultDialer(getInstrumentation(),PACKAGE);
+    }
+
+    private static String setDefaultDialer(Instrumentation instrumentation, String packageName)
+            throws Exception {
+        return executeShellCommand(instrumentation, COMMAND_SET_DEFAULT_DIALER + packageName);
+    }
+
+    private static String getDefaultDialer(Instrumentation instrumentation) throws Exception {
+        return executeShellCommand(instrumentation, COMMAND_GET_DEFAULT_DIALER);
+    }
+
+    /**
+     * Executes the given shell command and returns the output in a string. Note that even if we
+     * don't care about the output, we have to read the stream completely to make the command
+     * execute.
+     */
+    private static String executeShellCommand(Instrumentation instrumentation,
+            String command) throws Exception {
+        final ParcelFileDescriptor parcelFileDescriptor =
+                instrumentation.getUiAutomation().executeShellCommand(command);
+        BufferedReader bufferedReader = null;
+        try (InputStream in = new FileInputStream(parcelFileDescriptor.getFileDescriptor())) {
+            bufferedReader = new BufferedReader(new InputStreamReader(in, StandardCharsets.UTF_8));
+            String string = null;
+            StringBuilder out = new StringBuilder();
+            while ((string = bufferedReader.readLine()) != null) {
+                out.append(string);
+            }
+            return out.toString();
+        } finally {
+            if (bufferedReader != null) {
+                closeQuietly(bufferedReader);
+            }
+            closeQuietly(parcelFileDescriptor);
+        }
+    }
+
+    private static void closeQuietly(AutoCloseable closeable) {
+        if (closeable != null) {
+            try {
+                closeable.close();
+            } catch (RuntimeException rethrown) {
+                throw rethrown;
+            } catch (Exception ignored) {
+                // Quietly.
+            }
+        }
+    }
 }
diff --git a/tests/tests/view/res/layout/default_focus_highlight_layout.xml b/tests/tests/view/res/layout/default_focus_highlight_layout.xml
index 827de43..44a0f68 100644
--- a/tests/tests/view/res/layout/default_focus_highlight_layout.xml
+++ b/tests/tests/view/res/layout/default_focus_highlight_layout.xml
@@ -89,5 +89,16 @@
         android:orientation="vertical">
     </LinearLayout>
 
+    <Button
+        android:id="@+id/button_to_test_highlight_needed"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:text="@string/id_ok"/>
+
+    <ImageView
+        android:id="@+id/image_view_to_test_highlight_needed"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:text="@string/id_ok"/>
 </LinearLayout>
 
diff --git a/apps/CtsVerifier/res/xml/tapjacking_device_admin.xml b/tests/tests/view/res/layout/include_layout_explicit.xml
similarity index 60%
copy from apps/CtsVerifier/res/xml/tapjacking_device_admin.xml
copy to tests/tests/view/res/layout/include_layout_explicit.xml
index d884663..e13301e 100644
--- a/apps/CtsVerifier/res/xml/tapjacking_device_admin.xml
+++ b/tests/tests/view/res/layout/include_layout_explicit.xml
@@ -5,7 +5,7 @@
      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
+         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,
@@ -14,15 +14,7 @@
      limitations under the License.
 -->
 
-<device-admin xmlns:android="http://schemas.android.com/apk/res/android">
-    <uses-policies>
-        <limit-password />
-        <watch-login />
-        <encrypted-storage />
-        <wipe-data />
-        <reset-password />
-        <disable-keyguard-features />
-        <force-lock />
-        <limit-password />
-    </uses-policies>
-</device-admin>
+<View xmlns:android="http://schemas.android.com/apk/res/android"
+      android:layout_width="match_parent"
+      android:layout_height="wrap_content"
+      android:id="@+id/include_layout_explicit" />
diff --git a/apps/CtsVerifier/res/xml/tapjacking_device_admin.xml b/tests/tests/view/res/layout/include_layout_from_attr.xml
similarity index 60%
rename from apps/CtsVerifier/res/xml/tapjacking_device_admin.xml
rename to tests/tests/view/res/layout/include_layout_from_attr.xml
index d884663..e0e8fd3 100644
--- a/apps/CtsVerifier/res/xml/tapjacking_device_admin.xml
+++ b/tests/tests/view/res/layout/include_layout_from_attr.xml
@@ -5,7 +5,7 @@
      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
+         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,
@@ -14,15 +14,7 @@
      limitations under the License.
 -->
 
-<device-admin xmlns:android="http://schemas.android.com/apk/res/android">
-    <uses-policies>
-        <limit-password />
-        <watch-login />
-        <encrypted-storage />
-        <wipe-data />
-        <reset-password />
-        <disable-keyguard-features />
-        <force-lock />
-        <limit-password />
-    </uses-policies>
-</device-admin>
+<View xmlns:android="http://schemas.android.com/apk/res/android"
+      android:layout_width="match_parent"
+      android:layout_height="wrap_content"
+      android:id="@+id/include_layout_from_attr" />
diff --git a/apps/CtsVerifier/res/xml/tapjacking_device_admin.xml b/tests/tests/view/res/layout/inflater_layout_include.xml
similarity index 60%
copy from apps/CtsVerifier/res/xml/tapjacking_device_admin.xml
copy to tests/tests/view/res/layout/inflater_layout_include.xml
index d884663..7423830 100644
--- a/apps/CtsVerifier/res/xml/tapjacking_device_admin.xml
+++ b/tests/tests/view/res/layout/inflater_layout_include.xml
@@ -5,7 +5,7 @@
      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
+         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,
@@ -14,15 +14,12 @@
      limitations under the License.
 -->
 
-<device-admin xmlns:android="http://schemas.android.com/apk/res/android">
-    <uses-policies>
-        <limit-password />
-        <watch-login />
-        <encrypted-storage />
-        <wipe-data />
-        <reset-password />
-        <disable-keyguard-features />
-        <force-lock />
-        <limit-password />
-    </uses-policies>
-</device-admin>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+              android:layout_width="match_parent"
+              android:layout_height="wrap_content">
+
+    <include layout="@layout/include_layout_explicit" />
+
+    <include layout="?attr/includeLayout" />
+
+</LinearLayout>
diff --git a/tests/tests/view/res/layout/view_padding.xml b/tests/tests/view/res/layout/view_padding.xml
index e3b0deb..c0e6e90 100644
--- a/tests/tests/view/res/layout/view_padding.xml
+++ b/tests/tests/view/res/layout/view_padding.xml
@@ -132,10 +132,12 @@
     <LinearLayout
             android:orientation="horizontal"
             android:padding="@dimen/insetAll"
-            android:paddingLeft="0dp"
-            android:paddingTop="0dp"
-            android:paddingRight="0dp"
-            android:paddingBottom="0dp"
+            android:paddingHorizontal="@dimen/insetHorizontal"
+            android:paddingVertical="@dimen/insetVertical"
+            android:paddingLeft="@dimen/insetLeft"
+            android:paddingTop="@dimen/insetTop"
+            android:paddingRight="@dimen/insetRight"
+            android:paddingBottom="@dimen/insetBottom"
             android:layout_width="match_parent"
             android:layout_height="100dp">
         <View
@@ -144,5 +146,32 @@
                 android:id="@+id/view10"/>
     </LinearLayout>
 
+    <LinearLayout
+            android:orientation="horizontal"
+            android:padding="@dimen/insetAll"
+            android:paddingStart="@dimen/insetStart"
+            android:paddingEnd="@dimen/insetEnd"
+            android:layout_width="match_parent"
+            android:layout_height="100dp">
+        <View
+                android:layout_width="match_parent"
+                android:layout_height="match_parent"
+                android:id="@+id/view11"/>
+    </LinearLayout>
+
+    <LinearLayout
+            android:orientation="horizontal"
+            android:paddingHorizontal="@dimen/insetHorizontal"
+            android:paddingStart="@dimen/insetStart"
+            android:paddingEnd="@dimen/insetEnd"
+            android:layout_width="match_parent"
+            android:layout_height="100dp">
+        <View
+                android:layout_width="match_parent"
+                android:layout_height="match_parent"
+                android:id="@+id/view12"/>
+    </LinearLayout>
+
+
 </LinearLayout>
 
diff --git a/tests/tests/view/res/layout/viewgroup_margin_layout_verticalhorizontal.xml b/tests/tests/view/res/layout/viewgroup_margin_layout_verticalhorizontal.xml
index a1c4051..3049623 100644
--- a/tests/tests/view/res/layout/viewgroup_margin_layout_verticalhorizontal.xml
+++ b/tests/tests/view/res/layout/viewgroup_margin_layout_verticalhorizontal.xml
@@ -36,7 +36,7 @@
         <View
                 android:layout_width="match_parent"
                 android:layout_height="match_parent"
-                android:layout_marginHorizontal="10dp"
+                android:layout_marginHorizontal="@dimen/marginHorizontal"
                 android:id="@+id/view2"/>
     </LinearLayout>
 
@@ -47,7 +47,7 @@
         <View
                 android:layout_width="match_parent"
                 android:layout_height="match_parent"
-                android:layout_marginVertical="10dp"
+                android:layout_marginVertical="@dimen/marginVertical"
                 android:id="@+id/view3"/>
     </LinearLayout>
 
@@ -58,8 +58,8 @@
         <View
                 android:layout_width="match_parent"
                 android:layout_height="match_parent"
-                android:layout_marginHorizontal="10dp"
-                android:layout_marginVertical="10dp"
+                android:layout_marginHorizontal="@dimen/marginHorizontal"
+                android:layout_marginVertical="@dimen/marginVertical"
                 android:id="@+id/view4"/>
     </LinearLayout>
 
@@ -70,16 +70,15 @@
         <View
                 android:layout_width="match_parent"
                 android:layout_height="match_parent"
-                android:layout_marginHorizontal="10dp"
-                android:layout_marginLeft="5dp"
-                android:layout_marginRight="2dp"
-                android:layout_marginVertical="10dp"
-                android:layout_marginTop="5dp"
-                android:layout_marginBottom="2dp"
+                android:layout_marginHorizontal="@dimen/marginHorizontal"
+                android:layout_marginLeft="@dimen/marginLeft"
+                android:layout_marginRight="@dimen/marginRight"
+                android:layout_marginVertical="@dimen/marginVertical"
+                android:layout_marginTop="@dimen/marginTop"
+                android:layout_marginBottom="@dimen/marginBottom"
                 android:id="@+id/view5"/>
     </LinearLayout>
 
-
     <LinearLayout
             android:orientation="horizontal"
             android:layout_width="match_parent"
@@ -87,12 +86,26 @@
         <View
                 android:layout_width="match_parent"
                 android:layout_height="match_parent"
-                android:layout_marginHorizontal="10dp"
-                android:layout_marginStart="5dp"
-                android:layout_marginEnd="2dp"
-                android:layout_marginVertical="10dp"
+                android:layout_marginHorizontal="@dimen/marginHorizontal"
+                android:layout_marginStart="@dimen/marginStart"
+                android:layout_marginEnd="@dimen/marginEnd"
+                android:layout_marginVertical="@dimen/marginVertical"
                 android:id="@+id/view6"/>
     </LinearLayout>
 
+    <LinearLayout
+            android:orientation="horizontal"
+            android:layout_width="match_parent"
+            android:layout_height="100dp">
+        <View
+                android:layout_width="match_parent"
+                android:layout_height="match_parent"
+                android:layout_margin="@dimen/marginAll"
+                android:layout_marginStart="@dimen/marginStart"
+                android:layout_marginEnd="@dimen/marginEnd"
+                android:layout_marginVertical="@dimen/marginVertical"
+                android:id="@+id/view7"/>
+    </LinearLayout>
+
 </LinearLayout>
 
diff --git a/tests/tests/view/res/values/attrs.xml b/tests/tests/view/res/values/attrs.xml
index 4c3d9db..e7181fc 100644
--- a/tests/tests/view/res/values/attrs.xml
+++ b/tests/tests/view/res/values/attrs.xml
@@ -129,7 +129,6 @@
     <attr name="themeType" format="integer"/>
     <!-- Theme reference used to override parent theme. -->
     <attr name="themeOverrideAttr" format="reference"/>
-
     <!-- Drawable theming attributes -->
     <attr name="themeBoolean" />
     <attr name="themeColor" />
@@ -142,4 +141,6 @@
     <attr name="themeGravity" />
     <attr name="themeTileMode" />
     <attr name="themeAngle" />
+    <!-- Used by LayoutInflaterTest to specify layout from ?attr. -->
+    <attr name="includeLayout" format="reference" />
 </resources>
diff --git a/tests/tests/view/res/values/dimens.xml b/tests/tests/view/res/values/dimens.xml
index 59369a7..5680de0 100644
--- a/tests/tests/view/res/values/dimens.xml
+++ b/tests/tests/view/res/values/dimens.xml
@@ -24,9 +24,20 @@
     <dimen name="insetTop">6dp</dimen>
     <dimen name="insetRight">7dp</dimen>
     <dimen name="insetBottom">8dp</dimen>
-    <dimen name="insetAll">9dp</dimen>
-    <dimen name="insetHorizontal">10dp</dimen>
-    <dimen name="insetVertical">11dp</dimen>
+    <dimen name="insetStart">9dp</dimen>
+    <dimen name="insetEnd">10dp</dimen>
+    <dimen name="insetAll">11dp</dimen>
+    <dimen name="insetHorizontal">12dp</dimen>
+    <dimen name="insetVertical">13dp</dimen>
+    <dimen name="marginLeft">5dp</dimen>
+    <dimen name="marginTop">6dp</dimen>
+    <dimen name="marginRight">7dp</dimen>
+    <dimen name="marginBottom">8dp</dimen>
+    <dimen name="marginStart">9dp</dimen>
+    <dimen name="marginEnd">10dp</dimen>
+    <dimen name="marginAll">11dp</dimen>
+    <dimen name="marginHorizontal">12dp</dimen>
+    <dimen name="marginVertical">13dp</dimen>
     <dimen name="hover_target_margin">4dp</dimen>
     <dimen name="hover_target_size">8dp</dimen>
     <dimen name="hover_target_size_double">16dp</dimen>
diff --git a/tests/tests/view/res/values/styles.xml b/tests/tests/view/res/values/styles.xml
index 4979241..17bc2a3 100644
--- a/tests/tests/view/res/values/styles.xml
+++ b/tests/tests/view/res/values/styles.xml
@@ -136,6 +136,7 @@
         <item name="android:windowNoTitle">true</item>
         <item name="android:panelColorForeground">#ff000000</item>
         <item name="android:panelColorBackground">#ffffffff</item>
+        <item name="includeLayout">@layout/include_layout_from_attr</item>
     </style>
 
     <style name="Theme_OverrideOuter">
diff --git a/tests/tests/view/src/android/view/cts/LayoutInflaterTest.java b/tests/tests/view/src/android/view/cts/LayoutInflaterTest.java
index b445165..3460525 100644
--- a/tests/tests/view/src/android/view/cts/LayoutInflaterTest.java
+++ b/tests/tests/view/src/android/view/cts/LayoutInflaterTest.java
@@ -37,6 +37,7 @@
 import android.util.AttributeSet;
 import android.util.TypedValue;
 import android.util.Xml;
+import android.view.ContextThemeWrapper;
 import android.view.Gravity;
 import android.view.InflateException;
 import android.view.LayoutInflater;
@@ -395,6 +396,22 @@
         assertEquals(tagId + " has tag " + expectedValue, expectedValue, tag);
     }
 
+    @Test
+    public void testInclude() {
+        final Context themedContext = new ContextThemeWrapper(mContext, R.style.Test_Theme);
+        final LayoutInflater inflater = LayoutInflater.from(themedContext);
+
+        final View container = inflater.inflate(R.layout.inflater_layout_include, null);
+        assertNotNull(container.findViewById(R.id.include_layout_explicit));
+        assertNotNull(container.findViewById(R.id.include_layout_from_attr));
+    }
+
+    @Test(expected = InflateException.class)
+    public void testIncludeMissingAttr() {
+        final View container = mLayoutInflater.inflate(R.layout.inflater_layout_include, null);
+        assertNotNull(container.findViewById(R.id.include_layout_from_attr));
+    }
+
     static class MockLayoutInflater extends LayoutInflater {
 
         public MockLayoutInflater(Context c) {
diff --git a/tests/tests/view/src/android/view/cts/ViewGroup_MarginLayoutParamsTest.java b/tests/tests/view/src/android/view/cts/ViewGroup_MarginLayoutParamsTest.java
index 34a6020..0867338 100644
--- a/tests/tests/view/src/android/view/cts/ViewGroup_MarginLayoutParamsTest.java
+++ b/tests/tests/view/src/android/view/cts/ViewGroup_MarginLayoutParamsTest.java
@@ -22,6 +22,7 @@
 
 import android.content.Context;
 import android.content.res.XmlResourceParser;
+import android.graphics.Rect;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
@@ -234,27 +235,32 @@
         View view4 = viewGroup.findViewById(R.id.view4);
         View view5 = viewGroup.findViewById(R.id.view5);
         View view6 = viewGroup.findViewById(R.id.view6);
+        View view7 = viewGroup.findViewById(R.id.view7);
 
-        int defaultWidth = view1.getWidth();
-        int defaultHeight = view1.getHeight();
-        int marginPixels = (int) (mContext.getResources().getDisplayMetrics().density * 10 + .5f);
+        Rect defaultBounds = new Rect(view1.getLeft(), view1.getTop(), view1.getRight(),
+                view1.getBottom());
+        int marginStart = mContext.getResources().getDimensionPixelSize(R.dimen.marginStart);
+        int marginEnd = mContext.getResources().getDimensionPixelSize(R.dimen.marginEnd);
+        int marginAll = mContext.getResources().getDimensionPixelSize(R.dimen.marginAll);
+        int marginHorizontal =
+                mContext.getResources().getDimensionPixelSize(R.dimen.marginHorizontal);
+        int marginVertical = mContext.getResources().getDimensionPixelSize(R.dimen.marginVertical);
 
-        assertEquals("Width value", defaultWidth, view1.getWidth());
-        assertEquals("Height value", defaultHeight, view1.getHeight());
+        checkBounds(view2, defaultBounds, marginHorizontal, 0, marginHorizontal, 0);
+        checkBounds(view3, defaultBounds, 0, marginVertical, 0, marginVertical);
+        checkBounds(view4, defaultBounds, marginHorizontal, marginVertical,
+                marginHorizontal, marginVertical);
+        checkBounds(view5, defaultBounds, marginHorizontal, marginVertical,
+                marginHorizontal, marginVertical);
+        checkBounds(view6, defaultBounds, marginStart, marginVertical, marginEnd, marginVertical);
+        checkBounds(view7, defaultBounds, marginAll, marginAll, marginAll, marginAll);
+    }
 
-        assertEquals("Width value", defaultWidth - 2 * marginPixels, view2.getWidth());
-        assertEquals("Height value", defaultHeight, view2.getHeight());
-
-        assertEquals("Width value", defaultWidth, view3.getWidth());
-        assertEquals("Height value", defaultHeight - 2 * marginPixels, view3.getHeight());
-
-        assertEquals("Width value", defaultWidth - 2 * marginPixels, view4.getWidth());
-        assertEquals("Height value", defaultHeight - 2 * marginPixels, view4.getHeight());
-
-        assertEquals("Width value", defaultWidth - 2 * marginPixels, view5.getWidth());
-        assertEquals("Height value", defaultHeight - 2 * marginPixels, view5.getHeight());
-
-        assertEquals("Width value", defaultWidth - 2 * marginPixels, view6.getWidth());
-        assertEquals("Height value", defaultHeight - 2 * marginPixels, view6.getHeight());
+    private void checkBounds(View view, Rect defaultBounds,
+            int insetLeft, int insetTop, int insetRight, int insetBottom) {
+        assertEquals("Left", defaultBounds.left + insetLeft, view.getLeft());
+        assertEquals("Top", defaultBounds.top + insetTop, view.getTop());
+        assertEquals("Right", defaultBounds.right - insetRight, view.getRight());
+        assertEquals("Bottom", defaultBounds.bottom - insetBottom, view.getBottom());
     }
 }
diff --git a/tests/tests/view/src/android/view/cts/ViewPaddingTest.java b/tests/tests/view/src/android/view/cts/ViewPaddingTest.java
index b146f9d..60fa170 100644
--- a/tests/tests/view/src/android/view/cts/ViewPaddingTest.java
+++ b/tests/tests/view/src/android/view/cts/ViewPaddingTest.java
@@ -61,6 +61,8 @@
         View view8 = viewGroup.findViewById(R.id.view8);
         View view9 = viewGroup.findViewById(R.id.view9);
         View view10 = viewGroup.findViewById(R.id.view10);
+        View view11 = viewGroup.findViewById(R.id.view11);
+        View view12 = viewGroup.findViewById(R.id.view12);
 
         Rect defaultBounds = new Rect(view1.getLeft(), view1.getTop(), view1.getRight(),
                 view1.getBottom());
@@ -68,6 +70,8 @@
         int insetRight = mContext.getResources().getDimensionPixelSize(R.dimen.insetRight);
         int insetTop = mContext.getResources().getDimensionPixelSize(R.dimen.insetTop);
         int insetBottom = mContext.getResources().getDimensionPixelSize(R.dimen.insetBottom);
+        int insetStart = mContext.getResources().getDimensionPixelSize(R.dimen.insetStart);
+        int insetEnd = mContext.getResources().getDimensionPixelSize(R.dimen.insetEnd);
         int insetAll = mContext.getResources().getDimensionPixelSize(R.dimen.insetAll);
         int insetHorizontal =
                 mContext.getResources().getDimensionPixelSize(R.dimen.insetHorizontal);
@@ -84,6 +88,8 @@
         checkBounds(view9, defaultBounds, insetHorizontal, insetVertical, insetHorizontal,
                 insetVertical);
         checkBounds(view10, defaultBounds, insetAll, insetAll, insetAll, insetAll);
+        checkBounds(view11, defaultBounds, insetStart, insetAll, insetEnd, insetAll);
+        checkBounds(view12, defaultBounds, insetStart, 0, insetEnd, 0);
     }
 
     private void checkBounds(View view, Rect defaultBounds,
diff --git a/tests/tests/view/src/android/view/cts/View_DefaultFocusHighlightTest.java b/tests/tests/view/src/android/view/cts/View_DefaultFocusHighlightTest.java
index 5e362cf..b019fb1 100644
--- a/tests/tests/view/src/android/view/cts/View_DefaultFocusHighlightTest.java
+++ b/tests/tests/view/src/android/view/cts/View_DefaultFocusHighlightTest.java
@@ -20,6 +20,11 @@
 import static org.junit.Assert.assertTrue;
 
 import android.app.Activity;
+import android.content.res.ColorStateList;
+import android.graphics.Color;
+import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.RippleDrawable;
 import android.support.test.annotation.UiThreadTest;
 import android.support.test.filters.MediumTest;
 import android.support.test.rule.ActivityTestRule;
@@ -27,6 +32,7 @@
 import android.view.View;
 import android.widget.Button;
 import android.widget.EditText;
+import android.widget.ImageView;
 import android.widget.LinearLayout;
 import android.widget.ListView;
 
@@ -90,4 +96,59 @@
         assertFalse(button.getDefaultFocusHighlightEnabled());
         assertFalse(linearLayout.getDefaultFocusHighlightEnabled());
     }
+
+    @UiThreadTest
+    @Test
+    public void testIsDefaultFocusHighlightNeeded() {
+        Activity activity = mActivityRule.getActivity();
+        final Button button = (Button) activity.findViewById(R.id.button_to_test_highlight_needed);
+        final ImageView imageView =
+                (ImageView) activity.findViewById(R.id.image_view_to_test_highlight_needed);
+
+        final Drawable[] drawables = new Drawable[] {
+                null,  // null
+                new ColorDrawable(Color.WHITE), // not stateful
+                new RippleDrawable(ColorStateList.valueOf(Color.WHITE), null, null) // stateful
+        };
+        final boolean[] lackFocusState = new boolean[]{
+                true, // for null
+                true, // for not stateful
+                false, // for stateful
+        };
+
+        boolean isNeeded, expected;
+
+        // View
+        for (int i = 0; i < drawables.length; i++) {
+            for (int j = 0; j < drawables.length; j++) {
+                // Turn on default focus highlight.
+                button.setDefaultFocusHighlightEnabled(true);
+                isNeeded = button.isDefaultFocusHighlightNeeded(drawables[i], drawables[j]);
+                expected = lackFocusState[i] && lackFocusState[j];
+                assertTrue(isNeeded == expected);
+                // Turn off default focus highlight.
+                button.setDefaultFocusHighlightEnabled(false);
+                isNeeded = button.isDefaultFocusHighlightNeeded(drawables[i], drawables[j]);
+                assertFalse(isNeeded);
+            }
+        }
+
+        // ImageView
+        for (int k = 0; k < drawables.length; k++) {
+            imageView.setImageDrawable(drawables[k]);
+            for (int i = 0; i < drawables.length; i++) {
+                for (int j = 0; j < drawables.length; j++) {
+                    // Turn on default focus highlight.
+                    imageView.setDefaultFocusHighlightEnabled(true);
+                    isNeeded = imageView.isDefaultFocusHighlightNeeded(drawables[i], drawables[j]);
+                    expected = lackFocusState[i] && lackFocusState[j] && lackFocusState[k];
+                    assertTrue(isNeeded == expected);
+                    // Turn off default focus highlight.
+                    imageView.setDefaultFocusHighlightEnabled(false);
+                    isNeeded = imageView.isDefaultFocusHighlightNeeded(drawables[i], drawables[j]);
+                    assertFalse(isNeeded);
+                }
+            }
+        }
+    }
 }
diff --git a/tests/tests/widget/src/android/widget/cts/AdapterViewTest.java b/tests/tests/widget/src/android/widget/cts/AdapterViewTest.java
index 94a9923..cc86808 100644
--- a/tests/tests/widget/src/android/widget/cts/AdapterViewTest.java
+++ b/tests/tests/widget/src/android/widget/cts/AdapterViewTest.java
@@ -36,6 +36,7 @@
 import android.content.Context;
 import android.os.Parcelable;
 import android.provider.Settings;
+import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.SmallTest;
 import android.support.test.rule.ActivityTestRule;
 import android.support.test.runner.AndroidJUnit4;
@@ -290,6 +291,9 @@
                         ViewGroup.LayoutParams.MATCH_PARENT)), true);
 
         mActivityRule.runOnUiThread(() -> setArrayAdapter(mAdapterView));
+        // Wait for the UI to "settle down" since selection is fired asynchronously
+        // on the next layout pass, and we don't want to trigger the listener too early
+        InstrumentationRegistry.getInstrumentation().waitForIdleSync();
 
         AdapterView.OnItemSelectedListener mockSelectedListener =
                 mock(AdapterView.OnItemSelectedListener.class);
diff --git a/tests/tests/widget/src/android/widget/cts/ProgressBarDrawableContainerTest.java b/tests/tests/widget/src/android/widget/cts/ProgressBarDrawableContainerTest.java
new file mode 100644
index 0000000..2acf8ecd
--- /dev/null
+++ b/tests/tests/widget/src/android/widget/cts/ProgressBarDrawableContainerTest.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.widget.cts;
+
+import static org.junit.Assert.assertEquals;
+
+import android.app.Activity;
+import android.graphics.drawable.DrawableContainer;
+import android.support.test.annotation.UiThreadTest;
+import android.support.test.filters.SmallTest;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+import android.widget.ProgressBar;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class ProgressBarDrawableContainerTest {
+    private Activity mActivity;
+
+    @Rule
+    public ActivityTestRule<RadioGroupCtsActivity> mActivityRule =
+            new ActivityTestRule<>(RadioGroupCtsActivity.class);
+
+    @Before
+    public void setup() {
+        mActivity = mActivityRule.getActivity();
+    }
+
+    @UiThreadTest
+    @Test
+    public void testMutate() {
+        DrawableContainer d = (DrawableContainer) new ProgressBar(
+                mActivity).getIndeterminateDrawable().mutate();
+
+        boolean mirrored = d.isAutoMirrored();
+        d.setAutoMirrored(!mirrored);
+
+        ProgressBar newBar = new ProgressBar(mActivity);
+        DrawableContainer d2 = (DrawableContainer) newBar.getIndeterminateDrawable();
+        boolean newMirrored = d2.isAutoMirrored();
+        assertEquals(newMirrored, mirrored);
+    }
+
+}