[automerger skipped] DO NOT MERGE CTS for presence of avc fix for b/70897454
am: 2215338715  -s ours

Change-Id: I9d60b68be04ed4f32a782ba09f406b2953f4938b
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/aidl/Android.mk b/tests/cts/hostside/aidl/Android.mk
new file mode 100644
index 0000000..a7ec6ef
--- /dev/null
+++ b/tests/cts/hostside/aidl/Android.mk
@@ -0,0 +1,22 @@
+# 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 := com/android/cts/net/hostside/IRemoteSocketFactory.aidl
+LOCAL_MODULE := CtsHostsideNetworkTestsAidl
+include $(BUILD_JAVA_LIBRARY)
diff --git a/tests/cts/hostside/aidl/com/android/cts/net/hostside/IRemoteSocketFactory.aidl b/tests/cts/hostside/aidl/com/android/cts/net/hostside/IRemoteSocketFactory.aidl
new file mode 100644
index 0000000..68176ad
--- /dev/null
+++ b/tests/cts/hostside/aidl/com/android/cts/net/hostside/IRemoteSocketFactory.aidl
@@ -0,0 +1,25 @@
+/*
+ * 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 android.os.ParcelFileDescriptor;
+
+interface IRemoteSocketFactory {
+    ParcelFileDescriptor openSocketFd(String host, int port, int timeoutMs);
+    String getPackageName();
+    int getUid();
+}
diff --git a/tests/cts/hostside/app/Android.mk b/tests/cts/hostside/app/Android.mk
index 055287a..9519ec5 100644
--- a/tests/cts/hostside/app/Android.mk
+++ b/tests/cts/hostside/app/Android.mk
@@ -20,7 +20,8 @@
 
 LOCAL_MODULE_TAGS := tests
 LOCAL_SDK_VERSION := current
-LOCAL_STATIC_JAVA_LIBRARIES := ctsdeviceutil ctstestrunner ub-uiautomator
+LOCAL_STATIC_JAVA_LIBRARIES := ctsdeviceutil ctstestrunner ub-uiautomator \
+        CtsHostsideNetworkTestsAidl
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
@@ -29,4 +30,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..0598a3b 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" />
@@ -29,6 +31,14 @@
                 <action android:name="android.net.VpnService"/>
             </intent-filter>
         </service>
+        <service
+            android:name=".MyNotificationListenerService"
+            android:label="MyNotificationListenerService"
+            android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE" >
+            <intent-filter>
+                <action android:name="android.service.notification.NotificationListenerService" />
+            </intent-filter>
+        </service>
     </application>
 
     <instrumentation
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractAppIdleTestCase.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractAppIdleTestCase.java
new file mode 100644
index 0000000..fb773cb
--- /dev/null
+++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractAppIdleTestCase.java
@@ -0,0 +1,132 @@
+/*
+ * 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 android.util.Log;
+
+/**
+ * Base class for metered and non-metered tests on idle apps.
+ */
+abstract class AbstractAppIdleTestCase extends AbstractRestrictBackgroundNetworkTestCase {
+
+    @Override
+    protected final void setUp() throws Exception {
+        super.setUp();
+
+        if (!isSupported()) return;
+
+        // Set initial state.
+        removePowerSaveModeWhitelist(TEST_APP2_PKG);
+        setAppIdle(false);
+        turnBatteryOff();
+
+        registerBroadcastReceiver();
+    }
+
+    @Override
+    protected final void tearDown() throws Exception {
+        super.tearDown();
+
+        if (!isSupported()) return;
+
+        try {
+            tearDownMeteredNetwork();
+        } finally {
+            turnBatteryOn();
+            setAppIdle(false);
+        }
+    }
+
+    @Override
+    protected boolean isSupported() throws Exception {
+        boolean supported = isDozeModeEnabled();
+        if (!supported) {
+            Log.i(TAG, "Skipping " + getClass() + "." + getName()
+                    + "() because device does not support Doze Mode");
+        }
+        return supported;
+    }
+
+    /**
+     * Resets the (non) metered network state.
+     *
+     * <p>By default is empty - it's up to subclasses to override.
+     */
+    protected void tearDownMeteredNetwork() throws Exception {
+    }
+
+    public void testBackgroundNetworkAccess_enabled() throws Exception {
+        if (!isSupported()) return;
+
+        setAppIdle(true);
+        assertBackgroundNetworkAccess(false);
+
+        assertsForegroundAlwaysHasNetworkAccess();
+        setAppIdle(true);
+        assertBackgroundNetworkAccess(false);
+
+        // Make sure foreground app doesn't lose access upon enabling it.
+        setAppIdle(true);
+        launchActivity();
+        assertAppIdle(false); // Sanity check - not idle anymore, since activity was launched...
+        assertForegroundNetworkAccess();
+        finishActivity();
+        assertAppIdle(false); // Sanity check - not idle anymore, since activity was launched...
+        assertBackgroundNetworkAccess(true);
+        setAppIdle(true);
+        assertBackgroundNetworkAccess(false);
+
+        // Same for foreground service.
+        setAppIdle(true);
+        startForegroundService();
+        assertAppIdle(true); // Sanity check - still idle
+        assertForegroundServiceNetworkAccess();
+        stopForegroundService();
+        assertAppIdle(true);
+        assertBackgroundNetworkAccess(false);
+    }
+
+    public void testBackgroundNetworkAccess_whitelisted() throws Exception {
+        if (!isSupported()) return;
+
+        setAppIdle(true);
+        assertBackgroundNetworkAccess(false);
+
+        addPowerSaveModeWhitelist(TEST_APP2_PKG);
+        assertAppIdle(false); // Sanity check - not idle anymore, since whitelisted
+        assertBackgroundNetworkAccess(true);
+
+        removePowerSaveModeWhitelist(TEST_APP2_PKG);
+        assertAppIdle(true); // Sanity check - idle again, once whitelisted was removed
+        assertBackgroundNetworkAccess(false);
+
+        assertsForegroundAlwaysHasNetworkAccess();
+
+        // Sanity check - no whitelist, no access!
+        setAppIdle(true);
+        assertBackgroundNetworkAccess(false);
+    }
+
+    public void testBackgroundNetworkAccess_disabled() throws Exception {
+        if (!isSupported()) return;
+
+        assertBackgroundNetworkAccess(true);
+
+        assertsForegroundAlwaysHasNetworkAccess();
+        assertBackgroundNetworkAccess(true);
+    }
+}
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractBatterySaverModeTestCase.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractBatterySaverModeTestCase.java
new file mode 100644
index 0000000..ed738a6
--- /dev/null
+++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractBatterySaverModeTestCase.java
@@ -0,0 +1,130 @@
+/*
+ * 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 android.util.Log;
+
+/**
+ * Base class for metered and non-metered Battery Saver Mode tests.
+ */
+abstract class AbstractBatterySaverModeTestCase extends AbstractRestrictBackgroundNetworkTestCase {
+
+    @Override
+    protected final void setUp() throws Exception {
+        super.setUp();
+
+        if (!isSupported()) return;
+
+        // Set initial state.
+        removePowerSaveModeWhitelist(TEST_APP2_PKG);
+        setBatterySaverMode(false);
+
+        registerBroadcastReceiver();
+    }
+
+    @Override
+    protected final void tearDown() throws Exception {
+        super.tearDown();
+
+        if (!isSupported()) return;
+
+        try {
+            tearDownMeteredNetwork();
+        } finally {
+            setBatterySaverMode(false);
+        }
+    }
+
+    @Override
+    protected boolean isSupported() throws Exception {
+        boolean supported = isDozeModeEnabled();
+        if (!supported) {
+            Log.i(TAG, "Skipping " + getClass() + "." + getName()
+                    + "() because device does not support Doze Mode");
+        }
+        return supported;
+    }
+
+    /**
+     * Sets the initial (non) metered network state.
+     *
+     * <p>By default is empty - it's up to subclasses to override.
+     */
+    protected void setUpMeteredNetwork() throws Exception {
+    }
+
+    /**
+     * Resets the (non) metered network state.
+     *
+     * <p>By default is empty - it's up to subclasses to override.
+     */
+    protected void tearDownMeteredNetwork() throws Exception {
+    }
+
+    public void testBackgroundNetworkAccess_enabled() throws Exception {
+        if (!isSupported()) return;
+
+        setBatterySaverMode(true);
+        assertBackgroundNetworkAccess(false);
+
+        assertsForegroundAlwaysHasNetworkAccess();
+        assertBackgroundNetworkAccess(false);
+
+        // Make sure foreground app doesn't lose access upon enabling it.
+        setBatterySaverMode(false);
+        launchActivity();
+        assertForegroundNetworkAccess();
+        setBatterySaverMode(true);
+        assertForegroundNetworkAccess();
+        finishActivity();
+        assertBackgroundNetworkAccess(false);
+
+        // Same for foreground service.
+        setBatterySaverMode(false);
+        startForegroundService();
+        assertForegroundNetworkAccess();
+        setBatterySaverMode(true);
+        assertForegroundNetworkAccess();
+        stopForegroundService();
+        assertBackgroundNetworkAccess(false);
+    }
+
+    public void testBackgroundNetworkAccess_whitelisted() throws Exception {
+        if (!isSupported()) return;
+
+        setBatterySaverMode(true);
+        assertBackgroundNetworkAccess(false);
+
+        addPowerSaveModeWhitelist(TEST_APP2_PKG);
+        assertBackgroundNetworkAccess(true);
+
+        removePowerSaveModeWhitelist(TEST_APP2_PKG);
+        assertBackgroundNetworkAccess(false);
+
+        assertsForegroundAlwaysHasNetworkAccess();
+        assertBackgroundNetworkAccess(false);
+    }
+
+    public void testBackgroundNetworkAccess_disabled() throws Exception {
+        if (!isSupported()) return;
+
+        assertBackgroundNetworkAccess(true);
+
+        assertsForegroundAlwaysHasNetworkAccess();
+        assertBackgroundNetworkAccess(true);
+    }
+}
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractDozeModeTestCase.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractDozeModeTestCase.java
new file mode 100644
index 0000000..cc05b04
--- /dev/null
+++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractDozeModeTestCase.java
@@ -0,0 +1,153 @@
+/*
+ * 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 android.os.SystemClock;
+import android.util.Log;
+
+/**
+ * Base class for metered and non-metered Doze Mode tests.
+ */
+abstract class AbstractDozeModeTestCase extends AbstractRestrictBackgroundNetworkTestCase {
+
+    @Override
+    protected final void setUp() throws Exception {
+        super.setUp();
+
+        if (!isSupported()) return;
+
+        // Set initial state.
+        removePowerSaveModeWhitelist(TEST_APP2_PKG);
+        setDozeMode(false);
+
+        registerBroadcastReceiver();
+    }
+
+    @Override
+    protected final void tearDown() throws Exception {
+        super.tearDown();
+
+        if (!isSupported()) return;
+
+        try {
+            tearDownMeteredNetwork();
+        } finally {
+            setDozeMode(false);
+        }
+    }
+
+    @Override
+    protected boolean isSupported() throws Exception {
+        boolean supported = isDozeModeEnabled();
+        if (!supported) {
+            Log.i(TAG, "Skipping " + getClass() + "." + getName()
+                    + "() because device does not support Doze Mode");
+        }
+        return supported;
+    }
+
+    /**
+     * Sets the initial (non) metered network state.
+     *
+     * <p>By default is empty - it's up to subclasses to override.
+     */
+    protected void setUpMeteredNetwork() throws Exception {
+    }
+
+    /**
+     * Resets the (non) metered network state.
+     *
+     * <p>By default is empty - it's up to subclasses to override.
+     */
+    protected void tearDownMeteredNetwork() throws Exception {
+    }
+
+    public void testBackgroundNetworkAccess_enabled() throws Exception {
+        if (!isSupported()) return;
+
+        setDozeMode(true);
+        assertBackgroundNetworkAccess(false);
+
+        assertsForegroundAlwaysHasNetworkAccess();
+        assertBackgroundNetworkAccess(false);
+
+        // Make sure foreground service doesn't lose network access upon enabling doze.
+        setDozeMode(false);
+        startForegroundService();
+        assertForegroundNetworkAccess();
+        setDozeMode(true);
+        assertForegroundNetworkAccess();
+        stopForegroundService();
+        assertBackgroundState();
+        assertBackgroundNetworkAccess(false);
+    }
+
+    public void testBackgroundNetworkAccess_whitelisted() throws Exception {
+        if (!isSupported()) return;
+
+        setDozeMode(true);
+        assertBackgroundNetworkAccess(false);
+
+        addPowerSaveModeWhitelist(TEST_APP2_PKG);
+        assertBackgroundNetworkAccess(true);
+
+        removePowerSaveModeWhitelist(TEST_APP2_PKG);
+        assertBackgroundNetworkAccess(false);
+
+        assertsForegroundAlwaysHasNetworkAccess();
+        assertBackgroundNetworkAccess(false);
+    }
+
+    public void testBackgroundNetworkAccess_disabled() throws Exception {
+        if (!isSupported()) return;
+
+        assertBackgroundNetworkAccess(true);
+
+        assertsForegroundAlwaysHasNetworkAccess();
+        assertBackgroundNetworkAccess(true);
+    }
+
+    public void testBackgroundNetworkAccess_enabledButWhitelistedOnNotificationAction()
+            throws Exception {
+        if (!isSupported()) return;
+
+        setPendingIntentWhitelistDuration(NETWORK_TIMEOUT_MS);
+        try {
+            registerNotificationListenerService();
+            setDozeMode(true);
+            assertBackgroundNetworkAccess(false);
+
+            sendNotification(42);
+            assertBackgroundNetworkAccess(true);
+            // Make sure access is disabled after it expires
+            SystemClock.sleep(NETWORK_TIMEOUT_MS);
+            assertBackgroundNetworkAccess(false);
+        } finally {
+            resetDeviceIdleSettings();
+        }
+    }
+
+    // Must override so it only tests foreground service - once an app goes to foreground, device
+    // leaves Doze Mode.
+    @Override
+    protected void assertsForegroundAlwaysHasNetworkAccess() throws Exception {
+        startForegroundService();
+        assertForegroundServiceNetworkAccess();
+        stopForegroundService();
+        assertBackgroundState();
+    }
+}
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..aad7561
--- /dev/null
+++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/AbstractRestrictBackgroundNetworkTestCase.java
@@ -0,0 +1,862 @@
+/*
+ * 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.Instrumentation;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.net.ConnectivityManager;
+import android.net.NetworkInfo;
+import android.net.NetworkInfo.DetailedState;
+import android.net.NetworkInfo.State;
+import android.net.wifi.WifiManager;
+import android.os.SystemClock;
+import android.service.notification.NotificationListenerService;
+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_GET_RESTRICT_BACKGROUND_STATUS =
+            "com.android.cts.net.hostside.app2.action.GET_RESTRICT_BACKGROUND_STATUS";
+    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";
+    static final String ACTION_SEND_NOTIFICATION =
+            "com.android.cts.net.hostside.app2.action.SEND_NOTIFICATION";
+    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 EXTRA_NOTIFICATION_ID =
+            "com.android.cts.net.hostside.app2.extra.NOTIFICATION_ID";
+    private static final String NETWORK_STATUS_SEPARATOR = "\\|";
+    private static final int SECOND_IN_MS = 1000;
+    static final int NETWORK_TIMEOUT_MS = 15 * SECOND_IN_MS;
+    private static final int PROCESS_STATE_FOREGROUND_SERVICE = 4;
+    private static final int PROCESS_STATE_TOP = 2;
+
+
+    // 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 int mMyUid;
+    private String mMeteredWifi;
+    private boolean mSupported;
+
+    @Override
+    protected 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);
+        mMyUid = getUid(mContext.getPackageName());
+        mSupported = setUpActiveNetworkMeteringState();
+
+        Log.i(TAG, "Apps status on " + getName() + ":\n"
+                + "\ttest app: uid=" + mMyUid + ", state=" + getProcessStateByUid(mMyUid) + "\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");
+            SystemClock.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 after " + timeoutMs + "ms: " + 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 expectedStatus) throws Exception {
+        final Intent intent = new Intent(ACTION_GET_RESTRICT_BACKGROUND_STATUS);
+        final String resultData = sendOrderedBroadcast(intent);
+        assertNotNull("timeout waiting for ordered broadcast result", resultData);
+        final String actualStatus = toString(Integer.parseInt(resultData));
+        assertEquals("wrong status", toString(expectedStatus), actualStatus);
+    }
+
+    protected void assertMyRestrictBackgroundStatus(int expectedStatus) throws Exception {
+        final int actualStatus = mCm.getRestrictBackgroundStatus();
+        assertEquals("Wrong status", toString(expectedStatus), toString(actualStatus));
+    }
+
+    protected boolean isMyRestrictBackgroundStatus(int expectedStatus) throws Exception {
+        final int actualStatus = mCm.getRestrictBackgroundStatus();
+        if (expectedStatus != actualStatus) {
+            Log.d(TAG, "Expected: " + toString(expectedStatus)
+                    + " but actual: " + toString(actualStatus));
+            return false;
+        }
+        return true;
+    }
+
+    protected void assertBackgroundNetworkAccess(boolean expectAllowed) throws Exception {
+        assertBackgroundState(); // Sanity check.
+        assertNetworkAccess(expectAllowed);
+    }
+
+    protected void assertForegroundNetworkAccess() throws Exception {
+        assertForegroundState(); // Sanity check.
+        assertNetworkAccess(true);
+    }
+
+    protected void assertForegroundServiceNetworkAccess() throws Exception {
+        assertForegroundServiceState(); // Sanity check.
+        assertNetworkAccess(true);
+    }
+
+    /**
+     * Whether this device suport this type of test.
+     *
+     * <p>Should be overridden when necessary (but always calling
+     * {@code super.isSupported()} first), and explicitly used before each test
+     * Example:
+     *
+     * <pre><code>
+     * public void testSomething() {
+     *    if (!isSupported()) return;
+     * </code></pre>
+     *
+     * @return {@code true} by default.
+     */
+    protected boolean isSupported() throws Exception {
+        return mSupported;
+    }
+
+    /**
+     * Asserts that an app always have access while on foreground or running a foreground service.
+     *
+     * <p>This method will launch an activity and a foreground service to make the assertion, but
+     * will finish the activity / stop the service afterwards.
+     */
+    protected void assertsForegroundAlwaysHasNetworkAccess() throws Exception{
+        // Checks foreground first.
+        launchActivity();
+        assertForegroundNetworkAccess();
+        finishActivity();
+
+        // Then foreground service
+        startForegroundService();
+        assertForegroundServiceNetworkAccess();
+        stopForegroundService();
+    }
+
+    protected final void assertBackgroundState() throws Exception {
+        final int maxTries = 30;
+        ProcessState state = null;
+        for (int i = 1; i <= maxTries; i++) {
+            state = getProcessStateByUid(mUid);
+            Log.v(TAG, "assertBackgroundState(): status for app2 (" + mUid + ") on attempt #" + i
+                    + ": " + state);
+            if (isBackground(state.state)) {
+                return;
+            }
+            Log.d(TAG, "App not on background state on attempt #" + i
+                    + "; sleeping 1s before trying again");
+            SystemClock.sleep(SECOND_IN_MS);
+        }
+        fail("App2 is not on background state after " + maxTries + " attempts: " + state );
+    }
+
+    protected final void assertForegroundState() throws Exception {
+        final int maxTries = 30;
+        ProcessState state = null;
+        for (int i = 1; i <= maxTries; i++) {
+            state = getProcessStateByUid(mUid);
+            Log.v(TAG, "assertForegroundState(): status for app2 (" + mUid + ") on attempt #" + i
+                    + ": " + state);
+            if (!isBackground(state.state)) {
+                return;
+            }
+            Log.d(TAG, "App not on foreground state on attempt #" + i
+                    + "; sleeping 1s before trying again");
+            SystemClock.sleep(SECOND_IN_MS);
+        }
+        fail("App2 is not on foreground state after " + maxTries + " attempts: " + state );
+    }
+
+    protected final void assertForegroundServiceState() throws Exception {
+        final int maxTries = 30;
+        ProcessState state = null;
+        for (int i = 1; i <= maxTries; i++) {
+            state = getProcessStateByUid(mUid);
+            Log.v(TAG, "assertForegroundServiceState(): status for app2 (" + mUid + ") on attempt #"
+                    + i + ": " + state);
+            if (state.state == PROCESS_STATE_FOREGROUND_SERVICE) {
+                return;
+            }
+            Log.d(TAG, "App not on foreground service state on attempt #" + i
+                    + "; sleeping 1s before trying again");
+            SystemClock.sleep(SECOND_IN_MS);
+        }
+        fail("App2 is not on foreground service state after " + maxTries + " attempts: " + state );
+    }
+
+    /**
+     * Returns whether an app state should be considered "background" for restriction purposes.
+     */
+    protected boolean isBackground(int state) {
+        return state > PROCESS_STATE_FOREGROUND_SERVICE;
+    }
+
+    /**
+     * Asserts whether the active network is available or not.
+     */
+    private void assertNetworkAccess(boolean expectAvailable) throws Exception {
+        final Intent intent = new Intent(ACTION_CHECK_NETWORK);
+
+        final int maxTries = 5;
+        String resultData = null;
+        for (int i = 1; i <= maxTries; i++) {
+            resultData = sendOrderedBroadcast(intent);
+            assertNotNull("timeout waiting for ordered broadcast", resultData);
+
+            // Network status format is described on MyBroadcastReceiver.checkNetworkStatus()
+            final String[] parts = resultData.split(NETWORK_STATUS_SEPARATOR);
+            assertEquals("Wrong network status: " + resultData, 5, parts.length); // Sanity check
+            final State state = State.valueOf(parts[0]);
+            final DetailedState detailedState = DetailedState.valueOf(parts[1]);
+            final boolean connected = Boolean.valueOf(parts[2]);
+            final String connectionCheckDetails = parts[3];
+            final String networkInfo = parts[4];
+
+            if (expectAvailable) {
+                if (!connected) {
+                    // Since it's establishing a connection to an external site, it could be flaky.
+                    Log.w(TAG, "Failed to connect to an external site on attempt #" + i +
+                            " (error: " + connectionCheckDetails + ", NetworkInfo: " + networkInfo
+                            + "); sleeping " + NETWORK_TIMEOUT_MS + "ms before trying again");
+                    SystemClock.sleep(NETWORK_TIMEOUT_MS);
+                    continue;
+                }
+                if (state != State.CONNECTED) {
+                    Log.d(TAG, "State (" + state + ") not set to CONNECTED on attempt #" + i
+                            + "; sleeping 1s before trying again");
+                    SystemClock.sleep(SECOND_IN_MS);
+                } else {
+                    assertEquals("wrong detailed state for " + networkInfo,
+                            DetailedState.CONNECTED, detailedState);
+                    return;
+                }
+                return;
+            } else {
+                assertFalse("should not be connected: " + connectionCheckDetails
+                        + " (network info: " + networkInfo + ")", connected);
+                if (state != State.DISCONNECTED) {
+                    // When the network info state change, it's possible the app still get the
+                    // previous value, so we need to retry a couple times.
+                    Log.d(TAG, "State (" + state + ") not set to DISCONNECTED on attempt #" + i
+                            + "; sleeping 1s before trying again");
+                    SystemClock.sleep(SECOND_IN_MS);
+                } else {
+                    assertEquals("wrong detailed state for " + networkInfo,
+                            DetailedState.BLOCKED, detailedState);
+                   return;
+                }
+            }
+        }
+        fail("Invalid state for expectAvailable=" + expectAvailable + " after " + maxTries
+                + " attempts. Last data: " + resultData);
+    }
+
+    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, 5, 1, expectedResult);
+    }
+
+    protected void assertDelayedShellCommand(String command, int maxTries, int napTimeSeconds,
+            final String expectedResult) throws Exception {
+        assertDelayedShellCommand(command, maxTries, napTimeSeconds, 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 {
+        assertDelayedShellCommand(command, 5, 1, checker);
+    }
+    protected void assertDelayedShellCommand(String command, int maxTries, int napTimeSeconds,
+            ExpectResultChecker checker) throws Exception {
+        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 " + napTimeSeconds + "s before trying again");
+            SystemClock.sleep(napTimeSeconds * SECOND_IN_MS);
+        }
+        fail("Command '" + command + "' did not return '" + checker.getExpected() + "' after "
+                + maxTries
+                + " attempts. Last result: '" + result + "'");
+    }
+
+    /**
+     * Sets the initial metering state for the active network.
+     *
+     * <p>It's called on setup and by default does nothing - it's up to the
+     * subclasses to override.
+     *
+     * @return whether the tests in the subclass are supported on this device.
+     */
+    protected boolean setUpActiveNetworkMeteringState() throws Exception {
+        return true;
+    }
+
+    /**
+     * Makes sure the active network is not metered.
+     *
+     * <p>If the device does not supoprt un-metered networks (for example if it
+     * only has cellular data but not wi-fi), it should return {@code false};
+     * otherwise, it should return {@code true} (or fail if the un-metered
+     * network could not be set).
+     *
+     * @return {@code true} if the network is now unmetered.
+     */
+    protected boolean setUnmeteredNetwork() throws Exception {
+        final NetworkInfo info = mCm.getActiveNetworkInfo();
+        assertNotNull("Could not get active network", info);
+        if (!mCm.isActiveNetworkMetered()) {
+            Log.d(TAG, "Active network is not metered: " + info);
+        } else if (info.getType() == ConnectivityManager.TYPE_WIFI) {
+            Log.i(TAG, "Setting active WI-FI network as not metered: " + info );
+            setWifiMeteredStatus(false);
+        } else {
+            Log.d(TAG, "Active network cannot be set to un-metered: " + info);
+            return false;
+        }
+        assertActiveNetworkMetered(false); // Sanity check.
+        return true;
+    }
+
+    /**
+     * Enables metering on the active network if supported.
+     *
+     * <p>If the device does not support metered networks it should return
+     * {@code false}; otherwise, it should return {@code true} (or fail if the
+     * metered network could not be set).
+     *
+     * @return {@code true} if the network is now metered.
+     */
+    protected boolean setMeteredNetwork() throws Exception {
+        final NetworkInfo info = mCm.getActiveNetworkInfo();
+        final boolean metered = mCm.isActiveNetworkMetered();
+        if (metered) {
+            Log.d(TAG, "Active network already metered: " + info);
+            return true;
+        } else if (info.getType() != ConnectivityManager.TYPE_WIFI) {
+            Log.w(TAG, "Active network does not support metering: " + info);
+            return false;
+        } 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);
+        assertActiveNetworkMetered(true);
+        return true;
+    }
+
+    /**
+     * Resets the device metering state to what it was before the test started.
+     *
+     * <p>This reverts any metering changes made by {@code setMeteredNetwork}.
+     */
+    protected void resetMeteredNetwork() throws Exception {
+        if (mMeteredWifi != null) {
+            Log.i(TAG, "resetMeteredNetwork(): SID '" + mMeteredWifi
+                    + "' was set as metered by test case; resetting it");
+            setWifiMeteredStatus(mMeteredWifi, false);
+            assertActiveNetworkMetered(false); // Sanity check.
+        }
+    }
+
+    private void assertActiveNetworkMetered(boolean expected) throws Exception {
+        final int maxTries = 5;
+        NetworkInfo info = null;
+        for (int i = 1; i <= maxTries; i++) {
+            info = mCm.getActiveNetworkInfo();
+            if (info != null) {
+                break;
+            }
+            Log.v(TAG, "No active network info on attempt #" + i
+                    + "; sleeping 1s before polling again");
+            Thread.sleep(SECOND_IN_MS);
+        }
+        assertNotNull("No active network after " + maxTries + " attempts", info);
+        assertEquals("Wrong metered status for active network " + info, expected,
+                mCm.isActiveNetworkMetered());
+    }
+
+    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;
+        final String expectedUid = Integer.toString(uid);
+        String uids = "";
+        for (int i = 1; i <= maxTries; i++) {
+            final String output =
+                    executeShellCommand("cmd netpolicy list " + list);
+            uids = output.split(":")[1];
+            for (String candidate : uids.split(" ")) {
+                actual = candidate.trim().equals(expectedUid);
+                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");
+            SystemClock.sleep(SECOND_IN_MS);
+        }
+        fail(list + " check for uid " + uid + " failed: expected " + expected + ", got " + actual
+                + ". Full list: " + uids);
+    }
+
+    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 turnBatteryOff() throws Exception {
+        executeSilentShellCommand("cmd battery unplug");
+    }
+
+    protected void turnBatteryOn() throws Exception {
+        executeSilentShellCommand("cmd battery reset");
+    }
+
+    protected void turnScreenOff() throws Exception {
+        executeSilentShellCommand("input keyevent KEYCODE_SLEEP");
+    }
+
+    protected void turnScreenOn() throws Exception {
+        executeSilentShellCommand("input keyevent KEYCODE_WAKEUP");
+        executeSilentShellCommand("wm dismiss-keyguard");
+    }
+
+    protected void setBatterySaverMode(boolean enabled) throws Exception {
+        Log.i(TAG, "Setting Battery Saver Mode to " + enabled);
+        if (enabled) {
+            turnBatteryOff();
+            executeSilentShellCommand("cmd battery unplug");
+            executeSilentShellCommand("settings put global low_power 1");
+        } else {
+            executeSilentShellCommand("settings put global low_power 0");
+            turnBatteryOn();
+        }
+    }
+
+    protected void setDozeMode(boolean enabled) throws Exception {
+        // Sanity check, since tests should check beforehand....
+        assertTrue("Device does not support Doze Mode", isDozeModeEnabled());
+
+        Log.i(TAG, "Setting Doze Mode to " + enabled);
+        if (enabled) {
+            turnBatteryOff();
+            turnScreenOff();
+            executeShellCommand("dumpsys deviceidle force-idle deep");
+        } else {
+            turnScreenOn();
+            turnBatteryOn();
+            executeShellCommand("dumpsys deviceidle unforce");
+        }
+        // Sanity check.
+        assertDozeMode(enabled);
+    }
+
+    protected void assertDozeMode(boolean enabled) throws Exception {
+        assertDelayedShellCommand("dumpsys deviceidle get deep", enabled ? "IDLE" : "ACTIVE");
+    }
+
+    protected boolean isDozeModeEnabled() throws Exception {
+        final String result = executeShellCommand("cmd deviceidle enabled deep").trim();
+        return result.equals("1");
+    }
+
+    protected void setAppIdle(boolean enabled) throws Exception {
+        Log.i(TAG, "Setting app idle to " + enabled);
+        executeSilentShellCommand("am set-inactive " + TEST_APP2_PKG + " " + enabled );
+        assertAppIdle(enabled); // Sanity check
+    }
+
+    protected void assertAppIdle(boolean enabled) throws Exception {
+        assertDelayedShellCommand("am get-inactive " + TEST_APP2_PKG, 10, 2, "Idle=" + enabled);
+    }
+
+    /**
+     * 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 = 10;
+        for (int i = 1; i <= maxTries; i++) {
+            final String message =
+                    sendOrderedBroadcast(new Intent(ACTION_RECEIVER_READY), 4 * 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");
+            SystemClock.sleep(SECOND_IN_MS);
+        }
+        fail("app2 receiver is not ready");
+    }
+
+    /**
+     * Registers a {@link NotificationListenerService} implementation that will execute the
+     * notification actions right after the notification is sent.
+     */
+    protected void registerNotificationListenerService() throws Exception {
+        final StringBuilder listeners = new StringBuilder(getNotificationListenerServices());
+        if (listeners.length() > 0) {
+            listeners.append(":");
+        }
+        listeners.append(MyNotificationListenerService.getId());
+        executeShellCommand("settings put secure enabled_notification_listeners " + listeners);
+        final String newListeners = getNotificationListenerServices();
+        assertEquals("Failed to set 'enabled_notification_listeners'",
+                listeners.toString(), newListeners);
+    }
+
+    private String getNotificationListenerServices() throws Exception {
+        return executeShellCommand("settings get secure enabled_notification_listeners");
+    }
+
+    protected void setPendingIntentWhitelistDuration(int durationMs) throws Exception {
+        final String command = String.format(
+                "settings put global device_idle_constants %s=%d",
+                "notification_whitelist_duration", durationMs);
+        executeSilentShellCommand(command);
+    }
+
+    protected void resetDeviceIdleSettings() throws Exception {
+        executeShellCommand("settings delete global device_idle_constants");
+    }
+
+    protected void startForegroundService() throws Exception {
+        executeShellCommand(
+                "am startservice -f 1 com.android.cts.net.hostside.app2/.MyForegroundService");
+        assertForegroundServiceState();
+    }
+
+    protected void stopForegroundService() throws Exception {
+        executeShellCommand(
+                "am startservice -f 2 com.android.cts.net.hostside.app2/.MyForegroundService");
+        // NOTE: cannot assert state because it depends on whether activity was on top before.
+    }
+
+    /**
+     * Launches an activity on app2 so its process is elevated to foreground status.
+     */
+    protected void launchActivity() throws Exception {
+        turnScreenOn();
+        executeShellCommand("am start com.android.cts.net.hostside.app2/.MyActivity");
+        final int maxTries = 30;
+        ProcessState state = null;
+        for (int i = 1; i <= maxTries; i++) {
+            state = getProcessStateByUid(mUid);
+            if (state.state == PROCESS_STATE_TOP) return;
+            Log.w(TAG, "launchActivity(): uid " + mUid + " not on TOP state on attempt #" + i
+                    + "; turning screen on and sleeping 1s before checking again");
+            turnScreenOn();
+            SystemClock.sleep(SECOND_IN_MS);
+        }
+        fail("App2 is not on foreground state after " + maxTries + " attempts: " + state);
+    }
+
+    /**
+     * Finishes an activity on app2 so its process is demoted fromforeground status.
+     */
+    protected void finishActivity() throws Exception {
+        executeShellCommand("am broadcast -a "
+                + " com.android.cts.net.hostside.app2.action.FINISH_ACTIVITY "
+                + "--receiver-foreground --receiver-registered-only");
+    }
+
+    protected void sendNotification(int notificationId) {
+        final Intent intent = new Intent(ACTION_SEND_NOTIFICATION);
+        intent.putExtra(EXTRA_NOTIFICATION_ID, notificationId);
+        Log.d(TAG, "Sending broadcast: " + intent);
+        mContext.sendBroadcast(intent);
+    }
+
+    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;
+        }
+    }
+
+    /**
+     * Helper class used to assert the result of a Shell command.
+     */
+    protected static interface ExpectResultChecker {
+        /**
+         * Checkes whether the result of the command matched the expectation.
+         */
+        boolean isExpected(String result);
+        /**
+         * Gets the expected result so it's displayed on log and failure messages.
+         */
+        String getExpected();
+    }
+}
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/AppIdleMeteredTest.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/AppIdleMeteredTest.java
new file mode 100644
index 0000000..622d993
--- /dev/null
+++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/AppIdleMeteredTest.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.net.hostside;
+
+public class AppIdleMeteredTest extends AbstractAppIdleTestCase {
+
+    @Override
+    protected boolean setUpActiveNetworkMeteringState() throws Exception {
+        return setMeteredNetwork();
+    }
+
+    @Override
+    protected void tearDownMeteredNetwork() throws Exception {
+        resetMeteredNetwork();
+    }
+}
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/AppIdleNonMeteredTest.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/AppIdleNonMeteredTest.java
new file mode 100644
index 0000000..bde71f9
--- /dev/null
+++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/AppIdleNonMeteredTest.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.hostside;
+
+public class AppIdleNonMeteredTest extends AbstractAppIdleTestCase {
+    @Override
+    protected boolean setUpActiveNetworkMeteringState() throws Exception {
+        return setUnmeteredNetwork();
+    }
+}
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/BatterySaverModeMeteredTest.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/BatterySaverModeMeteredTest.java
new file mode 100644
index 0000000..3071cfe
--- /dev/null
+++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/BatterySaverModeMeteredTest.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.net.hostside;
+
+public class BatterySaverModeMeteredTest extends AbstractBatterySaverModeTestCase {
+
+    @Override
+    protected boolean setUpActiveNetworkMeteringState() throws Exception {
+        return setMeteredNetwork();
+    }
+
+    @Override
+    protected void tearDownMeteredNetwork() throws Exception {
+        resetMeteredNetwork();
+    }
+}
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..6d3076f
--- /dev/null
+++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/BatterySaverModeNonMeteredTest.java
@@ -0,0 +1,25 @@
+/*
+ * 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;
+
+public class BatterySaverModeNonMeteredTest extends AbstractBatterySaverModeTestCase {
+
+    @Override
+    protected boolean setUpActiveNetworkMeteringState() throws Exception {
+        return setUnmeteredNetwork();
+    }
+}
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..d5e236f
--- /dev/null
+++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/DataSaverModeTest.java
@@ -0,0 +1,211 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.net.hostside;
+
+import static android.net.ConnectivityManager.RESTRICT_BACKGROUND_STATUS_DISABLED;
+import static android.net.ConnectivityManager.RESTRICT_BACKGROUND_STATUS_ENABLED;
+import static android.net.ConnectivityManager.RESTRICT_BACKGROUND_STATUS_WHITELISTED;
+
+import android.util.Log;
+
+public class DataSaverModeTest extends AbstractRestrictBackgroundNetworkTestCase {
+
+    private static final String[] REQUIRED_WHITELISTED_PACKAGES = {
+        "com.android.providers.downloads"
+    };
+
+    private boolean mIsDataSaverSupported;
+
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+
+        mIsDataSaverSupported = isDataSaverSupported();
+        if (!isSupported()) return;
+
+        // Set initial state.
+        setRestrictBackground(false);
+        removeRestrictBackgroundWhitelist(mUid);
+        removeRestrictBackgroundBlacklist(mUid);
+
+        registerBroadcastReceiver();
+        assertRestrictBackgroundChangedReceived(0);
+   }
+
+    @Override
+    protected void tearDown() throws Exception {
+        super.tearDown();
+
+        if (!isSupported()) return;
+
+        try {
+            resetMeteredNetwork();
+        } finally {
+            setRestrictBackground(false);
+        }
+    }
+
+    @Override
+    protected boolean setUpActiveNetworkMeteringState() throws Exception {
+        return setMeteredNetwork();
+    }
+
+    @Override
+    protected boolean isSupported() throws Exception {
+        if (!mIsDataSaverSupported) {
+            Log.i(TAG, "Skipping " + getClass() + "." + getName()
+                    + "() because device does not support Data Saver Mode");
+        }
+        return mIsDataSaverSupported && super.isSupported();
+    }
+
+    /**
+     * As per CDD requirements, if the device doesn't support data saver mode then
+     * ConnectivityManager.getRestrictBackgroundStatus() will always return
+     * RESTRICT_BACKGROUND_STATUS_DISABLED. So, enable the data saver mode and check if
+     * ConnectivityManager.getRestrictBackgroundStatus() for an app in background returns
+     * RESTRICT_BACKGROUND_STATUS_DISABLED or not.
+     */
+    private boolean isDataSaverSupported() throws Exception {
+        assertMyRestrictBackgroundStatus(RESTRICT_BACKGROUND_STATUS_DISABLED);
+        try {
+            setRestrictBackground(true);
+            return !isMyRestrictBackgroundStatus(RESTRICT_BACKGROUND_STATUS_DISABLED);
+        } finally {
+            setRestrictBackground(false);
+        }
+    }
+
+    public void testGetRestrictBackgroundStatus_disabled() throws Exception {
+        if (!isSupported()) return;
+
+        assertDataSaverStatusOnBackground(RESTRICT_BACKGROUND_STATUS_DISABLED);
+
+        // Sanity check: make sure status is always disabled, never whitelisted
+        addRestrictBackgroundWhitelist(mUid);
+        assertRestrictBackgroundChangedReceived(0);
+        assertDataSaverStatusOnBackground(RESTRICT_BACKGROUND_STATUS_DISABLED);
+
+        assertsForegroundAlwaysHasNetworkAccess();
+        assertDataSaverStatusOnBackground(RESTRICT_BACKGROUND_STATUS_DISABLED);
+    }
+
+    public void testGetRestrictBackgroundStatus_whitelisted() throws Exception {
+        if (!isSupported()) return;
+
+        setRestrictBackground(true);
+        assertRestrictBackgroundChangedReceived(1);
+        assertDataSaverStatusOnBackground(RESTRICT_BACKGROUND_STATUS_ENABLED);
+
+        addRestrictBackgroundWhitelist(mUid);
+        assertRestrictBackgroundChangedReceived(2);
+        assertDataSaverStatusOnBackground(RESTRICT_BACKGROUND_STATUS_WHITELISTED);
+
+        removeRestrictBackgroundWhitelist(mUid);
+        assertRestrictBackgroundChangedReceived(3);
+        assertDataSaverStatusOnBackground(RESTRICT_BACKGROUND_STATUS_ENABLED);
+
+        assertsForegroundAlwaysHasNetworkAccess();
+        assertDataSaverStatusOnBackground(RESTRICT_BACKGROUND_STATUS_ENABLED);
+    }
+
+    public void testGetRestrictBackgroundStatus_enabled() throws Exception {
+        if (!isSupported()) return;
+
+        setRestrictBackground(true);
+        assertRestrictBackgroundChangedReceived(1);
+        assertDataSaverStatusOnBackground(RESTRICT_BACKGROUND_STATUS_ENABLED);
+
+        assertsForegroundAlwaysHasNetworkAccess();
+        assertDataSaverStatusOnBackground(RESTRICT_BACKGROUND_STATUS_ENABLED);
+
+        // Make sure foreground app doesn't lose access upon enabling it.
+        setRestrictBackground(false);
+        launchActivity();
+        assertForegroundNetworkAccess();
+        setRestrictBackground(true);
+        assertForegroundNetworkAccess();
+        finishActivity();
+        assertBackgroundNetworkAccess(false);
+
+        // Same for foreground service.
+        setRestrictBackground(false);
+        startForegroundService();
+        assertForegroundNetworkAccess();
+        setRestrictBackground(true);
+        assertForegroundNetworkAccess();
+        stopForegroundService();
+        assertBackgroundNetworkAccess(false);
+    }
+
+    public void testGetRestrictBackgroundStatus_blacklisted() throws Exception {
+        if (!isSupported()) return;
+
+        addRestrictBackgroundBlacklist(mUid);
+        assertRestrictBackgroundChangedReceived(1);
+        assertDataSaverStatusOnBackground(RESTRICT_BACKGROUND_STATUS_ENABLED);
+
+        assertsForegroundAlwaysHasNetworkAccess();
+        assertDataSaverStatusOnBackground(RESTRICT_BACKGROUND_STATUS_ENABLED);
+
+        // Make sure blacklist prevails over whitelist.
+        setRestrictBackground(true);
+        assertRestrictBackgroundChangedReceived(2);
+        assertDataSaverStatusOnBackground(RESTRICT_BACKGROUND_STATUS_ENABLED);
+        addRestrictBackgroundWhitelist(mUid);
+        assertRestrictBackgroundChangedReceived(3);
+        assertDataSaverStatusOnBackground(RESTRICT_BACKGROUND_STATUS_ENABLED);
+
+        // Check status after removing blacklist.
+        removeRestrictBackgroundBlacklist(mUid);
+        assertRestrictBackgroundChangedReceived(4);
+        assertDataSaverStatusOnBackground(RESTRICT_BACKGROUND_STATUS_WHITELISTED);
+        setRestrictBackground(false);
+        assertRestrictBackgroundChangedReceived(5);
+        assertDataSaverStatusOnBackground(RESTRICT_BACKGROUND_STATUS_DISABLED);
+
+        assertsForegroundAlwaysHasNetworkAccess();
+        assertDataSaverStatusOnBackground(RESTRICT_BACKGROUND_STATUS_DISABLED);
+    }
+
+    public void testGetRestrictBackgroundStatus_requiredWhitelistedPackages() throws Exception {
+        if (!isSupported()) return;
+
+        final StringBuilder error = new StringBuilder();
+        for (String packageName : REQUIRED_WHITELISTED_PACKAGES) {
+            int uid = -1;
+            try {
+                uid = getUid(packageName);
+                assertRestrictBackgroundWhitelist(uid, true);
+            } catch (Throwable t) {
+                error.append("\nFailed for '").append(packageName).append("'");
+                if (uid > 0) {
+                    error.append(" (uid ").append(uid).append(")");
+                }
+                error.append(": ").append(t).append("\n");
+            }
+        }
+        if (error.length() > 0) {
+            fail(error.toString());
+        }
+    }
+
+    private void assertDataSaverStatusOnBackground(int expectedStatus) throws Exception {
+        assertRestrictBackgroundStatus(expectedStatus);
+        assertBackgroundNetworkAccess(expectedStatus != RESTRICT_BACKGROUND_STATUS_ENABLED);
+    }
+}
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/DozeModeMeteredTest.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/DozeModeMeteredTest.java
new file mode 100644
index 0000000..e4189af
--- /dev/null
+++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/DozeModeMeteredTest.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.net.hostside;
+
+public class DozeModeMeteredTest extends AbstractDozeModeTestCase {
+
+    @Override
+    protected boolean setUpActiveNetworkMeteringState() throws Exception {
+        return setMeteredNetwork();
+    }
+
+    @Override
+    protected void tearDownMeteredNetwork() throws Exception {
+        resetMeteredNetwork();
+    }
+}
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/DozeModeNonMeteredTest.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/DozeModeNonMeteredTest.java
new file mode 100644
index 0000000..edbbb9e
--- /dev/null
+++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/DozeModeNonMeteredTest.java
@@ -0,0 +1,25 @@
+/*
+ * 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;
+
+public class DozeModeNonMeteredTest extends AbstractDozeModeTestCase {
+
+    @Override
+    protected boolean setUpActiveNetworkMeteringState() throws Exception {
+        return setUnmeteredNetwork();
+    }
+}
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/MixedModesTest.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/MixedModesTest.java
new file mode 100644
index 0000000..ec49eee
--- /dev/null
+++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/MixedModesTest.java
@@ -0,0 +1,203 @@
+/*
+ * 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 android.util.Log;
+
+/**
+ * Test cases for the more complex scenarios where multiple restrictions (like Battery Saver Mode
+ * and Data Saver Mode) are applied simultaneously.
+ * <p>
+ * <strong>NOTE: </strong>it might sound like the test methods on this class are testing too much,
+ * which would make it harder to diagnose individual failures, but the assumption is that such
+ * failure most likely will happen when the restriction is tested individually as well.
+ */
+public class MixedModesTest extends AbstractRestrictBackgroundNetworkTestCase {
+    private static final String TAG = "MixedModesTest";
+
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+
+        if (!isSupported()) return;
+
+        // Set initial state.
+        removeRestrictBackgroundWhitelist(mUid);
+        removeRestrictBackgroundBlacklist(mUid);
+        removePowerSaveModeWhitelist(TEST_APP2_PKG);
+
+        registerBroadcastReceiver();
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        super.tearDown();
+
+        if (!isSupported()) return;
+
+        try {
+            setRestrictBackground(false);
+        } finally {
+            setBatterySaverMode(false);
+        }
+    }
+
+    /**
+     * Tests all DS ON and BS ON scenarios from network-policy-restrictions.md on metered networks.
+     */
+    public void testDataAndBatterySaverModes_meteredNetwork() throws Exception {
+        if (!isSupported()) return;
+
+        if (!isDozeModeEnabled()) {
+            Log.w(TAG, "testDataAndBatterySaverModes_meteredNetwork() skipped because "
+                    + "device does not support Doze Mode");
+            return;
+        }
+
+        Log.i(TAG, "testDataAndBatterySaverModes_meteredNetwork() tests");
+        if (!setMeteredNetwork()) {
+            Log.w(TAG, "testDataAndBatterySaverModes_meteredNetwork() skipped because "
+                    + "device cannot use a metered network");
+            return;
+        }
+
+        try {
+            setRestrictBackground(true);
+            setBatterySaverMode(true);
+
+            Log.v(TAG, "Not whitelisted for any.");
+            assertBackgroundNetworkAccess(false);
+            assertsForegroundAlwaysHasNetworkAccess();
+            assertBackgroundNetworkAccess(false);
+
+            Log.v(TAG, "Whitelisted for Data Saver but not for Battery Saver.");
+            addRestrictBackgroundWhitelist(mUid);
+            removePowerSaveModeWhitelist(TEST_APP2_PKG);
+            assertBackgroundNetworkAccess(false);
+            assertsForegroundAlwaysHasNetworkAccess();
+            assertBackgroundNetworkAccess(false);
+            removeRestrictBackgroundWhitelist(mUid);
+
+            Log.v(TAG, "Whitelisted for Battery Saver but not for Data Saver.");
+            addPowerSaveModeWhitelist(TEST_APP2_PKG);
+            removeRestrictBackgroundWhitelist(mUid);
+            assertBackgroundNetworkAccess(false);
+            assertsForegroundAlwaysHasNetworkAccess();
+            assertBackgroundNetworkAccess(false);
+            removePowerSaveModeWhitelist(TEST_APP2_PKG);
+
+            Log.v(TAG, "Whitelisted for both.");
+            addRestrictBackgroundWhitelist(mUid);
+            addPowerSaveModeWhitelist(TEST_APP2_PKG);
+            assertBackgroundNetworkAccess(true);
+            assertsForegroundAlwaysHasNetworkAccess();
+            assertBackgroundNetworkAccess(true);
+            removePowerSaveModeWhitelist(TEST_APP2_PKG);
+            assertBackgroundNetworkAccess(false);
+            removeRestrictBackgroundWhitelist(mUid);
+
+            Log.v(TAG, "Blacklisted for Data Saver, not whitelisted for Battery Saver.");
+            addRestrictBackgroundBlacklist(mUid);
+            removePowerSaveModeWhitelist(TEST_APP2_PKG);
+            assertBackgroundNetworkAccess(false);
+            assertsForegroundAlwaysHasNetworkAccess();
+            assertBackgroundNetworkAccess(false);
+            removeRestrictBackgroundBlacklist(mUid);
+
+            Log.v(TAG, "Blacklisted for Data Saver, whitelisted for Battery Saver.");
+            addRestrictBackgroundBlacklist(mUid);
+            addPowerSaveModeWhitelist(TEST_APP2_PKG);
+            assertBackgroundNetworkAccess(false);
+            assertsForegroundAlwaysHasNetworkAccess();
+            assertBackgroundNetworkAccess(false);
+            removeRestrictBackgroundBlacklist(mUid);
+            removePowerSaveModeWhitelist(TEST_APP2_PKG);
+        } finally {
+            resetMeteredNetwork();
+        }
+    }
+
+    /**
+     * Tests all DS ON and BS ON scenarios from network-policy-restrictions.md on non-metered
+     * networks.
+     */
+    public void testDataAndBatterySaverModes_nonMeteredNetwork() throws Exception {
+        if (!isSupported()) return;
+
+        if (!isDozeModeEnabled()) {
+            Log.w(TAG, "testDataAndBatterySaverModes_nonMeteredNetwork() skipped because "
+                    + "device does not support Doze Mode");
+            return;
+        }
+
+        if (!setUnmeteredNetwork()) {
+            Log.w(TAG, "testDataAndBatterySaverModes_nonMeteredNetwork() skipped because network"
+                    + " is metered");
+            return;
+        }
+        Log.i(TAG, "testDataAndBatterySaverModes_nonMeteredNetwork() tests");
+        setRestrictBackground(true);
+        setBatterySaverMode(true);
+
+        Log.v(TAG, "Not whitelisted for any.");
+        assertBackgroundNetworkAccess(false);
+        assertsForegroundAlwaysHasNetworkAccess();
+        assertBackgroundNetworkAccess(false);
+
+        Log.v(TAG, "Whitelisted for Data Saver but not for Battery Saver.");
+        addRestrictBackgroundWhitelist(mUid);
+        removePowerSaveModeWhitelist(TEST_APP2_PKG);
+        assertBackgroundNetworkAccess(false);
+        assertsForegroundAlwaysHasNetworkAccess();
+        assertBackgroundNetworkAccess(false);
+        removeRestrictBackgroundWhitelist(mUid);
+
+        Log.v(TAG, "Whitelisted for Battery Saver but not for Data Saver.");
+        addPowerSaveModeWhitelist(TEST_APP2_PKG);
+        removeRestrictBackgroundWhitelist(mUid);
+        assertBackgroundNetworkAccess(true);
+        assertsForegroundAlwaysHasNetworkAccess();
+        assertBackgroundNetworkAccess(true);
+        removePowerSaveModeWhitelist(TEST_APP2_PKG);
+
+        Log.v(TAG, "Whitelisted for both.");
+        addRestrictBackgroundWhitelist(mUid);
+        addPowerSaveModeWhitelist(TEST_APP2_PKG);
+        assertBackgroundNetworkAccess(true);
+        assertsForegroundAlwaysHasNetworkAccess();
+        assertBackgroundNetworkAccess(true);
+        removePowerSaveModeWhitelist(TEST_APP2_PKG);
+        assertBackgroundNetworkAccess(false);
+        removeRestrictBackgroundWhitelist(mUid);
+
+        Log.v(TAG, "Blacklisted for Data Saver, not whitelisted for Battery Saver.");
+        addRestrictBackgroundBlacklist(mUid);
+        removePowerSaveModeWhitelist(TEST_APP2_PKG);
+        assertBackgroundNetworkAccess(false);
+        assertsForegroundAlwaysHasNetworkAccess();
+        assertBackgroundNetworkAccess(false);
+        removeRestrictBackgroundBlacklist(mUid);
+
+        Log.v(TAG, "Blacklisted for Data Saver, whitelisted for Battery Saver.");
+        addRestrictBackgroundBlacklist(mUid);
+        addPowerSaveModeWhitelist(TEST_APP2_PKG);
+        assertBackgroundNetworkAccess(true);
+        assertsForegroundAlwaysHasNetworkAccess();
+        assertBackgroundNetworkAccess(true);
+        removeRestrictBackgroundBlacklist(mUid);
+        removePowerSaveModeWhitelist(TEST_APP2_PKG);
+    }
+}
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/MyNotificationListenerService.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/MyNotificationListenerService.java
new file mode 100644
index 0000000..b9c3031
--- /dev/null
+++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/MyNotificationListenerService.java
@@ -0,0 +1,61 @@
+/*
+ * 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 android.app.Notification;
+import android.app.PendingIntent.CanceledException;
+import android.service.notification.NotificationListenerService;
+import android.service.notification.StatusBarNotification;
+import android.util.Log;
+
+/**
+ * NotificationListenerService implementation that executes the notification actions once they're
+ * created.
+ */
+public class MyNotificationListenerService extends NotificationListenerService {
+    private static final String TAG = "MyNotificationListenerService";
+
+    @Override
+    public void onListenerConnected() {
+        Log.d(TAG, "onListenerConnected()");
+    }
+
+    @Override
+    public void onNotificationPosted(StatusBarNotification sbn) {
+        Log.d(TAG, "onNotificationPosted(): "  + sbn);
+        if (!sbn.getPackageName().startsWith(getPackageName())) {
+            Log.v(TAG, "ignoring notification from a different package");
+            return;
+        }
+        final Notification notification = sbn.getNotification();
+        if (notification.actions == null) {
+            Log.w(TAG, "ignoring notification without an action");
+        }
+        for (Notification.Action action : notification.actions) {
+            Log.i(TAG, "Sending pending intent " + action.actionIntent);
+            try {
+                action.actionIntent.send();
+            } catch (CanceledException e) {
+                Log.w(TAG, "Pending Intent canceled");
+            }
+        }
+    }
+
+    static String getId() {
+        return String.format("%s/%s", MyNotificationListenerService.class.getPackage().getName(),
+                MyNotificationListenerService.class.getName());
+    }
+}
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/app/src/com/android/cts/net/hostside/RemoteSocketFactoryClient.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/RemoteSocketFactoryClient.java
new file mode 100644
index 0000000..799fe50
--- /dev/null
+++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/RemoteSocketFactoryClient.java
@@ -0,0 +1,91 @@
+/*
+ * 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 android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.ConditionVariable;
+import android.os.IBinder;
+import android.os.RemoteException;
+
+import com.android.cts.net.hostside.IRemoteSocketFactory;
+
+import java.io.FileDescriptor;
+
+public class RemoteSocketFactoryClient {
+    private static final int TIMEOUT_MS = 5000;
+    private static final String PACKAGE = RemoteSocketFactoryClient.class.getPackage().getName();
+    private static final String APP2_PACKAGE = PACKAGE + ".app2";
+    private static final String SERVICE_NAME = APP2_PACKAGE + ".RemoteSocketFactoryService";
+
+    private Context mContext;
+    private ServiceConnection mServiceConnection;
+    private IRemoteSocketFactory mService;
+
+    public RemoteSocketFactoryClient(Context context) {
+        mContext = context;
+    }
+
+    public void bind() {
+        if (mService != null) {
+            throw new IllegalStateException("Already bound");
+        }
+
+        final ConditionVariable cv = new ConditionVariable();
+        mServiceConnection = new ServiceConnection() {
+            @Override
+            public void onServiceConnected(ComponentName name, IBinder service) {
+                mService = IRemoteSocketFactory.Stub.asInterface(service);
+                cv.open();
+            }
+            @Override
+            public void onServiceDisconnected(ComponentName name) {
+                mService = null;
+            }
+        };
+
+        final Intent intent = new Intent();
+        intent.setComponent(new ComponentName(APP2_PACKAGE, SERVICE_NAME));
+        mContext.bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE);
+        cv.block(TIMEOUT_MS);
+        if (mService == null) {
+            throw new IllegalStateException(
+                    "Could not bind to RemoteSocketFactory service after " + TIMEOUT_MS + "ms");
+        }
+    }
+
+    public void unbind() {
+        if (mService != null) {
+            mContext.unbindService(mServiceConnection);
+        }
+    }
+
+    public FileDescriptor openSocketFd(
+            String host, int port, int timeoutMs) throws RemoteException {
+        return mService.openSocketFd(host, port, timeoutMs).getFileDescriptor();
+    }
+
+    public String getPackageName() throws RemoteException {
+        return mService.getPackageName();
+    }
+
+    public int getUid() throws RemoteException {
+        return mService.getUid();
+    }
+}
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/VpnTest.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/VpnTest.java
index 5045cc2..12fe625 100755
--- a/tests/cts/hostside/app/src/com/android/cts/net/hostside/VpnTest.java
+++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/VpnTest.java
@@ -27,6 +27,8 @@
 import android.net.NetworkCapabilities;
 import android.net.NetworkRequest;
 import android.net.VpnService;
+import android.os.ParcelFileDescriptor;
+import android.os.Process;
 import android.support.test.uiautomator.UiDevice;
 import android.support.test.uiautomator.UiObject;
 import android.support.test.uiautomator.UiObjectNotFoundException;
@@ -40,11 +42,18 @@
 import android.text.TextUtils;
 import android.util.Log;
 
+import com.android.cts.net.hostside.IRemoteSocketFactory;
+
+import java.io.BufferedReader;
 import java.io.Closeable;
 import java.io.FileDescriptor;
+import java.io.FileOutputStream;
+import java.io.FileInputStream;
+import java.io.InputStreamReader;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
+import java.io.PrintWriter;
 import java.net.DatagramPacket;
 import java.net.DatagramSocket;
 import java.net.Inet6Address;
@@ -52,6 +61,8 @@
 import java.net.InetSocketAddress;
 import java.net.ServerSocket;
 import java.net.Socket;
+import java.net.SocketException;
+import java.nio.charset.StandardCharsets;
 import java.util.Random;
 
 /**
@@ -79,11 +90,14 @@
     public static String TAG = "VpnTest";
     public static int TIMEOUT_MS = 3 * 1000;
     public static int SOCKET_TIMEOUT_MS = 100;
+    public static String TEST_HOST = "connectivitycheck.gstatic.com";
 
     private UiDevice mDevice;
     private MyActivity mActivity;
     private String mPackageName;
     private ConnectivityManager mCM;
+    private RemoteSocketFactoryClient mRemoteSocketFactoryClient;
+
     Network mNetwork;
     NetworkCallback mCallback;
     final Object mLock = new Object();
@@ -107,11 +121,14 @@
                 MyActivity.class, null);
         mPackageName = mActivity.getPackageName();
         mCM = (ConnectivityManager) mActivity.getSystemService(mActivity.CONNECTIVITY_SERVICE);
+        mRemoteSocketFactoryClient = new RemoteSocketFactoryClient(mActivity);
+        mRemoteSocketFactoryClient.bind();
         mDevice.waitForIdle();
     }
 
     @Override
     public void tearDown() throws Exception {
+        mRemoteSocketFactoryClient.unbind();
         if (mCallback != null) {
             mCM.unregisterNetworkCallback(mCallback);
         }
@@ -441,7 +458,7 @@
         }
     }
 
-    private void checkTrafficOnVpn() throws IOException, ErrnoException {
+    private void checkTrafficOnVpn() throws Exception {
         checkUdpEcho("192.0.2.251", "192.0.2.2");
         checkUdpEcho("2001:db8:dead:beef::f00", "2001:db8:1:2::ffe");
         checkPing("2001:db8:dead:beef::f00");
@@ -449,29 +466,88 @@
         checkTcpReflection("2001:db8:dead:beef::f00", "2001:db8:1:2::ffe");
     }
 
-    private void checkNoTrafficOnVpn() throws IOException, ErrnoException {
+    private void checkNoTrafficOnVpn() throws Exception {
         checkUdpEcho("192.0.2.251", null);
         checkUdpEcho("2001:db8:dead:beef::f00", null);
         checkTcpReflection("192.0.2.252", null);
         checkTcpReflection("2001:db8:dead:beef::f00", null);
     }
 
+    private FileDescriptor openSocketFd(String host, int port, int timeoutMs) throws Exception {
+        Socket s = new Socket(host, port);
+        s.setSoTimeout(timeoutMs);
+        return ParcelFileDescriptor.fromSocket(s).getFileDescriptor();
+    }
+
+    private FileDescriptor openSocketFdInOtherApp(
+            String host, int port, int timeoutMs) throws Exception {
+        Log.d(TAG, String.format("Creating test socket in UID=%d, my UID=%d",
+                mRemoteSocketFactoryClient.getUid(), Os.getuid()));
+        FileDescriptor fd = mRemoteSocketFactoryClient.openSocketFd(host, port, TIMEOUT_MS);
+        return fd;
+    }
+
+    private void sendRequest(FileDescriptor fd, String host) throws Exception {
+        String request = "GET /generate_204 HTTP/1.1\r\n" +
+                "Host: " + host + "\r\n" +
+                "Connection: keep-alive\r\n\r\n";
+        byte[] requestBytes = request.getBytes(StandardCharsets.UTF_8);
+        int ret = Os.write(fd, requestBytes, 0, requestBytes.length);
+        Log.d(TAG, "Wrote " + ret + "bytes");
+
+        String expected = "HTTP/1.1 204 No Content\r\n";
+        byte[] response = new byte[expected.length()];
+        Os.read(fd, response, 0, response.length);
+
+        String actual = new String(response, StandardCharsets.UTF_8);
+        assertEquals(expected, actual);
+        Log.d(TAG, "Got response: " + actual);
+    }
+
+    private void assertSocketStillOpen(FileDescriptor fd, String host) throws Exception {
+        try {
+            sendRequest(fd, host);
+        } finally {
+            Os.close(fd);
+        }
+    }
+
+    private void assertSocketClosed(FileDescriptor fd, String host) throws Exception {
+        try {
+            sendRequest(fd, host);
+            fail("Socket opened before VPN connects should be closed when VPN connects");
+        } catch (ErrnoException expected) {
+            assertEquals(ECONNABORTED, expected.errno);
+        } finally {
+            Os.close(fd);
+        }
+    }
+
     public void testDefault() throws Exception {
         if (!supportedHardware()) return;
 
+        FileDescriptor fd = openSocketFdInOtherApp(TEST_HOST, 80, TIMEOUT_MS);
+
         startVpn(new String[] {"192.0.2.2/32", "2001:db8:1:2::ffe/128"},
                  new String[] {"0.0.0.0/0", "::/0"},
                  "", "");
 
+        assertSocketClosed(fd, TEST_HOST);
+
         checkTrafficOnVpn();
     }
 
     public void testAppAllowed() throws Exception {
         if (!supportedHardware()) return;
 
+        FileDescriptor fd = openSocketFdInOtherApp(TEST_HOST, 80, TIMEOUT_MS);
+
+        String allowedApps = mRemoteSocketFactoryClient.getPackageName() + "," + mPackageName;
         startVpn(new String[] {"192.0.2.2/32", "2001:db8:1:2::ffe/128"},
                  new String[] {"192.0.2.0/24", "2001:db8::/32"},
-                 mPackageName, "");
+                 allowedApps, "");
+
+        assertSocketClosed(fd, TEST_HOST);
 
         checkTrafficOnVpn();
     }
@@ -479,9 +555,16 @@
     public void testAppDisallowed() throws Exception {
         if (!supportedHardware()) return;
 
+        FileDescriptor localFd = openSocketFd(TEST_HOST, 80, TIMEOUT_MS);
+        FileDescriptor remoteFd = openSocketFdInOtherApp(TEST_HOST, 80, TIMEOUT_MS);
+
+        String disallowedApps = mRemoteSocketFactoryClient.getPackageName() + "," + mPackageName;
         startVpn(new String[] {"192.0.2.2/32", "2001:db8:1:2::ffe/128"},
                  new String[] {"192.0.2.0/24", "2001:db8::/32"},
-                 "", mPackageName);
+                 "", disallowedApps);
+
+        assertSocketStillOpen(localFd, TEST_HOST);
+        assertSocketStillOpen(remoteFd, TEST_HOST);
 
         checkNoTrafficOnVpn();
     }
diff --git a/tests/cts/hostside/app2/Android.mk b/tests/cts/hostside/app2/Android.mk
new file mode 100644
index 0000000..706455d
--- /dev/null
+++ b/tests/cts/hostside/app2/Android.mk
@@ -0,0 +1,35 @@
+#
+# Copyright (C) 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+LOCAL_SDK_VERSION := current
+LOCAL_STATIC_JAVA_LIBRARIES := CtsHostsideNetworkTestsAidl
+
+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..1fa49ba
--- /dev/null
+++ b/tests/cts/hostside/app2/AndroidManifest.xml
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2016 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="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.
+
+         This application also provides a service, RemoteSocketFactoryService, that the test app can
+         use to open sockets to remote hosts as a different user ID.
+    -->
+    <application>
+        <activity android:name=".MyActivity" android:exported="true"/>
+        <service android:name=".MyService" android:exported="true"/>
+        <service android:name=".MyForegroundService" android:exported="true"/>
+        <service android:name=".RemoteSocketFactoryService" 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.GET_RESTRICT_BACKGROUND_STATUS" />
+                <action android:name="com.android.cts.net.hostside.app2.action.CHECK_NETWORK" />
+                <action android:name="com.android.cts.net.hostside.app2.action.SEND_NOTIFICATION" />
+                </intent-filter>
+        </receiver>
+    </application>
+
+</manifest>
diff --git a/tests/cts/hostside/app2/res/drawable/ic_notification.png b/tests/cts/hostside/app2/res/drawable/ic_notification.png
new file mode 100644
index 0000000..6ae570b
--- /dev/null
+++ b/tests/cts/hostside/app2/res/drawable/ic_notification.png
Binary files differ
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..f02f651
--- /dev/null
+++ b/tests/cts/hostside/app2/src/com/android/cts/net/hostside/app2/Common.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.cts.net.hostside.app2;
+
+import android.content.Context;
+import android.content.pm.PackageManager.NameNotFoundException;
+
+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_GET_RESTRICT_BACKGROUND_STATUS =
+            "com.android.cts.net.hostside.app2.action.GET_RESTRICT_BACKGROUND_STATUS";
+    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 ACTION_FINISH_ACTIVITY =
+            "com.android.cts.net.hostside.app2.action.FINISH_ACTIVITY";
+    static final String ACTION_SEND_NOTIFICATION =
+            "com.android.cts.net.hostside.app2.action.SEND_NOTIFICATION";
+    static final String 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 String EXTRA_NOTIFICATION_ID =
+            "com.android.cts.net.hostside.app2.extra.NOTIFICATION_ID";
+
+    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/MyActivity.java b/tests/cts/hostside/app2/src/com/android/cts/net/hostside/app2/MyActivity.java
new file mode 100644
index 0000000..444b696
--- /dev/null
+++ b/tests/cts/hostside/app2/src/com/android/cts/net/hostside/app2/MyActivity.java
@@ -0,0 +1,57 @@
+/*
+ * 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.ACTION_FINISH_ACTIVITY;
+import static com.android.cts.net.hostside.app2.Common.TAG;
+
+import android.app.Activity;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Bundle;
+import android.util.Log;
+
+/**
+ * Activity used to bring process to foreground.
+ */
+public class MyActivity extends Activity {
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        registerReceiver(new BroadcastReceiver() {
+
+            @Override
+            public void onReceive(Context context, Intent intent) {
+                Log.d(TAG, "Finishing MyActivity");
+                MyActivity.this.finish();
+            }}, new IntentFilter(ACTION_FINISH_ACTIVITY));
+    }
+
+    @Override
+    protected void onStart() {
+        super.onStart();
+        Log.d(TAG, "MyActivity.onStart()");
+    }
+
+    @Override
+    protected void onDestroy() {
+        Log.d(TAG, "MyActivity.onDestroy()");
+        super.onDestroy();
+    }
+}
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..60e5de1
--- /dev/null
+++ b/tests/cts/hostside/app2/src/com/android/cts/net/hostside/app2/MyBroadcastReceiver.java
@@ -0,0 +1,251 @@
+/*
+ * 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_GET_RESTRICT_BACKGROUND_STATUS;
+import static com.android.cts.net.hostside.app2.Common.ACTION_RECEIVER_READY;
+import static com.android.cts.net.hostside.app2.Common.ACTION_SEND_NOTIFICATION;
+import static com.android.cts.net.hostside.app2.Common.EXTRA_ACTION;
+import static com.android.cts.net.hostside.app2.Common.EXTRA_NOTIFICATION_ID;
+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.TAG;
+import static com.android.cts.net.hostside.app2.Common.getUid;
+
+import android.app.Notification;
+import android.app.Notification.Action;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+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.os.Bundle;
+import android.util.Log;
+
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * 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_GET_RESTRICT_BACKGROUND_STATUS:
+                getRestrictBackgroundStatus(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;
+            case ACTION_SEND_NOTIFICATION:
+                sendNotification(context, intent);
+                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 getRestrictBackgroundStatus(Context context, Intent intent) {
+        final ConnectivityManager cm = (ConnectivityManager) context
+                .getSystemService(Context.CONNECTIVITY_SERVICE);
+        final int apiStatus = cm.getRestrictBackgroundStatus();
+        Log.d(TAG, "getRestrictBackgroundStatus: returning " + apiStatus);
+        setResultData(Integer.toString(apiStatus));
+    }
+
+    private void checkNetwork(final Context context, Intent intent) {
+        final ConnectivityManager cm = (ConnectivityManager) context
+                .getSystemService(Context.CONNECTIVITY_SERVICE);
+
+        String netStatus = null;
+        try {
+            netStatus = checkNetworkStatus(context, cm);
+        } catch (InterruptedException e) {
+            Log.e(TAG, "Timeout checking network status");
+        }
+        Log.d(TAG, "checkNetwork(): returning " + netStatus);
+        setResultData(netStatus);
+    }
+
+
+    private static final String NETWORK_STATUS_TEMPLATE = "%s|%s|%s|%s|%s";
+    /**
+     * Checks whether the network is available and return a string which can then be send as a
+     * result data for the ordered broadcast.
+     *
+     * <p>
+     * The string has the following format:
+     *
+     * <p><pre><code>
+     * NetinfoState|NetinfoDetailedState|RealConnectionCheck|RealConnectionCheckDetails|Netinfo
+     * </code></pre>
+     *
+     * <p>Where:
+     *
+     * <ul>
+     * <li>{@code NetinfoState}: enum value of {@link NetworkInfo.State}.
+     * <li>{@code NetinfoDetailedState}: enum value of {@link NetworkInfo.DetailedState}.
+     * <li>{@code RealConnectionCheck}: boolean value of a real connection check (i.e., an attempt
+     *     to access an external website.
+     * <li>{@code RealConnectionCheckDetails}: if HTTP output core or exception string of the real
+     *     connection attempt
+     * <li>{@code Netinfo}: string representation of the {@link NetworkInfo}.
+     * </ul>
+     *
+     * For example, if the connection was established fine, the result would be something like:
+     * <p><pre><code>
+     * CONNECTED|CONNECTED|true|200|[type: WIFI[], state: CONNECTED/CONNECTED, reason: ...]
+     * </code></pre>
+     *
+     */
+    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);
+                boolean checkStatus = false;
+                String checkDetails = "N/A";
+                try {
+                    final URL url = new URL(address);
+                    final HttpURLConnection conn = (HttpURLConnection) url.openConnection();
+                    conn.setReadTimeout(NETWORK_TIMEOUT_MS);
+                    conn.setConnectTimeout(NETWORK_TIMEOUT_MS / 2);
+                    conn.setRequestMethod("GET");
+                    conn.setDoInput(true);
+                    conn.connect();
+                    final int response = conn.getResponseCode();
+                    checkStatus = true;
+                    checkDetails = "HTTP response for " + address + ": " + response;
+                } catch (Exception e) {
+                    checkStatus = false;
+                    checkDetails = "Exception getting " + address + ": " + e;
+                }
+                Log.d(TAG, checkDetails);
+                final String status = String.format(NETWORK_STATUS_TEMPLATE,
+                        networkInfo.getState().name(), networkInfo.getDetailedState().name(),
+                        Boolean.toString(checkStatus), checkDetails, networkInfo);
+                Log.d(TAG, "Offering " + status);
+                result.offer(status);
+            }
+        }, 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));
+    }
+
+    /**
+     * Sends a system notification containing actions with pending intents to launch the app's
+     * main activitiy or service.
+     */
+    private void sendNotification(Context context, Intent intent) {
+        final int notificationId = intent.getIntExtra(EXTRA_NOTIFICATION_ID, -1);
+        final Intent serviceIntent = new Intent(context, MyService.class);
+        final PendingIntent pendingIntent = PendingIntent.getService(context, 0, serviceIntent, 0);
+        final Bundle badBundle = new Bundle();
+        badBundle.putCharSequence("parcelable", "I am not");
+        final Action action = new Action.Builder(
+                R.drawable.ic_notification, "ACTION", pendingIntent)
+                .addExtras(badBundle)
+                .build();
+
+        final Notification notification = new Notification.Builder(context)
+                .setSmallIcon(R.drawable.ic_notification)
+                .setContentTitle("Light, Cameras...")
+                .setContentIntent(pendingIntent)
+                .addAction(action)
+                .build();
+        ((NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE))
+            .notify(notificationId, notification);
+    }
+}
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..b88c45d
--- /dev/null
+++ b/tests/cts/hostside/app2/src/com/android/cts/net/hostside/app2/MyForegroundService.java
@@ -0,0 +1,58 @@
+/*
+ * 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 {
+
+    private static final int FLAG_START_FOREGROUND = 1;
+    private static final int FLAG_STOP_FOREGROUND = 2;
+
+    @Override
+    public IBinder onBind(Intent intent) {
+        return null;
+    }
+
+    @Override
+    public int onStartCommand(Intent intent, int flags, int startId) {
+        Log.v(TAG, "MyForegroundService.onStartCommand(): " + intent);
+        switch (intent.getFlags()) {
+            case FLAG_START_FOREGROUND:
+                Log.d(TAG, "Starting foreground");
+                startForeground(42, new Notification.Builder(this)
+                        .setSmallIcon(R.drawable.ic_dialog_alert) // any icon is fine
+                        .build());
+                break;
+            case FLAG_STOP_FOREGROUND:
+                Log.d(TAG, "Stopping foreground");
+                stopForeground(true);
+                break;
+            default:
+                Log.wtf(TAG, "Invalid flag on intent " + intent);
+        }
+        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/app2/src/com/android/cts/net/hostside/app2/RemoteSocketFactoryService.java b/tests/cts/hostside/app2/src/com/android/cts/net/hostside/app2/RemoteSocketFactoryService.java
new file mode 100644
index 0000000..b1b7d77
--- /dev/null
+++ b/tests/cts/hostside/app2/src/com/android/cts/net/hostside/app2/RemoteSocketFactoryService.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.net.hostside.app2;
+
+import android.app.Service;
+import android.content.Context;
+import android.content.Intent;
+import android.os.IBinder;
+import android.os.ParcelFileDescriptor;
+import android.os.Process;
+import android.util.Log;
+
+import com.android.cts.net.hostside.IRemoteSocketFactory;
+
+import java.net.Socket;
+
+
+public class RemoteSocketFactoryService extends Service {
+
+    private static final String TAG = RemoteSocketFactoryService.class.getSimpleName();
+
+    private IRemoteSocketFactory.Stub mBinder = new IRemoteSocketFactory.Stub() {
+        @Override
+        public ParcelFileDescriptor openSocketFd(String host, int port, int timeoutMs) {
+            try {
+                Socket s = new Socket(host, port);
+                s.setSoTimeout(timeoutMs);
+                return ParcelFileDescriptor.fromSocket(s);
+            } catch (Exception e) {
+                throw new IllegalArgumentException(e);
+            }
+        }
+
+        @Override
+        public String getPackageName() {
+            return RemoteSocketFactoryService.this.getPackageName();
+        }
+
+        @Override
+        public int getUid() {
+            return Process.myUid();
+        }
+    };
+
+    @Override
+    public IBinder onBind(Intent intent) {
+        return mBinder;
+    }
+}
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..e96537c
--- /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";
+    protected static final String TEST_APP2_PKG = "com.android.cts.net.hostside.app2";
+    protected static final String TEST_APP2_APK = "CtsHostsideNetworkTestsApp2.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);
+
+        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..7d5f817
--- /dev/null
+++ b/tests/cts/hostside/src/com/android/cts/net/HostsideRestrictBackgroundNetworkTests.java
@@ -0,0 +1,301 @@
+/*
+ * 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 {
+
+    @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);
+    }
+
+    /**************************
+     * Data Saver Mode tests. *
+     **************************/
+
+    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_APP2_PKG);
+
+        // Make sure whitelist is revoked when package is removed
+        addRestrictBackgroundWhitelist(oldUid);
+
+        uninstallPackage(TEST_APP2_PKG, true);
+        assertPackageUninstalled(TEST_APP2_PKG);
+        assertRestrictBackgroundWhitelist(oldUid, false);
+
+        installPackage(TEST_APP2_APK);
+        final int newUid = getUid(TEST_APP2_PKG);
+        assertRestrictBackgroundWhitelist(oldUid, false);
+        assertRestrictBackgroundWhitelist(newUid, false);
+    }
+
+    public void testDataSaverMode_requiredWhitelistedPackages() throws Exception {
+        runDeviceTests(TEST_PKG, TEST_PKG + ".DataSaverModeTest",
+                "testGetRestrictBackgroundStatus_requiredWhitelistedPackages");
+    }
+
+    /*****************************
+     * Battery Saver Mode tests. *
+     *****************************/
+
+    public void testBatterySaverModeMetered_disabled() throws Exception {
+        runDeviceTests(TEST_PKG, TEST_PKG + ".BatterySaverModeMeteredTest",
+                "testBackgroundNetworkAccess_disabled");
+    }
+
+    public void testBatterySaverModeMetered_whitelisted() throws Exception {
+        runDeviceTests(TEST_PKG, TEST_PKG + ".BatterySaverModeMeteredTest",
+                "testBackgroundNetworkAccess_whitelisted");
+    }
+
+    public void testBatterySaverModeMetered_enabled() throws Exception {
+        runDeviceTests(TEST_PKG, TEST_PKG + ".BatterySaverModeMeteredTest",
+                "testBackgroundNetworkAccess_enabled");
+    }
+
+    public void testBatterySaverMode_reinstall() throws Exception {
+        if (!isDozeModeEnabled()) {
+            Log.w(TAG, "testBatterySaverMode_reinstall() skipped because device does not support "
+                    + "Doze Mode");
+            return;
+        }
+
+        addPowerSaveModeWhitelist(TEST_APP2_PKG);
+
+        uninstallPackage(TEST_APP2_PKG, true);
+        assertPackageUninstalled(TEST_APP2_PKG);
+        assertPowerSaveModeWhitelist(TEST_APP2_PKG, false);
+
+        installPackage(TEST_APP2_APK);
+        assertPowerSaveModeWhitelist(TEST_APP2_PKG, false);
+    }
+
+    public void testBatterySaverModeNonMetered_disabled() throws Exception {
+        runDeviceTests(TEST_PKG, TEST_PKG + ".BatterySaverModeNonMeteredTest",
+                "testBackgroundNetworkAccess_disabled");
+    }
+
+    public void testBatterySaverModeNonMetered_whitelisted() throws Exception {
+        runDeviceTests(TEST_PKG, TEST_PKG + ".BatterySaverModeNonMeteredTest",
+                "testBackgroundNetworkAccess_whitelisted");
+    }
+
+    public void testBatterySaverModeNonMetered_enabled() throws Exception {
+        runDeviceTests(TEST_PKG, TEST_PKG + ".BatterySaverModeNonMeteredTest",
+                "testBackgroundNetworkAccess_enabled");
+    }
+
+    /*******************
+     * App idle tests. *
+     *******************/
+
+    public void testAppIdleMetered_disabled() throws Exception {
+        runDeviceTests(TEST_PKG, TEST_PKG + ".AppIdleMeteredTest",
+                "testBackgroundNetworkAccess_disabled");
+    }
+
+    public void testAppIdleMetered_whitelisted() throws Exception {
+        runDeviceTests(TEST_PKG, TEST_PKG + ".AppIdleMeteredTest",
+                "testBackgroundNetworkAccess_whitelisted");
+    }
+
+    public void testAppIdleMetered_enabled() throws Exception {
+        runDeviceTests(TEST_PKG, TEST_PKG + ".AppIdleMeteredTest",
+                "testBackgroundNetworkAccess_enabled");
+    }
+
+    // TODO: currently power-save mode and idle uses the same whitelist, so this test would be
+    // redundant (as it would be testing the same as testBatterySaverMode_reinstall())
+    //    public void testAppIdle_reinstall() throws Exception {
+    //    }
+
+    public void testAppIdleNonMetered_disabled() throws Exception {
+        runDeviceTests(TEST_PKG, TEST_PKG + ".AppIdleNonMeteredTest",
+                "testBackgroundNetworkAccess_disabled");
+    }
+
+    public void testAppIdleNonMetered_whitelisted() throws Exception {
+        runDeviceTests(TEST_PKG, TEST_PKG + ".AppIdleNonMeteredTest",
+                "testBackgroundNetworkAccess_whitelisted");
+    }
+
+    public void testAppIdleNonMetered_enabled() throws Exception {
+        runDeviceTests(TEST_PKG, TEST_PKG + ".AppIdleNonMeteredTest",
+                "testBackgroundNetworkAccess_enabled");
+    }
+
+    /********************
+     * Doze Mode tests. *
+     ********************/
+
+    public void testDozeModeMetered_disabled() throws Exception {
+        runDeviceTests(TEST_PKG, TEST_PKG + ".DozeModeMeteredTest",
+                "testBackgroundNetworkAccess_disabled");
+    }
+
+    public void testDozeModeMetered_whitelisted() throws Exception {
+        runDeviceTests(TEST_PKG, TEST_PKG + ".DozeModeMeteredTest",
+                "testBackgroundNetworkAccess_whitelisted");
+    }
+
+    public void testDozeModeMetered_enabled() throws Exception {
+        runDeviceTests(TEST_PKG, TEST_PKG + ".DozeModeMeteredTest",
+                "testBackgroundNetworkAccess_enabled");
+    }
+
+    public void testDozeModeMetered_enabledButWhitelistedOnNotificationAction() throws Exception {
+        runDeviceTests(TEST_PKG, TEST_PKG + ".DozeModeMeteredTest",
+                "testBackgroundNetworkAccess_enabledButWhitelistedOnNotificationAction");
+    }
+
+    // TODO: currently power-save mode and idle uses the same whitelist, so this test would be
+    // redundant (as it would be testing the same as testBatterySaverMode_reinstall())
+    //    public void testDozeMode_reinstall() throws Exception {
+    //    }
+
+    public void testDozeModeNonMetered_disabled() throws Exception {
+        runDeviceTests(TEST_PKG, TEST_PKG + ".DozeModeNonMeteredTest",
+                "testBackgroundNetworkAccess_disabled");
+    }
+
+    public void testDozeModeNonMetered_whitelisted() throws Exception {
+        runDeviceTests(TEST_PKG, TEST_PKG + ".DozeModeNonMeteredTest",
+                "testBackgroundNetworkAccess_whitelisted");
+    }
+
+    public void testDozeModeNonMetered_enabled() throws Exception {
+        runDeviceTests(TEST_PKG, TEST_PKG + ".DozeModeNonMeteredTest",
+                "testBackgroundNetworkAccess_enabled");
+    }
+
+    public void testDozeModeNonMetered_enabledButWhitelistedOnNotificationAction()
+            throws Exception {
+        runDeviceTests(TEST_PKG, TEST_PKG + ".DozeModeNonMeteredTest",
+                "testBackgroundNetworkAccess_enabledButWhitelistedOnNotificationAction");
+    }
+
+    /**********************
+     * Mixed modes tests. *
+     **********************/
+
+    public void testDataAndBatterySaverModes_meteredNetwork() throws Exception {
+        runDeviceTests(TEST_PKG, TEST_PKG + ".MixedModesTest",
+                "testDataAndBatterySaverModes_meteredNetwork");
+    }
+
+    public void testDataAndBatterySaverModes_nonMeteredNetwork() throws Exception {
+        runDeviceTests(TEST_PKG, TEST_PKG + ".MixedModesTest",
+                "testDataAndBatterySaverModes_nonMeteredNetwork");
+    }
+
+    /*******************
+     * Helper methods. *
+     *******************/
+
+    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");
+    }
+
+    protected void addRestrictBackgroundWhitelist(int uid) throws Exception {
+        runCommand("cmd netpolicy add restrict-background-whitelist " + uid);
+        assertRestrictBackgroundWhitelist(uid, true);
+    }
+
+    private 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
+        runCommand("dumpsys deviceidle whitelist +" + packageName);
+        assertPowerSaveModeWhitelist(packageName, true); // Sanity check
+    }
+
+    protected boolean isDozeModeEnabled() throws Exception {
+        final String result = runCommand("cmd deviceidle enabled deep").trim();
+        return result.equals("1");
+    }
+}
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..69b07af
--- /dev/null
+++ b/tests/cts/hostside/src/com/android/cts/net/HostsideVpnTests.java
@@ -0,0 +1,47 @@
+/*
+ * 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 {
+
+    @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 testDefault() throws Exception {
+        runDeviceTests(TEST_PKG, TEST_PKG + ".VpnTest", "testDefault");
+    }
+
+    public void testAppAllowed() throws Exception {
+        runDeviceTests(TEST_PKG, TEST_PKG + ".VpnTest", "testAppAllowed");
+    }
+
+    public void testAppDisallowed() throws Exception {
+        runDeviceTests(TEST_PKG, TEST_PKG + ".VpnTest", "testAppDisallowed");
+    }
+}
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 001e294..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" />
@@ -28,14 +28,21 @@
     <uses-permission android:name="android.permission.RECORD_AUDIO" />
     <uses-permission android:name="android.permission.WAKE_LOCK" />
     <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+    <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
 
     <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..389b926
--- /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.ApkInstaller">
+        <option name="cleanup-apks" value="true" />
+        <option name="test-file-name" value="CtsNetTestCases.apk" />
+        <option name="test-file-name" value="CtsNetTestAppForApi23.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/appForApi23/Android.mk b/tests/cts/net/appForApi23/Android.mk
new file mode 100644
index 0000000..f0d3535
--- /dev/null
+++ b/tests/cts/net/appForApi23/Android.mk
@@ -0,0 +1,38 @@
+# Copyright (C) 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+# don't include this package in any target
+LOCAL_MODULE_TAGS := optional
+# and when built explicitly put it in the data partition
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+
+# Include both the 32 and 64 bit versions
+LOCAL_MULTILIB := both
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_PACKAGE_NAME := CtsNetTestAppForApi23
+
+LOCAL_SDK_VERSION := 23
+
+# 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/appForApi23/AndroidManifest.xml b/tests/cts/net/appForApi23/AndroidManifest.xml
new file mode 100644
index 0000000..ed4cedb
--- /dev/null
+++ b/tests/cts/net/appForApi23/AndroidManifest.xml
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="android.net.cts.appForApi23">
+
+    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
+    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
+    <uses-permission android:name="android.permission.INTERNET" />
+
+    <application>
+        <receiver android:name=".ConnectivityReceiver">
+            <intent-filter>
+                <action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
+            </intent-filter>
+            <intent-filter>
+                <action android:name="android.net.cts.appForApi23.getWifiConnectivityActionCount" />
+            </intent-filter>
+        </receiver>
+
+        <activity android:name=".ConnectivityListeningActivity"
+                  android:label="ConnectivityListeningActivity"
+                  android:exported="true">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
+        </activity>
+
+    </application>
+
+</manifest>
+
diff --git a/tests/cts/net/appForApi23/src/android/net/cts/appForApi23/ConnectivityListeningActivity.java b/tests/cts/net/appForApi23/src/android/net/cts/appForApi23/ConnectivityListeningActivity.java
new file mode 100644
index 0000000..24fb68e8
--- /dev/null
+++ b/tests/cts/net/appForApi23/src/android/net/cts/appForApi23/ConnectivityListeningActivity.java
@@ -0,0 +1,22 @@
+/*
+ * 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.appForApi23;
+
+import android.app.Activity;
+
+// Stub activity used to start the app
+public class ConnectivityListeningActivity extends Activity {
+}
\ No newline at end of file
diff --git a/tests/cts/net/appForApi23/src/android/net/cts/appForApi23/ConnectivityReceiver.java b/tests/cts/net/appForApi23/src/android/net/cts/appForApi23/ConnectivityReceiver.java
new file mode 100644
index 0000000..8039a4f
--- /dev/null
+++ b/tests/cts/net/appForApi23/src/android/net/cts/appForApi23/ConnectivityReceiver.java
@@ -0,0 +1,41 @@
+/*
+ * 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.appForApi23;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.net.ConnectivityManager;
+
+public class ConnectivityReceiver extends BroadcastReceiver {
+    public static String GET_WIFI_CONNECTIVITY_ACTION_COUNT =
+            "android.net.cts.appForApi23.getWifiConnectivityActionCount";
+
+    private static int sWifiConnectivityActionCount = 0;
+
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        if (ConnectivityManager.CONNECTIVITY_ACTION.equals(intent.getAction())) {
+            int networkType = intent.getIntExtra(ConnectivityManager.EXTRA_NETWORK_TYPE, 0);
+            if (networkType == ConnectivityManager.TYPE_WIFI) {
+                sWifiConnectivityActionCount++;
+            }
+        }
+        if (GET_WIFI_CONNECTIVITY_ACTION_COUNT.equals(intent.getAction())) {
+            setResultCode(sWifiConnectivityActionCount);
+        }
+    }
+}
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/native/qtaguid/Android.mk b/tests/cts/net/native/qtaguid/Android.mk
index 4a09906..4f5bf9f 100644
--- a/tests/cts/net/native/qtaguid/Android.mk
+++ b/tests/cts/net/native/qtaguid/Android.mk
@@ -16,7 +16,7 @@
 
 LOCAL_PATH:= $(call my-dir)
 
-test_executable := NativeNetTest
+test_executable := CtsNativeNetTestCases
 list_executable := $(test_executable)_list
 
 include $(CLEAR_VARS)
@@ -46,6 +46,9 @@
 
 LOCAL_CTS_TEST_PACKAGE := android.net.native
 
+# Tag this module as a cts test artifact
+LOCAL_COMPATIBILITY_SUITE := cts
+
 LOCAL_CFLAGS := -Werror -Wall
 
 include $(BUILD_CTS_EXECUTABLE)
diff --git a/tests/cts/net/native/qtaguid/AndroidTest.xml b/tests/cts/net/native/qtaguid/AndroidTest.xml
new file mode 100644
index 0000000..6f4e8f5
--- /dev/null
+++ b/tests/cts/net/native/qtaguid/AndroidTest.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2017 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<configuration description="Config for CTS Native Network xt_qtaguid test cases">
+    <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
+        <option name="cleanup" value="true" />
+        <option name="push" value="CtsNativeNetTestCases->/data/local/tmp/CtsNativeNetTestCases" />
+        <option name="append-bitness" value="true" />
+    </target_preparer>
+    <test class="com.android.tradefed.testtype.GTest" >
+        <option name="native-test-device-path" value="/data/local/tmp" />
+        <option name="module-name" value="CtsNativeNetTestCases" />
+        <option name="runtime-hint" value="1m" />
+    </test>
+</configuration>
diff --git a/tests/cts/net/src/android/net/cts/AirplaneModeTest.java b/tests/cts/net/src/android/net/cts/AirplaneModeTest.java
new file mode 100644
index 0000000..0a3146c
--- /dev/null
+++ b/tests/cts/net/src/android/net/cts/AirplaneModeTest.java
@@ -0,0 +1,85 @@
+/*
+ * 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.ContentResolver;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.provider.Settings;
+import android.test.AndroidTestCase;
+import android.util.Log;
+
+import java.lang.Thread;
+
+public class AirplaneModeTest extends AndroidTestCase {
+    private static final String TAG = "AirplaneModeTest";
+    private static final String FEATURE_BLUETOOTH = "android.hardware.bluetooth";
+    private static final String FEATURE_WIFI = "android.hardware.wifi";
+    private static final int TIMEOUT_MS = 10 * 1000;
+    private boolean mHasFeature;
+    private Context mContext;
+    private ContentResolver resolver;
+
+    public void setup() {
+        mContext= getContext();
+        resolver = mContext.getContentResolver();
+        mHasFeature = (mContext.getPackageManager().hasSystemFeature(FEATURE_BLUETOOTH)
+                       || mContext.getPackageManager().hasSystemFeature(FEATURE_WIFI));
+    }
+
+    public void testAirplaneMode() {
+        setup();
+        if (!mHasFeature) {
+            Log.i(TAG, "The device doesn't support network bluetooth or wifi feature");
+            return;
+        }
+
+        for (int testCount = 0; testCount < 2; testCount++) {
+            if (!doOneTest()) {
+                fail("Airplane mode failed to change in " + TIMEOUT_MS + "msec");
+                return;
+            }
+        }
+    }
+
+    private boolean doOneTest() {
+        boolean airplaneModeOn = isAirplaneModeOn();
+        setAirplaneModeOn(!airplaneModeOn);
+
+        try {
+            Thread.sleep(TIMEOUT_MS);
+        } catch (InterruptedException e) {
+            Log.e(TAG, "Sleep time interrupted.", e);
+        }
+
+        if (airplaneModeOn == isAirplaneModeOn()) {
+            return false;
+        }
+        return true;
+    }
+
+    private void setAirplaneModeOn(boolean enabling) {
+        // Change the system setting for airplane mode
+        Settings.Global.putInt(resolver, Settings.Global.AIRPLANE_MODE_ON, enabling ? 1 : 0);
+    }
+
+    private boolean isAirplaneModeOn() {
+        // Read the system setting for airplane mode
+        return Settings.Global.getInt(mContext.getContentResolver(),
+                                      Settings.Global.AIRPLANE_MODE_ON, 0) != 0;
+    }
+}
diff --git a/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java b/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
index 9a99c22..185ebfa 100644
--- a/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
+++ b/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
@@ -21,6 +21,7 @@
 
 import android.app.PendingIntent;
 import android.content.BroadcastReceiver;
+import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
@@ -35,18 +36,22 @@
 import android.net.NetworkInfo.State;
 import android.net.NetworkRequest;
 import android.net.wifi.WifiManager;
+import android.os.SystemProperties;
+import android.system.Os;
+import android.system.OsConstants;
 import android.test.AndroidTestCase;
 import android.util.Log;
-import android.os.SystemProperties;
 
 import com.android.internal.telephony.PhoneConstants;
 
-import java.util.ArrayList;
+import java.io.InputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.net.Socket;
+import java.net.InetSocketAddress;
 import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
 import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.LinkedBlockingQueue;
 import java.util.concurrent.TimeUnit;
 
 public class ConnectivityManagerTest extends AndroidTestCase {
@@ -57,15 +62,29 @@
 
     public static final int TYPE_MOBILE = ConnectivityManager.TYPE_MOBILE;
     public static final int TYPE_WIFI = ConnectivityManager.TYPE_WIFI;
+
     private static final int HOST_ADDRESS = 0x7f000001;// represent ip 127.0.0.1
+    private static final String TEST_HOST = "connectivitycheck.gstatic.com";
+    private static final int SOCKET_TIMEOUT_MS = 2000;
+    private static final int SEND_BROADCAST_TIMEOUT = 30000;
+    private static final int HTTP_PORT = 80;
+    private static final String HTTP_REQUEST =
+            "GET /generate_204 HTTP/1.0\r\n" +
+            "Host: " + TEST_HOST + "\r\n" +
+            "Connection: keep-alive\r\n\r\n";
 
     // Action sent to ConnectivityActionReceiver when a network callback is sent via PendingIntent.
     private static final String NETWORK_CALLBACK_ACTION =
             "ConnectivityManagerTest.NetworkCallbackAction";
 
+    // Intent string to get the number of wifi CONNECTIVITY_ACTION callbacks the test app has seen
+    public static final String GET_WIFI_CONNECTIVITY_ACTION_COUNT =
+            "android.net.cts.appForApi23.getWifiConnectivityActionCount";
+
     // device could have only one interface: data, wifi.
     private static final int MIN_NUM_NETWORK_TYPES = 1;
 
+    private Context mContext;
     private ConnectivityManager mCm;
     private WifiManager mWifiManager;
     private PackageManager mPackageManager;
@@ -75,13 +94,14 @@
     @Override
     protected void setUp() throws Exception {
         super.setUp();
-        mCm = (ConnectivityManager) getContext().getSystemService(Context.CONNECTIVITY_SERVICE);
-        mWifiManager = (WifiManager) getContext().getSystemService(Context.WIFI_SERVICE);
-        mPackageManager = getContext().getPackageManager();
+        mContext = getContext();
+        mCm = (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
+        mWifiManager = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE);
+        mPackageManager = mContext.getPackageManager();
 
         // Get com.android.internal.R.array.networkAttributes
-        int resId = getContext().getResources().getIdentifier("networkAttributes", "array", "android");
-        String[] naStrings = getContext().getResources().getStringArray(resId);
+        int resId = mContext.getResources().getIdentifier("networkAttributes", "array", "android");
+        String[] naStrings = mContext.getResources().getStringArray(resId);
         //TODO: What is the "correct" way to determine if this is a wifi only device?
         boolean wifiOnly = SystemProperties.getBoolean("ro.radio.noril", false);
         for (String naString : naStrings) {
@@ -249,6 +269,12 @@
         mCm.getBackgroundDataSetting();
     }
 
+    private NetworkRequest makeWifiNetworkRequest() {
+        return new NetworkRequest.Builder()
+                .addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
+                .build();
+    }
+
     /**
      * Exercises both registerNetworkCallback and unregisterNetworkCallback. This checks to
      * see if we get a callback for the TRANSPORT_WIFI transport type being available.
@@ -265,13 +291,14 @@
         }
 
         // We will register for a WIFI network being available or lost.
-        NetworkRequest request = new NetworkRequest.Builder()
-                .addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
-                .build();
-        TestNetworkCallback callback = new TestNetworkCallback();
-        mCm.registerNetworkCallback(request, callback);
+        final TestNetworkCallback callback = new TestNetworkCallback();
+        mCm.registerNetworkCallback(makeWifiNetworkRequest(), callback);
 
-        boolean previousWifiEnabledState = mWifiManager.isWifiEnabled();
+        final TestNetworkCallback defaultTrackingCallback = new TestNetworkCallback();
+        mCm.registerDefaultNetworkCallback(defaultTrackingCallback);
+
+        final boolean previousWifiEnabledState = mWifiManager.isWifiEnabled();
+        Network wifiNetwork = null;
 
         try {
             // Make sure WiFi is connected to an access point to start with.
@@ -282,16 +309,21 @@
             // Now we should expect to get a network callback about availability of the wifi
             // network even if it was already connected as a state-based action when the callback
             // is registered.
-            assertTrue("Did not receive NetworkCallback.onAvailable for TRANSPORT_WIFI",
-                    callback.waitForAvailable());
+            wifiNetwork = callback.waitForAvailable();
+            assertNotNull("Did not receive NetworkCallback.onAvailable for TRANSPORT_WIFI",
+                    wifiNetwork);
+
+            assertNotNull("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();
+                disconnectFromWifi(wifiNetwork);
             }
         }
     }
@@ -322,12 +354,9 @@
                 mContext, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT);
 
         // We will register for a WIFI network being available or lost.
-        NetworkRequest request = new NetworkRequest.Builder()
-                .addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
-                .build();
-        mCm.registerNetworkCallback(request, pendingIntent);
+        mCm.registerNetworkCallback(makeWifiNetworkRequest(), pendingIntent);
 
-        boolean previousWifiEnabledState = mWifiManager.isWifiEnabled();
+        final boolean previousWifiEnabledState = mWifiManager.isWifiEnabled();
 
         try {
             // Make sure WiFi is connected to an access point to start with.
@@ -347,15 +376,105 @@
             pendingIntent.cancel();
             mContext.unregisterReceiver(receiver);
 
-            // Return WiFI to its original enabled/disabled state.
+            // Return WiFi to its original enabled/disabled state.
             if (!previousWifiEnabledState) {
-                disconnectFromWifi();
+                disconnectFromWifi(null);
             }
         }
     }
 
+    /**
+     * Tests reporting of connectivity changed.
+     */
+    public void testConnectivityChanged_manifestRequestOnly_shouldNotReceiveIntent() {
+        if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_WIFI)) {
+            Log.i(TAG, "testConnectivityChanged_manifestRequestOnly_shouldNotReceiveIntent cannot execute unless device supports WiFi");
+            return;
+        }
+        ConnectivityReceiver.prepare();
+
+        toggleWifi();
+
+        // 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());
+    }
+
+    public void testConnectivityChanged_whenRegistered_shouldReceiveIntent() {
+        if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_WIFI)) {
+            Log.i(TAG, "testConnectivityChanged_whenRegistered_shouldReceiveIntent cannot execute unless device supports WiFi");
+            return;
+        }
+        ConnectivityReceiver.prepare();
+        ConnectivityReceiver receiver = new ConnectivityReceiver();
+        IntentFilter filter = new IntentFilter();
+        filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
+        mContext.registerReceiver(receiver, filter);
+
+        toggleWifi();
+        Intent finalIntent = new Intent(ConnectivityReceiver.FINAL_ACTION);
+        finalIntent.setClass(mContext, ConnectivityReceiver.class);
+        mContext.sendBroadcast(finalIntent);
+
+        assertTrue(ConnectivityReceiver.waitForBroadcast());
+    }
+
+    public void testConnectivityChanged_manifestRequestOnlyPreN_shouldReceiveIntent()
+            throws InterruptedException {
+        if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_WIFI)) {
+            Log.i(TAG, "testConnectivityChanged_manifestRequestOnlyPreN_shouldReceiveIntent cannot execute unless device supports WiFi");
+            return;
+        }
+        Intent startIntent = new Intent();
+        startIntent.setComponent(new ComponentName("android.net.cts.appForApi23",
+                "android.net.cts.appForApi23.ConnectivityListeningActivity"));
+        mContext.startActivity(startIntent);
+
+        toggleWifi();
+
+        Intent getConnectivityCount = new Intent(GET_WIFI_CONNECTIVITY_ACTION_COUNT);
+        assertEquals(2, sendOrderedBroadcastAndReturnResultCode(
+                getConnectivityCount, SEND_BROADCAST_TIMEOUT));
+    }
+
+    private int sendOrderedBroadcastAndReturnResultCode(
+            Intent intent, int timeoutMs) throws InterruptedException {
+        final LinkedBlockingQueue<Integer> result = new LinkedBlockingQueue<>(1);
+        mContext.sendOrderedBroadcast(intent, null, new BroadcastReceiver() {
+            @Override
+            public void onReceive(Context context, Intent intent) {
+                result.offer(getResultCode());
+            }
+        }, null, 0, null, null);
+
+        Integer resultCode = result.poll(timeoutMs, TimeUnit.MILLISECONDS);
+        assertNotNull("Timed out (more than " + timeoutMs +
+                " milliseconds) waiting for result code for broadcast", resultCode);
+        return resultCode;
+    }
+
+    // Toggle WiFi twice, leaving it in the state it started in
+    private void toggleWifi() {
+        if (mWifiManager.isWifiEnabled()) {
+            Network wifiNetwork = getWifiNetwork();
+            disconnectFromWifi(wifiNetwork);
+            connectToWifi();
+        } else {
+            connectToWifi();
+            Network wifiNetwork = getWifiNetwork();
+            disconnectFromWifi(wifiNetwork);
+        }
+    }
+
     /** Enable WiFi and wait for it to become connected to a network. */
