Merge "Merge "Add assertion message if testDns ipv6 lookup reutrns ipv4." into lollipop-mr1-cts-dev am: b996de8" into marshmallow-cts-dev am: 48216c2 am: 275a0e6 am: 4be27c8 am: 0399523
am: fd3f832

* commit 'fd3f832fcfb9dc4fa3e4d497a97ebdf1636269b2':
  Add assertion message if testDns ipv6 lookup reutrns ipv4.

Change-Id: I774f8188a4329508bd590564859f38b3ec049f7c
diff --git a/tests/cts/hostside/Android.mk b/tests/cts/hostside/Android.mk
index 6637d61..ad97ecd 100644
--- a/tests/cts/hostside/Android.mk
+++ b/tests/cts/hostside/Android.mk
@@ -23,8 +23,13 @@
 
 LOCAL_JAVA_LIBRARIES := cts-tradefed tradefed-prebuilt
 
+LOCAL_STATIC_JAVA_LIBRARIES := cts-migration-lib
+
 LOCAL_CTS_TEST_PACKAGE := android.net.hostsidenetwork
 
+# Tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts
+
 include $(BUILD_CTS_HOST_JAVA_LIBRARY)
 
 # Build the test APKs using their own makefiles
diff --git a/tests/cts/hostside/AndroidTest.xml b/tests/cts/hostside/AndroidTest.xml
new file mode 100644
index 0000000..9945805
--- /dev/null
+++ b/tests/cts/hostside/AndroidTest.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2015 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<configuration description="Config for CTS net host test cases">
+    <test class="com.android.compatibility.common.tradefed.testtype.JarHostTest" >
+        <option name="jar" value="CtsHostsideNetworkTests.jar" />
+        <option name="runtime-hint" value="3m56s" />
+    </test>
+</configuration>
diff --git a/tests/cts/hostside/app/Android.mk b/tests/cts/hostside/app/Android.mk
index 055287a..7f8da07 100644
--- a/tests/cts/hostside/app/Android.mk
+++ b/tests/cts/hostside/app/Android.mk
@@ -29,4 +29,7 @@
 LOCAL_PROGUARD_ENABLED := disabled
 LOCAL_DEX_PREOPT := false
 
+# Tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts
+
 include $(BUILD_CTS_SUPPORT_PACKAGE)
diff --git a/tests/cts/hostside/app/AndroidManifest.xml b/tests/cts/hostside/app/AndroidManifest.xml
index cdde7dc..c7978f8 100644
--- a/tests/cts/hostside/app/AndroidManifest.xml
+++ b/tests/cts/hostside/app/AndroidManifest.xml
@@ -17,8 +17,10 @@
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
         package="com.android.cts.net.hostside">
 
-    <uses-permission android:name="android.permission.INTERNET"/>
     <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
+    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
+    <uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/>
+    <uses-permission android:name="android.permission.INTERNET"/>
 
     <application>
         <uses-library android:name="android.test.runner" />
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractRestrictBackgroundNetworkTestCase.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractRestrictBackgroundNetworkTestCase.java
new file mode 100644
index 0000000..27d4a2b
--- /dev/null
+++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractRestrictBackgroundNetworkTestCase.java
@@ -0,0 +1,497 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.net.hostside;
+
+import static android.cts.util.SystemUtil.runShellCommand;
+import static android.net.ConnectivityManager.ACTION_RESTRICT_BACKGROUND_CHANGED;
+import static android.net.ConnectivityManager.RESTRICT_BACKGROUND_STATUS_DISABLED;
+import static android.net.ConnectivityManager.RESTRICT_BACKGROUND_STATUS_ENABLED;
+import static android.net.ConnectivityManager.RESTRICT_BACKGROUND_STATUS_WHITELISTED;
+
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.TimeUnit;
+
+import android.app.ActivityManager;
+import android.app.Instrumentation;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.net.ConnectivityManager;
+import android.net.NetworkInfo;
+import android.net.wifi.WifiManager;
+import android.test.InstrumentationTestCase;
+import android.util.Log;
+
+/**
+ * Superclass for tests related to background network restrictions.
+ */
+abstract class AbstractRestrictBackgroundNetworkTestCase extends InstrumentationTestCase {
+    protected static final String TAG = "RestrictBackgroundNetworkTests";
+
+    protected static final String TEST_PKG = "com.android.cts.net.hostside";
+    protected static final String TEST_APP2_PKG = "com.android.cts.net.hostside.app2";
+
+    private static final int SLEEP_TIME_SEC = 1;
+    private static final boolean DEBUG = true;
+
+    // Constants below must match values defined on app2's Common.java
+    private static final String MANIFEST_RECEIVER = "ManifestReceiver";
+    private static final String DYNAMIC_RECEIVER = "DynamicReceiver";
+    private static final String ACTION_GET_COUNTERS =
+            "com.android.cts.net.hostside.app2.action.GET_COUNTERS";
+    private static final String ACTION_CHECK_NETWORK =
+            "com.android.cts.net.hostside.app2.action.CHECK_NETWORK";
+    private static final String ACTION_RECEIVER_READY =
+            "com.android.cts.net.hostside.app2.action.RECEIVER_READY";
+    private static final String EXTRA_ACTION = "com.android.cts.net.hostside.app2.extra.ACTION";
+    private static final String EXTRA_RECEIVER_NAME =
+            "com.android.cts.net.hostside.app2.extra.RECEIVER_NAME";
+    private static final String RESULT_SEPARATOR = ";";
+    private static final String STATUS_NETWORK_UNAVAILABLE_PREFIX = "NetworkUnavailable:";
+    private static final String STATUS_NETWORK_AVAILABLE_PREFIX = "NetworkAvailable:";
+    private static final int SECOND_IN_MS = 1000;
+    private static final int NETWORK_TIMEOUT_MS = 15 * SECOND_IN_MS;
+    private static final int PROCESS_STATE_FOREGROUND_SERVICE = 4;
+
+
+    // Must be higher than NETWORK_TIMEOUT_MS
+    private static final int ORDERED_BROADCAST_TIMEOUT_MS = NETWORK_TIMEOUT_MS * 4;
+
+    protected Context mContext;
+    protected Instrumentation mInstrumentation;
+    protected ConnectivityManager mCm;
+    protected WifiManager mWfm;
+    protected int mUid;
+    private String mMeteredWifi;
+
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+
+        mInstrumentation = getInstrumentation();
+        mContext = mInstrumentation.getContext();
+        mCm = (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
+        mWfm = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE);
+        mUid = getUid(TEST_APP2_PKG);
+        final int myUid = getUid(mContext.getPackageName());
+
+        Log.i(TAG, "Apps status on " + getName() + ":\n"
+                + "\ttest app: uid=" + myUid + ", state=" + getProcessStateByUid(myUid) + "\n"
+                + "\tapp2: uid=" + mUid + ", state=" + getProcessStateByUid(mUid));
+   }
+
+    protected int getUid(String packageName) throws Exception {
+        return mContext.getPackageManager().getPackageUid(packageName, 0);
+    }
+
+    protected void assertRestrictBackgroundChangedReceived(int expectedCount) throws Exception {
+        assertRestrictBackgroundChangedReceived(DYNAMIC_RECEIVER, expectedCount);
+        assertRestrictBackgroundChangedReceived(MANIFEST_RECEIVER, 0);
+    }
+
+    protected void assertRestrictBackgroundChangedReceived(String receiverName, int expectedCount)
+            throws Exception {
+        int attempts = 0;
+        int count = 0;
+        final int maxAttempts = 5;
+        do {
+            attempts++;
+            count = getNumberBroadcastsReceived(receiverName, ACTION_RESTRICT_BACKGROUND_CHANGED);
+            if (count == expectedCount) {
+                break;
+            }
+            Log.d(TAG, "Expecting count " + expectedCount + " but actual is " + count + " after "
+                    + attempts + " attempts; sleeping "
+                    + SLEEP_TIME_SEC + " seconds before trying again");
+            Thread.sleep(SLEEP_TIME_SEC * SECOND_IN_MS);
+        } while (attempts <= maxAttempts);
+        assertEquals("Number of expected broadcasts for " + receiverName + " not reached after "
+                + maxAttempts * SLEEP_TIME_SEC + " seconds", expectedCount, count);
+    }
+
+    protected String sendOrderedBroadcast(Intent intent) throws Exception {
+        return sendOrderedBroadcast(intent, ORDERED_BROADCAST_TIMEOUT_MS);
+    }
+
+    protected String sendOrderedBroadcast(Intent intent, int timeoutMs) throws Exception {
+        final LinkedBlockingQueue<String> result = new LinkedBlockingQueue<>(1);
+        Log.d(TAG, "Sending ordered broadcast: " + intent);
+        mContext.sendOrderedBroadcast(intent, null, new BroadcastReceiver() {
+
+            @Override
+            public void onReceive(Context context, Intent intent) {
+                final String resultData = getResultData();
+                if (resultData == null) {
+                    Log.e(TAG, "Received null data from ordered intent");
+                    return;
+                }
+                result.offer(resultData);
+            }
+        }, null, 0, null, null);
+
+        final String resultData = result.poll(timeoutMs, TimeUnit.MILLISECONDS);
+        Log.d(TAG, "Ordered broadcast response: " + resultData);
+        return resultData;
+    }
+
+    protected int getNumberBroadcastsReceived(String receiverName, String action) throws Exception {
+        final Intent intent = new Intent(ACTION_GET_COUNTERS);
+        intent.putExtra(EXTRA_ACTION, ACTION_RESTRICT_BACKGROUND_CHANGED);
+        intent.putExtra(EXTRA_RECEIVER_NAME, receiverName);
+        final String resultData = sendOrderedBroadcast(intent);
+        assertNotNull("timeout waiting for ordered broadcast result", resultData);
+        return Integer.valueOf(resultData);
+    }
+
+    protected void assertRestrictBackgroundStatus(int expectedApiStatus) throws Exception {
+        assertBackgroundState(); // Sanity check.
+        final Intent intent = new Intent(ACTION_CHECK_NETWORK);
+        final String resultData = sendOrderedBroadcast(intent);
+        final String[] resultItems = resultData.split(RESULT_SEPARATOR);
+        final String actualApiStatus = toString(Integer.parseInt(resultItems[0]));
+        // First asserts the API returns the proper value...
+        assertEquals("wrong status", toString(expectedApiStatus), actualApiStatus);
+
+        //...then the actual network status in the background thread.
+        final String networkStatus = getNetworkStatus(resultItems);
+        assertNetworkStatus(expectedApiStatus != RESTRICT_BACKGROUND_STATUS_ENABLED, networkStatus);
+    }
+
+    protected void assertBackgroundNetworkAccess(boolean expectAllowed) throws Exception {
+        assertBackgroundState(); // Sanity check.
+        final Intent intent = new Intent(ACTION_CHECK_NETWORK);
+        final String resultData = sendOrderedBroadcast(intent);
+        final String[] resultItems = resultData.split(RESULT_SEPARATOR);
+        final String networkStatus = getNetworkStatus(resultItems);
+        assertNetworkStatus(expectAllowed, networkStatus);
+    }
+
+    protected final void assertBackgroundState() throws Exception {
+        final ProcessState state = getProcessStateByUid(mUid);
+        Log.v(TAG, "assertBackgroundState(): status for app2 (" + mUid + "): " + state);
+        final boolean isBackground = isBackground(state.state);
+        assertTrue("App2 is not on background state: " + state, isBackground);
+    }
+
+    protected final void assertForegroundServiceState() throws Exception {
+        final ProcessState state = getProcessStateByUid(mUid);
+        Log.v(TAG, "assertForegroundServiceState(): status for app2 (" + mUid + "): " + state);
+        assertEquals("App2 is not on foreground service state: " + state,
+                PROCESS_STATE_FOREGROUND_SERVICE, state.state);
+    }
+
+    /**
+     * Returns whether an app state should be considered "background" for restriction purposes.
+     */
+    protected boolean isBackground(int state) {
+        return state >= PROCESS_STATE_FOREGROUND_SERVICE;
+    }
+
+    private String getNetworkStatus(String[] resultItems) {
+        return resultItems.length < 2 ? null : resultItems[1];
+    }
+
+    private void assertNetworkStatus(boolean expectAvailable, String status) throws Exception {
+        if (status == null) {
+            Log.d(TAG, "timeout waiting for ordered broadcast");
+            if (expectAvailable) {
+                fail("did not get network status when access was allowed");
+            }
+            return;
+        }
+        final String expectedPrefix = expectAvailable ?
+                STATUS_NETWORK_AVAILABLE_PREFIX : STATUS_NETWORK_UNAVAILABLE_PREFIX;
+        assertTrue("Wrong network status (" + status + ") when expectedAvailable is "
+                + expectAvailable, status.startsWith(expectedPrefix));
+    }
+
+    protected String executeShellCommand(String command) throws Exception {
+        final String result = runShellCommand(mInstrumentation, command).trim();
+        if (DEBUG) Log.d(TAG, "Command '" + command + "' returned '" + result + "'");
+        return result;
+    }
+
+    /**
+     * Runs a Shell command which is not expected to generate output.
+     */
+    protected void executeSilentShellCommand(String command) throws Exception {
+        final String result = executeShellCommand(command);
+        assertTrue("Command '" + command + "' failed: " + result, result.trim().isEmpty());
+    }
+
+    /**
+     * Asserts the result of a command, wait and re-running it a couple times if necessary.
+     */
+    protected void assertDelayedShellCommand(String command, final String expectedResult)
+            throws Exception {
+        assertDelayedShellCommand(command, new ExpectResultChecker() {
+
+            @Override
+            public boolean isExpected(String result) {
+                return expectedResult.equals(result);
+            }
+
+            @Override
+            public String getExpected() {
+                return expectedResult;
+            }
+        });
+    }
+
+    protected void assertDelayedShellCommand(String command, ExpectResultChecker checker)
+            throws Exception {
+        final int maxTries = 5;
+        String result = "";
+        for (int i = 1; i <= maxTries; i++) {
+            result = executeShellCommand(command).trim();
+            if (checker.isExpected(result)) return;
+            Log.v(TAG, "Command '" + command + "' returned '" + result + " instead of '"
+                    + checker.getExpected() + "' on attempt #" + i
+                    + "; sleeping 1s before trying again");
+            Thread.sleep(SECOND_IN_MS);
+        }
+        fail("Command '" + command + "' did not return '" + checker.getExpected() + "' after "
+                + maxTries
+                + " attempts. Last result: '" + result + "'");
+    }
+
+    protected void setMeteredNetwork() throws Exception {
+        final NetworkInfo info = mCm.getActiveNetworkInfo();
+        final boolean metered = mCm.isActiveNetworkMetered();
+        if (metered) {
+            Log.d(TAG, "Active network already metered: " + info);
+            return;
+        } else {
+            Log.w(TAG, "Active network not metered: " + info);
+        }
+        final String netId = setWifiMeteredStatus(true);
+
+        // Set flag so status is reverted on resetMeteredNetwork();
+        mMeteredWifi = netId;
+        // Sanity check.
+        assertWifiMeteredStatus(netId, true);
+        assertTrue("Could not set wifi '" + netId + "' as metered ("
+                + mCm.getActiveNetworkInfo() +")", mCm.isActiveNetworkMetered());
+    }
+
+    protected void resetMeteredNetwork() throws Exception {
+        if (mMeteredWifi == null) {
+            Log.d(TAG, "resetMeteredNetwork(): wifi not set as metered");
+            return;
+        }
+        Log.i(TAG, "resetMeteredNetwork(): resetting " + mMeteredWifi);
+        setWifiMeteredStatus(mMeteredWifi, false);
+    }
+
+    private String setWifiMeteredStatus(boolean metered) throws Exception {
+        // We could call setWifiEnabled() here, but it might take sometime to be in a consistent
+        // state (for example, if one of the saved network is not properly authenticated), so it's
+        // better to let the hostside test take care of that.
+        assertTrue("wi-fi is disabled", mWfm.isWifiEnabled());
+        // TODO: if it's not guaranteed the device has wi-fi, we need to change the tests
+        // to make the actual verification of restrictions optional.
+        final String ssid = mWfm.getConnectionInfo().getSSID();
+        return setWifiMeteredStatus(ssid, metered);
+    }
+
+    private String setWifiMeteredStatus(String ssid, boolean metered) throws Exception {
+        assertNotNull("null SSID", ssid);
+        final String netId = ssid.trim().replaceAll("\"", ""); // remove quotes, if any.
+        assertFalse("empty SSID", ssid.isEmpty());
+
+        Log.i(TAG, "Setting wi-fi network " + netId + " metered status to " + metered);
+        final String setCommand = "cmd netpolicy set metered-network " + netId + " " + metered;
+        assertDelayedShellCommand(setCommand, "");
+
+        return netId;
+    }
+
+    private void assertWifiMeteredStatus(String netId, boolean status) throws Exception {
+        final String command = "cmd netpolicy list wifi-networks";
+        final String expectedLine = netId + ";" + status;
+        assertDelayedShellCommand(command, new ExpectResultChecker() {
+
+            @Override
+            public boolean isExpected(String result) {
+                return result.contains(expectedLine);
+            }
+
+            @Override
+            public String getExpected() {
+                return "line containing " + expectedLine;
+            }
+        });
+    }
+
+    protected void setRestrictBackground(boolean enabled) throws Exception {
+        executeShellCommand("cmd netpolicy set restrict-background " + enabled);
+        final String output = executeShellCommand("cmd netpolicy get restrict-background ");
+        final String expectedSuffix = enabled ? "enabled" : "disabled";
+        // TODO: use MoreAsserts?
+        assertTrue("output '" + output + "' should end with '" + expectedSuffix + "'",
+                output.endsWith(expectedSuffix));
+      }
+
+    protected void addRestrictBackgroundWhitelist(int uid) throws Exception {
+        executeShellCommand("cmd netpolicy add restrict-background-whitelist " + uid);
+        assertRestrictBackgroundWhitelist(uid, true);
+    }
+
+    protected void removeRestrictBackgroundWhitelist(int uid) throws Exception {
+        executeShellCommand("cmd netpolicy remove restrict-background-whitelist " + uid);
+        assertRestrictBackgroundWhitelist(uid, false);
+    }
+
+    protected void assertRestrictBackgroundWhitelist(int uid, boolean expected) throws Exception {
+        assertRestrictBackground("restrict-background-whitelist", uid, expected);
+    }
+
+    protected void addRestrictBackgroundBlacklist(int uid) throws Exception {
+        executeShellCommand("cmd netpolicy add restrict-background-blacklist " + uid);
+        assertRestrictBackgroundBlacklist(uid, true);
+    }
+
+    protected void removeRestrictBackgroundBlacklist(int uid) throws Exception {
+        executeShellCommand("cmd netpolicy remove restrict-background-blacklist " + uid);
+        assertRestrictBackgroundBlacklist(uid, false);
+    }
+
+    protected void assertRestrictBackgroundBlacklist(int uid, boolean expected) throws Exception {
+        assertRestrictBackground("restrict-background-blacklist", uid, expected);
+    }
+
+    private void assertRestrictBackground(String list, int uid, boolean expected) throws Exception {
+        final int maxTries = 5;
+        boolean actual = false;
+        for (int i = 1; i <= maxTries; i++) {
+            final String output =
+                    executeShellCommand("cmd netpolicy list " + list);
+            actual = output.contains(Integer.toString(uid));
+            if (expected == actual) {
+                return;
+            }
+            Log.v(TAG, list + " check for uid " + uid + " doesn't match yet (expected "
+                    + expected + ", got " + actual + "); sleeping 1s before polling again");
+            Thread.sleep(SECOND_IN_MS);
+        }
+        fail(list + " check for uid " + uid + " failed: expected " + expected + ", got " + actual);
+    }
+
+    protected void assertPowerSaveModeWhitelist(String packageName, boolean expected)
+            throws Exception {
+        // TODO: currently the power-save mode is behaving like idle, but once it changes, we'll
+        // need to use netpolicy for whitelisting
+        assertDelayedShellCommand("dumpsys deviceidle whitelist =" + packageName,
+                Boolean.toString(expected));
+    }
+
+    protected void addPowerSaveModeWhitelist(String packageName) throws Exception {
+        Log.i(TAG, "Adding package " + packageName + " to power-save-mode whitelist");
+        // TODO: currently the power-save mode is behaving like idle, but once it changes, we'll
+        // need to use netpolicy for whitelisting
+        executeShellCommand("dumpsys deviceidle whitelist +" + packageName);
+        assertPowerSaveModeWhitelist(packageName, true); // Sanity check
+    }
+
+    protected void removePowerSaveModeWhitelist(String packageName) throws Exception {
+        Log.i(TAG, "Removing package " + packageName + " from power-save-mode whitelist");
+        // TODO: currently the power-save mode is behaving like idle, but once it changes, we'll
+        // need to use netpolicy for whitelisting
+        executeShellCommand("dumpsys deviceidle whitelist -" + packageName);
+        assertPowerSaveModeWhitelist(packageName, false); // Sanity check
+    }
+
+    protected void setPowerSaveMode(boolean enabled) throws Exception {
+        Log.i(TAG, "Setting power mode to " + enabled);
+        if (enabled) {
+            executeSilentShellCommand("cmd battery unplug");
+            executeSilentShellCommand("settings put global low_power 1");
+        } else {
+            executeSilentShellCommand("cmd battery reset");
+        }
+    }
+
+    /**
+     * Starts a service that will register a broadcast receiver to receive
+     * {@code RESTRICT_BACKGROUND_CHANGE} intents.
+     * <p>
+     * The service must run in a separate app because otherwise it would be killed every time
+     * {@link #runDeviceTests(String, String)} is executed.
+     */
+    protected void registerBroadcastReceiver() throws Exception {
+        executeShellCommand("am startservice com.android.cts.net.hostside.app2/.MyService");
+        // Wait until receiver is ready.
+        final int maxTries = 5;
+        for (int i = 1; i <= maxTries; i++) {
+            final String message =
+                    sendOrderedBroadcast(new Intent(ACTION_RECEIVER_READY), SECOND_IN_MS);
+            Log.d(TAG, "app2 receiver acked: " + message);
+            if (message != null) {
+                return;
+            }
+            Log.v(TAG, "app2 receiver is not ready yet; sleeping 1s before polling again");
+            Thread.sleep(SECOND_IN_MS);
+        }
+        fail("app2 receiver is not ready");
+    }
+
+    protected void startForegroundService() throws Exception {
+        executeShellCommand(
+                "am startservice com.android.cts.net.hostside.app2/.MyForegroundService");
+    }
+
+    private String toString(int status) {
+        switch (status) {
+            case RESTRICT_BACKGROUND_STATUS_DISABLED:
+                return "DISABLED";
+            case RESTRICT_BACKGROUND_STATUS_WHITELISTED:
+                return "WHITELISTED";
+            case RESTRICT_BACKGROUND_STATUS_ENABLED:
+                return "ENABLED";
+            default:
+                return "UNKNOWN_STATUS_" + status;
+        }
+    }
+
+    private ProcessState getProcessStateByUid(int uid) throws Exception {
+        return new ProcessState(executeShellCommand("cmd activity get-uid-state " + uid));
+    }
+
+    private static class ProcessState {
+        private final String fullState;
+        final int state;
+
+        ProcessState(String fullState) {
+            this.fullState = fullState;
+            try {
+                this.state = Integer.parseInt(fullState.split(" ")[0]);
+            } catch (Exception e) {
+                throw new IllegalArgumentException("Could not parse " + fullState);
+            }
+        }
+
+        @Override
+        public String toString() {
+            return fullState;
+        }
+    }
+
+    protected static interface ExpectResultChecker {
+        boolean isExpected(String result);
+        String getExpected();
+    }
+}
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/BatterySaverModeNonMeteredTest.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/BatterySaverModeNonMeteredTest.java
new file mode 100644
index 0000000..8e83fa2
--- /dev/null
+++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/BatterySaverModeNonMeteredTest.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.net.hostside;
+
+//TODO: move this and BatterySaverModeTest's logic into a common superclass
+public class BatterySaverModeNonMeteredTest extends AbstractRestrictBackgroundNetworkTestCase {
+
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+
+        setPowerSaveMode(false);
+        assertPowerSaveModeWhitelist(TEST_APP2_PKG, false); // Sanity check
+        registerBroadcastReceiver();
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        super.tearDown();
+
+        setPowerSaveMode(false);
+    }
+
+    public void testBackgroundNetworkAccess_enabled() throws Exception {
+        setPowerSaveMode(true);
+        assertBackgroundNetworkAccess(false);
+        // Make sure app is allowed if running a foreground service.
+        startForegroundService();
+        assertForegroundServiceState();
+        assertBackgroundNetworkAccess(true);
+    }
+
+    public void testBackgroundNetworkAccess_whitelisted() throws Exception {
+        setPowerSaveMode(true);
+        assertBackgroundNetworkAccess(false);
+        addPowerSaveModeWhitelist(TEST_APP2_PKG);
+        assertBackgroundNetworkAccess(true);
+        removePowerSaveModeWhitelist(TEST_APP2_PKG);
+        assertBackgroundNetworkAccess(false);
+    }
+
+    public void testBackgroundNetworkAccess_disabled() throws Exception {
+        setPowerSaveMode(false);
+        assertBackgroundNetworkAccess(true);
+    }
+}
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/BatterySaverModeTest.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/BatterySaverModeTest.java
new file mode 100644
index 0000000..d1217b4
--- /dev/null
+++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/BatterySaverModeTest.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.net.hostside;
+
+//TODO: move this and BatterySaverModeNonMeteredTest's logic into a common superclass
+public class BatterySaverModeTest extends AbstractRestrictBackgroundNetworkTestCase {
+
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+
+        setMeteredNetwork();
+        setPowerSaveMode(false);
+        assertPowerSaveModeWhitelist(TEST_APP2_PKG, false); // Sanity check
+        registerBroadcastReceiver();
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        super.tearDown();
+
+        try {
+            resetMeteredNetwork();
+        } finally {
+            setPowerSaveMode(false);
+        }
+    }
+
+    public void testBackgroundNetworkAccess_enabled() throws Exception {
+        setPowerSaveMode(true);
+        assertBackgroundNetworkAccess(false);
+
+        // Make sure app is allowed if running a foreground service.
+        startForegroundService();
+        assertForegroundServiceState();
+        assertBackgroundNetworkAccess(true);
+    }
+
+    public void testBackgroundNetworkAccess_whitelisted() throws Exception {
+        setPowerSaveMode(true);
+        assertBackgroundNetworkAccess(false);
+        addPowerSaveModeWhitelist(TEST_APP2_PKG);
+        assertBackgroundNetworkAccess(true);
+        removePowerSaveModeWhitelist(TEST_APP2_PKG);
+        assertBackgroundNetworkAccess(false);
+    }
+
+    public void testBackgroundNetworkAccess_disabled() throws Exception {
+        setPowerSaveMode(false);
+        assertBackgroundNetworkAccess(true);
+    }
+}
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/DataSaverModeTest.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/DataSaverModeTest.java
new file mode 100644
index 0000000..62f670e
--- /dev/null
+++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/DataSaverModeTest.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.net.hostside;
+
+import static android.net.ConnectivityManager.RESTRICT_BACKGROUND_STATUS_DISABLED;
+import static android.net.ConnectivityManager.RESTRICT_BACKGROUND_STATUS_ENABLED;
+import static android.net.ConnectivityManager.RESTRICT_BACKGROUND_STATUS_WHITELISTED;
+
+/*
+ * TODO: need to add more scenarios:
+ * - test access on foreground app
+ * - test access on foreground service app
+ * - make sure it works when app is on foreground and state is transitioned:
+ *   - data saver is enabled
+ *   - app is added/removed to blacklist
+ *
+ */
+public class DataSaverModeTest extends AbstractRestrictBackgroundNetworkTestCase {
+
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+
+        setMeteredNetwork();
+        setRestrictBackground(false);
+        registerBroadcastReceiver();
+   }
+
+    @Override
+    protected void tearDown() throws Exception {
+        super.tearDown();
+
+        try {
+            resetMeteredNetwork();
+        } finally {
+            setRestrictBackground(false);
+        }
+    }
+
+    public void testGetRestrictBackgroundStatus_disabled() throws Exception {
+        removeRestrictBackgroundWhitelist(mUid);
+        assertRestrictBackgroundChangedReceived(0);
+        assertRestrictBackgroundStatus(RESTRICT_BACKGROUND_STATUS_DISABLED);
+
+        // Sanity check: make sure status is always disabled, never whitelisted
+        addRestrictBackgroundWhitelist(mUid);
+        assertRestrictBackgroundChangedReceived(0);
+        assertRestrictBackgroundStatus(RESTRICT_BACKGROUND_STATUS_DISABLED);
+    }
+
+    public void testGetRestrictBackgroundStatus_whitelisted() throws Exception {
+        setRestrictBackground(true);
+        assertRestrictBackgroundChangedReceived(1);
+
+        addRestrictBackgroundWhitelist(mUid);
+        assertRestrictBackgroundChangedReceived(2);
+        assertRestrictBackgroundStatus(RESTRICT_BACKGROUND_STATUS_WHITELISTED);
+    }
+
+    public void testGetRestrictBackgroundStatus_enabled() throws Exception {
+        setRestrictBackground(true);
+        assertRestrictBackgroundChangedReceived(1);
+        assertRestrictBackgroundStatus(RESTRICT_BACKGROUND_STATUS_ENABLED);
+
+        removeRestrictBackgroundWhitelist(mUid);
+        assertRestrictBackgroundChangedReceived(1);
+        assertRestrictBackgroundStatus(RESTRICT_BACKGROUND_STATUS_ENABLED);
+
+        // Make sure app is allowed if running a foreground service.
+        assertBackgroundNetworkAccess(false);
+        startForegroundService();
+        assertForegroundServiceState();
+        assertBackgroundNetworkAccess(true);
+    }
+
+    public void testGetRestrictBackgroundStatus_blacklisted() throws Exception {
+        addRestrictBackgroundBlacklist(mUid);
+        assertRestrictBackgroundChangedReceived(1);
+        assertRestrictBackgroundStatus(RESTRICT_BACKGROUND_STATUS_ENABLED);
+
+        // Make sure blacklist prevails over whitelist.
+        setRestrictBackground(true);
+        assertRestrictBackgroundChangedReceived(2);
+        assertRestrictBackgroundStatus(RESTRICT_BACKGROUND_STATUS_ENABLED);
+        addRestrictBackgroundWhitelist(mUid);
+        assertRestrictBackgroundChangedReceived(3);
+        assertRestrictBackgroundStatus(RESTRICT_BACKGROUND_STATUS_ENABLED);
+
+        // Check status after removing blacklist.
+        removeRestrictBackgroundBlacklist(mUid);
+        assertRestrictBackgroundChangedReceived(4);
+        assertRestrictBackgroundStatus(RESTRICT_BACKGROUND_STATUS_WHITELISTED);
+        setRestrictBackground(false);
+        assertRestrictBackgroundChangedReceived(5);
+        assertRestrictBackgroundStatus(RESTRICT_BACKGROUND_STATUS_DISABLED);
+    }
+}
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/MyActivity.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/MyActivity.java
index 375c852..0d0bc58 100644
--- a/tests/cts/hostside/app/src/com/android/cts/net/hostside/MyActivity.java
+++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/MyActivity.java
@@ -18,13 +18,9 @@
 
 import android.app.Activity;
 import android.content.Intent;
