Merge "Camera: improve accuracy of  sensor fusion test" into nyc-mr1-dev
diff --git a/apps/CameraITS/tests/scene0/test_metadata.py b/apps/CameraITS/tests/scene0/test_metadata.py
index ed1426b..e5fbba5 100644
--- a/apps/CameraITS/tests/scene0/test_metadata.py
+++ b/apps/CameraITS/tests/scene0/test_metadata.py
@@ -97,9 +97,12 @@
         print "Assert field of view: %.1f degrees" % fov
         assert 30 <= fov <= 130
 
-        hyperfocal = 1.0 / props["android.lens.info.hyperfocalDistance"]
-        print "Assert hyperfocal distance: %.2f m" % hyperfocal
-        assert 0.02 <= hyperfocal
+        if its.caps.lens_approx_calibrated(props):
+            diopter_hyperfocal = props["android.lens.info.hyperfocalDistance"]
+            if diopter_hyperfocal != 0.0:
+                hyperfocal = 1.0 / diopter_hyperfocal
+                print "Assert hyperfocal distance: %.2f m" % hyperfocal
+                assert 0.02 <= hyperfocal
 
 
 def getval(expr, default=None):
diff --git a/apps/CtsVerifier/AndroidManifest.xml b/apps/CtsVerifier/AndroidManifest.xml
index 824ab67..6f29335 100644
--- a/apps/CtsVerifier/AndroidManifest.xml
+++ b/apps/CtsVerifier/AndroidManifest.xml
@@ -1246,6 +1246,18 @@
             </intent-filter>
         </service>
 
+        <activity android:name=".notifications.ShortcutThrottlingResetActivity"
+            android:label="@string/shortcut_reset_test"
+            android:configChanges="mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|screenLayout|fontScale|uiMode|orientation|screenSize|smallestScreenSize|layoutDirection">
+            <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_notifications" />
+            <meta-data android:name="test_excluded_features"
+                android:value="android.hardware.type.watch:android.software.leanback" />
+        </activity>
+
         <activity android:name=".vr.VrListenerVerifierActivity"
             android:label="@string/vr_tests">
             <intent-filter>
diff --git a/apps/CtsVerifier/res/values/strings.xml b/apps/CtsVerifier/res/values/strings.xml
index 2406b84..d6b0f47 100644
--- a/apps/CtsVerifier/res/values/strings.xml
+++ b/apps/CtsVerifier/res/values/strings.xml
@@ -1204,6 +1204,16 @@
     <string name="package_priority_default">Find \"%s\" in the \"Notifications\" settings panel, and disallow it to Override Do Not Disturb.</string>
     <string name="package_priority_user_order">Check that ranker respects user priorities.</string>
 
+    <string name="shortcut_reset_test">Shortcut Reset Rate-limiting Test</string>
+    <string name="shortcut_reset_info">This test checks that when an inline-reply happens, ShortcutManager\'s rate-limiting
+        is reset.</string>
+    <string name="shortcut_reset_bot">Verifying that the CTS Robot helper package is installed.</string>
+    <string name="shortcut_reset_start">Showing the notification.</string>
+    <string name="shortcut_reset_prompt_inline_reply">Open the notification shade,
+        find the \"Shortcut Reset Rate-limiting Test\" notification, type something in the inline-reply field and
+        press the send button.</string>
+    <string name="shortcut_reset_check_result">Check ShortcutManager rate-limit has been reset.</string>
+
     <string name="attention_test">Notification Attention Management Test</string>
     <string name="attention_info">This test checks that the NotificationManagerService is
         respecting user preferences about notification ranking and filtering.
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/notifications/PackagePriorityVerifierActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/notifications/PackagePriorityVerifierActivity.java
index 5870981..b40ecc6 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/notifications/PackagePriorityVerifierActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/notifications/PackagePriorityVerifierActivity.java
@@ -39,7 +39,7 @@
     private static final String ACTION_CANCEL = "com.android.cts.robot.ACTION_CANCEL";
     private static final String EXTRA_ID = "ID";
     private static final String EXTRA_NOTIFICATION = "NOTIFICATION";
-    private static final String NOTIFICATION_BOT_PACKAGE = "com.android.cts.robot";
+    static final String NOTIFICATION_BOT_PACKAGE = "com.android.cts.robot";
     private CharSequence mAppLabel;
 
     @Override
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/notifications/ShortcutThrottlingResetActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/notifications/ShortcutThrottlingResetActivity.java
new file mode 100644
index 0000000..313ebfa
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/notifications/ShortcutThrottlingResetActivity.java
@@ -0,0 +1,207 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.verifier.notifications;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.PackageManager;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.View;
+import android.view.ViewGroup;
+
+import com.android.cts.verifier.R;
+
+import java.security.SecureRandom;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicReference;
+
+/**
+ * Test to make sure, when an inline reply happens, the shortcut manager rate-limiting must
+ * be reset.
+ *
+ * We use the "BOT" apk here, because rate-limiting will be reset when an app shows an activity
+ * too -- so as long as this (or any) test activity is shown, CTS verifier won't be rate-limited.
+ */
+public class ShortcutThrottlingResetActivity extends InteractiveVerifierActivity {
+    private static final String TAG = "ShortcutThrottlingReset";
+
+    private static final String NOTIFICATION_BOT_PACKAGE
+            = PackagePriorityVerifierActivity.NOTIFICATION_BOT_PACKAGE;
+
+    private static final String ACTION_RESET_SETUP_NOTIFICATION =
+            "com.android.cts.robot.ACTION_RESET_SETUP_NOTIFICATION";
+
+    private static final String EXTRA_NOTIFICATION_TITLE = "EXTRA_NOTIFICATION_TITLE";
+    private static final String EXTRA_RESET_REPLY_PACKAGE = "EXTRA_RESET_REPLY_PACKAGE";
+    private static final String EXTRA_RESET_REPLY_ACTION = "EXTRA_RESET_REPLY_ACTION";
+    private static final String EXTRA_RESET_REPLY_ERROR = "EXTRA_RESET_REPLY_ERROR";
+
+    private static final String SUCCESS = "**SUCCESS**";
+
+    private String mReplyAction;
+
+    private final AtomicReference<Intent> mReplyIntent = new AtomicReference<>(null);
+
+    @Override
+    int getTitleResource() {
+        return R.string.shortcut_reset_test;
+    }
+
+    @Override
+    int getInstructionsResource() {
+        return R.string.shortcut_reset_info;
+    }
+
+    @Override
+    protected void onCreate(Bundle savedState) {
+        super.onCreate(savedState);
+
+        // Generate an unique reply action and register the reply receiver.
+        mReplyAction = "reply_" + new SecureRandom().nextLong();
+        final IntentFilter replyFilter = new IntentFilter(mReplyAction);
+        registerReceiver(mReplyReceiver, replyFilter);
+    }
+
+    @Override
+    protected void onDestroy() {
+        unregisterReceiver(mReplyReceiver);
+        super.onDestroy();
+    }
+
+    @Override
+    protected List<InteractiveTestCase> createTestItems() {
+        List<InteractiveTestCase> tests = new ArrayList<>();
+        tests.add(new CheckForBot());
+        tests.add(new SetupNotification());
+        tests.add(new WaitForTestReply());
+        tests.add(new CheckResult());
+        return tests;
+    }
+
+
+    private final BroadcastReceiver mReplyReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            Log.i(TAG, "Received reply from robot helper: " + intent);
+            mReplyIntent.set(intent);
+        }
+    };
+
+
+    /** Make sure the helper package is installed. */
+    protected class CheckForBot extends InteractiveTestCase {
+        @Override
+        View inflate(ViewGroup parent) {
+            return createAutoItem(parent, R.string.shortcut_reset_bot);
+        }
+
+        @Override
+        void test() {
+            PackageManager pm = mContext.getPackageManager();
+            try {
+                pm.getPackageInfo(NOTIFICATION_BOT_PACKAGE, 0);
+                status = PASS;
+            } catch (PackageManager.NameNotFoundException e) {
+                status = FAIL;
+                logFail("You must install the CTS Robot helper, aka " + NOTIFICATION_BOT_PACKAGE);
+            }
+            next();
+        }
+    }
+
+    /**
+     * Request the bot apk to show the notification.
+     */
+    protected class SetupNotification extends InteractiveTestCase {
+        @Override
+        View inflate(ViewGroup parent) {
+            return createAutoItem(parent, R.string.shortcut_reset_start);
+        }
+
+        @Override
+        void test() {
+            final Intent intent = new Intent(ACTION_RESET_SETUP_NOTIFICATION);
+            intent.setPackage(NOTIFICATION_BOT_PACKAGE);
+
+            intent.putExtra(EXTRA_NOTIFICATION_TITLE, getResources().getString(getTitleResource()));
+
+            intent.putExtra(EXTRA_RESET_REPLY_PACKAGE, getPackageName());
+            intent.putExtra(EXTRA_RESET_REPLY_ACTION, mReplyAction);
+
+            intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES);
+            sendBroadcast(intent);
+            status = PASS;
+            next();
+        }
+    }
+
+    /**
+     * Let the human tester do an inline reply, and wait for the reply broadcast from the bot apk.
+     */
+    protected class WaitForTestReply extends InteractiveTestCase {
+        @Override
+        View inflate(ViewGroup parent) {
+            return createAutoItem(parent, R.string.shortcut_reset_prompt_inline_reply);
+        }
+
+        @Override
+        void test() {
+            final Intent replyIntent = mReplyIntent.get();
+            if (replyIntent == null) {
+                // Reply not received yet.
+                status = RETEST;
+                delay();
+                return;
+            }
+            status = PASS;
+            next();
+        }
+    }
+
+    /**
+     * Check the reply from the bot apk.
+     */
+    protected class CheckResult extends InteractiveTestCase {
+        @Override
+        View inflate(ViewGroup parent) {
+            return createAutoItem(parent, R.string.shortcut_reset_check_result);
+        }
+
+        @Override
+        void test() {
+            final Intent replyIntent = mReplyIntent.get();
+            if (replyIntent == null) {
+                logFail("Internal error, replyIntent shouldn't be null here.");
+                status = FAIL;
+                return;
+            }
+            final String error = replyIntent.getStringExtra(EXTRA_RESET_REPLY_ERROR);
+            if (SUCCESS.equals(error)) {
+                status = PASS;
+                next();
+                return;
+            }
+            logFail("Test failed. Error message=" + error);
+            status = FAIL;
+            next();
+        }
+    }
+}
diff --git a/apps/NotificationBot/AndroidManifest.xml b/apps/NotificationBot/AndroidManifest.xml
index b63791f..0388cbc 100644
--- a/apps/NotificationBot/AndroidManifest.xml
+++ b/apps/NotificationBot/AndroidManifest.xml
@@ -39,10 +39,10 @@
             <intent-filter>
                 <action android:name="com.android.cts.robot.ACTION_POST" />
                 <action android:name="com.android.cts.robot.ACTION_CANCEL" />
+                <action android:name="com.android.cts.robot.ACTION_RESET_SETUP_NOTIFICATION" />
+                <action android:name="com.android.cts.robot.ACTION_INLINE_REPLY" />
             </intent-filter>
         </receiver>
-
-
     </application>
 
 </manifest>
diff --git a/apps/NotificationBot/src/com/android/cts/robot/NotificationBot.java b/apps/NotificationBot/src/com/android/cts/robot/NotificationBot.java
index 2aa5f41..746b840 100644
--- a/apps/NotificationBot/src/com/android/cts/robot/NotificationBot.java
+++ b/apps/NotificationBot/src/com/android/cts/robot/NotificationBot.java
@@ -15,14 +15,23 @@
  */
 package com.android.cts.robot;
 
-import android.app.Activity;
 import android.app.Notification;
+import android.app.Notification.Action;
 import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.app.RemoteInput;
 import android.content.BroadcastReceiver;
+import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
+import android.content.pm.ShortcutInfo;
+import android.content.pm.ShortcutManager;
+import android.os.SystemClock;
 import android.util.Log;
 
