Merge "Check config in multi-display tests" into oc-dev
diff --git a/apps/CameraITS/tools/run_all_tests.py b/apps/CameraITS/tools/run_all_tests.py
index d20cb4a..b1c8177 100644
--- a/apps/CameraITS/tools/run_all_tests.py
+++ b/apps/CameraITS/tools/run_all_tests.py
@@ -23,6 +23,9 @@
 import its.device
 from its.device import ItsSession
 
+CHART_DELAY = 1  # seconds
+
+
 def main():
     """Run all the automated tests, saving intermediate files, and producing
     a summary/report of the results.
@@ -227,6 +230,8 @@
                         cmd = ['python',
                                os.path.join(os.getcwd(), 'tools/load_scene.py'),
                                scene_arg, screen_id_arg]
+                    else:
+                        time.sleep(CHART_DELAY)
                 else:
                     # Skip scene validation for scene 5 running in parallel
                     if not merge_result_switch or scene != 'scene5':
diff --git a/apps/CtsVerifier/AndroidManifest.xml b/apps/CtsVerifier/AndroidManifest.xml
index 3a96709..99a4ee3 100644
--- a/apps/CtsVerifier/AndroidManifest.xml
+++ b/apps/CtsVerifier/AndroidManifest.xml
@@ -1959,6 +1959,46 @@
                   android:label="@string/aware_data_path_open_passive_subscribe"
                   android:configChanges="keyboardHidden|orientation|screenSize" />
 
+        <activity android:name=".wifiaware.DataPathPassphraseUnsolicitedPublishTestActivity"
+                  android:label="@string/aware_data_path_passphrase_unsolicited_publish"
+                  android:configChanges="keyboardHidden|orientation|screenSize" />
+
+        <activity android:name=".wifiaware.DataPathPassphrasePassiveSubscribeTestActivity"
+                  android:label="@string/aware_data_path_passphrase_passive_subscribe"
+                  android:configChanges="keyboardHidden|orientation|screenSize" />
+
+        <activity android:name=".wifiaware.DataPathOpenSolicitedPublishTestActivity"
+                  android:label="@string/aware_data_path_open_solicited_publish"
+                  android:configChanges="keyboardHidden|orientation|screenSize" />
+
+        <activity android:name=".wifiaware.DataPathOpenActiveSubscribeTestActivity"
+                  android:label="@string/aware_data_path_open_active_subscribe"
+                  android:configChanges="keyboardHidden|orientation|screenSize" />
+
+        <activity android:name=".wifiaware.DataPathPassphraseSolicitedPublishTestActivity"
+                  android:label="@string/aware_data_path_passphrase_solicited_publish"
+                  android:configChanges="keyboardHidden|orientation|screenSize" />
+
+        <activity android:name=".wifiaware.DataPathPassphraseActiveSubscribeTestActivity"
+                  android:label="@string/aware_data_path_passphrase_active_subscribe"
+                  android:configChanges="keyboardHidden|orientation|screenSize" />
+
+        <activity android:name=".wifiaware.DataPathOobOpenResponderTestActivity"
+                  android:label="@string/aware_data_path_oob_open_responder"
+                  android:configChanges="keyboardHidden|orientation|screenSize" />
+
+        <activity android:name=".wifiaware.DataPathOobOpenInitiatorTestActivity"
+                  android:label="@string/aware_data_path_oob_open_initiator"
+                  android:configChanges="keyboardHidden|orientation|screenSize" />
+
+        <activity android:name=".wifiaware.DataPathOobPassphraseResponderTestActivity"
+                  android:label="@string/aware_data_path_oob_passphrase_responder"
+                  android:configChanges="keyboardHidden|orientation|screenSize" />
+
+        <activity android:name=".wifiaware.DataPathOobPassphraseInitiatorTestActivity"
+                  android:label="@string/aware_data_path_oob_passphrase_initiator"
+                  android:configChanges="keyboardHidden|orientation|screenSize" />
+
         <activity-alias android:name=".CtsVerifierActivity" android:label="@string/app_name"
                 android:targetActivity=".TestListActivity">
             <intent-filter>
diff --git a/apps/CtsVerifier/res/values/strings.xml b/apps/CtsVerifier/res/values/strings.xml
index 2d7c498..02cb70f 100755
--- a/apps/CtsVerifier/res/values/strings.xml
+++ b/apps/CtsVerifier/res/values/strings.xml
@@ -1392,13 +1392,71 @@
         Test failed.\n\nUnexpected error. Check logcat.</string>
 
     <string name="aware_dp_ib_open_unsolicited">Data Path: Open: Unsolicited/Passive</string>
+    <string name="aware_dp_ib_passphrase_unsolicited">Data Path: Passphrase: Unsolicited/Passive</string>
+    <string name="aware_dp_ib_open_solicited">Data Path: Open: Solicited/Active</string>
+    <string name="aware_dp_ib_passphrase_solicited">Data Path: Passphrase: Solicited/Active</string>
     <string name="aware_publish">Publish</string>
     <string name="aware_subscribe">Subscribe</string>
 
+    <string name="aware_dp_oob_open">Data Path (OOB): Open</string>
+    <string name="aware_dp_oob_passphrase">Data Path (OOB): Passphrase</string>
+    <string name="aware_responder">Responder</string>
+    <string name="aware_initiator">Initiator</string>
+
+    <string name="aware_status_attached">Attached ...</string>
+    <string name="aware_status_attach_fail">Attach failure!</string>
+    <string name="aware_status_attach_timeout">Attach failure - timed out!</string>
+    <string name="aware_status_identity">Discovery (Identity) MAC address: %1$s ...</string>
+    <string name="aware_status_identity_fail">Identity listener failure - timed out!</string>
+    <string name="aware_status_subscribe_started">Subscribe discovery session started ...</string>
+    <string name="aware_status_subscribe_failed">Subscribe failure!</string>
+    <string name="aware_status_subscribe_timeout">Subscribe failure - timed out!</string>
+    <string name="aware_status_subscribe_null_session">Subscribe failure - null session!</string>
+    <string name="aware_status_publish_started">Publish discovery session started ...</string>
+    <string name="aware_status_publish_failed">Publish failure!</string>
+    <string name="aware_status_publish_timeout">Publish failure - timed out!</string>
+    <string name="aware_status_publish_null_session">Publish failure - null session!</string>
+    <string name="aware_status_discovery">Service discovered ...</string>
+    <string name="aware_status_discovery_timeout">Service discovery failure - timed out!</string>
+    <string name="aware_status_discovery_fail">Service discovery failure - parameter mismatch!</string>
+    <string name="aware_status_send_success">Sent message successfully ...</string>
+    <string name="aware_status_send_failed">Send message failure!</string>
+    <string name="aware_status_send_timeout">Send message failure - timed out!</string>
+    <string name="aware_status_send_fail_parameter">Send message failure - mismatched ids!</string>
+    <string name="aware_status_received">Received message ...</string>
+    <string name="aware_status_received_mac">Received peer MAC address: %1$s ...</string>
+    <string name="aware_status_receive_timeout">Receive message failure - timed out!</string>
+    <string name="aware_status_receive_failure">Receive message failure - didn\'t receive expected message!</string>
+    <string name="aware_status_network_requested">Network requested ...</string>
+    <string name="aware_status_network_success">Network formed ...</string>
+    <string name="aware_status_network_failed">Network request failure - timed out!</string>
+    <string name="aware_status_sleeping_wait_for_responder">Pausing to let Responder time to set up ...</string>
+    <string name="aware_status_lifecycle_ok">Discovery lifecycle validated!</string>
+
     <string name="aware_data_path_open_unsolicited_publish">Data Path: Open: Unsolicited Publish</string>
     <string name="aware_data_path_open_unsolicited_publish_info">The publisher is now ready.\n\nOn the other device: start the \'Data Path: Open: Unsolicited/Passive\' / \'Subscribe\' test.</string>
     <string name="aware_data_path_open_passive_subscribe">Data Path: Open: Passive Subscribe</string>
 
+    <string name="aware_data_path_passphrase_unsolicited_publish">Data Path: Passphrase: Unsolicited Publish</string>
+    <string name="aware_data_path_passphrase_unsolicited_publish_info">The publisher is now ready.\n\nOn the other device: start the \'Data Path: Passphrase: Unsolicited/Passive\' / \'Subscribe\' test.</string>
+    <string name="aware_data_path_passphrase_passive_subscribe">Data Path: Passphrase: Passive Subscribe</string>
+
+    <string name="aware_data_path_open_solicited_publish">Data Path: Open: Solicited Publish</string>
+    <string name="aware_data_path_open_solicited_publish_info">The publisher is now ready.\n\nOn the other device: start the \'Data Path: Open: Solicited/Active\' / \'Subscribe\' test.</string>
+    <string name="aware_data_path_open_active_subscribe">Data Path: Open: Active Subscribe</string>
+
+    <string name="aware_data_path_passphrase_solicited_publish">Data Path: Passphrase: Solicited Publish</string>
+    <string name="aware_data_path_passphrase_solicited_publish_info">The publisher is now ready.\n\nOn the other device: start the \'Data Path: Passphrase: Solicited/Active\' / \'Subscribe\' test.</string>
+    <string name="aware_data_path_passphrase_active_subscribe">Data Path: Passphrase: Active Subscribe</string>
+
+    <string name="aware_data_path_oob_open_responder">Data Path (OOB): Open: Responder</string>
+    <string name="aware_data_path_oob_open_responder_info">The responder is now ready.\n\nOn the other device: start the \'Data Path (OOB): Open\' / \'Initiator\' test.</string>
+    <string name="aware_data_path_oob_open_initiator">Data Path (OOB): Open: Initiator</string>
+
+    <string name="aware_data_path_oob_passphrase_responder">Data Path (OOB): Passphrase: Responder</string>
+    <string name="aware_data_path_oob_passphrase_responder_info">The responder is now ready.\n\nOn the other device: start the \'Data Path (OOB): Passphrase\' / \'Initiator\' test.</string>
+    <string name="aware_data_path_oob_passphrase_initiator">Data Path (OOB): Passphrase: Initiator</string>
+
     <string name="camera_fov_calibration">Camera FOV Calibration</string>
     <string name="camera_fov_calibration_done">Done</string>
     <string name="camera_fov_general_settings">General settings</string>
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/wifiaware/BaseTestCase.java b/apps/CtsVerifier/src/com/android/cts/verifier/wifiaware/BaseTestCase.java
index 523487d..8556b19 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/wifiaware/BaseTestCase.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/wifiaware/BaseTestCase.java
@@ -17,6 +17,7 @@
 package com.android.cts.verifier.wifiaware;
 
 import android.content.Context;
+import android.content.res.Resources;
 import android.net.wifi.aware.WifiAwareManager;
 import android.os.Handler;
 import android.os.HandlerThread;
@@ -28,6 +29,7 @@
  */
 public abstract class BaseTestCase {
     protected Context mContext;
+    protected Resources mResources;
     protected Listener mListener;
 
     private Thread mThread;
@@ -38,6 +40,7 @@
 
     public BaseTestCase(Context context) {
         mContext = context;
+        mResources = mContext.getResources();
     }
 
     /**
@@ -152,4 +155,27 @@
          */
         void onTestFailed(String reason);
     }
+
+    /**
+     * Convert byte array to hex string representation utility.
+     */
+    public static String bytesToHex(byte[] bytes, Character separator) {
+        final char[] hexArray = "0123456789ABCDEF".toCharArray();
+        boolean useSeparator = separator != null;
+        char sep = 0;
+        if (useSeparator) {
+            sep = separator;
+        }
+        char[] hexChars = new char[bytes.length * 2 + (useSeparator ? bytes.length - 1 : 0)];
+        int base = 0;
+        for (int j = 0; j < bytes.length; j++) {
+            if (useSeparator && j != 0) {
+                hexChars[base++] = sep;
+            }
+            int v = bytes[j] & 0xFF;
+            hexChars[base++] = hexArray[v >> 4];
+            hexChars[base++] = hexArray[v & 0x0F];
+        }
+        return new String(hexChars);
+    }
 }
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/wifiaware/CallbackUtils.java b/apps/CtsVerifier/src/com/android/cts/verifier/wifiaware/CallbackUtils.java
new file mode 100644
index 0000000..4593ed4
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/wifiaware/CallbackUtils.java
@@ -0,0 +1,340 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.verifier.wifiaware;
+
+import android.net.ConnectivityManager;
+import android.net.Network;
+import android.net.wifi.aware.AttachCallback;
+import android.net.wifi.aware.DiscoverySessionCallback;
+import android.net.wifi.aware.IdentityChangedListener;
+import android.net.wifi.aware.PeerHandle;
+import android.net.wifi.aware.PublishDiscoverySession;
+import android.net.wifi.aware.SubscribeDiscoverySession;
+import android.net.wifi.aware.WifiAwareSession;
+import android.util.Log;
+import android.util.Pair;
+
+import java.util.ArrayDeque;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Blocking callbacks for Wi-Fi Aware and Connectivity Manager.
+ */
+public class CallbackUtils {
+    private static final String TAG = "CallbackUtils";
+
+    public static final int CALLBACK_TIMEOUT_SEC = 15;
+
+    /**
+     * Utility AttachCallback - provides mechanism to block execution with the
+     * waitForAttach method.
+     */
+    public static class AttachCb extends AttachCallback {
+        public static final int TIMEOUT = -1;
+        public static final int ON_ATTACHED = 0;
+        public static final int ON_ATTACH_FAILED = 1;
+
+        private CountDownLatch mBlocker = new CountDownLatch(1);
+        private int mCallback = TIMEOUT;
+        private WifiAwareSession mWifiAwareSession = null;
+
+        @Override
+        public void onAttached(WifiAwareSession session) {
+            mCallback = ON_ATTACHED;
+            mWifiAwareSession = session;
+            mBlocker.countDown();
+        }
+
+        @Override
+        public void onAttachFailed() {
+            mCallback = ON_ATTACH_FAILED;
+            mBlocker.countDown();
+        }
+
+        /**
+         * Wait (blocks) for any AttachCallback callback or timeout.
+         *
+         * @return A pair of values: the callback constant (or TIMEOUT) and the WifiAwareSession
+         * created when attach successful - null otherwise (attach failure or timeout).
+         */
+        public Pair<Integer, WifiAwareSession> waitForAttach() throws InterruptedException {
+            if (mBlocker.await(CALLBACK_TIMEOUT_SEC, TimeUnit.SECONDS)) {
+                return new Pair<>(mCallback, mWifiAwareSession);
+            }
+
+            return new Pair<>(TIMEOUT, null);
+        }
+    }
+
+    /**
+     * Utility IdentityChangedListener - provides mechanism to block execution with the
+     * waitForIdentity method. Single shot listener - only listens for the first triggered
+     * callback.
+     */
+    public static class IdentityListenerSingleShot extends IdentityChangedListener {
+        private CountDownLatch mBlocker = new CountDownLatch(1);
+        private byte[] mMac = null;
+
+        @Override
+        public void onIdentityChanged(byte[] mac) {
+            if (mMac != null) {
+                return;
+            }
+
+            mMac = mac;
+            mBlocker.countDown();
+        }
+
+        /**
+         * Wait (blocks) for the onIdentityChanged callback or a timeout.
+         *
+         * @return The MAC address returned by the onIdentityChanged() callback, or null on timeout.
+         */
+        public byte[] waitForMac() throws InterruptedException {
+            if (mBlocker.await(CALLBACK_TIMEOUT_SEC, TimeUnit.SECONDS)) {
+                return mMac;
+            }
+
+            return null;
+        }
+    }
+
+    /**
+     * Utility NetworkCallback - provides mechanism for blocking/serializing access with the
+     * waitForNetwork method.
+     */
+    public static class NetworkCb extends ConnectivityManager.NetworkCallback {
+        private CountDownLatch mBlocker = new CountDownLatch(1);
+        private boolean mNetworkAvailable = false;
+
+        @Override
+        public void onAvailable(Network network) {
+            mNetworkAvailable = true;
+            mBlocker.countDown();
+        }
+
+        @Override
+        public void onUnavailable() {
+            mNetworkAvailable = false;
+            mBlocker.countDown();
+        }
+
+        /**
+         * Wait (blocks) for Available or Unavailable callbacks - or timesout.
+         *
+         * @return true if Available, false otherwise (Unavailable or timeout).
+         */
+        public boolean waitForNetwork() throws InterruptedException {
+            if (mBlocker.await(CALLBACK_TIMEOUT_SEC, TimeUnit.SECONDS)) {
+                return mNetworkAvailable;
+            }
+            return false;
+        }
+    }
+
+    /**
+     * Utility DiscoverySessionCallback - provides mechanism to block/serialize Aware discovery
+     * operations using the waitForCallbacks() method.
+     */
+    public static class DiscoveryCb extends DiscoverySessionCallback {
+        public static final int TIMEOUT = -1;
+        public static final int ON_PUBLISH_STARTED = 0x1 << 0;
+        public static final int ON_SUBSCRIBE_STARTED = 0x1 << 1;
+        public static final int ON_SESSION_CONFIG_UPDATED = 0x1 << 2;
+        public static final int ON_SESSION_CONFIG_FAILED = 0x1 << 3;
+        public static final int ON_SESSION_TERMINATED = 0x1 << 4;
+        public static final int ON_SERVICE_DISCOVERED = 0x1 << 5;
+        public static final int ON_MESSAGE_SEND_SUCCEEDED = 0x1 << 6;
+        public static final int ON_MESSAGE_SEND_FAILED = 0x1 << 7;
+        public static final int ON_MESSAGE_RECEIVED = 0x1 << 8;
+
+        /**
+         * Data container for all parameters which can be returned by any DiscoverySessionCallback
+         * callback.
+         */
+        public static class CallbackData {
+            public CallbackData(int callback) {
+                this.callback = callback;
+            }
+
+            public int callback;
+
+            public PublishDiscoverySession publishDiscoverySession;
+            public SubscribeDiscoverySession subscribeDiscoverySession;
+            public PeerHandle peerHandle;
+            public byte[] serviceSpecificInfo;
+            public List<byte[]> matchFilter;
+            public int messageId;
+        }
+
+        private CountDownLatch mBlocker = null;
+        private int mWaitForCallbackMask = 0;
+
+        private final Object mLock = new Object();
+        private ArrayDeque<CallbackData> mCallbackQueue = new ArrayDeque<>();
+
+        private void processCallback(CallbackData callbackData) {
+            synchronized (mLock) {
+                mCallbackQueue.addLast(callbackData);
+                if (mBlocker != null && (mWaitForCallbackMask & callbackData.callback)
+                        == callbackData.callback) {
+                    mBlocker.countDown();
+                }
+            }
+        }
+
+        private CallbackData getAndRemoveFirst(int callbackMask) {
+            synchronized (mLock) {
+                for (CallbackData cbd : mCallbackQueue) {
+                    if ((cbd.callback & callbackMask) == cbd.callback) {
+                        mCallbackQueue.remove(cbd);
+                        return cbd;
+                    }
+                }
+            }
+
+            return null;
+        }
+
+        private CallbackData waitForCallbacks(int callbackMask, boolean timeout)
+                throws InterruptedException {
+            synchronized (mLock) {
+                CallbackData cbd = getAndRemoveFirst(callbackMask);
+                if (cbd != null) {
+                    return cbd;
+                }
+
+                mWaitForCallbackMask = callbackMask;
+                mBlocker = new CountDownLatch(1);
+            }
+
+            boolean finishedNormally = true;
+            if (timeout) {
+                finishedNormally = mBlocker.await(CALLBACK_TIMEOUT_SEC, TimeUnit.SECONDS);
+            } else {
+                mBlocker.await();
+            }
+            if (finishedNormally) {
+                CallbackData cbd = getAndRemoveFirst(callbackMask);
+                if (cbd != null) {
+                    return cbd;
+                }
+
+                Log.wtf(TAG, "DiscoveryCb.waitForCallback: callbackMask=" + callbackMask
+                        + ": did not time-out but doesn't have any of the requested callbacks in "
+                        + "the stack!?");
+                // falling-through to TIMEOUT
+            }
+
+            return new CallbackData(TIMEOUT);
+        }
+
+        /**
+         * Wait for the specified callbacks - a bitmask of any of the ON_* constants. Returns the
+         * CallbackData structure whose CallbackData.callback specifies the callback which was
+         * triggered. The callback may be TIMEOUT.
+         *
+         * Note: other callbacks happening while while waiting for the specified callback(s) will
+         * be queued.
+         */
+        public CallbackData waitForCallbacks(int callbackMask) throws InterruptedException {
+            return waitForCallbacks(callbackMask, true);
+        }
+
+        /**
+         * Wait for the specified callbacks - a bitmask of any of the ON_* constants. Returns the
+         * CallbackData structure whose CallbackData.callback specifies the callback which was
+         * triggered.
+         *
+         * This call will not timeout - it can be interrupted though (which results in a thrown
+         * exception).
+         *
+         * Note: other callbacks happening while while waiting for the specified callback(s) will
+         * be queued.
+         */
+        public CallbackData waitForCallbacksNoTimeout(int callbackMask)
+                throws InterruptedException {
+            return waitForCallbacks(callbackMask, false);
+        }
+
+        @Override
+        public void onPublishStarted(PublishDiscoverySession session) {
+            CallbackData callbackData = new CallbackData(ON_PUBLISH_STARTED);
+            callbackData.publishDiscoverySession = session;
+            processCallback(callbackData);
+        }
+
+        @Override
+        public void onSubscribeStarted(SubscribeDiscoverySession session) {
+            CallbackData callbackData = new CallbackData(ON_SUBSCRIBE_STARTED);
+            callbackData.subscribeDiscoverySession = session;
+            processCallback(callbackData);
+        }
+
+        @Override
+        public void onSessionConfigUpdated() {
+            CallbackData callbackData = new CallbackData(ON_SESSION_CONFIG_UPDATED);
+            processCallback(callbackData);
+        }
+
+        @Override
+        public void onSessionConfigFailed() {
+            CallbackData callbackData = new CallbackData(ON_SESSION_CONFIG_FAILED);
+            processCallback(callbackData);
+        }
+
+        @Override
+        public void onSessionTerminated() {
+            CallbackData callbackData = new CallbackData(ON_SESSION_TERMINATED);
+            processCallback(callbackData);
+        }
+
+        @Override
+        public void onServiceDiscovered(PeerHandle peerHandle, byte[] serviceSpecificInfo,
+                List<byte[]> matchFilter) {
+            CallbackData callbackData = new CallbackData(ON_SERVICE_DISCOVERED);
+            callbackData.peerHandle = peerHandle;
+            callbackData.serviceSpecificInfo = serviceSpecificInfo;
+            callbackData.matchFilter = matchFilter;
+            processCallback(callbackData);
+        }
+
+        @Override
+        public void onMessageSendSucceeded(int messageId) {
+            CallbackData callbackData = new CallbackData(ON_MESSAGE_SEND_SUCCEEDED);
+            callbackData.messageId = messageId;
+            processCallback(callbackData);
+        }
+
+        @Override
+        public void onMessageSendFailed(int messageId) {
+            CallbackData callbackData = new CallbackData(ON_MESSAGE_SEND_FAILED);
+            callbackData.messageId = messageId;
+            processCallback(callbackData);
+        }
+
+        @Override
+        public void onMessageReceived(PeerHandle peerHandle, byte[] message) {
+            CallbackData callbackData = new CallbackData(ON_MESSAGE_RECEIVED);
+            callbackData.peerHandle = peerHandle;
+            callbackData.serviceSpecificInfo = message;
+            processCallback(callbackData);
+        }
+    }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/wifiaware/DataPathOobOpenInitiatorTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/wifiaware/DataPathOobOpenInitiatorTestActivity.java
new file mode 100644
index 0000000..107b945
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/wifiaware/DataPathOobOpenInitiatorTestActivity.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.verifier.wifiaware;
+
+import android.content.Context;
+
+import com.android.cts.verifier.wifiaware.testcase.DataPathOutOfBandTestCase;
+
+/**
+ * Test activity for data-path, open, passive subscribe
+ */
+public class DataPathOobOpenInitiatorTestActivity extends BaseTestActivity {
+    @Override
+    protected BaseTestCase getTestCase(Context context) {
+        return new DataPathOutOfBandTestCase(context, true, false);
+    }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/wifiaware/DataPathOobOpenResponderTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/wifiaware/DataPathOobOpenResponderTestActivity.java
new file mode 100644
index 0000000..4080bd7
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/wifiaware/DataPathOobOpenResponderTestActivity.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.verifier.wifiaware;
+
+import android.content.Context;
+import android.os.Bundle;
+
+import com.android.cts.verifier.R;
+import com.android.cts.verifier.wifiaware.testcase.DataPathOutOfBandTestCase;
+
+/**
+ * Test activity for data-path, open, unsolicited publish
+ */
+public class DataPathOobOpenResponderTestActivity extends BaseTestActivity {
+    @Override
+    protected BaseTestCase getTestCase(Context context) {
+        return new DataPathOutOfBandTestCase(context, true, true);
+    }
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setInfoResources(R.string.aware_data_path_oob_open_responder,
+                R.string.aware_data_path_oob_open_responder_info, 0);
+    }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/wifiaware/DataPathOobPassphraseInitiatorTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/wifiaware/DataPathOobPassphraseInitiatorTestActivity.java
new file mode 100644
index 0000000..cfdec55
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/wifiaware/DataPathOobPassphraseInitiatorTestActivity.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.verifier.wifiaware;
+
+import android.content.Context;
+
+import com.android.cts.verifier.wifiaware.testcase.DataPathOutOfBandTestCase;
+
+/**
+ * Test activity for data-path, open, passive subscribe
+ */
+public class DataPathOobPassphraseInitiatorTestActivity extends BaseTestActivity {
+    @Override
+    protected BaseTestCase getTestCase(Context context) {
+        return new DataPathOutOfBandTestCase(context, false, false);
+    }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/wifiaware/DataPathOobPassphraseResponderTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/wifiaware/DataPathOobPassphraseResponderTestActivity.java
new file mode 100644
index 0000000..b97e74e
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/wifiaware/DataPathOobPassphraseResponderTestActivity.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.verifier.wifiaware;
+
+import android.content.Context;
+import android.os.Bundle;
+
+import com.android.cts.verifier.R;
+import com.android.cts.verifier.wifiaware.testcase.DataPathOutOfBandTestCase;
+
+/**
+ * Test activity for data-path, open, unsolicited publish
+ */
+public class DataPathOobPassphraseResponderTestActivity extends BaseTestActivity {
+    @Override
+    protected BaseTestCase getTestCase(Context context) {
+        return new DataPathOutOfBandTestCase(context, false, true);
+    }
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setInfoResources(R.string.aware_data_path_oob_passphrase_responder,
+                R.string.aware_data_path_oob_passphrase_responder_info, 0);
+    }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/wifiaware/DataPathOpenActiveSubscribeTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/wifiaware/DataPathOpenActiveSubscribeTestActivity.java
new file mode 100644
index 0000000..f6afecc
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/wifiaware/DataPathOpenActiveSubscribeTestActivity.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.verifier.wifiaware;
+
+import android.content.Context;
+
+import com.android.cts.verifier.wifiaware.testcase.DataPathInBandTestCase;
+
+/**
+ * Test activity for data-path, open, passive subscribe
+ */
+public class DataPathOpenActiveSubscribeTestActivity extends BaseTestActivity {
+    @Override
+    protected BaseTestCase getTestCase(Context context) {
+        return new DataPathInBandTestCase(context, true, false, false);
+    }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/wifiaware/DataPathOpenSolicitedPublishTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/wifiaware/DataPathOpenSolicitedPublishTestActivity.java
new file mode 100644
index 0000000..154dcfe
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/wifiaware/DataPathOpenSolicitedPublishTestActivity.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.verifier.wifiaware;
+
+import android.content.Context;
+import android.os.Bundle;
+
+import com.android.cts.verifier.R;
+import com.android.cts.verifier.wifiaware.testcase.DataPathInBandTestCase;
+
+/**
+ * Test activity for data-path, open, unsolicited publish
+ */
+public class DataPathOpenSolicitedPublishTestActivity extends BaseTestActivity {
+    @Override
+    protected BaseTestCase getTestCase(Context context) {
+        return new DataPathInBandTestCase(context, true, true, false);
+    }
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setInfoResources(R.string.aware_data_path_open_solicited_publish,
+                R.string.aware_data_path_open_solicited_publish_info, 0);
+    }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/wifiaware/DataPathPassphraseActiveSubscribeTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/wifiaware/DataPathPassphraseActiveSubscribeTestActivity.java
new file mode 100644
index 0000000..c8ef3ea
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/wifiaware/DataPathPassphraseActiveSubscribeTestActivity.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.verifier.wifiaware;
+
+import android.content.Context;
+
+import com.android.cts.verifier.wifiaware.testcase.DataPathInBandTestCase;
+
+/**
+ * Test activity for data-path, open, passive subscribe
+ */
+public class DataPathPassphraseActiveSubscribeTestActivity extends BaseTestActivity {
+    @Override
+    protected BaseTestCase getTestCase(Context context) {
+        return new DataPathInBandTestCase(context, false, false, false);
+    }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/wifiaware/DataPathPassphrasePassiveSubscribeTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/wifiaware/DataPathPassphrasePassiveSubscribeTestActivity.java
new file mode 100644
index 0000000..ff40e03
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/wifiaware/DataPathPassphrasePassiveSubscribeTestActivity.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.verifier.wifiaware;
+
+import android.content.Context;
+
+import com.android.cts.verifier.wifiaware.testcase.DataPathInBandTestCase;
+
+/**
+ * Test activity for data-path, open, passive subscribe
+ */
+public class DataPathPassphrasePassiveSubscribeTestActivity extends BaseTestActivity {
+    @Override
+    protected BaseTestCase getTestCase(Context context) {
+        return new DataPathInBandTestCase(context, false, false, true);
+    }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/wifiaware/DataPathPassphraseSolicitedPublishTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/wifiaware/DataPathPassphraseSolicitedPublishTestActivity.java
new file mode 100644
index 0000000..dcde6ff
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/wifiaware/DataPathPassphraseSolicitedPublishTestActivity.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.verifier.wifiaware;
+
+import android.content.Context;
+import android.os.Bundle;
+
+import com.android.cts.verifier.R;
+import com.android.cts.verifier.wifiaware.testcase.DataPathInBandTestCase;
+
+/**
+ * Test activity for data-path, open, unsolicited publish
+ */
+public class DataPathPassphraseSolicitedPublishTestActivity extends BaseTestActivity {
+    @Override
+    protected BaseTestCase getTestCase(Context context) {
+        return new DataPathInBandTestCase(context, false, true, false);
+    }
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setInfoResources(R.string.aware_data_path_passphrase_solicited_publish,
+                R.string.aware_data_path_passphrase_solicited_publish_info, 0);
+    }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/wifiaware/DataPathPassphraseUnsolicitedPublishTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/wifiaware/DataPathPassphraseUnsolicitedPublishTestActivity.java
new file mode 100644
index 0000000..7a25bb4
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/wifiaware/DataPathPassphraseUnsolicitedPublishTestActivity.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.verifier.wifiaware;
+
+import android.content.Context;
+import android.os.Bundle;
+
+import com.android.cts.verifier.R;
+import com.android.cts.verifier.wifiaware.testcase.DataPathInBandTestCase;
+
+/**
+ * Test activity for data-path, open, unsolicited publish
+ */
+public class DataPathPassphraseUnsolicitedPublishTestActivity extends BaseTestActivity {
+    @Override
+    protected BaseTestCase getTestCase(Context context) {
+        return new DataPathInBandTestCase(context, false, true, true);
+    }
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setInfoResources(R.string.aware_data_path_passphrase_unsolicited_publish,
+                R.string.aware_data_path_passphrase_unsolicited_publish_info, 0);
+    }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/wifiaware/TestListActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/wifiaware/TestListActivity.java
index f591ff4..c2c7636 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/wifiaware/TestListActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/wifiaware/TestListActivity.java
@@ -70,6 +70,56 @@
                 R.string.aware_subscribe,
                 DataPathOpenPassiveSubscribeTestActivity.class.getName(),
                 new Intent(this, DataPathOpenPassiveSubscribeTestActivity.class), null));
+        adapter.add(TestListAdapter.TestListItem.newCategory(this,
+                R.string.aware_dp_ib_passphrase_unsolicited));
+        adapter.add(TestListAdapter.TestListItem.newTest(this,
+                R.string.aware_publish,
+                DataPathPassphraseUnsolicitedPublishTestActivity.class.getName(),
+                new Intent(this, DataPathPassphraseUnsolicitedPublishTestActivity.class), null));
+        adapter.add(TestListAdapter.TestListItem.newTest(this,
+                R.string.aware_subscribe,
+                DataPathPassphrasePassiveSubscribeTestActivity.class.getName(),
+                new Intent(this, DataPathPassphrasePassiveSubscribeTestActivity.class), null));
+        adapter.add(TestListAdapter.TestListItem.newCategory(this,
+                R.string.aware_dp_ib_open_solicited));
+        adapter.add(TestListAdapter.TestListItem.newTest(this,
+                R.string.aware_publish,
+                DataPathOpenSolicitedPublishTestActivity.class.getName(),
+                new Intent(this, DataPathOpenSolicitedPublishTestActivity.class), null));
+        adapter.add(TestListAdapter.TestListItem.newTest(this,
+                R.string.aware_subscribe,
+                DataPathOpenActiveSubscribeTestActivity.class.getName(),
+                new Intent(this, DataPathOpenActiveSubscribeTestActivity.class), null));
+        adapter.add(TestListAdapter.TestListItem.newCategory(this,
+                R.string.aware_dp_ib_passphrase_solicited));
+        adapter.add(TestListAdapter.TestListItem.newTest(this,
+                R.string.aware_publish,
+                DataPathPassphraseSolicitedPublishTestActivity.class.getName(),
+                new Intent(this, DataPathPassphraseSolicitedPublishTestActivity.class), null));
+        adapter.add(TestListAdapter.TestListItem.newTest(this,
+                R.string.aware_subscribe,
+                DataPathPassphraseActiveSubscribeTestActivity.class.getName(),
+                new Intent(this, DataPathPassphraseActiveSubscribeTestActivity.class), null));
+        adapter.add(TestListAdapter.TestListItem.newCategory(this,
+                R.string.aware_dp_oob_open));
+        adapter.add(TestListAdapter.TestListItem.newTest(this,
+                R.string.aware_responder,
+                DataPathOobOpenResponderTestActivity.class.getName(),
+                new Intent(this, DataPathOobOpenResponderTestActivity.class), null));
+        adapter.add(TestListAdapter.TestListItem.newTest(this,
+                R.string.aware_initiator,
+                DataPathOobOpenInitiatorTestActivity.class.getName(),
+                new Intent(this, DataPathOobOpenInitiatorTestActivity.class), null));
+        adapter.add(TestListAdapter.TestListItem.newCategory(this,
+                R.string.aware_dp_oob_passphrase));
+        adapter.add(TestListAdapter.TestListItem.newTest(this,
+                R.string.aware_responder,
+                DataPathOobPassphraseResponderTestActivity.class.getName(),
+                new Intent(this, DataPathOobPassphraseResponderTestActivity.class), null));
+        adapter.add(TestListAdapter.TestListItem.newTest(this,
+                R.string.aware_initiator,
+                DataPathOobPassphraseInitiatorTestActivity.class.getName(),
+                new Intent(this, DataPathOobPassphraseInitiatorTestActivity.class), null));
 
         adapter.registerDataSetObserver(new DataSetObserver() {
             @Override
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/wifiaware/testcase/DataPathInBandTestCase.java b/apps/CtsVerifier/src/com/android/cts/verifier/wifiaware/testcase/DataPathInBandTestCase.java
index 9d58bc2..7ecdc30 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/wifiaware/testcase/DataPathInBandTestCase.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/wifiaware/testcase/DataPathInBandTestCase.java
@@ -16,18 +16,71 @@
 
 package com.android.cts.verifier.wifiaware.testcase;
 
-import android.content.Context;
+import static com.android.cts.verifier.wifiaware.CallbackUtils.CALLBACK_TIMEOUT_SEC;
 
+import android.content.Context;
+import android.net.ConnectivityManager;
+import android.net.NetworkCapabilities;
+import android.net.NetworkRequest;
+import android.net.wifi.aware.PeerHandle;
+import android.net.wifi.aware.PublishConfig;
+import android.net.wifi.aware.PublishDiscoverySession;
+import android.net.wifi.aware.SubscribeConfig;
+import android.net.wifi.aware.SubscribeDiscoverySession;
+import android.net.wifi.aware.WifiAwareSession;
+import android.util.Log;
+import android.util.Pair;
+
+import com.android.cts.verifier.R;
 import com.android.cts.verifier.wifiaware.BaseTestCase;
+import com.android.cts.verifier.wifiaware.CallbackUtils;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
 
 /**
  * Test case for data-path, in-band test cases:
  * open/passphrase * solicited/unsolicited * publish/subscribe.
+ *
+ * Subscribe test sequence:
+ * 1. Attach
+ *    wait for results (session)
+ * 2. Subscribe
+ *    wait for results (subscribe session)
+ * 3. Wait for discovery
+ * 4. Send message
+ *    Wait for success
+ * 5. Wait for rx message
+ * 6. Request network
+ *    Wait for network
+ * 7. Destroy session
+ *
+ * Publish test sequence:
+ * 1. Attach
+ *    wait for results (session)
+ * 2. Publish
+ *    wait for results (publish session)
+ * 3. Wait for rx message
+ * 4. Request network
+ * 5. Send message
+ *    Wait for success
+ * 6. Wait for network
+ * 7. Destroy session
  */
 public class DataPathInBandTestCase extends BaseTestCase {
     private static final String TAG = "DataPathInBandTestCase";
     private static final boolean DBG = true;
 
+    private static final String SERVICE_NAME = "CtsVerifierTestService";
+    private static final byte[] MATCH_FILTER_BYTES = "bytes used for matching".getBytes();
+    private static final byte[] PUB_SSI = "Extra bytes in the publisher discovery".getBytes();
+    private static final byte[] SUB_SSI = "Arbitrary bytes for the subscribe discovery".getBytes();
+    private static final byte[] MSG_SUB_TO_PUB = "Let's talk".getBytes();
+    private static final byte[] MSG_PUB_TO_SUB = "Ready".getBytes();
+    private static final String PASSPHRASE = "Some super secret password";
+    private static final int MESSAGE_ID = 1234;
+
     private boolean mIsSecurityOpen;
     private boolean mIsPublish;
     private boolean mIsUnsolicited;
@@ -35,6 +88,7 @@
     private final Object mLock = new Object();
 
     private String mFailureReason;
+    private WifiAwareSession mWifiAwareSession;
 
     public DataPathInBandTestCase(Context context, boolean isSecurityOpen, boolean isPublish,
             boolean isUnsolicited) {
@@ -46,7 +100,42 @@
 
     @Override
     protected boolean executeTest() throws InterruptedException {
-        return false;
+        if (DBG) {
+            Log.d(TAG,
+                    "executeTest: mIsSecurityOpen=" + mIsSecurityOpen + ", mIsPublish=" + mIsPublish
+                            + ", mIsUnsolicited=" + mIsUnsolicited);
+        }
+
+        // 1. attach
+        CallbackUtils.AttachCb attachCb = new CallbackUtils.AttachCb();
+        mWifiAwareManager.attach(attachCb, mHandler);
+        Pair<Integer, WifiAwareSession> results = attachCb.waitForAttach();
+        switch (results.first) {
+            case CallbackUtils.AttachCb.TIMEOUT:
+                setFailureReason(mContext.getString(R.string.aware_status_attach_timeout));
+                Log.e(TAG, "executeTest: attach TIMEOUT");
+                return false;
+            case CallbackUtils.AttachCb.ON_ATTACH_FAILED:
+                setFailureReason(mContext.getString(R.string.aware_status_attach_fail));
+                Log.e(TAG, "executeTest: attach ON_ATTACH_FAILED");
+                return false;
+        }
+        mWifiAwareSession = results.second;
+        if (mWifiAwareSession == null) {
+            setFailureReason(mContext.getString(R.string.aware_status_attach_fail));
+            Log.e(TAG, "executeTest: attach callback succeeded but null session returned!?");
+            return false;
+        }
+        mListener.onTestMsgReceived(mContext.getString(R.string.aware_status_attached));
+        if (DBG) {
+            Log.d(TAG, "executeTest: attach succeeded");
+        }
+
+        if (mIsPublish) {
+            return executeTestPublisher();
+        } else {
+            return executeTestSubscriber();
+        }
     }
 
     private void setFailureReason(String reason) {
@@ -61,4 +150,282 @@
             return mFailureReason;
         }
     }
-}
\ No newline at end of file
+
+    @Override
+    protected void tearDown() {
+        if (mWifiAwareSession != null) {
+            mWifiAwareSession.destroy();
+            mWifiAwareSession = null;
+        }
+        super.tearDown();
+    }
+
+    private boolean executeTestSubscriber() throws InterruptedException {
+        if (DBG) Log.d(TAG, "executeTestSubscriber");
+        CallbackUtils.DiscoveryCb discoveryCb = new CallbackUtils.DiscoveryCb();
+
+        // 2. subscribe
+        List<byte[]> matchFilter = new ArrayList<>();
+        matchFilter.add(MATCH_FILTER_BYTES);
+        SubscribeConfig subscribeConfig = new SubscribeConfig.Builder().setServiceName(
+                SERVICE_NAME).setServiceSpecificInfo(SUB_SSI).setMatchFilter(
+                matchFilter).setSubscribeType(
+                mIsUnsolicited ? SubscribeConfig.SUBSCRIBE_TYPE_PASSIVE
+                        : SubscribeConfig.SUBSCRIBE_TYPE_ACTIVE).setMatchStyle(
+                SubscribeConfig.MATCH_STYLE_FIRST_ONLY).setTerminateNotificationEnabled(
+                true).build();
+        if (DBG) Log.d(TAG, "executeTestSubscriber: subscribeConfig=" + subscribeConfig);
+        mWifiAwareSession.subscribe(subscribeConfig, discoveryCb, mHandler);
+
+        //    wait for results - subscribe session
+        CallbackUtils.DiscoveryCb.CallbackData callbackData = discoveryCb.waitForCallbacks(
+                CallbackUtils.DiscoveryCb.ON_SUBSCRIBE_STARTED
+                        | CallbackUtils.DiscoveryCb.ON_SESSION_CONFIG_FAILED);
+        switch (callbackData.callback) {
+            case CallbackUtils.DiscoveryCb.TIMEOUT:
+                setFailureReason(mContext.getString(R.string.aware_status_subscribe_timeout));
+                Log.e(TAG, "executeTestSubscriber: subscribe TIMEOUT");
+                return false;
+            case CallbackUtils.DiscoveryCb.ON_SESSION_CONFIG_FAILED:
+                setFailureReason(mContext.getString(R.string.aware_status_subscribe_failed));
+                Log.e(TAG, "executeTestSubscriber: subscribe ON_SESSION_CONFIG_FAILED");
+                return false;
+        }
+        SubscribeDiscoverySession discoverySession = callbackData.subscribeDiscoverySession;
+        if (discoverySession == null) {
+            setFailureReason(mContext.getString(R.string.aware_status_subscribe_null_session));
+            Log.e(TAG, "executeTestSubscriber: subscribe succeeded but null session returned");
+            return false;
+        }
+        mListener.onTestMsgReceived(mContext.getString(R.string.aware_status_subscribe_started));
+        if (DBG) Log.d(TAG, "executeTestSubscriber: subscribe succeeded");
+
+        // 3. wait for discovery
+        callbackData = discoveryCb.waitForCallbacks(
+                CallbackUtils.DiscoveryCb.ON_SERVICE_DISCOVERED);
+        switch (callbackData.callback) {
+            case CallbackUtils.DiscoveryCb.TIMEOUT:
+                setFailureReason(mContext.getString(R.string.aware_status_discovery_timeout));
+                Log.e(TAG, "executeTestSubscriber: waiting for discovery TIMEOUT");
+                return false;
+        }
+        PeerHandle peerHandle = callbackData.peerHandle;
+        mListener.onTestMsgReceived(mContext.getString(R.string.aware_status_discovery));
+        if (DBG) Log.d(TAG, "executeTestSubscriber: discovery");
+
+        //    validate discovery parameters match
+        if (!Arrays.equals(PUB_SSI, callbackData.serviceSpecificInfo)) {
+            setFailureReason(mContext.getString(R.string.aware_status_discovery_fail));
+            Log.e(TAG, "executeTestSubscriber: discovery but SSI mismatch: rx='" + new String(
+                    callbackData.serviceSpecificInfo) + "'");
+            return false;
+        }
+        if (callbackData.matchFilter.size() != 1 || !Arrays.equals(MATCH_FILTER_BYTES,
+                callbackData.matchFilter.get(0))) {
+            setFailureReason(mContext.getString(R.string.aware_status_discovery_fail));
+            StringBuffer sb = new StringBuffer();
+            sb.append("size=").append(callbackData.matchFilter.size());
+            for (byte[] mf: callbackData.matchFilter) {
+                sb.append(", e='").append(new String(mf)).append("'");
+            }
+            Log.e(TAG, "executeTestSubscriber: discovery but matchFilter mismatch: "
+                    + sb.toString());
+            return false;
+        }
+        if (peerHandle == null) {
+            setFailureReason(mContext.getString(R.string.aware_status_discovery_fail));
+            Log.e(TAG, "executeTestSubscriber: discovery but null peerHandle");
+            return false;
+        }
+
+        // 4. send message & wait for send status
+        discoverySession.sendMessage(peerHandle, MESSAGE_ID, MSG_SUB_TO_PUB);
+        callbackData = discoveryCb.waitForCallbacks(
+                CallbackUtils.DiscoveryCb.ON_MESSAGE_SEND_SUCCEEDED
+                        | CallbackUtils.DiscoveryCb.ON_MESSAGE_SEND_FAILED);
+        switch (callbackData.callback) {
+            case CallbackUtils.DiscoveryCb.TIMEOUT:
+                setFailureReason(mContext.getString(R.string.aware_status_send_timeout));
+                Log.e(TAG, "executeTestSubscriber: send message TIMEOUT");
+                return false;
+            case CallbackUtils.DiscoveryCb.ON_MESSAGE_SEND_FAILED:
+                setFailureReason(mContext.getString(R.string.aware_status_send_failed));
+                Log.e(TAG, "executeTestSubscriber: send message ON_MESSAGE_SEND_FAILED");
+                return false;
+        }
+        mListener.onTestMsgReceived(mContext.getString(R.string.aware_status_send_success));
+        if (DBG) Log.d(TAG, "executeTestSubscriber: send message succeeded");
+
+        if (callbackData.messageId != MESSAGE_ID) {
+            setFailureReason(mContext.getString(R.string.aware_status_send_fail_parameter));
+            Log.e(TAG, "executeTestSubscriber: send message message ID mismatch: "
+                    + callbackData.messageId);
+            return false;
+        }
+
+        // 5. wait to receive message
+        callbackData = discoveryCb.waitForCallbacks(CallbackUtils.DiscoveryCb.ON_MESSAGE_RECEIVED);
+        switch (callbackData.callback) {
+            case CallbackUtils.DiscoveryCb.TIMEOUT:
+                setFailureReason(mContext.getString(R.string.aware_status_receive_timeout));
+                Log.e(TAG, "executeTestSubscriber: receive message TIMEOUT");
+                return false;
+        }
+        mListener.onTestMsgReceived(mContext.getString(R.string.aware_status_received));
+        if (DBG) Log.d(TAG, "executeTestSubscriber: received message");
+
+        //    validate that received the expected message
+        if (!Arrays.equals(MSG_PUB_TO_SUB, callbackData.serviceSpecificInfo)) {
+            setFailureReason(mContext.getString(R.string.aware_status_receive_failure));
+            Log.e(TAG, "executeTestSubscriber: receive message message content mismatch: rx='"
+                    + new String(callbackData.serviceSpecificInfo) + "'");
+            return false;
+        }
+
+        // 6. request network
+        ConnectivityManager cm = (ConnectivityManager) mContext.getSystemService(
+                Context.CONNECTIVITY_SERVICE);
+        String networkSpecifier = mIsSecurityOpen ? discoverySession.createNetworkSpecifierOpen(
+                peerHandle) : discoverySession.createNetworkSpecifierPassphrase(peerHandle,
+                PASSPHRASE);
+        NetworkRequest nr = new NetworkRequest.Builder().addTransportType(
+                NetworkCapabilities.TRANSPORT_WIFI_AWARE).setNetworkSpecifier(
+                networkSpecifier).build();
+        CallbackUtils.NetworkCb networkCb = new CallbackUtils.NetworkCb();
+        cm.requestNetwork(nr, CALLBACK_TIMEOUT_SEC * 1000, networkCb);
+        mListener.onTestMsgReceived(mContext.getString(R.string.aware_status_network_requested));
+        if (DBG) Log.d(TAG, "executeTestSubscriber: requested network");
+        boolean networkAvailable = networkCb.waitForNetwork();
+        cm.unregisterNetworkCallback(networkCb);
+        if (!networkAvailable) {
+            setFailureReason(mContext.getString(R.string.aware_status_network_failed));
+            Log.e(TAG, "executeTestSubscriber: network request rejected - ON_UNAVAILABLE");
+            return false;
+        }
+        mListener.onTestMsgReceived(mContext.getString(R.string.aware_status_network_success));
+        if (DBG) Log.d(TAG, "executeTestSubscriber: network request granted - AVAILABLE");
+
+        // 7. destroy session
+        discoverySession.destroy();
+
+        mListener.onTestMsgReceived(mContext.getString(R.string.aware_status_lifecycle_ok));
+        return true;
+    }
+
+    private boolean executeTestPublisher() throws InterruptedException {
+        if (DBG) Log.d(TAG, "executeTestPublisher");
+        CallbackUtils.DiscoveryCb discoveryCb = new CallbackUtils.DiscoveryCb();
+
+        // 2. publish
+        List<byte[]> matchFilter = new ArrayList<>();
+        matchFilter.add(MATCH_FILTER_BYTES);
+        PublishConfig publishConfig = new PublishConfig.Builder().setServiceName(
+                SERVICE_NAME).setServiceSpecificInfo(PUB_SSI).setMatchFilter(
+                matchFilter).setPublishType(mIsUnsolicited ? PublishConfig.PUBLISH_TYPE_UNSOLICITED
+                : PublishConfig.PUBLISH_TYPE_SOLICITED).setTerminateNotificationEnabled(
+                true).build();
+        if (DBG) Log.d(TAG, "executeTestPublisher: publishConfig=" + publishConfig);
+        mWifiAwareSession.publish(publishConfig, discoveryCb, mHandler);
+
+        //    wait for results - publish session
+        CallbackUtils.DiscoveryCb.CallbackData callbackData = discoveryCb.waitForCallbacks(
+                CallbackUtils.DiscoveryCb.ON_PUBLISH_STARTED
+                        | CallbackUtils.DiscoveryCb.ON_SESSION_CONFIG_FAILED);
+        switch (callbackData.callback) {
+            case CallbackUtils.DiscoveryCb.TIMEOUT:
+                setFailureReason(mContext.getString(R.string.aware_status_publish_timeout));
+                Log.e(TAG, "executeTestPublisher: publish TIMEOUT");
+                return false;
+            case CallbackUtils.DiscoveryCb.ON_SESSION_CONFIG_FAILED:
+                setFailureReason(mContext.getString(R.string.aware_status_publish_failed));
+                Log.e(TAG, "executeTestPublisher: publish ON_SESSION_CONFIG_FAILED");
+                return false;
+        }
+        PublishDiscoverySession discoverySession = callbackData.publishDiscoverySession;
+        if (discoverySession == null) {
+            setFailureReason(mContext.getString(R.string.aware_status_publish_null_session));
+            Log.e(TAG, "executeTestPublisher: publish succeeded but null session returned");
+            return false;
+        }
+        mListener.onTestMsgReceived(mContext.getString(R.string.aware_status_publish_started));
+        if (DBG) Log.d(TAG, "executeTestPublisher: publish succeeded");
+
+        // 3. wait to receive message: no timeout since this depends on (human) operator starting
+        //    the test on the subscriber device.
+        callbackData = discoveryCb.waitForCallbacksNoTimeout(
+                CallbackUtils.DiscoveryCb.ON_MESSAGE_RECEIVED);
+        PeerHandle peerHandle = callbackData.peerHandle;
+        mListener.onTestMsgReceived(mContext.getString(R.string.aware_status_received));
+        if (DBG) Log.d(TAG, "executeTestPublisher: received message");
+
+        //    validate that received the expected message
+        if (!Arrays.equals(MSG_SUB_TO_PUB, callbackData.serviceSpecificInfo)) {
+            setFailureReason(mContext.getString(R.string.aware_status_receive_failure));
+            Log.e(TAG, "executeTestPublisher: receive message message content mismatch: rx='"
+                    + new String(callbackData.serviceSpecificInfo) + "'");
+            return false;
+        }
+        if (peerHandle == null) {
+            setFailureReason(mContext.getString(R.string.aware_status_receive_failure));
+            Log.e(TAG, "executeTestPublisher: received message but peerHandle is null!?");
+            return false;
+        }
+
+        // 4. Request network
+        ConnectivityManager cm = (ConnectivityManager) mContext.getSystemService(
+                Context.CONNECTIVITY_SERVICE);
+        String networkSpecifier = mIsSecurityOpen ? discoverySession.createNetworkSpecifierOpen(
+                peerHandle) : discoverySession.createNetworkSpecifierPassphrase(peerHandle,
+                PASSPHRASE);
+        NetworkRequest nr = new NetworkRequest.Builder().addTransportType(
+                NetworkCapabilities.TRANSPORT_WIFI_AWARE).setNetworkSpecifier(
+                networkSpecifier).build();
+        CallbackUtils.NetworkCb networkCb = new CallbackUtils.NetworkCb();
+        cm.requestNetwork(nr, CALLBACK_TIMEOUT_SEC * 1000, networkCb);
+        mListener.onTestMsgReceived(mContext.getString(R.string.aware_status_network_requested));
+        if (DBG) Log.d(TAG, "executeTestPublisher: requested network");
+
+        // 5. send message & wait for send status
+        discoverySession.sendMessage(peerHandle, MESSAGE_ID, MSG_PUB_TO_SUB);
+        callbackData = discoveryCb.waitForCallbacks(
+                CallbackUtils.DiscoveryCb.ON_MESSAGE_SEND_SUCCEEDED
+                        | CallbackUtils.DiscoveryCb.ON_MESSAGE_SEND_FAILED);
+        switch (callbackData.callback) {
+            case CallbackUtils.DiscoveryCb.TIMEOUT:
+                setFailureReason(mContext.getString(R.string.aware_status_send_timeout));
+                Log.e(TAG, "executeTestPublisher: send message TIMEOUT");
+                return false;
+            case CallbackUtils.DiscoveryCb.ON_MESSAGE_SEND_FAILED:
+                setFailureReason(mContext.getString(R.string.aware_status_send_failed));
+                Log.e(TAG, "executeTestPublisher: send message ON_MESSAGE_SEND_FAILED");
+                return false;
+        }
+        mListener.onTestMsgReceived(mContext.getString(R.string.aware_status_send_success));
+        if (DBG) Log.d(TAG, "executeTestPublisher: send message succeeded");
+
+        if (callbackData.messageId != MESSAGE_ID) {
+            setFailureReason(mContext.getString(R.string.aware_status_send_fail_parameter));
+            Log.e(TAG, "executeTestPublisher: send message succeeded but message ID mismatch : "
+                    + callbackData.messageId);
+            return false;
+        }
+
+        // 6. wait for network
+        boolean networkAvailable = networkCb.waitForNetwork();
+        cm.unregisterNetworkCallback(networkCb);
+        if (!networkAvailable) {
+            setFailureReason(mContext.getString(R.string.aware_status_network_failed));
+            Log.e(TAG, "executeTestPublisher: request network rejected - ON_UNAVAILABLE");
+            return false;
+        }
+        mListener.onTestMsgReceived(mContext.getString(R.string.aware_status_network_success));
+        if (DBG) Log.d(TAG, "executeTestPublisher: network request granted - AVAILABLE");
+
+        // 7. destroy session
+        discoverySession.destroy();
+
+        mListener.onTestMsgReceived(mContext.getString(R.string.aware_status_lifecycle_ok));
+        return true;
+
+    }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/wifiaware/testcase/DataPathOutOfBandTestCase.java b/apps/CtsVerifier/src/com/android/cts/verifier/wifiaware/testcase/DataPathOutOfBandTestCase.java
new file mode 100644
index 0000000..a2e0de5
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/wifiaware/testcase/DataPathOutOfBandTestCase.java
@@ -0,0 +1,424 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.verifier.wifiaware.testcase;
+
+import static com.android.cts.verifier.wifiaware.CallbackUtils.CALLBACK_TIMEOUT_SEC;
+
+import android.content.Context;
+import android.net.ConnectivityManager;
+import android.net.NetworkCapabilities;
+import android.net.NetworkRequest;
+import android.net.wifi.aware.PeerHandle;
+import android.net.wifi.aware.PublishConfig;
+import android.net.wifi.aware.PublishDiscoverySession;
+import android.net.wifi.aware.SubscribeConfig;
+import android.net.wifi.aware.SubscribeDiscoverySession;
+import android.net.wifi.aware.WifiAwareManager;
+import android.net.wifi.aware.WifiAwareSession;
+import android.util.Log;
+import android.util.Pair;
+
+import com.android.cts.verifier.R;
+import com.android.cts.verifier.wifiaware.BaseTestCase;
+import com.android.cts.verifier.wifiaware.CallbackUtils;
+
+/**
+ *Test case for data-path, out-of-band (OOB) test cases:
+ * open/passphrase * responder/initiator.
+ *
+ * OOB assumes that there's an alternative channel over which to communicate the discovery MAC
+ * address of the Aware interface. That channel (e.g. bluetooth or a host test device) is not
+ * readily available (don't want to have Aware tests dependent on BLE). Instead will fake the OOB
+ * channel using Aware itself: will do normal discovery during which the devices will exchange their
+ * MAC addresses, then destroy the discovery sessions and use the MAC addresses to perform OOB data-
+ * path requests.
+ *
+ * Responder test sequence:
+ * 1. Attach (with identity listener)
+ *    wait for results (session)
+ *    wait for identity
+ * 2. Publish
+ *    wait for results (publish session)
+ * 3. Wait for rx message (with MAC)
+ * 4. Send message with MAC
+ *    wait for success
+ * 5. Destroy discovery session
+ * 6. Request network (as Responder)
+ *    wait for network
+ *
+ * Initiator test sequence:
+ * 1. Attach (with identity listener)
+ *    wait for results (session)
+ *    wait for identity
+ * 2. Subscribe
+ *    wait for results (subscribe session)
+ * 3. Wait for discovery
+ * 4. Send message with MAC
+ *    wait for success
+ * 5. Wait for rx message (with MAC)
+ * 6. Destroy discovery session
+ * 7. Sleep for 5 seconds to let Responder time to set up
+ * 8. Request network (as Initiator)
+ *    wait for network
+ */
+public class DataPathOutOfBandTestCase extends BaseTestCase {
+    private static final String TAG = "DataPathOutOfBandTestCase";
+    private static final boolean DBG = true;
+
+    private static final int MAC_BYTES_LEN = 6;
+
+    private static final String SERVICE_NAME = "CtsVerifierTestService";
+    private static final String PASSPHRASE = "Some super secret password";
+    private static final int MESSAGE_ID = 1234;
+
+    private boolean mIsSecurityOpen;
+    private boolean mIsResponder;
+
+    private final Object mLock = new Object();
+
+    private String mFailureReason;
+    private WifiAwareSession mWifiAwareSession;
+    private byte[] mDiscoveryMac;
+
+    public DataPathOutOfBandTestCase(Context context, boolean isSecurityOpen,
+            boolean isResponder) {
+        super(context);
+        mIsSecurityOpen = isSecurityOpen;
+        mIsResponder = isResponder;
+    }
+
+    @Override
+    protected boolean executeTest() throws InterruptedException {
+        if (DBG) {
+            Log.d(TAG, "executeTest: mIsSecurityOpen=" + mIsSecurityOpen + ", mIsResponder="
+                    + mIsResponder);
+        }
+
+        // 1. attach (with identity listener)
+        CallbackUtils.AttachCb attachCb = new CallbackUtils.AttachCb();
+        CallbackUtils.IdentityListenerSingleShot identityL = new CallbackUtils
+                .IdentityListenerSingleShot();
+        mWifiAwareManager.attach(attachCb, identityL, mHandler);
+        Pair<Integer, WifiAwareSession> results = attachCb.waitForAttach();
+        switch (results.first) {
+            case CallbackUtils.AttachCb.TIMEOUT:
+                setFailureReason(mContext.getString(R.string.aware_status_attach_timeout));
+                Log.e(TAG, "executeTest: attach TIMEOUT");
+                return false;
+            case CallbackUtils.AttachCb.ON_ATTACH_FAILED:
+                setFailureReason(mContext.getString(R.string.aware_status_attach_fail));
+                Log.e(TAG, "executeTest: attach ON_ATTACH_FAILED");
+                return false;
+        }
+        mWifiAwareSession = results.second;
+        if (mWifiAwareSession == null) {
+            setFailureReason(mContext.getString(R.string.aware_status_attach_fail));
+            Log.e(TAG, "executeTest: attach callback succeeded but null session returned!?");
+            return false;
+        }
+        mListener.onTestMsgReceived(mContext.getString(R.string.aware_status_attached));
+        if (DBG) {
+            Log.d(TAG, "executeTest: attach succeeded");
+        }
+        mDiscoveryMac = identityL.waitForMac();
+        if (mDiscoveryMac == null) {
+            setFailureReason(mContext.getString(R.string.aware_status_identity_fail));
+            Log.e(TAG, "executeTest: identity callback not triggered");
+            return false;
+        }
+        mListener.onTestMsgReceived(mResources.getString(R.string.aware_status_identity,
+                bytesToHex(mDiscoveryMac, ':')));
+        if (DBG) {
+            Log.d(TAG, "executeTest: identity received: " + bytesToHex(mDiscoveryMac, ':'));
+        }
+
+        if (mIsResponder) {
+            return executeTestResponder();
+        } else {
+            return executeTestInitiator();
+        }
+    }
+
+    private void setFailureReason(String reason) {
+        synchronized (mLock) {
+            mFailureReason = reason;
+        }
+    }
+
+    @Override
+    protected String getFailureReason() {
+        synchronized (mLock) {
+            return mFailureReason;
+        }
+    }
+
+    @Override
+    protected void tearDown() {
+        if (mWifiAwareSession != null) {
+            mWifiAwareSession.destroy();
+            mWifiAwareSession = null;
+        }
+        super.tearDown();
+    }
+
+    private boolean executeTestResponder() throws InterruptedException {
+        if (DBG) Log.d(TAG, "executeTestResponder");
+        CallbackUtils.DiscoveryCb discoveryCb = new CallbackUtils.DiscoveryCb();
+
+        // 2. publish
+        PublishConfig publishConfig = new PublishConfig.Builder().setServiceName(
+                SERVICE_NAME).build();
+        if (DBG) Log.d(TAG, "executeTestResponder: publishConfig=" + publishConfig);
+        mWifiAwareSession.publish(publishConfig, discoveryCb, mHandler);
+
+        //    wait for results - publish session
+        CallbackUtils.DiscoveryCb.CallbackData callbackData = discoveryCb.waitForCallbacks(
+                CallbackUtils.DiscoveryCb.ON_PUBLISH_STARTED
+                        | CallbackUtils.DiscoveryCb.ON_SESSION_CONFIG_FAILED);
+        switch (callbackData.callback) {
+            case CallbackUtils.DiscoveryCb.TIMEOUT:
+                setFailureReason(mContext.getString(R.string.aware_status_publish_timeout));
+                Log.e(TAG, "executeTestResponder: publish TIMEOUT");
+                return false;
+            case CallbackUtils.DiscoveryCb.ON_SESSION_CONFIG_FAILED:
+                setFailureReason(mContext.getString(R.string.aware_status_publish_failed));
+                Log.e(TAG, "executeTestResponder: publish ON_SESSION_CONFIG_FAILED");
+                return false;
+        }
+        PublishDiscoverySession discoverySession = callbackData.publishDiscoverySession;
+        if (discoverySession == null) {
+            setFailureReason(mContext.getString(R.string.aware_status_publish_null_session));
+            Log.e(TAG, "executeTestResponder: publish succeeded but null session returned");
+            return false;
+        }
+        mListener.onTestMsgReceived(mContext.getString(R.string.aware_status_publish_started));
+        if (DBG) Log.d(TAG, "executeTestResponder: publish succeeded");
+
+        // 3. Wait for rx message (with MAC)
+        callbackData = discoveryCb.waitForCallbacks(CallbackUtils.DiscoveryCb.ON_MESSAGE_RECEIVED);
+        switch (callbackData.callback) {
+            case CallbackUtils.DiscoveryCb.TIMEOUT:
+                setFailureReason(mContext.getString(R.string.aware_status_receive_timeout));
+                Log.e(TAG, "executeTestResponder: receive message TIMEOUT");
+                return false;
+        }
+
+        if (callbackData.serviceSpecificInfo == null
+                || callbackData.serviceSpecificInfo.length != MAC_BYTES_LEN) {
+            setFailureReason(mContext.getString(R.string.aware_status_receive_failure));
+            Log.e(TAG, "executeTestResponder: receive message message content mismatch: "
+                    + bytesToHex(callbackData.serviceSpecificInfo, ':'));
+            return false;
+        }
+
+        PeerHandle peerHandle = callbackData.peerHandle;
+        byte[] peerMac = callbackData.serviceSpecificInfo;
+
+        mListener.onTestMsgReceived(mResources.getString(R.string.aware_status_received_mac,
+                bytesToHex(peerMac, ':')));
+        if (DBG) {
+            Log.d(TAG, "executeTestResponder: received MAC address: " + bytesToHex(peerMac, ':'));
+        }
+
+        // 4. Send message with MAC and wait for success
+        discoverySession.sendMessage(peerHandle, MESSAGE_ID, mDiscoveryMac);
+        callbackData = discoveryCb.waitForCallbacks(
+                CallbackUtils.DiscoveryCb.ON_MESSAGE_SEND_SUCCEEDED
+                        | CallbackUtils.DiscoveryCb.ON_MESSAGE_SEND_FAILED);
+        switch (callbackData.callback) {
+            case CallbackUtils.DiscoveryCb.TIMEOUT:
+                setFailureReason(mContext.getString(R.string.aware_status_send_timeout));
+                Log.e(TAG, "executeTestResponder: send message TIMEOUT");
+                return false;
+            case CallbackUtils.DiscoveryCb.ON_MESSAGE_SEND_FAILED:
+                setFailureReason(mContext.getString(R.string.aware_status_send_failed));
+                Log.e(TAG, "executeTestResponder: send message ON_MESSAGE_SEND_FAILED");
+                return false;
+        }
+        mListener.onTestMsgReceived(mContext.getString(R.string.aware_status_send_success));
+        if (DBG) Log.d(TAG, "executeTestResponder: send message succeeded");
+
+        if (callbackData.messageId != MESSAGE_ID) {
+            setFailureReason(mContext.getString(R.string.aware_status_send_fail_parameter));
+            Log.e(TAG, "executeTestResponder: send message message ID mismatch: "
+                    + callbackData.messageId);
+            return false;
+        }
+
+        // 5. Destroy discovery session
+        discoverySession.destroy();
+
+        // 6. Request network (as Responder) and wait for network
+        ConnectivityManager cm = (ConnectivityManager) mContext.getSystemService(
+                Context.CONNECTIVITY_SERVICE);
+        String networkSpecifier = mIsSecurityOpen ? mWifiAwareSession.createNetworkSpecifierOpen(
+                WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_RESPONDER, peerMac)
+                : mWifiAwareSession.createNetworkSpecifierPassphrase(
+                        WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_RESPONDER, peerMac, PASSPHRASE);
+        NetworkRequest nr = new NetworkRequest.Builder().addTransportType(
+                NetworkCapabilities.TRANSPORT_WIFI_AWARE).setNetworkSpecifier(
+                networkSpecifier).build();
+        CallbackUtils.NetworkCb networkCb = new CallbackUtils.NetworkCb();
+        cm.requestNetwork(nr, CALLBACK_TIMEOUT_SEC * 1000, networkCb);
+        mListener.onTestMsgReceived(mContext.getString(R.string.aware_status_network_requested));
+        if (DBG) Log.d(TAG, "executeTestResponder: requested network");
+        boolean networkAvailable = networkCb.waitForNetwork();
+        cm.unregisterNetworkCallback(networkCb);
+        if (!networkAvailable) {
+            setFailureReason(mContext.getString(R.string.aware_status_network_failed));
+            Log.e(TAG, "executeTestResponder: network request rejected - ON_UNAVAILABLE");
+            return false;
+        }
+        mListener.onTestMsgReceived(mContext.getString(R.string.aware_status_network_success));
+        if (DBG) Log.d(TAG, "executeTestResponder: network request granted - AVAILABLE");
+
+        mListener.onTestMsgReceived(mContext.getString(R.string.aware_status_lifecycle_ok));
+        return true;
+    }
+
+    private boolean executeTestInitiator() throws InterruptedException {
+        if (DBG) Log.d(TAG, "executeTestInitiator");
+        CallbackUtils.DiscoveryCb discoveryCb = new CallbackUtils.DiscoveryCb();
+
+        // 2. Subscribe
+        SubscribeConfig subscribeConfig = new SubscribeConfig.Builder().setServiceName(
+                SERVICE_NAME).build();
+        if (DBG) Log.d(TAG, "executeTestInitiator: subscribeConfig=" + subscribeConfig);
+        mWifiAwareSession.subscribe(subscribeConfig, discoveryCb, mHandler);
+
+        //    wait for results - subscribe session
+        CallbackUtils.DiscoveryCb.CallbackData callbackData = discoveryCb.waitForCallbacks(
+                CallbackUtils.DiscoveryCb.ON_SUBSCRIBE_STARTED
+                        | CallbackUtils.DiscoveryCb.ON_SESSION_CONFIG_FAILED);
+        switch (callbackData.callback) {
+            case CallbackUtils.DiscoveryCb.TIMEOUT:
+                setFailureReason(mContext.getString(R.string.aware_status_subscribe_timeout));
+                Log.e(TAG, "executeTestInitiator: subscribe TIMEOUT");
+                return false;
+            case CallbackUtils.DiscoveryCb.ON_SESSION_CONFIG_FAILED:
+                setFailureReason(mContext.getString(R.string.aware_status_subscribe_failed));
+                Log.e(TAG, "executeTestInitiator: subscribe ON_SESSION_CONFIG_FAILED");
+                return false;
+        }
+        SubscribeDiscoverySession discoverySession = callbackData.subscribeDiscoverySession;
+        if (discoverySession == null) {
+            setFailureReason(mContext.getString(R.string.aware_status_subscribe_null_session));
+            Log.e(TAG, "executeTestInitiator: subscribe succeeded but null session returned");
+            return false;
+        }
+        mListener.onTestMsgReceived(mContext.getString(R.string.aware_status_subscribe_started));
+        if (DBG) Log.d(TAG, "executeTestInitiator: subscribe succeeded");
+
+        // 3. Wait for discovery
+        callbackData = discoveryCb.waitForCallbacks(
+                CallbackUtils.DiscoveryCb.ON_SERVICE_DISCOVERED);
+        switch (callbackData.callback) {
+            case CallbackUtils.DiscoveryCb.TIMEOUT:
+                setFailureReason(mContext.getString(R.string.aware_status_discovery_timeout));
+                Log.e(TAG, "executeTestInitiator: waiting for discovery TIMEOUT");
+                return false;
+        }
+        PeerHandle peerHandle = callbackData.peerHandle;
+        mListener.onTestMsgReceived(mContext.getString(R.string.aware_status_discovery));
+        if (DBG) Log.d(TAG, "executeTestInitiator: discovery");
+
+        // 4. Send message with MAC and wait for success
+        discoverySession.sendMessage(peerHandle, MESSAGE_ID, mDiscoveryMac);
+        callbackData = discoveryCb.waitForCallbacks(
+                CallbackUtils.DiscoveryCb.ON_MESSAGE_SEND_SUCCEEDED
+                        | CallbackUtils.DiscoveryCb.ON_MESSAGE_SEND_FAILED);
+        switch (callbackData.callback) {
+            case CallbackUtils.DiscoveryCb.TIMEOUT:
+                setFailureReason(mContext.getString(R.string.aware_status_send_timeout));
+                Log.e(TAG, "executeTestInitiator: send message TIMEOUT");
+                return false;
+            case CallbackUtils.DiscoveryCb.ON_MESSAGE_SEND_FAILED:
+                setFailureReason(mContext.getString(R.string.aware_status_send_failed));
+                Log.e(TAG, "executeTestInitiator: send message ON_MESSAGE_SEND_FAILED");
+                return false;
+        }
+        mListener.onTestMsgReceived(mContext.getString(R.string.aware_status_send_success));
+        if (DBG) Log.d(TAG, "executeTestInitiator: send message succeeded");
+
+        if (callbackData.messageId != MESSAGE_ID) {
+            setFailureReason(mContext.getString(R.string.aware_status_send_fail_parameter));
+            Log.e(TAG, "executeTestInitiator: send message message ID mismatch: "
+                    + callbackData.messageId);
+            return false;
+        }
+
+        // 5. Wait for rx message (with MAC)
+        callbackData = discoveryCb.waitForCallbacks(CallbackUtils.DiscoveryCb.ON_MESSAGE_RECEIVED);
+        switch (callbackData.callback) {
+            case CallbackUtils.DiscoveryCb.TIMEOUT:
+                setFailureReason(mContext.getString(R.string.aware_status_receive_timeout));
+                Log.e(TAG, "executeTestInitiator: receive message TIMEOUT");
+                return false;
+        }
+
+        if (callbackData.serviceSpecificInfo == null
+                || callbackData.serviceSpecificInfo.length != MAC_BYTES_LEN) {
+            setFailureReason(mContext.getString(R.string.aware_status_receive_failure));
+            Log.e(TAG, "executeTestInitiator: receive message message content mismatch: "
+                    + bytesToHex(callbackData.serviceSpecificInfo, ':'));
+            return false;
+        }
+
+        byte[] peerMac = callbackData.serviceSpecificInfo;
+
+        mListener.onTestMsgReceived(mResources.getString(R.string.aware_status_received_mac,
+                bytesToHex(peerMac, ':')));
+        if (DBG) {
+            Log.d(TAG, "executeTestInitiator: received MAC address: " + bytesToHex(peerMac, ':'));
+        }
+
+        // 6. Destroy discovery session
+        discoverySession.destroy();
+
+        // 7. Sleep for 5 seconds to let Responder time to set up
+        mListener.onTestMsgReceived(
+                mContext.getString(R.string.aware_status_sleeping_wait_for_responder));
+        Thread.sleep(5000);
+
+        // 8. Request network (as Initiator) and wait for network
+        ConnectivityManager cm = (ConnectivityManager) mContext.getSystemService(
+                Context.CONNECTIVITY_SERVICE);
+        String networkSpecifier = mIsSecurityOpen ? mWifiAwareSession.createNetworkSpecifierOpen(
+                WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_INITIATOR, peerMac)
+                : mWifiAwareSession.createNetworkSpecifierPassphrase(
+                        WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_INITIATOR, peerMac, PASSPHRASE);
+        NetworkRequest nr = new NetworkRequest.Builder().addTransportType(
+                NetworkCapabilities.TRANSPORT_WIFI_AWARE).setNetworkSpecifier(
+                networkSpecifier).build();
+        CallbackUtils.NetworkCb networkCb = new CallbackUtils.NetworkCb();
+        cm.requestNetwork(nr, CALLBACK_TIMEOUT_SEC * 1000, networkCb);
+        mListener.onTestMsgReceived(mContext.getString(R.string.aware_status_network_requested));
+        if (DBG) Log.d(TAG, "executeTestInitiator: requested network");
+        boolean networkAvailable = networkCb.waitForNetwork();
+        cm.unregisterNetworkCallback(networkCb);
+        if (!networkAvailable) {
+            setFailureReason(mContext.getString(R.string.aware_status_network_failed));
+            Log.e(TAG, "executeTestInitiator: network request rejected - ON_UNAVAILABLE");
+            return false;
+        }
+        mListener.onTestMsgReceived(mContext.getString(R.string.aware_status_network_success));
+        if (DBG) Log.d(TAG, "executeTestInitiator: network request granted - AVAILABLE");
+
+        mListener.onTestMsgReceived(mContext.getString(R.string.aware_status_lifecycle_ok));
+        return true;
+    }
+}
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 a2dad7e..30d5f95 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
@@ -366,6 +366,8 @@
                 // Get the tests to run in this shard
                 modules = mModuleRepo.getModules(getDevice().getSerialNumber(), mShardIndex);
             }
+            mExcludeFilters.clear();
+            mIncludeFilters.clear();
             // Update BuildInfo in each shard to store the original command-line arguments from
             // the session to be retried. These arguments will be serialized in the report later.
             if (mRetrySessionId != null) {
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 ad12490..794e45e 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
@@ -286,6 +286,7 @@
                         configFile.getName()), e);
             }
         }
+        mExcludeFilters.clear();
         TestRunHandler.setTestRuns(new CompatibilityBuildHelper(buildInfo), shardedTestCounts);
     }
 
diff --git a/hostsidetests/incident/apps/errorsapp/src/com/android/server/cts/errors/ErrorsTests.java b/hostsidetests/incident/apps/errorsapp/src/com/android/server/cts/errors/ErrorsTests.java
index 9243dcc..630df5c 100644
--- a/hostsidetests/incident/apps/errorsapp/src/com/android/server/cts/errors/ErrorsTests.java
+++ b/hostsidetests/incident/apps/errorsapp/src/com/android/server/cts/errors/ErrorsTests.java
@@ -15,7 +15,6 @@
  */
 package com.android.server.cts.errors;
 
-import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 
 import android.content.BroadcastReceiver;
@@ -41,6 +40,12 @@
 public class ErrorsTests {
     private static final String TAG = "ErrorsTests";
 
+    private static final String CRASH_TAG = "data_app_crash";
+    private static final String ANR_TAG = "data_app_anr";
+    private static final String NATIVE_CRASH_TAG = "SYSTEM_TOMBSTONE";
+
+    private static final int TIMEOUT_SECS = 60 * 3;
+
     private CountDownLatch mResultsReceivedSignal;
     private DropBoxManager mDropbox;
     private long mStartMs;
@@ -51,7 +56,6 @@
         mContext = InstrumentationRegistry.getTargetContext();
         mDropbox = (DropBoxManager) mContext.getSystemService(Context.DROPBOX_SERVICE);
         mResultsReceivedSignal = new CountDownLatch(1);
-        registerReceiver(mContext, mResultsReceivedSignal);
         mStartMs = System.currentTimeMillis();
     }
 
@@ -59,60 +63,63 @@
     public void testException() throws Exception {
         Log.i(TAG, "testException");
 
+        registerReceiver(mContext, mResultsReceivedSignal, CRASH_TAG,
+                 mContext.getPackageName() + ":TestProcess",
+                "java.lang.RuntimeException: This is a test exception");
         Intent intent = new Intent();
         intent.setClass(mContext, ExceptionActivity.class);
         mContext.startActivity(intent);
 
-        mResultsReceivedSignal.await(10, TimeUnit.SECONDS);
-        assertDropboxContains("data_app_crash", mContext.getPackageName() + ":TestProcess",
-                "java.lang.RuntimeException: This is a test exception");
+        assertTrue(mResultsReceivedSignal.await(TIMEOUT_SECS, TimeUnit.SECONDS));
     }
 
     @Test
     public void testANR() throws Exception {
         Log.i(TAG, "testANR");
 
+        registerReceiver(mContext, mResultsReceivedSignal, ANR_TAG,
+                mContext.getPackageName() + ":TestProcess",
+                "Subject: Broadcast of Intent { act=android.intent.action.SCREEN_ON");
         Intent intent = new Intent();
         intent.setClass(mContext, ANRActivity.class);
         mContext.startActivity(intent);
 
-        mResultsReceivedSignal.await(60, TimeUnit.SECONDS);
-        assertDropboxContains("data_app_anr", mContext.getPackageName() + ":TestProcess",
-                "Subject: Broadcast of Intent { act=android.intent.action.SCREEN_ON");
+        assertTrue(mResultsReceivedSignal.await(TIMEOUT_SECS, TimeUnit.SECONDS));
     }
 
     @Test
     public void testNativeCrash() throws Exception {
         Log.i(TAG, "testNativeCrash");
 
+        registerReceiver(mContext, mResultsReceivedSignal, NATIVE_CRASH_TAG,
+                mContext.getPackageName() + ":TestProcess", "backtrace:");
         Intent intent = new Intent();
         intent.setClass(mContext, NativeActivity.class);
         mContext.startActivity(intent);
 
-        mResultsReceivedSignal.await(10, TimeUnit.SECONDS);
-        assertDropboxContains("SYSTEM_TOMBSTONE", mContext.getPackageName() + ":TestProcess",
-                "backtrace:");
+        assertTrue(mResultsReceivedSignal.await(TIMEOUT_SECS, TimeUnit.SECONDS));
     }
 
-    static void registerReceiver(Context ctx, CountDownLatch onReceiveLatch) {
+    void registerReceiver(Context ctx, CountDownLatch onReceiveLatch, String wantTag,
+            String... wantInStackTrace) {
         ctx.registerReceiver(new BroadcastReceiver() {
             @Override
             public void onReceive(Context context, Intent intent) {
-                onReceiveLatch.countDown();
+                // DropBox might receive other entries while we're waiting for the error
+                // entry, so we need to check the tag and stack trace before continuing.
+                DropBoxManager.Entry entry = mDropbox.getNextEntry(wantTag, mStartMs);
+                if (entry != null) {
+                    String stackTrace = entry.getText(10000); // Only need to check a few lines.
+                    boolean allMatches = true;
+                    for (String line : wantInStackTrace) {
+                        allMatches &= stackTrace.contains(line);
+                    }
+                    entry.close();
+                    if (allMatches) {
+                        onReceiveLatch.countDown();
+                    }
+                }
             }
         }, new IntentFilter(DropBoxManager.ACTION_DROPBOX_ENTRY_ADDED));
     }
-
-    private void assertDropboxContains(String tag, String... wantInStackTrace) throws Exception {
-        DropBoxManager.Entry entry = mDropbox.getNextEntry(tag, mStartMs);
-        assertTrue("No entry found with tag: " + tag, entry != null);
-
-        assertEquals("Tag", tag, entry.getTag());
-
-        String stackTrace = entry.getText(10000); // Only need to check a few lines.
-        for (String line : wantInStackTrace) {
-            assertTrue(tag + ": Stack trace did not contain: " + line, stackTrace.contains(line));
-        }
-        entry.close();
-    }
 }
diff --git a/hostsidetests/services/activityandwindowmanager/windowmanager/frametestapp/AndroidManifest.xml b/hostsidetests/services/activityandwindowmanager/windowmanager/frametestapp/AndroidManifest.xml
index 8cb377d..46c9b03 100755
--- a/hostsidetests/services/activityandwindowmanager/windowmanager/frametestapp/AndroidManifest.xml
+++ b/hostsidetests/services/activityandwindowmanager/windowmanager/frametestapp/AndroidManifest.xml
@@ -22,7 +22,7 @@
         <activity android:name=".DialogTestActivity"
                 android:exported="true"
         />
-        <activity android:name=".MovingPopupTestActivity"
+        <activity android:name=".MovingChildTestActivity"
                   android:exported="true"
         />
     </application>
diff --git a/hostsidetests/services/activityandwindowmanager/windowmanager/frametestapp/src/android/server/frametestapp/MovingPopupTestActivity.java b/hostsidetests/services/activityandwindowmanager/windowmanager/frametestapp/src/android/server/frametestapp/MovingChildTestActivity.java
similarity index 67%
rename from hostsidetests/services/activityandwindowmanager/windowmanager/frametestapp/src/android/server/frametestapp/MovingPopupTestActivity.java
rename to hostsidetests/services/activityandwindowmanager/windowmanager/frametestapp/src/android/server/frametestapp/MovingChildTestActivity.java
index 91b9788..de6f597 100644
--- a/hostsidetests/services/activityandwindowmanager/windowmanager/frametestapp/src/android/server/frametestapp/MovingPopupTestActivity.java
+++ b/hostsidetests/services/activityandwindowmanager/windowmanager/frametestapp/src/android/server/frametestapp/MovingChildTestActivity.java
@@ -18,23 +18,23 @@
 
 import android.app.Activity;
 import android.content.Intent;
+import android.content.Context;
 import android.os.Bundle;
 import android.view.WindowManager;
 import android.view.Window;
 import android.view.Gravity;
 import android.view.View;
 import android.widget.Space;
-import android.widget.PopupWindow;
 import android.widget.Button;
 import android.view.ViewGroup.LayoutParams;
+import android.view.WindowManager;
 import android.widget.FrameLayout;
 
-// This activity will parent a Popup to the main window, and then move
-// the main window around. We can use this to verify the Popup
+// This activity will parent a Child to the main window, and then move
+// the main window around. We can use this to verify the Child
 // is properly updated.
-public class MovingPopupTestActivity extends Activity {
+public class MovingChildTestActivity extends Activity {
     Space mView;
-    PopupWindow mPopupWindow;
     int mX = 0;
     int mY = 0;
 
@@ -43,6 +43,7 @@
             public void run() {
                 final Window w = getWindow();
                 final WindowManager.LayoutParams attribs = w.getAttributes();
+                attribs.privateFlags = WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION;
                 attribs.x = mX % 1000;
                 attribs.y = mY % 1000;
                 w.setAttributes(attribs);
@@ -52,16 +53,19 @@
             }
     };
 
-    final Runnable makePopup = new Runnable() {
+    final Runnable makeChild = new Runnable() {
             @Override
             public void run() {
-                Button b = new Button(MovingPopupTestActivity.this);
+                Button b = new Button(MovingChildTestActivity.this);
+                WindowManager.LayoutParams p = new WindowManager.LayoutParams(
+                        WindowManager.LayoutParams.TYPE_APPLICATION_PANEL);
+                p.privateFlags = WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION;
+                p.x = 0;
+                p.y = 0;
+                p.token = mView.getWindowToken();
+                p.setTitle("ChildWindow");
 
-                mPopupWindow = new PopupWindow(MovingPopupTestActivity.this);
-                mPopupWindow.setContentView(b);
-                mPopupWindow.setWidth(50);
-                mPopupWindow.setHeight(50);
-                mPopupWindow.showAtLocation(mView, 0, 0, 0);
+                ((WindowManager)getSystemService(Context.WINDOW_SERVICE)).addView(b, p);
 
                 mView.postDelayed(moveWindow, 50);
             }
@@ -76,6 +80,6 @@
         mView = new Space(this);
 
         setContentView(mView, p);
-        mView.post(makePopup);
+        mView.post(makeChild);
     }
 }
diff --git a/hostsidetests/services/activityandwindowmanager/windowmanager/src/android/server/cts/PopupMovementTests.java b/hostsidetests/services/activityandwindowmanager/windowmanager/src/android/server/cts/ChildMovementTests.java
similarity index 89%
rename from hostsidetests/services/activityandwindowmanager/windowmanager/src/android/server/cts/PopupMovementTests.java
rename to hostsidetests/services/activityandwindowmanager/windowmanager/src/android/server/cts/ChildMovementTests.java
index 6c7cad0..218fcfe 100644
--- a/hostsidetests/services/activityandwindowmanager/windowmanager/src/android/server/cts/PopupMovementTests.java
+++ b/hostsidetests/services/activityandwindowmanager/windowmanager/src/android/server/cts/ChildMovementTests.java
@@ -32,17 +32,17 @@
 import android.server.cts.ActivityManagerTestBase;
 import android.server.cts.WindowManagerState.WindowState;
 
-public class PopupMovementTests extends ParentChildTestBase {
+public class ChildMovementTests extends ParentChildTestBase {
     private List<WindowState> mWindowList = new ArrayList();
 
     @Override
     String intentKey() {
-        return "android.server.FrameTestApp.PopupTestCase";
+        return "android.server.FrameTestApp.ChildTestCase";
     }
 
     @Override
     String activityName() {
-        return "MovingPopupTestActivity";
+        return "MovingChildTestActivity";
     }
 
     WindowState getSingleWindow(String fullWindowName) {
@@ -66,7 +66,7 @@
     }
 
     void doSingleTest(ParentChildTest t) throws Exception {
-        String popupName = "PopupWindow";
+        String popupName = "ChildWindow";
         final String[] waitForVisible = new String[] { popupName };
 
         mAmWmState.setUseActivityNamesForWindowNames(false);
@@ -85,7 +85,7 @@
 
     SurfaceTraceReceiver.SurfaceObserver observer = new SurfaceTraceReceiver.SurfaceObserver() {
         int transactionCount = 0;
-        boolean sawPopupMove = false;
+        boolean sawChildMove = false;
         boolean sawMainMove = false;
         int timesSeen = 0;
 
@@ -93,7 +93,7 @@
         public void openTransaction() {
             transactionCount++;
             if (transactionCount == 1) {
-                sawPopupMove = false;
+                sawChildMove = false;
                 sawMainMove = false;
             }
         }
@@ -105,7 +105,7 @@
                 return;
             }
             synchronized (monitor) {
-                if (sawPopupMove ^ sawMainMove ) {
+                if (sawChildMove ^ sawMainMove ) {
                     monitor.notifyAll();
                     return;
                 }
@@ -119,7 +119,7 @@
         @Override
         public void setPosition(String windowName, float x, float y) {
             if (windowName.equals(popupName)) {
-                sawPopupMove = true;
+                sawChildMove = true;
                 timesSeen++;
             } else if (windowName.equals(mainName)) {
                 sawMainMove = true;
@@ -128,10 +128,10 @@
     };
 
     /**
-     * Here we test that a Popup moves in the same transaction
-     * as its parent. We launch an activity with a Popup which will
+     * Here we test that a Child moves in the same transaction
+     * as its parent. We launch an activity with a Child which will
      * move around its own main window. Then we listen to WindowManager transactions.
-     * Since the Popup is static within the window, if we ever see one of
+     * Since the Child is static within the window, if we ever see one of
      * them move xor the other one we have a problem!
      */
     public void testSurfaceMovesWithParent() throws Exception {
diff --git a/hostsidetests/services/activityandwindowmanager/windowmanager/src/android/server/cts/DialogFrameTests.java b/hostsidetests/services/activityandwindowmanager/windowmanager/src/android/server/cts/DialogFrameTests.java
index 56b1b40..6fb5fbd 100644
--- a/hostsidetests/services/activityandwindowmanager/windowmanager/src/android/server/cts/DialogFrameTests.java
+++ b/hostsidetests/services/activityandwindowmanager/windowmanager/src/android/server/cts/DialogFrameTests.java
@@ -135,7 +135,9 @@
     // With FLAG_LAYOUT_NO_LIMITS  we should get the size we request, even if its much
     // larger than the screen.
     public void testOversizedDimensionsNoLimits() throws Exception {
-        doParentChildTest("OversizedDimensionsNoLimits",
+        // TODO(b/36890978): We only run this in fullscreen because of the
+        // unclear status of NO_LIMITS for non-child surfaces in MW modes
+        doFullscreenTest("OversizedDimensionsNoLimits",
             (WindowState parent, WindowState dialog) -> {
                 Rectangle contentFrame = parent.getContentFrame();
                 Rectangle expectedFrame = new Rectangle(contentFrame.x, contentFrame.y,
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityVolumeTest.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityVolumeTest.java
index 8291633..b67fc28 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityVolumeTest.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityVolumeTest.java
@@ -14,6 +14,7 @@
 package android.accessibilityservice.cts;
 
 import android.app.Instrumentation;
+import android.content.pm.PackageManager;
 import android.support.test.InstrumentationRegistry;
 import android.support.test.runner.AndroidJUnit4;
 import android.media.AudioManager;
@@ -31,16 +32,25 @@
 public class AccessibilityVolumeTest {
     Instrumentation mInstrumentation;
     AudioManager mAudioManager;
+    // If a platform collects all volumes into one, these tests aren't relevant
+    boolean mSingleVolume;
 
     @Before
     public void setUp() {
         mInstrumentation = InstrumentationRegistry.getInstrumentation();
         mAudioManager =
                 (AudioManager) mInstrumentation.getContext().getSystemService(AUDIO_SERVICE);
+        // TVs have a single volume
+        PackageManager pm = mInstrumentation.getContext().getPackageManager();
+        mSingleVolume = (pm != null) && (pm.hasSystemFeature(PackageManager.FEATURE_LEANBACK)
+                || pm.hasSystemFeature(PackageManager.FEATURE_TELEVISION));
     }
 
     @Test
     public void testChangeAccessibilityVolume_outsideValidAccessibilityService_shouldFail() {
+        if (mSingleVolume) {
+            return;
+        }
         int startingVolume = mAudioManager.getStreamVolume(AudioManager.STREAM_ACCESSIBILITY);
         int otherVolume = (startingVolume == 0) ? 1 : startingVolume - 1;
         mAudioManager.setStreamVolume(AudioManager.STREAM_ACCESSIBILITY, otherVolume, 0);
@@ -50,6 +60,9 @@
 
     @Test
     public void testChangeAccessibilityVolume_inAccessibilityService_shouldWork() {
+        if (mSingleVolume) {
+            return;
+        }
         int startingVolume = mAudioManager.getStreamVolume(AudioManager.STREAM_ACCESSIBILITY);
         int otherVolume = (startingVolume == 0) ? 1 : startingVolume - 1;
         InstrumentedAccessibilityService service = InstrumentedAccessibilityService.enableService(
diff --git a/tests/app/src/android/app/cts/NotificationTest.java b/tests/app/src/android/app/cts/NotificationTest.java
index ca10e56..c7f6c20 100644
--- a/tests/app/src/android/app/cts/NotificationTest.java
+++ b/tests/app/src/android/app/cts/NotificationTest.java
@@ -48,6 +48,7 @@
     private static final NotificationChannel CHANNEL = new NotificationChannel("id", "name",
             NotificationManager.IMPORTANCE_HIGH);
     private static final String SHORTCUT_ID = "shortcutId";
+    private static final String SETTING_TEXT = "work chats";
 
     @Override
     protected void setUp() throws Exception {
@@ -91,6 +92,7 @@
                 .setBadgeIconType(Notification.BADGE_ICON_SMALL)
                 .setShortcutId(SHORTCUT_ID)
                 .setTimeout(TIMEOUT)
+                .setSettingsText(SETTING_TEXT)
                 .build();
         mNotification.icon = 0;
         mNotification.number = 1;
@@ -145,6 +147,7 @@
         assertEquals(mNotification.getBadgeIconType(), result.getBadgeIconType());
         assertEquals(mNotification.getTimeout(), result.getTimeout());
         assertEquals(mNotification.getChannel(), result.getChannel());
+        assertEquals(mNotification.getSettingsText(), result.getSettingsText());
 
         mNotification.contentIntent = null;
         parcel = Parcel.obtain();
@@ -203,6 +206,7 @@
                 .setBadgeIconType(Notification.BADGE_ICON_SMALL)
                 .setShortcutId(SHORTCUT_ID)
                 .setTimeout(TIMEOUT)
+                .setSettingsText(SETTING_TEXT)
                 .build();
         assertEquals(CONTENT_TEXT, mNotification.extras.getString(Notification.EXTRA_TEXT));
         assertEquals(CONTENT_TITLE, mNotification.extras.getString(Notification.EXTRA_TITLE));
@@ -212,6 +216,7 @@
         assertEquals(Notification.BADGE_ICON_SMALL, mNotification.getBadgeIconType());
         assertEquals(SHORTCUT_ID, mNotification.getShortcutId());
         assertEquals(TIMEOUT, mNotification.getTimeout());
+        assertEquals(SETTING_TEXT, mNotification.getSettingsText());
     }
 
     public void testActionBuilder() {
diff --git a/tests/autofillservice/src/android/autofillservice/cts/GridActivity.java b/tests/autofillservice/src/android/autofillservice/cts/GridActivity.java
index f925a7b..6df6f10 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/GridActivity.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/GridActivity.java
@@ -70,11 +70,11 @@
         mClearButton.setOnClickListener((v) -> resetFields());
     }
 
-    private void save() {
+    void save() {
         getSystemService(AutofillManager.class).commit();
     }
 
-    private void resetFields() {
+    void resetFields() {
         for (int i = 0; i < N_ROWS; i++) {
             for (int j = 0; j < N_COLS; j++) {
                 mCells[i][j].setText("");
diff --git a/tests/autofillservice/src/android/autofillservice/cts/InstrumentedAutoFillService.java b/tests/autofillservice/src/android/autofillservice/cts/InstrumentedAutoFillService.java
index 39e4741..c78a6e1 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/InstrumentedAutoFillService.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/InstrumentedAutoFillService.java
@@ -158,7 +158,6 @@
             this.callback = callback;
             this.flags = flags;
         }
-
     }
 
     /**
diff --git a/tests/autofillservice/src/android/autofillservice/cts/LoginActivityTest.java b/tests/autofillservice/src/android/autofillservice/cts/LoginActivityTest.java
index 2d5a33c..f6ddb13 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/LoginActivityTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/LoginActivityTest.java
@@ -61,6 +61,9 @@
 import android.support.test.rule.ActivityTestRule;
 import android.support.test.uiautomator.UiObject2;
 import android.view.View;
+import android.view.View.AccessibilityDelegate;
+import android.view.accessibility.AccessibilityNodeInfo;
+import android.view.accessibility.AccessibilityNodeProvider;
 import android.view.autofill.AutofillManager;
 
 import org.junit.After;
@@ -90,11 +93,8 @@
  */
 public class LoginActivityTest extends AutoFillServiceTestCase {
 
-    // TODO(b/33197203 , b/36855717): remove when bug 36855717 is fixed
-    private static final boolean BUG_36855717_FIXED = false;
-
     // TODO(b/33197203 , b/35707731): remove when fixed
-    private static final boolean SUPPORTS_PARTIOTINED_AUTH = false;
+    private static final boolean SUPPORTS_PARTITIONED_AUTH = false;
 
     @Rule
     public final ActivityTestRule<LoginActivity> mActivityRule =
@@ -174,6 +174,25 @@
     }
 
     @Test
+    public void testAutoFillWhenViewHasChildAccessibilityNodes() throws Exception {
+        mActivity.onUsername((v) -> v.setAccessibilityDelegate(new AccessibilityDelegate() {
+            @Override
+            public AccessibilityNodeProvider getAccessibilityNodeProvider(View host) {
+                return new AccessibilityNodeProvider() {
+                    @Override
+                    public AccessibilityNodeInfo createAccessibilityNodeInfo(int virtualViewId) {
+                        final AccessibilityNodeInfo info = AccessibilityNodeInfo.obtain();
+                        info.addChild(v, virtualViewId);
+                        return info;
+                    }
+                };
+            }
+        }));
+
+        testAutoFillOneDataset();
+    }
+
+    @Test
     public void testAutoFillOneDatasetAndMoveFocusAround() throws Exception {
         // Set service.
         enableService();
@@ -975,13 +994,9 @@
         sUiBot.assertNotShownByText("Tap to auth response");
 
         // ...and select it this time
-        if (BUG_36855717_FIXED) {
-            callback.assertUiShownEvent(username);
-        }
+        callback.assertUiShownEvent(username);
         sUiBot.selectDataset("Dataset");
-        if (BUG_36855717_FIXED) {
-            callback.assertUiHiddenEvent(username);
-        }
+        callback.assertUiHiddenEvent(username);
         sUiBot.assertNoDatasets();
         sUiBot.assertNotShownByText("Tap to auth response");
 
@@ -1036,7 +1051,7 @@
         callback.assertUiShownEvent(username);
         sUiBot.assertShownByText("Tap to auth response");
 
-        if (SUPPORTS_PARTIOTINED_AUTH) {
+        if (SUPPORTS_PARTITIONED_AUTH) {
             // Make sure UI is not show on 2nd field
             final View password = mActivity.getPassword();
             mActivity.onPassword(View::requestFocus);
@@ -1065,13 +1080,9 @@
         callback.assertUiHiddenEvent(username);
         sUiBot.assertNotShownByText("Tap to auth response");
 
-        if (BUG_36855717_FIXED) {
-            callback.assertUiShownEvent(username);
-        }
+        callback.assertUiShownEvent(username);
         sUiBot.selectDataset("Dataset");
-        if (BUG_36855717_FIXED) {
-            callback.assertUiHiddenEvent(username);
-        }
+        callback.assertUiHiddenEvent(username);
         sUiBot.assertNoDatasets();
         sUiBot.assertNotShownByText("Tap to auth response");
 
diff --git a/tests/autofillservice/src/android/autofillservice/cts/PartitionedActivityTest.java b/tests/autofillservice/src/android/autofillservice/cts/PartitionedActivityTest.java
index d82ce48..8c350aa 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/PartitionedActivityTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/PartitionedActivityTest.java
@@ -19,8 +19,13 @@
 import static android.autofillservice.cts.GridActivity.ID_L1C2;
 import static android.autofillservice.cts.GridActivity.ID_L2C1;
 import static android.autofillservice.cts.GridActivity.ID_L2C2;
+import static android.autofillservice.cts.GridActivity.ID_L3C1;
+import static android.autofillservice.cts.GridActivity.ID_L3C2;
+import static android.autofillservice.cts.GridActivity.ID_L4C1;
+import static android.autofillservice.cts.GridActivity.ID_L4C2;
 import static android.autofillservice.cts.Helper.assertTextIsSanitized;
 import static android.autofillservice.cts.Helper.assertValue;
+import static android.service.autofill.SaveInfo.SAVE_DATA_TYPE_PASSWORD;
 
 import static com.google.common.truth.Truth.assertThat;
 import static com.google.common.truth.Truth.assertWithMessage;
@@ -29,6 +34,8 @@
 import android.autofillservice.cts.CannedFillResponse.CannedDataset;
 import android.autofillservice.cts.GridActivity.FillExpectation;
 import android.autofillservice.cts.InstrumentedAutoFillService.FillRequest;
+import android.autofillservice.cts.InstrumentedAutoFillService.SaveRequest;
+import android.os.Bundle;
 import android.support.test.rule.ActivityTestRule;
 
 import org.junit.Before;
@@ -189,6 +196,103 @@
         expectation2.assertAutoFilled();
     }
 
+    @Test
+    public void testAutofillBundleDataIsPassedAlong() throws Exception {
+        // Set service.
+        enableService();
+
+        final Bundle extras = new Bundle();
+        extras.putString("numbers", "4");
+
+        // Prepare 1st partition.
+        final CannedFillResponse response1 = new CannedFillResponse.Builder()
+                .addDataset(new CannedDataset.Builder()
+                        .setField(ID_L1C1, "l1c1", createPresentation("l1c1"))
+                        .setField(ID_L1C2, "l1c2", createPresentation("l1c2"))
+                        .build())
+                .setRequiredSavableIds(SAVE_DATA_TYPE_PASSWORD, ID_L1C1)
+                .setExtras(extras)
+                .build();
+        sReplier.addResponse(response1);
+
+        // Trigger auto-fill on 1st partition.
+        mActivity.focusCell(1, 1);
+        final FillRequest fillRequest1 = sReplier.getNextFillRequest();
+        assertThat(fillRequest1.data).isNull();
+        sUiBot.assertDatasets("l1c1");
+
+        // Prepare 2nd partition; it replaces 'number' and adds 'numbers2'
+        extras.clear();
+        extras.putString("numbers", "48");
+        extras.putString("numbers2", "1516");
+
+        final CannedFillResponse response2 = new CannedFillResponse.Builder()
+                .addDataset(new CannedDataset.Builder()
+                        .setField(ID_L2C1, "l2c1", createPresentation("l2c1"))
+                        .setField(ID_L2C2, "l2c2", createPresentation("l2c2"))
+                        .build())
+                .setExtras(extras)
+                .build();
+        sReplier.addResponse(response2);
+
+        // Trigger auto-fill on 2nd partition
+        mActivity.focusCell(2, 1);
+        final FillRequest fillRequest2 = sReplier.getNextFillRequest();
+        assertWithMessage("null bundle on request 2").that(fillRequest2.data).isNotNull();
+        assertWithMessage("wrong number of extras on request 2 bundle")
+                .that(fillRequest2.data.size()).isEqualTo(1);
+        assertThat(fillRequest2.data.getString("numbers")).isEqualTo("4");
+
+        // Prepare 3nd partition; it has no extras
+        final CannedFillResponse response3 = new CannedFillResponse.Builder()
+                .addDataset(new CannedDataset.Builder()
+                        .setField(ID_L3C1, "l3c1", createPresentation("l3c1"))
+                        .setField(ID_L3C2, "l3c2", createPresentation("l3c2"))
+                        .build())
+                .setExtras(null)
+                .build();
+        sReplier.addResponse(response3);
+
+        // Trigger auto-fill on 3rd partition
+        mActivity.focusCell(3, 1);
+        final FillRequest fillRequest3 = sReplier.getNextFillRequest();
+        assertWithMessage("null bundle on request 3").that(fillRequest2.data).isNotNull();
+        assertWithMessage("wrong number of extras on request 3 bundle")
+                .that(fillRequest3.data.size()).isEqualTo(2);
+        assertThat(fillRequest3.data.getString("numbers")).isEqualTo("48");
+        assertThat(fillRequest3.data.getString("numbers2")).isEqualTo("1516");
+
+
+        // Prepare 4th partition; it contains just 'numbers4'
+        extras.clear();
+        extras.putString("numbers4", "2342");
+
+        final CannedFillResponse response4 = new CannedFillResponse.Builder()
+                .addDataset(new CannedDataset.Builder()
+                        .setField(ID_L4C1, "l4c1", createPresentation("l4c1"))
+                        .setField(ID_L4C2, "l4c2", createPresentation("l4c2"))
+                        .build())
+                .setExtras(extras)
+                .build();
+        sReplier.addResponse(response4);
+
+        // Trigger auto-fill on 4th partition
+        mActivity.focusCell(4, 1);
+        final FillRequest fillRequest4 = sReplier.getNextFillRequest();
+        assertWithMessage("non-null bundle on request 4").that(fillRequest4.data).isNull();
+
+        // Trigger save
+        mActivity.setText(1, 1, "L1C1");
+        mActivity.save();
+
+        sUiBot.saveForAutofill(SAVE_DATA_TYPE_PASSWORD, true);
+        final SaveRequest saveRequest = sReplier.getNextSaveRequest();
+
+        assertWithMessage("wrong number of extras on save request bundle")
+                .that(saveRequest.data.size()).isEqualTo(1);
+        assertThat(saveRequest.data.getString("numbers4")).isEqualTo("2342");
+    }
+
     // TODO(b/33197203, b/35707731): test force autofill after autofilled
     // TODO(b/33197203, b/35707731): add test for saving (1, 2, 3, or more unique save types)
 }
diff --git a/tests/fragment/src/android/fragment/cts/FragmentManagerNonConfigTest.java b/tests/fragment/src/android/fragment/cts/FragmentManagerNonConfigTest.java
index 0ce4aa3..1b28290 100644
--- a/tests/fragment/src/android/fragment/cts/FragmentManagerNonConfigTest.java
+++ b/tests/fragment/src/android/fragment/cts/FragmentManagerNonConfigTest.java
@@ -35,23 +35,16 @@
     public ActivityTestRule<NonConfigOnStopActivity> mActivityRule =
             new ActivityTestRule<>(NonConfigOnStopActivity.class);
 
-    @After
-    public void resetActivity() {
-        FragmentTestUtil.resetOrientation();
-    }
-
     /**
      * When a fragment is added during onStop(), it shouldn't show up in non-config
      * state when restored.
      */
     @Test
     public void nonConfigStop() throws Throwable {
-        if (!FragmentTestUtil.switchOrientation()) {
-            return; // nothing to do -- we can't change the orientation
-        }
+        NonConfigOnStopActivity activity = FragmentTestUtil.recreateActivity(mActivityRule,
+                mActivityRule.getActivity());
 
         // A fragment was added in onStop(), but we shouldn't see it here...
-        Activity activity = OrientationChangeActivity.sActivity;
         assertTrue(activity.getFragmentManager().getFragments().isEmpty());
     }
 
diff --git a/tests/fragment/src/android/fragment/cts/FragmentTestUtil.java b/tests/fragment/src/android/fragment/cts/FragmentTestUtil.java
index 4b2ae16..c175164 100644
--- a/tests/fragment/src/android/fragment/cts/FragmentTestUtil.java
+++ b/tests/fragment/src/android/fragment/cts/FragmentTestUtil.java
@@ -24,8 +24,6 @@
 import android.app.FragmentManager;
 import android.app.FragmentManagerNonConfig;
 import android.app.Instrumentation;
-import android.content.pm.ActivityInfo;
-import android.content.res.Configuration;
 import android.os.Looper;
 import android.os.Parcelable;
 import android.support.test.InstrumentationRegistry;
@@ -35,8 +33,6 @@
 import android.view.ViewGroup;
 import android.view.accessibility.AccessibilityNodeInfo;
 
-import java.lang.ref.WeakReference;
-import java.util.ArrayList;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 
@@ -218,57 +214,28 @@
     }
 
     /**
-     * Switches the orientation of the OrientationChangeActivity.
+     * Restarts the RecreatedActivity and waits for the new activity to be resumed.
      *
-     * @return {@code true} if the orientation changed or {@code false} if the screen is square
-     * or some other error happens
+     * @return The newly-restarted Activity
      */
-    public static boolean switchOrientation() throws InterruptedException {
-        OrientationChangeActivity activity = OrientationChangeActivity.sActivity;
-
-        int currentOrientation = activity.getResources().getConfiguration().orientation;
-
-        int nextOrientation;
-        int expectedOrientation;
-        if (currentOrientation == Configuration.ORIENTATION_LANDSCAPE) {
-            nextOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
-            expectedOrientation = Configuration.ORIENTATION_PORTRAIT;
-        } else if (currentOrientation == Configuration.ORIENTATION_PORTRAIT) {
-            nextOrientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
-            expectedOrientation = Configuration.ORIENTATION_LANDSCAPE;
-        } else {
-            return false; // Don't know what to do with square or unknown orientations
-        }
-
+    public static <T extends RecreatedActivity> T recreateActivity(
+            ActivityTestRule<? extends Activity> rule, T activity) throws InterruptedException {
         // Now switch the orientation
-        LoaderActivity.sResumed = new CountDownLatch(1);
-        LoaderActivity.sDestroyed = new CountDownLatch(1);
+        RecreatedActivity.sResumed = new CountDownLatch(1);
+        RecreatedActivity.sDestroyed = new CountDownLatch(1);
 
-        activity.setRequestedOrientation(nextOrientation);
-        assertTrue(LoaderActivity.sResumed.await(1, TimeUnit.SECONDS));
-        assertTrue(LoaderActivity.sDestroyed.await(1, TimeUnit.SECONDS));
+        runOnUiThreadRethrow(rule, () -> {
+            activity.recreate();
+        });
+        assertTrue(RecreatedActivity.sResumed.await(1, TimeUnit.SECONDS));
+        assertTrue(RecreatedActivity.sDestroyed.await(1, TimeUnit.SECONDS));
+        T newActivity = (T) RecreatedActivity.sActivity;
 
-        int switchedOrientation =
-                LoaderActivity.sActivity.getResources().getConfiguration().orientation;
-        assertEquals(expectedOrientation, switchedOrientation);
-        return true;
+        waitForExecution(rule);
+
+        RecreatedActivity.clearState();
+        return newActivity;
     }
+}
 
-    /**
-     * After calling {@link #switchOrientation()}, this must be called in an After.
-     */
-    public static void resetOrientation() {
-        final OrientationChangeActivity activity = OrientationChangeActivity.sActivity;
-        final int unspecifiedOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
-        if (activity != null && activity.getRequestedOrientation() != unspecifiedOrientation) {
-            OrientationChangeActivity.sResumed = new CountDownLatch(1);
-            activity.setRequestedOrientation(unspecifiedOrientation);
-            // Wait for the orientation change to settle, if there was a change
-            try {
-                OrientationChangeActivity.sResumed.await(1, TimeUnit.SECONDS);
-            } catch (InterruptedException e) {
-                // I guess there wasn't a change in orientation after all
-            }
-        }
-        OrientationChangeActivity.clearState();
-    }}
+
diff --git a/tests/fragment/src/android/fragment/cts/LoaderActivity.java b/tests/fragment/src/android/fragment/cts/LoaderActivity.java
index 5654b42..9c69f75 100644
--- a/tests/fragment/src/android/fragment/cts/LoaderActivity.java
+++ b/tests/fragment/src/android/fragment/cts/LoaderActivity.java
@@ -15,7 +15,6 @@
  */
 package android.fragment.cts;
 
-import android.app.Activity;
 import android.app.LoaderManager;
 import android.content.AsyncTaskLoader;
 import android.content.Context;
@@ -24,13 +23,11 @@
 import android.view.ViewGroup;
 import android.widget.TextView;
 
-import java.util.concurrent.CountDownLatch;
-
 /**
  * This Activity sets the text when loading completes. It also tracks the Activity in
  * a static variable, so it must be cleared in test tear down.
  */
-public class LoaderActivity extends OrientationChangeActivity {
+public class LoaderActivity extends RecreatedActivity {
     public TextView textView;
     public TextView textViewB;
 
diff --git a/tests/fragment/src/android/fragment/cts/LoaderTest.java b/tests/fragment/src/android/fragment/cts/LoaderTest.java
index 880d13e..2eb897e 100755
--- a/tests/fragment/src/android/fragment/cts/LoaderTest.java
+++ b/tests/fragment/src/android/fragment/cts/LoaderTest.java
@@ -22,14 +22,10 @@
 
 import android.app.Fragment;
 import android.app.FragmentManager;
-import android.app.Instrumentation;
 import android.app.LoaderManager;
 import android.content.AsyncTaskLoader;
 import android.content.Context;
-import android.content.Intent;
 import android.content.Loader;
-import android.content.pm.ActivityInfo;
-import android.content.res.Configuration;
 import android.os.Bundle;
 import android.os.SystemClock;
 import android.support.test.InstrumentationRegistry;
@@ -37,7 +33,6 @@
 import android.support.test.rule.ActivityTestRule;
 import android.support.test.runner.AndroidJUnit4;
 
-import org.junit.After;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -55,25 +50,17 @@
     public ActivityTestRule<LoaderActivity> mActivityRule =
             new ActivityTestRule<>(LoaderActivity.class);
 
-    @After
-    public void resetActivity() {
-        FragmentTestUtil.resetOrientation();
-    }
-
     /**
      * Test to ensure that there is no Activity leak due to Loader
      */
     @Test
     public void testLeak() throws Throwable {
-        Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
-        Intent intent = new Intent(mActivityRule.getActivity(), LoaderActivity.class);
-        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-        LoaderActivity.sResumed = new CountDownLatch(1);
-        instrumentation.startActivitySync(intent);
-        assertTrue(LoaderActivity.sResumed.await(1, TimeUnit.SECONDS));
-
+        // Restart the activity because mActivityRule keeps a strong reference to the
+        // old activity.
+        LoaderActivity activity = FragmentTestUtil.recreateActivity(mActivityRule,
+                mActivityRule.getActivity());
         LoaderFragment fragment = new LoaderFragment();
-        FragmentManager fm = LoaderActivity.sActivity.getFragmentManager();
+        FragmentManager fm = activity.getFragmentManager();
 
         fm.beginTransaction()
                 .add(fragment, "1")
@@ -87,12 +74,11 @@
                 .commit();
 
         FragmentTestUtil.executePendingTransactions(mActivityRule, fm);
+        fm = null; // clear it so that it can be released
 
         WeakReference<LoaderActivity> weakActivity = new WeakReference(LoaderActivity.sActivity);
 
-        if (!FragmentTestUtil.switchOrientation()) {
-            return; // can't switch orientation for square screens
-        }
+        activity = FragmentTestUtil.recreateActivity(mActivityRule, activity);
 
         // Wait for everything to settle. We have to make sure that the old Activity
         // is ready to be collected.
@@ -113,12 +99,9 @@
 
         assertEquals("Loaded!", activity.textView.getText().toString());
 
-        if (!FragmentTestUtil.switchOrientation()) {
-            return; // can't switch orientation for square screens
-        }
+        activity = FragmentTestUtil.recreateActivity(mActivityRule, activity);
 
         // After orientation change, the text should still be loaded properly
-        activity = (LoaderActivity) LoaderActivity.sActivity;
         assertEquals("Loaded!", activity.textView.getText().toString());
     }
 
diff --git a/tests/fragment/src/android/fragment/cts/NonConfigOnStopActivity.java b/tests/fragment/src/android/fragment/cts/NonConfigOnStopActivity.java
index e61bd50..a93b616 100644
--- a/tests/fragment/src/android/fragment/cts/NonConfigOnStopActivity.java
+++ b/tests/fragment/src/android/fragment/cts/NonConfigOnStopActivity.java
@@ -17,7 +17,7 @@
 
 import android.app.Fragment;
 
-public class NonConfigOnStopActivity extends OrientationChangeActivity {
+public class NonConfigOnStopActivity extends RecreatedActivity {
     @Override
     protected void onStop() {
         super.onStop();
diff --git a/tests/fragment/src/android/fragment/cts/OrientationChangeActivity.java b/tests/fragment/src/android/fragment/cts/RecreatedActivity.java
similarity index 92%
rename from tests/fragment/src/android/fragment/cts/OrientationChangeActivity.java
rename to tests/fragment/src/android/fragment/cts/RecreatedActivity.java
index d8aeb31..82b32a9 100644
--- a/tests/fragment/src/android/fragment/cts/OrientationChangeActivity.java
+++ b/tests/fragment/src/android/fragment/cts/RecreatedActivity.java
@@ -20,9 +20,9 @@
 
 import java.util.concurrent.CountDownLatch;
 
-public class OrientationChangeActivity extends Activity {
+public class RecreatedActivity extends Activity {
     // These must be cleared after each test using clearState()
-    public static OrientationChangeActivity sActivity;
+    public static RecreatedActivity sActivity;
     public static CountDownLatch sResumed;
     public static CountDownLatch sDestroyed;
 
diff --git a/tests/tests/graphics/assets/almost-red-adobe.png b/tests/tests/graphics/assets/almost-red-adobe.png
new file mode 100644
index 0000000..531b5a4
--- /dev/null
+++ b/tests/tests/graphics/assets/almost-red-adobe.png
Binary files differ
diff --git a/tests/tests/graphics/src/android/graphics/cts/CanvasTest.java b/tests/tests/graphics/src/android/graphics/cts/CanvasTest.java
index 64dba77..05f74f8 100644
--- a/tests/tests/graphics/src/android/graphics/cts/CanvasTest.java
+++ b/tests/tests/graphics/src/android/graphics/cts/CanvasTest.java
@@ -51,12 +51,15 @@
 import android.text.SpannedString;
 import android.util.DisplayMetrics;
 
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Vector;
+
+import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-import java.util.Vector;
-
 @SmallTest
 @RunWith(AndroidJUnit4.class)
 public class CanvasTest {
@@ -2114,4 +2117,24 @@
             assertEquals(matrices.elementAt(i), mCanvas.getMatrix());
         }
     }
+
+    @Test
+    public void testDrawBitmapColorBehavior() {
+        try {
+            // Create a wide gamut bitmap where the pixel value is slightly less than max red.
+            Resources resources = InstrumentationRegistry.getTargetContext().getResources();
+            InputStream in = resources.getAssets().open("almost-red-adobe.png");
+            Bitmap bitmap = BitmapFactory.decodeStream(in);
+
+            // Draw the bitmap to an sRGB canvas.
+            Bitmap canvasBitmap = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888);
+            Canvas canvas = new Canvas(canvasBitmap);
+            canvas.drawBitmap(bitmap, 0, 0, null);
+
+            // Verify that the pixel is now max red.
+            Assert.assertEquals(0xFFFF0000, canvasBitmap.getPixel(0, 0));
+        } catch (IOException e) {
+            Assert.fail();
+        }
+    }
 }
diff --git a/tests/tests/media/Android.mk b/tests/tests/media/Android.mk
index b2ae259..1fb5460 100644
--- a/tests/tests/media/Android.mk
+++ b/tests/tests/media/Android.mk
@@ -45,23 +45,26 @@
 LOCAL_MULTILIB := both
 
 LOCAL_STATIC_JAVA_LIBRARIES := \
-    ctsmediautil \
     compatibility-device-util \
-    ctstestserver \
+    ctsdeviceutillegacy \
+    ctsmediautil \
     ctstestrunner \
-    ndkaudio \
+    ctstestserver \
     junit \
-    legacy-android-test
+    legacy-android-test \
+    ndkaudio
 
 LOCAL_JNI_SHARED_LIBRARIES := \
-    libctsimagereader_jni \
-    libctsmediacodec_jni \
     libaudio_jni \
+    libctsimagereader_jni \
+    libctsmediadrm_jni \
+    libctsmediacodec_jni \
     libnativehelper_compat_libc++ \
     libndkaudioLib
 
 # do not compress VP9 video files
 LOCAL_AAPT_FLAGS := -0 .vp9
+LOCAL_AAPT_FLAGS += -0 .ts
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
diff --git a/tests/tests/media/libmediandkjni/AMediaObjects.h b/tests/tests/media/libmediandkjni/AMediaObjects.h
new file mode 100644
index 0000000..c4d5397
--- /dev/null
+++ b/tests/tests/media/libmediandkjni/AMediaObjects.h
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef AMEDIAOBJECTS_H_
+#define AMEDIAOBJECTS_H_
+
+#include <utils/Log.h>
+
+#include "media/NdkMediaCrypto.h"
+#include "media/NdkMediaDrm.h"
+#include "media/NdkMediaExtractor.h"
+
+namespace {
+
+// Simple class to manage deletion of AMedia objects
+class AMediaObjects {
+ public:
+    AMediaObjects();
+    virtual ~AMediaObjects();
+
+    void setCrypto(AMediaCrypto* const theCrypto) {
+        mCrypto = theCrypto;
+    }
+    void setDrm(AMediaDrm* const theDrm) {
+        mDrm = theDrm;
+    }
+    void setVideoExtractor(AMediaExtractor* const theExtractor) {
+        mVideoExtractor = theExtractor;
+    }
+    void setAudioExtractor(AMediaExtractor* const theExtractor) {
+        mAudioExtractor = theExtractor;
+    }
+
+    AMediaCrypto* getCrypto() const { return mCrypto; }
+    AMediaDrm* getDrm() const { return mDrm; }
+    AMediaExtractor* getAudioExtractor() const { return mAudioExtractor; }
+    AMediaExtractor* getVideoExtractor() const { return mVideoExtractor; }
+
+ private:
+    AMediaCrypto *mCrypto;
+    AMediaDrm* mDrm;
+    AMediaExtractor* mAudioExtractor;
+    AMediaExtractor* mVideoExtractor;
+
+    // Disallow copy and assignment
+    AMediaObjects(const AMediaObjects&);
+    void operator=(const AMediaObjects&);
+};
+
+AMediaObjects::AMediaObjects(void) : mCrypto(NULL), mDrm(NULL),
+        mAudioExtractor(NULL), mVideoExtractor(NULL) {
+}
+
+AMediaObjects::~AMediaObjects() {
+    if (mCrypto) {
+        AMediaCrypto_delete(mCrypto);
+    }
+    if (mAudioExtractor) {
+        AMediaExtractor_delete(mAudioExtractor);
+    }
+    if (mVideoExtractor) {
+        AMediaExtractor_delete(mVideoExtractor);
+    }
+    if (mDrm) {
+        AMediaDrm_release(mDrm);
+    }
+}
+
+}  // anonymous namespace
+#endif  // AMEDIAOBJECTS_H_
+
diff --git a/tests/tests/media/libmediandkjni/Android.mk b/tests/tests/media/libmediandkjni/Android.mk
index 39dbea3..e42d388 100644
--- a/tests/tests/media/libmediandkjni/Android.mk
+++ b/tests/tests/media/libmediandkjni/Android.mk
@@ -14,27 +14,67 @@
 #
 LOCAL_PATH := $(call my-dir)
 
+#------------------------------------------------------------------------------
+# Builds libctsmediacodec_jni.so
+#
 include $(CLEAR_VARS)
 
-LOCAL_MODULE    := libctsmediacodec_jni
+LOCAL_MODULE := libctsmediacodec_jni
 
 LOCAL_MODULE_TAGS := optional
 
 LOCAL_SRC_FILES := \
-	native-media-jni.cpp \
-	codec-utils-jni.cpp  \
-	md5_utils.cpp
+  native-media-jni.cpp \
+  codec-utils-jni.cpp  \
+  md5_utils.cpp
 
 LOCAL_C_INCLUDES := \
-	$(JNI_H_INCLUDE) \
-	system/core/include
+  $(JNI_H_INCLUDE) \
+  system/core/include
 
 LOCAL_C_INCLUDES += $(call include-path-for, mediandk)
 
-LOCAL_SHARED_LIBRARIES := libandroid libnativehelper_compat_libc++ liblog libmediandk libEGL
+LOCAL_SHARED_LIBRARIES := \
+  libandroid libnativehelper_compat_libc++ \
+  liblog libmediandk libEGL
 
 LOCAL_SDK_VERSION := current
 
 LOCAL_CFLAGS := -Werror -Wall -DEGL_EGLEXT_PROTOTYPES
 
 include $(BUILD_SHARED_LIBRARY)
+
+#------------------------------------------------------------------------------
+# Builds libctsmediadrm_jni.so
+#
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := libctsmediadrm_jni
+
+# Don't include this package in any configuration by default.
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_SRC_FILES := \
+  CtsMediaDrmJniOnLoad.cpp \
+  codec-utils-jni.cpp  \
+  md5_utils.cpp \
+  native-mediadrm-jni.cpp \
+
+LOCAL_C_INCLUDES := \
+  $(JNI_H_INCLUDE) \
+  system/core/include
+
+
+LOCAL_C_INCLUDES += $(call include-path-for, mediandk)
+
+LOCAL_SHARED_LIBRARIES := \
+  libandroid libnativehelper_compat_libc++ \
+  liblog libmediandk libdl libEGL
+
+LOCAL_SDK_VERSION := current
+
+LOCAL_CFLAGS := -Werror -Wall -DEGL_EGLEXT_PROTOTYPES
+
+LOCAL_NDK_STL_VARIANT := c++_static
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/tests/tests/media/libmediandkjni/CtsMediaDrmJniOnLoad.cpp b/tests/tests/media/libmediandkjni/CtsMediaDrmJniOnLoad.cpp
new file mode 100644
index 0000000..24714a3
--- /dev/null
+++ b/tests/tests/media/libmediandkjni/CtsMediaDrmJniOnLoad.cpp
@@ -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.
+ */
+
+#include <jni.h>
+#include <stdio.h>
+
+extern int register_android_media_cts_NativeClearKeySystemTest(JNIEnv*);
+
+jint JNI_OnLoad(JavaVM *vm, void */*reserved*/) {
+    JNIEnv *env = NULL;
+
+    if (vm->GetEnv((void **) &env, JNI_VERSION_1_4) != JNI_OK) {
+        return JNI_ERR;
+    }
+
+    if (register_android_media_cts_NativeClearKeySystemTest(env)) {
+        return JNI_ERR;
+    }
+
+    return JNI_VERSION_1_4;
+}
diff --git a/tests/tests/media/libmediandkjni/md5_utils.cpp b/tests/tests/media/libmediandkjni/md5_utils.cpp
index f4f893a..8e520e1 100644
--- a/tests/tests/media/libmediandkjni/md5_utils.cpp
+++ b/tests/tests/media/libmediandkjni/md5_utils.cpp
@@ -157,7 +157,7 @@
  */
 void
 MD5Transform(UWORD32 buf[4], UWORD32 const in[16]) {
-  register UWORD32 a, b, c, d;
+  UWORD32 a, b, c, d;
 
   a = buf[0];
   b = buf[1];
diff --git a/tests/tests/media/libmediandkjni/native-mediadrm-jni.cpp b/tests/tests/media/libmediandkjni/native-mediadrm-jni.cpp
new file mode 100644
index 0000000..b98a6af
--- /dev/null
+++ b/tests/tests/media/libmediandkjni/native-mediadrm-jni.cpp
@@ -0,0 +1,690 @@
+/*
+ * 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.
+ */
+
+#define TAG "NativeMediaDrm"
+
+#include <utils/Log.h>
+#include <sys/types.h>
+
+#include <string>
+#include <vector>
+
+#include <assert.h>
+#include <jni.h>
+#include <JNIHelp.h>
+
+#include <android/native_window_jni.h>
+
+#include "AMediaObjects.h"
+
+#include "media/NdkMediaCodec.h"
+#include "media/NdkMediaCrypto.h"
+#include "media/NdkMediaDrm.h"
+#include "media/NdkMediaExtractor.h"
+#include "media/NdkMediaFormat.h"
+#include "media/NdkMediaMuxer.h"
+
+typedef std::vector<uint8_t> Uuid;
+
+struct fields_t {
+    jfieldID surface;
+    jfieldID mimeType;
+    jfieldID audioUrl;
+    jfieldID videoUrl;
+};
+
+struct PlaybackParams {
+    jobject surface;
+    jstring mimeType;
+    jstring audioUrl;
+    jstring videoUrl;
+};
+
+static fields_t gFieldIds;
+static bool gGotVendorDefinedEvent = false;
+
+static const size_t kPlayTimeSeconds = 30;
+static const size_t kUuidSize = 16;
+
+static const uint8_t kWidevineUuid[kUuidSize] = {
+    0xed, 0xef, 0x8b, 0xa9, 0x79, 0xd6, 0x4a, 0xce,
+    0xa3, 0xc8, 0x27, 0xdc, 0xd5, 0x1d, 0x21, 0xed
+};
+
+// The test content is not packaged with clearkey UUID,
+// we have to use a canned clearkey pssh for the test.
+static const uint8_t kClearkeyPssh[] = {
+    // BMFF box header (4 bytes size + 'pssh')
+    0x00, 0x00, 0x00, 0x34, 0x70, 0x73, 0x73, 0x68,
+    // full box header (version = 1 flags = 0)
+    0x01, 0x00, 0x00, 0x00,
+    // system id
+    0x10, 0x77, 0xef, 0xec, 0xc0, 0xb2, 0x4d, 0x02,
+    0xac, 0xe3, 0x3c, 0x1e, 0x52, 0xe2, 0xfb, 0x4b,
+    // number of key ids
+    0x00, 0x00, 0x00, 0x01,
+    // key id
+    0x60, 0x06, 0x1e, 0x01, 0x7e, 0x47, 0x7e, 0x87,
+    0x7e, 0x57, 0xd0, 0x0d, 0x1e, 0xd0, 0x0d, 0x1e,
+    // size of data, must be zero
+    0x00, 0x00, 0x00, 0x00
+};
+
+static const uint8_t kKeyRequestData[] = {
+    0x7b, 0x22, 0x6b, 0x69, 0x64,
+    0x73, 0x22, 0x3a, 0x5b, 0x22,
+    0x59, 0x41, 0x59, 0x65, 0x41,
+    0x58, 0x35, 0x48, 0x66, 0x6f,
+    0x64, 0x2b, 0x56, 0x39, 0x41,
+    0x4e, 0x48, 0x74, 0x41, 0x4e,
+    0x48, 0x67, 0x22, 0x5d, 0x2c,
+    0x22, 0x74, 0x79, 0x70, 0x65,
+    0x22, 0x3a, 0x22, 0x74, 0x65,
+    0x6d, 0x70, 0x6f, 0x72, 0x61,
+    0x72, 0x79, 0x22, 0x7d,
+};
+
+static const size_t kKeyRequestSize = sizeof(kKeyRequestData);
+
+// base 64 encoded JSON response string, must not contain padding character '='
+static const char kResponse[] = "{\"keys\":[{\"kty\":\"oct\"," \
+        "\"kid\":\"YAYeAX5Hfod+V9ANHtANHg\",\"k\":" \
+        "\"GoogleTestKeyBase64ggg\"}]}";
+
+static bool isUuidSizeValid(Uuid uuid) {
+    return (uuid.size() == kUuidSize);
+}
+
+static std::vector<uint8_t> jbyteArrayToVector(
+    JNIEnv* env, jbyteArray const &byteArray) {
+    uint8_t* buffer = reinterpret_cast<uint8_t*>(
+        env->GetByteArrayElements(byteArray, /*is_copy*/NULL));
+    std::vector<uint8_t> vector;
+    for (jsize i = 0; i < env->GetArrayLength(byteArray); ++i) {
+        vector.push_back(buffer[i]);
+    }
+    return vector;
+}
+
+static Uuid jbyteArrayToUuid(JNIEnv* env, jbyteArray const &uuid) {
+    Uuid juuid;
+    juuid.resize(0);
+    if (uuid != NULL) {
+        juuid = jbyteArrayToVector(env, uuid);
+    }
+    return juuid;
+}
+
+extern "C" jboolean Java_android_media_cts_NativeClearKeySystemTest_isCryptoSchemeSupportedNative(
+    JNIEnv* env, jclass /*clazz*/, jbyteArray uuid) {
+
+    if (NULL == uuid) {
+        jniThrowException(env, "java/lang/NullPointerException", "null uuid");
+        return JNI_FALSE;
+    }
+
+    Uuid juuid = jbyteArrayToUuid(env, uuid);
+    if (isUuidSizeValid(juuid)) {
+         return AMediaDrm_isCryptoSchemeSupported(&juuid[0], NULL);
+    } else {
+          jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
+                  "invalid UUID size, expected %u bytes", kUuidSize);
+    }
+    return JNI_FALSE;
+}
+
+void initPlaybackParams(JNIEnv* env, const jobject &playbackParams, PlaybackParams &params) {
+    params.surface = env->GetObjectField(
+        playbackParams, gFieldIds.surface);
+
+    params.mimeType = static_cast<jstring>(env->GetObjectField(
+        playbackParams, gFieldIds.mimeType));
+
+    params.audioUrl = static_cast<jstring>(env->GetObjectField(
+        playbackParams, gFieldIds.audioUrl));
+
+    params.videoUrl = static_cast<jstring>(env->GetObjectField(
+        playbackParams, gFieldIds.videoUrl));
+}
+
+extern "C" jboolean Java_android_media_cts_NativeClearKeySystemTest_testGetPropertyStringNative(
+    JNIEnv* env, jclass clazz, jbyteArray uuid,
+    jstring name, jobject outValue) {
+
+    if (NULL == uuid || NULL == name || NULL == outValue) {
+        jniThrowException(env, "java/lang/NullPointerException",
+                "One or more null input parameters");
+        return JNI_FALSE;
+    }
+
+    Uuid juuid = jbyteArrayToUuid(env, uuid);
+    if (!isUuidSizeValid(juuid)) {
+        jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
+                "invalid UUID size, expected %u bytes", kUuidSize);
+        return JNI_FALSE;
+    }
+
+    AMediaObjects aMediaObjects;
+    aMediaObjects.setDrm(AMediaDrm_createByUUID(&juuid[0]));
+    if (NULL == aMediaObjects.getDrm()) {
+        jniThrowException(env, "java/lang/RuntimeException", "null MediaDrm");
+        return JNI_FALSE;
+    }
+
+    const char *utf8_name = env->GetStringUTFChars(name, NULL);
+    const char *utf8_outValue = NULL;
+    media_status_t status = AMediaDrm_getPropertyString(aMediaObjects.getDrm(),
+            utf8_name, &utf8_outValue);
+    env->ReleaseStringUTFChars(name, utf8_name);
+
+    if (NULL != utf8_outValue) {
+        clazz = env->GetObjectClass(outValue);
+        jmethodID mId = env->GetMethodID (clazz, "append",
+                "(Ljava/lang/String;)Ljava/lang/StringBuffer;");
+        jstring outString = env->NewStringUTF(
+                static_cast<const char *>(utf8_outValue));
+        env->CallObjectMethod(outValue, mId, outString);
+    } else {
+        jniThrowExceptionFmt(env, "java/lang/RuntimeException",
+                "get property string returns %d", status);
+        return JNI_FALSE;
+    }
+    return JNI_TRUE;
+}
+
+extern "C" jboolean Java_android_media_cts_NativeClearKeySystemTest__testPsshNative(
+    JNIEnv* env, jclass /*clazz*/, jbyteArray uuid, jstring videoUrl) {
+
+    if (NULL == uuid || NULL == videoUrl) {
+        jniThrowException(env, "java/lang/NullPointerException",
+                "null uuid or null videoUrl");
+        return JNI_FALSE;
+    }
+
+    Uuid juuid = jbyteArrayToUuid(env, uuid);
+    if (!isUuidSizeValid(juuid)) {
+        jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
+                "invalid UUID size, expected %u bytes", kUuidSize);
+        return JNI_FALSE;
+    }
+
+    AMediaObjects aMediaObjects;
+    aMediaObjects.setVideoExtractor(AMediaExtractor_new());
+    const char* url = env->GetStringUTFChars(videoUrl, 0);
+    if (url) {
+        media_status_t status = AMediaExtractor_setDataSource(
+            aMediaObjects.getVideoExtractor(), url);
+        env->ReleaseStringUTFChars(videoUrl, url);
+
+        if (status != AMEDIA_OK) {
+            jniThrowExceptionFmt(env, "java/lang/RuntimeException",
+                    "set video data source error=%d", status);
+            return JNI_FALSE;
+        }
+    }
+
+    PsshInfo* psshInfo = AMediaExtractor_getPsshInfo(aMediaObjects.getVideoExtractor());
+    if (psshInfo == NULL) {
+        jniThrowException(env, "java/lang/RuntimeException", "null psshInfo");
+        return JNI_FALSE;
+    }
+
+    jboolean testResult = JNI_FALSE;
+    for (size_t i = 0; i < psshInfo->numentries; i++) {
+        PsshEntry *entry = &psshInfo->entries[i];
+
+        // We do not have clearkey content that contains ClearKey UUID in the
+        // pssh box. So we have to test if it has Widevine UUID instead.
+        // TODO: Replace kWidevineUuid with uuid when test content contains
+        // ClearKey UUID.
+        if (0 == memcmp(entry->uuid, kWidevineUuid, sizeof(entry->uuid))) {
+            aMediaObjects.setCrypto(
+                AMediaCrypto_new(entry->uuid, entry->data, entry->datalen));
+            if (aMediaObjects.getCrypto()) {
+                testResult = JNI_TRUE;
+            } else {
+                ALOGE("Failed to create media crypto=%zd", i);
+                testResult = JNI_FALSE;
+            }
+            break;
+        }
+    }
+    return testResult;
+}
+
+static bool isVideo(const char* mime) {
+    return !strncmp(mime, "video/", 6) ? true : false;
+}
+
+static bool isAudio(const char* mime) {
+    return !strncmp(mime, "audio/", 6) ? true : false;
+}
+
+static void addTrack(const AMediaFormat* format,
+        const char* mime, const AMediaCrypto* crypto,
+        const ANativeWindow* window, AMediaCodec** codec) {
+
+    *codec = AMediaCodec_createDecoderByType(mime);
+    if (codec == NULL) {
+        ALOGE("cannot create codec for %s", mime);
+        return;
+    }
+
+    AMediaCodec_configure(*codec, format,
+            const_cast<ANativeWindow*>(window),
+            const_cast<AMediaCrypto*>(crypto), 0);
+}
+
+static void addTracks(const AMediaExtractor* extractor,
+        const AMediaCrypto* crypto, const ANativeWindow* window,
+        AMediaCodec** codec) {
+    size_t numTracks = AMediaExtractor_getTrackCount(
+        const_cast<AMediaExtractor*>(extractor));
+    AMediaFormat* trackFormat = NULL;
+    for (size_t i = 0; i < numTracks; ++i) {
+        trackFormat = AMediaExtractor_getTrackFormat(
+            const_cast<AMediaExtractor*>(extractor), i);
+        if (trackFormat) {
+            ALOGV("track %zd format: %s", i,
+                    AMediaFormat_toString(trackFormat));
+
+            const char* mime = "";
+            if (!AMediaFormat_getString(
+                trackFormat, AMEDIAFORMAT_KEY_MIME, &mime)) {
+                ALOGE("no mime type");
+                AMediaFormat_delete(trackFormat);
+                return;
+            } else if (isAudio(mime) || isVideo(mime)) {
+                AMediaExtractor_selectTrack(
+                    const_cast<AMediaExtractor*>(extractor), i);
+                ALOGV("track %zd codec format: %s", i,
+                        AMediaFormat_toString(trackFormat));
+
+                addTrack(trackFormat, mime, crypto, window, codec);
+                AMediaCodec_start(*codec);
+                AMediaCodec_flush(*codec);
+                AMediaExtractor_seekTo(
+                    const_cast<AMediaExtractor*>(extractor), 0,
+                            AMEDIAEXTRACTOR_SEEK_PREVIOUS_SYNC);
+            }
+            AMediaFormat_delete(trackFormat);
+        }
+    }
+}
+
+static int64_t getSystemNanoTime() {
+    timespec now;
+    clock_gettime(CLOCK_MONOTONIC, &now);
+    return now.tv_sec * 1000000000LL + now.tv_nsec;
+}
+
+static void fillDecoder(AMediaCodec* codec, AMediaExtractor* extractor,
+        int64_t* presentationTimeUs, bool* eosReached) {
+    media_status_t status = AMEDIA_OK;
+
+    ssize_t bufferIndex = AMediaCodec_dequeueInputBuffer(codec, 2000);
+    if (bufferIndex >= 0) {
+        size_t bufsize;
+        uint8_t* buf = AMediaCodec_getInputBuffer(codec, bufferIndex, &bufsize);
+
+        int sampleSize = AMediaExtractor_readSampleData(extractor, buf, bufsize);
+        if (sampleSize < 0) {
+            sampleSize = 0;
+            *eosReached = true;
+        }
+
+        *presentationTimeUs = AMediaExtractor_getSampleTime(extractor);
+
+        AMediaCodecCryptoInfo *cryptoInfo =
+            AMediaExtractor_getSampleCryptoInfo(extractor);
+        if (cryptoInfo) {
+            status = AMediaCodec_queueSecureInputBuffer(
+                codec, bufferIndex, 0, cryptoInfo,
+                *presentationTimeUs,
+                *eosReached ? AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM : 0);
+            AMediaCodecCryptoInfo_delete(cryptoInfo);
+        } else {
+            status = AMediaCodec_queueInputBuffer(
+                codec, bufferIndex, 0, sampleSize,
+                *presentationTimeUs,
+                *eosReached ? AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM : 0);
+        }
+        AMediaExtractor_advance(extractor);
+    }
+}
+
+static bool drainDecoder(AMediaCodec* codec, int64_t presentationTimeUs,
+    int64_t* startTimeNano) {
+
+    AMediaCodecBufferInfo info;
+    ssize_t bufferIndex  = AMediaCodec_dequeueOutputBuffer(codec, &info, 0);
+    if (bufferIndex >= 0) {
+        if (info.flags & AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM) {
+            return true;  // eos reached
+        }
+
+        if (*startTimeNano < 0) {
+            *startTimeNano = getSystemNanoTime() - (presentationTimeUs * 1000);
+        }
+        int64_t delay = (*startTimeNano + presentationTimeUs * 1000) -
+                getSystemNanoTime();
+        if (delay > 0) {
+            usleep(delay / 1000);
+        }
+
+        AMediaCodec_releaseOutputBuffer(codec, bufferIndex, info.size != 0);
+    } else if (bufferIndex == AMEDIACODEC_INFO_OUTPUT_BUFFERS_CHANGED) {
+        ALOGV("output buffers changed");
+    } else if (bufferIndex == AMEDIACODEC_INFO_OUTPUT_FORMAT_CHANGED) {
+        AMediaFormat* format = AMediaCodec_getOutputFormat(codec);
+        ALOGV("format changed to: %s", AMediaFormat_toString(format));
+        AMediaFormat_delete(format);
+    } else if (bufferIndex == AMEDIACODEC_INFO_TRY_AGAIN_LATER) {
+         ALOGV("no output buffer right now");
+         usleep(20000);
+    } else {
+         ALOGV("unexpected info code: %zd", bufferIndex);
+    }
+    return false;
+}
+
+static jboolean playContent(JNIEnv* env, const AMediaObjects& aMediaObjects,
+        PlaybackParams& params, const AMediaDrmSessionId& sessionId, Uuid uuid) {
+
+    ANativeWindow *window = ANativeWindow_fromSurface(env, params.surface);
+    AMediaExtractor* audioExtractor = aMediaObjects.getAudioExtractor();
+    AMediaExtractor* videoExtractor = aMediaObjects.getVideoExtractor();
+
+    AMediaCodec* audioCodec = NULL;
+    AMediaCodec* videoCodec = NULL;
+    AMediaCrypto* crypto = NULL;
+
+    crypto = AMediaCrypto_new(&uuid[0], sessionId.ptr, sessionId.length);
+    if (crypto == NULL) {
+        jniThrowException(env, "java/lang/RuntimeException",
+                "failed to create crypto object");
+        return JNI_FALSE;
+    }
+
+    addTracks(audioExtractor, NULL, NULL, &audioCodec);
+    addTracks(videoExtractor, crypto, window, &videoCodec);
+
+    bool sawAudioInputEos = false;
+    bool sawAudioOutputEos = false;
+    bool sawVideoInputEos = false;
+    bool sawVideoOutputEos = false;
+    int64_t videoPresentationTimeUs = 0;
+    int64_t videoStartTimeNano = -1;
+    struct timespec timeSpec;
+    clock_gettime(CLOCK_MONOTONIC, &timeSpec);
+    time_t startTimeSec = timeSpec.tv_sec;
+
+    while (!sawAudioOutputEos && !sawVideoOutputEos) {
+        if (!sawVideoInputEos) {
+            fillDecoder(videoCodec, videoExtractor,
+                    &videoPresentationTimeUs, &sawVideoInputEos);
+        }
+
+        if (!sawAudioInputEos) {
+            // skip audio, still need to advance the audio extractor
+            AMediaExtractor_advance(audioExtractor);
+        }
+
+        if (!sawVideoOutputEos) {
+            sawVideoOutputEos = drainDecoder(videoCodec, videoPresentationTimeUs,
+                    &videoStartTimeNano);
+        }
+
+        clock_gettime(CLOCK_MONOTONIC, &timeSpec);
+        if (timeSpec.tv_sec >= static_cast<time_t>(
+            (startTimeSec + kPlayTimeSeconds))) {
+            // stop reading samples and drain the output buffers
+            sawAudioInputEos = sawVideoInputEos = true;
+            sawAudioOutputEos = true; // ignore audio
+        }
+    }
+
+    if (audioCodec) {
+        AMediaCodec_stop(audioCodec);
+        AMediaCodec_delete(audioCodec);
+    }
+    if (videoCodec) {
+        AMediaCodec_stop(videoCodec);
+        AMediaCodec_delete(videoCodec);
+    }
+
+    AMediaCrypto_delete(crypto);
+    ANativeWindow_release(window);
+    return JNI_TRUE;
+}
+
+static void listener(
+    AMediaDrm* /*drm*/, const AMediaDrmSessionId* /*sessionId*/,
+    AMediaDrmEventType eventType,
+    int /*extra*/, const uint8_t* /*data*/, size_t /*dataSize*/) {
+
+    switch (eventType) {
+        case EVENT_PROVISION_REQUIRED:
+            ALOGD("EVENT_PROVISION_REQUIRED received");
+            break;
+        case EVENT_KEY_REQUIRED:
+            ALOGD("EVENT_KEY_REQUIRED received");
+            break;
+        case EVENT_KEY_EXPIRED:
+            ALOGD("EVENT_KEY_EXPIRED received");
+            break;
+        case EVENT_VENDOR_DEFINED:
+            gGotVendorDefinedEvent = true;
+            ALOGD("EVENT_VENDOR_DEFINED received");
+            break;
+        default:
+            ALOGD("Unknown event received");
+            break;
+    }
+}
+
+extern "C" jboolean Java_android_media_cts_NativeClearKeySystemTest_testClearKeyPlaybackNative(
+    JNIEnv* env, jclass /*clazz*/, jbyteArray uuid, jobject playbackParams) {
+    if (NULL == uuid || NULL == playbackParams) {
+        jniThrowException(env, "java/lang/NullPointerException",
+                "null uuid or null playback parameters");
+        return JNI_FALSE;
+    }
+
+    Uuid juuid = jbyteArrayToUuid(env, uuid);
+    if (!isUuidSizeValid(juuid)) {
+        jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
+                "invalid UUID size, expected %u bytes", kUuidSize);
+        return JNI_FALSE;
+    }
+
+    PlaybackParams params;
+    initPlaybackParams(env, playbackParams, params);
+
+    AMediaObjects aMediaObjects;
+    media_status_t status = AMEDIA_OK;
+    aMediaObjects.setDrm(AMediaDrm_createByUUID(&juuid[0]));
+    if (NULL == aMediaObjects.getDrm()) {
+        jniThrowException(env, "java/lang/RuntimeException", "null MediaDrm");
+        return JNI_FALSE;
+    }
+
+    status = AMediaDrm_setOnEventListener(aMediaObjects.getDrm(), listener);
+    if (status != AMEDIA_OK) {
+        jniThrowException(env, "java/lang/RuntimeException",
+                "setOnEventListener failed");
+        return JNI_FALSE;
+    }
+
+    aMediaObjects.setAudioExtractor(AMediaExtractor_new());
+    const char* url = env->GetStringUTFChars(params.audioUrl, 0);
+    if (url) {
+        status = AMediaExtractor_setDataSource(
+            aMediaObjects.getAudioExtractor(), url);
+        env->ReleaseStringUTFChars(params.audioUrl, url);
+
+        if (status != AMEDIA_OK) {
+            jniThrowExceptionFmt(env, "java/lang/RuntimeException",
+                    "set audio data source error=%d", status);
+            return JNI_FALSE;
+        }
+    }
+
+    aMediaObjects.setVideoExtractor(AMediaExtractor_new());
+    url = env->GetStringUTFChars(params.videoUrl, 0);
+    if (url) {
+        status = AMediaExtractor_setDataSource(
+            aMediaObjects.getVideoExtractor(), url);
+        env->ReleaseStringUTFChars(params.videoUrl, url);
+
+        if (status != AMEDIA_OK) {
+            jniThrowExceptionFmt(env, "java/lang/RuntimeException",
+                    "set video data source error=%d", status);
+            return JNI_FALSE;
+        }
+    }
+
+    AMediaDrmSessionId sessionId;
+    status = AMediaDrm_openSession(aMediaObjects.getDrm(), &sessionId);
+    if (status != AMEDIA_OK) {
+        jniThrowException(env, "java/lang/RuntimeException",
+                "openSession failed");
+        return JNI_FALSE;
+    }
+
+    // Pointer to keyRequest memory, which remains until the next
+    // AMediaDrm_getKeyRequest call or until the drm object is released.
+    const uint8_t* keyRequest;
+    size_t keyRequestSize = 0;
+
+    // The server recognizes "video/mp4" but not "video/avc".
+    status = AMediaDrm_getKeyRequest(aMediaObjects.getDrm(), &sessionId,
+            kClearkeyPssh, sizeof(kClearkeyPssh),
+            "video/mp4" /*mimeType*/, KEY_TYPE_STREAMING,
+            NULL, 0, &keyRequest, &keyRequestSize);
+    if (status != AMEDIA_OK) {
+        jniThrowExceptionFmt(env, "java/lang/RuntimeException",
+                "getKeyRequest failed, error = %d", status);
+        AMediaDrm_closeSession(aMediaObjects.getDrm(), &sessionId);
+        return JNI_FALSE;
+    }
+
+    if (kKeyRequestSize != keyRequestSize) {
+        ALOGE("Invalid keyRequestSize %zd", keyRequestSize);
+        return JNI_FALSE;
+    }
+
+    if (memcmp(kKeyRequestData, keyRequest, kKeyRequestSize) != 0) {
+        ALOGE("Invalid keyRequest data is returned");
+        return JNI_FALSE;
+    }
+
+    AMediaDrmKeySetId keySetId;
+    gGotVendorDefinedEvent = false;
+    status = AMediaDrm_provideKeyResponse(aMediaObjects.getDrm(), &sessionId,
+            reinterpret_cast<const uint8_t*>(kResponse),
+            sizeof(kResponse), &keySetId);
+    if (status != AMEDIA_OK) {
+        jniThrowExceptionFmt(env, "java/lang/RuntimeException",
+                "provideKeyResponse failed, error = %d", status);
+        AMediaDrm_closeSession(aMediaObjects.getDrm(), &sessionId);
+        return JNI_FALSE;
+    }
+
+    // Check if the event listener has received the expected event sent by
+    // provideKeyResponse. This is for testing AMediaDrm_setOnEventListener().
+    const char *utf8_outValue = NULL;
+    status = AMediaDrm_getPropertyString(aMediaObjects.getDrm(),
+            "listenerTestSupport", &utf8_outValue);
+    if (status == AMEDIA_OK && NULL != utf8_outValue) {
+        std::string eventType(utf8_outValue);
+        if (eventType.compare("true") == 0) {
+            int count = 0;
+            while (!gGotVendorDefinedEvent && count++ < 5) {
+               // Prevents race condition when the event arrives late
+               usleep(1000);
+            }
+            if (!gGotVendorDefinedEvent) {
+                ALOGE("Event listener did not receive the expected event.");
+                jniThrowExceptionFmt(env, "java/lang/RuntimeException",
+                        "Event listener did not receive the expected event.");
+                AMediaDrm_closeSession(aMediaObjects.getDrm(), &sessionId);
+                return JNI_FALSE;
+           }
+        }
+    }
+
+    playContent(env, aMediaObjects, params, sessionId, juuid);
+
+    status = AMediaDrm_closeSession(aMediaObjects.getDrm(), &sessionId);
+    if (status != AMEDIA_OK) {
+        jniThrowException(env, "java/lang/RuntimeException",
+                "closeSession failed");
+        return JNI_FALSE;
+    }
+    return JNI_TRUE;
+}
+
+static JNINativeMethod gMethods[] = {
+    { "isCryptoSchemeSupportedNative", "([B)Z",
+            (void *)Java_android_media_cts_NativeClearKeySystemTest_isCryptoSchemeSupportedNative },
+
+    { "testClearKeyPlaybackNative",
+            "([BLandroid/media/cts/NativeClearKeySystemTest$PlaybackParams;)Z",
+            (void *)Java_android_media_cts_NativeClearKeySystemTest_testClearKeyPlaybackNative },
+
+    { "testGetPropertyStringNative",
+            "([BLjava/lang/String;Ljava/lang/StringBuffer;)Z",
+            (void *)Java_android_media_cts_NativeClearKeySystemTest_testGetPropertyStringNative },
+
+    { "testPsshNative", "([BLjava/lang/String;)Z",
+            (void *)Java_android_media_cts_NativeClearKeySystemTest__testPsshNative },
+};
+
+int register_android_media_cts_NativeClearKeySystemTest(JNIEnv* env) {
+    jint result = JNI_ERR;
+    jclass testClass =
+        env->FindClass("android/media/cts/NativeClearKeySystemTest");
+    if (testClass) {
+        jclass playbackParamsClass = env->FindClass(
+            "android/media/cts/NativeClearKeySystemTest$PlaybackParams");
+        if (playbackParamsClass) {
+            jclass surfaceClass =
+                env->FindClass("android/view/Surface");
+            if (surfaceClass) {
+                gFieldIds.surface = env->GetFieldID(playbackParamsClass,
+                        "surface", "Landroid/view/Surface;");
+            } else {
+                gFieldIds.surface = NULL;
+            }
+            gFieldIds.mimeType = env->GetFieldID(playbackParamsClass,
+                    "mimeType", "Ljava/lang/String;");
+            gFieldIds.audioUrl = env->GetFieldID(playbackParamsClass,
+                    "audioUrl", "Ljava/lang/String;");
+            gFieldIds.videoUrl = env->GetFieldID(playbackParamsClass,
+                    "videoUrl", "Ljava/lang/String;");
+        } else {
+            ALOGE("PlaybackParams class not found");
+        }
+
+    } else {
+        ALOGE("NativeClearKeySystemTest class not found");
+    }
+
+    result = env->RegisterNatives(testClass, gMethods,
+            sizeof(gMethods) / sizeof(JNINativeMethod));
+    return result;
+}
diff --git a/tests/tests/media/res/raw/segment000001.ts b/tests/tests/media/res/raw/segment000001.ts
new file mode 100644
index 0000000..fb112ec
--- /dev/null
+++ b/tests/tests/media/res/raw/segment000001.ts
Binary files differ
diff --git a/tests/tests/media/res/raw/segment000001_scrambled.ts b/tests/tests/media/res/raw/segment000001_scrambled.ts
new file mode 100644
index 0000000..4c7d368
--- /dev/null
+++ b/tests/tests/media/res/raw/segment000001_scrambled.ts
Binary files differ
diff --git a/tests/tests/media/src/android/media/cts/AudioManagerTest.java b/tests/tests/media/src/android/media/cts/AudioManagerTest.java
index b48db78..ba657bf 100644
--- a/tests/tests/media/src/android/media/cts/AudioManagerTest.java
+++ b/tests/tests/media/src/android/media/cts/AudioManagerTest.java
@@ -471,8 +471,7 @@
             int[] streams = {AudioManager.STREAM_ALARM,
                     AudioManager.STREAM_MUSIC,
                     AudioManager.STREAM_VOICE_CALL,
-                    AudioManager.STREAM_RING,
-                    AudioManager.STREAM_ACCESSIBILITY};
+                    AudioManager.STREAM_RING};
 
             mAudioManager.adjustVolume(ADJUST_RAISE, 0);
             // adjusting volume is aynchronous, wait before other volume checks
@@ -482,12 +481,12 @@
             Thread.sleep(ASYNC_TIMING_TOLERANCE_MS);
             int maxMusicVolume = mAudioManager.getStreamMaxVolume(STREAM_MUSIC);
 
-            for (int i = 0; i < streams.length; i++) {
+            for (int stream : streams) {
                 // set ringer mode to back normal to not interfere with volume tests
                 mAudioManager.setRingerMode(RINGER_MODE_NORMAL);
 
-                int maxVolume = mAudioManager.getStreamMaxVolume(streams[i]);
-                int minVolume = mAudioManager.getStreamMinVolume(streams[i]);
+                int maxVolume = mAudioManager.getStreamMaxVolume(stream);
+                int minVolume = mAudioManager.getStreamMinVolume(stream);
 
                 // validate min
                 assertTrue(String.format("minVolume(%d) must be >= 0", minVolume), minVolume >= 0);
@@ -495,14 +494,15 @@
                         maxVolume),
                         minVolume < maxVolume);
 
-                mAudioManager.setStreamVolume(streams[i], 1, 0);
+                mAudioManager.setStreamVolume(stream, 1, 0);
                 if (mUseFixedVolume) {
-                    assertEquals(maxVolume, mAudioManager.getStreamVolume(streams[i]));
+                    assertEquals(maxVolume, mAudioManager.getStreamVolume(stream));
                     continue;
                 }
-                assertEquals(1, mAudioManager.getStreamVolume(streams[i]));
+                assertEquals(String.format("stream=%d", stream),
+                             1, mAudioManager.getStreamVolume(stream));
 
-                if (streams[i] == AudioManager.STREAM_MUSIC && mAudioManager.isWiredHeadsetOn()) {
+                if (stream == AudioManager.STREAM_MUSIC && mAudioManager.isWiredHeadsetOn()) {
                     // due to new regulations, music sent over a wired headset may be volume limited
                     // until the user explicitly increases the limit, so we can't rely on being able
                     // to set the volume to getStreamMaxVolume(). Instead, determine the current limit
@@ -512,52 +512,52 @@
                     int prevvol = 0;
                     do {
                         prevvol = curvol;
-                        mAudioManager.adjustStreamVolume(streams[i], ADJUST_RAISE, 0);
-                        curvol = mAudioManager.getStreamVolume(streams[i]);
+                        mAudioManager.adjustStreamVolume(stream, ADJUST_RAISE, 0);
+                        curvol = mAudioManager.getStreamVolume(stream);
                     } while (curvol != prevvol);
                     maxVolume = maxMusicVolume = curvol;
                 }
-                mAudioManager.setStreamVolume(streams[i], maxVolume, 0);
-                mAudioManager.adjustStreamVolume(streams[i], ADJUST_RAISE, 0);
-                assertEquals(maxVolume, mAudioManager.getStreamVolume(streams[i]));
+                mAudioManager.setStreamVolume(stream, maxVolume, 0);
+                mAudioManager.adjustStreamVolume(stream, ADJUST_RAISE, 0);
+                assertEquals(maxVolume, mAudioManager.getStreamVolume(stream));
 
-                volumeDelta = getVolumeDelta(mAudioManager.getStreamVolume(streams[i]));
-                mAudioManager.adjustSuggestedStreamVolume(ADJUST_LOWER, streams[i], 0);
+                volumeDelta = getVolumeDelta(mAudioManager.getStreamVolume(stream));
+                mAudioManager.adjustSuggestedStreamVolume(ADJUST_LOWER, stream, 0);
                 Thread.sleep(ASYNC_TIMING_TOLERANCE_MS);
-                assertEquals(maxVolume - volumeDelta, mAudioManager.getStreamVolume(streams[i]));
+                assertEquals(maxVolume - volumeDelta, mAudioManager.getStreamVolume(stream));
 
                 // volume lower
-                mAudioManager.setStreamVolume(streams[i], maxVolume, 0);
-                volume = mAudioManager.getStreamVolume(streams[i]);
+                mAudioManager.setStreamVolume(stream, maxVolume, 0);
+                volume = mAudioManager.getStreamVolume(stream);
                 while (volume > minVolume) {
-                    volumeDelta = getVolumeDelta(mAudioManager.getStreamVolume(streams[i]));
-                    mAudioManager.adjustStreamVolume(streams[i], ADJUST_LOWER, 0);
+                    volumeDelta = getVolumeDelta(mAudioManager.getStreamVolume(stream));
+                    mAudioManager.adjustStreamVolume(stream, ADJUST_LOWER, 0);
                     assertEquals(Math.max(0, volume - volumeDelta),
-                            mAudioManager.getStreamVolume(streams[i]));
-                    volume = mAudioManager.getStreamVolume(streams[i]);
+                            mAudioManager.getStreamVolume(stream));
+                    volume = mAudioManager.getStreamVolume(stream);
                 }
 
-                mAudioManager.adjustStreamVolume(streams[i], ADJUST_SAME, 0);
+                mAudioManager.adjustStreamVolume(stream, ADJUST_SAME, 0);
 
                 // volume raise
-                mAudioManager.setStreamVolume(streams[i], 1, 0);
-                volume = mAudioManager.getStreamVolume(streams[i]);
+                mAudioManager.setStreamVolume(stream, 1, 0);
+                volume = mAudioManager.getStreamVolume(stream);
                 while (volume < maxVolume) {
-                    volumeDelta = getVolumeDelta(mAudioManager.getStreamVolume(streams[i]));
-                    mAudioManager.adjustStreamVolume(streams[i], ADJUST_RAISE, 0);
+                    volumeDelta = getVolumeDelta(mAudioManager.getStreamVolume(stream));
+                    mAudioManager.adjustStreamVolume(stream, ADJUST_RAISE, 0);
                     assertEquals(Math.min(volume + volumeDelta, maxVolume),
-                            mAudioManager.getStreamVolume(streams[i]));
-                    volume = mAudioManager.getStreamVolume(streams[i]);
+                            mAudioManager.getStreamVolume(stream));
+                    volume = mAudioManager.getStreamVolume(stream);
                 }
 
                 // volume same
-                mAudioManager.setStreamVolume(streams[i], maxVolume, 0);
+                mAudioManager.setStreamVolume(stream, maxVolume, 0);
                 for (int k = 0; k < maxVolume; k++) {
-                    mAudioManager.adjustStreamVolume(streams[i], ADJUST_SAME, 0);
-                    assertEquals(maxVolume, mAudioManager.getStreamVolume(streams[i]));
+                    mAudioManager.adjustStreamVolume(stream, ADJUST_SAME, 0);
+                    assertEquals(maxVolume, mAudioManager.getStreamVolume(stream));
                 }
 
-                mAudioManager.setStreamVolume(streams[i], maxVolume, 0);
+                mAudioManager.setStreamVolume(stream, maxVolume, 0);
             }
 
             if (mUseFixedVolume) {
@@ -660,18 +660,18 @@
                 AudioManager.STREAM_NOTIFICATION,
                 AudioManager.STREAM_SYSTEM};
         if (mUseFixedVolume) {
-            for (int i = 0; i < streams.length; i++) {
-                mAudioManager.adjustStreamVolume(streams[i], AudioManager.ADJUST_MUTE, 0);
+            for (int stream : streams) {
+                mAudioManager.adjustStreamVolume(stream, AudioManager.ADJUST_MUTE, 0);
                 assertFalse("Muting should not affect a fixed volume device.",
-                        mAudioManager.isStreamMute(streams[i]));
+                        mAudioManager.isStreamMute(stream));
 
-                mAudioManager.adjustStreamVolume(streams[i], AudioManager.ADJUST_TOGGLE_MUTE, 0);
+                mAudioManager.adjustStreamVolume(stream, AudioManager.ADJUST_TOGGLE_MUTE, 0);
                 assertFalse("Toggling mute should not affect a fixed volume device.",
-                        mAudioManager.isStreamMute(streams[i]));
+                        mAudioManager.isStreamMute(stream));
 
-                mAudioManager.setStreamMute(streams[i], true);
+                mAudioManager.setStreamMute(stream, true);
                 assertFalse("Muting should not affect a fixed volume device.",
-                        mAudioManager.isStreamMute(streams[i]));
+                        mAudioManager.isStreamMute(stream));
             }
         }
     }
@@ -689,16 +689,16 @@
             Utils.toggleNotificationPolicyAccess(
                     mContext.getPackageName(), getInstrumentation(), false);
             // Verify streams cannot be unmuted without policy access.
-            for (int i = 0; i < streams.length; i++) {
+            for (int stream : streams) {
                 try {
-                    mAudioManager.adjustStreamVolume(streams[i], AudioManager.ADJUST_UNMUTE, 0);
+                    mAudioManager.adjustStreamVolume(stream, AudioManager.ADJUST_UNMUTE, 0);
                     assertEquals("Apps without Notification policy access can't change ringer mode",
                             RINGER_MODE_SILENT, mAudioManager.getRingerMode());
                 } catch (SecurityException e) {
                 }
 
                 try {
-                    mAudioManager.adjustStreamVolume(streams[i], AudioManager.ADJUST_TOGGLE_MUTE,
+                    mAudioManager.adjustStreamVolume(stream, AudioManager.ADJUST_TOGGLE_MUTE,
                             0);
                     assertEquals("Apps without Notification policy access can't change ringer mode",
                             RINGER_MODE_SILENT, mAudioManager.getRingerMode());
@@ -706,7 +706,7 @@
                 }
 
                 try {
-                    mAudioManager.setStreamMute(streams[i], false);
+                    mAudioManager.setStreamMute(stream, false);
                     assertEquals("Apps without Notification policy access can't change ringer mode",
                             RINGER_MODE_SILENT, mAudioManager.getRingerMode());
                 } catch (SecurityException e) {
@@ -717,37 +717,37 @@
             Utils.toggleNotificationPolicyAccess(
                     mContext.getPackageName(), getInstrumentation(), true);
             mAudioManager.setRingerMode(RINGER_MODE_NORMAL);
-            for (int i = 0; i < streams.length; i++) {
+            for (int stream : streams) {
                 // ensure each stream is on and turned up.
-                mAudioManager.setStreamVolume(streams[i],
-                        mAudioManager.getStreamMaxVolume(streams[i]),
+                mAudioManager.setStreamVolume(stream,
+                        mAudioManager.getStreamMaxVolume(stream),
                         0);
 
                 Utils.toggleNotificationPolicyAccess(
                         mContext.getPackageName(), getInstrumentation(), false);
                 try {
-                    mAudioManager.adjustStreamVolume(streams[i], AudioManager.ADJUST_MUTE, 0);
+                    mAudioManager.adjustStreamVolume(stream, AudioManager.ADJUST_MUTE, 0);
                     assertEquals("Apps without Notification policy access can't change ringer mode",
                             RINGER_MODE_NORMAL, mAudioManager.getRingerMode());
                 } catch (SecurityException e) {
                 }
                 try {
                     mAudioManager.adjustStreamVolume(
-                            streams[i], AudioManager.ADJUST_TOGGLE_MUTE, 0);
+                            stream, AudioManager.ADJUST_TOGGLE_MUTE, 0);
                     assertEquals("Apps without Notification policy access can't change ringer mode",
                             RINGER_MODE_NORMAL, mAudioManager.getRingerMode());
                 } catch (SecurityException e) {
                 }
 
                 try {
-                    mAudioManager.setStreamMute(streams[i], true);
+                    mAudioManager.setStreamMute(stream, true);
                     assertEquals("Apps without Notification policy access can't change ringer mode",
                             RINGER_MODE_NORMAL, mAudioManager.getRingerMode());
                 } catch (SecurityException e) {
                 }
                 Utils.toggleNotificationPolicyAccess(
                         mContext.getPackageName(), getInstrumentation(), true);
-                testStreamMuting(streams[i]);
+                testStreamMuting(stream);
             }
         } finally {
             Utils.toggleNotificationPolicyAccess(
@@ -780,25 +780,25 @@
             mAudioManager.setRingerMode(RINGER_MODE_NORMAL);
             Utils.toggleNotificationPolicyAccess(
                     mContext.getPackageName(), getInstrumentation(), false);
-            for (int i = 0; i < streams.length; i++) {
+            for (int stream : streams) {
                 // ensure each stream is on and turned up.
-                mAudioManager.setStreamVolume(streams[i],
-                        mAudioManager.getStreamMaxVolume(streams[i]),
+                mAudioManager.setStreamVolume(stream,
+                        mAudioManager.getStreamMaxVolume(stream),
                         0);
-                if (((1 << streams[i]) & muteAffectedStreams) == 0) {
-                    mAudioManager.adjustStreamVolume(streams[i], AudioManager.ADJUST_MUTE, 0);
-                    assertFalse("Stream " + streams[i] + " should not be affected by mute.",
-                            mAudioManager.isStreamMute(streams[i]));
-                    mAudioManager.setStreamMute(streams[i], true);
-                    assertFalse("Stream " + streams[i] + " should not be affected by mute.",
-                            mAudioManager.isStreamMute(streams[i]));
-                    mAudioManager.adjustStreamVolume(streams[i], AudioManager.ADJUST_TOGGLE_MUTE,
+                if (((1 << stream) & muteAffectedStreams) == 0) {
+                    mAudioManager.adjustStreamVolume(stream, AudioManager.ADJUST_MUTE, 0);
+                    assertFalse("Stream " + stream + " should not be affected by mute.",
+                            mAudioManager.isStreamMute(stream));
+                    mAudioManager.setStreamMute(stream, true);
+                    assertFalse("Stream " + stream + " should not be affected by mute.",
+                            mAudioManager.isStreamMute(stream));
+                    mAudioManager.adjustStreamVolume(stream, AudioManager.ADJUST_TOGGLE_MUTE,
                             0);
-                    assertFalse("Stream " + streams[i] + " should not be affected by mute.",
-                            mAudioManager.isStreamMute(streams[i]));
+                    assertFalse("Stream " + stream + " should not be affected by mute.",
+                            mAudioManager.isStreamMute(stream));
                     continue;
                 }
-                testStreamMuting(streams[i]);
+                testStreamMuting(stream);
             }
         } finally {
             Utils.toggleNotificationPolicyAccess(
diff --git a/tests/tests/media/src/android/media/cts/ClearKeySystemTest.java b/tests/tests/media/src/android/media/cts/ClearKeySystemTest.java
index 69f3ee8..5ea33ba 100644
--- a/tests/tests/media/src/android/media/cts/ClearKeySystemTest.java
+++ b/tests/tests/media/src/android/media/cts/ClearKeySystemTest.java
@@ -66,8 +66,10 @@
     private static final int SLEEP_TIME_MS = 1000;
     private static final int VIDEO_WIDTH_CENC = 1280;
     private static final int VIDEO_HEIGHT_CENC = 720;
-    private static final int VIDEO_WIDTH_WEBM = 320;
-    private static final int VIDEO_HEIGHT_WEBM = 180;
+    private static final int VIDEO_WIDTH_WEBM = 352;
+    private static final int VIDEO_HEIGHT_WEBM = 288;
+    private static final int VIDEO_WIDTH_MPEG2TS = 320;
+    private static final int VIDEO_HEIGHT_MPEG2TS = 240;
     private static final long PLAY_TIME_MS = TimeUnit.MILLISECONDS.convert(25, TimeUnit.SECONDS);
     private static final String MIME_VIDEO_AVC = MediaFormat.MIMETYPE_VIDEO_AVC;
     private static final String MIME_VIDEO_VP8 = MediaFormat.MIMETYPE_VIDEO_VP8;
@@ -78,6 +80,10 @@
             "http://yt-dash-mse-test.commondatastorage.googleapis.com/media/car_cenc-20120827-88.mp4");
     private static final Uri WEBM_URL = Uri.parse(
             "android.resource://android.media.cts/" + R.raw.video_320x240_webm_vp8_800kbps_30fps_vorbis_stereo_128kbps_44100hz_crypt);
+    private static final Uri MPEG2TS_SCRAMBLED_URL = Uri.parse(
+            "android.resource://android.media.cts/" + R.raw.segment000001_scrambled);
+    private static final Uri MPEG2TS_CLEAR_URL = Uri.parse(
+            "android.resource://android.media.cts/" + R.raw.segment000001);
 
     private static final UUID CLEARKEY_SCHEME_UUID =
             new UUID(0x1077efecc0b24d02L, 0xace33c1e52e2fb4bL);
@@ -327,15 +333,20 @@
             String initDataType, byte[][] clearKeys,
             Uri audioUrl, boolean audioEncrypted,
             Uri videoUrl, boolean videoEncrypted,
-            int videoWidth, int videoHeight) throws Exception {
-        MediaDrm drm = startDrm(clearKeys, initDataType);
-        if (null == drm) {
-            throw new Error("Failed to create drm.");
-        }
+            int videoWidth, int videoHeight, boolean scrambled) throws Exception {
+        MediaDrm drm = null;
+        mSessionId = null;
+        if (!scrambled) {
+            drm = startDrm(clearKeys, initDataType);
+            if (null == drm) {
+                throw new Error("Failed to create drm.");
+            }
 
-        if (!drm.isCryptoSchemeSupported(CLEARKEY_SCHEME_UUID)) {
-            stopDrm(drm);
-            throw new Error("Crypto scheme is not supported.");
+            if (!drm.isCryptoSchemeSupported(CLEARKEY_SCHEME_UUID)) {
+                stopDrm(drm);
+                throw new Error("Crypto scheme is not supported.");
+            }
+            mSessionId = openSession(drm);
         }
 
         if (!isResolutionSupported(videoMime, videoFeatures, videoWidth, videoHeight)) {
@@ -364,19 +375,19 @@
         }
         connectionStatus.testConnection(videoUrl);
 
-        mSessionId = openSession(drm);
         mMediaCodecPlayer = new MediaCodecClearKeyPlayer(
                 getActivity().getSurfaceHolder(),
-                mSessionId,
+                mSessionId, scrambled,
                 mContext.getResources());
 
         mMediaCodecPlayer.setAudioDataSource(audioUrl, null, audioEncrypted);
         mMediaCodecPlayer.setVideoDataSource(videoUrl, null, videoEncrypted);
         mMediaCodecPlayer.start();
         mMediaCodecPlayer.prepare();
-        mDrmInitData = mMediaCodecPlayer.getDrmInitData();
-
-        getKeys(mDrm, initDataType, mSessionId, mDrmInitData, clearKeys);
+        if (!scrambled) {
+            mDrmInitData = mMediaCodecPlayer.getDrmInitData();
+            getKeys(mDrm, initDataType, mSessionId, mDrmInitData, clearKeys);
+        }
         // starts video playback
         mMediaCodecPlayer.startThread();
 
@@ -392,8 +403,10 @@
 
         Log.d(TAG, "playVideo player.reset()");
         mMediaCodecPlayer.reset();
-        closeSession(drm, mSessionId);
-        stopDrm(drm);
+        if (!scrambled) {
+            closeSession(drm, mSessionId);
+            stopDrm(drm);
+        }
     }
 
     public void testClearKeyPlaybackCenc() throws Exception {
@@ -403,7 +416,7 @@
             "cenc", new byte[][] { CLEAR_KEY_CENC },
             CENC_AUDIO_URL, false,
             CENC_VIDEO_URL, true,
-            VIDEO_WIDTH_CENC, VIDEO_HEIGHT_CENC);
+            VIDEO_WIDTH_CENC, VIDEO_HEIGHT_CENC, false);
     }
 
     public void testClearKeyPlaybackWebm() throws Exception {
@@ -412,6 +425,24 @@
             "webm", new byte[][] { CLEAR_KEY_WEBM },
             WEBM_URL, true,
             WEBM_URL, true,
-            VIDEO_WIDTH_WEBM, VIDEO_WIDTH_WEBM);
+            VIDEO_WIDTH_WEBM, VIDEO_HEIGHT_WEBM, false);
+    }
+
+    public void testClearKeyPlaybackMpeg2ts() throws Exception {
+        testClearKeyPlayback(
+            MIME_VIDEO_AVC, new String[0],
+            "mpeg2ts", null,
+            MPEG2TS_SCRAMBLED_URL, false,
+            MPEG2TS_SCRAMBLED_URL, false,
+            VIDEO_WIDTH_MPEG2TS, VIDEO_HEIGHT_MPEG2TS, true);
+    }
+
+    public void testPlaybackMpeg2ts() throws Exception {
+        testClearKeyPlayback(
+            MIME_VIDEO_AVC, new String[0],
+            "mpeg2ts", null,
+            MPEG2TS_CLEAR_URL, false,
+            MPEG2TS_CLEAR_URL, false,
+            VIDEO_WIDTH_MPEG2TS, VIDEO_HEIGHT_MPEG2TS, false);
     }
 }
diff --git a/tests/tests/media/src/android/media/cts/CodecState.java b/tests/tests/media/src/android/media/cts/CodecState.java
index 51be918..12ebeae 100644
--- a/tests/tests/media/src/android/media/cts/CodecState.java
+++ b/tests/tests/media/src/android/media/cts/CodecState.java
@@ -322,12 +322,10 @@
 
             mSawOutputEOS = true;
 
-            // We need to stop the audio track so that all audio frames are played
-            // and the video codec can consume all of its data.
-            // After audio track stop, getAudioTimeUs will return 0.
-            if (mAudioTrack != null) {
-                mAudioTrack.stop();
-            }
+            // Do not stop audio track here. Video presentation may not finish
+            // yet, stopping the auido track now would result in getAudioTimeUs
+            // returning 0 and prevent video samples from being presented.
+            // We stop the audio track before the playback thread exits.
             return false;
         }
 
@@ -390,4 +388,10 @@
             mAudioTrack.process();
         }
     }
+
+    public void stop() {
+        if (mAudioTrack != null) {
+            mAudioTrack.stop();
+        }
+    }
 }
diff --git a/tests/tests/media/src/android/media/cts/MediaCodecClearKeyPlayer.java b/tests/tests/media/src/android/media/cts/MediaCodecClearKeyPlayer.java
index 1d95463..253a5ab 100644
--- a/tests/tests/media/src/android/media/cts/MediaCodecClearKeyPlayer.java
+++ b/tests/tests/media/src/android/media/cts/MediaCodecClearKeyPlayer.java
@@ -19,11 +19,15 @@
 import android.content.res.AssetFileDescriptor;
 import android.media.AudioManager;
 import android.media.DrmInitData;
+import android.media.MediaCas;
+import android.media.MediaCasException;
+import android.media.MediaCasException.UnsupportedCasException;
 import android.media.MediaCodec;
 import android.media.MediaCodecInfo;
 import android.media.MediaCodecList;
 import android.media.MediaCrypto;
 import android.media.MediaCryptoException;
+import android.media.MediaDescrambler;
 import android.media.MediaExtractor;
 import android.media.MediaFormat;
 import android.net.Uri;
@@ -60,6 +64,7 @@
     private boolean mEncryptedVideo;
     private volatile boolean mThreadStarted = false;
     private byte[] mSessionId;
+    private boolean mScrambled;
     private CodecState mAudioTrackState;
     private int mMediaFormatHeight;
     private int mMediaFormatWidth;
@@ -72,6 +77,8 @@
     private Map<String, String> mVideoHeaders;
     private Map<UUID, byte[]> mPsshInitData;
     private MediaCrypto mCrypto;
+    private MediaCas mMediaCas;
+    private MediaDescrambler mDescrambler;
     private MediaExtractor mAudioExtractor;
     private MediaExtractor mVideoExtractor;
     private SurfaceHolder mSurfaceHolder;
@@ -91,6 +98,19 @@
             "00000000"            // Size of Data, must be zero
             );
 
+    // ClearKey CAS/Descrambler test provision string
+    private static final String sProvisionStr =
+            "{                                                   " +
+            "  \"id\": 21140844,                                 " +
+            "  \"name\": \"Test Title\",                         " +
+            "  \"lowercase_organization_name\": \"Android\",     " +
+            "  \"asset_key\": {                                  " +
+            "  \"encryption_key\": \"nezAr3CHFrmBR9R8Tedotw==\"  " +
+            "  },                                                " +
+            "  \"cas_type\": 1,                                  " +
+            "  \"track_types\": [ ]                              " +
+            "}                                                   " ;
+
     /**
      * Convert a hex string into byte array.
      */
@@ -108,8 +128,9 @@
      * Media player class to stream CENC content using MediaCodec class.
      */
     public MediaCodecClearKeyPlayer(
-            SurfaceHolder holder, byte[] sessionId, Resources resources) {
+            SurfaceHolder holder, byte[] sessionId, boolean scrambled, Resources resources) {
         mSessionId = sessionId;
+        mScrambled = scrambled;
         mSurfaceHolder = holder;
         mResources = resources;
         mState = STATE_IDLE;
@@ -127,6 +148,9 @@
                         Log.d(TAG, "Thread interrupted");
                     }
                 }
+                if (mAudioTrackState != null) {
+                    mAudioTrackState.stop();
+                }
             }
         });
     }
@@ -206,6 +230,8 @@
     private void prepareVideo() throws IOException {
         boolean hasVideo = false;
 
+        android.media.DrmInitData drmInitData = mVideoExtractor.getDrmInitData();
+
         for (int i = mVideoExtractor.getTrackCount(); i-- > 0;) {
             MediaFormat format = mVideoExtractor.getTrackFormat(i);
             String mime = format.getString(MediaFormat.KEY_MIME);
@@ -218,6 +244,14 @@
             Log.d(TAG, "video track #" + i + " " + format + " " + mime +
                   " Width:" + mMediaFormatWidth + ", Height:" + mMediaFormatHeight);
 
+            if (mScrambled && drmInitData != null && mime.startsWith("video/")) {
+                android.media.DrmInitData.SchemeInitData schemeInitData =
+                        drmInitData.get(new UUID(0, i));
+                if (schemeInitData != null) {
+                    mDescrambler.setMediaCasSession(schemeInitData.data);
+                }
+            }
+
             if (!hasVideo) {
                 mVideoExtractor.selectTrack(i);
                 addTrack(i, format, mEncryptedVideo);
@@ -239,11 +273,10 @@
                 }
             }
         }
-        return;
     }
 
     private void setDataSource(MediaExtractor extractor, Uri uri, Map<String, String> headers)
-            throws IOException {
+            throws IOException, MediaCasException {
         String scheme = uri.getScheme();
         if (scheme.startsWith("http")) {
             extractor.setDataSource(uri.toString(), headers);
@@ -256,26 +289,30 @@
         }
     }
 
-    public void prepare() throws IOException, MediaCryptoException {
-        if (null == mAudioExtractor) {
-            mAudioExtractor = new MediaExtractor();
-            if (null == mAudioExtractor) {
-                Log.e(TAG, "Cannot create Audio extractor.");
-                return;
+    private void initCasAndDescrambler(MediaExtractor extractor) throws MediaCasException {
+        int trackCount = extractor.getTrackCount();
+        android.media.DrmInitData drmInitData = extractor.getDrmInitData();
+        for (int trackId = 0; trackId < trackCount; trackId++) {
+            android.media.MediaFormat format = extractor.getTrackFormat(trackId);
+            String mime = format.getString(android.media.MediaFormat.KEY_MIME);
+            Log.d(TAG, "track "+ trackId + ": " + mime);
+            if ("video/scrambled".equals(mime) || "audio/scrambled".equals(mime)) {
+                if (drmInitData == null) {
+                    throw new IllegalArgumentException("found scrambled track without drmInitData!");
+                }
+                android.media.DrmInitData.SchemeInitData schemeInitData =
+                        drmInitData.get(new UUID(0, trackId));
+                int CA_system_id = (schemeInitData.data[0] & 0xff)
+                                | ((schemeInitData.data[1] & 0xff) << 8);
+                mMediaCas = new MediaCas(CA_system_id);
+                mDescrambler = new MediaDescrambler(CA_system_id);
+                mMediaCas.provision(sProvisionStr);
+                extractor.setMediaCas(mMediaCas);
             }
         }
+    }
 
-        if (null == mVideoExtractor){
-            mVideoExtractor = new MediaExtractor();
-            if (null == mVideoExtractor) {
-                Log.e(TAG, "Cannot create Video extractor.");
-                return;
-            }
-        }
-
-        setDataSource(mAudioExtractor, mAudioUri, mAudioHeaders);
-        setDataSource(mVideoExtractor, mVideoUri, mVideoHeaders);
-
+    public void prepare() throws IOException, MediaCryptoException, MediaCasException {
         if (null == mCrypto && (mEncryptedVideo || mEncryptedAudio)) {
             try {
                 byte[] initData = new byte[0];
@@ -288,8 +325,29 @@
             mCrypto.setMediaDrmSession(mSessionId);
         } else {
             reset();
-            mCrypto.release();
-            mCrypto = null;
+        }
+
+        if (null == mAudioExtractor) {
+            mAudioExtractor = new MediaExtractor();
+            if (null == mAudioExtractor) {
+                Log.e(TAG, "Cannot create Audio extractor.");
+                return;
+            }
+        }
+        setDataSource(mAudioExtractor, mAudioUri, mAudioHeaders);
+
+        if (mScrambled) {
+            initCasAndDescrambler(mAudioExtractor);
+            mVideoExtractor = mAudioExtractor;
+        } else {
+            if (null == mVideoExtractor){
+                mVideoExtractor = new MediaExtractor();
+                if (null == mVideoExtractor) {
+                    Log.e(TAG, "Cannot create Video extractor.");
+                    return;
+                }
+            }
+            setDataSource(mVideoExtractor, mVideoUri, mVideoHeaders);
         }
 
         if (null == mVideoCodecStates) {
@@ -325,11 +383,19 @@
             codec = MediaCodec.createDecoderByType(mime);
         }
 
-        codec.configure(
-                format,
-                isVideo ? mSurfaceHolder.getSurface() : null,
-                mCrypto,
-                0);
+        if (!mScrambled) {
+            codec.configure(
+                    format,
+                    isVideo ? mSurfaceHolder.getSurface() : null,
+                    mCrypto,
+                    0);
+        } else {
+            codec.configure(
+                    format,
+                    isVideo ? mSurfaceHolder.getSurface() : null,
+                    0,
+                    isVideo ? mDescrambler : null);
+        }
 
         CodecState state;
         if (isVideo) {
@@ -483,6 +549,16 @@
             mCrypto = null;
         }
 
+        if (mMediaCas != null) {
+            mMediaCas.release();
+            mMediaCas = null;
+        }
+
+        if (mDescrambler != null) {
+            mDescrambler.release();
+            mDescrambler = null;
+        }
+
         mDurationUs = -1;
         mState = STATE_IDLE;
     }
diff --git a/tests/tests/media/src/android/media/cts/NativeClearKeySystemTest.java b/tests/tests/media/src/android/media/cts/NativeClearKeySystemTest.java
new file mode 100644
index 0000000..59c2e8e
--- /dev/null
+++ b/tests/tests/media/src/android/media/cts/NativeClearKeySystemTest.java
@@ -0,0 +1,219 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.media.cts;
+
+import static org.junit.Assert.assertThat;
+import static org.junit.matchers.JUnitMatchers.containsString;
+
+import android.net.Uri;
+import android.util.Log;
+import android.view.Surface;
+import android.view.SurfaceHolder;
+
+import com.android.compatibility.common.util.MediaUtils;
+import com.google.android.collect.Lists;
+
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.UUID;
+
+/**
+ * Tests MediaDrm NDK APIs. ClearKey system uses a subset of NDK APIs,
+ * this test only tests the APIs that are supported by ClearKey system.
+ */
+public class NativeClearKeySystemTest extends MediaPlayerTestBase {
+    private static final String TAG = NativeClearKeySystemTest.class.getSimpleName();
+
+    private static final int CONNECTION_RETRIES = 10;
+    private static final int VIDEO_WIDTH_CENC = 1280;
+    private static final int VIDEO_HEIGHT_CENC = 720;
+    private static final String ISO_BMFF_VIDEO_MIME_TYPE = "video/avc";
+    private static final String ISO_BMFF_AUDIO_MIME_TYPE = "audio/avc";
+    private static final Uri CENC_AUDIO_URL = Uri.parse(
+        "http://yt-dash-mse-test.commondatastorage.googleapis.com/media/" +
+        "car_cenc-20120827-8c.mp4");
+    private static final Uri CENC_CLEARKEY_VIDEO_URL = Uri.parse(
+        "http://yt-dash-mse-test.commondatastorage.googleapis.com/media/" +
+        "car_cenc-20120827-88.mp4");
+
+    private static final int UUID_BYTE_SIZE = 16;
+    private static final UUID CLEARKEY_SCHEME_UUID =
+            new UUID(0x1077efecc0b24d02L, 0xace33c1e52e2fb4bL);
+    private static final UUID BAD_SCHEME_UUID =
+            new UUID(0xffffffffffffffffL, 0xffffffffffffffffL);
+    private MediaCodecClearKeyPlayer mMediaCodecPlayer;
+
+    static {
+        try {
+            System.loadLibrary("ctsmediadrm_jni");
+        } catch (UnsatisfiedLinkError e) {
+            Log.e(TAG, "NativeClearKeySystemTest: Error loading JNI library");
+            e.printStackTrace();
+        }
+        try {
+            System.loadLibrary("mediandk");
+        } catch (UnsatisfiedLinkError e) {
+            Log.e(TAG, "NativeClearKeySystemTest: Error loading JNI library");
+            e.printStackTrace();
+        }
+    }
+
+    public static class PlaybackParams {
+        public Surface surface;
+        public String mimeType;
+        public String audioUrl;
+        public String videoUrl;
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        if (false == deviceHasMediaDrm()) {
+            tearDown();
+        }
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        super.tearDown();
+    }
+
+    private boolean deviceHasMediaDrm() {
+        // ClearKey is introduced after KitKat.
+        if (android.os.Build.VERSION.SDK_INT <= android.os.Build.VERSION_CODES.KITKAT) {
+            Log.i(TAG, "This test is designed to work after Android KitKat.");
+            return false;
+        }
+        return true;
+    }
+
+    private static final byte[] uuidByteArray(UUID uuid) {
+        ByteBuffer buffer = ByteBuffer.wrap(new byte[UUID_BYTE_SIZE]);
+        buffer.putLong(uuid.getMostSignificantBits());
+        buffer.putLong(uuid.getLeastSignificantBits());
+        return buffer.array();
+    }
+
+    public void testIsCryptoSchemeSupported() throws Exception {
+        assertTrue(isCryptoSchemeSupportedNative(uuidByteArray(CLEARKEY_SCHEME_UUID)));
+    }
+
+    public void testIsCryptoSchemeNotSupported() throws Exception {
+        assertFalse(isCryptoSchemeSupportedNative(uuidByteArray(BAD_SCHEME_UUID)));
+    }
+
+    public void testPssh() throws Exception {
+        assertTrue(testPsshNative(uuidByteArray(CLEARKEY_SCHEME_UUID),
+                CENC_CLEARKEY_VIDEO_URL.toString()));
+    }
+
+    public void testGetPropertyString() throws Exception {
+        StringBuffer value = new StringBuffer();
+        testGetPropertyStringNative(uuidByteArray(CLEARKEY_SCHEME_UUID), "description", value);
+        assertEquals("ClearKey CDM", value.toString());
+    }
+
+    public void testUnknownPropertyString() throws Exception {
+        try {
+            StringBuffer value = new StringBuffer();
+            testGetPropertyStringNative(uuidByteArray(CLEARKEY_SCHEME_UUID),
+                    "unknown-property", value);
+        } catch (RuntimeException e) {
+            Log.e(TAG, "testUnknownPropertyString error = '" + e.getMessage() + "'");
+            assertThat(e.getMessage(), containsString("get property string returns"));
+        }
+    }
+
+    /**
+     * Tests native clear key system playback.
+     */
+    private void testClearKeyPlayback(
+            String mimeType, /*String initDataType,*/ Uri audioUrl, Uri videoUrl,
+            int videoWidth, int videoHeight) throws Exception {
+
+        if (!isCryptoSchemeSupportedNative(uuidByteArray(CLEARKEY_SCHEME_UUID))) {
+            throw new Error("Crypto scheme is not supported.");
+        }
+
+        IConnectionStatus connectionStatus = new ConnectionStatus(mContext);
+        if (!connectionStatus.isAvailable()) {
+            throw new Error("Network is not available, reason: " +
+                    connectionStatus.getNotConnectedReason());
+        }
+
+        // If device is not online, recheck the status a few times.
+        int retries = 0;
+        while (!connectionStatus.isConnected()) {
+            if (retries++ >= CONNECTION_RETRIES) {
+                throw new Error("Device is not online, reason: " +
+                        connectionStatus.getNotConnectedReason());
+            }
+            try {
+                Thread.sleep(100);
+            } catch (InterruptedException e) {
+                // do nothing
+            }
+        }
+        connectionStatus.testConnection(videoUrl);
+
+        if (!MediaUtils.checkCodecsForPath(mContext, videoUrl.getPath())) {
+            Log.i(TAG, "Device does not support " +
+                  videoWidth + "x" + videoHeight + " resolution for " + mimeType);
+            return;  // skip
+        }
+
+        PlaybackParams params = new PlaybackParams();
+        params.surface = mActivity.getSurfaceHolder().getSurface();
+        params.mimeType = mimeType;
+        params.audioUrl = audioUrl.toString();
+        params.videoUrl = videoUrl.toString();
+
+        if (!testClearKeyPlaybackNative(
+            uuidByteArray(CLEARKEY_SCHEME_UUID), params)) {
+            Log.e(TAG, "Fails play back using native media drm APIs.");
+        }
+        params.surface.release();
+    }
+
+    private ArrayList<Integer> intVersion(String version) {
+        String versions[] = version.split("\\.");
+
+        ArrayList<Integer> versionNumbers = Lists.newArrayList();
+        for (String subVersion : versions) {
+            versionNumbers.add(Integer.parseInt(subVersion));
+        }
+        return versionNumbers;
+    }
+
+    private static native boolean isCryptoSchemeSupportedNative(final byte[] uuid);
+
+    private static native boolean testClearKeyPlaybackNative(final byte[] uuid,
+            PlaybackParams params);
+
+    private static native boolean testGetPropertyStringNative(final byte[] uuid,
+            final String name, StringBuffer value);
+
+    private static native boolean testPsshNative(final byte[] uuid, final String videoUrl);
+
+    public void testClearKeyPlaybackCenc() throws Exception {
+        testClearKeyPlayback(
+                ISO_BMFF_VIDEO_MIME_TYPE,
+                CENC_AUDIO_URL,
+                CENC_CLEARKEY_VIDEO_URL,
+                VIDEO_WIDTH_CENC, VIDEO_HEIGHT_CENC);
+    }
+}
diff --git a/tests/tests/permission2/res/raw/android_manifest.xml b/tests/tests/permission2/res/raw/android_manifest.xml
index f226e12..4e6b9ea 100644
--- a/tests/tests/permission2/res/raw/android_manifest.xml
+++ b/tests/tests/permission2/res/raw/android_manifest.xml
@@ -913,7 +913,7 @@
         android:permissionGroup="android.permission-group.PHONE"
         android:label="@string/permlab_answerPhoneCalls"
         android:description="@string/permdesc_answerPhoneCalls"
-        android:protectionLevel="dangerous" />
+        android:protectionLevel="dangerous|runtime" />
 
 
     <!-- ====================================================================== -->
@@ -3464,7 +3464,7 @@
             android:theme="@style/Theme.NoDisplay"
             android:excludeFromRecents="true">
             <intent-filter>
-                <action android:name="android.intent.action.ACTION_REQUEST_SHUTDOWN" />
+                <action android:name="com.android.internal.intent.action.REQUEST_SHUTDOWN" />
                 <category android:name="android.intent.category.DEFAULT" />
             </intent-filter>
             <intent-filter>
diff --git a/tests/tests/permission2/src/android/permission2/cts/PermissionPolicyTest.java b/tests/tests/permission2/src/android/permission2/cts/PermissionPolicyTest.java
index 58611a3..577bb92 100644
--- a/tests/tests/permission2/src/android/permission2/cts/PermissionPolicyTest.java
+++ b/tests/tests/permission2/src/android/permission2/cts/PermissionPolicyTest.java
@@ -106,7 +106,7 @@
 
         // OEMs cannot define permissions in the platform namespace
         for (String permission : declaredPermissionsMap.keySet()) {
-            assertFalse("Cannot define permission in android namespace",
+            assertFalse("Cannot define permission in android namespace:" + permission,
                     permission.startsWith(PLATFORM_ROOT_NAMESPACE));
         }
 
@@ -207,6 +207,9 @@
                 case "ephemeral": {
                     protectionLevel |= PermissionInfo.PROTECTION_FLAG_EPHEMERAL;
                 } break;
+                case "runtime": {
+                    protectionLevel |= PermissionInfo.PROTECTION_FLAG_RUNTIME_ONLY;
+                } break;
             }
         }
         return protectionLevel;
diff --git a/tests/tests/uirendering/Android.mk b/tests/tests/uirendering/Android.mk
index 9d00124..4b915da 100644
--- a/tests/tests/uirendering/Android.mk
+++ b/tests/tests/uirendering/Android.mk
@@ -38,6 +38,6 @@
 LOCAL_PACKAGE_NAME := CtsUiRenderingTestCases
 
 # uncomment when dalvik.annotation.Test* are removed or part of SDK
-#LOCAL_SDK_VERSION := current
+#LOCAL_SDK_VERSION := test_current
 
 include $(BUILD_CTS_PACKAGE)
diff --git a/tests/tests/uirendering/res/layout/simple_white_layout.xml b/tests/tests/uirendering/res/layout/simple_white_layout.xml
new file mode 100644
index 0000000..43a10f5
--- /dev/null
+++ b/tests/tests/uirendering/res/layout/simple_white_layout.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  * Copyright (C) 2017 The Android Open Source Project
+  *
+  * Licensed under the Apache License, Version 2.0 (the "License");
+  * you may not use this file except in compliance with the License.
+  * You may obtain a copy of the License at
+  *
+  *      http://www.apache.org/licenses/LICENSE-2.0
+  *
+  * Unless required by applicable law or agreed to in writing, software
+  * distributed under the License is distributed on an "AS IS" BASIS,
+  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  * See the License for the specific language governing permissions and
+  * limitations under the License.
+  -->
+<View xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="@dimen/test_width"
+    android:layout_height="@dimen/test_height"
+    android:background="@android:color/white"/>
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/AutofillHighlightTests.java b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/AutofillHighlightTests.java
new file mode 100644
index 0000000..e3d56ba
--- /dev/null
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/AutofillHighlightTests.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.uirendering.cts.testclasses;
+
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.drawable.Drawable;
+import android.support.test.filters.MediumTest;
+import android.uirendering.cts.R;
+import android.uirendering.cts.bitmapcomparers.MSSIMComparer;
+import android.uirendering.cts.bitmapverifiers.GoldenImageVerifier;
+import android.uirendering.cts.testinfrastructure.ActivityTestBase;
+
+import org.junit.Test;
+
+@MediumTest
+public class AutofillHighlightTests extends ActivityTestBase {
+    @Test
+    public void testHighlightedFrameLayout() {
+        Bitmap goldenBitmap = Bitmap.createBitmap(ActivityTestBase.TEST_WIDTH,
+                ActivityTestBase.TEST_HEIGHT, Bitmap.Config.ARGB_8888);
+        goldenBitmap.eraseColor(Color.WHITE);
+        Canvas canvas = new Canvas(goldenBitmap);
+
+        Drawable autofilledDrawable = getActivity().getDrawable(
+                android.R.drawable.autofilled_highlight);
+        autofilledDrawable.setBounds(0, 0, ActivityTestBase.TEST_WIDTH,
+                ActivityTestBase.TEST_HEIGHT);
+        autofilledDrawable.draw(canvas);
+
+        createTest().addLayout(R.layout.simple_white_layout, view -> view.setAutofilled(true))
+                .runWithVerifier(new GoldenImageVerifier(goldenBitmap, new MSSIMComparer(0.99)));
+    }
+}
+
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/ColorSpaceTests.java b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/ColorSpaceTests.java
index f66f5f4..c5aa1b3 100644
--- a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/ColorSpaceTests.java
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/ColorSpaceTests.java
@@ -81,7 +81,7 @@
                         new Point[] {
                                 point(0, 0), point(48, 0), point(32, 40), point(0, 40), point(0, 56)
                         },
-                        new int[] { 0xff75fb4c, 0xff75fb4c, 0xff75fb4c, 0xffffffff, 0xffba7d26 }
+                        new int[] { 0xff00ff00, 0xff00ff00, 0xff00ff00, 0xffffffff, 0xff7f7f00 }
                 ));
     }
 
@@ -114,7 +114,7 @@
                         new Point[] {
                                 point(0, 0), point(48, 0), point(32, 40), point(0, 40), point(0, 56)
                         },
-                        new int[] { 0xffc07f2c, 0xffc07f2c, 0xffc07f2c, 0xffffffff, 0xffe03f16 }
+                        new int[] { 0xffff7f00, 0xffff7f00, 0xffff7f00, 0xffffffff, 0xffff3f00 }
                 ));
     }
 
diff --git a/tests/tests/view/src/android/view/cts/ViewTest.java b/tests/tests/view/src/android/view/cts/ViewTest.java
index 03b8994..ca39aab 100644
--- a/tests/tests/view/src/android/view/cts/ViewTest.java
+++ b/tests/tests/view/src/android/view/cts/ViewTest.java
@@ -73,7 +73,6 @@
 import android.util.Xml;
 import android.view.ActionMode;
 import android.view.ContextMenu;
-import android.view.ContextMenu.ContextMenuInfo;
 import android.view.Display;
 import android.view.HapticFeedbackConstants;
 import android.view.InputDevice;
@@ -1089,7 +1088,7 @@
         assertTrue(view.hasCalledOnCreateContextMenu());
         assertTrue(mMockParent.hasCreateContextMenu());
         verify(listener, times(1)).onCreateContextMenu(
-                eq(contextMenu), eq(view), any(ContextMenuInfo.class));
+                eq(contextMenu), eq(view), any());
     }
 
     @Test(expected=NullPointerException.class)
diff --git a/tests/tests/widget/src/android/widget/cts/AdapterViewTest.java b/tests/tests/widget/src/android/widget/cts/AdapterViewTest.java
index f7fc033..4e33156 100644
--- a/tests/tests/widget/src/android/widget/cts/AdapterViewTest.java
+++ b/tests/tests/widget/src/android/widget/cts/AdapterViewTest.java
@@ -270,7 +270,7 @@
 
         verifyZeroInteractions(mockClickListener);
         assertTrue(mAdapterView.performItemClick(null, 0, 0));
-        verify(mockClickListener, times(1)).onItemClick(eq(mAdapterView), any(View.class),
+        verify(mockClickListener, times(1)).onItemClick(eq(mAdapterView), any(),
                 eq(0), eq(0L));
 
         setArrayAdapter(mAdapterView);
diff --git a/tests/tests/widget/src/android/widget/cts/ArrayAdapterTest.java b/tests/tests/widget/src/android/widget/cts/ArrayAdapterTest.java
index 84c60c8..7595c98 100644
--- a/tests/tests/widget/src/android/widget/cts/ArrayAdapterTest.java
+++ b/tests/tests/widget/src/android/widget/cts/ArrayAdapterTest.java
@@ -26,6 +26,7 @@
 import static org.mockito.Mockito.verifyZeroInteractions;
 
 import android.content.Context;
+import android.content.res.Resources;
 import android.content.res.Resources.Theme;
 import android.database.DataSetObserver;
 import android.support.test.annotation.UiThreadTest;
@@ -411,7 +412,7 @@
         ArrayAdapter.createFromResource(null, R.array.string, R.layout.simple_spinner_item);
     }
 
-    @Test(expected=NullPointerException.class)
+    @Test(expected=Resources.NotFoundException.class)
     public void testCreateFromResourceWithInvalidId() {
         ArrayAdapter.createFromResource(mContext, INVALID_ID, R.layout.simple_spinner_item);
     }
diff --git a/tests/tests/widget/src/android/widget/cts/ExpandableListViewTest.java b/tests/tests/widget/src/android/widget/cts/ExpandableListViewTest.java
index 796073f..1482cae 100644
--- a/tests/tests/widget/src/android/widget/cts/ExpandableListViewTest.java
+++ b/tests/tests/widget/src/android/widget/cts/ExpandableListViewTest.java
@@ -164,7 +164,7 @@
         mExpandableListView.setOnItemClickListener(mockOnItemClickListener);
         assertTrue(mExpandableListView.performItemClick(null, 100, 99));
         verify(mockOnItemClickListener, times(1)).onItemClick(eq(mExpandableListView),
-                any(View.class), eq(100), eq(99L));
+                any(), eq(100), eq(99L));
     }
 
     @Test
@@ -309,7 +309,7 @@
 
         mExpandableListView.performItemClick(null, 0, 0);
         verify(mockOnGroupClickListener, times(1)).onGroupClick(eq(mExpandableListView),
-                any(View.class), eq(0), eq(0L));
+                any(), eq(0), eq(0L));
     }
 
     @UiThreadTest
@@ -328,7 +328,7 @@
         // click on the child list of the first group
         mExpandableListView.performItemClick(null, 1, 0);
         verify(mockOnChildClickListener, times(1)).onChildClick(eq(mExpandableListView),
-                any(View.class), eq(0), eq(0), eq(0L));
+                any(), eq(0), eq(0), eq(0L));
     }
 
     @UiThreadTest
diff --git a/tools/cts-tradefed/etc/cts-tradefed b/tools/cts-tradefed/etc/cts-tradefed
index 86f2fe3..15249c3 100755
--- a/tools/cts-tradefed/etc/cts-tradefed
+++ b/tools/cts-tradefed/etc/cts-tradefed
@@ -121,5 +121,5 @@
     JAR_PATH=${JAR_PATH}:$j
 done
 
-java $RDBG_FLAG -XX:+HeapDumpOnOutOfMemoryError -cp ${JAR_PATH} -DCTS_ROOT=${CTS_ROOT} com.android.compatibility.common.tradefed.command.CompatibilityConsole "$@"
+java $RDBG_FLAG -Xmx4g -XX:+HeapDumpOnOutOfMemoryError -cp ${JAR_PATH} -DCTS_ROOT=${CTS_ROOT} com.android.compatibility.common.tradefed.command.CompatibilityConsole "$@"
 
diff --git a/tools/cts-tradefed/res/config/cts-vendor-interface.xml b/tools/cts-tradefed/res/config/cts-vendor-interface.xml
index 5f38264..582cebf 100644
--- a/tools/cts-tradefed/res/config/cts-vendor-interface.xml
+++ b/tools/cts-tradefed/res/config/cts-vendor-interface.xml
@@ -48,6 +48,7 @@
     <option name="compatibility:include-filter" value="CtsPrintTestCases" />
     <option name="compatibility:include-filter" value="CtsProviderTestCases" />
     <option name="compatibility:include-filter" value="CtsRsBlasTestCases" />
+    <option name="compatibility:include-filter" value="CtsSecurityHostTestCases" />
     <option name="compatibility:include-filter" value="CtsSecurityTestCases" />
     <option name="compatibility:include-filter" value="CtsSensorTestCases" />
     <option name="compatibility:include-filter" value="CtsShortcutHostTestCases" />
diff --git a/tools/selinux/SELinuxNeverallowTestGen.py b/tools/selinux/SELinuxNeverallowTestGen.py
index bc775d6..6194e2d 100755
--- a/tools/selinux/SELinuxNeverallowTestGen.py
+++ b/tools/selinux/SELinuxNeverallowTestGen.py
@@ -16,7 +16,7 @@
         # remove comments
         no_comments = re.sub(r'#.+?$', r'', policy_str, flags = re.M)
         # match neverallow rules
-        return re.findall(r'(^neverallow\s.+?;)', no_comments, flags = re.M |re.S);
+        return re.findall(r'^\s*(neverallow\s.+?;)', no_comments, flags = re.M |re.S);
 
 # neverallow_rule_to_test - takes a neverallow statement and transforms it into
 # the output necessary to form a cts unit test in a java source file.