-    private void connectToWifi() {
+    private Network connectToWifi() {
+        final TestNetworkCallback callback = new TestNetworkCallback();
+        mCm.registerNetworkCallback(makeWifiNetworkRequest(), callback);
+        Network wifiNetwork = null;
+
         ConnectivityActionReceiver receiver = new ConnectivityActionReceiver(
                 ConnectivityManager.TYPE_WIFI, NetworkInfo.State.CONNECTED);
         IntentFilter filter = new IntentFilter();
@@ -365,36 +484,94 @@
         boolean connected = false;
         try {
             assertTrue(mWifiManager.setWifiEnabled(true));
+            // Ensure we get both an onAvailable callback and a CONNECTIVITY_ACTION.
+            wifiNetwork = callback.waitForAvailable();
+            assertNotNull(wifiNetwork);
             connected = receiver.waitForState();
         } catch (InterruptedException ex) {
             fail("connectToWifi was interrupted");
         } finally {
+            mCm.unregisterNetworkCallback(callback);
             mContext.unregisterReceiver(receiver);
         }
 
         assertTrue("Wifi must be configured to connect to an access point for this test.",
                 connected);
+        return wifiNetwork;
+    }
+
+    private Socket getBoundSocket(Network network, String host, int port) throws IOException {
+        InetSocketAddress addr = new InetSocketAddress(host, port);
+        Socket s = network.getSocketFactory().createSocket();
+        try {
+            s.setSoTimeout(SOCKET_TIMEOUT_MS);
+            s.connect(addr, SOCKET_TIMEOUT_MS);
+        } catch (IOException e) {
+            s.close();
+            throw e;
+        }
+        return s;
+    }
+
+    private void testHttpRequest(Socket s) throws IOException {
+        OutputStream out = s.getOutputStream();
+        InputStream in = s.getInputStream();
+
+        final byte[] requestBytes = HTTP_REQUEST.getBytes("UTF-8");
+        byte[] responseBytes = new byte[4096];
+        out.write(requestBytes);
+        in.read(responseBytes);
+        assertTrue(new String(responseBytes, "UTF-8").startsWith("HTTP/1.0 204 No Content\r\n"));
     }
 
     /** Disable WiFi and wait for it to become disconnected from the network. */
-    private void disconnectFromWifi() {
+    private void disconnectFromWifi(Network wifiNetworkToCheck) {
+        final TestNetworkCallback callback = new TestNetworkCallback();
+        mCm.registerNetworkCallback(makeWifiNetworkRequest(), callback);
+        Network lostWifiNetwork = null;
+
         ConnectivityActionReceiver receiver = new ConnectivityActionReceiver(
                 ConnectivityManager.TYPE_WIFI, NetworkInfo.State.DISCONNECTED);
         IntentFilter filter = new IntentFilter();
         filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
         mContext.registerReceiver(receiver, filter);
 
+        // Assert that we can establish a TCP connection on wifi.
+        Socket wifiBoundSocket = null;
+        if (wifiNetworkToCheck != null) {
+            try {
+                wifiBoundSocket = getBoundSocket(wifiNetworkToCheck, TEST_HOST, HTTP_PORT);
+                testHttpRequest(wifiBoundSocket);
+            } catch (IOException e) {
+                fail("HTTP request before wifi disconnected failed with: " + e);
+            }
+        }
+
         boolean disconnected = false;
         try {
             assertTrue(mWifiManager.setWifiEnabled(false));
+            // Ensure we get both an onLost callback and a CONNECTIVITY_ACTION.
+            lostWifiNetwork = callback.waitForLost();
+            assertNotNull(lostWifiNetwork);
             disconnected = receiver.waitForState();
         } catch (InterruptedException ex) {
             fail("disconnectFromWifi was interrupted");
         } finally {
+            mCm.unregisterNetworkCallback(callback);
             mContext.unregisterReceiver(receiver);
         }
 
         assertTrue("Wifi failed to reach DISCONNECTED state.", disconnected);
+
+        // Check that the socket is closed when wifi disconnects.
+        if (wifiBoundSocket != null) {
+            try {
+                testHttpRequest(wifiBoundSocket);
+                fail("HTTP request should not succeed after wifi disconnects");
+            } catch (IOException expected) {
+                assertEquals(Os.strerror(OsConstants.ECONNABORTED), expected.getMessage());
+            }
+        }
     }
 
     /**
@@ -461,15 +638,48 @@
      */
     private static class TestNetworkCallback extends ConnectivityManager.NetworkCallback {
         private final CountDownLatch mAvailableLatch = new CountDownLatch(1);
+        private final CountDownLatch mLostLatch = new CountDownLatch(1);
 
-        public boolean waitForAvailable() throws InterruptedException {
-            return mAvailableLatch.await(30, TimeUnit.SECONDS);
+        public Network currentNetwork;
+        public Network lastLostNetwork;
+
+        public Network waitForAvailable() throws InterruptedException {
+            return mAvailableLatch.await(30, TimeUnit.SECONDS) ? currentNetwork : null;
+        }
+
+        public Network waitForLost() throws InterruptedException {
+            return mLostLatch.await(30, TimeUnit.SECONDS) ? lastLostNetwork : null;
         }
 
         @Override
         public void onAvailable(Network network) {
+            currentNetwork = network;
             mAvailableLatch.countDown();
         }
+
+        @Override
+        public void onLost(Network network) {
+            lastLostNetwork = network;
+            if (network.equals(currentNetwork)) {
+                currentNetwork = null;
+            }
+            mLostLatch.countDown();
+        }
+    }
+
+    private Network getWifiNetwork() {
+        TestNetworkCallback callback = new TestNetworkCallback();
+        mCm.registerNetworkCallback(makeWifiNetworkRequest(), callback);
+        Network network = null;
+        try {
+            network = callback.waitForAvailable();
+        } catch (InterruptedException e) {
+            fail("NetworkCallback wait was interrupted.");
+        } finally {
+            mCm.unregisterNetworkCallback(callback);
+        }
+        assertNotNull("Cannot find Network for wifi. Is wifi connected?", network);
+        return network;
     }
 
     /** Verify restricted networks cannot be requested. */
@@ -486,8 +696,6 @@
         try {
             mCm.requestNetwork(request, callback);
             fail("No exception thrown when restricted network requested.");
-        } catch (SecurityException e) {
-            // Expected.
-        }
+        } catch (SecurityException expected) {}
     }
 }
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/DnsTest.java b/tests/cts/net/src/android/net/cts/DnsTest.java
index fed0a8d..8575c33 100644
--- a/tests/cts/net/src/android/net/cts/DnsTest.java
+++ b/tests/cts/net/src/android/net/cts/DnsTest.java
@@ -62,8 +62,8 @@
         try {
             addrs = InetAddress.getAllByName("www.google.com");
         } catch (UnknownHostException e) {}