+import java.util.ArrayList;
+import java.util.List;
+
 
 public class NotificationBot extends BroadcastReceiver {
     private static final String TAG = "NotificationBot";
@@ -30,6 +39,21 @@
     private static final String EXTRA_NOTIFICATION = "NOTIFICATION";
     private static final String ACTION_POST = "com.android.cts.robot.ACTION_POST";
     private static final String ACTION_CANCEL = "com.android.cts.robot.ACTION_CANCEL";
+    private static final String ACTION_RESET_SETUP_NOTIFICATION =
+            "com.android.cts.robot.ACTION_RESET_SETUP_NOTIFICATION";
+
+    private static final String ACTION_INLINE_REPLY =
+            "com.android.cts.robot.ACTION_INLINE_REPLY";
+
+    private static final String EXTRA_RESET_REPLY_PACKAGE = "EXTRA_RESET_REPLY_PACKAGE";
+    private static final String EXTRA_RESET_REPLY_ACTION = "EXTRA_RESET_REPLY_ACTION";
+    private static final String EXTRA_NOTIFICATION_TITLE = "EXTRA_NOTIFICATION_TITLE";
+
+    private static final String EXTRA_RESET_REPLY_ERROR = "EXTRA_RESET_REPLY_ERROR";
+
+    private static final String EXTRA_RESET_REQUEST_INTENT = "EXTRA_RESET_REQUEST_INTENT";
+
+    private static final String SUCCESS = "**SUCCESS**";
 
     @Override
     public void onReceive(Context context, Intent intent) {
@@ -60,8 +84,112 @@
                     (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
             noMa.cancel(id);
 
+        } else if (ACTION_RESET_SETUP_NOTIFICATION.equals(intent.getAction())) {
+            testShortcutResetSetupNotification(context, intent);
+
+        } else if (ACTION_INLINE_REPLY.equals(intent.getAction())) {
+            testShortcutResetInlineReplyReceived(context, intent);
+
         } else {
             Log.i(TAG, "received unexpected action: " + intent.getAction());
         }
     }
+
+    /**
+     * Test start request from CTS verifier.  Show a notification with inline reply, which will
+     * trigger {@link #testShortcutResetInlineReplyReceived}.
+     */
+    private static void testShortcutResetSetupNotification(Context context, Intent intent) {
+        final NotificationManager nm = context.getSystemService(NotificationManager.class);
+        nm.cancelAll();
+
+        final ShortcutManager sm = context.getSystemService(ShortcutManager.class);
+
+        final List<ShortcutInfo> EMPTY_LIST = new ArrayList<>();
+
+        long timeout = SystemClock.elapsedRealtime() + 10 * 1000;
+
+        // First, make sure this package is throttled.
+        while (!sm.isRateLimitingActive()) {
+            sm.setDynamicShortcuts(EMPTY_LIST);
+            try {
+                Thread.sleep(0);
+            } catch (InterruptedException e) {
+            }
+            if (SystemClock.elapsedRealtime() >= timeout) {
+                sendShortcutResetReply(context, intent,
+                        "ShortcutMager rate-limiting not activated.");
+                return;
+            }
+        }
+
+        // Show a notification with inline reply.
+        final PendingIntent receiverIntent =
+                PendingIntent.getBroadcast(context, 0,
+                        new Intent(ACTION_INLINE_REPLY)
+                                .setComponent(new ComponentName(context, NotificationBot.class))
+                                .putExtra(EXTRA_RESET_REQUEST_INTENT, intent),
+                        PendingIntent.FLAG_UPDATE_CURRENT);
+        final RemoteInput ri = new RemoteInput.Builder("result")
+                .setLabel("Type something here and press send button").build();
+
+        final Notification.Builder nb = new Notification.Builder(context)
+                .setContentTitle(intent.getStringExtra(EXTRA_NOTIFICATION_TITLE))
+                .setSmallIcon(android.R.drawable.ic_popup_sync)
+                .addAction(new Action.Builder(0,
+                        "Type something here and press send button", receiverIntent)
+                        .addRemoteInput(ri)
+                        .build());
+        context.getSystemService(NotificationManager.class).notify(0, nb.build());
+    }
+
+    /**
+     * Invoked when the inline reply from {@link #testShortcutResetSetupNotification} is performed.
+     *
+     * Check the shortcut manager rate-limiting state, and post the reply to CTS verifier.
+     */
+    private static void testShortcutResetInlineReplyReceived(Context context, Intent intent) {
+        Log.i(TAG, "Inline reply received");
+
+        final NotificationManager nm = context.getSystemService(NotificationManager.class);
+        nm.cancelAll();
+
+        // Close notification shade.
+        context.sendBroadcast(new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS));
+
+        // Check if rate-limiting has been reset.
+        final ShortcutManager sm = context.getSystemService(ShortcutManager.class);
+
+        String error;
+        final boolean success = !sm.isRateLimitingActive();
+        if (success) {
+            error = SUCCESS;
+        } else {
+            error = "Inline reply received, but ShortcutManager rate-limiting is still active.";
+        }
+
+        // Send back the result.
+        sendShortcutResetReply(context,
+                intent.getParcelableExtra(EXTRA_RESET_REQUEST_INTENT), error);
+    }
+
+    /**
+     * Reply an error message, or {@link #SUCCESS} for success, to CTS verifier for shortcut manager
+     * reset rate-limiting test.
+
+     * @param requestIntent original intent sent from the verifier to
+     *     {@link #testShortcutResetSetupNotification}.
+     * @param error error message, or {@link #SUCCESS} if success.
+     */
+    private static void sendShortcutResetReply(Context context, Intent requestIntent, String error) {
+        final Intent replyIntent = new Intent();
+        replyIntent.setAction(requestIntent.getStringExtra(EXTRA_RESET_REPLY_ACTION));
+        replyIntent.putExtra(EXTRA_RESET_REPLY_ERROR, error);
+
+        if (error != null) {
+            Log.e(TAG, error);
+        }
+
+        context.sendBroadcast(replyIntent);
+    }
 }
diff --git a/common/device-side/device-info/src/com/android/compatibility/common/deviceinfo/DeviceInfo.java b/common/device-side/device-info/src/com/android/compatibility/common/deviceinfo/DeviceInfo.java
index 8aa3380..750e45f 100644
--- a/common/device-side/device-info/src/com/android/compatibility/common/deviceinfo/DeviceInfo.java
+++ b/common/device-side/device-info/src/com/android/compatibility/common/deviceinfo/DeviceInfo.java
@@ -87,7 +87,7 @@
                     mResultCode = ResultCode.COMPLETED;
                 }
             } catch (Exception e) {
-                failed("Could not collect device info: " + e.getMessage());
+                failed("Could not collect device info", e);
             }
         }
 
@@ -136,6 +136,12 @@
         Log.e(LOG_TAG, message, exception);
     }
 