-import android.net.VpnService;
 import android.os.Bundle;
-import android.os.ParcelFileDescriptor;
 import android.view.WindowManager;
 
-import java.util.Arrays;
-import java.util.ArrayList;
 import java.util.concurrent.LinkedBlockingQueue;
 import java.util.concurrent.TimeUnit;
 
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/MyVpnService.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/MyVpnService.java
index a3f400c..90a3ce4 100644
--- a/tests/cts/hostside/app/src/com/android/cts/net/hostside/MyVpnService.java
+++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/MyVpnService.java
@@ -26,7 +26,6 @@
 import java.io.IOException;
 import java.net.InetAddress;
 import java.net.UnknownHostException;
-import java.util.ArrayList;
 
 public class MyVpnService extends VpnService {
 
diff --git a/tests/cts/hostside/app2/Android.mk b/tests/cts/hostside/app2/Android.mk
new file mode 100644
index 0000000..3b59f8f
--- /dev/null
+++ b/tests/cts/hostside/app2/Android.mk
@@ -0,0 +1,34 @@
+#
+# Copyright (C) 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+LOCAL_SDK_VERSION := current
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_PACKAGE_NAME := CtsHostsideNetworkTestsApp2
+
+LOCAL_PROGUARD_ENABLED := disabled
+LOCAL_DEX_PREOPT := false
+
+# Tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts
+
+include $(BUILD_CTS_SUPPORT_PACKAGE)
diff --git a/tests/cts/hostside/app2/AndroidManifest.xml b/tests/cts/hostside/app2/AndroidManifest.xml
new file mode 100644
index 0000000..9ce5781
--- /dev/null
+++ b/tests/cts/hostside/app2/AndroidManifest.xml
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2016 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.cts.net.hostside.app2" >
+
+    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
+    <uses-permission android:name="android.permission.INTERNET" />
+
+    <!--
+         This application is used to listen to RESTRICT_BACKGROUND_CHANGED intents and store
+         them in a shared preferences which is then read by the test app. These broadcasts are
+         handled by 2 listeners, one defined the manifest and another dynamically registered by
+         a service.
+
+         The manifest-defined listener also handles ordered broadcasts used to share data with the
+         test app.
+    -->
+    <application>
+        <service android:name=".MyService" android:exported="true"/>
+        <service android:name=".MyForegroundService" android:exported="true"/>
+
+        <receiver android:name=".MyBroadcastReceiver" >
+            <intent-filter>
+                <action android:name="android.net.conn.RESTRICT_BACKGROUND_CHANGED" />
+                <action android:name="com.android.cts.net.hostside.app2.action.GET_COUNTERS" />
+                <action android:name="com.android.cts.net.hostside.app2.action.CHECK_NETWORK" />
+            </intent-filter>
+        </receiver>
+    </application>
+
+</manifest>
\ No newline at end of file
diff --git a/tests/cts/hostside/app2/src/com/android/cts/net/hostside/app2/Common.java b/tests/cts/hostside/app2/src/com/android/cts/net/hostside/app2/Common.java
new file mode 100644
index 0000000..668669b
--- /dev/null
+++ b/tests/cts/hostside/app2/src/com/android/cts/net/hostside/app2/Common.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.cts.net.hostside.app2;
+
+import android.content.Context;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.util.Log;
+
+public final class Common {
+
+    static final String TAG = "CtsNetApp2";
+
+    // Constants below must match values defined on app's
+    // AbstractRestrictBackgroundNetworkTestCase.java
+    static final String MANIFEST_RECEIVER = "ManifestReceiver";
+    static final String DYNAMIC_RECEIVER = "DynamicReceiver";
+    static final String ACTION_GET_COUNTERS =
+            "com.android.cts.net.hostside.app2.action.GET_COUNTERS";
+    static final String ACTION_CHECK_NETWORK =
+            "com.android.cts.net.hostside.app2.action.CHECK_NETWORK";
+    static final String ACTION_RECEIVER_READY =
+            "com.android.cts.net.hostside.app2.action.RECEIVER_READY";
+    static final String EXTRA_ACTION = "com.android.cts.net.hostside.app2.extra.ACTION";
+    static final String EXTRA_RECEIVER_NAME =
+            "com.android.cts.net.hostside.app2.extra.RECEIVER_NAME";
+    static final char RESULT_SEPARATOR = ';';
+    static final String STATUS_NETWORK_UNAVAILABLE_PREFIX = "NetworkUnavailable:";
+    static final String STATUS_NETWORK_AVAILABLE_PREFIX = "NetworkAvailable:";
+
+    static int getUid(Context context) {
+        final String packageName = context.getPackageName();
+        try {
+            return context.getPackageManager().getPackageUid(packageName, 0);
+        } catch (NameNotFoundException e) {
+            throw new IllegalStateException("Could not get UID for " + packageName, e);
+        }
+    }
+}
diff --git a/tests/cts/hostside/app2/src/com/android/cts/net/hostside/app2/MyBroadcastReceiver.java b/tests/cts/hostside/app2/src/com/android/cts/net/hostside/app2/MyBroadcastReceiver.java
new file mode 100644
index 0000000..07f717b
--- /dev/null
+++ b/tests/cts/hostside/app2/src/com/android/cts/net/hostside/app2/MyBroadcastReceiver.java
@@ -0,0 +1,178 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.net.hostside.app2;
+
+import static android.net.ConnectivityManager.ACTION_RESTRICT_BACKGROUND_CHANGED;
+import static com.android.cts.net.hostside.app2.Common.ACTION_CHECK_NETWORK;
+import static com.android.cts.net.hostside.app2.Common.ACTION_GET_COUNTERS;
+import static com.android.cts.net.hostside.app2.Common.ACTION_RECEIVER_READY;
+import static com.android.cts.net.hostside.app2.Common.EXTRA_ACTION;
+import static com.android.cts.net.hostside.app2.Common.EXTRA_RECEIVER_NAME;
+import static com.android.cts.net.hostside.app2.Common.MANIFEST_RECEIVER;
+import static com.android.cts.net.hostside.app2.Common.RESULT_SEPARATOR;
+import static com.android.cts.net.hostside.app2.Common.STATUS_NETWORK_AVAILABLE_PREFIX;
+import static com.android.cts.net.hostside.app2.Common.STATUS_NETWORK_UNAVAILABLE_PREFIX;
+import static com.android.cts.net.hostside.app2.Common.TAG;
+import static com.android.cts.net.hostside.app2.Common.getUid;
+
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.TimeUnit;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.net.ConnectivityManager;
+import android.net.NetworkInfo;
+import android.util.Log;
+
+/**
+ * Receiver used to:
+ * <ol>
+ * <li>Stored received RESTRICT_BACKGROUND_CHANGED broadcasts in a shared preference.
+ * <li>Returned the number of RESTRICT_BACKGROUND_CHANGED broadcasts in an ordered broadcast.
+ * </ol>
+ */
+public class MyBroadcastReceiver extends BroadcastReceiver {
+
+    private static final int NETWORK_TIMEOUT_MS = 15 * 1000;
+
+    private final String mName;
+
+    public MyBroadcastReceiver() {
+        this(MANIFEST_RECEIVER);
+    }
+
+    MyBroadcastReceiver(String name) {
+        Log.d(TAG, "Constructing MyBroadcastReceiver named " + name);
+        mName = name;
+    }
+
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        Log.d(TAG, "onReceive() for " + mName + ": " + intent);
+        final String action = intent.getAction();
+        switch (action) {
+            case ACTION_RESTRICT_BACKGROUND_CHANGED:
+                increaseCounter(context, action);
+                break;
+            case ACTION_GET_COUNTERS:
+                setResultDataFromCounter(context, intent);
+                break;
+            case ACTION_CHECK_NETWORK:
+                checkNetwork(context, intent);
+                break;
+            case ACTION_RECEIVER_READY:
+                final String message = mName + " is ready to rumble";
+                Log.d(TAG, message);
+                setResultData(message);
+                break;
+            default:
+                Log.e(TAG, "received unexpected action: " + action);
+        }
+    }
+
+    private void increaseCounter(Context context, String action) {
+        final SharedPreferences prefs = context.getSharedPreferences(mName, Context.MODE_PRIVATE);
+        final int value = prefs.getInt(action, 0) + 1;
+        Log.d(TAG, "increaseCounter('" + action + "'): setting '" + mName + "' to " + value);
+        prefs.edit().putInt(action, value).apply();
+    }
+
+    private int getCounter(Context context, String action, String receiverName) {
+        final SharedPreferences prefs = context.getSharedPreferences(receiverName,
+                Context.MODE_PRIVATE);
+        final int value = prefs.getInt(action, 0);
+        Log.d(TAG, "getCounter('" + action + "', '" + receiverName + "'): " + value);
+        return value;
+    }
+
+    private void checkNetwork(final Context context, Intent intent) {
+        final ConnectivityManager cm = (ConnectivityManager) context
+                .getSystemService(Context.CONNECTIVITY_SERVICE);
+
+        final StringBuilder data = new StringBuilder();
+        final int apiStatus = cm.getRestrictBackgroundStatus();
+        String netStatus;
+        try {
+            netStatus = checkNetworkStatus(context, cm);
+        } catch (InterruptedException e) {
+            Log.e(TAG, "Timeout checking network status");
+            setResultData(null);
+            return;
+        }
+        data.append(apiStatus).append(RESULT_SEPARATOR);
+        if (netStatus != null) {
+            data.append(netStatus);
+        }
+        Log.d(TAG, "checkNetwork: returning " + data);
+        setResultData(data.toString());
+    }
+
+    private String checkNetworkStatus(final Context context, final ConnectivityManager cm)
+            throws InterruptedException {
+        final LinkedBlockingQueue<String> result = new LinkedBlockingQueue<>(1);
+        new Thread(new Runnable() {
+
+            @Override
+            public void run() {
+                // TODO: connect to a hostside server instead
+                final String address = "http://example.com";
+                final NetworkInfo networkInfo = cm.getActiveNetworkInfo();
+                Log.d(TAG, "Running checkNetworkStatus() on thread "
+                        + Thread.currentThread().getName() + " for UID " + getUid(context)
+                        + "\n\tactiveNetworkInfo: " + networkInfo + "\n\tURL: " + address);
+                String prefix = STATUS_NETWORK_AVAILABLE_PREFIX;
+                try {
+                    final URL url = new URL(address);
+                    final HttpURLConnection conn = (HttpURLConnection) url.openConnection();
+                    conn.setReadTimeout(NETWORK_TIMEOUT_MS);
+                    conn.setConnectTimeout(NETWORK_TIMEOUT_MS);
+                    conn.setRequestMethod("GET");
+                    conn.setDoInput(true);
+                    conn.connect();
+                    final int response = conn.getResponseCode();
+                    Log.d(TAG, "HTTP response for " + address + ": " + response);
+                } catch (Exception e) {
+                    Log.d(TAG, "Exception getting " + address + ": " + e);
+                    prefix = STATUS_NETWORK_UNAVAILABLE_PREFIX + "Exception " + e + ":";
+                }
+                final String netInfo = prefix + networkInfo;
+                Log.d(TAG, "Offering " + netInfo);
+                result.offer(netInfo);
+            }
+        }, mName).start();
+        return result.poll(NETWORK_TIMEOUT_MS * 2, TimeUnit.MILLISECONDS);
+    }
+
+    private void setResultDataFromCounter(Context context, Intent intent) {
+        final String action = intent.getStringExtra(EXTRA_ACTION);
+        if (action == null) {
+            Log.e(TAG, "Missing extra '" + EXTRA_ACTION + "' on " + intent);
+            return;
+        }
+        final String receiverName = intent.getStringExtra(EXTRA_RECEIVER_NAME);
+        if (receiverName == null) {
+            Log.e(TAG, "Missing extra '" + EXTRA_RECEIVER_NAME + "' on " + intent);
+            return;
+        }
+        final int counter = getCounter(context, action, receiverName);
+        setResultData(String.valueOf(counter));
+    }
+}
diff --git a/tests/cts/hostside/app2/src/com/android/cts/net/hostside/app2/MyForegroundService.java b/tests/cts/hostside/app2/src/com/android/cts/net/hostside/app2/MyForegroundService.java
new file mode 100644
index 0000000..bbafd4c
--- /dev/null
+++ b/tests/cts/hostside/app2/src/com/android/cts/net/hostside/app2/MyForegroundService.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.cts.net.hostside.app2;
+
+import static com.android.cts.net.hostside.app2.Common.TAG;
+import android.R;
+import android.app.Notification;
+import android.app.Service;
+import android.content.Intent;
+import android.os.IBinder;
+import android.util.Log;
+
+/**
+ * Service used to change app state to FOREGROUND_SERVICE.
+ */
+public class MyForegroundService extends Service {
+
+    @Override
+    public IBinder onBind(Intent intent) {
+        return null;
+    }
+
+    @Override
+    public int onStartCommand(Intent intent, int flags, int startId) {
+        Log.d(TAG, "MyForegroundService.onStartCommand: " + intent);
+        startForeground(42, new Notification.Builder(this)
+            .setSmallIcon(R.drawable.ic_dialog_alert) // any icon is fine
+            .build());
+        return START_STICKY;
+    }
+}
diff --git a/tests/cts/hostside/app2/src/com/android/cts/net/hostside/app2/MyService.java b/tests/cts/hostside/app2/src/com/android/cts/net/hostside/app2/MyService.java
new file mode 100644
index 0000000..e6454c7
--- /dev/null
+++ b/tests/cts/hostside/app2/src/com/android/cts/net/hostside/app2/MyService.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.cts.net.hostside.app2;
+
+import static android.net.ConnectivityManager.ACTION_RESTRICT_BACKGROUND_CHANGED;
+import static com.android.cts.net.hostside.app2.Common.ACTION_RECEIVER_READY;
+import static com.android.cts.net.hostside.app2.Common.DYNAMIC_RECEIVER;
+import static com.android.cts.net.hostside.app2.Common.TAG;
+
+import android.app.Service;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.IBinder;
+import android.util.Log;
+
+/**
+ * Service used to dynamically register a broadcast receiver.
+ */
+public class MyService extends Service {
+
+    @Override
+    public IBinder onBind(Intent intent) {
+        return null;
+    }
+
+    @Override
+    public int onStartCommand(Intent intent, int flags, int startId) {
+        Log.d(TAG, "MyService.onStartCommand: " + intent);
+        final Context context = getApplicationContext();
+        final MyBroadcastReceiver myReceiver = new MyBroadcastReceiver(DYNAMIC_RECEIVER);
+        context.registerReceiver(myReceiver, new IntentFilter(ACTION_RECEIVER_READY));
+        context.registerReceiver(myReceiver, new IntentFilter(ACTION_RESTRICT_BACKGROUND_CHANGED));
+        Log.d(TAG, "receiver registered");
+        return START_STICKY;
+    }
+}
diff --git a/tests/cts/hostside/src/com/android/cts/net/HostsideNetworkTestCase.java b/tests/cts/hostside/src/com/android/cts/net/HostsideNetworkTestCase.java
new file mode 100644
index 0000000..39b5652
--- /dev/null
+++ b/tests/cts/hostside/src/com/android/cts/net/HostsideNetworkTestCase.java
@@ -0,0 +1,184 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.net;
+
+import com.android.cts.migration.MigrationHelper;
+import com.android.ddmlib.Log;
+import com.android.ddmlib.testrunner.RemoteAndroidTestRunner;
+import com.android.ddmlib.testrunner.TestIdentifier;
+import com.android.ddmlib.testrunner.TestResult;
+import com.android.ddmlib.testrunner.TestResult.TestStatus;
+import com.android.ddmlib.testrunner.TestRunResult;
+import com.android.tradefed.build.IBuildInfo;
+import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.result.CollectingTestListener;
+import com.android.tradefed.testtype.DeviceTestCase;
+import com.android.tradefed.testtype.IAbi;
+import com.android.tradefed.testtype.IAbiReceiver;
+import com.android.tradefed.testtype.IBuildReceiver;
+
+import java.io.FileNotFoundException;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+abstract class HostsideNetworkTestCase extends DeviceTestCase implements IAbiReceiver,
+        IBuildReceiver {
+    protected static final boolean DEBUG = false;
+    protected static final String TAG = "HostsideNetworkTests";
+    protected static final String TEST_PKG = "com.android.cts.net.hostside";
+    protected static final String TEST_APK = "CtsHostsideNetworkTestsApp.apk";
+
+    private IAbi mAbi;
+    private IBuildInfo mCtsBuild;
+
+    @Override
+    public void setAbi(IAbi abi) {
+        mAbi = abi;
+    }
+
+    @Override
+    public void setBuild(IBuildInfo buildInfo) {
+        mCtsBuild = buildInfo;
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+
+        assertNotNull(mAbi);
+        assertNotNull(mCtsBuild);
+
+        assertTrue("wi-fi not enabled", getDevice().isWifiEnabled());
+
+        uninstallPackage(TEST_PKG, false);
+        installPackage(TEST_APK);
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        super.tearDown();
+
+        uninstallPackage(TEST_PKG, true);
+    }
+
+    protected void installPackage(String apk) throws FileNotFoundException,
+            DeviceNotAvailableException {
+        assertNull(getDevice().installPackage(MigrationHelper.getTestFile(mCtsBuild, apk), false));
+    }
+
+    protected void uninstallPackage(String packageName, boolean shouldSucceed)
+            throws DeviceNotAvailableException {
+        final String result = getDevice().uninstallPackage(packageName);
+        if (shouldSucceed) {
+            assertNull("uninstallPackage(" + packageName + ") failed: " + result, result);
+        }
+    }
+
+    protected void assertPackageUninstalled(String packageName) throws DeviceNotAvailableException,
+            InterruptedException {
+        final String command = "cmd package list packages " + packageName;
+        final int max_tries = 5;
+        for (int i = 1; i <= max_tries; i++) {
+            final String result = runCommand(command);
+            if (result.trim().isEmpty()) {
+                return;
+            }
+            // 'list packages' filters by substring, so we need to iterate with the results
+            // and check one by one, otherwise 'com.android.cts.net.hostside' could return
+            // 'com.android.cts.net.hostside.app2'
+            boolean found = false;
+            for (String line : result.split("[\\r\\n]+")) {
+                if (line.endsWith(packageName)) {
+                    found = true;
+                    break;
+                }
+            }
+            if (!found) {
+                return;
+            }
+            i++;
+            Log.v(TAG, "Package " + packageName + " not uninstalled yet (" + result
+                    + "); sleeping 1s before polling again");
+            Thread.sleep(1000);
+        }
+        fail("Package '" + packageName + "' not uinstalled after " + max_tries + " seconds");
+    }
+
+    protected void runDeviceTests(String packageName, String testClassName)
+            throws DeviceNotAvailableException {
+        runDeviceTests(packageName, testClassName, null);
+    }
+
+    protected void runDeviceTests(String packageName, String testClassName, String methodName)
+            throws DeviceNotAvailableException {
+        RemoteAndroidTestRunner testRunner = new RemoteAndroidTestRunner(packageName,
+                "android.support.test.runner.AndroidJUnitRunner", getDevice().getIDevice());
+
+        if (testClassName != null) {
+            if (methodName != null) {
+                testRunner.setMethodName(testClassName, methodName);
+            } else {
+                testRunner.setClassName(testClassName);
+            }
+        }
+
+        final CollectingTestListener listener = new CollectingTestListener();
+        getDevice().runInstrumentationTests(testRunner, listener);
+
+        final TestRunResult result = listener.getCurrentRunResults();
+        if (result.isRunFailure()) {
+            throw new AssertionError("Failed to successfully run device tests for "
+                    + result.getName() + ": " + result.getRunFailureMessage());
+        }
+
+        if (result.hasFailedTests()) {
+            // build a meaningful error message
+            StringBuilder errorBuilder = new StringBuilder("on-device tests failed:\n");
+            for (Map.Entry<TestIdentifier, TestResult> resultEntry :
+                result.getTestResults().entrySet()) {
+                if (!resultEntry.getValue().getStatus().equals(TestStatus.PASSED)) {
+                    errorBuilder.append(resultEntry.getKey().toString());
+                    errorBuilder.append(":\n");
+                    errorBuilder.append(resultEntry.getValue().getStackTrace());
+                }
+            }
+            throw new AssertionError(errorBuilder.toString());
+        }
+    }
+
+    private static final Pattern UID_PATTERN =
+            Pattern.compile(".*userId=([0-9]+)$", Pattern.MULTILINE);
+
+    protected int getUid(String packageName) throws DeviceNotAvailableException {
+        final String output = runCommand("dumpsys package " + packageName);
+        final Matcher matcher = UID_PATTERN.matcher(output);
+        while (matcher.find()) {
+            final String match = matcher.group(1);
+            return Integer.parseInt(match);
+        }
+        throw new RuntimeException("Did not find regexp '" + UID_PATTERN + "' on adb output\n"
+                + output);
+    }
+
+    protected String runCommand(String command) throws DeviceNotAvailableException {
+        Log.d(TAG, "Command: '" + command + "'");
+        final String output = getDevice().executeShellCommand(command);
+        if (DEBUG) Log.v(TAG, "Output: " + output.trim());
+        return output;
+    }
+}
diff --git a/tests/cts/hostside/src/com/android/cts/net/HostsideNetworkTests.java b/tests/cts/hostside/src/com/android/cts/net/HostsideNetworkTests.java
deleted file mode 100644
index a7698f3..0000000
--- a/tests/cts/hostside/src/com/android/cts/net/HostsideNetworkTests.java
+++ /dev/null
@@ -1,105 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.cts.net;
-
-import com.android.cts.tradefed.build.CtsBuildHelper;
-import com.android.ddmlib.testrunner.RemoteAndroidTestRunner;
-import com.android.ddmlib.testrunner.TestIdentifier;
-import com.android.ddmlib.testrunner.TestResult;
-import com.android.ddmlib.testrunner.TestResult.TestStatus;
-import com.android.ddmlib.testrunner.TestRunResult;
-import com.android.tradefed.build.IBuildInfo;
-import com.android.tradefed.device.DeviceNotAvailableException;
-import com.android.tradefed.testtype.DeviceTestCase;
-import com.android.tradefed.testtype.IAbi;
-import com.android.tradefed.testtype.IAbiReceiver;
-import com.android.tradefed.testtype.IBuildReceiver;
-import com.android.tradefed.device.DeviceNotAvailableException;
-import com.android.tradefed.device.ITestDevice;
-import com.android.tradefed.result.CollectingTestListener;
-
-import java.util.Map;
-
-public class HostsideNetworkTests extends DeviceTestCase implements IAbiReceiver, IBuildReceiver {
-    private static final String TEST_PKG = "com.android.cts.net.hostside";
-    private static final String TEST_APK = "CtsHostsideNetworkTestsApp.apk";
-
-    private IAbi mAbi;
-    private CtsBuildHelper mCtsBuild;
-
-    @Override
-    public void setAbi(IAbi abi) {
-        mAbi = abi;
-    }
-
-    @Override
-    public void setBuild(IBuildInfo buildInfo) {
-        mCtsBuild = CtsBuildHelper.createBuildHelper(buildInfo);
-    }
-
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
-
-        assertNotNull(mAbi);
-        assertNotNull(mCtsBuild);
-
-        getDevice().uninstallPackage(TEST_PKG);
-
-        assertNull(getDevice().installPackage(mCtsBuild.getTestApp(TEST_APK), false));
-    }
-
-    @Override
-    protected void tearDown() throws Exception {
-        super.tearDown();
-
-        getDevice().uninstallPackage(TEST_PKG);
-    }
-
-    public void testVpn() throws Exception {
-        runDeviceTests(TEST_PKG, ".VpnTest");
-    }
-
-    public void runDeviceTests(String packageName, String testClassName)
-           throws DeviceNotAvailableException {
-        RemoteAndroidTestRunner testRunner = new RemoteAndroidTestRunner(packageName,
-                "android.support.test.runner.AndroidJUnitRunner", getDevice().getIDevice());
-
-        final CollectingTestListener listener = new CollectingTestListener();
-        getDevice().runInstrumentationTests(testRunner, listener);
-
-        final TestRunResult result = listener.getCurrentRunResults();
-        if (result.isRunFailure()) {
-            throw new AssertionError("Failed to successfully run device tests for "
-                    + result.getName() + ": " + result.getRunFailureMessage());
-        }
-
-        if (result.hasFailedTests()) {
-            // build a meaningful error message
-            StringBuilder errorBuilder = new StringBuilder("on-device tests failed:\n");
-            for (Map.Entry<TestIdentifier, TestResult> resultEntry :
-                result.getTestResults().entrySet()) {
-                if (!resultEntry.getValue().getStatus().equals(TestStatus.PASSED)) {
-                    errorBuilder.append(resultEntry.getKey().toString());
-                    errorBuilder.append(":\n");
-                    errorBuilder.append(resultEntry.getValue().getStackTrace());
-                }
-            }
-            throw new AssertionError(errorBuilder.toString());
-        }
-    }
-}
diff --git a/tests/cts/hostside/src/com/android/cts/net/HostsideRestrictBackgroundNetworkTests.java b/tests/cts/hostside/src/com/android/cts/net/HostsideRestrictBackgroundNetworkTests.java
new file mode 100644
index 0000000..15a4e0a
--- /dev/null
+++ b/tests/cts/hostside/src/com/android/cts/net/HostsideRestrictBackgroundNetworkTests.java
@@ -0,0 +1,158 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.net;
+
+import com.android.ddmlib.Log;
+import com.android.tradefed.device.DeviceNotAvailableException;
+
+public class HostsideRestrictBackgroundNetworkTests extends HostsideNetworkTestCase {
+
+    private static final String TEST_APP2_PKG = "com.android.cts.net.hostside.app2";
+    private static final String TEST_APP2_APK = "CtsHostsideNetworkTestsApp2.apk";
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+
+        uninstallPackage(TEST_APP2_PKG, false);
+        installPackage(TEST_APP2_APK);
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        super.tearDown();
+
+        uninstallPackage(TEST_APP2_PKG, true);
+    }
+
+    public void testDataSaverMode_disabled() throws Exception {
+        runDeviceTests(TEST_PKG, TEST_PKG + ".DataSaverModeTest",
+                "testGetRestrictBackgroundStatus_disabled");
+    }
+
+    public void testDataSaverMode_whitelisted() throws Exception {
+        runDeviceTests(TEST_PKG, TEST_PKG + ".DataSaverModeTest",
+                "testGetRestrictBackgroundStatus_whitelisted");
+    }
+
+    public void testDataSaverMode_enabled() throws Exception {
+        runDeviceTests(TEST_PKG, TEST_PKG + ".DataSaverModeTest",
+                "testGetRestrictBackgroundStatus_enabled");
+    }
+
+    public void testDataSaverMode_blacklisted() throws Exception {
+        runDeviceTests(TEST_PKG, TEST_PKG + ".DataSaverModeTest",
+                "testGetRestrictBackgroundStatus_blacklisted");
+    }
+
+    public void testDataSaverMode_reinstall() throws Exception {
+        final int oldUid = getUid(TEST_PKG);
+        testDataSaverMode_whitelisted();
+
+        uninstallPackage(TEST_PKG, true);
+        assertPackageUninstalled(TEST_PKG);
+        assertRestrictBackgroundWhitelist(oldUid, false);
+
+        installPackage(TEST_APK);
+        final int newUid = getUid(TEST_PKG);
+        assertRestrictBackgroundWhitelist(oldUid, false);
+        assertRestrictBackgroundWhitelist(newUid, false);
+    }
+
+    public void testBatterySaverMode_disabled() throws Exception {
+        runDeviceTests(TEST_PKG, TEST_PKG + ".BatterySaverModeTest",
+                "testBackgroundNetworkAccess_disabled");
+    }
+
+    public void testBatterySaverMode_whitelisted() throws Exception {
+        runDeviceTests(TEST_PKG, TEST_PKG + ".BatterySaverModeTest",
+                "testBackgroundNetworkAccess_whitelisted");
+    }
+
+    public void testBatterySaverMode_enabled() throws Exception {
+        runDeviceTests(TEST_PKG, TEST_PKG + ".BatterySaverModeTest",
+                "testBackgroundNetworkAccess_enabled");
+    }
+
+    public void testBatterySaverMode_reinstall() throws Exception {
+        testBatterySaverMode_whitelisted();
+
+        uninstallPackage(TEST_PKG, true);
+        assertPackageUninstalled(TEST_PKG);
+        assertPowerSaveModeWhitelist(TEST_PKG, false);
+
+        installPackage(TEST_APK);
+        assertPowerSaveModeWhitelist(TEST_PKG, false);
+    }
+
+    public void testBatteryBatterySaverModeNonMeteredTest_disabled() throws Exception {
+        runDeviceTests(TEST_PKG, TEST_PKG + ".BatterySaverModeNonMeteredTest",
+                "testBackgroundNetworkAccess_disabled");
+    }
+
+    public void testBatteryBatterySaverModeNonMeteredTest_whitelisted() throws Exception {
+        runDeviceTests(TEST_PKG, TEST_PKG + ".BatterySaverModeNonMeteredTest",
+                "testBackgroundNetworkAccess_whitelisted");
+    }
+
+    public void testBatteryBatterySaverModeNonMeteredTest_enabled() throws Exception {
+        runDeviceTests(TEST_PKG, TEST_PKG + ".BatterySaverModeNonMeteredTest",
+                "testBackgroundNetworkAccess_enabled");
+    }
+
+    private void assertRestrictBackgroundWhitelist(int uid, boolean expected) throws Exception {
+        final int max_tries = 5;
+        boolean actual = false;
+        for (int i = 1; i <= max_tries; i++) {
+            final String output = runCommand("cmd netpolicy list restrict-background-whitelist ");
+            actual = output.contains(Integer.toString(uid));
+            if (expected == actual) {
+                return;
+            }
+            Log.v(TAG, "whitelist check for uid " + uid + " doesn't match yet (expected "
+                    + expected + ", got " + actual + "); sleeping 1s before polling again");
+            Thread.sleep(1000);
+        }
+        fail("whitelist check for uid " + uid + " failed: expected "
+                + expected + ", got " + actual);
+    }
+
+    private void assertPowerSaveModeWhitelist(String packageName, boolean expected)
+            throws Exception {
+        // TODO: currently the power-save mode is behaving like idle, but once it changes, we'll
+        // need to use netpolicy for whitelisting
+        assertDelayedCommand("dumpsys deviceidle whitelist =" + packageName,
+                Boolean.toString(expected));
+    }
+
+    /**
+     * Asserts the result of a command, wait and re-running it a couple times if necessary.
+     */
+    private void assertDelayedCommand(String command, String expectedResult)
+            throws InterruptedException, DeviceNotAvailableException {
+        final int maxTries = 5;
+        for (int i = 1; i <= maxTries; i++) {
+            final String result = runCommand(command).trim();
+            if (result.equals(expectedResult)) return;
+            Log.v(TAG, "Command '" + command + "' returned '" + result + " instead of '"
+                    + expectedResult + "' on attempt #; sleeping 1s before polling again");
+            Thread.sleep(1000);
+        }
+        fail("Command '" + command + "' did not return '" + expectedResult + "' after " + maxTries
+                + " attempts");
+    }
+}
diff --git a/tests/cts/hostside/src/com/android/cts/net/HostsideVpnTests.java b/tests/cts/hostside/src/com/android/cts/net/HostsideVpnTests.java
new file mode 100644
index 0000000..dc965c5
--- /dev/null
+++ b/tests/cts/hostside/src/com/android/cts/net/HostsideVpnTests.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.net;
+
+public class HostsideVpnTests extends HostsideNetworkTestCase {
+
+    public void testVpn() throws Exception {
+        runDeviceTests(TEST_PKG, TEST_PKG + ".VpnTest");
+    }
+}
diff --git a/tests/cts/net/Android.mk b/tests/cts/net/Android.mk
index 6524871..c553a9b 100644
--- a/tests/cts/net/Android.mk
+++ b/tests/cts/net/Android.mk
@@ -27,7 +27,7 @@
 LOCAL_JAVA_LIBRARIES := voip-common conscrypt org.apache.http.legacy
 
 LOCAL_JNI_SHARED_LIBRARIES := libcts_jni libnativedns_jni \