-        assertTrue("[RERUN] DNS could not resolve www.gooogle.com. Check internet connection",
-            addrs.length != 0);
+        assertTrue("[RERUN] DNS could not resolve www.google.com. Check internet connection",
+                addrs.length != 0);
         boolean foundV4 = false, foundV6 = false;
         for (InetAddress addr : addrs) {
             if (addr instanceof Inet4Address) foundV4 = true;
@@ -71,11 +71,8 @@
             if (DBG) Log.e(TAG, "www.google.com gave " + addr.toString());
         }
 
-        // assertTrue(foundV4);
-        // assertTrue(foundV6);
-
         // We should have at least one of the addresses to connect!
-        assertTrue(foundV4 || foundV6);
+        assertTrue("www.google.com must have IPv4 and/or IPv6 address", foundV4 || foundV6);
 
         // Skip the rest of the test if the active network for watch is PROXY.
         // TODO: Check NetworkInfo type in addition to type name once ag/601257 is merged.
@@ -85,21 +82,22 @@
             return;
         }
 
+        // Clear test state so we don't get confused with the previous results.
+        addrs = new InetAddress[0];
+        foundV4 = foundV6 = false;
         try {
             addrs = InetAddress.getAllByName("ipv6.google.com");
         } catch (UnknownHostException e) {}