+    private void failed(String message, Throwable exception) {
+        mResultCode = ResultCode.FAILED;
+        mErrorMessage = message;
+        Log.e(LOG_TAG, message, exception);
+    }
+
     private void failed(String message) {
         mResultCode = ResultCode.FAILED;
         mErrorMessage = message;
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/DeviceInfoStore.java b/common/device-side/util/src/com/android/compatibility/common/util/DeviceInfoStore.java
index 9f1bda5..735b955 100644
--- a/common/device-side/util/src/com/android/compatibility/common/util/DeviceInfoStore.java
+++ b/common/device-side/util/src/com/android/compatibility/common/util/DeviceInfoStore.java
@@ -135,9 +135,7 @@
      */
     @Override
     public void addResult(String name, float value) throws IOException {
-        checkName(name);
-        mJsonWriter.name(name);
-        mJsonWriter.value(value);
+        addResult(name, (double) value);
     }
 
     /**
@@ -146,8 +144,12 @@
     @Override
     public void addResult(String name, double value) throws IOException {
         checkName(name);
-        mJsonWriter.name(name);
-        mJsonWriter.value(value);
+        if (isDoubleNaNOrInfinite(value)) {
+            return;
+        } else {
+            mJsonWriter.name(name);
+            mJsonWriter.value(value);
+        }
     }
 
     /**
@@ -203,13 +205,11 @@
      */
     @Override
     public void addArrayResult(String name, float[] array) throws IOException {
-        checkName(name);
-        mJsonWriter.name(name);
-        mJsonWriter.beginArray();
-        for (float value : checkArray(array)) {
-            mJsonWriter.value(value);
+        double[] doubleArray = new double[array.length];
+        for (int i = 0; i < array.length; i++) {
+            doubleArray[i] = array[i];
         }
-        mJsonWriter.endArray();
+        addArrayResult(name, doubleArray);
     }
 
     /**
@@ -221,6 +221,9 @@
         mJsonWriter.name(name);
         mJsonWriter.beginArray();
         for (double value : checkArray(array)) {
+            if (isDoubleNaNOrInfinite(value)) {
+                continue;
+            }
             mJsonWriter.value(value);
         }
         mJsonWriter.endArray();
diff --git a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/command/CompatibilityConsole.java b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/command/CompatibilityConsole.java
index ce5d023..b8ab089 100644
--- a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/command/CompatibilityConsole.java
+++ b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/command/CompatibilityConsole.java
@@ -216,8 +216,8 @@
         helpBuilder.append("Options:\n");
         helpBuilder.append("  --session/-s <session_id>: The session used to create a subplan.\n");
         helpBuilder.append("  --name/-n <subplan_name>: The name of the new subplan.\n");
-        helpBuilder.append("  --result/-r <status>: Which results to include in the subplan. ");
-        helpBuilder.append("One of passed, failed, not_executed. Repeatable.\n");
+        helpBuilder.append("  --result-type/-r <status>: Which results to include in the");
+        helpBuilder.append(" subplan. One of passed, failed, not_executed. Repeatable.\n");
 
         return helpBuilder.toString();
     }
diff --git a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/result/SubPlanCreator.java b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/result/SubPlanCreator.java
index 1c697e4..cbe3d2e 100644
--- a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/result/SubPlanCreator.java
+++ b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/result/SubPlanCreator.java
@@ -20,9 +20,13 @@
 import com.android.compatibility.common.tradefed.testtype.ISubPlan;
 import com.android.compatibility.common.tradefed.testtype.SubPlan;
 import com.android.compatibility.common.tradefed.util.OptionHelper;
+import com.android.compatibility.common.util.ICaseResult;
 import com.android.compatibility.common.util.IInvocationResult;
+import com.android.compatibility.common.util.IModuleResult;
+import com.android.compatibility.common.util.ITestResult;
 import com.android.compatibility.common.util.ResultHandler;
 import com.android.compatibility.common.util.TestFilter;
+import com.android.compatibility.common.util.TestStatus;
 import com.android.ddmlib.Log.LogLevel;
 import com.android.tradefed.config.ArgsOptionParser;
 import com.android.tradefed.config.ConfigurationException;
@@ -35,11 +39,12 @@
 import java.io.FileNotFoundException;
 import java.io.FileOutputStream;
 import java.io.IOException;
-import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
+import java.util.Collections;
 import java.util.HashSet;
-import java.util.List;
+import java.util.HashMap;
+import java.util.Map;
 import java.util.Set;
 
 /**
@@ -47,9 +52,18 @@
  */
 public class SubPlanCreator {
 
-    private static final String PASSED = "passed";
-    private static final String FAILED = "failed";
-    private static final String NOT_EXECUTED = "not_executed";
+    // result types
+    public static final String PASSED = "passed";
+    public static final String FAILED = "failed";
+    public static final String NOT_EXECUTED = "not_executed";
+    // static mapping of result types to TestStatuses
+    private static final Map<String, TestStatus> mStatusMap;
+    static {
+        Map<String, TestStatus> statusMap = new HashMap<String, TestStatus>();
+        statusMap.put(PASSED, TestStatus.PASS);
+        statusMap.put(FAILED, TestStatus.FAIL);
+        mStatusMap = Collections.unmodifiableMap(statusMap);
+    }
 
     @Option (name = "name", shortName = 'n', description = "the name of the subplan to create",
             importance=Importance.IF_UNSET)
@@ -59,21 +73,21 @@
             importance=Importance.IF_UNSET)
     private Integer mSessionId = null;
 
-    @Option (name = "result", shortName = 'r',
+    @Option (name = "result-type", shortName = 'r',
             description = "the result type to include. One of passed, failed, not_executed."
             + " Option may be repeated",
             importance=Importance.IF_UNSET)
-    private Set<String> mResultFilterStrings = new HashSet<String>();
+    private Set<String> mResultTypes = new HashSet<String>();
 
     @Option(name = CompatibilityTest.INCLUDE_FILTER_OPTION,
             description = "the include module filters to apply.",
             importance = Importance.NEVER)
-    private List<String> mIncludeFilters = new ArrayList<>();
+    private Set<String> mIncludeFilters = new HashSet<String>();
 
     @Option(name = CompatibilityTest.EXCLUDE_FILTER_OPTION,
             description = "the exclude module filters to apply.",
             importance = Importance.NEVER)
-    private List<String> mExcludeFilters = new ArrayList<>();
+    private Set<String> mExcludeFilters = new HashSet<String>();
 
     @Option(name = CompatibilityTest.MODULE_OPTION, shortName = 'm',
             description = "the test module to run.",
@@ -87,7 +101,7 @@
 
     @Option(name = CompatibilityTest.ABI_OPTION, shortName = 'a',
             description = "the abi to test.",
-            importance = Importance.IF_UNSET)
+            importance = Importance.NEVER)
     private String mAbiName = null;
 
     File mSubPlanFile = null;
@@ -104,10 +118,26 @@
     /**
      * Create a {@link SubPlanCreator} using the specified option values.
      */
-    public SubPlanCreator(String name, int session, Collection<String> resultFilterStrings) {
+    public SubPlanCreator(String name, int session, Collection<String> resultTypes) {
         mSubPlanName = name;
         mSessionId = session;
-        mResultFilterStrings.addAll(resultFilterStrings);
+        mResultTypes.addAll(resultTypes);
+    }
+
+    /**
+     * Set the result from which to derive the subplan.
+     * @param result
+     */
+    public void setResult(IInvocationResult result) {
+        mResult = result;
+    }
+
+    /**
+     * Add a result type from which to derive the subplan. PASSED, FAILED, or NOT_EXECUTED
+     * @param resultType
+     */
+    public void addResultType(String resultType) {
+        mResultTypes.add(resultType);
     }
 
     /**
@@ -137,8 +167,7 @@
     /**
      * Create a subplan derived from a result.
      * <p/>
-     * {@link Option} values must all be set before this is called.
-     *
+     * {@link Option} values must be set before this is called.
      * @param build
      * @return subplan
      * @throws ConfigurationException
@@ -147,56 +176,135 @@
             throws ConfigurationException {
         setupFields(buildHelper);
         ISubPlan subPlan = new SubPlan();
-        // At least one of the following three is true
-        boolean notExecuted = mResultFilterStrings.contains(NOT_EXECUTED);
-        boolean passed = mResultFilterStrings.contains(PASSED);
-        boolean failed = mResultFilterStrings.contains(FAILED);
-        if (notExecuted) {
-            // if including not_executed tests, include filters from previous session to
-            // track which tests must run
-            subPlan.addAllIncludeFilters(new HashSet<String>(mIncludeFilters));
-            subPlan.addAllExcludeFilters(new HashSet<String>(mExcludeFilters));
-            if (mModuleName != null) {
-                subPlan.addIncludeFilter(
-                        new TestFilter(mAbiName, mModuleName, mTestName).toString());
-            }
-            if (!passed) {
-                subPlan.excludePassed(mResult);
-            }
-            if (!failed) {
-                subPlan.excludeFailed(mResult);
-            }
-        } else {
-            // if only including executed tests, add each filter explicitly without filters from
-            // previous session
-            if (passed) {
-                subPlan.includePassed(mResult);
-            }
-            if (failed) {
-                subPlan.includeFailed(mResult);
-            }
+
+        // add filters from previous session to track which tests must run
+        subPlan.addAllIncludeFilters(mIncludeFilters);
+        subPlan.addAllExcludeFilters(mExcludeFilters);
+        if (mModuleName != null) {
+            subPlan.addIncludeFilter(new TestFilter(mAbiName, mModuleName, mTestName).toString());
         }
 
+        for (IModuleResult module : mResult.getModules()) {
+            if (shouldRunModule(module)) {
+                Set<TestStatus> statusesToRun = getStatusesToRun();
+                TestFilter moduleInclude =
+                            new TestFilter(module.getAbi(), module.getName(), null /*test*/);
+                if (shouldRunEntireModule(module)) {
+                    // include entire module
+                    subPlan.addIncludeFilter(moduleInclude.toString());
+                } else if (mResultTypes.contains(NOT_EXECUTED) && !module.isDone()) {
+                    // add module include and test excludes
+                    subPlan.addIncludeFilter(moduleInclude.toString());
+                    for (ICaseResult caseResult : module.getResults()) {
+                        for (ITestResult testResult : caseResult.getResults()) {
+                            if (!statusesToRun.contains(testResult.getResultStatus())) {
+                                TestFilter testExclude = new TestFilter(module.getAbi(),
+                                        module.getName(), testResult.getFullName());
+                                subPlan.addExcludeFilter(testExclude.toString());
+                            }
+                        }
+                    }
+                } else {
+                    // Not-executed tests should not be rerun and/or this module is completed
+                    // In any such case, it suffices to add includes for each test to rerun
+                    for (ICaseResult caseResult : module.getResults()) {
+                        for (ITestResult testResult : caseResult.getResults()) {
+                            if (statusesToRun.contains(testResult.getResultStatus())) {
+                                TestFilter testInclude = new TestFilter(module.getAbi(),
+                                        module.getName(), testResult.getFullName());
+                                subPlan.addIncludeFilter(testInclude.toString());
+                            }
+                        }
+                    }
+                }
+            } else {
+                // module should not run, exclude entire module
+                TestFilter moduleExclude =
+                        new TestFilter(module.getAbi(), module.getName(), null /*test*/);
+                subPlan.addExcludeFilter(moduleExclude.toString());
+            }
+        }
         return subPlan;
     }
 
     /**
+     * Whether any tests within the given {@link IModuleResult} should be run, based on
+     * the content of mResultTypes.
+     * @param module
+     * @return true if at least one test in the module should run
+     */
+    private boolean shouldRunModule(IModuleResult module) {
+        if (mResultTypes.contains(NOT_EXECUTED) && !module.isDone()) {
+            // module has not executed tests that the subplan should run
+            return true;
+        }
+        for (TestStatus status : getStatusesToRun()) {
+            if (module.countResults(status) > 0) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Whether all tests within the given {@link IModuleResult} should be run, based on
+     * the content of mResultTypes.
+     * @param module
+     * @return true if every test in the module should run
+     */
+    private boolean shouldRunEntireModule(IModuleResult module) {
+        if (!mResultTypes.contains(NOT_EXECUTED) && !module.isDone()) {
+            // module has not executed tests that the subplan should not run
+            return false;
+        }
+        Set<TestStatus> statusesToRun = getStatusesToRun();
+        for (TestStatus status : TestStatus.values()) {
+            if (!statusesToRun.contains(status)) {
+                // status is a TestStatus we don't want to run
+                if (module.countResults(status) > 0) {
+                    return false;
+                }
+            }
+        }
+        return true;
+    }
+
+    /**
+     * Retrieves a {@link Set} of {@link TestStatus}es to run, based on the content of
+     * mResultTypes. Does not account for result type NOT_EXECUTED, since no such TestStatus
+     * exists.
+     * @return set of TestStatuses to run
+     */
+    private Set<TestStatus> getStatusesToRun() {
+        Set<TestStatus> statusesToRun = new HashSet<TestStatus>();
+        for (String resultType : mResultTypes) {
+            // no test status exists for not-executed tests
+            if (resultType != NOT_EXECUTED) {
+                statusesToRun.add(mStatusMap.get(resultType));
+            }
+        }
+        return statusesToRun;
+    }
+
+    /**
      * Ensure that all {@Option}s and fields are populated with valid values.
      * @param buildHelper
      * @throws ConfigurationException if any option has an invalid value
      */
     private void setupFields(CompatibilityBuildHelper buildHelper) throws ConfigurationException {
-        if (mSessionId == null) {
-            throw new ConfigurationException("Missing --session argument");
-        }
-        try {
-            mResult = ResultHandler.findResult(buildHelper.getResultsDir(), mSessionId);
-        } catch (FileNotFoundException e) {
-            throw new RuntimeException(e);
-        }
         if (mResult == null) {
-            throw new IllegalArgumentException(String.format(
-                    "Could not find session with id %d", mSessionId));
+            if (mSessionId == null) {
+                throw new ConfigurationException("Missing --session argument");
+            }
+            try {
+                mResult = ResultHandler.findResult(buildHelper.getResultsDir(), mSessionId);
+            } catch (FileNotFoundException e) {
+                throw new RuntimeException(e);
+            }
+            if (mResult == null) {
+                throw new IllegalArgumentException(String.format(
+                        "Could not find session with id %d", mSessionId));
+            }
         }
 
         String retryCommandLineArgs = mResult.getCommandLineArgs();
@@ -210,18 +318,15 @@
             }
         }
 
-        if (mResultFilterStrings.isEmpty()) {
+        if (mResultTypes.isEmpty()) {
             // add all valid values, include all tests of all statuses
-            mResultFilterStrings.addAll(
+            mResultTypes.addAll(
                     new HashSet<String>(Arrays.asList(PASSED, FAILED, NOT_EXECUTED)));
         }
         // validate all test status values
-        for (String filterString : mResultFilterStrings) {
-            if (!filterString.equals(PASSED)
-                    && !filterString.equals(FAILED)
-                    && !filterString.equals(NOT_EXECUTED)) {
-                throw new ConfigurationException(String.format("result filter string %s invalid",
-                        filterString));
+        for (String type : mResultTypes) {
+            if (!type.equals(PASSED) && !type.equals(FAILED) && !type.equals(NOT_EXECUTED)) {
+                throw new ConfigurationException(String.format("result type %s invalid", type));
             }
         }
 
@@ -239,10 +344,19 @@
         }
     }
 
+    /**
+     * Helper to create a plan name if none is explicitly set
+     */
     private String createPlanName() {
         StringBuilder sb = new StringBuilder();
-        sb.append(String.join("_", mResultFilterStrings));
-        sb.append(Integer.toString(mSessionId));
+        sb.append(String.join("_", mResultTypes));
+        sb.append("_");
+        if (mSessionId != null) {
+            sb.append(Integer.toString(mSessionId));
+            sb.append("_");
+        }
+        // use unique start time for name
+        sb.append(CompatibilityBuildHelper.getDirSuffix(mResult.getStartTime()));
         return sb.toString();
     }
 }
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 70e3588..9bd8647 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
@@ -18,6 +18,7 @@
 
 import com.android.compatibility.SuiteInfo;
 import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
+import com.android.compatibility.common.tradefed.result.SubPlanCreator;
 import com.android.compatibility.common.tradefed.targetprep.NetworkConnectivityChecker;
 import com.android.compatibility.common.tradefed.targetprep.SystemStatusChecker;
 import com.android.compatibility.common.tradefed.util.OptionHelper;
@@ -115,12 +116,12 @@
     @Option(name = INCLUDE_FILTER_OPTION,
             description = "the include module filters to apply.",
             importance = Importance.ALWAYS)
-    private List<String> mIncludeFilters = new ArrayList<>();
+    private Set<String> mIncludeFilters = new HashSet<>();
 
     @Option(name = EXCLUDE_FILTER_OPTION,
             description = "the exclude module filters to apply.",
             importance = Importance.ALWAYS)
-    private List<String> mExcludeFilters = new ArrayList<>();
+    private Set<String> mExcludeFilters = new HashSet<>();
 
     @Option(name = MODULE_OPTION,
             shortName = 'm',
@@ -611,15 +612,26 @@
                 }
             }
 
-            ISubPlan retryPlan = new SubPlan();
-            retryPlan.excludePassed(result); // always exclude passed tests on retry
+            SubPlanCreator retryPlanCreator = new SubPlanCreator();
+            retryPlanCreator.setResult(result);
             if (RetryType.FAILED.equals(mRetryType)) {
-                    retryPlan.includeFailed(result); // retry only failed tests
+                // retry only failed tests
+                retryPlanCreator.addResultType(SubPlanCreator.FAILED);
             } else if (RetryType.NOT_EXECUTED.equals(mRetryType)){
-                    retryPlan.excludeFailed(result); // retry only not executed tests
+                // retry only not executed tests
+                retryPlanCreator.addResultType(SubPlanCreator.NOT_EXECUTED);
+            } else {
+                // retry both failed and not executed tests
+                retryPlanCreator.addResultType(SubPlanCreator.FAILED);
+                retryPlanCreator.addResultType(SubPlanCreator.NOT_EXECUTED);
             }
-            mIncludeFilters.addAll(retryPlan.getIncludeFilters());
-            mExcludeFilters.addAll(retryPlan.getExcludeFilters());
+            try {
+                ISubPlan retryPlan = retryPlanCreator.createSubPlan(mBuildHelper);
+                mIncludeFilters.addAll(retryPlan.getIncludeFilters());
+                mExcludeFilters.addAll(retryPlan.getExcludeFilters());
+            } catch (ConfigurationException e) {
+                throw new RuntimeException ("Failed to create subplan for retry", e);
+            }
         }
         if (mSubPlan != null) {
             try {
@@ -670,8 +682,8 @@
     }
 
     /* Helper method designed to remove filters in a list not applicable to the given module */
-    private static void cleanFilters(List<String> filters, String module) {
-        List<String> cleanedFilters = new ArrayList<String>();
+    private static void cleanFilters(Set<String> filters, String module) {
+        Set<String> cleanedFilters = new HashSet<String>();
         for (String filter : filters) {
             if (module.equals(TestFilter.createFrom(filter).getName())) {
                 cleanedFilters.add(filter); // Module name matches, filter passes
diff --git a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/IModuleRepo.java b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/IModuleRepo.java
index 7cbf7a3..540373b 100644
--- a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/IModuleRepo.java
+++ b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/IModuleRepo.java
@@ -47,8 +47,8 @@
      * Initializes the repository.
      */
     void initialize(int shards, File testsDir, Set<IAbi> abis, List<String> deviceTokens,
-            List<String> testArgs, List<String> moduleArgs, List<String> mIncludeFilters,
-            List<String> mExcludeFilters, IBuildInfo buildInfo);
+            List<String> testArgs, List<String> moduleArgs, Set<String> mIncludeFilters,
+            Set<String> mExcludeFilters, IBuildInfo buildInfo);
 
     /**
      * @return a {@link Map} of all modules to run on the device referenced by the given serial.
diff --git a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/ISubPlan.java b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/ISubPlan.java
index adc0227..eda40b2 100644
--- a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/ISubPlan.java
+++ b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/ISubPlan.java
@@ -15,7 +15,6 @@
  */
 package com.android.compatibility.common.tradefed.testtype;
 
-import com.android.compatibility.common.util.IInvocationResult;
 import com.android.tradefed.testtype.ITestFilterReceiver;
 import com.android.tradefed.util.xml.AbstractXmlParser.ParseException;
 
@@ -37,30 +36,6 @@
     public void parse(InputStream xmlInputStream) throws ParseException;
 
     /**
-     * Add include filters for {@link ITestResult}s that have passed.
-     * @param result the {@link IInvocationResult} from which to read {@link TestStatus}es
-     */
-    public void includePassed(IInvocationResult result);
-
-    /**
-     * Add include filters for {@link ITestResult}s that have failed.
-     * @param result the {@link IInvocationResult} from which to read {@link TestStatus}es
-     */
-    public void includeFailed(IInvocationResult result);
-
-    /**
-     * Add exclude filters for {@link ITestResult}s that have passed.
-     * @param result the {@link IInvocationResult} from which to read {@link TestStatus}es
-     */
-    public void excludePassed(IInvocationResult result);
-
-    /**
-     * Add exclude filters for {@link ITestResult}s that have failed.
-     * @param result the {@link IInvocationResult} from which to read {@link TestStatus}es
-     */
-    public void excludeFailed(IInvocationResult result);
-
-    /**
      * Retrieve the set of include filters previously added or parsed from XML.
      * @return a set of include filter strings
      */
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 c4ff1b0..3d1b234 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
@@ -233,8 +233,8 @@
      */
     @Override
     public void initialize(int shards, File testsDir, Set<IAbi> abis, List<String> deviceTokens,
-            List<String> testArgs, List<String> moduleArgs, List<String> includeFilters,
-            List<String> excludeFilters, IBuildInfo buildInfo) {
+            List<String> testArgs, List<String> moduleArgs, Set<String> includeFilters,
+            Set<String> excludeFilters, IBuildInfo buildInfo) {
         CLog.d("Initializing ModuleRepo\nShards:%d\nTests Dir:%s\nABIs:%s\nDevice Tokens:%s\n" +
                 "Test Args:%s\nModule Args:%s\nIncludes:%s\nExcludes:%s",
                 shards, testsDir.getAbsolutePath(), abis, deviceTokens, testArgs, moduleArgs,
@@ -355,7 +355,7 @@
         return shardedList;
     }
 
-    private static void addFilters(List<String> stringFilters,
+    private static void addFilters(Set<String> stringFilters,
             Map<String, List<TestFilter>> filters, Set<IAbi> abis) {
         for (String filterString : stringFilters) {
             TestFilter filter = TestFilter.createFrom(filterString);
diff --git a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/SubPlan.java b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/SubPlan.java
index 80ca30e..54d2869 100644
--- a/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/SubPlan.java
+++ b/common/host-side/tradefed/src/com/android/compatibility/common/tradefed/testtype/SubPlan.java
@@ -15,13 +15,7 @@
  */
 package com.android.compatibility.common.tradefed.testtype;
 
-import com.android.compatibility.common.util.ICaseResult;
-import com.android.compatibility.common.util.IInvocationResult;
-import com.android.compatibility.common.util.IModuleResult;
-import com.android.compatibility.common.util.ITestResult;
-
 import com.android.compatibility.common.util.TestFilter;
-import com.android.compatibility.common.util.TestStatus;
 import com.android.tradefed.util.xml.AbstractXmlParser;
 
 import org.kxml2.io.KXmlSerializer;
@@ -66,88 +60,6 @@
      * {@inheritDoc}
      */
     @Override
-    public void includePassed(IInvocationResult result) {
-        for (IModuleResult module : result.getModules()) {
-            if (module.isPassed()) {
-                // Whole module passed, exclude
-                TestFilter filter =
-                        new TestFilter(module.getAbi(), module.getName(), null /*test*/);
-                mIncludes.add(filter.toString());
-            } else {
-                for (ICaseResult testResultList : module.getResults()) {
-                    for (ITestResult testResult : testResultList.getResults(TestStatus.PASS)) {
-                        // Test passed, exclude
-                        TestFilter filter = new TestFilter(
-                                module.getAbi(), module.getName(), testResult.getFullName());
-                        mIncludes.add(filter.toString());
-                    }
-                }
-            }
-        }
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public void includeFailed(IInvocationResult result) {
-        for (IModuleResult moduleResult : result.getModules()) {
-            for (ICaseResult caseResult : moduleResult.getResults()) {
-                for (ITestResult testResult : caseResult.getResults(TestStatus.FAIL)) {
-                    // Test failed, include for retry
-                    TestFilter filter = new TestFilter(moduleResult.getAbi(),
-                            moduleResult.getName(), testResult.getFullName());
-                    mIncludes.add(filter.toString());
-                }
-            }
-        }
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public void excludePassed(IInvocationResult result) {
-        for (IModuleResult module : result.getModules()) {
-            if (module.isPassed()) {
-                // Whole module passed, exclude
-                TestFilter filter =
-                        new TestFilter(module.getAbi(), module.getName(), null /*test*/);
-                mExcludes.add(filter.toString());
-            } else {
-                for (ICaseResult testResultList : module.getResults()) {
-                    for (ITestResult testResult : testResultList.getResults(TestStatus.PASS)) {
-                        // Test passed, exclude
-                        TestFilter filter = new TestFilter(
-                                module.getAbi(), module.getName(), testResult.getFullName());
-                        mExcludes.add(filter.toString());
-                    }
-                }
-            }
-        }
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public void excludeFailed(IInvocationResult result) {
-        for (IModuleResult moduleResult : result.getModules()) {
-            for (ICaseResult caseResult : moduleResult.getResults()) {
-                for (ITestResult testResult : caseResult.getResults(TestStatus.FAIL)) {
-                    // Test failed, include for retry
-                    TestFilter filter = new TestFilter(moduleResult.getAbi(),
-                            moduleResult.getName(), testResult.getFullName());
-                    mExcludes.add(filter.toString());
-                }
-            }
-        }
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
     public void addIncludeFilter(String filter) {
         mIncludes.add(filter);
     }
diff --git a/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/result/SubPlanCreatorTest.java b/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/result/SubPlanCreatorTest.java
index 2ad7260..192aabb 100644
--- a/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/result/SubPlanCreatorTest.java
+++ b/common/host-side/tradefed/tests/src/com/android/compatibility/common/tradefed/result/SubPlanCreatorTest.java
@@ -117,13 +117,13 @@
         ISubPlan plan = mSubPlanCreator.createSubPlan(mBuildHelper);
         Set<String> planIncludes = plan.getIncludeFilters();
         Set<String> planExcludes = plan.getExcludeFilters();
-        TestFilter passingTest1 =
-                new TestFilter(ABI, NAME_A, String.format("%s#%s", CLASS_A, METHOD_1));
-        TestFilter passingTest2 =
-                new TestFilter(ABI, NAME_B, String.format("%s#%s", CLASS_B, METHOD_4));
+        TestFilter mf1 = new TestFilter(ABI, NAME_A, null);
+        TestFilter tf1 = new TestFilter(ABI, NAME_A, String.format("%s#%s", CLASS_A, METHOD_1));
+        TestFilter tf3 = new TestFilter(ABI, NAME_B, String.format("%s#%s", CLASS_B, METHOD_3));
         assertTrue(planIncludes.contains("CtsMyModuleTestCases")); // command-line '-m' arg
-        assertTrue(planExcludes.contains(passingTest1.toString()));
-        assertTrue(planExcludes.contains(passingTest2.toString()));
+        assertTrue(planIncludes.contains(mf1.toString())); // include module with not-executed test
+        assertTrue(planExcludes.contains(tf1.toString())); // exclude passing test in that module
+        assertTrue(planIncludes.contains(tf3.toString())); // include failure in executed module
     }
 
     private void populateResults() throws Exception {
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 3b8e4cb..5a0ebed 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
@@ -59,8 +59,8 @@
     private static final List<String> DEVICE_TOKENS = new ArrayList<>();
     private static final List<String> TEST_ARGS= new ArrayList<>();
     private static final List<String> MODULE_ARGS = new ArrayList<>();
-    private static final List<String> INCLUDES = new ArrayList<>();
-    private static final List<String> EXCLUDES = new ArrayList<>();
+    private static final Set<String> INCLUDES = new HashSet<>();
+    private static final Set<String> EXCLUDES = new HashSet<>();
     private static final Set<String> FILES = new HashSet<>();
     private static final String FILENAME = "%s.config";
     private static final String ABI_32 = "armeabi-v7a";
@@ -184,9 +184,9 @@
     }
 
     public void testFiltering() throws Exception {
-        List<String> includeFilters = new ArrayList<>();
+        Set<String> includeFilters = new HashSet<>();
         includeFilters.add(MODULE_NAME_A);
-        List<String> excludeFilters = new ArrayList<>();
+        Set<String> excludeFilters = new HashSet<>();
         excludeFilters.add(ID_A_32);
         excludeFilters.add(MODULE_NAME_B);
         mRepo.initialize(1, mTestsDir, ABIS, DEVICE_TOKENS, TEST_ARGS, MODULE_ARGS, includeFilters,
@@ -234,8 +234,8 @@
         abis.add(new Abi(ABI_64, "64"));
         ArrayList<String> emptyList = new ArrayList<>();
 
-        mRepo.initialize(3, mTestsDir, abis, DEVICE_TOKENS, emptyList, emptyList, emptyList,
-                         emptyList, mBuild);
+        mRepo.initialize(3, mTestsDir, abis, DEVICE_TOKENS, emptyList, emptyList, INCLUDES,
+                         EXCLUDES, mBuild);
 
         List<IModuleDef> modules = new ArrayList<>();
         modules.addAll(mRepo.getLargeModules());
diff --git a/common/util/src/com/android/compatibility/common/util/InfoStore.java b/common/util/src/com/android/compatibility/common/util/InfoStore.java
index 6c5cc0d..b8014f7 100644
--- a/common/util/src/com/android/compatibility/common/util/InfoStore.java
+++ b/common/util/src/com/android/compatibility/common/util/InfoStore.java
@@ -189,4 +189,8 @@
         }
         return value;
     }
+
+    protected static boolean isDoubleNaNOrInfinite(Double value) {
+        return Double.isNaN(value) || Double.isInfinite(value);
+    }
 }
diff --git a/hostsidetests/appsecurity/test-apps/DocumentClient/src/com/android/cts/documentclient/ScopedDirectoryAccessClientTest.java b/hostsidetests/appsecurity/test-apps/DocumentClient/src/com/android/cts/documentclient/ScopedDirectoryAccessClientTest.java
index 5747adf..dec8769 100644
--- a/hostsidetests/appsecurity/test-apps/DocumentClient/src/com/android/cts/documentclient/ScopedDirectoryAccessClientTest.java
+++ b/hostsidetests/appsecurity/test-apps/DocumentClient/src/com/android/cts/documentclient/ScopedDirectoryAccessClientTest.java
@@ -246,10 +246,14 @@
     }
 
     public void testRemovePackageStep1UserDenies() throws Exception {
+        if (!supportedHardware()) return;
+
         deniesOnceForAllTest(getPrimaryVolume(), DIRECTORY_NOTIFICATIONS);
     }
 
     public void testRemovePackageStep2UserAcceptsDoNotClear() throws Exception {
+        if (!supportedHardware()) return;
+
         userAcceptsTest(getPrimaryVolume(), DIRECTORY_NOTIFICATIONS);
     }
 
diff --git a/hostsidetests/net/app/src/com/android/cts/net/hostside/AbstractAppIdleTestCase.java b/hostsidetests/net/app/src/com/android/cts/net/hostside/AbstractAppIdleTestCase.java
index ba56665..78ba4b9 100644
--- a/hostsidetests/net/app/src/com/android/cts/net/hostside/AbstractAppIdleTestCase.java
+++ b/hostsidetests/net/app/src/com/android/cts/net/hostside/AbstractAppIdleTestCase.java
@@ -16,6 +16,7 @@
 
 package com.android.cts.net.hostside;
 
+import android.os.SystemClock;
 import android.util.Log;
 
 /**
@@ -138,4 +139,40 @@
         assertsForegroundAlwaysHasNetworkAccess();
         assertBackgroundNetworkAccess(true);
     }
+
+    public void testAppIdleNetworkAccess_whenCharging() throws Exception {
+        if (!isSupported()) return;
+
+        // Check that app is paroled when charging
+        setAppIdle(true);
+        assertBackgroundNetworkAccess(false);
+        turnBatteryOn();
+        assertBackgroundNetworkAccess(true);
+        turnBatteryOff();
+        assertBackgroundNetworkAccess(false);
+
+        // Check that app is restricted when not idle but power-save is on
+        setAppIdle(false);
+        assertBackgroundNetworkAccess(true);
+        setBatterySaverMode(true);
+        assertBackgroundNetworkAccess(false);
+        turnBatteryOn();
+        assertBackgroundNetworkAccess(true);
+
+        // And when no longer charging, it still has network access, since it's not idle
+        turnBatteryOff();
+        assertBackgroundNetworkAccess(true);
+    }
+
+    public void testAppIdle_toast() throws Exception {
+        if (!isSupported()) return;
+
+        setAppIdle(true);
+        assertAppIdle(true);
+        assertEquals("Shown", showToast());
+        assertAppIdle(true);
+        // Wait for a couple of seconds for the toast to actually be shown
+        SystemClock.sleep(2000);
+        assertAppIdle(true);
+    }
 }
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 9245a6f..9c9ec0b 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
@@ -64,6 +64,8 @@
             "com.android.cts.net.hostside.app2.action.RECEIVER_READY";
     static final String ACTION_SEND_NOTIFICATION =
             "com.android.cts.net.hostside.app2.action.SEND_NOTIFICATION";
+    static final String ACTION_SHOW_TOAST =
+            "com.android.cts.net.hostside.app2.action.SHOW_TOAST";
     private static final String EXTRA_ACTION = "com.android.cts.net.hostside.app2.extra.ACTION";
     private static final String EXTRA_RECEIVER_NAME =
             "com.android.cts.net.hostside.app2.extra.RECEIVER_NAME";
@@ -782,6 +784,17 @@
         mContext.sendBroadcast(intent);
     }
 
+    protected String showToast() {
+        final Intent intent = new Intent(ACTION_SHOW_TOAST);
+        intent.setPackage(TEST_APP2_PKG);
+        Log.d(TAG, "Sending request to show toast");
+        try {
+            return sendOrderedBroadcast(intent, 3 * SECOND_IN_MS);
+        } catch (Exception e) {
+            return "";
+        }
+    }
+
     private String toString(int status) {
         switch (status) {
             case RESTRICT_BACKGROUND_STATUS_DISABLED:
diff --git a/hostsidetests/net/app2/AndroidManifest.xml b/hostsidetests/net/app2/AndroidManifest.xml
index 1fa49ba..adf0045 100644
--- a/hostsidetests/net/app2/AndroidManifest.xml
+++ b/hostsidetests/net/app2/AndroidManifest.xml
@@ -46,6 +46,7 @@
                 <action android:name="com.android.cts.net.hostside.app2.action.GET_RESTRICT_BACKGROUND_STATUS" />
                 <action android:name="com.android.cts.net.hostside.app2.action.CHECK_NETWORK" />
                 <action android:name="com.android.cts.net.hostside.app2.action.SEND_NOTIFICATION" />
+                <action android:name="com.android.cts.net.hostside.app2.action.SHOW_TOAST" />
                 </intent-filter>
         </receiver>
     </application>
diff --git a/hostsidetests/net/app2/src/com/android/cts/net/hostside/app2/Common.java b/hostsidetests/net/app2/src/com/android/cts/net/hostside/app2/Common.java
index 8806e3b..e07c0f5 100644
--- a/hostsidetests/net/app2/src/com/android/cts/net/hostside/app2/Common.java
+++ b/hostsidetests/net/app2/src/com/android/cts/net/hostside/app2/Common.java
@@ -38,6 +38,8 @@
             "com.android.cts.net.hostside.app2.action.FINISH_ACTIVITY";
     static final String ACTION_SEND_NOTIFICATION =
             "com.android.cts.net.hostside.app2.action.SEND_NOTIFICATION";
+    static final String ACTION_SHOW_TOAST =
+            "com.android.cts.net.hostside.app2.action.SHOW_TOAST";
     static final String EXTRA_ACTION = "com.android.cts.net.hostside.app2.extra.ACTION";
     static final String EXTRA_RECEIVER_NAME =
             "com.android.cts.net.hostside.app2.extra.RECEIVER_NAME";
diff --git a/hostsidetests/net/app2/src/com/android/cts/net/hostside/app2/MyBroadcastReceiver.java b/hostsidetests/net/app2/src/com/android/cts/net/hostside/app2/MyBroadcastReceiver.java
index 6d01b15..733c3aa 100644
--- a/hostsidetests/net/app2/src/com/android/cts/net/hostside/app2/MyBroadcastReceiver.java
+++ b/hostsidetests/net/app2/src/com/android/cts/net/hostside/app2/MyBroadcastReceiver.java
@@ -23,6 +23,7 @@
 import static com.android.cts.net.hostside.app2.Common.ACTION_GET_RESTRICT_BACKGROUND_STATUS;
 import static com.android.cts.net.hostside.app2.Common.ACTION_RECEIVER_READY;
 import static com.android.cts.net.hostside.app2.Common.ACTION_SEND_NOTIFICATION;
+import static com.android.cts.net.hostside.app2.Common.ACTION_SHOW_TOAST;
 import static com.android.cts.net.hostside.app2.Common.EXTRA_ACTION;
 import static com.android.cts.net.hostside.app2.Common.EXTRA_NOTIFICATION_ID;
 import static com.android.cts.net.hostside.app2.Common.EXTRA_NOTIFICATION_TYPE;
@@ -51,6 +52,7 @@
 import android.net.NetworkInfo;
 import android.os.Bundle;
 import android.util.Log;
+import android.widget.Toast;
 
 import java.net.HttpURLConnection;
 import java.net.URL;
@@ -104,6 +106,9 @@
             case ACTION_SEND_NOTIFICATION:
                 sendNotification(context, intent);
                 break;
+            case ACTION_SHOW_TOAST:
+                showToast(context);
+                break;
             default:
                 Log.e(TAG, "received unexpected action: " + action);
         }
@@ -302,4 +307,9 @@
         ((NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE))
             .notify(notificationId, notification);
     }
+
+    private void showToast(Context context) {
+        Toast.makeText(context, "Toast from CTS test", Toast.LENGTH_SHORT).show();
+        setResultData("Shown");
+    }
 }
diff --git a/hostsidetests/net/src/com/android/cts/net/HostsideRestrictBackgroundNetworkTests.java b/hostsidetests/net/src/com/android/cts/net/HostsideRestrictBackgroundNetworkTests.java
index 7d5f817..faf75d9 100644
--- a/hostsidetests/net/src/com/android/cts/net/HostsideRestrictBackgroundNetworkTests.java
+++ b/hostsidetests/net/src/com/android/cts/net/HostsideRestrictBackgroundNetworkTests.java
@@ -171,6 +171,22 @@
                 "testBackgroundNetworkAccess_enabled");
     }
 
+    public void testAppIdleNonMetered_whenCharging() throws Exception {
+        runDeviceTests(TEST_PKG, TEST_PKG + ".AppIdleNonMeteredTest",
+                "testAppIdleNetworkAccess_whenCharging");
+    }
+
+    public void testAppIdleMetered_whenCharging() throws Exception {
+        runDeviceTests(TEST_PKG, TEST_PKG + ".AppIdleMeteredTest",
+                "testAppIdleNetworkAccess_whenCharging");
+    }
+
+    public void testAppIdle_toast() throws Exception {
+        // Check that showing a toast doesn't bring an app out of standby
+        runDeviceTests(TEST_PKG, TEST_PKG + ".AppIdleNonMeteredTest",
+                "testAppIdle_toast");
+    }
+
     /********************
      * Doze Mode tests. *
      ********************/
diff --git a/tests/camera/src/android/hardware/camera2/cts/DngCreatorTest.java b/tests/camera/src/android/hardware/camera2/cts/DngCreatorTest.java
index 6ab55b0..8f32d52 100644
--- a/tests/camera/src/android/hardware/camera2/cts/DngCreatorTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/DngCreatorTest.java
@@ -40,6 +40,7 @@
 import android.media.Image;
 import android.media.ImageReader;
 import android.os.ConditionVariable;
+import android.os.SystemClock;
 import android.util.Log;
 import android.util.Pair;
 import android.util.Size;
@@ -54,8 +55,10 @@
 import java.util.Arrays;
 import java.util.Calendar;
 import java.util.Collections;
+import java.util.Date;
 import java.util.List;
 import java.util.TimeZone;
+import java.text.SimpleDateFormat;
 
 import static android.hardware.camera2.cts.helpers.AssertHelpers.*;
 
@@ -243,10 +246,19 @@
                         previewListener));
                 captureListeners.add(previewListener);
 
+                Date beforeCaptureDate = new Date();
                 Pair<List<Image>, CaptureResult> resultPair = captureSingleRawShot(activeArraySize,
                         captureReaders, /*waitForAe*/false, captureListeners);
+                Date afterCaptureDate = new Date();
                 CameraCharacteristics characteristics = mStaticInfo.getCharacteristics();
 
+                if (VERBOSE) {
+                    Log.v(TAG, "Sensor timestamp (ms): " +
+                            resultPair.second.get(CaptureResult.SENSOR_TIMESTAMP) / 1000000);
+                    Log.v(TAG, "SystemClock.elapsedRealtimeNanos (ms): " +
+                            SystemClock.elapsedRealtimeNanos() / 1000000);
+                    Log.v(TAG, "SystemClock.uptimeMillis(): " + SystemClock.uptimeMillis());
+                }
                 // Test simple writeImage, no header checks
                 DngCreator dngCreator = new DngCreator(characteristics, resultPair.second);
                 Location l = new Location("test");
@@ -264,7 +276,7 @@
 
                 String filePath = DEBUG_FILE_NAME_BASE + "/camera_thumb_" + deviceId + "_" +
                         DEBUG_DNG_FILE;
-                // Write out captured DNG file for the first camera device if setprop is enabled
+                // Write out captured DNG file for the first camera device
                 fileStream = new FileOutputStream(filePath);
                 fileStream.write(outputStream.toByteArray());
                 fileStream.flush();
@@ -292,6 +304,30 @@
                         exifInterface.getAttributeInt(ExifInterface.TAG_ORIENTATION,
                                 ExifInterface.ORIENTATION_UNDEFINED));
 
+                // Verify the date/time
+                final SimpleDateFormat dngDateTimeStampFormat =
+                        new SimpleDateFormat("yyyy:MM:dd HH:mm:ss");
+                dngDateTimeStampFormat.setLenient(false);
+
+                String dateTimeString =
+                        exifInterface.getAttribute(ExifInterface.TAG_DATETIME);
+                assertTrue(dateTimeString != null);
+
+                Date dateTime = dngDateTimeStampFormat.parse(dateTimeString);
+                long captureTimeMs = dateTime.getTime();
+
+                Log.i(TAG, "DNG DateTime tag: " + dateTimeString);
+                Log.i(TAG, "Before capture time: " + beforeCaptureDate.getTime());
+                Log.i(TAG, "Capture time: " + captureTimeMs);
+                Log.i(TAG, "After capture time: " + afterCaptureDate.getTime());
+
+                // Offset beforeCaptureTime by 1 second to account for rounding down of
+                // DNG tag
+                long beforeCaptureTimeMs = beforeCaptureDate.getTime() - 1000;
+                long afterCaptureTimeMs = afterCaptureDate.getTime();
+                assertTrue(captureTimeMs >= beforeCaptureTimeMs);
+                assertTrue(captureTimeMs <= afterCaptureTimeMs);
+
                 if (!VERBOSE) {
                     // Delete the captured DNG file.
                     File dngFile = new File(filePath);
diff --git a/tests/tests/media/src/android/media/cts/DeviceUtils.java b/tests/tests/media/src/android/media/cts/DeviceUtils.java
index 41afce1..c2b1c32 100644
--- a/tests/tests/media/src/android/media/cts/DeviceUtils.java
+++ b/tests/tests/media/src/android/media/cts/DeviceUtils.java
@@ -16,14 +16,21 @@
 
 package android.media.cts;
 
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.PackageManager;
+
 import android.media.AudioDeviceInfo;
 import android.media.AudioManager;
 
+import android.util.Log;
+
 /* package */ class DeviceUtils {
     private static final String TAG = "DeviceUtils";
 
     /* package */ static boolean hasOutputDevice(AudioManager audioMgr) {
-
         AudioDeviceInfo[] devices = audioMgr.getDevices(AudioManager.GET_DEVICES_OUTPUTS);
         return devices.length != 0;
     }
@@ -32,4 +39,27 @@
         AudioDeviceInfo[] devices = audioMgr.getDevices(AudioManager.GET_DEVICES_INPUTS);
         return devices.length != 0;
     }
+
+    /* package */ static boolean isTVDevice(Context context) {
+        return context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_LEANBACK);
+    }
+
+    /*
+     * HDMI
+     */
+    /* package */ static boolean isHDMIConnected(Context context) {
+        // configure the IntentFilter
+        IntentFilter intentFilter = new IntentFilter();
+        intentFilter.addAction(AudioManager.ACTION_HDMI_AUDIO_PLUG);
+        Intent intent = context.registerReceiver(null, intentFilter);
+
+        String action = intent.getAction();
+        boolean isHDMIConnected = false;
+        if (action.equals(AudioManager.ACTION_HDMI_AUDIO_PLUG)) {
+            isHDMIConnected =
+                    intent.getIntExtra(AudioManager.EXTRA_AUDIO_PLUG_STATE, 0) != 0;
+        }
+
+        return isHDMIConnected;
+    }
 }
diff --git a/tests/tests/media/src/android/media/cts/EnumDevicesTest.java b/tests/tests/media/src/android/media/cts/EnumDevicesTest.java
index 94af087..e3ed453 100644
--- a/tests/tests/media/src/android/media/cts/EnumDevicesTest.java
+++ b/tests/tests/media/src/android/media/cts/EnumDevicesTest.java
@@ -28,10 +28,14 @@
 
 import android.test.AndroidTestCase;
 
+import android.util.Log;
+
 /**
  * TODO: Insert description here. (generated by pmclean)
  */
 public class EnumDevicesTest extends AndroidTestCase {
+    private static final String TAG = "EnumDevicesTest";
+
     private AudioManager mAudioManager;
 
     boolean mAddCallbackCalled = false;
@@ -54,27 +58,41 @@
         assertTrue(deviceList != null);
         assertTrue(deviceList.length == 0);
 
+        PackageManager pkgMgr = mContext.getPackageManager();
+
+        boolean isTvDevice = DeviceUtils.isTVDevice(mContext);
+
         int numOutputDevices = 0;
-        if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUDIO_OUTPUT)) {
+        if (pkgMgr.hasSystemFeature(PackageManager.FEATURE_AUDIO_OUTPUT)) {
             // test OUTPUTS
             deviceList = mAudioManager.getDevices(AudioManager.GET_DEVICES_OUTPUTS);
             assertTrue(deviceList != null);
-            numOutputDevices = deviceList.length;
-            assertTrue(numOutputDevices != 0);
 
-            // all should be "sinks"
+            numOutputDevices = deviceList.length;
+            if (numOutputDevices == 0) {
+                boolean isHDMIConnected = DeviceUtils.isHDMIConnected(mContext);
+                if (isTvDevice && !isHDMIConnected) {
+                    Log.w(TAG, "getDevices test: failure due to missing reported output " +
+                               "or the test is run on a TV device with no HDMI connected");
+                }
+                assertTrue("getDevices test: failure due to missing HDMI connection " +
+                           "or missing output", false);
+            }
+
+            // any reported output devices should be "sinks"
             for(int index = 0; index < numOutputDevices; index++) {
                 assertTrue(deviceList[index].isSink());
             }
         }
 
         int numInputDevices = 0;
-        if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_MICROPHONE)) {
+        if (pkgMgr.hasSystemFeature(PackageManager.FEATURE_MICROPHONE)) {
             // test INPUTS
             deviceList = mAudioManager.getDevices(AudioManager.GET_DEVICES_INPUTS);
             assertTrue(deviceList != null);
+
             numInputDevices = deviceList.length;
-            assertTrue(numInputDevices != 0);
+            assertTrue(numOutputDevices != 0);
 
             // all should be "sources"
             for(int index = 0; index < numInputDevices; index++) {
@@ -86,6 +104,7 @@
         if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUDIO_OUTPUT) &&
                 mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_MICROPHONE)) {
             deviceList = mAudioManager.getDevices(AudioManager.GET_DEVICES_ALL);
+            assertTrue(deviceList != null);
             assertTrue(deviceList.length == (numOutputDevices + numInputDevices));
         }
     }
diff --git a/tests/tests/os/src/android/os/cts/SecurityPatchTest.java b/tests/tests/os/src/android/os/cts/SecurityPatchTest.java
index 4aaf7f2..2a5e360 100644
--- a/tests/tests/os/src/android/os/cts/SecurityPatchTest.java
+++ b/tests/tests/os/src/android/os/cts/SecurityPatchTest.java
@@ -32,7 +32,7 @@
     private static final String SECURITY_PATCH_DATE_ERROR =
             "ro.build.version.security_patch should be \"%d-%02d\" or later. Found \"%s\"";
     private static final int SECURITY_PATCH_YEAR = 2016;
-    private static final int SECURITY_PATCH_MONTH = 10;
+    private static final int SECURITY_PATCH_MONTH = 11;
 
     private boolean mSkipTests = false;
 
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/SingleSourceForEachTest.java b/tests/tests/renderscript/src/android/renderscript/cts/SingleSourceForEachTest.java
index e005013..a55d847 100644
--- a/tests/tests/renderscript/src/android/renderscript/cts/SingleSourceForEachTest.java
+++ b/tests/tests/renderscript/src/android/renderscript/cts/SingleSourceForEachTest.java
@@ -83,6 +83,12 @@
     }
 
     public void testKernelLaunchWithOptions() {
+        // Initialize the testOutputAlloc and baselineOutputAlloc to be the same
+        // as the input. When we the check the entire output later, the lower half
+        // of the output Allocations should also be the same.
+        baselineOutputAlloc.copyFrom(testInputArray);
+        testOutputAlloc.copyFrom(testInputArray);
+
         Script.LaunchOptions sc = new Script.LaunchOptions();
         sc.setX(0, X);
         sc.setY(0, Y / 2);
diff --git a/tests/tests/systemintents/Android.mk b/tests/tests/systemintents/Android.mk
new file mode 100644
index 0000000..1af6702
--- /dev/null
+++ b/tests/tests/systemintents/Android.mk
@@ -0,0 +1,34 @@
+# Copyright (C) 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+
+# Tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_PACKAGE_NAME := CtsSystemIntentTestCases
+
+LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner
+
+LOCAL_SDK_VERSION := test_current
+
+include $(BUILD_CTS_PACKAGE)
diff --git a/tests/tests/systemintents/AndroidManifest.xml b/tests/tests/systemintents/AndroidManifest.xml
new file mode 100644
index 0000000..da0cbac
--- /dev/null
+++ b/tests/tests/systemintents/AndroidManifest.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+  ~ Copyright (C) 2016 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="android.systemintents.cts">
+
+    <application>
+        <uses-library android:name="android.test.runner" />
+    </application>
+
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+                     android:targetPackage="android.systemintents.cts"
+                     android:label="System intent tests"/>
+</manifest>
\ No newline at end of file
diff --git a/tests/tests/systemintents/AndroidTest.xml b/tests/tests/systemintents/AndroidTest.xml
new file mode 100644
index 0000000..eceb909
--- /dev/null
+++ b/tests/tests/systemintents/AndroidTest.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2016 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License
+  -->
+<configuration description="Config for CTS system intent test cases">
+    <target_preparer class="com.android.compatibility.common.tradefed.targetprep.ApkInstaller">
+        <option name="cleanup-apks" value="true" />
+        <option name="test-file-name" value="CtsSystemIntentTestCases.apk" />
+    </target_preparer>
+    <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+        <option name="package" value="android.systemintents.cts" />
+    </test>
+</configuration>
\ No newline at end of file
diff --git a/tests/tests/systemintents/src/android/systemintents/cts/TestSystemIntents.java b/tests/tests/systemintents/src/android/systemintents/cts/TestSystemIntents.java
new file mode 100644
index 0000000..c572629
--- /dev/null
+++ b/tests/tests/systemintents/src/android/systemintents/cts/TestSystemIntents.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.systemintents.cts;
+
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.res.Configuration;
+import android.net.Uri;
+import android.provider.Settings;
+import android.support.test.filters.MediumTest;
+import android.test.AndroidTestCase;
+import android.util.Log;
+
+import org.junit.Rule;
+import org.junit.Test;
+
+@MediumTest
+public class TestSystemIntents extends AndroidTestCase {
+    /*
+     * List of activity intents defined by the system.  Activities to handle each of these
+     * intents must all exist.
+     *
+     * They are Intents here rather than simply action strings so that the test can
+     * easily accommodate data URIs or similar for correct resolution.
+     *
+     * The flags associated with each intent indicate kinds of device on which the given
+     * UI intent is *not* applicable.
+     */
+
+    private static final int EXCLUDE_TV = 1 << 0;
+    private static final int EXCLUDE_WATCH = 1 << 1;
+    private static final int EXCLUDE_NON_TELEPHONY = 1 << 2;
+
+    class IntentEntry {
+        public int flags;
+        public Intent intent;
+
+        public IntentEntry(int f, Intent i) {
+            flags = f;
+            intent = i;
+        }
+    }
+
+    @Rule
+    private final IntentEntry[] mTestIntents = {
+            /* Settings-namespace intent actions */
+            new IntentEntry(0, new Intent(Settings.ACTION_SETTINGS)),
+            new IntentEntry(0, new Intent(Settings.ACTION_WEBVIEW_SETTINGS)),
+            new IntentEntry(0, new Intent(Settings.ACTION_APPLICATION_DEVELOPMENT_SETTINGS)),
+            new IntentEntry(0, new Intent(Settings.ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS)),
+            new IntentEntry(0, new Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS)
+                    .setData(Uri.parse("package:android.systemintents.cts"))),
+            new IntentEntry(0, new Intent(Settings.ACTION_IGNORE_BACKGROUND_DATA_RESTRICTIONS_SETTINGS)
+                    .setData(Uri.parse("package:android.systemintents.cts"))),
+            new IntentEntry(0, new Intent(Settings.ACTION_HOME_SETTINGS)),
+            new IntentEntry(EXCLUDE_NON_TELEPHONY,
+                    new Intent(Settings.ACTION_APN_SETTINGS)),
+            new IntentEntry(EXCLUDE_TV|EXCLUDE_WATCH,
+                    new Intent(Settings.ACTION_NOTIFICATION_POLICY_ACCESS_SETTINGS))
+    };
+
+    @Test
+    public void testSystemIntents() {
+        final PackageManager pm = getContext().getPackageManager();
+        int productFlags = 0;
+
+        if (pm.hasSystemFeature(PackageManager.FEATURE_LEANBACK)) {
+            productFlags |= EXCLUDE_TV;
+        }
+
+        if (!pm.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
+            productFlags |= EXCLUDE_NON_TELEPHONY;
+        }
+
+        final Configuration config = getContext().getResources().getConfiguration();
+        if ((config.uiMode & Configuration.UI_MODE_TYPE_WATCH) != 0) {
+            productFlags |= EXCLUDE_WATCH;
+        }
+
+        for (IntentEntry e : mTestIntents) {
+            if ((productFlags & e.flags) == 0) {
+                final ResolveInfo ri = pm.resolveActivity(e.intent, PackageManager.MATCH_DEFAULT_ONLY);
+                assertTrue("API intent " + e.intent + " not implemented by any activity", ri != null);
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/tests/tests/telephony/src/android/telephony/cts/SmsMessageTest.java b/tests/tests/telephony/src/android/telephony/cts/SmsMessageTest.java
index 2406ad8..e247775 100644
--- a/tests/tests/telephony/src/android/telephony/cts/SmsMessageTest.java
+++ b/tests/tests/telephony/src/android/telephony/cts/SmsMessageTest.java
@@ -114,7 +114,7 @@
 
         // Test create from null Pdu
         sms = SmsMessage.createFromPdu(null, SmsMessage.FORMAT_3GPP);
-        assertNotNull(sms);
+        assertNull(sms);
 
         // Test create from long Pdu
         pdu = "07912160130310F2040B915121927786F300036060924180008A0DA"
diff --git a/tests/tests/view/AndroidManifest.xml b/tests/tests/view/AndroidManifest.xml
index 7f7bd8c..1061ff3 100644
--- a/tests/tests/view/AndroidManifest.xml
+++ b/tests/tests/view/AndroidManifest.xml
@@ -241,6 +241,14 @@
             </intent-filter>
         </activity>
 
+        <activity android:name="android.view.cts.DragDropActivity"
+                  android:label="DragDropActivity">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.FRAMEWORK_INSTRUMENTATION_TEST" />
+            </intent-filter>
+        </activity>
+
         <activity android:name="android.view.cts.surfacevalidator.CapturedActivity"
                   android:theme="@style/WhiteBackgroundTheme">
             <intent-filter>
diff --git a/tests/tests/view/res/layout/drag_drop_layout.xml b/tests/tests/view/res/layout/drag_drop_layout.xml
new file mode 100644
index 0000000..3800d99
--- /dev/null
+++ b/tests/tests/view/res/layout/drag_drop_layout.xml
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ -->
+
+<LinearLayout
+        android:id="@+id/drag_drop_activity_main"
+        xmlns:android="http://schemas.android.com/apk/res/android"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:orientation="vertical">
+    <FrameLayout
+            android:id="@+id/container"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_margin="64px"
+            android:background="#BBBBBB">
+        <FrameLayout
+                android:id="@+id/subcontainer"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_margin="64px"
+                android:background="#666666">
+            <View
+                    android:id="@+id/inner"
+                    android:layout_width="64px"
+                    android:layout_height="64px"
+                    android:layout_margin="64px"
+                    android:background="#00FF00" />
+        </FrameLayout>
+    </FrameLayout>
+    <View
+            android:id="@+id/draggable"
+            android:layout_width="64px"
+            android:layout_height="64px"
+            android:layout_margin="64px"
+            android:background="#0000FF" />
+</LinearLayout>
diff --git a/tests/tests/view/src/android/view/cts/DragDropActivity.java b/tests/tests/view/src/android/view/cts/DragDropActivity.java
new file mode 100644
index 0000000..b4324e3
--- /dev/null
+++ b/tests/tests/view/src/android/view/cts/DragDropActivity.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.cts;
+
+import android.view.cts.R;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+public class DragDropActivity extends Activity {
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.drag_drop_layout);
+    }
+}
\ No newline at end of file
diff --git a/tests/tests/view/src/android/view/cts/DragDropTest.java b/tests/tests/view/src/android/view/cts/DragDropTest.java
new file mode 100644
index 0000000..968dc7b
--- /dev/null
+++ b/tests/tests/view/src/android/view/cts/DragDropTest.java
@@ -0,0 +1,592 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.cts;
+
+import com.android.internal.util.ArrayUtils;
+
+import android.app.Instrumentation;
+import android.app.UiAutomation;
+import android.content.pm.PackageManager;
+import android.os.SystemClock;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+import android.view.DragEvent;
+import android.view.InputDevice;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.lang.InterruptedException;
+import java.lang.StringBuilder;
+import java.lang.Thread;
+import java.util.ArrayList;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.Objects;
+
+import static junit.framework.TestCase.*;
+
+@RunWith(AndroidJUnit4.class)
+public class DragDropTest {
+    static final String TAG = "DragDropTest";
+
+    final Instrumentation mInstrumentation = InstrumentationRegistry.getInstrumentation();
+    final UiAutomation mAutomation = mInstrumentation.getUiAutomation();
+
+    @Rule
+    public ActivityTestRule<DragDropActivity> mActivityRule =
+            new ActivityTestRule<>(DragDropActivity.class);
+
+    private DragDropActivity mActivity;
+
+    private CountDownLatch mEndReceived;
+
+    static boolean equal(DragEvent ev1, DragEvent ev2) {
+        return ev1.getAction() == ev2.getAction() &&
+                ev1.getX() == ev2.getX() &&
+                ev1.getY() == ev2.getY() &&
+                Objects.equals(ev1.getClipData(), ev2.getClipData()) &&
+                Objects.equals(ev1.getClipDescription(), ev2.getClipDescription()) &&
+                Objects.equals(ev1.getDragAndDropPermissions(), ev2.getDragAndDropPermissions()) &&
+                Objects.equals(ev1.getLocalState(), ev2.getLocalState()) &&
+                ev1.getResult() == ev2.getResult();
+    }
+
+    class LogEntry {
+        public View v;
+        public DragEvent ev;
+
+        public LogEntry(View v, DragEvent ev) {
+            this.v = v;
+            this.ev = DragEvent.obtain(ev);
+        }
+
+        public boolean eq(LogEntry other) {
+            return v == other.v && equal(ev, other.ev);
+        }
+    }
+
+    // Actual and expected sequences of events.
+    // While the test is running, logs should be accessed only from the main thread.
+    final private ArrayList<LogEntry> mActual = new ArrayList<LogEntry> ();
+    final private ArrayList<LogEntry> mExpected = new ArrayList<LogEntry> ();
+
+    static private DragEvent obtainDragEvent(int action, int x, int y, boolean result) {
+        return DragEvent.obtain(action, x, y, null, null, null, null, result);
+    }
+
+    private void logEvent(View v, DragEvent ev) {
+        if (ev.getAction() == DragEvent.ACTION_DRAG_ENDED) {
+            mEndReceived.countDown();
+        }
+        mActual.add(new LogEntry(v, ev));
+    }
+
+    // Add expected event for a view, with zero coordinates.
+    private void expectEvent5(int action, int viewId) {
+        View v = mActivity.findViewById(viewId);
+        mExpected.add(new LogEntry(v, obtainDragEvent(action, 0, 0, false)));
+    }
+
+    // Add expected event for a view.
+    private void expectEndEvent(int viewId, int x, int y, boolean result) {
+        View v = mActivity.findViewById(viewId);
+        mExpected.add(new LogEntry(v, obtainDragEvent(DragEvent.ACTION_DRAG_ENDED, x, y, result)));
+    }
+
+    // Add expected successful-end event for a view.
+    private void expectEndEventSuccess(int viewId) {
+        expectEndEvent(viewId, 0, 0, true);
+    }
+
+    // Add expected failed-end event for a view, with the release coordinates shifted by 6 relative
+    // to the left-upper corner of a view with id releaseViewId.
+    private void expectEndEventFailure6(int viewId, int releaseViewId) {
+        View v = mActivity.findViewById(viewId);
+        View release = mActivity.findViewById(releaseViewId);
+        int [] releaseLoc = release.getLocationOnScreen();
+        mExpected.add(new LogEntry(v, obtainDragEvent(DragEvent.ACTION_DRAG_ENDED,
+                releaseLoc[0] + 6, releaseLoc[1] + 6, false)));
+    }
+
+    // Add expected event for a view, with coordinates over view locationViewId, with the specified
+    // offset from the location view's upper-left corner.
+    private void expectEventWithOffset(int action, int viewId, int locationViewId, int offset) {
+        View v = mActivity.findViewById(viewId);
+        View locationView = mActivity.findViewById(locationViewId);
+        int [] viewLocation = v.getLocationOnScreen();
+        int [] locationViewLocation = locationView.getLocationOnScreen();
+        mExpected.add(new LogEntry(v, obtainDragEvent(action,
+                locationViewLocation[0] - viewLocation[0] + offset,
+                locationViewLocation[1] - viewLocation[1] + offset, false)));
+    }
+
+    private void expectEvent5(int action, int viewId, int locationViewId) {
+        expectEventWithOffset(action, viewId, locationViewId, 5);
+    }
+
+    // See comment for injectMouse6 on why we need both *5 and *6 methods.
+    private void expectEvent6(int action, int viewId, int locationViewId) {
+        expectEventWithOffset(action, viewId, locationViewId, 6);
+    }
+
+    // Inject mouse event over a given view, with specified offset from its left-upper corner.
+    private void injectMouseWithOffset(int viewId, int action, int offset) {
+        runOnMain(() -> {
+            View v = mActivity.findViewById(viewId);
+            int [] destLoc = v.getLocationOnScreen();
+            long downTime = SystemClock.uptimeMillis();
+            MotionEvent event = MotionEvent.obtain(downTime, downTime, action,
+                    destLoc[0] + offset, destLoc[1] + offset, 1);
+            event.setSource(InputDevice.SOURCE_TOUCHSCREEN);
+            mAutomation.injectInputEvent(event, false);
+        });
+
+        // Wait till the mouse event generates drag events. Also, some waiting needed because the
+        // system seems to collapse too frequent mouse events.
+        try {
+            Thread.sleep(100);
+        } catch (Exception e) {
+            fail("Exception while wait: " + e);
+        }
+    }
+
+    // Inject mouse event over a given view, with offset 5 from its left-upper corner.
+    private void injectMouse5(int viewId, int action) {
+        injectMouseWithOffset(viewId, action, 5);
+    }
+
+    // Inject mouse event over a given view, with offset 6 from its left-upper corner.
+    // We need both injectMouse5 and injectMouse6 if we want to inject 2 events in a row in the same
+    // view, and want them to produce distinct drag events or simply drag events with different
+    // coordinates.
+    private void injectMouse6(int viewId, int action) {
+        injectMouseWithOffset(viewId, action, 6);
+    }
+
+    private String logToString(ArrayList<LogEntry> log) {
+        StringBuilder sb = new StringBuilder();
+        for (int i = 0; i < log.size(); ++i) {
+            LogEntry e = log.get(i);
+            sb.append("#").append(i + 1).append(": ").append(e.ev).append(" @ ").
+                    append(e.v.toString()).append('\n');
+        }
+        return sb.toString();
+    }
+
+    private void failWithLogs(String message) {
+        fail(message + ":\nExpected event sequence:\n" + logToString(mExpected) +
+                "\nActual event sequence:\n" + logToString(mActual));
+    }
+
+    private void verifyEventLog() {
+        try {
+            assertTrue("Timeout while waiting for END event",
+                    mEndReceived.await(1, TimeUnit.SECONDS));
+        } catch (InterruptedException e) {
+            fail("Got InterruptedException while waiting for END event");
+        }
+
+        // Verify the log.
+        runOnMain(() -> {
+            if (mExpected.size() != mActual.size()) {
+                failWithLogs("Actual log has different size than expected");
+            }
+
+            for (int i = 0; i < mActual.size(); ++i) {
+                if (!mActual.get(i).eq(mExpected.get(i))) {
+                    failWithLogs("Actual event #" + (i + 1) + " is different from expected");
+                }
+            }
+        });
+    }
+
+    private boolean init() {
+        // Only run for non-watch devices
+        if (mActivity.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH)) {
+            return false;
+        }
+        return true;
+    }
+
+    @Before
+    public void setUp() {
+        mActivity = mActivityRule.getActivity();
+        mEndReceived = new CountDownLatch(1);
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        mActual.clear();
+        mExpected.clear();
+    }
+
+    // Sets handlers on all views in a tree, which log the event and return false.
+    private void setRejectingHandlersOnTree(View v) {
+        v.setOnDragListener((_v, ev) -> {
+            logEvent(_v, ev);
+            return false;
+        });
+
+        if (v instanceof ViewGroup) {
+            ViewGroup group = (ViewGroup) v;
+            for (int i = 0; i < group.getChildCount(); ++i) {
+                setRejectingHandlersOnTree(group.getChildAt(i));
+            }
+        }
+    }
+
+    private void runOnMain(Runnable runner) {
+        mInstrumentation.runOnMainSync(runner);
+    }
+
+    private void startDrag() {
+        // Mouse down. Required for the drag to start.
+        injectMouse5(R.id.draggable, MotionEvent.ACTION_DOWN);
+
+        runOnMain(() -> {
+            // Start drag.
+            View v = mActivity.findViewById(R.id.draggable);
+            assertTrue("Couldn't start drag",
+                    v.startDragAndDrop(null, new View.DragShadowBuilder(v), null, 0));
+        });
+    }
+
+    /**
+     * Tests that no drag-drop events are sent to views that aren't supposed to receive them.
+     */
+    @Test
+    public void testNoExtraEvents() throws Exception {
+        if (!init()) {
+            return;
+        }
+
+        runOnMain(() -> {
+            // Tell all views in layout to return false to all events, and log them.
+            setRejectingHandlersOnTree(mActivity.findViewById(R.id.drag_drop_activity_main));
+
+            // Override handlers for the inner view and its parent to return true.
+            mActivity.findViewById(R.id.inner).setOnDragListener((v, ev) -> {
+                logEvent(v, ev);
+                return true;
+            });
+            mActivity.findViewById(R.id.subcontainer).setOnDragListener((v, ev) -> {
+                logEvent(v, ev);
+                return true;
+            });
+        });
+
+        startDrag();
+
+        // Move mouse to the outmost view. This shouldn't generate any events since it returned
+        // false to STARTED.
+        injectMouse5(R.id.container, MotionEvent.ACTION_MOVE);
+        // Release mouse over the inner view. This produces DROP there.
+        injectMouse5(R.id.inner, MotionEvent.ACTION_UP);
+
+        expectEvent5(DragEvent.ACTION_DRAG_STARTED, R.id.inner, R.id.draggable);
+        expectEvent5(DragEvent.ACTION_DRAG_STARTED, R.id.subcontainer, R.id.draggable);
+        expectEvent5(DragEvent.ACTION_DRAG_STARTED, R.id.container, R.id.draggable);
+        expectEvent5(DragEvent.ACTION_DRAG_STARTED, R.id.draggable, R.id.draggable);
+        expectEvent5(DragEvent.ACTION_DRAG_STARTED, R.id.drag_drop_activity_main, R.id.draggable);
+
+        expectEvent5(DragEvent.ACTION_DRAG_ENTERED, R.id.inner);
+        expectEvent5(DragEvent.ACTION_DROP, R.id.inner, R.id.inner);
+
+        expectEndEventSuccess(R.id.inner);
+        expectEndEventSuccess(R.id.subcontainer);
+
+        verifyEventLog();
+    }
+
+    /**
+     * Tests events over a non-accepting view with an accepting child get delivered to that view's
+     * parent.
+     */
+    @Test
+    public void testBlackHole() throws Exception {
+        if (!init()) {
+            return;
+        }
+
+        runOnMain(() -> {
+            // Accepting child.
+            mActivity.findViewById(R.id.inner).setOnDragListener((v, ev) -> {
+                logEvent(v, ev);
+                return true;
+            });
+            // Non-accepting parent of that child.
+            mActivity.findViewById(R.id.subcontainer).setOnDragListener((v, ev) -> {
+                logEvent(v, ev);
+                return false;
+            });
+            // Accepting parent of the previous view.
+            mActivity.findViewById(R.id.container).setOnDragListener((v, ev) -> {
+                logEvent(v, ev);
+                return true;
+            });
+        });
+
+        startDrag();
+
+        // Move mouse to the non-accepting view.
+        injectMouse5(R.id.subcontainer, MotionEvent.ACTION_MOVE);
+        // Release mouse over the non-accepting view, with different coordinates.
+        injectMouse6(R.id.subcontainer, MotionEvent.ACTION_UP);
+
+        expectEvent5(DragEvent.ACTION_DRAG_STARTED, R.id.inner, R.id.draggable);
+        expectEvent5(DragEvent.ACTION_DRAG_STARTED, R.id.subcontainer, R.id.draggable);
+        expectEvent5(DragEvent.ACTION_DRAG_STARTED, R.id.container, R.id.draggable);
+
+        expectEvent5(DragEvent.ACTION_DRAG_ENTERED, R.id.container);
+        expectEvent5(DragEvent.ACTION_DRAG_LOCATION, R.id.container, R.id.subcontainer);
+        expectEvent6(DragEvent.ACTION_DROP, R.id.container, R.id.subcontainer);
+
+        expectEndEventSuccess(R.id.inner);
+        expectEndEventSuccess(R.id.container);
+
+        verifyEventLog();
+    }
+
+    /**
+     * Tests generation of ENTER/EXIT events.
+     */
+    @Test
+    public void testEnterExit() throws Exception {
+        if (!init()) {
+            return;
+        }
+
+        runOnMain(() -> {
+            // The setup is same as for testBlackHole.
+
+            // Accepting child.
+            mActivity.findViewById(R.id.inner).setOnDragListener((v, ev) -> {
+                logEvent(v, ev);
+                return true;
+            });
+            // Non-accepting parent of that child.
+            mActivity.findViewById(R.id.subcontainer).setOnDragListener((v, ev) -> {
+                logEvent(v, ev);
+                return false;
+            });
+            // Accepting parent of the previous view.
+            mActivity.findViewById(R.id.container).setOnDragListener((v, ev) -> {
+                logEvent(v, ev);
+                return true;
+            });
+
+        });
+
+        startDrag();
+
+        // Move mouse to the non-accepting view, then to the inner one, then back to the
+        // non-accepting view, then release over the inner.
+        injectMouse5(R.id.subcontainer, MotionEvent.ACTION_MOVE);
+        injectMouse5(R.id.inner, MotionEvent.ACTION_MOVE);
+        injectMouse5(R.id.subcontainer, MotionEvent.ACTION_MOVE);
+        injectMouse5(R.id.inner, MotionEvent.ACTION_UP);
+
+        expectEvent5(DragEvent.ACTION_DRAG_STARTED, R.id.inner, R.id.draggable);
+        expectEvent5(DragEvent.ACTION_DRAG_STARTED, R.id.subcontainer, R.id.draggable);
+        expectEvent5(DragEvent.ACTION_DRAG_STARTED, R.id.container, R.id.draggable);
+
+        expectEvent5(DragEvent.ACTION_DRAG_ENTERED, R.id.container);
+        expectEvent5(DragEvent.ACTION_DRAG_LOCATION, R.id.container, R.id.subcontainer);
+        expectEvent5(DragEvent.ACTION_DRAG_EXITED, R.id.container);
+
+        expectEvent5(DragEvent.ACTION_DRAG_ENTERED, R.id.inner);
+        expectEvent5(DragEvent.ACTION_DRAG_LOCATION, R.id.inner, R.id.inner);
+        expectEvent5(DragEvent.ACTION_DRAG_EXITED, R.id.inner);
+
+        expectEvent5(DragEvent.ACTION_DRAG_ENTERED, R.id.container);
+        expectEvent5(DragEvent.ACTION_DRAG_LOCATION, R.id.container, R.id.subcontainer);
+        expectEvent5(DragEvent.ACTION_DRAG_EXITED, R.id.container);
+
+        expectEvent5(DragEvent.ACTION_DRAG_ENTERED, R.id.inner);
+        expectEvent5(DragEvent.ACTION_DROP, R.id.inner, R.id.inner);
+
+        expectEndEventSuccess(R.id.inner);
+        expectEndEventSuccess(R.id.container);
+
+        verifyEventLog();
+    }
+    /**
+     * Tests events over a non-accepting view that has no accepting ancestors.
+     */
+    @Test
+    public void testOverNowhere() throws Exception {
+        if (!init()) {
+            return;
+        }
+
+        runOnMain(() -> {
+            // Accepting child.
+            mActivity.findViewById(R.id.inner).setOnDragListener((v, ev) -> {
+                logEvent(v, ev);
+                return true;
+            });
+            // Non-accepting parent of that child.
+            mActivity.findViewById(R.id.subcontainer).setOnDragListener((v, ev) -> {
+                logEvent(v, ev);
+                return false;
+            });
+        });
+
+        startDrag();
+
+        // Move mouse to the non-accepting view, then to accepting view, and back, and drop there.
+        injectMouse5(R.id.subcontainer, MotionEvent.ACTION_MOVE);
+        injectMouse5(R.id.inner, MotionEvent.ACTION_MOVE);
+        injectMouse5(R.id.subcontainer, MotionEvent.ACTION_MOVE);
+        injectMouse6(R.id.subcontainer, MotionEvent.ACTION_UP);
+
+        expectEvent5(DragEvent.ACTION_DRAG_STARTED, R.id.inner, R.id.draggable);
+        expectEvent5(DragEvent.ACTION_DRAG_STARTED, R.id.subcontainer, R.id.draggable);
+
+        expectEvent5(DragEvent.ACTION_DRAG_ENTERED, R.id.inner);
+        expectEvent5(DragEvent.ACTION_DRAG_LOCATION, R.id.inner, R.id.inner);
+        expectEvent5(DragEvent.ACTION_DRAG_EXITED, R.id.inner);
+
+        expectEndEventFailure6(R.id.inner, R.id.subcontainer);
+
+        verifyEventLog();
+    }
+
+    /**
+     * Tests that events are properly delivered to a view that is in the middle of the accepting
+     * hierarchy.
+     */
+    @Test
+    public void testAcceptingGroupInTheMiddle() throws Exception {
+        if (!init()) {
+            return;
+        }
+
+        runOnMain(() -> {
+            // Set accepting handlers to the inner view and its 2 ancestors.
+            mActivity.findViewById(R.id.inner).setOnDragListener((v, ev) -> {
+                logEvent(v, ev);
+                return true;
+            });
+            mActivity.findViewById(R.id.subcontainer).setOnDragListener((v, ev) -> {
+                logEvent(v, ev);
+                return true;
+            });
+            mActivity.findViewById(R.id.container).setOnDragListener((v, ev) -> {
+                logEvent(v, ev);
+                return true;
+            });
+        });
+
+        startDrag();
+
+        // Move mouse to the outmost container, then move to the subcontainer and drop there.
+        injectMouse5(R.id.container, MotionEvent.ACTION_MOVE);
+        injectMouse5(R.id.subcontainer, MotionEvent.ACTION_MOVE);
+        injectMouse6(R.id.subcontainer, MotionEvent.ACTION_UP);
+
+        expectEvent5(DragEvent.ACTION_DRAG_STARTED, R.id.inner, R.id.draggable);
+        expectEvent5(DragEvent.ACTION_DRAG_STARTED, R.id.subcontainer, R.id.draggable);
+        expectEvent5(DragEvent.ACTION_DRAG_STARTED, R.id.container, R.id.draggable);
+
+        expectEvent5(DragEvent.ACTION_DRAG_ENTERED, R.id.container);
+        expectEvent5(DragEvent.ACTION_DRAG_LOCATION, R.id.container, R.id.container);
+        expectEvent5(DragEvent.ACTION_DRAG_EXITED, R.id.container);
+
+        expectEvent5(DragEvent.ACTION_DRAG_ENTERED, R.id.subcontainer);
+        expectEvent5(DragEvent.ACTION_DRAG_LOCATION, R.id.subcontainer, R.id.subcontainer);
+        expectEvent6(DragEvent.ACTION_DROP, R.id.subcontainer, R.id.subcontainer);
+
+        expectEndEventSuccess(R.id.inner);
+        expectEndEventSuccess(R.id.subcontainer);
+        expectEndEventSuccess(R.id.container);
+
+        verifyEventLog();
+    }
+
+    /**
+     * Tests that state_drag_hovered and state_drag_can_accept are set correctly.
+     */
+    @Test
+    public void testDrawableState() throws Exception {
+        if (!init()) {
+            return;
+        }
+
+        runOnMain(() -> {
+            // Set accepting handler for the inner view.
+            mActivity.findViewById(R.id.inner).setOnDragListener((v, ev) -> {
+                logEvent(v, ev);
+                return true;
+            });
+            assertFalse(ArrayUtils.contains(
+                    mActivity.findViewById(R.id.inner).getDrawableState(),
+                    android.R.attr.state_drag_can_accept));
+        });
+
+        startDrag();
+
+        runOnMain(() -> {
+            assertFalse(ArrayUtils.contains(
+                    mActivity.findViewById(R.id.inner).getDrawableState(),
+                    android.R.attr.state_drag_hovered));
+            assertTrue(ArrayUtils.contains(
+                    mActivity.findViewById(R.id.inner).getDrawableState(),
+                    android.R.attr.state_drag_can_accept));
+        });
+
+        // Move mouse into the view.
+        injectMouse5(R.id.inner, MotionEvent.ACTION_MOVE);
+        runOnMain(() -> {
+            assertTrue(ArrayUtils.contains(
+                    mActivity.findViewById(R.id.inner).getDrawableState(),
+                    android.R.attr.state_drag_hovered));
+        });
+
+        // Move out.
+        injectMouse5(R.id.subcontainer, MotionEvent.ACTION_MOVE);
+        runOnMain(() -> {
+            assertFalse(ArrayUtils.contains(
+                    mActivity.findViewById(R.id.inner).getDrawableState(),
+                    android.R.attr.state_drag_hovered));
+        });
+
+        // Move in.
+        injectMouse5(R.id.inner, MotionEvent.ACTION_MOVE);
+        runOnMain(() -> {
+            assertTrue(ArrayUtils.contains(
+                    mActivity.findViewById(R.id.inner).getDrawableState(),
+                    android.R.attr.state_drag_hovered));
+        });
+
+        // Release there.
+        injectMouse5(R.id.inner, MotionEvent.ACTION_UP);
+        runOnMain(() -> {
+            assertFalse(ArrayUtils.contains(
+                    mActivity.findViewById(R.id.inner).getDrawableState(),
+                    android.R.attr.state_drag_hovered));
+        });
+    }
+}
\ No newline at end of file
diff --git a/tests/tests/widget/AndroidManifest.xml b/tests/tests/widget/AndroidManifest.xml
index 224966d..df6013d 100644
--- a/tests/tests/widget/AndroidManifest.xml
+++ b/tests/tests/widget/AndroidManifest.xml
@@ -380,6 +380,14 @@
             </intent-filter>
         </activity>
 
+        <activity android:name="android.widget.cts.TimePickerDialogCtsActivity"
+                  android:label="TimePickerDialogCtsActivity">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.FRAMEWORK_INSTRUMENTATION_TEST" />
+            </intent-filter>
+        </activity>
+
         <activity android:name="android.widget.cts.CalendarViewCtsActivity"
                   android:label="CalendarViewCtsActivity">
             <intent-filter>
diff --git a/tests/tests/widget/res/values-w320dp-h426dp/integers.xml b/tests/tests/widget/res/values-w320dp-h426dp/integers.xml
new file mode 100644
index 0000000..a9d049c
--- /dev/null
+++ b/tests/tests/widget/res/values-w320dp-h426dp/integers.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<resources>
+    <integer name="date_picker_mode">2</integer>
+    <integer name="time_picker_mode">2</integer>
+</resources>
diff --git a/tests/tests/widget/res/values-w426dp-h320dp/integers.xml b/tests/tests/widget/res/values-w426dp-h320dp/integers.xml
new file mode 100644
index 0000000..a9d049c
--- /dev/null
+++ b/tests/tests/widget/res/values-w426dp-h320dp/integers.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<resources>
+    <integer name="date_picker_mode">2</integer>
+    <integer name="time_picker_mode">2</integer>
+</resources>
diff --git a/tests/tests/widget/res/values/integers.xml b/tests/tests/widget/res/values/integers.xml
new file mode 100644
index 0000000..b2c1e65
--- /dev/null
+++ b/tests/tests/widget/res/values/integers.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<resources>
+    <integer name="date_picker_mode">1</integer>
+    <integer name="time_picker_mode">1</integer>
+</resources>
diff --git a/tests/tests/widget/res/values/styles.xml b/tests/tests/widget/res/values/styles.xml
index 345b450..8529d73 100644
--- a/tests/tests/widget/res/values/styles.xml
+++ b/tests/tests/widget/res/values/styles.xml
@@ -193,6 +193,11 @@
         <item name="android:windowSwipeToDismiss">false</item>
     </style>
 
+    <style name="Theme_Holo_With_Material_Pickers" parent="@android:style/Theme.Holo">
+        <item name="android:timePickerStyle">@android:style/Widget.Material.TimePicker</item>
+        <item name="android:datePickerStyle">@android:style/Widget.Material.DatePicker</item>
+    </style>
+
     <style name="PopupEmptyStyle" />
 
     <style name="TabWidgetCustomStyle" parent="android:Widget.TabWidget">
diff --git a/tests/tests/widget/src/android/widget/cts/DatePickerDialogTest.java b/tests/tests/widget/src/android/widget/cts/DatePickerDialogTest.java
index 1477f73..74d0ff5 100644
--- a/tests/tests/widget/src/android/widget/cts/DatePickerDialogTest.java
+++ b/tests/tests/widget/src/android/widget/cts/DatePickerDialogTest.java
@@ -22,6 +22,7 @@
 import android.content.Context;
 import android.test.ActivityInstrumentationTestCase2;
 import android.test.UiThreadTest;
+import android.widget.DatePicker;
 
 /**
  * Test {@link DatePickerDialog}.
@@ -48,7 +49,16 @@
 
         new DatePickerDialog(mActivity, AlertDialog.THEME_TRADITIONAL, null, 1970, 1, 1);
 
-        new DatePickerDialog(mActivity, AlertDialog.THEME_HOLO_DARK, null, 1970, 1, 1);
+        // Ensure the picker is shown using the Holo-style layout.
+        DatePickerDialog holoDialog = new DatePickerDialog(mActivity, AlertDialog.THEME_HOLO_DARK,
+                null, 1970, 1, 1);
+        assertEquals(DatePicker.MODE_SPINNER, holoDialog.getDatePicker().getMode());
+
+        // Ensure the picker is shown using the Material-style layout where available.
+        DatePickerDialog holoCalendarDialog = new DatePickerDialog(mActivity,
+                R.style.Theme_Holo_With_Material_Pickers, null, 1970, 1, 1);
+        final int expectedMode = mActivity.getResources().getInteger(R.integer.date_picker_mode);
+        assertEquals(expectedMode, holoCalendarDialog.getDatePicker().getMode());
 
         new DatePickerDialog(mActivity,
                 android.R.style.Theme_Material_Dialog_Alert, null, 1970, 1, 1);
diff --git a/tests/tests/widget/src/android/widget/cts/ListPopupWindowTest.java b/tests/tests/widget/src/android/widget/cts/ListPopupWindowTest.java
index b50f8c9..b882c0c 100644
--- a/tests/tests/widget/src/android/widget/cts/ListPopupWindowTest.java
+++ b/tests/tests/widget/src/android/widget/cts/ListPopupWindowTest.java
@@ -508,7 +508,10 @@
         final int[] lastChildOnScreenXY = new int[2];
         lastListChild.getLocationOnScreen(lastChildOnScreenXY);
 
-        assertTrue(lastChildOnScreenXY[1] + lastListChild.getHeight() <= promptViewOnScreenXY[1]);
+        // The child is above the prompt. They may overlap, as in the case
+        // when the list items do not all fit on screen, but this is still
+        // correct.
+        assertTrue(lastChildOnScreenXY[1] <= promptViewOnScreenXY[1]);
     }
 
     @Presubmit
diff --git a/tests/tests/widget/src/android/widget/cts/TimePickerDialogCtsActivity.java b/tests/tests/widget/src/android/widget/cts/TimePickerDialogCtsActivity.java
new file mode 100644
index 0000000..f5c544b
--- /dev/null
+++ b/tests/tests/widget/src/android/widget/cts/TimePickerDialogCtsActivity.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.widget.cts;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+/**
+ * A minimal application for TimePickerDialog test.
+ */
+public class TimePickerDialogCtsActivity extends Activity {
+    /**
+     * Called with the activity is first created.
+     */
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+    }
+}
diff --git a/tests/tests/widget/src/android/widget/cts/TimePickerDialogTest.java b/tests/tests/widget/src/android/widget/cts/TimePickerDialogTest.java
new file mode 100644
index 0000000..73ddc04
--- /dev/null
+++ b/tests/tests/widget/src/android/widget/cts/TimePickerDialogTest.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.widget.cts;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.DatePickerDialog;
+import android.app.TimePickerDialog;
+import android.test.ActivityInstrumentationTestCase2;
+import android.test.UiThreadTest;
+import android.widget.TimePicker;
+
+/**
+ * Test {@link TimePickerDialog}.
+ */
+public class TimePickerDialogTest extends
+        ActivityInstrumentationTestCase2<TimePickerDialogCtsActivity> {
+
+    private Activity mActivity;
+
+    public TimePickerDialogTest() {
+        super(TimePickerDialogCtsActivity.class);
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mActivity = getActivity();
+    }
+
+    @UiThreadTest
+    public void testConstructor() {
+        new TimePickerDialog(mActivity, null, 7, 0, true);
+
+        new TimePickerDialog(mActivity, AlertDialog.THEME_TRADITIONAL, null, 7, 0, true);
+
+        // Ensure the picker is shown using the Holo-style layout.
+        TimePickerDialog holoDialog = new TimePickerDialog(mActivity, AlertDialog.THEME_HOLO_DARK,
+                null, 7, 0, true);
+        assertEquals(TimePicker.MODE_SPINNER, holoDialog.getTimePicker().getMode());
+
+        // Ensure the picker is shown using the Material-style layout where available.
+        TimePickerDialog holoClockDialog = new TimePickerDialog(mActivity,
+                R.style.Theme_Holo_With_Material_Pickers, null, 7, 0, true);
+        final int expectedMode = mActivity.getResources().getInteger(R.integer.time_picker_mode);
+        assertEquals(expectedMode, holoClockDialog.getTimePicker().getMode());
+
+        new TimePickerDialog(mActivity,
+                android.R.style.Theme_Material_Dialog_Alert, null, 7, 0, true);
+
+        try {
+            new TimePickerDialog(null, null, 7, 0, true);
+            fail("should throw NullPointerException");
+        } catch (Exception e) {
+        }
+    }
+
+}
diff --git a/tools/cts-device-info/src/com/android/cts/deviceinfo/SampleDeviceInfo.java b/tools/cts-device-info/src/com/android/cts/deviceinfo/SampleDeviceInfo.java
index 8d3d7ada..67db187 100644
--- a/tools/cts-device-info/src/com/android/cts/deviceinfo/SampleDeviceInfo.java
+++ b/tools/cts-device-info/src/com/android/cts/deviceinfo/SampleDeviceInfo.java
@@ -33,6 +33,7 @@
         double[] doubles = {Double.MAX_VALUE, Double.MIN_VALUE};
         int[] ints = {Integer.MAX_VALUE, Integer.MIN_VALUE};
         long[] longs = {Long.MAX_VALUE, Long.MIN_VALUE};
+        float[] floats = {Float.MAX_VALUE, Float.MIN_VALUE, Float.NaN};
 
         // Group Foo
         store.startGroup("foo");
@@ -49,12 +50,16 @@
         store.addArrayResult("bar_double", doubles);
         store.addArrayResult("bar_int", ints);
         store.addArrayResult("bar_long", longs);
+        store.addArrayResult("bar_float", floats);
         store.endGroup(); // bar
 
         store.addResult("foo_double", Double.MAX_VALUE);
         store.addResult("foo_int", Integer.MAX_VALUE);
         store.addResult("foo_long", Long.MAX_VALUE);
         store.addResult("foo_string", "foo-string");
+        store.addResult("foo_float_nan", Float.NaN);
+        store.addResult("foo_float_max", Float.MAX_VALUE + 1);
+        store.addResult("foo_float_min", Float.MIN_VALUE - 1);
         store.endGroup(); // foo
     }
 }
diff --git a/tools/cts-tradefed/res/config/cts-known-failures.xml b/tools/cts-tradefed/res/config/cts-known-failures.xml
index e7b08cb..84528d5 100644
--- a/tools/cts-tradefed/res/config/cts-known-failures.xml
+++ b/tools/cts-tradefed/res/config/cts-known-failures.xml
@@ -207,6 +207,9 @@
     <option name="compatibility:exclude-filter" value="CtsUsageStatsTestCases android.app.usage.cts.UsageStatsTest#testPackageUsageStatsIntervals" />
     <option name="compatibility:exclude-filter" value="CtsUsageStatsTestCases android.app.usage.cts.UsageStatsTest#testUsageEventsParceling" />
 
+    <!-- b/31469490 -->
+    <option name="compatibility:exclude-filter" value="CtsViewTestCases android.view.cts.DragDropTest" />
+
     <!-- b/23238984 -->
     <option name="compatibility:exclude-filter" value="CtsVoiceSettingsTestCases android.voicesettings.cts.ZenModeTest#testAll" />