-                              libnativemultinetwork_jni
+                              libnativemultinetwork_jni libnativehelper_compat_libc++
 
 # include CtsTestServer as a temporary hack to free net.cts from cts.stub.
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
@@ -40,6 +40,9 @@
 # uncomment when b/13249961 is fixed
 #LOCAL_SDK_VERSION := current
 
+# Tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts
+
 include $(BUILD_CTS_PACKAGE)
 
 include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/tests/cts/net/AndroidManifest.xml b/tests/cts/net/AndroidManifest.xml
index 848ed99..dd310a1 100644
--- a/tests/cts/net/AndroidManifest.xml
+++ b/tests/cts/net/AndroidManifest.xml
@@ -16,7 +16,7 @@
  -->
 
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="com.android.cts.net">
+    package="android.net.cts">
 
     <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
     <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
@@ -33,10 +33,16 @@
     <application>
         <uses-library android:name="android.test.runner" />
         <uses-library android:name="org.apache.http.legacy" android:required="false" />
+
+        <receiver android:name=".ConnectivityReceiver">
+            <intent-filter>
+                <action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
+            </intent-filter>
+        </receiver>
     </application>
 
     <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
-                     android:targetPackage="com.android.cts.net"
+                     android:targetPackage="android.net.cts"
                      android:label="CTS tests of android.net">
         <meta-data android:name="listener"
             android:value="com.android.cts.runner.CtsTestRunListener" />