-        assertTrue(addrs.length != 0);
-        foundV4 = false;
-        foundV6 = false;
+        assertTrue("[RERUN] DNS could not resolve ipv6.google.com, check the network supports IPv6",
+                addrs.length != 0);
         for (InetAddress addr : addrs) {
-            if (addr instanceof Inet4Address) foundV4 = true;
-            else if (addr instanceof Inet6Address) foundV6 = true;
+            assertFalse ("[RERUN] ipv6.google.com returned IPv4 address: " + addr.getHostAddress() +
+                    ", check your network's DNS server", addr instanceof Inet4Address);
+            foundV6 |= (addr instanceof Inet6Address);
             if (DBG) Log.e(TAG, "ipv6.google.com gave " + addr.toString());
         }
 
-        assertTrue("[RERUN] ipv6.google.com returned an ipv4 address, check your network's DNS connection.",
-            foundV4 == false);
-        assertTrue(foundV6 == true);
+        assertTrue(foundV6);
 
         assertTrue(testNativeDns());
     }
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/cts/TheaterModeTest.java b/tests/cts/net/src/android/net/cts/TheaterModeTest.java
new file mode 100644
index 0000000..10fca6f
--- /dev/null
+++ b/tests/cts/net/src/android/net/cts/TheaterModeTest.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.cts;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.provider.Settings;
+import android.test.AndroidTestCase;
+import android.util.Log;
+
+import java.lang.Thread;
+
+public class TheaterModeTest extends AndroidTestCase {
+    private static final String TAG = "TheaterModeTest";
+    private static final String FEATURE_BLUETOOTH = "android.hardware.bluetooth";
+    private static final String FEATURE_WIFI = "android.hardware.wifi";
+    private static final int TIMEOUT_MS = 10 * 1000;
+    private boolean mHasFeature;
+    private Context mContext;
+    private ContentResolver resolver;
+
+    public void setup() {
+        mContext= getContext();
+        resolver = mContext.getContentResolver();
+        mHasFeature = (mContext.getPackageManager().hasSystemFeature(FEATURE_BLUETOOTH)
+                       || mContext.getPackageManager().hasSystemFeature(FEATURE_WIFI));
+    }
+
+    public void testTheaterMode() {
+        setup();
+        if (!mHasFeature) {
+            Log.i(TAG, "The device doesn't support network bluetooth or wifi feature");
+            return;
+        }
+
+        for (int testCount = 0; testCount < 2; testCount++) {
+            if (!doOneTest()) {
+                fail("Theater mode failed to change in " + TIMEOUT_MS + "msec");
+                return;
+            }
+        }
+    }
+
+    private boolean doOneTest() {
+        boolean theaterModeOn = isTheaterModeOn();
+
+        setTheaterModeOn(!theaterModeOn);
+        try {
+            Thread.sleep(TIMEOUT_MS);
+        } catch (InterruptedException e) {
+            Log.e(TAG, "Sleep time interrupted.", e);
+        }
+
+        if (theaterModeOn == isTheaterModeOn()) {
+            return false;
+        }
+        return true;
+    }
+
+    private void setTheaterModeOn(boolean enabling) {
+        // Change the system setting for theater mode
+        Settings.Global.putInt(resolver, Settings.Global.THEATER_MODE_ON, enabling ? 1 : 0);
+    }
+
+    private boolean isTheaterModeOn() {
+        // Read the system setting for theater mode
+        return Settings.Global.getInt(mContext.getContentResolver(),
+                                      Settings.Global.THEATER_MODE_ON, 0) != 0;
+    }
+}
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;
+            }
+        };
+    }
+}
+