diff --git a/tests/cts/net/AndroidTest.xml b/tests/cts/net/AndroidTest.xml
new file mode 100644
index 0000000..dc80339
--- /dev/null
+++ b/tests/cts/net/AndroidTest.xml
@@ -0,0 +1,25 @@
+<!-- Copyright (C) 2015 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<configuration description="Config for CTS Net test cases">
+    <target_preparer class="com.android.compatibility.common.tradefed.targetprep.WifiCheck" />
+    <target_preparer class="com.android.compatibility.common.tradefed.targetprep.ApkInstaller">
+        <option name="cleanup-apks" value="true" />
+        <option name="test-file-name" value="CtsNetTestCases.apk" />
+    </target_preparer>
+    <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+        <option name="package" value="android.net.cts" />
+        <option name="runtime-hint" value="9m4s" />
+    </test>
+</configuration>
diff --git a/tests/cts/net/jni/Android.mk b/tests/cts/net/jni/Android.mk
index ca82b30..0ec8d28 100644
--- a/tests/cts/net/jni/Android.mk
+++ b/tests/cts/net/jni/Android.mk
@@ -25,8 +25,8 @@
 
 LOCAL_C_INCLUDES := $(JNI_H_INCLUDE)
 
-LOCAL_SHARED_LIBRARIES := libnativehelper liblog
-
+LOCAL_SHARED_LIBRARIES := libnativehelper_compat_libc++ liblog
+LOCAL_CXX_STL := libc++_static
 include $(BUILD_SHARED_LIBRARY)
 
 include $(CLEAR_VARS)
@@ -35,5 +35,6 @@
 LOCAL_MODULE_TAGS := optional
 LOCAL_SRC_FILES := NativeMultinetworkJni.c
 LOCAL_C_INCLUDES := $(JNI_H_INCLUDE)
-LOCAL_SHARED_LIBRARIES := libandroid libnativehelper liblog
+LOCAL_SHARED_LIBRARIES := libandroid libnativehelper_compat_libc++ liblog
+LOCAL_CXX_STL := libc++_static
 include $(BUILD_SHARED_LIBRARY)
diff --git a/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java b/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
index 9a99c22..4112466 100644
--- a/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
+++ b/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
@@ -265,13 +265,16 @@
         }
 
         // We will register for a WIFI network being available or lost.
-        NetworkRequest request = new NetworkRequest.Builder()
+        final NetworkRequest request = new NetworkRequest.Builder()
                 .addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
                 .build();
-        TestNetworkCallback callback = new TestNetworkCallback();
+        final TestNetworkCallback callback = new TestNetworkCallback();
         mCm.registerNetworkCallback(request, callback);
 
-        boolean previousWifiEnabledState = mWifiManager.isWifiEnabled();
+        final TestNetworkCallback defaultTrackingCallback = new TestNetworkCallback();
+        mCm.registerDefaultNetworkCallback(defaultTrackingCallback);
+
+        final boolean previousWifiEnabledState = mWifiManager.isWifiEnabled();
 
         try {
             // Make sure WiFi is connected to an access point to start with.
@@ -284,12 +287,16 @@
             // is registered.
             assertTrue("Did not receive NetworkCallback.onAvailable for TRANSPORT_WIFI",
                     callback.waitForAvailable());
+
+            assertTrue("Did not receive NetworkCallback.onAvailable for any default network",
+                    defaultTrackingCallback.waitForAvailable());
         } catch (InterruptedException e) {
             fail("Broadcast receiver or NetworkCallback wait was interrupted.");
         } finally {
             mCm.unregisterNetworkCallback(callback);
+            mCm.unregisterNetworkCallback(defaultTrackingCallback);
 
-            // Return WiFI to its original enabled/disabled state.
+            // Return WiFi to its original enabled/disabled state.
             if (!previousWifiEnabledState) {
                 disconnectFromWifi();
             }
@@ -327,7 +334,7 @@
                 .build();
         mCm.registerNetworkCallback(request, pendingIntent);
 
-        boolean previousWifiEnabledState = mWifiManager.isWifiEnabled();
+        final boolean previousWifiEnabledState = mWifiManager.isWifiEnabled();
 
         try {
             // Make sure WiFi is connected to an access point to start with.
@@ -347,13 +354,43 @@
             pendingIntent.cancel();
             mContext.unregisterReceiver(receiver);
 
-            // Return WiFI to its original enabled/disabled state.
+            // Return WiFi to its original enabled/disabled state.
             if (!previousWifiEnabledState) {
                 disconnectFromWifi();
             }
         }
     }
 
+    /**
+     * Tests reporting of connectivity changed.
+     */
+    public void testConnectivityChanged() {
+        // We are going to ensure that we *don't* see the connectivity in the manifest.
+        ConnectivityReceiver.prepare();
+
+        // We will toggle the state of wifi to generate a connectivity change.
+        final boolean previousWifiEnabledState = mWifiManager.isWifiEnabled();
+        if (previousWifiEnabledState) {
+            disconnectFromWifi();
+        } else {
+            connectToWifi();
+        }
+
+        // The connectivity broadcast has been sent; push through a terminal broadcast
+        // to wait for in the receive to confirm it didn't see the connectivity change.
+        Intent finalIntent = new Intent(ConnectivityReceiver.FINAL_ACTION);
+        finalIntent.setClass(mContext, ConnectivityReceiver.class);
+        mContext.sendBroadcast(finalIntent);
+        assertFalse(ConnectivityReceiver.waitForBroadcast());
+
+        // Now restore previous state.
+        if (previousWifiEnabledState) {
+            connectToWifi();
+        } else {
+            disconnectFromWifi();
+        }
+    }
+
     /** Enable WiFi and wait for it to become connected to a network. */
     private void connectToWifi() {
         ConnectivityActionReceiver receiver = new ConnectivityActionReceiver(
diff --git a/tests/cts/net/src/android/net/cts/ConnectivityReceiver.java b/tests/cts/net/src/android/net/cts/ConnectivityReceiver.java
new file mode 100644
index 0000000..6a7b4a0
--- /dev/null
+++ b/tests/cts/net/src/android/net/cts/ConnectivityReceiver.java
@@ -0,0 +1,69 @@
+/*
+ * 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.net.cts;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.net.ConnectivityManager;
+import android.util.Log;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+public class ConnectivityReceiver extends BroadcastReceiver {
+    static boolean sReceivedConnectivity;
+    static boolean sReceivedFinal;
+    static CountDownLatch sLatch;
+
+    static void prepare() {
+        synchronized (ConnectivityReceiver.class) {
+            sReceivedConnectivity = sReceivedFinal = false;
+            sLatch = new CountDownLatch(1);
+        }
+    }
+
+    static boolean waitForBroadcast() {
+        try {
+            sLatch.await(30, TimeUnit.SECONDS);
+        } catch (InterruptedException e) {
+            throw new IllegalStateException(e);
+        }
+        synchronized (ConnectivityReceiver.class) {
+            sLatch = null;
+            if (!sReceivedFinal) {
+                throw new IllegalStateException("Never received final broadcast");
+            }
+            return sReceivedConnectivity;
+        }
+    }
+
+    static final String FINAL_ACTION = "android.net.cts.action.FINAL";
+
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        Log.i("ConnectivityReceiver", "Received: " + intent.getAction());
+        if (ConnectivityManager.CONNECTIVITY_ACTION.equals(intent.getAction())) {
+            sReceivedConnectivity = true;
+        } else if (FINAL_ACTION.equals(intent.getAction())) {
+            sReceivedFinal = true;
+            if (sLatch != null) {
+                sLatch.countDown();
+            }
+        }
+    }
+}
diff --git a/tests/cts/net/src/android/net/cts/LocalServerSocketTest.java b/tests/cts/net/src/android/net/cts/LocalServerSocketTest.java
index dc7be1f..103d1da 100644
--- a/tests/cts/net/src/android/net/cts/LocalServerSocketTest.java
+++ b/tests/cts/net/src/android/net/cts/LocalServerSocketTest.java
@@ -15,28 +15,27 @@
  */
 package android.net.cts;
 
+import junit.framework.TestCase;
+
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
 import android.net.LocalServerSocket;
 import android.net.LocalSocket;
 import android.net.LocalSocketAddress;
-import android.test.AndroidTestCase;
 
-public class LocalServerSocketTest extends AndroidTestCase {
+public class LocalServerSocketTest extends TestCase {
 
     public void testLocalServerSocket() throws IOException {
-        LocalServerSocket localServerSocket = new LocalServerSocket(LocalSocketTest.mSockAddr);
+        String address = "com.android.net.LocalServerSocketTest_testLocalServerSocket";
+        LocalServerSocket localServerSocket = new LocalServerSocket(address);
         assertNotNull(localServerSocket.getLocalSocketAddress());
-        commonFunctions(localServerSocket);
-    }
 
-    public void commonFunctions(LocalServerSocket localServerSocket) throws IOException {
         // create client socket
         LocalSocket clientSocket = new LocalSocket();
 
         // establish connection between client and server
-        clientSocket.connect(new LocalSocketAddress(LocalSocketTest.mSockAddr));
+        clientSocket.connect(new LocalSocketAddress(address));
         LocalSocket serverSocket = localServerSocket.accept();
 
         // send data from client to server
diff --git a/tests/cts/net/src/android/net/cts/LocalSocketTest.java b/tests/cts/net/src/android/net/cts/LocalSocketTest.java
index 865ec21..77f0a44 100644
--- a/tests/cts/net/src/android/net/cts/LocalSocketTest.java
+++ b/tests/cts/net/src/android/net/cts/LocalSocketTest.java
@@ -16,26 +16,31 @@
 
 package android.net.cts;
 
-import java.io.FileDescriptor;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
+import junit.framework.TestCase;
+
 import android.net.Credentials;
 import android.net.LocalServerSocket;
 import android.net.LocalSocket;
 import android.net.LocalSocketAddress;
-import android.test.AndroidTestCase;
 
-public class LocalSocketTest extends AndroidTestCase{
-    public final static String mSockAddr = "com.android.net.LocalSocketTest";
+import java.io.FileDescriptor;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
 
-    public void testLocalConnections() throws IOException{
+public class LocalSocketTest extends TestCase {
+    private final static String ADDRESS_PREFIX = "com.android.net.LocalSocketTest";
+
+    public void testLocalConnections() throws IOException {
+        String address = ADDRESS_PREFIX + "_testLocalConnections";
         // create client and server socket
-        LocalServerSocket localServerSocket = new LocalServerSocket(mSockAddr);
+        LocalServerSocket localServerSocket = new LocalServerSocket(address);
         LocalSocket clientSocket = new LocalSocket();
 
         // establish connection between client and server
-        LocalSocketAddress locSockAddr = new LocalSocketAddress(mSockAddr);
+        LocalSocketAddress locSockAddr = new LocalSocketAddress(address);
         assertFalse(clientSocket.isConnected());
         clientSocket.connect(locSockAddr);
         assertTrue(clientSocket.isConnected());
@@ -111,9 +116,10 @@
         }
     }
 
-    public void testAccessors() throws IOException{
+    public void testAccessors() throws IOException {
+        String address = ADDRESS_PREFIX + "_testAccessors";
         LocalSocket socket = new LocalSocket();
-        LocalSocketAddress addr = new LocalSocketAddress("secondary");
+        LocalSocketAddress addr = new LocalSocketAddress(address);
 
         assertFalse(socket.isBound());
         socket.bind(addr);
@@ -129,9 +135,9 @@
         socket.setSendBufferSize(3998);
         assertEquals(3998 << 1, socket.getSendBufferSize());
 
-        // Timeout is not support at present, so set is ignored
-        socket.setSoTimeout(1996);
         assertEquals(0, socket.getSoTimeout());
+        socket.setSoTimeout(1996);
+        assertTrue(socket.getSoTimeout() > 0);
 
         try {
             socket.getRemoteSocketAddress();
@@ -167,5 +173,127 @@
         } catch (UnsupportedOperationException e) {
             // expected
         }
+
+        socket.close();
+    }
+
+    public void testAvailable() throws Exception {
+        String address = ADDRESS_PREFIX + "_testAvailable";
+        LocalServerSocket localServerSocket = new LocalServerSocket(address);
+        LocalSocket clientSocket = new LocalSocket();
+
+        // establish connection between client and server
+        LocalSocketAddress locSockAddr = new LocalSocketAddress(address);
+        clientSocket.connect(locSockAddr);
+        assertTrue(clientSocket.isConnected());
+        LocalSocket serverSocket = localServerSocket.accept();
+
+        OutputStream clientOutputStream = clientSocket.getOutputStream();
+        InputStream serverInputStream = serverSocket.getInputStream();
+        assertEquals(0, serverInputStream.available());
+
+        byte[] buffer = new byte[50];
+        clientOutputStream.write(buffer);
+        assertEquals(50, serverInputStream.available());
+
+        InputStream clientInputStream = clientSocket.getInputStream();
+        OutputStream serverOutputStream = serverSocket.getOutputStream();
+        assertEquals(0, clientInputStream.available());
+        serverOutputStream.write(buffer);
+        assertEquals(50, serverInputStream.available());
+
+        clientSocket.close();
+        serverSocket.close();
+        localServerSocket.close();
+    }
+
+    public void testFlush() throws Exception {
+        String address = ADDRESS_PREFIX + "_testFlush";
+        LocalServerSocket localServerSocket = new LocalServerSocket(address);
+        LocalSocket clientSocket = new LocalSocket();
+
+        // establish connection between client and server
+        LocalSocketAddress locSockAddr = new LocalSocketAddress(address);
+        clientSocket.connect(locSockAddr);
+        assertTrue(clientSocket.isConnected());
+        LocalSocket serverSocket = localServerSocket.accept();
+
+        OutputStream clientOutputStream = clientSocket.getOutputStream();
+        InputStream serverInputStream = serverSocket.getInputStream();
+        testFlushWorks(clientOutputStream, serverInputStream);
+
+        OutputStream serverOutputStream = serverSocket.getOutputStream();
+        InputStream clientInputStream = clientSocket.getInputStream();
+        testFlushWorks(serverOutputStream, clientInputStream);
+
+        clientSocket.close();
+        serverSocket.close();
+        localServerSocket.close();
+    }
+
+    private void testFlushWorks(OutputStream outputStream, InputStream inputStream)
+            throws Exception {
+        final int bytesToTransfer = 50;
+        StreamReader inputStreamReader = new StreamReader(inputStream, bytesToTransfer);
+
+        byte[] buffer = new byte[bytesToTransfer];
+        outputStream.write(buffer);
+        assertEquals(bytesToTransfer, inputStream.available());
+
+        // Start consuming the data.
+        inputStreamReader.start();
+
+        // This doesn't actually flush any buffers, it just polls until the reader has read all the
+        // bytes.
+        outputStream.flush();
+
+        inputStreamReader.waitForCompletion(5000);
+        inputStreamReader.assertBytesRead(bytesToTransfer);
+        assertEquals(0, inputStream.available());
+    }
+
+    private static class StreamReader extends Thread {
+        private final InputStream is;
+        private final int expectedByteCount;
+        private final CountDownLatch completeLatch = new CountDownLatch(1);
+
+        private volatile Exception exception;
+        private int bytesRead;
+
+        private StreamReader(InputStream is, int expectedByteCount) {
+            this.is = is;
+            this.expectedByteCount = expectedByteCount;
+        }
+
+        @Override
+        public void run() {
+            try {
+                byte[] buffer = new byte[10];
+                int readCount;
+                while ((readCount = is.read(buffer)) >= 0) {
+                    bytesRead += readCount;
+                    if (bytesRead >= expectedByteCount) {
+                        break;
+                    }
+                }
+            } catch (IOException e) {
+                exception = e;
+            } finally {
+                completeLatch.countDown();
+            }
+        }
+
+        public void waitForCompletion(long waitMillis) throws Exception {
+            if (!completeLatch.await(waitMillis, TimeUnit.MILLISECONDS)) {
+                fail("Timeout waiting for completion");
+            }
+            if (exception != null) {
+                throw new Exception("Read failed", exception);
+            }
+        }
+
+        public void assertBytesRead(int expected) {
+            assertEquals(expected, bytesRead);
+        }
     }
 }
diff --git a/tests/cts/net/src/android/net/http/cts/X509TrustManagerExtensionsTest.java b/tests/cts/net/src/android/net/http/cts/X509TrustManagerExtensionsTest.java
index 9c0d774..99de614 100644
--- a/tests/cts/net/src/android/net/http/cts/X509TrustManagerExtensionsTest.java
+++ b/tests/cts/net/src/android/net/http/cts/X509TrustManagerExtensionsTest.java
@@ -17,61 +17,39 @@
 package android.net.http.cts;
 
 import android.net.http.X509TrustManagerExtensions;
-import android.util.Base64;
-
-import java.io.File;
-import java.io.ByteArrayInputStream;
 
 import java.security.KeyStore;
-import java.security.cert.CertificateFactory;
+import java.security.cert.Certificate;
 import java.security.cert.X509Certificate;
 
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.TrustManagerFactory;
+import javax.net.ssl.X509TrustManager;
+
 import junit.framework.TestCase;
 
-import com.android.org.conscrypt.TrustedCertificateStore;
-import com.android.org.conscrypt.TrustManagerImpl;
-
 public class X509TrustManagerExtensionsTest extends TestCase {
 
-    public void testIsUserAddedCert() throws Exception {
-        final String testCert =
-            "MIICfjCCAeegAwIBAgIJAMefIzKHY5H4MA0GCSqGSIb3DQEBBQUAMFgxCzAJBgNV" +
-            "BAYTAlVTMQswCQYDVQQIDAJDQTEWMBQGA1UEBwwNTW91bnRhaW4gVmlldzEPMA0G" +
-            "A1UECgwGR2V3Z3VsMRMwEQYDVQQDDApnZXdndWwuY29tMB4XDTEzMTEwNTAwNDE0" +
-            "MFoXDTEzMTIwNTAwNDE0MFowWDELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkNBMRYw" +
-            "FAYDVQQHDA1Nb3VudGFpbiBWaWV3MQ8wDQYDVQQKDAZHZXdndWwxEzARBgNVBAMM" +
-            "Cmdld2d1bC5jb20wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAKpc/I0Ss4sm" +
-            "yV2iX5xRMM7+XXAhiWrceGair4MpvDrGIa1kFj2phtx4IqTfDnNU7AhRJYkDYmJQ" +
-            "fUJ8i6F+I08uNiGVO4DtPJbZcBXg9ME9EMaJCslm995ueeNWSw1Ky8zM0tt4p+94" +
-            "BcXJ7PC3N2WgkvtE8xwNbaeUfhGPzJKXAgMBAAGjUDBOMB0GA1UdDgQWBBQQ/iW7" +
-            "JCkSI2sbn4nTBiZ9PSiO8zAfBgNVHSMEGDAWgBQQ/iW7JCkSI2sbn4nTBiZ9PSiO" +
-            "8zAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBQUAA4GBABQBrUOWTCSIl3vkRR3w" +
-            "3bPzh3BpqDmxH9xe4rZr+MVKKjpGjY1z2m2EEtyNz3tbgVQym5+si00DUHFL0IP1" +
-            "SuRULmPyEpTBVbV+PA5Kc967ZcDgYt4JtdMcCeKbIFaU6r8oEYEL2PTlNZmgbunM" +
-            "pXktkhVvNxZeSa8yM9bPhXkN";
+    private static X509TrustManager getFirstX509TrustManager(TrustManagerFactory tmf)
+            throws Exception {
+        for (TrustManager trustManager : tmf.getTrustManagers()) {
+             if (trustManager instanceof X509TrustManager) {
+                 return (X509TrustManager) trustManager;
+             }
+        }
+        fail("Unable to find X509TrustManager");
+        return null;
+    }
 
-        CertificateFactory cf = CertificateFactory.getInstance("X.509");
-        X509Certificate cert = (X509Certificate)cf.generateCertificate(
-            new ByteArrayInputStream(Base64.decode(testCert, Base64.DEFAULT)));
-
-        // Test without adding cert to keystore.
-        KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
-        X509TrustManagerExtensions tmeNegative =
-            new X509TrustManagerExtensions(new TrustManagerImpl(keyStore));
-        assertEquals(false, tmeNegative.isUserAddedCertificate(cert));
-
-        // Test with cert added to keystore.
-        final File DIR_TEMP = new File(System.getProperty("java.io.tmpdir"));
-        final File DIR_TEST = new File(DIR_TEMP, "test");
-        final File system = new File(DIR_TEST, "system-test");
-        final File added = new File(DIR_TEST, "added-test");
-        final File deleted = new File(DIR_TEST, "deleted-test");
-
-        TrustedCertificateStore tcs = new TrustedCertificateStore(system, added, deleted);
-        added.mkdirs();
-        tcs.installCertificate(cert);
-        X509TrustManagerExtensions tmePositive =
-            new X509TrustManagerExtensions(new TrustManagerImpl(keyStore, null, tcs));
-        assertEquals(true, tmePositive.isUserAddedCertificate(cert));
+    public void testIsUserAddedCertificateDefaults() throws Exception {
+        final TrustManagerFactory tmf =
+                TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
+        tmf.init((KeyStore) null);
+        X509TrustManager tm = getFirstX509TrustManager(tmf);
+        X509TrustManagerExtensions xtm = new X509TrustManagerExtensions(tm);
+        // Verify that all the default system provided CAs are not marked as user added.
+        for (Certificate cert : tm.getAcceptedIssuers()) {
+            assertFalse(xtm.isUserAddedCertificate((X509Certificate) cert));
+        }
     }
 }
diff --git a/tests/cts/net/src/android/net/wifi/cts/ConcurrencyTest.java b/tests/cts/net/src/android/net/wifi/cts/ConcurrencyTest.java
index 5accd77..a066ba8 100644
--- a/tests/cts/net/src/android/net/wifi/cts/ConcurrencyTest.java
+++ b/tests/cts/net/src/android/net/wifi/cts/ConcurrencyTest.java
@@ -20,12 +20,20 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.net.ConnectivityManager;
+import android.net.ConnectivityManager.NetworkCallback;
+import android.net.Network;
+import android.net.NetworkCapabilities;
+import android.net.NetworkRequest;
 import android.net.wifi.WifiManager;
 import android.net.wifi.p2p.WifiP2pManager;
 import static android.net.wifi.p2p.WifiP2pManager.WIFI_P2P_STATE_DISABLED;
 import static android.net.wifi.p2p.WifiP2pManager.WIFI_P2P_STATE_ENABLED;
 import android.test.AndroidTestCase;
 
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
 public class ConcurrencyTest extends AndroidTestCase {
     private class MySync {
         int expectedWifiState;
@@ -94,10 +102,7 @@
         }
         mContext.unregisterReceiver(mReceiver);
 
-        if (mWifiManager.isWifiEnabled()) {
-            assertTrue(mWifiManager.setWifiEnabled(false));
-            Thread.sleep(DURATION);
-        }
+        enableWifi();
         super.tearDown();
     }
 
@@ -114,6 +119,33 @@
         }
     }
 
+    /*
+     * Enables Wifi and block until connection is established.
+     */
+    private void enableWifi() throws InterruptedException {
+        if (!mWifiManager.isWifiEnabled()) {
+            assertTrue(mWifiManager.setWifiEnabled(true));
+        }
+
+        ConnectivityManager cm =
+            (ConnectivityManager) getContext().getSystemService(Context.CONNECTIVITY_SERVICE);
+        NetworkRequest request =
+            new NetworkRequest.Builder().addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
+                                        .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
+                                        .build();
+        final CountDownLatch latch = new CountDownLatch(1);
+        NetworkCallback networkCallback = new NetworkCallback() {
+            @Override
+            public void onAvailable(Network network) {
+                latch.countDown();
+            }
+        };
+        cm.registerNetworkCallback(request, networkCallback);
+        latch.await(DURATION, TimeUnit.MILLISECONDS);
+
+        cm.unregisterNetworkCallback(networkCallback);
+    }
+
     public void testConcurrency() {
         // Cannot support p2p alone
         if (!WifiFeature.isWifiSupported(getContext())) {
diff --git a/tests/cts/net/src/android/net/wifi/cts/NsdManagerTest.java b/tests/cts/net/src/android/net/wifi/cts/NsdManagerTest.java
index e132cce..2e2e75b 100644
--- a/tests/cts/net/src/android/net/wifi/cts/NsdManagerTest.java
+++ b/tests/cts/net/src/android/net/wifi/cts/NsdManagerTest.java
@@ -24,6 +24,7 @@
 
 import java.io.IOException;
 import java.net.ServerSocket;
+import java.util.Arrays;
 import java.util.Random;
 import java.util.List;
 import java.util.ArrayList;
@@ -41,6 +42,7 @@
     NsdManager.RegistrationListener mRegistrationListener;
     NsdManager.DiscoveryListener mDiscoveryListener;
     NsdManager.ResolveListener mResolveListener;
+    private NsdServiceInfo mResolvedService;
 
     public NsdManagerTest() {
         initRegistrationListener();
@@ -119,6 +121,7 @@
 
             @Override
             public void onServiceResolved(NsdServiceInfo serviceInfo) {
+                mResolvedService = serviceInfo;
                 setEvent("onServiceResolved", serviceInfo);
             }
         };
@@ -254,14 +257,87 @@
         if (DBG) Log.d(TAG, "Tear down test ...");
     }
 
-    public void runTest() throws Exception {
+    public void testNDSManager() throws Exception {
+        EventData lastEvent = null;
+
+        if (DBG) Log.d(TAG, "Starting test ...");
+
         NsdServiceInfo si = new NsdServiceInfo();
         si.setServiceType(SERVICE_TYPE);
         si.setServiceName(mServiceName);
 
-        EventData lastEvent = null;
+        byte testByteArray[] = new byte[] {-128, 127, 2, 1, 0, 1, 2};
+        String String256 = "1_________2_________3_________4_________5_________6_________" +
+                 "7_________8_________9_________10________11________12________13________" +
+                 "14________15________16________17________18________19________20________" +
+                 "21________22________23________24________25________123456";
 
-        if (DBG) Log.d(TAG, "Starting test ...");
+        // Illegal attributes
+        try {
+            si.setAttribute(null, (String) null);
+            fail("Could set null key");
+        } catch (IllegalArgumentException e) {
+            // expected
+        }
+
+        try {
+            si.setAttribute("", (String) null);
+            fail("Could set empty key");
+        } catch (IllegalArgumentException e) {
+            // expected
+        }
+
+        try {
+            si.setAttribute(String256, (String) null);
+            fail("Could set key with 255 characters");
+        } catch (IllegalArgumentException e) {
+            // expected
+        }
+
+        try {
+            si.setAttribute("key", String256.substring(3));
+            fail("Could set key+value combination with more than 255 characters");
+        } catch (IllegalArgumentException e) {
+            // expected
+        }
+
+        try {
+            si.setAttribute("key", String256.substring(4));
+            fail("Could set key+value combination with 255 characters");
+        } catch (IllegalArgumentException e) {
+            // expected
+        }
+
+        try {
+            si.setAttribute(new String(new byte[]{0x19}), (String) null);
+            fail("Could set key with invalid character");
+        } catch (IllegalArgumentException e) {
+            // expected
+        }
+
+        try {
+            si.setAttribute("=", (String) null);
+            fail("Could set key with invalid character");
+        } catch (IllegalArgumentException e) {
+            // expected
+        }
+
+        try {
+            si.setAttribute(new String(new byte[]{0x7F}), (String) null);
+            fail("Could set key with invalid character");
+        } catch (IllegalArgumentException e) {
+            // expected
+        }
+
+        // Allowed attributes
+        si.setAttribute("booleanAttr", (String) null);
+        si.setAttribute("keyValueAttr", "value");
+        si.setAttribute("keyEqualsAttr", "=");
+        si.setAttribute(" whiteSpaceKeyValueAttr ", " value ");
+        si.setAttribute("binaryDataAttr", testByteArray);
+        si.setAttribute("nullBinaryDataAttr", (byte[]) null);
+        si.setAttribute("emptyBinaryDataAttr", new byte[]{});
+        si.setAttribute("longkey", String256.substring(9));
 
         ServerSocket socket;
         int localPort;
@@ -347,6 +423,25 @@
         mNsdManager.resolveService(si, mResolveListener);
         lastEvent = waitForCallback("onServiceResolved");                   // id = 4
 
+        assertNotNull(mResolvedService);
+
+        // Check Txt attributes
+        assertEquals(8, mResolvedService.getAttributes().size());
+        assertTrue(mResolvedService.getAttributes().containsKey("booleanAttr"));
+        assertNull(mResolvedService.getAttributes().get("booleanAttr"));
+        assertEquals("value", new String(mResolvedService.getAttributes().get("keyValueAttr")));
+        assertEquals("=", new String(mResolvedService.getAttributes().get("keyEqualsAttr")));
+        assertEquals(" value ", new String(mResolvedService.getAttributes()
+                .get(" whiteSpaceKeyValueAttr ")));
+        assertEquals(String256.substring(9), new String(mResolvedService.getAttributes()
+                .get("longkey")));
+        assertTrue(Arrays.equals(testByteArray,
+                mResolvedService.getAttributes().get("binaryDataAttr")));
+        assertTrue(mResolvedService.getAttributes().containsKey("nullBinaryDataAttr"));
+        assertNull(mResolvedService.getAttributes().get("nullBinaryDataAttr"));
+        assertTrue(mResolvedService.getAttributes().containsKey("emptyBinaryDataAttr"));
+        assertNull(mResolvedService.getAttributes().get("emptyBinaryDataAttr"));
+
         assertTrue(lastEvent != null);
         assertTrue(lastEvent.mSucceeded);
 
@@ -394,7 +489,41 @@
         registeredName = lastEvent.mInfo.getServiceName();
 
         // Expect a record to be discovered
-        lastEvent = waitForCallback("onServiceFound");                      // id = 8
+        // Expect a service record to be discovered (and filter the ones
+        // that are unrelated to this test)
+        found = false;
+        for (int i = 0; i < 32; i++) {
+
+            lastEvent = waitForCallback("onServiceFound");                  // id = 8
+            if (lastEvent == null) {
+                // no more onServiceFound events are being reported!
+                break;
+            }
+
+            assertTrue(lastEvent.mSucceeded);
+
+            if (DBG) Log.d(TAG, "id = " + String.valueOf(mWaitId) + ": ServiceName = " +
+                    lastEvent.mInfo.getServiceName());
+
+            if (lastEvent.mInfo.getServiceName().equals(registeredName)) {
+                // Save it, as it will get overwritten with new serviceFound events
+                si = lastEvent.mInfo;
+                found = true;
+            }
+
+            // Remove this event from the event cache, so it won't be found by subsequent
+            // calls to waitForCallback
+            synchronized (mEventCache) {
+                mEventCache.remove(lastEvent);
+            }
+        }
+
+        assertTrue(found);
+
+        // Resolve the service
+        clearEventCache();
+        mNsdManager.resolveService(si, mResolveListener);
+        lastEvent = waitForCallback("onServiceResolved");                   // id = 9
 
         assertTrue(lastEvent != null);
         assertTrue(lastEvent.mSucceeded);
@@ -404,11 +533,16 @@
 
         assertTrue(lastEvent.mInfo.getServiceName().equals(registeredName));
 
+        assertNotNull(mResolvedService);
+
+        // Check that we don't have any TXT records
+        assertEquals(0, mResolvedService.getAttributes().size());
+
         checkForAdditionalEvents();
         clearEventCache();
 
         mNsdManager.stopServiceDiscovery(mDiscoveryListener);
-        lastEvent = waitForCallback("onDiscoveryStopped");                  // id = 9
+        lastEvent = waitForCallback("onDiscoveryStopped");                  // id = 10
         assertTrue(lastEvent != null);
         assertTrue(lastEvent.mSucceeded);
         assertTrue(checkCacheSize(1));
@@ -418,7 +552,7 @@
 
         mNsdManager.unregisterService(mRegistrationListener);
 
-        lastEvent =  waitForCallback("onServiceUnregistered");              // id = 10
+        lastEvent =  waitForCallback("onServiceUnregistered");              // id = 11
         assertTrue(lastEvent != null);
         assertTrue(lastEvent.mSucceeded);
         assertTrue(checkCacheSize(1));
diff --git a/tests/cts/net/src/android/net/wifi/cts/WifiEnterpriseConfigTest.java b/tests/cts/net/src/android/net/wifi/cts/WifiEnterpriseConfigTest.java
index 2cc5951..f3eb4e9 100644
--- a/tests/cts/net/src/android/net/wifi/cts/WifiEnterpriseConfigTest.java
+++ b/tests/cts/net/src/android/net/wifi/cts/WifiEnterpriseConfigTest.java
@@ -25,6 +25,10 @@
 import android.net.wifi.WifiManager;
 import android.test.AndroidTestCase;
 
+import java.io.ByteArrayInputStream;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
+
 public class WifiEnterpriseConfigTest extends AndroidTestCase {
     private  WifiManager mWifiManager;
 
@@ -39,6 +43,396 @@
     private static final String ANON_IDENTITY = "anonidentity";
     private static final int ENABLE_DELAY = 10000;
 
+    /*
+     * The keys and certificates below are generated with:
+     *
+     * openssl req -new -x509 -days 3650 -extensions v3_ca -keyout cakey.pem -out cacert.pem
+     * openssl ecparam -name prime256v1 -out ecparam.pem
+     * openssl req -newkey ec:ecparam.pem -keyout userkey.pem -nodes -days 3650 -out userkey.req
+     * mkdir -p demoCA/newcerts
+     * touch demoCA/index.txt
+     * echo "01" > demoCA/serial
+     * openssl ca -out usercert.pem -in userkey.req -cert cacert.pem -keyfile cakey.pem -days 3650
+     */
+
+    /**
+     * Generated from above and converted with:
+     *
+     * openssl x509 -outform d -in cacert.pem | xxd -i | sed 's/0x/(byte) 0x/g'
+     */
+
+    private static final byte[] FAKE_EC_1 = {
+        (byte) 0x30, (byte) 0x82, (byte) 0x04, (byte) 0x2f, (byte) 0x30, (byte) 0x82,
+        (byte) 0x03, (byte) 0x17, (byte) 0xa0, (byte) 0x03, (byte) 0x02, (byte) 0x01,
+        (byte) 0x02, (byte) 0x02, (byte) 0x09, (byte) 0x00, (byte) 0xa7, (byte) 0xe4,
+        (byte) 0x70, (byte) 0x50, (byte) 0x9b, (byte) 0xd2, (byte) 0x68, (byte) 0x68,
+        (byte) 0x30, (byte) 0x0d, (byte) 0x06, (byte) 0x09, (byte) 0x2a, (byte) 0x86,
+        (byte) 0x48, (byte) 0x86, (byte) 0xf7, (byte) 0x0d, (byte) 0x01, (byte) 0x01,
+        (byte) 0x0b, (byte) 0x05, (byte) 0x00, (byte) 0x30, (byte) 0x81, (byte) 0xad,
+        (byte) 0x31, (byte) 0x0b, (byte) 0x30, (byte) 0x09, (byte) 0x06, (byte) 0x03,
+        (byte) 0x55, (byte) 0x04, (byte) 0x06, (byte) 0x13, (byte) 0x02, (byte) 0x41,
+        (byte) 0x55, (byte) 0x31, (byte) 0x13, (byte) 0x30, (byte) 0x11, (byte) 0x06,
+        (byte) 0x03, (byte) 0x55, (byte) 0x04, (byte) 0x08, (byte) 0x0c, (byte) 0x0a,
+        (byte) 0x53, (byte) 0x6f, (byte) 0x6d, (byte) 0x65, (byte) 0x2d, (byte) 0x53,
+        (byte) 0x74, (byte) 0x61, (byte) 0x74, (byte) 0x65, (byte) 0x31, (byte) 0x12,
+        (byte) 0x30, (byte) 0x10, (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x04,
+        (byte) 0x07, (byte) 0x0c, (byte) 0x09, (byte) 0x53, (byte) 0x6f, (byte) 0x6d,
+        (byte) 0x65, (byte) 0x2d, (byte) 0x43, (byte) 0x69, (byte) 0x74, (byte) 0x79,
+        (byte) 0x31, (byte) 0x15, (byte) 0x30, (byte) 0x13, (byte) 0x06, (byte) 0x03,
+        (byte) 0x55, (byte) 0x04, (byte) 0x0a, (byte) 0x0c, (byte) 0x0c, (byte) 0x53,
+        (byte) 0x6f, (byte) 0x6d, (byte) 0x65, (byte) 0x2d, (byte) 0x43, (byte) 0x6f,
+        (byte) 0x6d, (byte) 0x70, (byte) 0x61, (byte) 0x6e, (byte) 0x79, (byte) 0x31,
+        (byte) 0x10, (byte) 0x30, (byte) 0x0e, (byte) 0x06, (byte) 0x03, (byte) 0x55,
+        (byte) 0x04, (byte) 0x0b, (byte) 0x0c, (byte) 0x07, (byte) 0x53, (byte) 0x65,
+        (byte) 0x63, (byte) 0x74, (byte) 0x69, (byte) 0x6f, (byte) 0x6e, (byte) 0x31,
+        (byte) 0x21, (byte) 0x30, (byte) 0x1f, (byte) 0x06, (byte) 0x03, (byte) 0x55,
+        (byte) 0x04, (byte) 0x03, (byte) 0x0c, (byte) 0x18, (byte) 0x57, (byte) 0x69,
+        (byte) 0x66, (byte) 0x69, (byte) 0x45, (byte) 0x6e, (byte) 0x74, (byte) 0x65,
+        (byte) 0x72, (byte) 0x70, (byte) 0x72, (byte) 0x69, (byte) 0x73, (byte) 0x65,
+        (byte) 0x43, (byte) 0x6f, (byte) 0x6e, (byte) 0x66, (byte) 0x69, (byte) 0x67,
+        (byte) 0x54, (byte) 0x65, (byte) 0x73, (byte) 0x74, (byte) 0x31, (byte) 0x29,
+        (byte) 0x30, (byte) 0x27, (byte) 0x06, (byte) 0x09, (byte) 0x2a, (byte) 0x86,
+        (byte) 0x48, (byte) 0x86, (byte) 0xf7, (byte) 0x0d, (byte) 0x01, (byte) 0x09,
+        (byte) 0x01, (byte) 0x16, (byte) 0x1a, (byte) 0x61, (byte) 0x6e, (byte) 0x2d,
+        (byte) 0x65, (byte) 0x6d, (byte) 0x61, (byte) 0x69, (byte) 0x6c, (byte) 0x2d,
+        (byte) 0x61, (byte) 0x64, (byte) 0x72, (byte) 0x65, (byte) 0x73, (byte) 0x73,
+        (byte) 0x40, (byte) 0x64, (byte) 0x6f, (byte) 0x6d, (byte) 0x61, (byte) 0x69,
+        (byte) 0x6e, (byte) 0x2e, (byte) 0x63, (byte) 0x6f, (byte) 0x6d, (byte) 0x30,
+        (byte) 0x1e, (byte) 0x17, (byte) 0x0d, (byte) 0x31, (byte) 0x36, (byte) 0x30,
+        (byte) 0x31, (byte) 0x31, (byte) 0x35, (byte) 0x31, (byte) 0x31, (byte) 0x31,
+        (byte) 0x38, (byte) 0x35, (byte) 0x31, (byte) 0x5a, (byte) 0x17, (byte) 0x0d,
+        (byte) 0x32, (byte) 0x36, (byte) 0x30, (byte) 0x31, (byte) 0x31, (byte) 0x32,
+        (byte) 0x31, (byte) 0x31, (byte) 0x31, (byte) 0x38, (byte) 0x35, (byte) 0x31,
+        (byte) 0x5a, (byte) 0x30, (byte) 0x81, (byte) 0xad, (byte) 0x31, (byte) 0x0b,
+        (byte) 0x30, (byte) 0x09, (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x04,
+        (byte) 0x06, (byte) 0x13, (byte) 0x02, (byte) 0x41, (byte) 0x55, (byte) 0x31,
+        (byte) 0x13, (byte) 0x30, (byte) 0x11, (byte) 0x06, (byte) 0x03, (byte) 0x55,
+        (byte) 0x04, (byte) 0x08, (byte) 0x0c, (byte) 0x0a, (byte) 0x53, (byte) 0x6f,
+        (byte) 0x6d, (byte) 0x65, (byte) 0x2d, (byte) 0x53, (byte) 0x74, (byte) 0x61,
+        (byte) 0x74, (byte) 0x65, (byte) 0x31, (byte) 0x12, (byte) 0x30, (byte) 0x10,
+        (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x04, (byte) 0x07, (byte) 0x0c,
+        (byte) 0x09, (byte) 0x53, (byte) 0x6f, (byte) 0x6d, (byte) 0x65, (byte) 0x2d,
+        (byte) 0x43, (byte) 0x69, (byte) 0x74, (byte) 0x79, (byte) 0x31, (byte) 0x15,
+        (byte) 0x30, (byte) 0x13, (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x04,
+        (byte) 0x0a, (byte) 0x0c, (byte) 0x0c, (byte) 0x53, (byte) 0x6f, (byte) 0x6d,
+        (byte) 0x65, (byte) 0x2d, (byte) 0x43, (byte) 0x6f, (byte) 0x6d, (byte) 0x70,
+        (byte) 0x61, (byte) 0x6e, (byte) 0x79, (byte) 0x31, (byte) 0x10, (byte) 0x30,
+        (byte) 0x0e, (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x04, (byte) 0x0b,
+        (byte) 0x0c, (byte) 0x07, (byte) 0x53, (byte) 0x65, (byte) 0x63, (byte) 0x74,
+        (byte) 0x69, (byte) 0x6f, (byte) 0x6e, (byte) 0x31, (byte) 0x21, (byte) 0x30,
+        (byte) 0x1f, (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x04, (byte) 0x03,
+        (byte) 0x0c, (byte) 0x18, (byte) 0x57, (byte) 0x69, (byte) 0x66, (byte) 0x69,
+        (byte) 0x45, (byte) 0x6e, (byte) 0x74, (byte) 0x65, (byte) 0x72, (byte) 0x70,
+        (byte) 0x72, (byte) 0x69, (byte) 0x73, (byte) 0x65, (byte) 0x43, (byte) 0x6f,
+        (byte) 0x6e, (byte) 0x66, (byte) 0x69, (byte) 0x67, (byte) 0x54, (byte) 0x65,
+        (byte) 0x73, (byte) 0x74, (byte) 0x31, (byte) 0x29, (byte) 0x30, (byte) 0x27,
+        (byte) 0x06, (byte) 0x09, (byte) 0x2a, (byte) 0x86, (byte) 0x48, (byte) 0x86,
+        (byte) 0xf7, (byte) 0x0d, (byte) 0x01, (byte) 0x09, (byte) 0x01, (byte) 0x16,
+        (byte) 0x1a, (byte) 0x61, (byte) 0x6e, (byte) 0x2d, (byte) 0x65, (byte) 0x6d,
+        (byte) 0x61, (byte) 0x69, (byte) 0x6c, (byte) 0x2d, (byte) 0x61, (byte) 0x64,
+        (byte) 0x72, (byte) 0x65, (byte) 0x73, (byte) 0x73, (byte) 0x40, (byte) 0x64,
+        (byte) 0x6f, (byte) 0x6d, (byte) 0x61, (byte) 0x69, (byte) 0x6e, (byte) 0x2e,
+        (byte) 0x63, (byte) 0x6f, (byte) 0x6d, (byte) 0x30, (byte) 0x82, (byte) 0x01,
+        (byte) 0x22, (byte) 0x30, (byte) 0x0d, (byte) 0x06, (byte) 0x09, (byte) 0x2a,
+        (byte) 0x86, (byte) 0x48, (byte) 0x86, (byte) 0xf7, (byte) 0x0d, (byte) 0x01,
+        (byte) 0x01, (byte) 0x01, (byte) 0x05, (byte) 0x00, (byte) 0x03, (byte) 0x82,
+        (byte) 0x01, (byte) 0x0f, (byte) 0x00, (byte) 0x30, (byte) 0x82, (byte) 0x01,
+        (byte) 0x0a, (byte) 0x02, (byte) 0x82, (byte) 0x01, (byte) 0x01, (byte) 0x00,
+        (byte) 0xb4, (byte) 0x6e, (byte) 0x66, (byte) 0x24, (byte) 0xe7, (byte) 0x5c,
+        (byte) 0xd8, (byte) 0x6f, (byte) 0x08, (byte) 0xd3, (byte) 0x80, (byte) 0xa3,
+        (byte) 0xb9, (byte) 0xaf, (byte) 0x90, (byte) 0xef, (byte) 0x1c, (byte) 0x2a,
+        (byte) 0x5f, (byte) 0x39, (byte) 0x0b, (byte) 0xbd, (byte) 0x75, (byte) 0x0d,
+        (byte) 0x3e, (byte) 0x19, (byte) 0x2e, (byte) 0x47, (byte) 0x1e, (byte) 0x14,
+        (byte) 0xc2, (byte) 0x1a, (byte) 0x59, (byte) 0xcc, (byte) 0x1b, (byte) 0xb6,
+        (byte) 0x9b, (byte) 0x46, (byte) 0x1f, (byte) 0x7f, (byte) 0x71, (byte) 0xdd,
+        (byte) 0x38, (byte) 0xbe, (byte) 0x89, (byte) 0x30, (byte) 0xba, (byte) 0x88,
+        (byte) 0xfb, (byte) 0x3f, (byte) 0x57, (byte) 0x35, (byte) 0xe7, (byte) 0xa7,
+        (byte) 0x2f, (byte) 0x2c, (byte) 0x8d, (byte) 0x7c, (byte) 0xe2, (byte) 0xd8,
+        (byte) 0x0c, (byte) 0x0a, (byte) 0xe6, (byte) 0x62, (byte) 0x46, (byte) 0x8c,
+        (byte) 0xf4, (byte) 0x51, (byte) 0xfc, (byte) 0x6a, (byte) 0x79, (byte) 0xdd,
+        (byte) 0x0a, (byte) 0x41, (byte) 0x23, (byte) 0xd3, (byte) 0xe9, (byte) 0x5e,
+        (byte) 0x91, (byte) 0xcd, (byte) 0xbd, (byte) 0x55, (byte) 0x28, (byte) 0x71,
+        (byte) 0xec, (byte) 0x52, (byte) 0x19, (byte) 0x85, (byte) 0x0c, (byte) 0x1b,
+        (byte) 0xfa, (byte) 0xbf, (byte) 0xfe, (byte) 0xae, (byte) 0x5c, (byte) 0x3b,
+        (byte) 0x99, (byte) 0x42, (byte) 0xd4, (byte) 0xe7, (byte) 0x17, (byte) 0xec,
+        (byte) 0x41, (byte) 0x22, (byte) 0x2c, (byte) 0x1e, (byte) 0x7b, (byte) 0x53,
+        (byte) 0xad, (byte) 0x02, (byte) 0xfd, (byte) 0xf6, (byte) 0x4a, (byte) 0xb1,
+        (byte) 0x6e, (byte) 0x6c, (byte) 0x87, (byte) 0xf5, (byte) 0x7d, (byte) 0x9b,
+        (byte) 0x34, (byte) 0x0e, (byte) 0x3b, (byte) 0x0e, (byte) 0xaa, (byte) 0xc5,
+        (byte) 0xc4, (byte) 0xef, (byte) 0xf2, (byte) 0x5a, (byte) 0xa9, (byte) 0xac,
+        (byte) 0x19, (byte) 0xce, (byte) 0x5f, (byte) 0xc5, (byte) 0xcc, (byte) 0x0d,
+        (byte) 0xee, (byte) 0x7f, (byte) 0x32, (byte) 0xb4, (byte) 0xfe, (byte) 0xc1,
+        (byte) 0xca, (byte) 0x9b, (byte) 0x3f, (byte) 0xad, (byte) 0x2c, (byte) 0x7a,
+        (byte) 0xc5, (byte) 0x8d, (byte) 0x48, (byte) 0xa1, (byte) 0xc9, (byte) 0x74,
+        (byte) 0xfe, (byte) 0x8a, (byte) 0xe3, (byte) 0xb0, (byte) 0x92, (byte) 0xee,
+        (byte) 0x73, (byte) 0x09, (byte) 0x0a, (byte) 0xbc, (byte) 0xc8, (byte) 0x63,
+        (byte) 0xba, (byte) 0x0e, (byte) 0x26, (byte) 0xab, (byte) 0x1e, (byte) 0xff,
+        (byte) 0xbc, (byte) 0x24, (byte) 0x12, (byte) 0x26, (byte) 0x11, (byte) 0xe0,
+        (byte) 0x04, (byte) 0xcb, (byte) 0x96, (byte) 0x7d, (byte) 0x41, (byte) 0xf7,
+        (byte) 0x79, (byte) 0x32, (byte) 0x05, (byte) 0x33, (byte) 0x19, (byte) 0x6e,
+        (byte) 0xb9, (byte) 0x75, (byte) 0xf3, (byte) 0x50, (byte) 0xa4, (byte) 0xc3,
+        (byte) 0x55, (byte) 0x9d, (byte) 0x8f, (byte) 0xb6, (byte) 0xab, (byte) 0x97,
+        (byte) 0xe7, (byte) 0xe2, (byte) 0xe8, (byte) 0x15, (byte) 0xfc, (byte) 0x35,
+        (byte) 0xbd, (byte) 0xce, (byte) 0x17, (byte) 0xbe, (byte) 0xe3, (byte) 0x73,
+        (byte) 0xd4, (byte) 0x88, (byte) 0x39, (byte) 0x27, (byte) 0x7e, (byte) 0x6d,
+        (byte) 0xa2, (byte) 0x27, (byte) 0xfa, (byte) 0x96, (byte) 0xe3, (byte) 0x38,
+        (byte) 0xc0, (byte) 0xa1, (byte) 0x55, (byte) 0xc6, (byte) 0xf3, (byte) 0x20,
+        (byte) 0xea, (byte) 0x50, (byte) 0x8d, (byte) 0x6c, (byte) 0x94, (byte) 0x9a,
+        (byte) 0x43, (byte) 0x74, (byte) 0xc0, (byte) 0xfa, (byte) 0xef, (byte) 0xe0,
+        (byte) 0xb1, (byte) 0x1c, (byte) 0x6d, (byte) 0x5e, (byte) 0x44, (byte) 0x08,
+        (byte) 0xef, (byte) 0xd5, (byte) 0x80, (byte) 0xad, (byte) 0x02, (byte) 0x03,
+        (byte) 0x01, (byte) 0x00, (byte) 0x01, (byte) 0xa3, (byte) 0x50, (byte) 0x30,
+        (byte) 0x4e, (byte) 0x30, (byte) 0x1d, (byte) 0x06, (byte) 0x03, (byte) 0x55,
+        (byte) 0x1d, (byte) 0x0e, (byte) 0x04, (byte) 0x16, (byte) 0x04, (byte) 0x14,
+        (byte) 0xe9, (byte) 0xd0, (byte) 0x9e, (byte) 0x0e, (byte) 0x62, (byte) 0x31,
+        (byte) 0x02, (byte) 0x9a, (byte) 0x33, (byte) 0xd7, (byte) 0x4a, (byte) 0x93,
+        (byte) 0x0d, (byte) 0xf3, (byte) 0xd6, (byte) 0x74, (byte) 0xce, (byte) 0x69,
+        (byte) 0xe1, (byte) 0xef, (byte) 0x30, (byte) 0x1f, (byte) 0x06, (byte) 0x03,
+        (byte) 0x55, (byte) 0x1d, (byte) 0x23, (byte) 0x04, (byte) 0x18, (byte) 0x30,
+        (byte) 0x16, (byte) 0x80, (byte) 0x14, (byte) 0xe9, (byte) 0xd0, (byte) 0x9e,
+        (byte) 0x0e, (byte) 0x62, (byte) 0x31, (byte) 0x02, (byte) 0x9a, (byte) 0x33,
+        (byte) 0xd7, (byte) 0x4a, (byte) 0x93, (byte) 0x0d, (byte) 0xf3, (byte) 0xd6,
+        (byte) 0x74, (byte) 0xce, (byte) 0x69, (byte) 0xe1, (byte) 0xef, (byte) 0x30,
+        (byte) 0x0c, (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x1d, (byte) 0x13,
+        (byte) 0x04, (byte) 0x05, (byte) 0x30, (byte) 0x03, (byte) 0x01, (byte) 0x01,
+        (byte) 0xff, (byte) 0x30, (byte) 0x0d, (byte) 0x06, (byte) 0x09, (byte) 0x2a,
+        (byte) 0x86, (byte) 0x48, (byte) 0x86, (byte) 0xf7, (byte) 0x0d, (byte) 0x01,
+        (byte) 0x01, (byte) 0x0b, (byte) 0x05, (byte) 0x00, (byte) 0x03, (byte) 0x82,
+        (byte) 0x01, (byte) 0x01, (byte) 0x00, (byte) 0x52, (byte) 0x70, (byte) 0xb6,
+        (byte) 0x10, (byte) 0x7f, (byte) 0xaa, (byte) 0x86, (byte) 0x8f, (byte) 0x02,
+        (byte) 0xb0, (byte) 0x97, (byte) 0x89, (byte) 0xb9, (byte) 0x04, (byte) 0x1d,
+        (byte) 0x79, (byte) 0xa3, (byte) 0x74, (byte) 0x7c, (byte) 0xdf, (byte) 0xad,
+        (byte) 0x87, (byte) 0xe4, (byte) 0x00, (byte) 0xd3, (byte) 0x3a, (byte) 0x5c,
+        (byte) 0x48, (byte) 0x3b, (byte) 0xfe, (byte) 0x77, (byte) 0xfd, (byte) 0xbe,
+        (byte) 0xce, (byte) 0x5b, (byte) 0xd2, (byte) 0xea, (byte) 0x3e, (byte) 0x7f,
+        (byte) 0xef, (byte) 0x20, (byte) 0x0d, (byte) 0x0b, (byte) 0xc7, (byte) 0xc4,
+        (byte) 0x25, (byte) 0x20, (byte) 0xe1, (byte) 0x8f, (byte) 0xc5, (byte) 0x19,
+        (byte) 0x37, (byte) 0x9c, (byte) 0xa0, (byte) 0x9d, (byte) 0x02, (byte) 0x30,
+        (byte) 0x5f, (byte) 0x49, (byte) 0x4e, (byte) 0x56, (byte) 0xc4, (byte) 0xab,
+        (byte) 0xcb, (byte) 0x5c, (byte) 0xe6, (byte) 0x40, (byte) 0x93, (byte) 0x92,
+        (byte) 0xee, (byte) 0xa1, (byte) 0x69, (byte) 0x7d, (byte) 0x10, (byte) 0x6b,
+        (byte) 0xd4, (byte) 0xf7, (byte) 0xec, (byte) 0xd9, (byte) 0xa5, (byte) 0x29,
+        (byte) 0x63, (byte) 0x29, (byte) 0xd9, (byte) 0x27, (byte) 0x2d, (byte) 0x5e,
+        (byte) 0x34, (byte) 0x37, (byte) 0xa9, (byte) 0xba, (byte) 0x0a, (byte) 0x7b,
+        (byte) 0x99, (byte) 0x1a, (byte) 0x7d, (byte) 0xa7, (byte) 0xa7, (byte) 0xf0,
+        (byte) 0xbf, (byte) 0x40, (byte) 0x29, (byte) 0x5d, (byte) 0x2f, (byte) 0x2e,
+        (byte) 0x0f, (byte) 0x35, (byte) 0x90, (byte) 0xb5, (byte) 0xc3, (byte) 0xfd,
+        (byte) 0x1e, (byte) 0xe2, (byte) 0xb3, (byte) 0xae, (byte) 0xf9, (byte) 0xde,
+        (byte) 0x9d, (byte) 0x76, (byte) 0xe1, (byte) 0x20, (byte) 0xf5, (byte) 0x1c,
+        (byte) 0x30, (byte) 0x42, (byte) 0x80, (byte) 0x2a, (byte) 0x4f, (byte) 0x85,
+        (byte) 0x5c, (byte) 0xb4, (byte) 0x49, (byte) 0x68, (byte) 0x6c, (byte) 0x7c,
+        (byte) 0x2a, (byte) 0xc8, (byte) 0xbc, (byte) 0x15, (byte) 0xed, (byte) 0x88,
+        (byte) 0xfd, (byte) 0x8a, (byte) 0x63, (byte) 0xe0, (byte) 0x93, (byte) 0xfd,
+        (byte) 0x86, (byte) 0xab, (byte) 0xa9, (byte) 0xf6, (byte) 0x63, (byte) 0xa5,
+        (byte) 0x29, (byte) 0xaf, (byte) 0xdc, (byte) 0x8f, (byte) 0xca, (byte) 0xc2,
+        (byte) 0x28, (byte) 0xe7, (byte) 0x26, (byte) 0x89, (byte) 0x75, (byte) 0xf1,
+        (byte) 0x3e, (byte) 0x2e, (byte) 0x86, (byte) 0x11, (byte) 0x8b, (byte) 0xfa,
+        (byte) 0xf5, (byte) 0xb4, (byte) 0xb4, (byte) 0x04, (byte) 0x02, (byte) 0xa3,
+        (byte) 0x85, (byte) 0x81, (byte) 0xad, (byte) 0xb3, (byte) 0xec, (byte) 0x2d,
+        (byte) 0x4b, (byte) 0x40, (byte) 0x59, (byte) 0x61, (byte) 0x0d, (byte) 0x59,
+        (byte) 0x09, (byte) 0x09, (byte) 0xee, (byte) 0xc7, (byte) 0x51, (byte) 0xef,
+        (byte) 0x6f, (byte) 0xd6, (byte) 0x9a, (byte) 0xa5, (byte) 0x45, (byte) 0xa2,
+        (byte) 0x89, (byte) 0xc2, (byte) 0x97, (byte) 0x93, (byte) 0xbc, (byte) 0x5b,
+        (byte) 0x37, (byte) 0x55, (byte) 0x73, (byte) 0x55, (byte) 0x0c, (byte) 0x9c,
+        (byte) 0xcb, (byte) 0x10, (byte) 0xec, (byte) 0x76, (byte) 0xfe, (byte) 0xa7,
+        (byte) 0x70, (byte) 0x4e, (byte) 0x9a, (byte) 0xa2, (byte) 0xf9, (byte) 0x40,
+        (byte) 0xdd, (byte) 0x96, (byte) 0x7d, (byte) 0x67, (byte) 0x5c, (byte) 0x8e,
+        (byte) 0x43, (byte) 0x1a, (byte) 0x26, (byte) 0xaa, (byte) 0xee, (byte) 0x38,
+        (byte) 0x11, (byte) 0x26, (byte) 0x3d, (byte) 0x69, (byte) 0xc7, (byte) 0x6a,
+        (byte) 0xe7, (byte) 0xbd, (byte) 0x67, (byte) 0x70, (byte) 0x35, (byte) 0xff,
+        (byte) 0x72, (byte) 0x2c, (byte) 0x87, (byte) 0x82, (byte) 0x68, (byte) 0x3f,
+        (byte) 0x8d
+    };
+
+    private static final byte[] FAKE_EC_2 = {
+        (byte) 0x30, (byte) 0x82, (byte) 0x04, (byte) 0x4f, (byte) 0x30, (byte) 0x82,
+        (byte) 0x03, (byte) 0x37, (byte) 0xa0, (byte) 0x03, (byte) 0x02, (byte) 0x01,
+        (byte) 0x02, (byte) 0x02, (byte) 0x09, (byte) 0x00, (byte) 0xd9, (byte) 0xc4,
+        (byte) 0xe1, (byte) 0xfc, (byte) 0x3d, (byte) 0x02, (byte) 0x21, (byte) 0x1f,
+        (byte) 0x30, (byte) 0x0d, (byte) 0x06, (byte) 0x09, (byte) 0x2a, (byte) 0x86,
+        (byte) 0x48, (byte) 0x86, (byte) 0xf7, (byte) 0x0d, (byte) 0x01, (byte) 0x01,
+        (byte) 0x0b, (byte) 0x05, (byte) 0x00, (byte) 0x30, (byte) 0x81, (byte) 0xbd,
+        (byte) 0x31, (byte) 0x0b, (byte) 0x30, (byte) 0x09, (byte) 0x06, (byte) 0x03,
+        (byte) 0x55, (byte) 0x04, (byte) 0x06, (byte) 0x13, (byte) 0x02, (byte) 0x41,
+        (byte) 0x55, (byte) 0x31, (byte) 0x13, (byte) 0x30, (byte) 0x11, (byte) 0x06,
+        (byte) 0x03, (byte) 0x55, (byte) 0x04, (byte) 0x08, (byte) 0x0c, (byte) 0x0a,
+        (byte) 0x53, (byte) 0x6f, (byte) 0x6d, (byte) 0x65, (byte) 0x2d, (byte) 0x53,
+        (byte) 0x74, (byte) 0x61, (byte) 0x74, (byte) 0x65, (byte) 0x31, (byte) 0x12,
+        (byte) 0x30, (byte) 0x10, (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x04,
+        (byte) 0x07, (byte) 0x0c, (byte) 0x09, (byte) 0x53, (byte) 0x6f, (byte) 0x6d,
+        (byte) 0x65, (byte) 0x2d, (byte) 0x43, (byte) 0x69, (byte) 0x74, (byte) 0x79,
+        (byte) 0x31, (byte) 0x1b, (byte) 0x30, (byte) 0x19, (byte) 0x06, (byte) 0x03,
+        (byte) 0x55, (byte) 0x04, (byte) 0x0a, (byte) 0x0c, (byte) 0x12, (byte) 0x53,
+        (byte) 0x6f, (byte) 0x6d, (byte) 0x65, (byte) 0x2d, (byte) 0x4f, (byte) 0x74,
+        (byte) 0x68, (byte) 0x65, (byte) 0x72, (byte) 0x2d, (byte) 0x43, (byte) 0x6f,
+        (byte) 0x6d, (byte) 0x70, (byte) 0x61, (byte) 0x6e, (byte) 0x79, (byte) 0x31,
+        (byte) 0x15, (byte) 0x30, (byte) 0x13, (byte) 0x06, (byte) 0x03, (byte) 0x55,
+        (byte) 0x04, (byte) 0x0b, (byte) 0x0c, (byte) 0x0c, (byte) 0x53, (byte) 0x6f,
+        (byte) 0x6d, (byte) 0x65, (byte) 0x2d, (byte) 0x53, (byte) 0x65, (byte) 0x63,
+        (byte) 0x74, (byte) 0x69, (byte) 0x6f, (byte) 0x6e, (byte) 0x31, (byte) 0x21,
+        (byte) 0x30, (byte) 0x1f, (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x04,
+        (byte) 0x03, (byte) 0x0c, (byte) 0x18, (byte) 0x57, (byte) 0x69, (byte) 0x66,
+        (byte) 0x69, (byte) 0x45, (byte) 0x6e, (byte) 0x74, (byte) 0x65, (byte) 0x72,
+        (byte) 0x70, (byte) 0x72, (byte) 0x69, (byte) 0x73, (byte) 0x65, (byte) 0x43,
+        (byte) 0x6f, (byte) 0x6e, (byte) 0x66, (byte) 0x69, (byte) 0x67, (byte) 0x54,
+        (byte) 0x65, (byte) 0x73, (byte) 0x74, (byte) 0x31, (byte) 0x2e, (byte) 0x30,
+        (byte) 0x2c, (byte) 0x06, (byte) 0x09, (byte) 0x2a, (byte) 0x86, (byte) 0x48,
+        (byte) 0x86, (byte) 0xf7, (byte) 0x0d, (byte) 0x01, (byte) 0x09, (byte) 0x01,
+        (byte) 0x16, (byte) 0x1f, (byte) 0x61, (byte) 0x6e, (byte) 0x2d, (byte) 0x65,
+        (byte) 0x6d, (byte) 0x61, (byte) 0x69, (byte) 0x6c, (byte) 0x2d, (byte) 0x61,
+        (byte) 0x64, (byte) 0x72, (byte) 0x65, (byte) 0x73, (byte) 0x73, (byte) 0x40,
+        (byte) 0x73, (byte) 0x6f, (byte) 0x6d, (byte) 0x65, (byte) 0x2d, (byte) 0x64,
+        (byte) 0x6f, (byte) 0x6d, (byte) 0x61, (byte) 0x69, (byte) 0x6e, (byte) 0x2e,
+        (byte) 0x63, (byte) 0x6f, (byte) 0x6d, (byte) 0x30, (byte) 0x1e, (byte) 0x17,
+        (byte) 0x0d, (byte) 0x31, (byte) 0x36, (byte) 0x30, (byte) 0x31, (byte) 0x31,
+        (byte) 0x35, (byte) 0x31, (byte) 0x31, (byte) 0x33, (byte) 0x32, (byte) 0x34,
+        (byte) 0x36, (byte) 0x5a, (byte) 0x17, (byte) 0x0d, (byte) 0x32, (byte) 0x36,
+        (byte) 0x30, (byte) 0x31, (byte) 0x31, (byte) 0x32, (byte) 0x31, (byte) 0x31,
+        (byte) 0x33, (byte) 0x32, (byte) 0x34, (byte) 0x36, (byte) 0x5a, (byte) 0x30,
+        (byte) 0x81, (byte) 0xbd, (byte) 0x31, (byte) 0x0b, (byte) 0x30, (byte) 0x09,
+        (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x04, (byte) 0x06, (byte) 0x13,
+        (byte) 0x02, (byte) 0x41, (byte) 0x55, (byte) 0x31, (byte) 0x13, (byte) 0x30,
+        (byte) 0x11, (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x04, (byte) 0x08,
+        (byte) 0x0c, (byte) 0x0a, (byte) 0x53, (byte) 0x6f, (byte) 0x6d, (byte) 0x65,
+        (byte) 0x2d, (byte) 0x53, (byte) 0x74, (byte) 0x61, (byte) 0x74, (byte) 0x65,
+        (byte) 0x31, (byte) 0x12, (byte) 0x30, (byte) 0x10, (byte) 0x06, (byte) 0x03,
+        (byte) 0x55, (byte) 0x04, (byte) 0x07, (byte) 0x0c, (byte) 0x09, (byte) 0x53,
+        (byte) 0x6f, (byte) 0x6d, (byte) 0x65, (byte) 0x2d, (byte) 0x43, (byte) 0x69,
+        (byte) 0x74, (byte) 0x79, (byte) 0x31, (byte) 0x1b, (byte) 0x30, (byte) 0x19,
+        (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x04, (byte) 0x0a, (byte) 0x0c,
+        (byte) 0x12, (byte) 0x53, (byte) 0x6f, (byte) 0x6d, (byte) 0x65, (byte) 0x2d,
+        (byte) 0x4f, (byte) 0x74, (byte) 0x68, (byte) 0x65, (byte) 0x72, (byte) 0x2d,
+        (byte) 0x43, (byte) 0x6f, (byte) 0x6d, (byte) 0x70, (byte) 0x61, (byte) 0x6e,
+        (byte) 0x79, (byte) 0x31, (byte) 0x15, (byte) 0x30, (byte) 0x13, (byte) 0x06,
+        (byte) 0x03, (byte) 0x55, (byte) 0x04, (byte) 0x0b, (byte) 0x0c, (byte) 0x0c,
+        (byte) 0x53, (byte) 0x6f, (byte) 0x6d, (byte) 0x65, (byte) 0x2d, (byte) 0x53,
+        (byte) 0x65, (byte) 0x63, (byte) 0x74, (byte) 0x69, (byte) 0x6f, (byte) 0x6e,
+        (byte) 0x31, (byte) 0x21, (byte) 0x30, (byte) 0x1f, (byte) 0x06, (byte) 0x03,
+        (byte) 0x55, (byte) 0x04, (byte) 0x03, (byte) 0x0c, (byte) 0x18, (byte) 0x57,
+        (byte) 0x69, (byte) 0x66, (byte) 0x69, (byte) 0x45, (byte) 0x6e, (byte) 0x74,
+        (byte) 0x65, (byte) 0x72, (byte) 0x70, (byte) 0x72, (byte) 0x69, (byte) 0x73,
+        (byte) 0x65, (byte) 0x43, (byte) 0x6f, (byte) 0x6e, (byte) 0x66, (byte) 0x69,
+        (byte) 0x67, (byte) 0x54, (byte) 0x65, (byte) 0x73, (byte) 0x74, (byte) 0x31,
+        (byte) 0x2e, (byte) 0x30, (byte) 0x2c, (byte) 0x06, (byte) 0x09, (byte) 0x2a,
+        (byte) 0x86, (byte) 0x48, (byte) 0x86, (byte) 0xf7, (byte) 0x0d, (byte) 0x01,
+        (byte) 0x09, (byte) 0x01, (byte) 0x16, (byte) 0x1f, (byte) 0x61, (byte) 0x6e,
+        (byte) 0x2d, (byte) 0x65, (byte) 0x6d, (byte) 0x61, (byte) 0x69, (byte) 0x6c,
+        (byte) 0x2d, (byte) 0x61, (byte) 0x64, (byte) 0x72, (byte) 0x65, (byte) 0x73,
+        (byte) 0x73, (byte) 0x40, (byte) 0x73, (byte) 0x6f, (byte) 0x6d, (byte) 0x65,
+        (byte) 0x2d, (byte) 0x64, (byte) 0x6f, (byte) 0x6d, (byte) 0x61, (byte) 0x69,
+        (byte) 0x6e, (byte) 0x2e, (byte) 0x63, (byte) 0x6f, (byte) 0x6d, (byte) 0x30,
+        (byte) 0x82, (byte) 0x01, (byte) 0x22, (byte) 0x30, (byte) 0x0d, (byte) 0x06,
+        (byte) 0x09, (byte) 0x2a, (byte) 0x86, (byte) 0x48, (byte) 0x86, (byte) 0xf7,
+        (byte) 0x0d, (byte) 0x01, (byte) 0x01, (byte) 0x01, (byte) 0x05, (byte) 0x00,
+        (byte) 0x03, (byte) 0x82, (byte) 0x01, (byte) 0x0f, (byte) 0x00, (byte) 0x30,
+        (byte) 0x82, (byte) 0x01, (byte) 0x0a, (byte) 0x02, (byte) 0x82, (byte) 0x01,
+        (byte) 0x01, (byte) 0x00, (byte) 0xa9, (byte) 0xa3, (byte) 0x21, (byte) 0xfd,
+        (byte) 0xa6, (byte) 0xc1, (byte) 0x04, (byte) 0x48, (byte) 0xc2, (byte) 0xc8,
+        (byte) 0x44, (byte) 0x50, (byte) 0xc4, (byte) 0x6d, (byte) 0x35, (byte) 0x24,
+        (byte) 0xf0, (byte) 0x6d, (byte) 0x69, (byte) 0xfb, (byte) 0xd1, (byte) 0xfc,
+        (byte) 0xde, (byte) 0xe9, (byte) 0xdb, (byte) 0xca, (byte) 0xee, (byte) 0x24,
+        (byte) 0x3d, (byte) 0x85, (byte) 0x8d, (byte) 0x84, (byte) 0xb4, (byte) 0x73,
+        (byte) 0xd1, (byte) 0x09, (byte) 0x37, (byte) 0x16, (byte) 0x80, (byte) 0x70,
+        (byte) 0x6b, (byte) 0x61, (byte) 0xcc, (byte) 0xf2, (byte) 0x98, (byte) 0xbd,
+        (byte) 0x53, (byte) 0x3a, (byte) 0x68, (byte) 0x60, (byte) 0x02, (byte) 0xba,
+        (byte) 0x0c, (byte) 0x53, (byte) 0x96, (byte) 0xfb, (byte) 0x80, (byte) 0xd1,
+        (byte) 0x5b, (byte) 0xc3, (byte) 0xcb, (byte) 0x7a, (byte) 0x81, (byte) 0x00,
+        (byte) 0x5d, (byte) 0x20, (byte) 0x72, (byte) 0xc0, (byte) 0xe4, (byte) 0x48,
+        (byte) 0x0e, (byte) 0xa2, (byte) 0xcd, (byte) 0xa2, (byte) 0x63, (byte) 0x8c,
+        (byte) 0x05, (byte) 0x7c, (byte) 0x63, (byte) 0x5b, (byte) 0xda, (byte) 0x0e,
+        (byte) 0xa7, (byte) 0x05, (byte) 0x09, (byte) 0x6d, (byte) 0xd5, (byte) 0xe4,
+        (byte) 0x3a, (byte) 0x4e, (byte) 0xa1, (byte) 0xf5, (byte) 0xfd, (byte) 0x47,
+        (byte) 0xee, (byte) 0x7b, (byte) 0xa3, (byte) 0x4c, (byte) 0x8c, (byte) 0xd3,
+        (byte) 0xbb, (byte) 0x58, (byte) 0x0f, (byte) 0x1c, (byte) 0x56, (byte) 0x80,
+        (byte) 0x80, (byte) 0xb5, (byte) 0xf9, (byte) 0x80, (byte) 0xc2, (byte) 0xd1,
+        (byte) 0x1d, (byte) 0x3f, (byte) 0xe8, (byte) 0x2a, (byte) 0x63, (byte) 0x0b,
+        (byte) 0x54, (byte) 0x5f, (byte) 0xd4, (byte) 0xcb, (byte) 0xb7, (byte) 0x94,
+        (byte) 0xe2, (byte) 0x35, (byte) 0x65, (byte) 0x59, (byte) 0xd1, (byte) 0x72,
+        (byte) 0xa4, (byte) 0xb8, (byte) 0xee, (byte) 0x82, (byte) 0x11, (byte) 0x7a,
+        (byte) 0x4c, (byte) 0x26, (byte) 0x66, (byte) 0x9b, (byte) 0x27, (byte) 0x3d,
+        (byte) 0x14, (byte) 0x4b, (byte) 0x4b, (byte) 0xc8, (byte) 0xf0, (byte) 0x6e,
+        (byte) 0x43, (byte) 0x8f, (byte) 0xee, (byte) 0x1f, (byte) 0xeb, (byte) 0x20,
+        (byte) 0xe2, (byte) 0x4c, (byte) 0x79, (byte) 0xbf, (byte) 0x21, (byte) 0x0d,
+        (byte) 0x36, (byte) 0xed, (byte) 0x5f, (byte) 0xcc, (byte) 0x70, (byte) 0x68,
+        (byte) 0x8a, (byte) 0x05, (byte) 0x7c, (byte) 0x2f, (byte) 0x1b, (byte) 0xe9,
+        (byte) 0xec, (byte) 0x83, (byte) 0x6e, (byte) 0x9a, (byte) 0x78, (byte) 0x31,
+        (byte) 0x3d, (byte) 0xf4, (byte) 0xde, (byte) 0x1b, (byte) 0xd2, (byte) 0x76,
+        (byte) 0x32, (byte) 0x6c, (byte) 0x1e, (byte) 0xc9, (byte) 0x90, (byte) 0x7f,
+        (byte) 0xc4, (byte) 0x30, (byte) 0xc0, (byte) 0xae, (byte) 0xab, (byte) 0x70,
+        (byte) 0x08, (byte) 0x78, (byte) 0xbf, (byte) 0x2e, (byte) 0x8b, (byte) 0x07,
+        (byte) 0xab, (byte) 0x8f, (byte) 0x03, (byte) 0xc5, (byte) 0xd3, (byte) 0xeb,
+        (byte) 0x98, (byte) 0x19, (byte) 0x50, (byte) 0x83, (byte) 0x52, (byte) 0xf7,
+        (byte) 0xff, (byte) 0xf5, (byte) 0x89, (byte) 0xe6, (byte) 0xe7, (byte) 0xa7,
+        (byte) 0xcb, (byte) 0xdf, (byte) 0x96, (byte) 0x9d, (byte) 0x14, (byte) 0x04,
+        (byte) 0x5e, (byte) 0x45, (byte) 0x82, (byte) 0xf7, (byte) 0x23, (byte) 0x1a,
+        (byte) 0xb6, (byte) 0x64, (byte) 0x57, (byte) 0xe8, (byte) 0x7e, (byte) 0xa1,
+        (byte) 0xaf, (byte) 0x58, (byte) 0x68, (byte) 0x70, (byte) 0xc5, (byte) 0x0f,
+        (byte) 0x8d, (byte) 0x54, (byte) 0xf3, (byte) 0x49, (byte) 0xa3, (byte) 0x97,
+        (byte) 0x32, (byte) 0xa7, (byte) 0x2a, (byte) 0x79, (byte) 0xbe, (byte) 0xcd,
+        (byte) 0x02, (byte) 0x03, (byte) 0x01, (byte) 0x00, (byte) 0x01, (byte) 0xa3,
+        (byte) 0x50, (byte) 0x30, (byte) 0x4e, (byte) 0x30, (byte) 0x1d, (byte) 0x06,
+        (byte) 0x03, (byte) 0x55, (byte) 0x1d, (byte) 0x0e, (byte) 0x04, (byte) 0x16,
+        (byte) 0x04, (byte) 0x14, (byte) 0xac, (byte) 0xf3, (byte) 0x73, (byte) 0x9a,
+        (byte) 0x25, (byte) 0x08, (byte) 0x01, (byte) 0x07, (byte) 0x86, (byte) 0x8b,
+        (byte) 0xc4, (byte) 0xed, (byte) 0xb1, (byte) 0x6b, (byte) 0x53, (byte) 0xa3,
+        (byte) 0x21, (byte) 0xb4, (byte) 0xb4, (byte) 0x46, (byte) 0x30, (byte) 0x1f,
+        (byte) 0x06, (byte) 0x03, (byte) 0x55, (byte) 0x1d, (byte) 0x23, (byte) 0x04,
+        (byte) 0x18, (byte) 0x30, (byte) 0x16, (byte) 0x80, (byte) 0x14, (byte) 0xac,
+        (byte) 0xf3, (byte) 0x73, (byte) 0x9a, (byte) 0x25, (byte) 0x08, (byte) 0x01,
+        (byte) 0x07, (byte) 0x86, (byte) 0x8b, (byte) 0xc4, (byte) 0xed, (byte) 0xb1,
+        (byte) 0x6b, (byte) 0x53, (byte) 0xa3, (byte) 0x21, (byte) 0xb4, (byte) 0xb4,
+        (byte) 0x46, (byte) 0x30, (byte) 0x0c, (byte) 0x06, (byte) 0x03, (byte) 0x55,
+        (byte) 0x1d, (byte) 0x13, (byte) 0x04, (byte) 0x05, (byte) 0x30, (byte) 0x03,
+        (byte) 0x01, (byte) 0x01, (byte) 0xff, (byte) 0x30, (byte) 0x0d, (byte) 0x06,
+        (byte) 0x09, (byte) 0x2a, (byte) 0x86, (byte) 0x48, (byte) 0x86, (byte) 0xf7,
+        (byte) 0x0d, (byte) 0x01, (byte) 0x01, (byte) 0x0b, (byte) 0x05, (byte) 0x00,
+        (byte) 0x03, (byte) 0x82, (byte) 0x01, (byte) 0x01, (byte) 0x00, (byte) 0x16,
+        (byte) 0xf6, (byte) 0xd0, (byte) 0xe1, (byte) 0x14, (byte) 0x2d, (byte) 0x52,
+        (byte) 0x47, (byte) 0xa2, (byte) 0x89, (byte) 0xe6, (byte) 0x7f, (byte) 0xac,
+        (byte) 0x88, (byte) 0x04, (byte) 0x15, (byte) 0x21, (byte) 0x00, (byte) 0x72,
+        (byte) 0xf9, (byte) 0xee, (byte) 0xb2, (byte) 0x1b, (byte) 0x8e, (byte) 0x46,
+        (byte) 0x8b, (byte) 0x90, (byte) 0x20, (byte) 0x4f, (byte) 0xa7, (byte) 0xae,
+        (byte) 0x30, (byte) 0xb6, (byte) 0x24, (byte) 0xc5, (byte) 0x54, (byte) 0xaf,
+        (byte) 0x6c, (byte) 0x1e, (byte) 0xd6, (byte) 0x73, (byte) 0x22, (byte) 0x48,
+        (byte) 0x07, (byte) 0xb5, (byte) 0x13, (byte) 0x35, (byte) 0xbb, (byte) 0x9e,
+        (byte) 0xd9, (byte) 0x19, (byte) 0x79, (byte) 0xda, (byte) 0x76, (byte) 0x7f,
+        (byte) 0xf7, (byte) 0x87, (byte) 0xc9, (byte) 0xc3, (byte) 0x0b, (byte) 0x38,
+        (byte) 0x20, (byte) 0x26, (byte) 0xfc, (byte) 0x7f, (byte) 0x32, (byte) 0x2a,
+        (byte) 0xd5, (byte) 0x09, (byte) 0x87, (byte) 0xda, (byte) 0x23, (byte) 0x1f,
+        (byte) 0x71, (byte) 0x83, (byte) 0x00, (byte) 0x17, (byte) 0xf6, (byte) 0xb9,
+        (byte) 0x57, (byte) 0x21, (byte) 0xdf, (byte) 0x29, (byte) 0xcc, (byte) 0xdb,
+        (byte) 0xe9, (byte) 0x2c, (byte) 0xba, (byte) 0x86, (byte) 0x34, (byte) 0x53,
+        (byte) 0x29, (byte) 0x09, (byte) 0xc7, (byte) 0x3c, (byte) 0x8e, (byte) 0xa3,
+        (byte) 0x86, (byte) 0x81, (byte) 0x26, (byte) 0x7b, (byte) 0xa1, (byte) 0xbe,
+        (byte) 0xbc, (byte) 0xc9, (byte) 0x83, (byte) 0xb5, (byte) 0x36, (byte) 0x65,
+        (byte) 0x51, (byte) 0xb4, (byte) 0x41, (byte) 0xf0, (byte) 0x05, (byte) 0x78,
+        (byte) 0x3a, (byte) 0xa6, (byte) 0xad, (byte) 0x4b, (byte) 0x08, (byte) 0xd1,
+        (byte) 0xe4, (byte) 0xf1, (byte) 0x2e, (byte) 0xc7, (byte) 0x23, (byte) 0x6d,
+        (byte) 0xf0, (byte) 0x9d, (byte) 0x60, (byte) 0x6d, (byte) 0xe7, (byte) 0x11,
+        (byte) 0xaf, (byte) 0x41, (byte) 0x68, (byte) 0xee, (byte) 0x06, (byte) 0x76,
+        (byte) 0x82, (byte) 0x48, (byte) 0xee, (byte) 0x41, (byte) 0xc4, (byte) 0xf8,
+        (byte) 0xe1, (byte) 0x83, (byte) 0xbc, (byte) 0xa8, (byte) 0xbd, (byte) 0x9c,
+        (byte) 0x17, (byte) 0x45, (byte) 0xf4, (byte) 0x36, (byte) 0x67, (byte) 0x47,
+        (byte) 0x0e, (byte) 0x32, (byte) 0x13, (byte) 0x6e, (byte) 0xc1, (byte) 0x1e,
+        (byte) 0x08, (byte) 0xef, (byte) 0x10, (byte) 0xdf, (byte) 0x45, (byte) 0xbf,
+        (byte) 0x5a, (byte) 0xc4, (byte) 0x44, (byte) 0x4c, (byte) 0xd0, (byte) 0xd5,
+        (byte) 0x23, (byte) 0xde, (byte) 0xd7, (byte) 0x83, (byte) 0x1e, (byte) 0xb0,
+        (byte) 0x27, (byte) 0x4d, (byte) 0x57, (byte) 0xa3, (byte) 0xe8, (byte) 0x36,
+        (byte) 0x52, (byte) 0x1c, (byte) 0x48, (byte) 0x0a, (byte) 0xc4, (byte) 0xd8,
+        (byte) 0x32, (byte) 0xfc, (byte) 0xd0, (byte) 0x26, (byte) 0x6f, (byte) 0xa4,
+        (byte) 0x61, (byte) 0x2c, (byte) 0x3a, (byte) 0xa9, (byte) 0xfe, (byte) 0xa4,
+        (byte) 0x7a, (byte) 0x58, (byte) 0x54, (byte) 0x58, (byte) 0x96, (byte) 0x2b,
+        (byte) 0x6e, (byte) 0x9c, (byte) 0xc9, (byte) 0x00, (byte) 0xda, (byte) 0xc6,
+        (byte) 0xbb, (byte) 0x97, (byte) 0xc4, (byte) 0x95, (byte) 0x32, (byte) 0x6b,
+        (byte) 0x03, (byte) 0x6f, (byte) 0x33, (byte) 0x59, (byte) 0xd4, (byte) 0xa4,
+        (byte) 0x4a, (byte) 0x29, (byte) 0x29, (byte) 0x9a, (byte) 0xf4, (byte) 0x87,
+        (byte) 0x26, (byte) 0xe6, (byte) 0xee, (byte) 0x5c, (byte) 0x0b, (byte) 0xe9,
+        (byte) 0x98, (byte) 0x5d, (byte) 0xab, (byte) 0x31, (byte) 0xa1, (byte) 0x63,
+        (byte) 0xaa, (byte) 0x1a, (byte) 0xea, (byte) 0x61, (byte) 0x27, (byte) 0x5e,
+        (byte) 0x9e, (byte) 0x34, (byte) 0x73
+    };
+
+
     private boolean hasWifi() {
         return getContext().getPackageManager().hasSystemFeature(
                 PackageManager.FEATURE_WIFI);
@@ -57,7 +451,7 @@
         }
     }
 
-    public void testSettersAndGetters() {
+    public void testSettersAndGetters() throws Exception {
         if (!hasWifi()) {
             return;
         }
@@ -87,7 +481,17 @@
         assertTrue(config.getAnonymousIdentity().equals(ANON_IDENTITY));
         config.setPassword(PASSWORD);
         assertTrue(config.getPassword().equals(PASSWORD));
-        config.setCaCertificate(null);
+        CertificateFactory factory = CertificateFactory.getInstance("X.509");
+        X509Certificate cert1 = (X509Certificate) factory.generateCertificate(
+                new ByteArrayInputStream(FAKE_EC_1));
+        X509Certificate cert2 = (X509Certificate) factory.generateCertificate(
+                new ByteArrayInputStream(FAKE_EC_2));
+        config.setCaCertificate(cert1);
+        assertTrue(config.getCaCertificate().getSerialNumber().equals(cert1.getSerialNumber()));
+        config.setCaCertificates(new X509Certificate[]{cert1, cert2});
+        X509Certificate[] certs = config.getCaCertificates();
+        assertTrue(cert1.getSerialNumber().equals(certs[0].getSerialNumber()));
+        assertTrue(cert2.getSerialNumber().equals(certs[1].getSerialNumber()));
         config.setClientKeyEntry(null, null);
         config.setSubjectMatch(SUBJECT_MATCH);
         assertTrue(config.getSubjectMatch().equals(SUBJECT_MATCH));
diff --git a/tests/cts/net/src/android/net/wifi/cts/WifiManagerTest.java b/tests/cts/net/src/android/net/wifi/cts/WifiManagerTest.java
index 4478bd4..897e5cf 100644
--- a/tests/cts/net/src/android/net/wifi/cts/WifiManagerTest.java
+++ b/tests/cts/net/src/android/net/wifi/cts/WifiManagerTest.java
@@ -296,6 +296,8 @@
         for (int i = 0; i < WIFI_SCAN_TEST_ITERATIONS; ++i) {
             startScan();
             // Make sure at least one AP is found.
+            assertTrue("mScanResult should not be null. This may be due to a scan timeout",
+                       mScanResults != null);
             assertFalse("empty scan results!", mScanResults.isEmpty());
             long nowMillis = SystemClock.elapsedRealtime();
             // Keep track of how many APs are fresh in one scan.
@@ -373,7 +375,7 @@
             assertTrue(notExist != pos);
 
             // Enable & disable network
-            boolean disableOthers = false;
+            boolean disableOthers = true;
             assertTrue(mWifiManager.enableNetwork(netId, disableOthers));
             wifiConfiguration = mWifiManager.getConfiguredNetworks().get(pos);
             assertDisableOthers(wifiConfiguration, disableOthers);
@@ -484,6 +486,9 @@
         }
         assertTrue(mWifiManager.isWifiEnabled());
 
+        // This will generate a distinct stack trace if the initial connection fails.
+        connectWifi();
+
         int i = 0;
         for (; i < 15; i++) {
             // Wait for a WiFi connection
diff --git a/tests/cts/net/src/org/apache/http/conn/ssl/cts/AbstractVerifierTest.java b/tests/cts/net/src/org/apache/http/conn/ssl/cts/AbstractVerifierTest.java
new file mode 100644
index 0000000..5e2a55e
--- /dev/null
+++ b/tests/cts/net/src/org/apache/http/conn/ssl/cts/AbstractVerifierTest.java
@@ -0,0 +1,306 @@
+/*
+ * Copyright (C) 2010 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 org.apache.http.conn.ssl.cts;
+
+import javax.security.auth.x500.X500Principal;
+import junit.framework.TestCase;
+
+import org.apache.http.conn.ssl.AbstractVerifier;
+
+import java.lang.Override;
+import java.math.BigInteger;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+import java.security.Principal;
+import java.security.PublicKey;
+import java.security.SignatureException;
+import java.security.cert.CertificateEncodingException;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateExpiredException;
+import java.security.cert.CertificateNotYetValidException;
+import java.security.cert.X509Certificate;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.Set;
+
+/**
+ * See also {@link libcore.javax.security.auth.x500.X500PrincipalTest} as it shows some cases
+ * we are not checking as they are not allowed by the X500 principal in the first place.
+ */
+public final class AbstractVerifierTest extends TestCase {
+
+    public void testGetCns() {
+        assertCns("");
+        assertCns("ou=xxx");
+        assertCns("ou=xxx,cn=xxx", "xxx");
+        assertCns("ou=xxx+cn=yyy,cn=zzz+cn=abc", "yyy", "zzz", "abc");
+        assertCns("cn=a,cn=b", "a", "b");
+        assertCns("cn=a   c,cn=b", "a   c", "b");
+        assertCns("cn=a   ,cn=b", "a", "b");
+        assertCns("cn=Cc,cn=Bb,cn=Aa", "Cc", "Bb", "Aa");
+        assertCns("cn=imap.gmail.com", "imap.gmail.com");
+        assertCns("l=\"abcn=a,b\", cn=c", "c");
+        assertCns("l=\"abcn=a,b\", cn=c", "c");
+        assertCns("l=\"abcn=a,b\", cn= c", "c");
+        assertCns("cn=<", "<");
+        assertCns("cn=>", ">");
+        assertCns("cn= >", ">");
+        assertCns("cn=a b", "a b");
+        assertCns("cn   =a b", "a b");
+        assertCns("Cn=a b", "a b");
+        assertCns("cN=a b", "a b");
+        assertCns("CN=a b", "a b");
+        assertCns("cn=a#b", "a#b");
+        assertCns("cn=#130161", "a");
+        assertCns("l=q\t+cn=p", "p");
+        assertCns("l=q\n+cn=p", "p");
+        assertCns("l=q\n,cn=p", "p");
+        assertCns("l=,cn=p", "p");
+        assertCns("l=\tq\n,cn=\tp", "\tp");
+    }
+
+    /** A cn=, generates an empty value, unless it's at the very end */
+    public void testEmptyValues() {
+        assertCns("l=,cn=+cn=q", "", "q");
+        assertCns("l=,cn=,cn=q", "", "q");
+        assertCns("l=,cn=");
+        assertCns("l=,cn=q,cn=   ", "q");
+        assertCns("l=,cn=q  ,cn=   ", "q");
+        assertCns("l=,cn=\"\"");
+        assertCns("l=,cn=\"  \",cn=\"  \"", "  ", "  ");
+        assertCns("l=,cn=  ,cn=  ","");
+        assertCns("l=,cn=,cn=  ,cn=  ", "", "");
+    }
+
+
+    public void testGetCns_escapedChars() {
+        assertCns("cn=\\,", ",");
+        assertCns("cn=\\#", "#");
+        assertCns("cn=\\+", "+");
+        assertCns("cn=\\\"", "\"");
+        assertCns("cn=\\\\", "\\");
+        assertCns("cn=\\<", "<");
+        assertCns("cn=\\>", ">");
+        assertCns("cn=\\;", ";");
+        assertCns("cn=\\+", "+");
+        assertCns("cn=\"\\+\"", "+");
+        assertCns("cn=\"\\,\"", ",");
+        assertCns("cn= a =", "a =");
+        assertCns("cn==", "=");
+    }
+
+    public void testGetCns_whitespace() {
+        assertCns("cn= p", "p");
+        assertCns("cn=\np", "p");
+        assertCns("cn=\tp", "\tp");
+    }
+
+    public void testGetCnsWithOid() {
+        assertCns("2.5.4.3=a,ou=xxx", "a");
+        assertCns("2.5.4.3=\" a \",ou=xxx", " a ");
+        assertCns("2.5.5.3=a,ou=xxx,cn=b", "b");
+    }
+
+    public void testGetCnsWithQuotedStrings() {
+        assertCns("cn=\"\\\" a ,=<>#;\"", "\" a ,=<>#;");
+        assertCns("cn=abc\\,def", "abc,def");
+        assertCns("cn=\"\\\" a ,\\=<>\\#;\"", "\" a ,=<>#;");
+    }
+
+    public void testGetCnsWithUtf8() {
+        assertCns("cn=\"Lu\\C4\\8Di\\C4\\87\"", "\u004c\u0075\u010d\u0069\u0107");
+        assertCns("cn=Lu\\C4\\8Di\\C4\\87", "\u004c\u0075\u010d\u0069\u0107");
+        assertCns("cn=Lu\\C4\\8di\\c4\\87", "\u004c\u0075\u010d\u0069\u0107");
+        assertCns("cn=\"Lu\\C4\\8di\\c4\\87\"", "\u004c\u0075\u010d\u0069\u0107");
+        assertCns("cn=\u004c\u0075\u010d\u0069\u0107", "\u004c\u0075\u010d\u0069\u0107");
+        // \63=c
+        assertExceptionInPrincipal("\\63n=ab");
+        assertExceptionInPrincipal("cn=\\a");
+    }
+
+    public void testGetCnsWithWhitespace() {
+        assertCns("ou=a, cn=  a  b  ,o=x", "a  b");
+        assertCns("cn=\"  a  b  \" ,o=x", "  a  b  ");
+    }
+
+    private static void assertCns(String dn, String... expected) {
+        String[] result = AbstractVerifier.getCNs(createStubCertificate(dn));
+        if (expected.length == 0) {
+            assertNull(result);
+        } else {
+            assertNotNull(dn, result);
+            assertEquals(dn, Arrays.asList(expected), Arrays.asList(result));
+        }
+    }
+
+    private static void assertExceptionInPrincipal(String dn) {
+        try {
+            X500Principal principal = new X500Principal(dn);
+            fail("Expected " + IllegalArgumentException.class.getName()
+                    + " because of incorrect input name");
+        } catch (IllegalArgumentException e) {
+            // Expected.
+        }
+    }
+
+    private static X509Certificate createStubCertificate(final String subjectName) {
+        return new X509Certificate() {
+            @Override
+            public X500Principal getSubjectX500Principal() {
+                return new X500Principal(subjectName);
+            }
+
+            @Override
+            public Set<String> getCriticalExtensionOIDs() {
+                return null;
+            }
+
+            @Override
+            public byte[] getExtensionValue(String oid) {
+                return new byte[0];
+            }
+
+            @Override
+            public Set<String> getNonCriticalExtensionOIDs() {
+                return null;
+            }
+
+            @Override
+            public boolean hasUnsupportedCriticalExtension() {
+                return false;
+            }
+
+            @Override
+            public byte[] getEncoded() throws CertificateEncodingException {
+                return new byte[0];
+            }
+
+            @Override
+            public void verify(PublicKey key)
+                    throws CertificateException, NoSuchAlgorithmException, InvalidKeyException,
+                    NoSuchProviderException, SignatureException {
+
+            }
+
+            @Override
+            public void verify(PublicKey key, String sigProvider)
+                    throws CertificateException, NoSuchAlgorithmException, InvalidKeyException,
+                    NoSuchProviderException, SignatureException {
+
+            }
+
+            @Override
+            public String toString() {
+                return null;
+            }
+
+            @Override
+            public PublicKey getPublicKey() {
+                return null;
+            }
+
+            @Override
+            public void checkValidity()
+                    throws CertificateExpiredException, CertificateNotYetValidException {
+
+            }
+
+            @Override
+            public void checkValidity(Date date)
+                    throws CertificateExpiredException, CertificateNotYetValidException {
+
+            }
+
+            @Override
+            public int getVersion() {
+                return 0;
+            }
+
+            @Override
+            public BigInteger getSerialNumber() {
+                return null;
+            }
+
+            @Override
+            public Principal getIssuerDN() {
+                return null;
+            }
+
+            @Override
+            public Principal getSubjectDN() {
+                return null;
+            }
+
+            @Override
+            public Date getNotBefore() {
+                return null;
+            }
+
+            @Override
+            public Date getNotAfter() {
+                return null;
+            }
+
+            @Override
+            public byte[] getTBSCertificate() throws CertificateEncodingException {
+                return new byte[0];
+            }
+
+            @Override
+            public byte[] getSignature() {
+                return new byte[0];
+            }
+
+            @Override
+            public String getSigAlgName() {
+                return null;
+            }
+
+            @Override
+            public String getSigAlgOID() {
+                return null;
+            }
+
+            @Override
+            public byte[] getSigAlgParams() {
+                return new byte[0];
+            }
+
+            @Override
+            public boolean[] getIssuerUniqueID() {
+                return new boolean[0];
+            }
+
+            @Override
+            public boolean[] getSubjectUniqueID() {
+                return new boolean[0];
+            }
+
+            @Override
+            public boolean[] getKeyUsage() {
+                return new boolean[0];
+            }
+
+            @Override
+            public int getBasicConstraints() {
+                return 0;
+            }
+        };
+    }
+}
+