[automerger skipped] Merge "Merge "Merge "Merge "Merge "Fix expected reverse lookup of Google DNS IP addresses" into nougat-cts-dev am: 3b416dd354" into nougat-mr1-cts-dev am: 50491fb1cc" into oreo-cts-dev am: b6d3940f89" into oreo-mr1-cts-dev am: d93f6d545c" into pie-cts-dev am: f9b246a320 -s ours
am: b54262d012 -s ours
am skip reason: change_id Iee8bfe418bf6003e5c78df77d75f6f9745249267 with SHA1 3a984ca812 is in history

Change-Id: Ib19798ab58a3a6c033706aed4a712fc93a93ed13
diff --git a/tests/cts/hostside/OWNERS b/tests/cts/hostside/OWNERS
new file mode 100644
index 0000000..52c8053
--- /dev/null
+++ b/tests/cts/hostside/OWNERS
@@ -0,0 +1,4 @@
+# Bug component: 61373
+sudheersai@google.com
+lorenzo@google.com
+jchalard@google.com
diff --git a/tests/cts/hostside/aidl/Android.mk b/tests/cts/hostside/aidl/Android.mk
index 85f71c3..20dabc1 100644
--- a/tests/cts/hostside/aidl/Android.mk
+++ b/tests/cts/hostside/aidl/Android.mk
@@ -19,6 +19,7 @@
 LOCAL_SDK_VERSION := current
 LOCAL_SRC_FILES := \
         com/android/cts/net/hostside/IMyService.aidl \
+        com/android/cts/net/hostside/INetworkCallback.aidl \
         com/android/cts/net/hostside/INetworkStateObserver.aidl \
         com/android/cts/net/hostside/IRemoteSocketFactory.aidl
 LOCAL_MODULE := CtsHostsideNetworkTestsAidl
diff --git a/tests/cts/hostside/aidl/com/android/cts/net/hostside/IMyService.aidl b/tests/cts/hostside/aidl/com/android/cts/net/hostside/IMyService.aidl
index 72d1059..a820ae5 100644
--- a/tests/cts/hostside/aidl/com/android/cts/net/hostside/IMyService.aidl
+++ b/tests/cts/hostside/aidl/com/android/cts/net/hostside/IMyService.aidl
@@ -16,10 +16,13 @@
 
 package com.android.cts.net.hostside;
 
+import com.android.cts.net.hostside.INetworkCallback;
+
 interface IMyService {
     void registerBroadcastReceiver();
     int getCounters(String receiverName, String action);
     String checkNetworkStatus();
     String getRestrictBackgroundStatus();
     void sendNotification(int notificationId, String notificationType);
+    void registerNetworkCallback(in INetworkCallback cb);
 }
diff --git a/tests/cts/hostside/aidl/com/android/cts/net/hostside/INetworkCallback.aidl b/tests/cts/hostside/aidl/com/android/cts/net/hostside/INetworkCallback.aidl
new file mode 100644
index 0000000..740ec26
--- /dev/null
+++ b/tests/cts/hostside/aidl/com/android/cts/net/hostside/INetworkCallback.aidl
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2019 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.net.Network;
+
+interface INetworkCallback {
+    void onBlockedStatusChanged(in Network network, boolean blocked);
+    void onAvailable(in Network network);
+    void onLost(in Network network);
+}
diff --git a/tests/cts/hostside/app/Android.mk b/tests/cts/hostside/app/Android.mk
index 6d89e58..11f6bb1 100644
--- a/tests/cts/hostside/app/Android.mk
+++ b/tests/cts/hostside/app/Android.mk
@@ -19,7 +19,8 @@
 include $(CLEAR_VARS)
 
 LOCAL_MODULE_TAGS := tests
-LOCAL_SDK_VERSION := current
+#LOCAL_SDK_VERSION := current
+LOCAL_PRIVATE_PLATFORM_APIS := true
 LOCAL_STATIC_JAVA_LIBRARIES := compatibility-device-util-axt ctstestrunner-axt ub-uiautomator \
         CtsHostsideNetworkTestsAidl
 
diff --git a/tests/cts/hostside/app/AndroidManifest.xml b/tests/cts/hostside/app/AndroidManifest.xml
index ba0e242..f54b5c1 100644
--- a/tests/cts/hostside/app/AndroidManifest.xml
+++ b/tests/cts/hostside/app/AndroidManifest.xml
@@ -23,6 +23,8 @@
     <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
     <uses-permission android:name="android.permission.INTERNET"/>
     <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
+    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
+    <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
     <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
 
     <application>
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
index 0e141c0..7bf7bd4 100644
--- 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
@@ -175,6 +175,25 @@
         assertBackgroundNetworkAccess(true);
     }
 
+    public void testAppIdleNetworkAccess_idleWhitelisted() throws Exception {
+        if (!isSupported()) return;
+
+        setAppIdle(true);
+        assertAppIdle(true);
+        assertBackgroundNetworkAccess(false);
+
+        addAppIdleWhitelist(mUid);
+        assertBackgroundNetworkAccess(true);
+
+        removeAppIdleWhitelist(mUid);
+        assertBackgroundNetworkAccess(false);
+
+        // Make sure whitelisting a random app doesn't affect the tested app.
+        addAppIdleWhitelist(mUid + 1);
+        assertBackgroundNetworkAccess(false);
+        removeAppIdleWhitelist(mUid + 1);
+    }
+
     public void testAppIdle_toast() throws Exception {
         if (!isSupported()) return;
 
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
index 5232372..4e4d1f6 100644
--- 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
@@ -26,10 +26,6 @@
 
 import static com.android.compatibility.common.util.SystemUtil.runShellCommand;
 
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.LinkedBlockingQueue;
-import java.util.concurrent.TimeUnit;
-
 import android.app.ActivityManager;
 import android.app.Instrumentation;
 import android.app.NotificationManager;
@@ -53,6 +49,10 @@
 import android.text.TextUtils;
 import android.util.Log;
 
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.TimeUnit;
+
 /**
  * Superclass for tests related to background network restrictions.
  */
@@ -92,7 +92,8 @@
     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 = 3;
+    private static int PROCESS_STATE_FOREGROUND_SERVICE;
+
     private static final int PROCESS_STATE_TOP = 2;
 
     private static final String KEY_NETWORK_STATE_OBSERVER = TEST_PKG + ".observer";
@@ -131,6 +132,8 @@
     protected void setUp() throws Exception {
         super.setUp();
 
+        PROCESS_STATE_FOREGROUND_SERVICE = (Integer) ActivityManager.class
+                .getDeclaredField("PROCESS_STATE_FOREGROUND_SERVICE").get(null);
         mInstrumentation = getInstrumentation();
         mContext = mInstrumentation.getContext();
         mCm = (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
@@ -370,6 +373,23 @@
     }
 
     /**
+     * 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.
+     */
+    protected boolean isDataSaverSupported() throws Exception {
+        assertMyRestrictBackgroundStatus(RESTRICT_BACKGROUND_STATUS_DISABLED);
+        try {
+            setRestrictBackground(true);
+            return !isMyRestrictBackgroundStatus(RESTRICT_BACKGROUND_STATUS_DISABLED);
+        } finally {
+            setRestrictBackground(false);
+        }
+    }
+
+    /**
      * Returns whether an app state should be considered "background" for restriction purposes.
      */
     protected boolean isBackground(int state) {
@@ -744,6 +764,20 @@
         assertRestrictBackground("restrict-background-blacklist", uid, expected);
     }
 
+    protected void addAppIdleWhitelist(int uid) throws Exception {
+        executeShellCommand("cmd netpolicy add app-idle-whitelist " + uid);
+        assertAppIdleWhitelist(uid, true);
+    }
+
+    protected void removeAppIdleWhitelist(int uid) throws Exception {
+        executeShellCommand("cmd netpolicy remove app-idle-whitelist " + uid);
+        assertAppIdleWhitelist(uid, false);
+    }
+
+    protected void assertAppIdleWhitelist(int uid, boolean expected) throws Exception {
+        assertRestrictBackground("app-idle-whitelist", uid, expected);
+    }
+
     private void assertRestrictBackground(String list, int uid, boolean expected) throws Exception {
         final int maxTries = 5;
         boolean actual = false;
@@ -962,6 +996,10 @@
         fail("app2 receiver is not ready");
     }
 
+    protected void registerNetworkCallback(INetworkCallback cb) throws Exception {
+        mServiceClient.registerNetworkCallback(cb);
+    }
+
     /**
      * Registers a {@link NotificationListenerService} implementation that will execute the
      * notification actions right after the notification is sent.
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
index 599a31c..72563d4 100644
--- 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
@@ -73,23 +73,6 @@
         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;
 
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
index 87f9d77..74875cd 100644
--- 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
@@ -306,4 +306,84 @@
             setBatterySaverMode(false);
         }
     }
+
+    /**
+     * Tests that the app idle whitelist works as expected when doze and appIdle mode are enabled.
+     */
+    public void testDozeAndAppIdle_appIdleWhitelist() throws Exception {
+        if (!isSupported()) {
+            return;
+        }
+
+        setDozeMode(true);
+        setAppIdle(true);
+
+        try {
+            assertBackgroundNetworkAccess(false);
+
+            // UID still shouldn't have access because of Doze.
+            addAppIdleWhitelist(mUid);
+            assertBackgroundNetworkAccess(false);
+
+            removeAppIdleWhitelist(mUid);
+            assertBackgroundNetworkAccess(false);
+        } finally {
+            setAppIdle(false);
+            setDozeMode(false);
+        }
+    }
+
+    public void testAppIdleAndDoze_tempPowerSaveAndAppIdleWhitelists() throws Exception {
+        if (!isSupported()) {
+            return;
+        }
+
+        setDozeMode(true);
+        setAppIdle(true);
+
+        try {
+            assertBackgroundNetworkAccess(false);
+
+            addAppIdleWhitelist(mUid);
+            assertBackgroundNetworkAccess(false);
+
+            addTempPowerSaveModeWhitelist(TEST_APP2_PKG, TEMP_POWERSAVE_WHITELIST_DURATION_MS);
+            assertBackgroundNetworkAccess(true);
+
+            // Wait until the whitelist duration is expired.
+            SystemClock.sleep(TEMP_POWERSAVE_WHITELIST_DURATION_MS);
+            assertBackgroundNetworkAccess(false);
+        } finally {
+            setAppIdle(false);
+            setDozeMode(false);
+            removeAppIdleWhitelist(mUid);
+        }
+    }
+
+    public void testAppIdleAndBatterySaver_tempPowerSaveAndAppIdleWhitelists() throws Exception {
+        if (!isSupported()) {
+            return;
+        }
+
+        setBatterySaverMode(true);
+        setAppIdle(true);
+
+        try {
+            assertBackgroundNetworkAccess(false);
+
+            addAppIdleWhitelist(mUid);
+            assertBackgroundNetworkAccess(false);
+
+            addTempPowerSaveModeWhitelist(TEST_APP2_PKG, TEMP_POWERSAVE_WHITELIST_DURATION_MS);
+            assertBackgroundNetworkAccess(true);
+
+            // Wait until the whitelist duration is expired.
+            SystemClock.sleep(TEMP_POWERSAVE_WHITELIST_DURATION_MS);
+            assertBackgroundNetworkAccess(false);
+        } finally {
+            setAppIdle(false);
+            setBatterySaverMode(false);
+            removeAppIdleWhitelist(mUid);
+        }
+    }
 }
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/MyServiceClient.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/MyServiceClient.java
index e2976c2..3ee7b99 100644
--- a/tests/cts/hostside/app/src/com/android/cts/net/hostside/MyServiceClient.java
+++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/MyServiceClient.java
@@ -26,8 +26,6 @@
 
 import com.android.cts.net.hostside.IMyService;
 
-import java.io.FileDescriptor;
-
 public class MyServiceClient {
     private static final int TIMEOUT_MS = 5000;
     private static final String PACKAGE = MyServiceClient.class.getPackage().getName();
@@ -98,4 +96,8 @@
     public void sendNotification(int notificationId, String notificationType) throws RemoteException {
         mService.sendNotification(notificationId, notificationType);
     }
+
+    public void registerNetworkCallback(INetworkCallback cb) throws RemoteException {
+        mService.registerNetworkCallback(cb);
+    }
 }
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 90a3ce4..7d91574 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
@@ -17,6 +17,7 @@
 package com.android.cts.net.hostside;
 
 import android.content.Intent;
+import android.net.ProxyInfo;
 import android.net.VpnService;
 import android.os.ParcelFileDescriptor;
 import android.content.pm.PackageManager.NameNotFoundException;
@@ -32,6 +33,10 @@
     private static String TAG = "MyVpnService";
     private static int MTU = 1799;
 
+    public static final String ACTION_ESTABLISHED = "com.android.cts.net.hostside.ESTABNLISHED";
+    public static final String EXTRA_ALWAYS_ON = "is-always-on";
+    public static final String EXTRA_LOCKDOWN_ENABLED = "is-lockdown-enabled";
+
     private ParcelFileDescriptor mFd = null;
     private PacketReflector mPacketReflector = null;
 
@@ -113,6 +118,8 @@
             }
         }
 
+        ProxyInfo vpnProxy = intent.getParcelableExtra(packageName + ".httpProxy");
+        builder.setHttpProxy(vpnProxy);
         builder.setMtu(MTU);
         builder.setBlocking(true);
         builder.setSession("MyVpnService");
@@ -126,10 +133,19 @@
         mFd = builder.establish();
         Log.i(TAG, "Established, fd=" + (mFd == null ? "null" : mFd.getFd()));
 
+        broadcastEstablished();
+
         mPacketReflector = new PacketReflector(mFd.getFileDescriptor(), MTU);
         mPacketReflector.start();
     }
 
+    private void broadcastEstablished() {
+        final Intent bcIntent = new Intent(ACTION_ESTABLISHED);
+        bcIntent.putExtra(EXTRA_ALWAYS_ON, isAlwaysOn());
+        bcIntent.putExtra(EXTRA_LOCKDOWN_ENABLED, isLockdownEnabled());
+        sendBroadcast(bcIntent);
+    }
+
     private void stop() {
         if (mPacketReflector != null) {
             mPacketReflector.interrupt();
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/NetworkCallbackTest.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/NetworkCallbackTest.java
new file mode 100644
index 0000000..24dde9d
--- /dev/null
+++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/NetworkCallbackTest.java
@@ -0,0 +1,241 @@
+/*
+ * Copyright (C) 2019 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.net.Network;
+
+import java.util.Objects;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.TimeUnit;
+
+public class NetworkCallbackTest extends AbstractRestrictBackgroundNetworkTestCase {
+
+    private boolean mIsDataSaverSupported;
+    private Network mNetwork;
+    private final TestNetworkCallback mTestNetworkCallback = new TestNetworkCallback();
+
+    enum CallbackState {
+        NONE,
+        AVAILABLE,
+        LOST,
+        BLOCKED_STATUS
+    }
+
+    private static class CallbackInfo {
+        public final CallbackState state;
+        public final Network network;
+        public final Object arg;
+
+        CallbackInfo(CallbackState s, Network n, Object o) {
+            state = s; network = n; arg = o;
+        }
+
+        public String toString() {
+            return String.format("%s (%s) (%s)", state, network, arg);
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (!(o instanceof CallbackInfo)) return false;
+            // Ignore timeMs, since it's unpredictable.
+            final CallbackInfo other = (CallbackInfo) o;
+            return (state == other.state) && Objects.equals(network, other.network)
+                    && Objects.equals(arg, other.arg);
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(state, network, arg);
+        }
+    }
+
+    private class TestNetworkCallback extends INetworkCallback.Stub {
+        private static final int TEST_CALLBACK_TIMEOUT_MS = 200;
+
+        private final LinkedBlockingQueue<CallbackInfo> mCallbacks = new LinkedBlockingQueue<>();
+
+        protected void setLastCallback(CallbackState state, Network network, Object o) {
+            mCallbacks.offer(new CallbackInfo(state, network, o));
+        }
+
+        CallbackInfo nextCallback(int timeoutMs) {
+            CallbackInfo cb = null;
+            try {
+                cb = mCallbacks.poll(timeoutMs, TimeUnit.MILLISECONDS);
+            } catch (InterruptedException e) {
+            }
+            if (cb == null) {
+                fail("Did not receive callback after " + timeoutMs + "ms");
+            }
+            return cb;
+        }
+
+        CallbackInfo expectCallback(CallbackState state, Network expectedNetwork, Object o) {
+            final CallbackInfo expected = new CallbackInfo(state, expectedNetwork, o);
+            final CallbackInfo actual = nextCallback(TEST_CALLBACK_TIMEOUT_MS);
+            assertEquals("Unexpected callback:", expected, actual);
+            return actual;
+        }
+
+        @Override
+        public void onAvailable(Network network) {
+            setLastCallback(CallbackState.AVAILABLE, network, null);
+        }
+
+        @Override
+        public void onLost(Network network) {
+            setLastCallback(CallbackState.LOST, network, null);
+        }
+
+        @Override
+        public void onBlockedStatusChanged(Network network, boolean blocked) {
+            setLastCallback(CallbackState.BLOCKED_STATUS, network, blocked);
+        }
+
+        public void expectLostCallback(Network expectedNetwork) {
+            expectCallback(CallbackState.LOST, expectedNetwork, null);
+        }
+
+        public void expectAvailableCallback(Network expectedNetwork) {
+            expectCallback(CallbackState.AVAILABLE, expectedNetwork, null);
+        }
+
+        public void expectBlockedStatusCallback(Network expectedNetwork, boolean expectBlocked) {
+            expectCallback(CallbackState.BLOCKED_STATUS, expectedNetwork,
+                    expectBlocked);
+        }
+
+        void assertNoCallback() {
+            CallbackInfo cb = null;
+            try {
+                cb = mCallbacks.poll(TEST_CALLBACK_TIMEOUT_MS, TimeUnit.MILLISECONDS);
+            } catch (InterruptedException e) {
+                // Expected.
+            }
+            if (cb != null) {
+                assertNull("Unexpected callback: " + cb, cb);
+            }
+        }
+    }
+
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+
+        mIsDataSaverSupported = isDataSaverSupported();
+
+        mNetwork = mCm.getActiveNetwork();
+
+        // Set initial state.
+        setBatterySaverMode(false);
+        registerBroadcastReceiver();
+
+        if (!mIsDataSaverSupported) return;
+        setRestrictBackground(false);
+        removeRestrictBackgroundWhitelist(mUid);
+        removeRestrictBackgroundBlacklist(mUid);
+        assertRestrictBackgroundChangedReceived(0);
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        super.tearDown();
+
+        if (!mIsDataSaverSupported) return;
+
+        try {
+            resetMeteredNetwork();
+        } finally {
+            setRestrictBackground(false);
+        }
+    }
+
+    public void testOnBlockedStatusChanged_data_saver() throws Exception {
+        if (!mIsDataSaverSupported) return;
+
+        // Prepare metered wifi
+        if (!setMeteredNetwork()) return;
+
+        // Register callback
+        registerNetworkCallback((INetworkCallback.Stub) mTestNetworkCallback);
+        mTestNetworkCallback.expectAvailableCallback(mNetwork);
+        mTestNetworkCallback.expectBlockedStatusCallback(mNetwork, false);
+
+        // Enable restrict background
+        setRestrictBackground(true);
+        assertBackgroundNetworkAccess(false);
+        mTestNetworkCallback.expectBlockedStatusCallback(mNetwork, true);
+
+        // Add to whitelist
+        addRestrictBackgroundWhitelist(mUid);
+        assertBackgroundNetworkAccess(true);
+        mTestNetworkCallback.expectBlockedStatusCallback(mNetwork, false);
+
+        // Remove from whitelist
+        removeRestrictBackgroundWhitelist(mUid);
+        assertBackgroundNetworkAccess(false);
+        mTestNetworkCallback.expectBlockedStatusCallback(mNetwork, true);
+
+        // Set to non-metered network
+        setUnmeteredNetwork();
+        assertBackgroundNetworkAccess(true);
+        mTestNetworkCallback.expectBlockedStatusCallback(mNetwork, false);
+
+        // Disable restrict background, should not trigger callback
+        setRestrictBackground(false);
+        assertBackgroundNetworkAccess(true);
+        mTestNetworkCallback.assertNoCallback();
+    }
+
+
+    public void testOnBlockedStatusChanged_power_saver() throws Exception {
+        // Prepare metered wifi
+        if (!setMeteredNetwork()) return;
+
+        // Register callback
+        registerNetworkCallback((INetworkCallback.Stub) mTestNetworkCallback);
+        mTestNetworkCallback.expectAvailableCallback(mNetwork);
+        mTestNetworkCallback.expectBlockedStatusCallback(mNetwork, false);
+
+        // Enable Power Saver
+        setBatterySaverMode(true);
+        assertBackgroundNetworkAccess(false);
+        mTestNetworkCallback.expectBlockedStatusCallback(mNetwork, true);
+
+        // Disable Power Saver
+        setBatterySaverMode(false);
+        assertBackgroundNetworkAccess(true);
+        mTestNetworkCallback.expectBlockedStatusCallback(mNetwork, false);
+
+        // Set to non-metered network
+        setUnmeteredNetwork();
+        mTestNetworkCallback.assertNoCallback();
+
+        // Enable Power Saver
+        setBatterySaverMode(true);
+        assertBackgroundNetworkAccess(false);
+        mTestNetworkCallback.expectBlockedStatusCallback(mNetwork, true);
+
+        // Disable Power Saver
+        setBatterySaverMode(false);
+        assertBackgroundNetworkAccess(true);
+        mTestNetworkCallback.expectBlockedStatusCallback(mNetwork, false);
+    }
+
+    // TODO: 1. test against VPN lockdown.
+    //       2. test against multiple networks.
+}
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/PacketReflector.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/PacketReflector.java
index a4a2956..124c2c3 100644
--- a/tests/cts/hostside/app/src/com/android/cts/net/hostside/PacketReflector.java
+++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/PacketReflector.java
@@ -16,6 +16,11 @@
 
 package com.android.cts.net.hostside;
 
+import static android.system.OsConstants.ICMP6_ECHO_REPLY;
+import static android.system.OsConstants.ICMP6_ECHO_REQUEST;
+import static android.system.OsConstants.ICMP_ECHO;
+import static android.system.OsConstants.ICMP_ECHOREPLY;
+
 import android.system.ErrnoException;
 import android.system.Os;
 import android.util.Log;
@@ -47,8 +52,6 @@
 
     private static final byte ICMP_ECHO = 8;
     private static final byte ICMP_ECHOREPLY = 0;
-    private static final byte ICMPV6_ECHO_REQUEST = (byte) 128;
-    private static final byte ICMPV6_ECHO_REPLY = (byte) 129;
 
     private static String TAG = "PacketReflector";
 
@@ -125,7 +128,7 @@
 
         byte type = buf[hdrLen];
         if (!(version == 4 && type == ICMP_ECHO) &&
-            !(version == 6 && type == ICMPV6_ECHO_REQUEST)) {
+            !(version == 6 && type == (byte) ICMP6_ECHO_REQUEST)) {
             return;
         }
 
@@ -145,10 +148,18 @@
             return;
         }
 
+        byte replyType = buf[hdrLen];
+        if ((type == ICMP_ECHO && replyType != ICMP_ECHOREPLY)
+                || (type == (byte) ICMP6_ECHO_REQUEST && replyType != (byte) ICMP6_ECHO_REPLY)) {
+            Log.i(TAG, "Received unexpected ICMP reply: original " + type
+                    + ", reply " + replyType);
+            return;
+        }
+
         // Compare the response we got with the original packet.
         // The only thing that should have changed are addresses, type and checksum.
         // Overwrite them with the received bytes and see if the packet is otherwise identical.
-        request[hdrLen] = buf[hdrLen];          // Type.
+        request[hdrLen] = buf[hdrLen];          // Type
         request[hdrLen + 2] = buf[hdrLen + 2];  // Checksum byte 1.
         request[hdrLen + 3] = buf[hdrLen + 3];  // Checksum byte 2.
 
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 bc982ce..17e1347 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
@@ -16,8 +16,10 @@
 
 package com.android.cts.net.hostside;
 
+import static android.os.Process.INVALID_UID;
 import static android.system.OsConstants.*;
 
+import android.content.Context;
 import android.content.Intent;
 import android.content.pm.PackageManager;
 import android.net.ConnectivityManager;
@@ -26,34 +28,31 @@
 import android.net.Network;
 import android.net.NetworkCapabilities;
 import android.net.NetworkRequest;
+import android.net.Proxy;
+import android.net.ProxyInfo;
 import android.net.VpnService;
 import android.os.ParcelFileDescriptor;
 import android.os.Process;
+import android.os.SystemProperties;
 import android.support.test.uiautomator.UiDevice;
 import android.support.test.uiautomator.UiObject;
-import android.support.test.uiautomator.UiObjectNotFoundException;
-import android.support.test.uiautomator.UiScrollable;
 import android.support.test.uiautomator.UiSelector;
 import android.system.ErrnoException;
 import android.system.Os;
+import android.system.OsConstants;
 import android.system.StructPollfd;
 import android.test.InstrumentationTestCase;
 import android.test.MoreAsserts;
 import android.text.TextUtils;
 import android.util.Log;
 
-import com.android.cts.net.hostside.IRemoteSocketFactory;
+import com.android.compatibility.common.util.BlockingBroadcastReceiver;
 
-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;
@@ -61,9 +60,9 @@
 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;
+import java.util.concurrent.TimeUnit;
 
 /**
  * Tests for the VpnService API.
@@ -194,9 +193,8 @@
     }
 
     private void startVpn(
-            String[] addresses, String[] routes,
-            String allowedApplications, String disallowedApplications) throws Exception {
-
+        String[] addresses, String[] routes, String allowedApplications,
+        String disallowedApplications, ProxyInfo proxyInfo) throws Exception {
         prepareVpn();
 
         // Register a callback so we will be notified when our VPN comes up.
@@ -222,7 +220,9 @@
                 .putExtra(mPackageName + ".addresses", TextUtils.join(",", addresses))
                 .putExtra(mPackageName + ".routes", TextUtils.join(",", routes))
                 .putExtra(mPackageName + ".allowedapplications", allowedApplications)
-                .putExtra(mPackageName + ".disallowedapplications", disallowedApplications);
+                .putExtra(mPackageName + ".disallowedapplications", disallowedApplications)
+                .putExtra(mPackageName + ".httpProxy", proxyInfo);
+
         mActivity.startService(intent);
         synchronized (mLock) {
             if (mNetwork == null) {
@@ -353,7 +353,7 @@
         MoreAsserts.assertEquals(data, read);
     }
 
-    private static void checkTcpReflection(String to, String expectedFrom) throws IOException {
+    private void checkTcpReflection(String to, String expectedFrom) throws IOException {
         // Exercise TCP over the VPN by "connecting to ourselves". We open a server socket and a
         // client socket, and connect the client socket to a remote host, with the port of the
         // server socket. The PacketReflector reflects the packets, changing the source addresses
@@ -391,7 +391,8 @@
             // Accept the connection on the server side.
             listen.setSoTimeout(SOCKET_TIMEOUT_MS);
             server = listen.accept();
-
+            checkConnectionOwnerUidTcp(client);
+            checkConnectionOwnerUidTcp(server);
             // Check that the source and peer addresses are as expected.
             assertEquals(expectedFrom, client.getLocalAddress().getHostAddress());
             assertEquals(expectedFrom, server.getLocalAddress().getHostAddress());
@@ -424,7 +425,23 @@
         }
     }
 
-    private static void checkUdpEcho(String to, String expectedFrom) throws IOException {
+    private void checkConnectionOwnerUidUdp(DatagramSocket s, boolean expectSuccess) {
+        final int expectedUid = expectSuccess ? Process.myUid() : INVALID_UID;
+        InetSocketAddress loc = new InetSocketAddress(s.getLocalAddress(), s.getLocalPort());
+        InetSocketAddress rem = new InetSocketAddress(s.getInetAddress(), s.getPort());
+        int uid = mCM.getConnectionOwnerUid(OsConstants.IPPROTO_UDP, loc, rem);
+        assertEquals(expectedUid, uid);
+    }
+
+    private void checkConnectionOwnerUidTcp(Socket s) {
+        final int expectedUid = Process.myUid();
+        InetSocketAddress loc = new InetSocketAddress(s.getLocalAddress(), s.getLocalPort());
+        InetSocketAddress rem = new InetSocketAddress(s.getInetAddress(), s.getPort());
+        int uid = mCM.getConnectionOwnerUid(OsConstants.IPPROTO_TCP, loc, rem);
+        assertEquals(expectedUid, uid);
+    }
+
+    private void checkUdpEcho(String to, String expectedFrom) throws IOException {
         DatagramSocket s;
         InetAddress address = InetAddress.getByName(to);
         if (address instanceof Inet6Address) {  // http://b/18094870
@@ -448,6 +465,7 @@
         try {
             if (expectedFrom != null) {
                 s.send(p);
+                checkConnectionOwnerUidUdp(s, true);
                 s.receive(p);
                 MoreAsserts.assertEquals(data, p.getData());
             } else {
@@ -455,7 +473,9 @@
                     s.send(p);
                     s.receive(p);
                     fail("Received unexpected reply");
-                } catch(IOException expected) {}
+                } catch (IOException expected) {
+                    checkConnectionOwnerUidUdp(s, false);
+                }
             }
         } finally {
             s.close();
@@ -537,16 +557,36 @@
 
     public void testDefault() throws Exception {
         if (!supportedHardware()) return;
+        // If adb TCP port opened, this test may running by adb over network.
+        // All of socket would be destroyed in this test. So this test don't
+        // support adb over network, see b/119382723.
+        if (SystemProperties.getInt("persist.adb.tcp.port", -1) > -1
+                || SystemProperties.getInt("service.adb.tcp.port", -1) > -1) {
+            Log.i(TAG, "adb is running over the network, so skip this test");
+            return;
+        }
+
+        final BlockingBroadcastReceiver receiver = new BlockingBroadcastReceiver(
+                getInstrumentation().getTargetContext(), MyVpnService.ACTION_ESTABLISHED);
+        receiver.register();
 
         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"},
-                 "", "");
+                 "", "", null);
+
+        final Intent intent = receiver.awaitForBroadcast(TimeUnit.MINUTES.toMillis(1));
+        assertNotNull("Failed to receive broadcast from VPN service", intent);
+        assertFalse("Wrong VpnService#isAlwaysOn",
+                intent.getBooleanExtra(MyVpnService.EXTRA_ALWAYS_ON, true));
+        assertFalse("Wrong VpnService#isLockdownEnabled",
+                intent.getBooleanExtra(MyVpnService.EXTRA_LOCKDOWN_ENABLED, true));
 
         assertSocketClosed(fd, TEST_HOST);
 
         checkTrafficOnVpn();
+        receiver.unregisterQuietly();
     }
 
     public void testAppAllowed() throws Exception {
@@ -554,10 +594,11 @@
 
         FileDescriptor fd = openSocketFdInOtherApp(TEST_HOST, 80, TIMEOUT_MS);
 
+        // Shell app must not be put in here or it would kill the ADB-over-network use case
         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"},
-                 allowedApps, "");
+                 allowedApps, "", null);
 
         assertSocketClosed(fd, TEST_HOST);
 
@@ -571,13 +612,188 @@
         FileDescriptor remoteFd = openSocketFdInOtherApp(TEST_HOST, 80, TIMEOUT_MS);
 
         String disallowedApps = mRemoteSocketFactoryClient.getPackageName() + "," + mPackageName;
+        // If adb TCP port opened, this test may running by adb over TCP.
+        // Add com.android.shell appllication into blacklist to exclude adb socket for VPN test,
+        // see b/119382723.
+        // Note: The test don't support running adb over network for root device
+        disallowedApps = disallowedApps + ",com.android.shell";
+        Log.i(TAG, "Append shell app to disallowedApps: " + disallowedApps);
         startVpn(new String[] {"192.0.2.2/32", "2001:db8:1:2::ffe/128"},
                  new String[] {"192.0.2.0/24", "2001:db8::/32"},
-                 "", disallowedApps);
+                 "", disallowedApps, null);
 
         assertSocketStillOpen(localFd, TEST_HOST);
         assertSocketStillOpen(remoteFd, TEST_HOST);
 
         checkNoTrafficOnVpn();
     }
+
+    public void testGetConnectionOwnerUidSecurity() throws Exception {
+        if (!supportedHardware()) return;
+
+        DatagramSocket s;
+        InetAddress address = InetAddress.getByName("localhost");
+        s = new DatagramSocket();
+        s.setSoTimeout(SOCKET_TIMEOUT_MS);
+        s.connect(address, 7);
+        InetSocketAddress loc = new InetSocketAddress(s.getLocalAddress(), s.getLocalPort());
+        InetSocketAddress rem = new InetSocketAddress(s.getInetAddress(), s.getPort());
+        try {
+            int uid = mCM.getConnectionOwnerUid(OsConstants.IPPROTO_TCP, loc, rem);
+            fail("Only an active VPN app may call this API.");
+        } catch (SecurityException expected) {
+            return;
+        }
+    }
+
+    public void testSetProxy() throws  Exception {
+        if (!supportedHardware()) return;
+        ProxyInfo initialProxy = mCM.getDefaultProxy();
+        // Receiver for the proxy change broadcast.
+        BlockingBroadcastReceiver proxyBroadcastReceiver = new ProxyChangeBroadcastReceiver();
+        proxyBroadcastReceiver.register();
+
+        String allowedApps = mPackageName;
+        ProxyInfo testProxyInfo = ProxyInfo.buildDirectProxy("10.0.0.1", 8888);
+        startVpn(new String[] {"192.0.2.2/32", "2001:db8:1:2::ffe/128"},
+                new String[] {"0.0.0.0/0", "::/0"}, allowedApps, "",
+                testProxyInfo);
+
+        // Check that the proxy change broadcast is received
+        try {
+            assertNotNull("No proxy change was broadcast when VPN is connected.",
+                    proxyBroadcastReceiver.awaitForBroadcast());
+        } finally {
+            proxyBroadcastReceiver.unregisterQuietly();
+        }
+
+        // Proxy is set correctly in network and in link properties.
+        assertNetworkHasExpectedProxy(testProxyInfo, mNetwork);
+        assertDefaultProxy(testProxyInfo);
+
+        proxyBroadcastReceiver = new ProxyChangeBroadcastReceiver();
+        proxyBroadcastReceiver.register();
+        stopVpn();
+        try {
+            assertNotNull("No proxy change was broadcast when VPN was disconnected.",
+                    proxyBroadcastReceiver.awaitForBroadcast());
+        } finally {
+            proxyBroadcastReceiver.unregisterQuietly();
+        }
+
+        // After disconnecting from VPN, the proxy settings are the ones of the initial network.
+        assertDefaultProxy(initialProxy);
+    }
+
+    public void testSetProxyDisallowedApps() throws Exception {
+        if (!supportedHardware()) return;
+        ProxyInfo initialProxy = mCM.getDefaultProxy();
+
+        // If adb TCP port opened, this test may running by adb over TCP.
+        // Add com.android.shell appllication into blacklist to exclude adb socket for VPN test,
+        // see b/119382723.
+        // Note: The test don't support running adb over network for root device
+        String disallowedApps = mPackageName + ",com.android.shell";
+        ProxyInfo testProxyInfo = ProxyInfo.buildDirectProxy("10.0.0.1", 8888);
+        startVpn(new String[] {"192.0.2.2/32", "2001:db8:1:2::ffe/128"},
+                new String[] {"0.0.0.0/0", "::/0"}, "", disallowedApps,
+                testProxyInfo);
+
+        // The disallowed app does has the proxy configs of the default network.
+        assertNetworkHasExpectedProxy(initialProxy, mCM.getActiveNetwork());
+        assertDefaultProxy(initialProxy);
+    }
+
+    public void testNoProxy() throws Exception {
+        if (!supportedHardware()) return;
+        ProxyInfo initialProxy = mCM.getDefaultProxy();
+        BlockingBroadcastReceiver proxyBroadcastReceiver = new ProxyChangeBroadcastReceiver();
+        proxyBroadcastReceiver.register();
+        String allowedApps = mPackageName;
+        startVpn(new String[] {"192.0.2.2/32", "2001:db8:1:2::ffe/128"},
+                new String[] {"0.0.0.0/0", "::/0"}, allowedApps, "", null);
+
+        try {
+            assertNotNull("No proxy change was broadcast.",
+                    proxyBroadcastReceiver.awaitForBroadcast());
+        } finally {
+            proxyBroadcastReceiver.unregisterQuietly();
+        }
+
+        // The VPN network has no proxy set.
+        assertNetworkHasExpectedProxy(null, mNetwork);
+
+        proxyBroadcastReceiver = new ProxyChangeBroadcastReceiver();
+        proxyBroadcastReceiver.register();
+        stopVpn();
+        try {
+            assertNotNull("No proxy change was broadcast.",
+                    proxyBroadcastReceiver.awaitForBroadcast());
+        } finally {
+            proxyBroadcastReceiver.unregisterQuietly();
+        }
+        // After disconnecting from VPN, the proxy settings are the ones of the initial network.
+        assertDefaultProxy(initialProxy);
+        assertNetworkHasExpectedProxy(initialProxy, mCM.getActiveNetwork());
+    }
+
+    public void testBindToNetworkWithProxy() throws Exception {
+        if (!supportedHardware()) return;
+        String allowedApps = mPackageName;
+        Network initialNetwork = mCM.getActiveNetwork();
+        ProxyInfo initialProxy = mCM.getDefaultProxy();
+        ProxyInfo testProxyInfo = ProxyInfo.buildDirectProxy("10.0.0.1", 8888);
+        // Receiver for the proxy change broadcast.
+        BlockingBroadcastReceiver proxyBroadcastReceiver = new ProxyChangeBroadcastReceiver();
+        proxyBroadcastReceiver.register();
+        startVpn(new String[] {"192.0.2.2/32", "2001:db8:1:2::ffe/128"},
+                new String[] {"0.0.0.0/0", "::/0"}, allowedApps, "",
+                testProxyInfo);
+
+        assertDefaultProxy(testProxyInfo);
+        mCM.bindProcessToNetwork(initialNetwork);
+        try {
+            assertNotNull("No proxy change was broadcast.",
+                proxyBroadcastReceiver.awaitForBroadcast());
+        } finally {
+            proxyBroadcastReceiver.unregisterQuietly();
+        }
+        assertDefaultProxy(initialProxy);
+    }
+
+    private void assertDefaultProxy(ProxyInfo expected) {
+        assertEquals("Incorrect proxy config.", expected, mCM.getDefaultProxy());
+        String expectedHost = expected == null ? null : expected.getHost();
+        String expectedPort = expected == null ? null : String.valueOf(expected.getPort());
+        assertEquals("Incorrect proxy host system property.", expectedHost,
+            System.getProperty("http.proxyHost"));
+        assertEquals("Incorrect proxy port system property.", expectedPort,
+            System.getProperty("http.proxyPort"));
+    }
+
+    private void assertNetworkHasExpectedProxy(ProxyInfo expected, Network network) {
+        LinkProperties lp = mCM.getLinkProperties(network);
+        assertNotNull("The network link properties object is null.", lp);
+        assertEquals("Incorrect proxy config.", expected, lp.getHttpProxy());
+
+        assertEquals(expected, mCM.getProxyForNetwork(network));
+    }
+
+    class ProxyChangeBroadcastReceiver extends BlockingBroadcastReceiver {
+        private boolean received;
+
+        public ProxyChangeBroadcastReceiver() {
+            super(VpnTest.this.getInstrumentation().getContext(), Proxy.PROXY_CHANGE_ACTION);
+            received = false;
+        }
+
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            if (!received) {
+                // Do not call onReceive() more than once.
+                super.onReceive(context, intent);
+            }
+            received = true;
+        }
+    }
 }
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
index 2496c4a..ec536af 100644
--- 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
@@ -16,6 +16,7 @@
 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;
@@ -26,13 +27,16 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
-import android.content.SharedPreferences;
+import android.net.ConnectivityManager;
+import android.net.Network;
+import android.net.NetworkCapabilities;
+import android.net.NetworkRequest;
 import android.os.IBinder;
-import android.os.Looper;
+import android.os.RemoteException;
 import android.util.Log;
-import android.widget.Toast;
 
 import com.android.cts.net.hostside.IMyService;
+import com.android.cts.net.hostside.INetworkCallback;
 
 /**
  * Service used to dynamically register a broadcast receiver.
@@ -40,7 +44,10 @@
 public class MyService extends Service {
     private static final String NOTIFICATION_CHANNEL_ID = "MyService";
 
+    ConnectivityManager mCm;
+
     private MyBroadcastReceiver mReceiver;
+    private ConnectivityManager.NetworkCallback mNetworkCallback;
 
     // TODO: move MyBroadcast static functions here - they were kept there to make git diff easier.
 
@@ -81,8 +88,67 @@
             MyBroadcastReceiver .sendNotification(getApplicationContext(), NOTIFICATION_CHANNEL_ID,
                     notificationId, notificationType);
         }
+
+        @Override
+        public void registerNetworkCallback(INetworkCallback cb) {
+            if (mNetworkCallback != null) {
+                Log.d(TAG, "unregister previous network callback: " + mNetworkCallback);
+                unregisterNetworkCallback();
+            }
+            Log.d(TAG, "registering network callback");
+
+            mNetworkCallback = new ConnectivityManager.NetworkCallback() {
+                @Override
+                public void onBlockedStatusChanged(Network network, boolean blocked) {
+                    try {
+                        cb.onBlockedStatusChanged(network, blocked);
+                    } catch (RemoteException e) {
+                        Log.d(TAG, "Cannot send onBlockedStatusChanged: " + e);
+                        unregisterNetworkCallback();
+                    }
+                }
+
+                @Override
+                public void onAvailable(Network network) {
+                    try {
+                        cb.onAvailable(network);
+                    } catch (RemoteException e) {
+                        Log.d(TAG, "Cannot send onAvailable: " + e);
+                        unregisterNetworkCallback();
+                    }
+                }
+
+                @Override
+                public void onLost(Network network) {
+                    try {
+                        cb.onLost(network);
+                    } catch (RemoteException e) {
+                        Log.d(TAG, "Cannot send onLost: " + e);
+                        unregisterNetworkCallback();
+                    }
+                }
+            };
+            mCm.registerNetworkCallback(makeWifiNetworkRequest(), mNetworkCallback);
+            try {
+                cb.asBinder().linkToDeath(() -> unregisterNetworkCallback(), 0);
+            } catch (RemoteException e) {
+                unregisterNetworkCallback();
+            }
+        }
       };
 
+    private void unregisterNetworkCallback() {
+        Log.d(TAG, "unregistering network callback");
+        mCm.unregisterNetworkCallback(mNetworkCallback);
+        mNetworkCallback = null;
+    }
+
+    private NetworkRequest makeWifiNetworkRequest() {
+        return new NetworkRequest.Builder()
+                .addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
+                .build();
+    }
+
     @Override
     public IBinder onBind(Intent intent) {
         return mBinder;
@@ -94,6 +160,8 @@
         ((NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE))
                 .createNotificationChannel(new NotificationChannel(NOTIFICATION_CHANNEL_ID,
                         NOTIFICATION_CHANNEL_ID, NotificationManager.IMPORTANCE_DEFAULT));
+        mCm = (ConnectivityManager) getApplicationContext()
+                .getSystemService(Context.CONNECTIVITY_SERVICE);
     }
 
     @Override
diff --git a/tests/cts/hostside/src/com/android/cts/net/HostsideNetworkCallbackTests.java b/tests/cts/hostside/src/com/android/cts/net/HostsideNetworkCallbackTests.java
new file mode 100644
index 0000000..8d6c4ac
--- /dev/null
+++ b/tests/cts/hostside/src/com/android/cts/net/HostsideNetworkCallbackTests.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2019 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 HostsideNetworkCallbackTests 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 testOnBlockedStatusChanged_data_saver() throws Exception {
+        runDeviceTests(TEST_PKG,
+                TEST_PKG + ".NetworkCallbackTest", "testOnBlockedStatusChanged_data_saver");
+    }
+
+    public void testOnBlockedStatusChanged_power_saver() throws Exception {
+        runDeviceTests(TEST_PKG,
+                TEST_PKG + ".NetworkCallbackTest", "testOnBlockedStatusChanged_power_saver");
+    }
+}
+
diff --git a/tests/cts/hostside/src/com/android/cts/net/HostsideRestrictBackgroundNetworkTests.java b/tests/cts/hostside/src/com/android/cts/net/HostsideRestrictBackgroundNetworkTests.java
index fe9d36c..5f5ea43 100644
--- a/tests/cts/hostside/src/com/android/cts/net/HostsideRestrictBackgroundNetworkTests.java
+++ b/tests/cts/hostside/src/com/android/cts/net/HostsideRestrictBackgroundNetworkTests.java
@@ -156,6 +156,11 @@
                 "testBackgroundNetworkAccess_enabled");
     }
 
+    public void testAppIdleMetered_idleWhitelisted() throws Exception {
+        runDeviceTests(TEST_PKG, TEST_PKG + ".AppIdleMeteredTest",
+                "testAppIdleNetworkAccess_idleWhitelisted");
+    }
+
     // 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 {
@@ -181,6 +186,11 @@
                 "testBackgroundNetworkAccess_enabled");
     }
 
+    public void testAppIdleNonMetered_idleWhitelisted() throws Exception {
+        runDeviceTests(TEST_PKG, TEST_PKG + ".AppIdleNonMeteredTest",
+                "testAppIdleNetworkAccess_idleWhitelisted");
+    }
+
     public void testAppIdleNonMetered_whenCharging() throws Exception {
         runDeviceTests(TEST_PKG, TEST_PKG + ".AppIdleNonMeteredTest",
                 "testAppIdleNetworkAccess_whenCharging");
@@ -281,6 +291,21 @@
                 "testAppIdleAndBatterySaver_tempPowerSaveWhitelists");
     }
 
+    public void testDozeAndAppIdle_appIdleWhitelist() throws Exception {
+        runDeviceTests(TEST_PKG, TEST_PKG + ".MixedModesTest",
+                "testDozeAndAppIdle_appIdleWhitelist");
+    }
+
+    public void testAppIdleAndDoze_tempPowerSaveAndAppIdleWhitelists() throws Exception {
+        runDeviceTests(TEST_PKG, TEST_PKG + ".MixedModesTest",
+                "testAppIdleAndDoze_tempPowerSaveAndAppIdleWhitelists");
+    }
+
+    public void testAppIdleAndBatterySaver_tempPowerSaveAndAppIdleWhitelists() throws Exception {
+        runDeviceTests(TEST_PKG, TEST_PKG + ".MixedModesTest",
+                "testAppIdleAndBatterySaver_tempPowerSaveAndAppIdleWhitelists");
+    }
+
     /*******************
      * Helper methods. *
      *******************/
diff --git a/tests/cts/hostside/src/com/android/cts/net/HostsideVpnTests.java b/tests/cts/hostside/src/com/android/cts/net/HostsideVpnTests.java
index 69b07af..e34ee89 100644
--- a/tests/cts/hostside/src/com/android/cts/net/HostsideVpnTests.java
+++ b/tests/cts/hostside/src/com/android/cts/net/HostsideVpnTests.java
@@ -44,4 +44,24 @@
     public void testAppDisallowed() throws Exception {
         runDeviceTests(TEST_PKG, TEST_PKG + ".VpnTest", "testAppDisallowed");
     }
+
+    public void testGetConnectionOwnerUidSecurity() throws Exception {
+        runDeviceTests(TEST_PKG, TEST_PKG + ".VpnTest", "testGetConnectionOwnerUidSecurity");
+    }
+
+    public void testSetProxy() throws Exception {
+        runDeviceTests(TEST_PKG, TEST_PKG + ".VpnTest", "testSetProxy");
+    }
+
+    public void testSetProxyDisallowedApps() throws Exception {
+        runDeviceTests(TEST_PKG, TEST_PKG + ".VpnTest", "testSetProxyDisallowedApps");
+    }
+
+    public void testNoProxy() throws Exception {
+        runDeviceTests(TEST_PKG, TEST_PKG + ".VpnTest", "testNoProxy");
+    }
+
+    public void testBindToNetworkWithProxy() throws Exception {
+        runDeviceTests(TEST_PKG, TEST_PKG + ".VpnTest", "testBindToNetworkWithProxy");
+    }
 }
diff --git a/tests/cts/hostside/src/com/android/cts/net/ProcNetTest.java b/tests/cts/hostside/src/com/android/cts/net/ProcNetTest.java
new file mode 100644
index 0000000..19e61c6
--- /dev/null
+++ b/tests/cts/hostside/src/com/android/cts/net/ProcNetTest.java
@@ -0,0 +1,169 @@
+/*
+ * Copyright (C) 2018 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.security.cts;
+
+import com.android.tradefed.build.IBuildInfo;
+import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.testtype.DeviceTestCase;
+import com.android.tradefed.testtype.IBuildReceiver;
+import com.android.tradefed.testtype.IDeviceTest;
+
+import java.lang.Integer;
+import java.lang.String;
+import java.util.Arrays;
+import java.util.List;
+import java.util.ArrayList;
+
+/**
+ * Host-side tests for values in /proc/net.
+ *
+ * These tests analyze /proc/net to verify that certain networking properties are correct.
+ */
+public class ProcNetTest extends DeviceTestCase implements IBuildReceiver, IDeviceTest {
+    private static final String SPI_TIMEOUT_SYSCTL = "/proc/sys/net/core/xfrm_acq_expires";
+    private static final int MIN_ACQ_EXPIRES = 3600;
+    // Global sysctls. Must be present and set to 1.
+    private static final String[] GLOBAL_SYSCTLS = {
+        "/proc/sys/net/ipv4/fwmark_reflect",
+        "/proc/sys/net/ipv6/fwmark_reflect",
+        "/proc/sys/net/ipv4/tcp_fwmark_accept",
+    };
+
+    // Per-interface IPv6 autoconf sysctls.
+    private static final String IPV6_SYSCTL_DIR = "/proc/sys/net/ipv6/conf";
+    private static final String AUTOCONF_SYSCTL = "accept_ra_rt_table";
+
+    // Expected values for MIN|MAX_PLEN.
+    private static final String ACCEPT_RA_RT_INFO_MIN_PLEN_STRING = "accept_ra_rt_info_min_plen";
+    private static final int ACCEPT_RA_RT_INFO_MIN_PLEN_VALUE = 48;
+    private static final String ACCEPT_RA_RT_INFO_MAX_PLEN_STRING = "accept_ra_rt_info_max_plen";
+    private static final int ACCEPT_RA_RT_INFO_MAX_PLEN_VALUE = 64;
+    // Expected values for RFC 7559 router soliciations.
+    // Maximum number of router solicitations to send. -1 means no limit.
+    private static final int IPV6_WIFI_ROUTER_SOLICITATIONS = -1;
+    private ITestDevice mDevice;
+    private IBuildInfo mBuild;
+    private String[] mSysctlDirs;
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void setBuild(IBuildInfo build) {
+        mBuild = build;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void setDevice(ITestDevice device) {
+        super.setDevice(device);
+        mDevice = device;
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mSysctlDirs = getSysctlDirs();
+    }
+
+    private String[] getSysctlDirs() throws Exception {
+        String interfaceDirs[] = mDevice.executeAdbCommand("shell", "ls", "-1",
+                IPV6_SYSCTL_DIR).split("\n");
+        List<String> interfaceDirsList = new ArrayList<String>(Arrays.asList(interfaceDirs));
+        interfaceDirsList.remove("all");
+        interfaceDirsList.remove("lo");
+        return interfaceDirsList.toArray(new String[interfaceDirsList.size()]);
+    }
+
+
+    protected void assertLess(String sysctl, int a, int b) {
+        assertTrue("value of " + sysctl + ": expected < " + b + " but was: " + a, a < b);
+    }
+
+    protected void assertAtLeast(String sysctl, int a, int b) {
+        assertTrue("value of " + sysctl + ": expected >= " + b + " but was: " + a, a >= b);
+    }
+
+    public int readIntFromPath(String path) throws Exception {
+        String mode = mDevice.executeAdbCommand("shell", "stat", "-c", "%a", path).trim();
+        String user = mDevice.executeAdbCommand("shell", "stat", "-c", "%u", path).trim();
+        String group = mDevice.executeAdbCommand("shell", "stat", "-c", "%g", path).trim();
+        assertEquals(mode, "644");
+        assertEquals(user, "0");
+        assertEquals(group, "0");
+        return Integer.parseInt(mDevice.executeAdbCommand("shell", "cat", path).trim());
+    }
+
+    /**
+     * Checks that SPI default timeouts are overridden, and set to a reasonable length of time
+     */
+    public void testMinAcqExpires() throws Exception {
+        int value = readIntFromPath(SPI_TIMEOUT_SYSCTL);
+        assertAtLeast(SPI_TIMEOUT_SYSCTL, value, MIN_ACQ_EXPIRES);
+    }
+
+    /**
+     * Checks that the sysctls for multinetwork kernel features are present and
+     * enabled.
+     */
+    public void testProcSysctls() throws Exception {
+        for (String sysctl : GLOBAL_SYSCTLS) {
+            int value = readIntFromPath(sysctl);
+            assertEquals(sysctl, 1, value);
+        }
+
+        for (String interfaceDir : mSysctlDirs) {
+            String path = IPV6_SYSCTL_DIR + "/" + interfaceDir + "/" + AUTOCONF_SYSCTL;
+            int value = readIntFromPath(path);
+            assertLess(path, value, 0);
+        }
+    }
+
+    /**
+     * Verify that accept_ra_rt_info_{min,max}_plen exists and is set to the expected value
+     */
+    public void testAcceptRaRtInfoMinMaxPlen() throws Exception {
+        for (String interfaceDir : mSysctlDirs) {
+            String path = IPV6_SYSCTL_DIR + "/" + interfaceDir + "/" + "accept_ra_rt_info_min_plen";
+            int value = readIntFromPath(path);
+            assertEquals(path, value, ACCEPT_RA_RT_INFO_MIN_PLEN_VALUE);
+            path = IPV6_SYSCTL_DIR + "/" + interfaceDir + "/" + "accept_ra_rt_info_max_plen";
+            value = readIntFromPath(path);
+            assertEquals(path, value, ACCEPT_RA_RT_INFO_MAX_PLEN_VALUE);
+        }
+    }
+
+    /**
+     * Verify that router_solicitations exists and is set to the expected value
+     * and verify that router_solicitation_max_interval exists and is in an acceptable interval.
+     */
+    public void testRouterSolicitations() throws Exception {
+        for (String interfaceDir : mSysctlDirs) {
+            String path = IPV6_SYSCTL_DIR + "/" + interfaceDir + "/" + "router_solicitations";
+            int value = readIntFromPath(path);
+            assertEquals(IPV6_WIFI_ROUTER_SOLICITATIONS, value);
+            path = IPV6_SYSCTL_DIR + "/" + interfaceDir + "/" + "router_solicitation_max_interval";
+            int interval = readIntFromPath(path);
+            final int lowerBoundSec = 15 * 60;
+            final int upperBoundSec = 60 * 60;
+            assertTrue(lowerBoundSec <= interval);
+            assertTrue(interval <= upperBoundSec);
+        }
+    }
+}
diff --git a/tests/cts/net/Android.mk b/tests/cts/net/Android.mk
index 45941a7..2084dbc 100644
--- a/tests/cts/net/Android.mk
+++ b/tests/cts/net/Android.mk
@@ -26,7 +26,6 @@
 
 LOCAL_JAVA_LIBRARIES := \
     voip-common \
-    conscrypt \
     org.apache.http.legacy \
     android.test.base.stubs \
 
@@ -46,7 +45,9 @@
     ctstestserver \
     mockwebserver \
     junit \
-    truth-prebuilt
+    junit-params \
+    truth-prebuilt \
+
 
 # uncomment when b/13249961 is fixed
 #LOCAL_SDK_VERSION := current
diff --git a/tests/cts/net/AndroidManifest.xml b/tests/cts/net/AndroidManifest.xml
index b261b39..dbd8fef 100644
--- a/tests/cts/net/AndroidManifest.xml
+++ b/tests/cts/net/AndroidManifest.xml
@@ -22,6 +22,7 @@
     <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
     <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
     <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
+    <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
     <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
     <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
     <uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
@@ -30,6 +31,7 @@
     <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" />
+    <uses-permission android:name="android.permission.CHANGE_WIFI_MULTICAST_STATE" />
 
     <application android:usesCleartextTraffic="true">
         <uses-library android:name="android.test.runner" />
diff --git a/tests/cts/net/AndroidTest.xml b/tests/cts/net/AndroidTest.xml
index 1326970..ff93afc 100644
--- a/tests/cts/net/AndroidTest.xml
+++ b/tests/cts/net/AndroidTest.xml
@@ -15,6 +15,8 @@
 <configuration description="Config for CTS Net test cases">
     <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="networking" />
+    <option name="config-descriptor:metadata" key="token" value="SIM_CARD" />
+    <option name="not-shardable" value="true" />
     <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
         <option name="cleanup-apks" value="true" />
         <option name="test-file-name" value="CtsNetTestCases.apk" />
@@ -24,5 +26,6 @@
         <option name="package" value="android.net.cts" />
         <option name="runtime-hint" value="9m4s" />
         <option name="hidden-api-checks" value="false" />
+        <option name="isolated-storage" value="false" />
     </test>
 </configuration>
diff --git a/tests/cts/net/OWNERS b/tests/cts/net/OWNERS
index dc82bb0..d558556 100644
--- a/tests/cts/net/OWNERS
+++ b/tests/cts/net/OWNERS
@@ -1,2 +1,3 @@
+# Bug component: 31808
 lorenzo@google.com
-satk@google.com
\ No newline at end of file
+satk@google.com
diff --git a/tests/cts/net/jni/Android.mk b/tests/cts/net/jni/Android.mk
index 727a44d..ccb1278 100644
--- a/tests/cts/net/jni/Android.mk
+++ b/tests/cts/net/jni/Android.mk
@@ -36,7 +36,7 @@
 LOCAL_MODULE := libnativemultinetwork_jni
 # Don't include this package in any configuration by default.
 LOCAL_MODULE_TAGS := optional
-LOCAL_SRC_FILES := NativeMultinetworkJni.c
+LOCAL_SRC_FILES := NativeMultinetworkJni.cpp
 LOCAL_CFLAGS := -Wall -Werror -Wno-format
 LOCAL_C_INCLUDES := $(JNI_H_INCLUDE)
 LOCAL_SHARED_LIBRARIES := libandroid libnativehelper_compat_libc++ liblog
diff --git a/tests/cts/net/jni/NativeMultinetworkJni.c b/tests/cts/net/jni/NativeMultinetworkJni.c
deleted file mode 100644
index 2fa5291..0000000
--- a/tests/cts/net/jni/NativeMultinetworkJni.c
+++ /dev/null
@@ -1,237 +0,0 @@
-/*
- * 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.
- */
-
-
-#define LOG_TAG "MultinetworkApiTest"
-#include <utils/Log.h>
-
-#include <arpa/inet.h>
-#include <errno.h>
-#include <inttypes.h>
-#include <jni.h>
-#include <netdb.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/socket.h>
-#include <sys/time.h>
-#include <android/multinetwork.h>
-
-#define UNUSED(X) ((void) (X))
-
-static const char kHostname[] = "connectivitycheck.android.com";
-
-
-JNIEXPORT jint Java_android_net_cts_MultinetworkApiTest_runGetaddrinfoCheck(
-        JNIEnv* env, jclass class, jlong nethandle) {
-    UNUSED(env);
-    UNUSED(class);
-    net_handle_t handle = (net_handle_t) nethandle;
-    struct addrinfo *res = NULL;
-
-    errno = 0;
-    int rval = android_getaddrinfofornetwork(handle, kHostname, NULL, NULL, &res);
-    const int saved_errno = errno;
-    freeaddrinfo(res);
-
-    ALOGD("android_getaddrinfofornetwork(%" PRIu64 ", %s) returned rval=%d errno=%d",
-          handle, kHostname, rval, saved_errno);
-    return rval == 0 ? 0 : -saved_errno;
-}
-
-JNIEXPORT jint Java_android_net_cts_MultinetworkApiTest_runSetprocnetwork(
-        JNIEnv* env, jclass class, jlong nethandle) {
-    UNUSED(env);
-    UNUSED(class);
-    net_handle_t handle = (net_handle_t) nethandle;
-
-    errno = 0;
-    int rval = android_setprocnetwork(handle);
-    const int saved_errno = errno;
-    ALOGD("android_setprocnetwork(%" PRIu64 ") returned rval=%d errno=%d",
-          handle, rval, saved_errno);
-    return rval == 0 ? 0 : -saved_errno;
-}
-
-JNIEXPORT jint Java_android_net_cts_MultinetworkApiTest_runSetsocknetwork(
-        JNIEnv* env, jclass class, jlong nethandle) {
-    UNUSED(env);
-    UNUSED(class);
-    net_handle_t handle = (net_handle_t) nethandle;
-
-    errno = 0;
-    int fd = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP);
-    if (fd < 0) {
-        ALOGD("socket() failed, errno=%d", errno);
-        return -errno;
-    }
-
-    errno = 0;
-    int rval = android_setsocknetwork(handle, fd);
-    const int saved_errno = errno;
-    ALOGD("android_setprocnetwork(%" PRIu64 ", %d) returned rval=%d errno=%d",
-          handle, fd, rval, saved_errno);
-    close(fd);
-    return rval == 0 ? 0 : -saved_errno;
-}
-
-// Use sizeof("x") - 1 because we need a compile-time constant, and strlen("x")
-// isn't guaranteed to fold to a constant.
-static const int kSockaddrStrLen = INET6_ADDRSTRLEN + sizeof("[]:65535") - 1;
-
-void sockaddr_ntop(const struct sockaddr *sa, socklen_t salen, char *dst, const size_t size) {
-    char addrstr[INET6_ADDRSTRLEN];
-    char portstr[sizeof("65535")];
-    char buf[kSockaddrStrLen+1];
-
-    int ret = getnameinfo(sa, salen,
-                          addrstr, sizeof(addrstr),
-                          portstr, sizeof(portstr),
-                          NI_NUMERICHOST | NI_NUMERICSERV);
-    if (ret == 0) {
-        snprintf(buf, sizeof(buf),
-                 (sa->sa_family == AF_INET6) ? "[%s]:%s" : "%s:%s",
-                 addrstr, portstr);
-    } else {
-        sprintf(buf, "???");
-    }
-
-    strlcpy(dst, buf, size);
-}
-
-JNIEXPORT jint Java_android_net_cts_MultinetworkApiTest_runDatagramCheck(
-        JNIEnv* env, jclass class, jlong nethandle) {
-    UNUSED(env);
-    UNUSED(class);
-    const struct addrinfo kHints = {
-        .ai_flags = AI_ADDRCONFIG,
-        .ai_family = AF_UNSPEC,
-        .ai_socktype = SOCK_DGRAM,
-        .ai_protocol = IPPROTO_UDP,
-    };
-    struct addrinfo *res = NULL;
-    net_handle_t handle = (net_handle_t) nethandle;
-
-    static const char kPort[] = "443";
-    int rval = android_getaddrinfofornetwork(handle, kHostname, kPort, &kHints, &res);
-    if (rval != 0) {
-        ALOGD("android_getaddrinfofornetwork(%llu, %s) returned rval=%d errno=%d",
-              handle, kHostname, rval, errno);
-        freeaddrinfo(res);
-        return -errno;
-    }
-
-    // Rely upon getaddrinfo sorting the best destination to the front.
-    int fd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
-    if (fd < 0) {
-        ALOGD("socket(%d, %d, %d) failed, errno=%d",
-              res->ai_family, res->ai_socktype, res->ai_protocol, errno);
-        freeaddrinfo(res);
-        return -errno;
-    }
-
-    rval = android_setsocknetwork(handle, fd);
-    ALOGD("android_setprocnetwork(%llu, %d) returned rval=%d errno=%d",
-          handle, fd, rval, errno);
-    if (rval != 0) {
-        close(fd);
-        freeaddrinfo(res);
-        return -errno;
-    }
-
-    char addrstr[kSockaddrStrLen+1];
-    sockaddr_ntop(res->ai_addr, res->ai_addrlen, addrstr, sizeof(addrstr));
-    ALOGD("Attempting connect() to %s ...", addrstr);
-
-    rval = connect(fd, res->ai_addr, res->ai_addrlen);
-    if (rval != 0) {
-        close(fd);
-        freeaddrinfo(res);
-        return -errno;
-    }
-    freeaddrinfo(res);
-
-    struct sockaddr_storage src_addr;
-    socklen_t src_addrlen = sizeof(src_addr);
-    if (getsockname(fd, (struct sockaddr *)&src_addr, &src_addrlen) != 0) {
-        close(fd);
-        return -errno;
-    }
-    sockaddr_ntop((const struct sockaddr *)&src_addr, sizeof(src_addr), addrstr, sizeof(addrstr));
-    ALOGD("... from %s", addrstr);
-
-    // Don't let reads or writes block indefinitely.
-    const struct timeval timeo = { 2, 0 };  // 2 seconds
-    setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &timeo, sizeof(timeo));
-    setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &timeo, sizeof(timeo));
-
-    // For reference see:
-    //     https://tools.ietf.org/html/draft-tsvwg-quic-protocol-01#section-6.1
-    uint8_t quic_packet[] = {
-        0x0c,                    // public flags: 64bit conn ID, 8bit sequence number
-        0, 0, 0, 0, 0, 0, 0, 0,  // 64bit connection ID
-        0x01,                    // sequence number
-        0x00,                    // private flags
-        0x07,                    // type: regular frame type "PING"
-    };
-
-    arc4random_buf(quic_packet + 1, 8);  // random connection ID
-
-    uint8_t response[1500];
-    ssize_t sent, rcvd;
-    static const int MAX_RETRIES = 5;
-    int i, errnum = 0;
-
-    for (i = 0; i < MAX_RETRIES; i++) {
-        sent = send(fd, quic_packet, sizeof(quic_packet), 0);
-        if (sent < (ssize_t)sizeof(quic_packet)) {
-            errnum = errno;
-            ALOGD("send(QUIC packet) returned sent=%zd, errno=%d", sent, errnum);
-            close(fd);
-            return -errnum;
-        }
-
-        rcvd = recv(fd, response, sizeof(response), 0);
-        if (rcvd > 0) {
-            break;
-        } else {
-            errnum = errno;
-            ALOGD("[%d/%d] recv(QUIC response) returned rcvd=%zd, errno=%d",
-                  i + 1, MAX_RETRIES, rcvd, errnum);
-        }
-    }
-    if (rcvd < sent) {
-        ALOGD("QUIC UDP %s: sent=%zd but rcvd=%zd, errno=%d", kPort, sent, rcvd, errnum);
-        if (rcvd <= 0) {
-            ALOGD("Does this network block UDP port %s?", kPort);
-        }
-        close(fd);
-        return -EPROTO;
-    }
-
-    int conn_id_cmp = memcmp(quic_packet + 1, response + 1, 8);
-    if (conn_id_cmp != 0) {
-        ALOGD("sent and received connection IDs do not match");
-        close(fd);
-        return -EPROTO;
-    }
-
-    // TODO: log, and compare to the IP address encoded in the
-    // response, since this should be a public reset packet.
-
-    close(fd);
-    return 0;
-}
diff --git a/tests/cts/net/jni/NativeMultinetworkJni.cpp b/tests/cts/net/jni/NativeMultinetworkJni.cpp
new file mode 100644
index 0000000..a6b5e90
--- /dev/null
+++ b/tests/cts/net/jni/NativeMultinetworkJni.cpp
@@ -0,0 +1,513 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+#define LOG_TAG "MultinetworkApiTest"
+#include <utils/Log.h>
+
+#include <arpa/inet.h>
+#include <arpa/nameser.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <jni.h>
+#include <netdb.h>
+#include <poll.h> /* poll */
+#include <resolv.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+
+#include <string>
+
+#include <android/multinetwork.h>
+#include <nativehelper/JNIHelp.h>
+
+#define EXPECT_GE(env, actual, expected, msg)                        \
+    do {                                                             \
+        if (actual < expected) {                                     \
+            jniThrowExceptionFmt(env, "java/lang/AssertionError",    \
+                    "%s:%d: %s EXPECT_GE: expected %d, got %d",      \
+                    __FILE__, __LINE__, msg, expected, actual);      \
+        }                                                            \
+    } while (0)
+
+#define EXPECT_GT(env, actual, expected, msg)                        \
+    do {                                                             \
+        if (actual <= expected) {                                    \
+            jniThrowExceptionFmt(env, "java/lang/AssertionError",    \
+                    "%s:%d: %s EXPECT_GT: expected %d, got %d",      \
+                    __FILE__, __LINE__, msg, expected, actual);      \
+        }                                                            \
+    } while (0)
+
+#define EXPECT_EQ(env, expected, actual, msg)                        \
+    do {                                                             \
+        if (actual != expected) {                                    \
+            jniThrowExceptionFmt(env, "java/lang/AssertionError",    \
+                    "%s:%d: %s EXPECT_EQ: expected %d, got %d",      \
+                    __FILE__, __LINE__, msg, expected, actual);      \
+        }                                                            \
+    } while (0)
+
+static const int MAXPACKET = 8 * 1024;
+static const int TIMEOUT_MS = 15000;
+static const char kHostname[] = "connectivitycheck.android.com";
+static const char kNxDomainName[] = "test1-nx.metric.gstatic.com";
+static const char kGoogleName[] = "www.google.com";
+
+int makeQuery(const char* name, int qtype, uint8_t* buf, size_t buflen) {
+    return res_mkquery(ns_o_query, name, ns_c_in, qtype, NULL, 0, NULL, buf, buflen);
+}
+
+int getAsyncResponse(JNIEnv* env, int fd, int timeoutMs, int* rcode, uint8_t* buf, size_t bufLen) {
+    struct pollfd wait_fd = { .fd = fd, .events = POLLIN };
+
+    poll(&wait_fd, 1, timeoutMs);
+    if (wait_fd.revents & POLLIN) {
+        int n = android_res_nresult(fd, rcode, buf, bufLen);
+        // Verify that android_res_nresult() closed the fd
+        char dummy;
+        EXPECT_EQ(env, -1, read(fd, &dummy, sizeof(dummy)), "res_nresult check for closing fd");
+        EXPECT_EQ(env, EBADF, errno, "res_nresult check for errno");
+        return n;
+    }
+
+    return -ETIMEDOUT;
+}
+
+int extractIpAddressAnswers(uint8_t* buf, size_t bufLen, int family) {
+    ns_msg handle;
+    if (ns_initparse((const uint8_t*) buf, bufLen, &handle) < 0) {
+        return -errno;
+    }
+    const int ancount = ns_msg_count(handle, ns_s_an);
+    // Answer count = 0 is valid(e.g. response of query with root)
+    if (!ancount) {
+        return 0;
+    }
+    ns_rr rr;
+    bool hasValidAns = false;
+    for (int i = 0; i < ancount; i++) {
+        if (ns_parserr(&handle, ns_s_an, i, &rr) < 0) {
+            // If there is no valid answer, test will fail.
+            continue;
+        }
+        const uint8_t* rdata = ns_rr_rdata(rr);
+        char buffer[INET6_ADDRSTRLEN];
+        if (inet_ntop(family, (const char*) rdata, buffer, sizeof(buffer)) == NULL) {
+            return -errno;
+        }
+        hasValidAns = true;
+    }
+    return hasValidAns ? 0 : -EBADMSG;
+}
+
+int expectAnswersValid(JNIEnv* env, int fd, int family, int expectedRcode) {
+    int rcode = -1;
+    uint8_t buf[MAXPACKET] = {};
+    int res = getAsyncResponse(env, fd, TIMEOUT_MS, &rcode, buf, MAXPACKET);
+    if (res < 0) {
+        return res;
+    }
+
+    EXPECT_EQ(env, expectedRcode, rcode, "rcode is not expected");
+
+    if (expectedRcode == ns_r_noerror && res > 0) {
+        return extractIpAddressAnswers(buf, res, family);
+    }
+    return 0;
+}
+
+int expectAnswersNotValid(JNIEnv* env, int fd, int expectedErrno) {
+    int rcode = -1;
+    uint8_t buf[MAXPACKET] = {};
+    int res = getAsyncResponse(env, fd, TIMEOUT_MS, &rcode, buf, MAXPACKET);
+    if (res != expectedErrno) {
+        ALOGD("res:%d, expectedErrno = %d", res, expectedErrno);
+        return (res > 0) ? -EREMOTEIO : res;
+    }
+    return 0;
+}
+
+extern "C"
+JNIEXPORT jint Java_android_net_cts_MultinetworkApiTest_runResNqueryCheck(
+        JNIEnv* env, jclass, jlong nethandle) {
+    net_handle_t handle = (net_handle_t) nethandle;
+
+    // V4
+    int fd = android_res_nquery(handle, kHostname, ns_c_in, ns_t_a, 0);
+    EXPECT_GE(env, fd, 0, "v4 res_nquery");
+    EXPECT_EQ(env, 0, expectAnswersValid(env, fd, AF_INET, ns_r_noerror),
+            "v4 res_nquery check answers");
+
+    // V4 NXDOMAIN
+    fd = android_res_nquery(handle, kNxDomainName, ns_c_in, ns_t_a, 0);
+    EXPECT_GE(env, fd, 0, "v4 res_nquery NXDOMAIN");
+    EXPECT_EQ(env, 0, expectAnswersValid(env, fd, AF_INET, ns_r_nxdomain),
+            "v4 res_nquery NXDOMAIN check answers");
+
+    // V6
+    fd = android_res_nquery(handle, kHostname, ns_c_in, ns_t_aaaa, 0);
+    EXPECT_GE(env, fd, 0, "v6 res_nquery");
+    EXPECT_EQ(env, 0, expectAnswersValid(env, fd, AF_INET, ns_r_noerror),
+            "v6 res_nquery check answers");
+
+    // V6 NXDOMAIN
+    fd = android_res_nquery(handle, kNxDomainName, ns_c_in, ns_t_aaaa, 0);
+    EXPECT_GE(env, fd, 0, "v6 res_nquery NXDOMAIN");
+    EXPECT_EQ(env, 0, expectAnswersValid(env, fd, AF_INET, ns_r_nxdomain),
+            "v6 res_nquery NXDOMAIN check answers");
+
+    return 0;
+}
+
+extern "C"
+JNIEXPORT jint Java_android_net_cts_MultinetworkApiTest_runResNsendCheck(
+        JNIEnv* env, jclass, jlong nethandle) {
+    net_handle_t handle = (net_handle_t) nethandle;
+    // V4
+    uint8_t buf1[MAXPACKET] = {};
+
+    int len1 = makeQuery(kGoogleName, ns_t_a, buf1, sizeof(buf1));
+    EXPECT_GT(env, len1, 0, "v4 res_mkquery 1st");
+
+    uint8_t buf2[MAXPACKET] = {};
+    int len2 = makeQuery(kHostname, ns_t_a, buf2, sizeof(buf2));
+    EXPECT_GT(env, len2, 0, "v4 res_mkquery 2nd");
+
+    int fd1 = android_res_nsend(handle, buf1, len1, 0);
+    EXPECT_GE(env, fd1, 0, "v4 res_nsend 1st");
+    int fd2 = android_res_nsend(handle, buf2, len2, 0);
+    EXPECT_GE(env, fd2, 0, "v4 res_nsend 2nd");
+
+    EXPECT_EQ(env, 0, expectAnswersValid(env, fd2, AF_INET, ns_r_noerror),
+            "v4 res_nsend 2nd check answers");
+    EXPECT_EQ(env, 0, expectAnswersValid(env, fd1, AF_INET, ns_r_noerror),
+            "v4 res_nsend 1st check answers");
+
+    // V4 NXDOMAIN
+    memset(buf1, 0, sizeof(buf1));
+    len1 = makeQuery(kNxDomainName, ns_t_a, buf1, sizeof(buf1));
+    EXPECT_GT(env, len1, 0, "v4 res_mkquery NXDOMAIN");
+    fd1 = android_res_nsend(handle, buf1, len1, 0);
+    EXPECT_GE(env, fd1, 0, "v4 res_nsend NXDOMAIN");
+    EXPECT_EQ(env, 0, expectAnswersValid(env, fd1, AF_INET, ns_r_nxdomain),
+            "v4 res_nsend NXDOMAIN check answers");
+
+    // V6
+    memset(buf1, 0, sizeof(buf1));
+    memset(buf2, 0, sizeof(buf2));
+    len1 = makeQuery(kGoogleName, ns_t_aaaa, buf1, sizeof(buf1));
+    EXPECT_GT(env, len1, 0, "v6 res_mkquery 1st");
+    len2 = makeQuery(kHostname, ns_t_aaaa, buf2, sizeof(buf2));
+    EXPECT_GT(env, len2, 0, "v6 res_mkquery 2nd");
+
+    fd1 = android_res_nsend(handle, buf1, len1, 0);
+    EXPECT_GE(env, fd1, 0, "v6 res_nsend 1st");
+    fd2 = android_res_nsend(handle, buf2, len2, 0);
+    EXPECT_GE(env, fd2, 0, "v6 res_nsend 2nd");
+
+    EXPECT_EQ(env, 0, expectAnswersValid(env, fd2, AF_INET6, ns_r_noerror),
+            "v6 res_nsend 2nd check answers");
+    EXPECT_EQ(env, 0, expectAnswersValid(env, fd1, AF_INET6, ns_r_noerror),
+            "v6 res_nsend 1st check answers");
+
+    // V6 NXDOMAIN
+    memset(buf1, 0, sizeof(buf1));
+    len1 = makeQuery(kNxDomainName, ns_t_aaaa, buf1, sizeof(buf1));
+    EXPECT_GT(env, len1, 0, "v6 res_mkquery NXDOMAIN");
+    fd1 = android_res_nsend(handle, buf1, len1, 0);
+    EXPECT_GE(env, fd1, 0, "v6 res_nsend NXDOMAIN");
+    EXPECT_EQ(env, 0, expectAnswersValid(env, fd1, AF_INET6, ns_r_nxdomain),
+            "v6 res_nsend NXDOMAIN check answers");
+
+    return 0;
+}
+
+extern "C"
+JNIEXPORT jint Java_android_net_cts_MultinetworkApiTest_runResNcancelCheck(
+        JNIEnv* env, jclass, jlong nethandle) {
+    net_handle_t handle = (net_handle_t) nethandle;
+
+    int fd = android_res_nquery(handle, kGoogleName, ns_c_in, ns_t_a, 0);
+    errno = 0;
+    android_res_cancel(fd);
+    int err = errno;
+    EXPECT_EQ(env, 0, err, "res_cancel");
+    // DO NOT call cancel or result with the same fd more than once,
+    // otherwise it will hit fdsan double-close fd.
+    return 0;
+}
+
+extern "C"
+JNIEXPORT jint Java_android_net_cts_MultinetworkApiTest_runResNapiMalformedCheck(
+        JNIEnv* env, jclass, jlong nethandle) {
+    net_handle_t handle = (net_handle_t) nethandle;
+
+    // It is the equivalent of "dig . a", Query with an empty name.
+    int fd = android_res_nquery(handle, "", ns_c_in, ns_t_a, 0);
+    EXPECT_GE(env, fd, 0, "res_nquery root");
+    EXPECT_EQ(env, 0, expectAnswersValid(env, fd, AF_INET, ns_r_noerror),
+            "res_nquery root check answers");
+
+    // Label limit 63
+    std::string exceedingLabelQuery = "www." + std::string(70, 'g') + ".com";
+    // Name limit 255
+    std::string exceedingDomainQuery = "www." + std::string(255, 'g') + ".com";
+
+    fd = android_res_nquery(handle, exceedingLabelQuery.c_str(), ns_c_in, ns_t_a, 0);
+    EXPECT_EQ(env, -EMSGSIZE, fd, "res_nquery exceedingLabelQuery");
+    fd = android_res_nquery(handle, exceedingDomainQuery.c_str(), ns_c_in, ns_t_aaaa, 0);
+    EXPECT_EQ(env, -EMSGSIZE, fd, "res_nquery exceedingDomainQuery");
+
+    uint8_t buf[10] = {};
+    // empty BLOB
+    fd = android_res_nsend(handle, buf, 10, 0);
+    EXPECT_GE(env, fd, 0, "res_nsend empty BLOB");
+    EXPECT_EQ(env, 0, expectAnswersNotValid(env, fd, -EINVAL),
+            "res_nsend empty BLOB check answers");
+
+    uint8_t largeBuf[2 * MAXPACKET] = {};
+    // A buffer larger than 8KB
+    fd = android_res_nsend(handle, largeBuf, sizeof(largeBuf), 0);
+    EXPECT_EQ(env, -EMSGSIZE, fd, "res_nsend buffer larger than 8KB");
+
+    // 5000 bytes filled with 0. This returns EMSGSIZE because FrameworkListener limits the size of
+    // commands to 4096 bytes.
+    fd = android_res_nsend(handle, largeBuf, 5000, 0);
+    EXPECT_EQ(env, -EMSGSIZE, fd, "res_nsend 5000 bytes filled with 0");
+
+    // 500 bytes filled with 0
+    fd = android_res_nsend(handle, largeBuf, 500, 0);
+    EXPECT_GE(env, fd, 0, "res_nsend 500 bytes filled with 0");
+    EXPECT_EQ(env, 0, expectAnswersNotValid(env, fd, -EINVAL),
+            "res_nsend 500 bytes filled with 0 check answers");
+
+    // 5000 bytes filled with 0xFF
+    uint8_t ffBuf[5001] = {};
+    memset(ffBuf, 0xFF, sizeof(ffBuf));
+    ffBuf[5000] = '\0';
+    fd = android_res_nsend(handle, ffBuf, sizeof(ffBuf), 0);
+    EXPECT_EQ(env, -EMSGSIZE, fd, "res_nsend 5000 bytes filled with 0xFF");
+
+    // 500 bytes filled with 0xFF
+    ffBuf[500] = '\0';
+    fd = android_res_nsend(handle, ffBuf, 501, 0);
+    EXPECT_GE(env, fd, 0, "res_nsend 500 bytes filled with 0xFF");
+    EXPECT_EQ(env, 0, expectAnswersNotValid(env, fd, -EINVAL),
+            "res_nsend 500 bytes filled with 0xFF check answers");
+
+    return 0;
+}
+
+extern "C"
+JNIEXPORT jint Java_android_net_cts_MultinetworkApiTest_runGetaddrinfoCheck(
+        JNIEnv*, jclass, jlong nethandle) {
+    net_handle_t handle = (net_handle_t) nethandle;
+    struct addrinfo *res = NULL;
+
+    errno = 0;
+    int rval = android_getaddrinfofornetwork(handle, kHostname, NULL, NULL, &res);
+    const int saved_errno = errno;
+    freeaddrinfo(res);
+
+    ALOGD("android_getaddrinfofornetwork(%" PRIu64 ", %s) returned rval=%d errno=%d",
+          handle, kHostname, rval, saved_errno);
+    return rval == 0 ? 0 : -saved_errno;
+}
+
+extern "C"
+JNIEXPORT jint Java_android_net_cts_MultinetworkApiTest_runSetprocnetwork(
+        JNIEnv*, jclass, jlong nethandle) {
+    net_handle_t handle = (net_handle_t) nethandle;
+
+    errno = 0;
+    int rval = android_setprocnetwork(handle);
+    const int saved_errno = errno;
+    ALOGD("android_setprocnetwork(%" PRIu64 ") returned rval=%d errno=%d",
+          handle, rval, saved_errno);
+    return rval == 0 ? 0 : -saved_errno;
+}
+
+extern "C"
+JNIEXPORT jint Java_android_net_cts_MultinetworkApiTest_runSetsocknetwork(
+        JNIEnv*, jclass, jlong nethandle) {
+    net_handle_t handle = (net_handle_t) nethandle;
+
+    errno = 0;
+    int fd = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP);
+    if (fd < 0) {
+        ALOGD("socket() failed, errno=%d", errno);
+        return -errno;
+    }
+
+    errno = 0;
+    int rval = android_setsocknetwork(handle, fd);
+    const int saved_errno = errno;
+    ALOGD("android_setprocnetwork(%" PRIu64 ", %d) returned rval=%d errno=%d",
+          handle, fd, rval, saved_errno);
+    close(fd);
+    return rval == 0 ? 0 : -saved_errno;
+}
+
+// Use sizeof("x") - 1 because we need a compile-time constant, and strlen("x")
+// isn't guaranteed to fold to a constant.
+static const int kSockaddrStrLen = INET6_ADDRSTRLEN + sizeof("[]:65535") - 1;
+
+void sockaddr_ntop(const struct sockaddr *sa, socklen_t salen, char *dst, const size_t size) {
+    char addrstr[INET6_ADDRSTRLEN];
+    char portstr[sizeof("65535")];
+    char buf[kSockaddrStrLen+1];
+
+    int ret = getnameinfo(sa, salen,
+                          addrstr, sizeof(addrstr),
+                          portstr, sizeof(portstr),
+                          NI_NUMERICHOST | NI_NUMERICSERV);
+    if (ret == 0) {
+        snprintf(buf, sizeof(buf),
+                 (sa->sa_family == AF_INET6) ? "[%s]:%s" : "%s:%s",
+                 addrstr, portstr);
+    } else {
+        sprintf(buf, "???");
+    }
+
+    strlcpy(dst, buf, size);
+}
+
+extern "C"
+JNIEXPORT jint Java_android_net_cts_MultinetworkApiTest_runDatagramCheck(
+        JNIEnv*, jclass, jlong nethandle) {
+    const struct addrinfo kHints = {
+        .ai_flags = AI_ADDRCONFIG,
+        .ai_family = AF_UNSPEC,
+        .ai_socktype = SOCK_DGRAM,
+        .ai_protocol = IPPROTO_UDP,
+    };
+    struct addrinfo *res = NULL;
+    net_handle_t handle = (net_handle_t) nethandle;
+
+    static const char kPort[] = "443";
+    int rval = android_getaddrinfofornetwork(handle, kHostname, kPort, &kHints, &res);
+    if (rval != 0) {
+        ALOGD("android_getaddrinfofornetwork(%llu, %s) returned rval=%d errno=%d",
+              handle, kHostname, rval, errno);
+        freeaddrinfo(res);
+        return -errno;
+    }
+
+    // Rely upon getaddrinfo sorting the best destination to the front.
+    int fd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
+    if (fd < 0) {
+        ALOGD("socket(%d, %d, %d) failed, errno=%d",
+              res->ai_family, res->ai_socktype, res->ai_protocol, errno);
+        freeaddrinfo(res);
+        return -errno;
+    }
+
+    rval = android_setsocknetwork(handle, fd);
+    ALOGD("android_setprocnetwork(%llu, %d) returned rval=%d errno=%d",
+          handle, fd, rval, errno);
+    if (rval != 0) {
+        close(fd);
+        freeaddrinfo(res);
+        return -errno;
+    }
+
+    char addrstr[kSockaddrStrLen+1];
+    sockaddr_ntop(res->ai_addr, res->ai_addrlen, addrstr, sizeof(addrstr));
+    ALOGD("Attempting connect() to %s ...", addrstr);
+
+    rval = connect(fd, res->ai_addr, res->ai_addrlen);
+    if (rval != 0) {
+        close(fd);
+        freeaddrinfo(res);
+        return -errno;
+    }
+    freeaddrinfo(res);
+
+    struct sockaddr_storage src_addr;
+    socklen_t src_addrlen = sizeof(src_addr);
+    if (getsockname(fd, (struct sockaddr *)&src_addr, &src_addrlen) != 0) {
+        close(fd);
+        return -errno;
+    }
+    sockaddr_ntop((const struct sockaddr *)&src_addr, sizeof(src_addr), addrstr, sizeof(addrstr));
+    ALOGD("... from %s", addrstr);
+
+    // Don't let reads or writes block indefinitely.
+    const struct timeval timeo = { 2, 0 };  // 2 seconds
+    setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &timeo, sizeof(timeo));
+    setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &timeo, sizeof(timeo));
+
+    // For reference see:
+    //     https://tools.ietf.org/html/draft-tsvwg-quic-protocol-01#section-6.1
+    uint8_t quic_packet[] = {
+        0x0c,                    // public flags: 64bit conn ID, 8bit sequence number
+        0, 0, 0, 0, 0, 0, 0, 0,  // 64bit connection ID
+        0x01,                    // sequence number
+        0x00,                    // private flags
+        0x07,                    // type: regular frame type "PING"
+    };
+
+    arc4random_buf(quic_packet + 1, 8);  // random connection ID
+
+    uint8_t response[1500];
+    ssize_t sent, rcvd;
+    static const int MAX_RETRIES = 5;
+    int i, errnum = 0;
+
+    for (i = 0; i < MAX_RETRIES; i++) {
+        sent = send(fd, quic_packet, sizeof(quic_packet), 0);
+        if (sent < (ssize_t)sizeof(quic_packet)) {
+            errnum = errno;
+            ALOGD("send(QUIC packet) returned sent=%zd, errno=%d", sent, errnum);
+            close(fd);
+            return -errnum;
+        }
+
+        rcvd = recv(fd, response, sizeof(response), 0);
+        if (rcvd > 0) {
+            break;
+        } else {
+            errnum = errno;
+            ALOGD("[%d/%d] recv(QUIC response) returned rcvd=%zd, errno=%d",
+                  i + 1, MAX_RETRIES, rcvd, errnum);
+        }
+    }
+    if (rcvd < sent) {
+        ALOGD("QUIC UDP %s: sent=%zd but rcvd=%zd, errno=%d", kPort, sent, rcvd, errnum);
+        if (rcvd <= 0) {
+            ALOGD("Does this network block UDP port %s?", kPort);
+        }
+        close(fd);
+        return -EPROTO;
+    }
+
+    int conn_id_cmp = memcmp(quic_packet + 1, response + 1, 8);
+    if (conn_id_cmp != 0) {
+        ALOGD("sent and received connection IDs do not match");
+        close(fd);
+        return -EPROTO;
+    }
+
+    // TODO: log, and compare to the IP address encoded in the
+    // response, since this should be a public reset packet.
+
+    close(fd);
+    return 0;
+}
diff --git a/tests/cts/net/native/dns/Android.bp b/tests/cts/net/native/dns/Android.bp
new file mode 100644
index 0000000..9fbc3fc
--- /dev/null
+++ b/tests/cts/net/native/dns/Android.bp
@@ -0,0 +1,39 @@
+cc_defaults {
+    name: "dns_async_defaults",
+
+    cflags: [
+        "-fstack-protector-all",
+        "-g",
+        "-Wall",
+        "-Wextra",
+        "-Werror",
+        "-Wnullable-to-nonnull-conversion",
+        "-Wsign-compare",
+        "-Wthread-safety",
+        "-Wunused-parameter",
+    ],
+    srcs: [
+        "NativeDnsAsyncTest.cpp",
+    ],
+    shared_libs: [
+        "libandroid",
+        "liblog",
+        "libutils",
+    ],
+}
+
+cc_test {
+    name: "CtsNativeNetDnsTestCases",
+    defaults: ["dns_async_defaults"],
+    multilib: {
+        lib32: {
+            suffix: "32",
+        },
+        lib64: {
+            suffix: "64",
+        },
+    },
+    test_suites: [
+        "cts",
+    ],
+}
\ No newline at end of file
diff --git a/tests/cts/net/native/dns/AndroidTest.xml b/tests/cts/net/native/dns/AndroidTest.xml
new file mode 100644
index 0000000..fe88cda
--- /dev/null
+++ b/tests/cts/net/native/dns/AndroidTest.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 2018 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 dns test cases">
+    <option name="test-suite-tag" value="cts" />
+    <option name="config-descriptor:metadata" key="component" value="networking" />
+    <option name="config-descriptor:metadata" key="parameter" value="instant_app" />
+    <option name="config-descriptor:metadata" key="parameter" value="multi_abi" />
+    <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
+        <option name="cleanup" value="true" />
+        <option name="push" value="CtsNativeNetDnsTestCases->/data/local/tmp/CtsNativeNetDnsTestCases" />
+        <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="CtsNativeNetDnsTestCases" />
+        <option name="runtime-hint" value="1m" />
+    </test>
+</configuration>
diff --git a/tests/cts/net/native/dns/NativeDnsAsyncTest.cpp b/tests/cts/net/native/dns/NativeDnsAsyncTest.cpp
new file mode 100644
index 0000000..e501475
--- /dev/null
+++ b/tests/cts/net/native/dns/NativeDnsAsyncTest.cpp
@@ -0,0 +1,257 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <arpa/inet.h>
+#include <arpa/nameser.h>
+#include <error.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <netinet/in.h>
+#include <poll.h> /* poll */
+#include <resolv.h>
+#include <string.h>
+#include <sys/socket.h>
+
+#include <android/multinetwork.h>
+#include <gtest/gtest.h>
+
+namespace {
+constexpr int MAXPACKET = 8 * 1024;
+constexpr int PTON_MAX = 16;
+constexpr int TIMEOUT_MS = 10000;
+
+int getAsyncResponse(int fd, int timeoutMs, int* rcode, uint8_t* buf, size_t bufLen) {
+    struct pollfd wait_fd[1];
+    wait_fd[0].fd = fd;
+    wait_fd[0].events = POLLIN;
+    short revents;
+    int ret;
+    ret = poll(wait_fd, 1, timeoutMs);
+    revents = wait_fd[0].revents;
+    if (revents & POLLIN) {
+        int n = android_res_nresult(fd, rcode, buf, bufLen);
+        // Verify that android_res_nresult() closed the fd
+        char dummy;
+        EXPECT_EQ(-1, read(fd, &dummy, sizeof dummy));
+        EXPECT_EQ(EBADF, errno);
+        return n;
+    }
+
+    return -1;
+}
+
+std::vector<std::string> extractIpAddressAnswers(uint8_t* buf, size_t bufLen, int ipType) {
+    ns_msg handle;
+    if (ns_initparse((const uint8_t*) buf, bufLen, &handle) < 0) {
+        return {};
+    }
+    const int ancount = ns_msg_count(handle, ns_s_an);
+    ns_rr rr;
+    std::vector<std::string> answers;
+    for (int i = 0; i < ancount; i++) {
+        if (ns_parserr(&handle, ns_s_an, i, &rr) < 0) {
+            continue;
+        }
+        const uint8_t* rdata = ns_rr_rdata(rr);
+        char buffer[INET6_ADDRSTRLEN];
+        if (inet_ntop(ipType, (const char*) rdata, buffer, sizeof(buffer))) {
+            answers.push_back(buffer);
+        }
+    }
+    return answers;
+}
+
+void expectAnswersValid(int fd, int ipType, int expectedRcode) {
+    int rcode = -1;
+    uint8_t buf[MAXPACKET] = {};
+    int res = getAsyncResponse(fd, TIMEOUT_MS, &rcode, buf, MAXPACKET);
+    EXPECT_GE(res, 0);
+    EXPECT_EQ(rcode, expectedRcode);
+
+    if (expectedRcode == ns_r_noerror) {
+        auto answers = extractIpAddressAnswers(buf, res, ipType);
+        EXPECT_GE(answers.size(), 0U);
+        for (auto &answer : answers) {
+            char pton[PTON_MAX];
+            EXPECT_EQ(1, inet_pton(ipType, answer.c_str(), pton));
+        }
+    }
+}
+
+void expectAnswersNotValid(int fd, int expectedErrno) {
+    int rcode = -1;
+    uint8_t buf[MAXPACKET] = {};
+    int res = getAsyncResponse(fd, TIMEOUT_MS, &rcode, buf, MAXPACKET);
+    EXPECT_EQ(expectedErrno, res);
+}
+
+} // namespace
+
+TEST (NativeDnsAsyncTest, Async_Query) {
+    // V4
+    int fd1 = android_res_nquery(
+            NETWORK_UNSPECIFIED, "www.google.com", ns_c_in, ns_t_a, 0);
+    EXPECT_GE(fd1, 0);
+    int fd2 = android_res_nquery(
+            NETWORK_UNSPECIFIED, "www.youtube.com", ns_c_in, ns_t_a, 0);
+    EXPECT_GE(fd2, 0);
+    expectAnswersValid(fd2, AF_INET, ns_r_noerror);
+    expectAnswersValid(fd1, AF_INET, ns_r_noerror);
+
+    // V6
+    fd1 = android_res_nquery(
+            NETWORK_UNSPECIFIED, "www.google.com", ns_c_in, ns_t_aaaa, 0);
+    EXPECT_GE(fd1, 0);
+    fd2 = android_res_nquery(
+            NETWORK_UNSPECIFIED, "www.youtube.com", ns_c_in, ns_t_aaaa, 0);
+    EXPECT_GE(fd2, 0);
+    expectAnswersValid(fd2, AF_INET6, ns_r_noerror);
+    expectAnswersValid(fd1, AF_INET6, ns_r_noerror);
+}
+
+TEST (NativeDnsAsyncTest, Async_Send) {
+    // V4
+    uint8_t buf1[MAXPACKET] = {};
+    int len1 = res_mkquery(ns_o_query, "www.googleapis.com",
+            ns_c_in, ns_t_a, nullptr, 0, nullptr, buf1, sizeof(buf1));
+    EXPECT_GT(len1, 0);
+
+    uint8_t buf2[MAXPACKET] = {};
+    int len2 = res_mkquery(ns_o_query, "play.googleapis.com",
+            ns_c_in, ns_t_a, nullptr, 0, nullptr, buf2, sizeof(buf2));
+    EXPECT_GT(len2, 0);
+
+    int fd1 = android_res_nsend(NETWORK_UNSPECIFIED, buf1, len1, 0);
+    EXPECT_GE(fd1, 0);
+    int fd2 = android_res_nsend(NETWORK_UNSPECIFIED, buf2, len2, 0);
+    EXPECT_GE(fd2, 0);
+
+    expectAnswersValid(fd2, AF_INET, ns_r_noerror);
+    expectAnswersValid(fd1, AF_INET, ns_r_noerror);
+
+    // V6
+    memset(buf1, 0, sizeof(buf1));
+    memset(buf2, 0, sizeof(buf2));
+    len1 = res_mkquery(ns_o_query, "www.googleapis.com",
+            ns_c_in, ns_t_aaaa, nullptr, 0, nullptr, buf1, sizeof(buf1));
+    EXPECT_GT(len1, 0);
+    len2 = res_mkquery(ns_o_query, "play.googleapis.com",
+            ns_c_in, ns_t_aaaa, nullptr, 0, nullptr, buf2, sizeof(buf2));
+    EXPECT_GT(len2, 0);
+
+    fd1 = android_res_nsend(NETWORK_UNSPECIFIED, buf1, len1, 0);
+    EXPECT_GE(fd1, 0);
+    fd2 = android_res_nsend(NETWORK_UNSPECIFIED, buf2, len2, 0);
+    EXPECT_GE(fd2, 0);
+
+    expectAnswersValid(fd2, AF_INET6, ns_r_noerror);
+    expectAnswersValid(fd1, AF_INET6, ns_r_noerror);
+}
+
+TEST (NativeDnsAsyncTest, Async_NXDOMAIN) {
+    uint8_t buf[MAXPACKET] = {};
+    int len = res_mkquery(ns_o_query, "test1-nx.metric.gstatic.com",
+            ns_c_in, ns_t_a, nullptr, 0, nullptr, buf, sizeof(buf));
+    EXPECT_GT(len, 0);
+    int fd1 = android_res_nsend(NETWORK_UNSPECIFIED, buf, len, ANDROID_RESOLV_NO_CACHE_LOOKUP);
+    EXPECT_GE(fd1, 0);
+
+    len = res_mkquery(ns_o_query, "test2-nx.metric.gstatic.com",
+            ns_c_in, ns_t_a, nullptr, 0, nullptr, buf, sizeof(buf));
+    EXPECT_GT(len, 0);
+    int fd2 = android_res_nsend(NETWORK_UNSPECIFIED, buf, len, ANDROID_RESOLV_NO_CACHE_LOOKUP);
+    EXPECT_GE(fd2, 0);
+
+    expectAnswersValid(fd2, AF_INET, ns_r_nxdomain);
+    expectAnswersValid(fd1, AF_INET, ns_r_nxdomain);
+
+    fd1 = android_res_nquery(
+            NETWORK_UNSPECIFIED, "test3-nx.metric.gstatic.com",
+            ns_c_in, ns_t_aaaa, ANDROID_RESOLV_NO_CACHE_LOOKUP);
+    EXPECT_GE(fd1, 0);
+    fd2 = android_res_nquery(
+            NETWORK_UNSPECIFIED, "test4-nx.metric.gstatic.com",
+            ns_c_in, ns_t_aaaa, ANDROID_RESOLV_NO_CACHE_LOOKUP);
+    EXPECT_GE(fd2, 0);
+    expectAnswersValid(fd2, AF_INET6, ns_r_nxdomain);
+    expectAnswersValid(fd1, AF_INET6, ns_r_nxdomain);
+}
+
+TEST (NativeDnsAsyncTest, Async_Cancel) {
+    int fd = android_res_nquery(
+            NETWORK_UNSPECIFIED, "www.google.com", ns_c_in, ns_t_a, 0);
+    errno = 0;
+    android_res_cancel(fd);
+    int err = errno;
+    EXPECT_EQ(err, 0);
+    // DO NOT call cancel or result with the same fd more than once,
+    // otherwise it will hit fdsan double-close fd.
+}
+
+TEST (NativeDnsAsyncTest, Async_Query_MALFORMED) {
+    // Empty string to create BLOB and query, we will get empty result and rcode = 0
+    // on DNSTLS.
+    int fd = android_res_nquery(
+            NETWORK_UNSPECIFIED, "", ns_c_in, ns_t_a, 0);
+    EXPECT_GE(fd, 0);
+    expectAnswersValid(fd, AF_INET, ns_r_noerror);
+
+    std::string exceedingLabelQuery = "www." + std::string(70, 'g') + ".com";
+    std::string exceedingDomainQuery = "www." + std::string(255, 'g') + ".com";
+
+    fd = android_res_nquery(NETWORK_UNSPECIFIED,
+            exceedingLabelQuery.c_str(), ns_c_in, ns_t_a, 0);
+    EXPECT_EQ(-EMSGSIZE, fd);
+    fd = android_res_nquery(NETWORK_UNSPECIFIED,
+            exceedingDomainQuery.c_str(), ns_c_in, ns_t_a, 0);
+    EXPECT_EQ(-EMSGSIZE, fd);
+}
+
+TEST (NativeDnsAsyncTest, Async_Send_MALFORMED) {
+    uint8_t buf[10] = {};
+    // empty BLOB
+    int fd = android_res_nsend(NETWORK_UNSPECIFIED, buf, 10, 0);
+    EXPECT_GE(fd, 0);
+    expectAnswersNotValid(fd, -EINVAL);
+
+    std::vector<uint8_t> largeBuf(2 * MAXPACKET, 0);
+    // A buffer larger than 8KB
+    fd = android_res_nsend(
+            NETWORK_UNSPECIFIED, largeBuf.data(), largeBuf.size(), 0);
+    EXPECT_EQ(-EMSGSIZE, fd);
+
+    // 5000 bytes filled with 0. This returns EMSGSIZE because FrameworkListener limits the size of
+    // commands to 4096 bytes.
+    fd = android_res_nsend(NETWORK_UNSPECIFIED, largeBuf.data(), 5000, 0);
+    EXPECT_EQ(-EMSGSIZE, fd);
+
+    // 500 bytes filled with 0
+    fd = android_res_nsend(NETWORK_UNSPECIFIED, largeBuf.data(), 500, 0);
+    EXPECT_GE(fd, 0);
+    expectAnswersNotValid(fd, -EINVAL);
+
+    // 5000 bytes filled with 0xFF
+    std::vector<uint8_t> ffBuf(5000, 0xFF);
+    fd = android_res_nsend(
+            NETWORK_UNSPECIFIED, ffBuf.data(), ffBuf.size(), 0);
+    EXPECT_EQ(-EMSGSIZE, fd);
+
+    // 500 bytes filled with 0xFF
+    fd = android_res_nsend(NETWORK_UNSPECIFIED, ffBuf.data(), 500, 0);
+    EXPECT_GE(fd, 0);
+    expectAnswersNotValid(fd, -EINVAL);
+}
diff --git a/tests/cts/net/native/qtaguid/AndroidTest.xml b/tests/cts/net/native/qtaguid/AndroidTest.xml
index 7591c87..a55afe7 100644
--- a/tests/cts/net/native/qtaguid/AndroidTest.xml
+++ b/tests/cts/net/native/qtaguid/AndroidTest.xml
@@ -16,6 +16,8 @@
 <configuration description="Config for CTS Native Network xt_qtaguid test cases">
     <option name="test-suite-tag" value="cts" />
     <option name="config-descriptor:metadata" key="component" value="networking" />
+    <option name="config-descriptor:metadata" key="parameter" value="instant_app" />
+    <option name="config-descriptor:metadata" key="parameter" value="multi_abi" />
     <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
         <option name="cleanup" value="true" />
         <option name="push" value="CtsNativeNetTestCases->/data/local/tmp/CtsNativeNetTestCases" />
diff --git a/tests/cts/net/native/qtaguid/src/NativeQtaguidTest.cpp b/tests/cts/net/native/qtaguid/src/NativeQtaguidTest.cpp
index 1892a44..7dc6240 100644
--- a/tests/cts/net/native/qtaguid/src/NativeQtaguidTest.cpp
+++ b/tests/cts/net/native/qtaguid/src/NativeQtaguidTest.cpp
@@ -18,36 +18,29 @@
 #include <error.h>
 #include <errno.h>
 #include <inttypes.h>
+#include <fcntl.h>
 #include <string.h>
 #include <sys/socket.h>
-#include <sys/utsname.h>
 
 #include <gtest/gtest.h>
 #include <qtaguid/qtaguid.h>
 
-int hasQtaguidKernelSupport() {
-    struct utsname buf;
-    int kernel_version_major;
-    int kernel_version_minor;
-
-    int ret = uname(&buf);
-    if (ret) {
-        ret = -errno;
-        return ret;
-    }
-    char dummy;
-    ret = sscanf(buf.release, "%d.%d%c", &kernel_version_major, &kernel_version_minor, &dummy);
-    if (ret < 3)
-        return -EINVAL;
-
-    if ((kernel_version_major == 4 && kernel_version_minor < 9) ||
-        (kernel_version_major < 4)) {
-        return 1;
-    } else {
-        return access("/proc/net/xt_qtaguid/ctrl", F_OK) != -1;
-    }
+int canAccessQtaguidFile() {
+    int fd = open("/proc/net/xt_qtaguid/ctrl", O_RDONLY | O_CLOEXEC);
+    close(fd);
+    return fd != -1;
 }
 
+#define SKIP_IF_QTAGUID_NOT_SUPPORTED()                                                       \
+  do {                                                                                        \
+    int res = canAccessQtaguidFile();                                                      \
+    ASSERT_LE(0, res);                                                                        \
+    if (!res) {                                                                               \
+          GTEST_LOG_(INFO) << "This test is skipped since kernel may not have the module\n";  \
+          return;                                                                             \
+    }                                                                                         \
+  } while (0)
+
 int getCtrlSkInfo(int tag, uid_t uid, uint64_t* sk_addr, int* ref_cnt) {
     FILE *fp;
     fp = fopen("/proc/net/xt_qtaguid/ctrl", "r");
@@ -95,12 +88,8 @@
 }
 
 TEST (NativeQtaguidTest, close_socket_without_untag) {
-    int res = hasQtaguidKernelSupport();
-    ASSERT_LE(0, res);
-    if (!res) {
-          GTEST_LOG_(INFO) << "This test is skipped since kernel may not have the module\n";
-          return;
-    }
+    SKIP_IF_QTAGUID_NOT_SUPPORTED();
+
     int sockfd = socket(AF_INET, SOCK_STREAM, 0);
     uid_t uid = getuid();
     int tag = arc4random();
@@ -114,12 +103,8 @@
 }
 
 TEST (NativeQtaguidTest, close_socket_without_untag_ipv6) {
-    int res = hasQtaguidKernelSupport();
-    ASSERT_LE(0, res);
-    if (!res) {
-          GTEST_LOG_(INFO) << "This test is skipped since kernel may not have the module\n";
-          return;
-    }
+    SKIP_IF_QTAGUID_NOT_SUPPORTED();
+
     int sockfd = socket(AF_INET6, SOCK_STREAM, 0);
     uid_t uid = getuid();
     int tag = arc4random();
@@ -133,12 +118,8 @@
 }
 
 TEST (NativeQtaguidTest, no_socket_addr_leak) {
-    int res = hasQtaguidKernelSupport();
-    ASSERT_LE(0, res);
-    if (!res) {
-          GTEST_LOG_(INFO) << "This test is skipped since kernel may not have the module\n";
-          return;
-    }
+  SKIP_IF_QTAGUID_NOT_SUPPORTED();
+
   checkNoSocketPointerLeaks(AF_INET);
   checkNoSocketPointerLeaks(AF_INET6);
 }
diff --git a/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java b/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
index 810b5df..cb1aa09 100644
--- a/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
+++ b/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
@@ -23,12 +23,17 @@
 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
 import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
 import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
+import static android.os.MessageQueue.OnFileDescriptorEventListener.EVENT_INPUT;
 import static android.provider.Settings.Global.NETWORK_METERED_MULTIPATH_PREFERENCE;
+import static android.system.OsConstants.AF_INET;
+import static android.system.OsConstants.AF_INET6;
+import static android.system.OsConstants.AF_UNSPEC;
 
 import static com.android.compatibility.common.util.SystemUtil.runShellCommand;
 
 import android.app.Instrumentation;
 import android.app.PendingIntent;
+import android.app.UiAutomation;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.ContentResolver;
@@ -46,8 +51,10 @@
 import android.net.NetworkInfo.DetailedState;
 import android.net.NetworkInfo.State;
 import android.net.NetworkRequest;
+import android.net.SocketKeepalive;
 import android.net.wifi.WifiManager;
 import android.os.Looper;
+import android.os.MessageQueue;
 import android.os.SystemClock;
 import android.os.SystemProperties;
 import android.provider.Settings;
@@ -59,18 +66,19 @@
 
 import androidx.test.InstrumentationRegistry;
 
+import com.android.compatibility.common.util.SystemUtil;
 import com.android.internal.R;
 import com.android.internal.telephony.PhoneConstants;
 
 import libcore.io.Streams;
 
-import java.io.File;
-import java.io.FileNotFoundException;
+import java.io.FileDescriptor;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.InputStreamReader;
 import java.io.OutputStream;
 import java.net.HttpURLConnection;
+import java.net.Inet4Address;
 import java.net.Inet6Address;
 import java.net.InetAddress;
 import java.net.InetSocketAddress;
@@ -80,8 +88,8 @@
 import java.nio.charset.StandardCharsets;
 import java.util.Collection;
 import java.util.HashMap;
-import java.util.Scanner;
 import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Executor;
 import java.util.concurrent.LinkedBlockingQueue;
 import java.util.concurrent.TimeUnit;
 import java.util.regex.Matcher;
@@ -99,7 +107,11 @@
     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 CONNECT_TIMEOUT_MS = 2000;
+    private static final int KEEPALIVE_CALLBACK_TIMEOUT_MS = 2000;
+    private static final int KEEPALIVE_SOCKET_TIMEOUT_MS = 5000;
     private static final int SEND_BROADCAST_TIMEOUT = 30000;
+    private static final int MIN_KEEPALIVE_INTERVAL = 10;
     private static final int NETWORK_CHANGE_METEREDNESS_TIMEOUT = 5000;
     private static final int NUM_TRIES_MULTIPATH_PREF_CHECK = 20;
     private static final long INTERVAL_MULTIPATH_PREF_CHECK_MS = 500;
@@ -109,17 +121,6 @@
             "Host: " + TEST_HOST + "\r\n" +
             "Connection: keep-alive\r\n\r\n";
 
-    // Base path for IPv6 sysctls
-    private static final String IPV6_SYSCTL_DIR = "/proc/sys/net/ipv6/conf";
-
-    // Expected values for MIN|MAX_PLEN.
-    private static final int IPV6_WIFI_ACCEPT_RA_RT_INFO_MIN_PLEN = 48;
-    private static final int IPV6_WIFI_ACCEPT_RA_RT_INFO_MAX_PLEN = 64;
-
-    // Expected values for RFC 7559 router soliciations.
-    // Maximum number of router solicitations to send. -1 means no limit.
-    private static final int IPV6_WIFI_ROUTER_SOLICITATIONS = -1;
-
     // Action sent to ConnectivityActionReceiver when a network callback is sent via PendingIntent.
     private static final String NETWORK_CALLBACK_ACTION =
             "ConnectivityManagerTest.NetworkCallbackAction";
@@ -140,7 +141,8 @@
             new HashMap<Integer, NetworkConfig>();
     boolean mWifiConnectAttempted;
     private TestNetworkCallback mCellNetworkCallback;
-
+    private UiAutomation mUiAutomation;
+    private boolean mShellPermissionIdentityAdopted;
 
     @Override
     protected void setUp() throws Exception {
@@ -167,6 +169,8 @@
                 mNetworks.put(n.type, n);
             } catch (Exception e) {}
         }
+        mUiAutomation = mInstrumentation.getUiAutomation();
+        mShellPermissionIdentityAdopted = false;
     }
 
     @Override
@@ -178,6 +182,7 @@
         if (cellConnectAttempted()) {
             disconnectFromCell();
         }
+        dropShellPermissionIdentity();
         super.tearDown();
     }
 
@@ -683,7 +688,7 @@
 
         boolean connected = false;
         try {
-            assertTrue(mWifiManager.setWifiEnabled(true));
+            SystemUtil.runShellCommand("svc wifi enable");
             // Ensure we get both an onAvailable callback and a CONNECTIVITY_ACTION.
             wifiNetwork = callback.waitForAvailable();
             assertNotNull(wifiNetwork);
@@ -749,7 +754,7 @@
 
         boolean disconnected = false;
         try {
-            assertTrue(mWifiManager.setWifiEnabled(false));
+            SystemUtil.runShellCommand("svc wifi disable");
             // Ensure we get both an onLost callback and a CONNECTIVITY_ACTION.
             lostWifiNetwork = callback.waitForLost();
             assertNotNull(lostWifiNetwork);
@@ -910,60 +915,6 @@
         } catch (SecurityException expected) {}
     }
 
-    private Scanner makeWifiSysctlScanner(String key) throws FileNotFoundException {
-        Network network = ensureWifiConnected();
-        String iface = mCm.getLinkProperties(network).getInterfaceName();
-        String path = IPV6_SYSCTL_DIR + "/" + iface + "/" + key;
-        return new Scanner(new File(path));
-    }
-
-    /** Verify that accept_ra_rt_info_min_plen exists and is set to the expected value */
-    public void testAcceptRaRtInfoMinPlen() throws Exception {
-        if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_WIFI)) {
-            Log.i(TAG, "testConnectivityChanged_manifestRequestOnlyPreN_shouldReceiveIntent cannot execute unless device supports WiFi");
-            return;
-        }
-        Scanner s = makeWifiSysctlScanner("accept_ra_rt_info_min_plen");
-        assertEquals(IPV6_WIFI_ACCEPT_RA_RT_INFO_MIN_PLEN, s.nextInt());
-    }
-
-    /** Verify that accept_ra_rt_info_max_plen exists and is set to the expected value */
-    public void testAcceptRaRtInfoMaxPlen() throws Exception {
-        if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_WIFI)) {
-            Log.i(TAG, "testConnectivityChanged_manifestRequestOnlyPreN_shouldReceiveIntent cannot execute unless device supports WiFi");
-            return;
-        }
-        Scanner s = makeWifiSysctlScanner("accept_ra_rt_info_max_plen");
-        assertEquals(IPV6_WIFI_ACCEPT_RA_RT_INFO_MAX_PLEN, s.nextInt());
-    }
-
-    /** Verify that router_solicitations exists and is set to the expected value */
-    public void testRouterSolicitations() throws Exception {
-        if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_WIFI)) {
-            Log.i(TAG, "testConnectivityChanged_manifestRequestOnlyPreN_shouldReceiveIntent cannot execute unless device supports WiFi");
-            return;
-        }
-        Scanner s = makeWifiSysctlScanner("router_solicitations");
-        assertEquals(IPV6_WIFI_ROUTER_SOLICITATIONS, s.nextInt());
-    }
-
-    /** Verify that router_solicitation_max_interval exists and is in an acceptable interval */
-    public void testRouterSolicitationMaxInterval() throws Exception {
-        if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_WIFI)) {
-            Log.i(TAG, "testConnectivityChanged_manifestRequestOnlyPreN_shouldReceiveIntent cannot execute unless device supports WiFi");
-            return;
-        }
-        Scanner s = makeWifiSysctlScanner("router_solicitation_max_interval");
-        int interval = s.nextInt();
-        // Verify we're in the interval [15 minutes, 60 minutes]. Lower values may adversely
-        // impact battery life and higher values can decrease the probability of detecting
-        // network changes.
-        final int lowerBoundSec = 15 * 60;
-        final int upperBoundSec = 60 * 60;
-        assertTrue(lowerBoundSec <= interval);
-        assertTrue(interval <= upperBoundSec);
-    }
-
     // Returns "true", "false" or "none"
     private String getWifiMeteredStatus(String ssid) throws Exception {
         // Interestingly giving the SSID as an argument to list wifi-networks
@@ -1105,4 +1056,226 @@
             setWifiMeteredStatus(ssid, oldMeteredSetting);
         }
     }
+
+    // TODO: move the following socket keep alive test to dedicated test class.
+    /**
+     * Callback used in tcp keepalive offload that allows caller to wait callback fires.
+     */
+    private static class TestSocketKeepaliveCallback extends SocketKeepalive.Callback {
+        public enum CallbackType { ON_STARTED, ON_STOPPED, ON_ERROR };
+
+        public static class CallbackValue {
+            public final CallbackType callbackType;
+            public final int error;
+
+            private CallbackValue(final CallbackType type, final int error) {
+                this.callbackType = type;
+                this.error = error;
+            }
+
+            public static class OnStartedCallback extends CallbackValue {
+                OnStartedCallback() { super(CallbackType.ON_STARTED, 0); }
+            }
+
+            public static class OnStoppedCallback extends CallbackValue {
+                OnStoppedCallback() { super(CallbackType.ON_STOPPED, 0); }
+            }
+
+            public static class OnErrorCallback extends CallbackValue {
+                OnErrorCallback(final int error) { super(CallbackType.ON_ERROR, error); }
+            }
+
+            @Override
+            public boolean equals(Object o) {
+                return o.getClass() == this.getClass()
+                        && this.callbackType == ((CallbackValue) o).callbackType
+                        && this.error == ((CallbackValue) o).error;
+            }
+
+            @Override
+            public String toString() {
+                return String.format("%s(%s, %d)", getClass().getSimpleName(), callbackType, error);
+            }
+        }
+
+        private final LinkedBlockingQueue<CallbackValue> mCallbacks = new LinkedBlockingQueue<>();
+
+        @Override
+        public void onStarted() {
+            mCallbacks.add(new CallbackValue.OnStartedCallback());
+        }
+
+        @Override
+        public void onStopped() {
+            mCallbacks.add(new CallbackValue.OnStoppedCallback());
+        }
+
+        @Override
+        public void onError(final int error) {
+            mCallbacks.add(new CallbackValue.OnErrorCallback(error));
+        }
+
+        public CallbackValue pollCallback() {
+            try {
+                return mCallbacks.poll(KEEPALIVE_CALLBACK_TIMEOUT_MS,
+                        TimeUnit.MILLISECONDS);
+            } catch (InterruptedException e) {
+                fail("Callback not seen after " + KEEPALIVE_CALLBACK_TIMEOUT_MS + " ms");
+            }
+            return null;
+        }
+        private void expectCallback(CallbackValue expectedCallback) {
+            final CallbackValue actualCallback = pollCallback();
+            assertEquals(expectedCallback, actualCallback);
+        }
+
+        public void expectStarted() {
+            expectCallback(new CallbackValue.OnStartedCallback());
+        }
+
+        public void expectStopped() {
+            expectCallback(new CallbackValue.OnStoppedCallback());
+        }
+
+        public void expectError(int error) {
+            expectCallback(new CallbackValue.OnErrorCallback(error));
+        }
+    }
+
+    private InetAddress getAddrByName(final String hostname, final int family) throws Exception {
+        final InetAddress[] allAddrs = InetAddress.getAllByName(hostname);
+        for (InetAddress addr : allAddrs) {
+            if (family == AF_INET && addr instanceof Inet4Address) return addr;
+
+            if (family == AF_INET6 && addr instanceof Inet6Address) return addr;
+
+            if (family == AF_UNSPEC) return addr;
+        }
+        return null;
+    }
+
+    private Socket getConnectedSocket(final Network network, final String host, final int port,
+            final int socketTimeOut, final int family) throws Exception {
+        final Socket s = network.getSocketFactory().createSocket();
+        try {
+            final InetAddress addr = getAddrByName(host, family);
+            if (addr == null) fail("Fail to get destination address for " + family);
+
+            final InetSocketAddress sockAddr = new InetSocketAddress(addr, port);
+            s.setSoTimeout(socketTimeOut);
+            s.connect(sockAddr, CONNECT_TIMEOUT_MS);
+        } catch (Exception e) {
+            s.close();
+            throw e;
+        }
+        return s;
+    }
+
+    private boolean isKeepaliveSupported() throws Exception {
+        final Network network = ensureWifiConnected();
+        final Executor executor = mContext.getMainExecutor();
+        final TestSocketKeepaliveCallback callback = new TestSocketKeepaliveCallback();
+        try (Socket s = getConnectedSocket(network, TEST_HOST,
+                HTTP_PORT, KEEPALIVE_SOCKET_TIMEOUT_MS, AF_INET);
+                SocketKeepalive sk = mCm.createSocketKeepalive(network, s, executor, callback)) {
+            sk.start(MIN_KEEPALIVE_INTERVAL);
+            final TestSocketKeepaliveCallback.CallbackValue result = callback.pollCallback();
+            switch (result.callbackType) {
+                case ON_STARTED:
+                    sk.stop();
+                    callback.expectStopped();
+                    return true;
+                case ON_ERROR:
+                    if (result.error == SocketKeepalive.ERROR_UNSUPPORTED) return false;
+                    // else fallthrough.
+                default:
+                    fail("Got unexpected callback: " + result);
+                    return false;
+            }
+        }
+    }
+
+    private void adoptShellPermissionIdentity() {
+        mUiAutomation.adoptShellPermissionIdentity();
+        mShellPermissionIdentityAdopted = true;
+    }
+
+    private void dropShellPermissionIdentity() {
+        if (mShellPermissionIdentityAdopted) {
+            mUiAutomation.dropShellPermissionIdentity();
+            mShellPermissionIdentityAdopted = false;
+        }
+    }
+
+    public void testCreateTcpKeepalive() throws Exception {
+        adoptShellPermissionIdentity();
+
+        if (!isKeepaliveSupported()) return;
+
+        final Network network = ensureWifiConnected();
+        final byte[] requestBytes = HTTP_REQUEST.getBytes("UTF-8");
+        // So far only ipv4 tcp keepalive offload is supported.
+        // TODO: add test case for ipv6 tcp keepalive offload when it is supported.
+        try (Socket s = getConnectedSocket(network, TEST_HOST, HTTP_PORT,
+                KEEPALIVE_SOCKET_TIMEOUT_MS, AF_INET)) {
+
+            // Should able to start keep alive offload when socket is idle.
+            final Executor executor = mContext.getMainExecutor();
+            final TestSocketKeepaliveCallback callback = new TestSocketKeepaliveCallback();
+            try (SocketKeepalive sk = mCm.createSocketKeepalive(network, s, executor, callback)) {
+                sk.start(MIN_KEEPALIVE_INTERVAL);
+                callback.expectStarted();
+
+                // App should not able to write during keepalive offload.
+                final OutputStream out = s.getOutputStream();
+                try {
+                    out.write(requestBytes);
+                    fail("Should not able to write");
+                } catch (IOException e) { }
+                // App should not able to read during keepalive offload.
+                final InputStream in = s.getInputStream();
+                byte[] responseBytes = new byte[4096];
+                try {
+                    in.read(responseBytes);
+                    fail("Should not able to read");
+                } catch (IOException e) { }
+
+                // Stop.
+                sk.stop();
+                callback.expectStopped();
+            }
+
+            // Ensure socket is still connected.
+            assertTrue(s.isConnected());
+            assertFalse(s.isClosed());
+
+            // Let socket be not idle.
+            try {
+                final OutputStream out = s.getOutputStream();
+                out.write(requestBytes);
+            } catch (IOException e) {
+                fail("Failed to write data " + e);
+            }
+            // Make sure response data arrives.
+            final MessageQueue fdHandlerQueue = Looper.getMainLooper().getQueue();
+            final FileDescriptor fd = s.getFileDescriptor$();
+            final CountDownLatch mOnReceiveLatch = new CountDownLatch(1);
+            fdHandlerQueue.addOnFileDescriptorEventListener(fd, EVENT_INPUT, (readyFd, events) -> {
+                mOnReceiveLatch.countDown();
+                return 0; // Unregister listener.
+            });
+            if (!mOnReceiveLatch.await(2, TimeUnit.SECONDS)) {
+                fdHandlerQueue.removeOnFileDescriptorEventListener(fd);
+                fail("Timeout: no response data");
+            }
+
+            // Should get ERROR_SOCKET_NOT_IDLE because there is still data in the receive queue
+            // that has not been read.
+            try (SocketKeepalive sk = mCm.createSocketKeepalive(network, s, executor, callback)) {
+                sk.start(MIN_KEEPALIVE_INTERVAL);
+                callback.expectError(SocketKeepalive.ERROR_SOCKET_NOT_IDLE);
+            }
+
+        }
+    }
 }
diff --git a/tests/cts/net/src/android/net/cts/DnsResolverTest.java b/tests/cts/net/src/android/net/cts/DnsResolverTest.java
new file mode 100644
index 0000000..0ff6cd8
--- /dev/null
+++ b/tests/cts/net/src/android/net/cts/DnsResolverTest.java
@@ -0,0 +1,576 @@
+/*
+ * Copyright (C) 2019 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 static android.net.DnsResolver.CLASS_IN;
+import static android.net.DnsResolver.FLAG_EMPTY;
+import static android.net.DnsResolver.FLAG_NO_CACHE_LOOKUP;
+import static android.net.DnsResolver.TYPE_A;
+import static android.net.DnsResolver.TYPE_AAAA;
+import static android.system.OsConstants.EBADF;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.net.ConnectivityManager;
+import android.net.DnsPacket;
+import android.net.DnsResolver;
+import android.net.Network;
+import android.net.NetworkCapabilities;
+import android.net.ParseException;
+import android.os.CancellationSignal;
+import android.os.Handler;
+import android.os.Looper;
+import android.system.ErrnoException;
+import android.test.AndroidTestCase;
+import android.util.Log;
+
+import java.net.InetAddress;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Executor;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicReference;
+
+public class DnsResolverTest extends AndroidTestCase {
+    private static final String TAG = "DnsResolverTest";
+    private static final char[] HEX_CHARS = {
+            '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
+    };
+    static final int TIMEOUT_MS = 12_000;
+    static final int CANCEL_RETRY_TIMES = 5;
+
+    private ConnectivityManager mCM;
+    private Executor mExecutor;
+    private DnsResolver mDns;
+
+    protected void setUp() throws Exception {
+        super.setUp();
+        mCM = (ConnectivityManager) getContext().getSystemService(Context.CONNECTIVITY_SERVICE);
+        mDns = DnsResolver.getInstance();
+        mExecutor = new Handler(Looper.getMainLooper())::post;
+    }
+
+    private static String byteArrayToHexString(byte[] bytes) {
+        char[] hexChars = new char[bytes.length * 2];
+        for (int i = 0; i < bytes.length; ++i) {
+            int b = bytes[i] & 0xFF;
+            hexChars[i * 2] = HEX_CHARS[b >>> 4];
+            hexChars[i * 2 + 1] = HEX_CHARS[b & 0x0F];
+        }
+        return new String(hexChars);
+    }
+
+    private Network[] getTestableNetworks() {
+        final ArrayList<Network> testableNetworks = new ArrayList<Network>();
+        for (Network network : mCM.getAllNetworks()) {
+            final NetworkCapabilities nc = mCM.getNetworkCapabilities(network);
+            if (nc != null
+                    && nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)
+                    && nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)) {
+                testableNetworks.add(network);
+            }
+        }
+
+        assertTrue(
+                "This test requires that at least one network be connected. " +
+                        "Please ensure that the device is connected to a network.",
+                testableNetworks.size() >= 1);
+        return testableNetworks.toArray(new Network[0]);
+    }
+
+    public void testQueryWithInetAddressCallback() {
+        final String dname = "www.google.com";
+        final String msg = "Query with InetAddressAnswerCallback " + dname;
+        for (Network network : getTestableNetworks()) {
+            final CountDownLatch latch = new CountDownLatch(1);
+            final AtomicReference<List<InetAddress>> answers = new AtomicReference<>();
+            final DnsResolver.InetAddressAnswerCallback callback =
+                    new DnsResolver.InetAddressAnswerCallback() {
+                @Override
+                public void onAnswer(@NonNull List<InetAddress> answerList) {
+                    answers.set(answerList);
+                    for (InetAddress addr : answerList) {
+                        Log.d(TAG, "Reported addr: " + addr.toString());
+                    }
+                    latch.countDown();
+                }
+
+                @Override
+                public void onParseException(@NonNull ParseException e) {
+                    fail(msg + e.getMessage());
+                }
+
+                @Override
+                public void onQueryException(@NonNull ErrnoException e) {
+                    fail(msg + e.getMessage());
+                }
+            };
+            mDns.query(network, dname, CLASS_IN, TYPE_A, FLAG_NO_CACHE_LOOKUP,
+                    mExecutor, null, callback);
+            try {
+                assertTrue(msg + " but no valid answer after " + TIMEOUT_MS + "ms.",
+                        latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+                assertGreaterThan(msg + " returned 0 result", answers.get().size(), 0);
+            } catch (InterruptedException e) {
+                fail(msg + " Waiting for DNS lookup was interrupted");
+            }
+        }
+    }
+
+    static private void assertGreaterThan(String msg, int first, int second) {
+        assertTrue(msg + " Excepted " + first + " to be greater than " + second, first > second);
+    }
+
+    static private void assertValidAnswer(String msg, @NonNull DnsAnswer ans) {
+        // Check rcode field.(0, No error condition).
+        assertTrue(msg + " Response error, rcode: " + ans.getRcode(), ans.getRcode() == 0);
+        // Check answer counts.
+        assertGreaterThan(msg + " No answer found", ans.getANCount(), 0);
+        // Check question counts.
+        assertGreaterThan(msg + " No question found", ans.getQDCount(), 0);
+    }
+
+    static private void assertValidEmptyAnswer(String msg, @NonNull DnsAnswer ans) {
+        // Check rcode field.(0, No error condition).
+        assertTrue(msg + " Response error, rcode: " + ans.getRcode(), ans.getRcode() == 0);
+        // Check answer counts. Expect 0 answer.
+        assertTrue(msg + " Not an empty answer", ans.getANCount() == 0);
+        // Check question counts.
+        assertGreaterThan(msg + " No question found", ans.getQDCount(), 0);
+    }
+
+    private static class DnsAnswer extends DnsPacket {
+        DnsAnswer(@NonNull byte[] data) throws ParseException {
+            super(data);
+            // Check QR field.(query (0), or a response (1)).
+            if ((mHeader.flags & (1 << 15)) == 0) {
+                throw new ParseException("Not an answer packet");
+            }
+        }
+
+        int getRcode() {
+            return mHeader.rcode;
+        }
+        int getANCount(){
+            return mHeader.getRecordCount(ANSECTION);
+        }
+        int getQDCount(){
+            return mHeader.getRecordCount(QDSECTION);
+        }
+    }
+
+    class RawAnswerCallbackImpl extends DnsResolver.RawAnswerCallback {
+        private final CountDownLatch mLatch = new CountDownLatch(1);
+        private final String mMsg;
+        private final int mTimeout;
+
+        RawAnswerCallbackImpl(@NonNull String msg, int timeout) {
+            this.mMsg = msg;
+            this.mTimeout = timeout;
+        }
+
+        RawAnswerCallbackImpl(@NonNull String msg) {
+            this(msg, TIMEOUT_MS);
+        }
+
+        public boolean waitForAnswer() throws InterruptedException {
+            return mLatch.await(mTimeout, TimeUnit.MILLISECONDS);
+        }
+
+        @Override
+        public void onAnswer(@NonNull byte[] answer) {
+            try {
+                assertValidAnswer(mMsg, new DnsAnswer(answer));
+                Log.d(TAG, "Reported blob: " + byteArrayToHexString(answer));
+                mLatch.countDown();
+            } catch (ParseException e) {
+                fail(mMsg + e.getMessage());
+            }
+        }
+
+        @Override
+        public void onParseException(@NonNull ParseException e) {
+            fail(mMsg + e.getMessage());
+        }
+
+        @Override
+        public void onQueryException(@NonNull ErrnoException e) {
+            fail(mMsg + e.getMessage());
+        }
+    }
+
+    public void testQueryWithRawAnswerCallback() {
+        final String dname = "www.google.com";
+        final String msg = "Query with RawAnswerCallback " + dname;
+        for (Network network : getTestableNetworks()) {
+            final RawAnswerCallbackImpl callback = new RawAnswerCallbackImpl(msg);
+            mDns.query(network, dname, CLASS_IN, TYPE_AAAA, FLAG_NO_CACHE_LOOKUP,
+                    mExecutor, null, callback);
+            try {
+                assertTrue(msg + " but no answer after " + TIMEOUT_MS + "ms.",
+                        callback.waitForAnswer());
+            } catch (InterruptedException e) {
+                fail(msg + " Waiting for DNS lookup was interrupted");
+            }
+        }
+    }
+
+    public void testQueryBlobWithRawAnswerCallback() {
+        final byte[] blob = new byte[]{
+            /* Header */
+            0x55, 0x66, /* Transaction ID */
+            0x01, 0x00, /* Flags */
+            0x00, 0x01, /* Questions */
+            0x00, 0x00, /* Answer RRs */
+            0x00, 0x00, /* Authority RRs */
+            0x00, 0x00, /* Additional RRs */
+            /* Queries */
+            0x03, 0x77, 0x77, 0x77, 0x06, 0x67, 0x6F, 0x6F, 0x67, 0x6c, 0x65,
+            0x03, 0x63, 0x6f, 0x6d, 0x00, /* Name */
+            0x00, 0x01, /* Type */
+            0x00, 0x01  /* Class */
+        };
+        final String msg = "Query with RawAnswerCallback " + byteArrayToHexString(blob);
+        for (Network network : getTestableNetworks()) {
+            final RawAnswerCallbackImpl callback = new RawAnswerCallbackImpl(msg);
+            mDns.query(network, blob, FLAG_NO_CACHE_LOOKUP, mExecutor, null, callback);
+            try {
+                assertTrue(msg + " but no answer after " + TIMEOUT_MS + "ms.",
+                        callback.waitForAnswer());
+            } catch (InterruptedException e) {
+                fail(msg + " Waiting for DNS lookup was interrupted");
+            }
+        }
+    }
+
+    public void testQueryRoot() {
+        final String dname = "";
+        final String msg = "Query with RawAnswerCallback empty dname(ROOT) ";
+        for (Network network : getTestableNetworks()) {
+            final CountDownLatch latch = new CountDownLatch(1);
+            final DnsResolver.RawAnswerCallback callback = new DnsResolver.RawAnswerCallback() {
+                @Override
+                public void onAnswer(@NonNull byte[] answer) {
+                    try {
+                        // Except no answer record because of querying with empty dname(ROOT)
+                        assertValidEmptyAnswer(msg, new DnsAnswer(answer));
+                        latch.countDown();
+                    } catch (ParseException e) {
+                        fail(msg + e.getMessage());
+                    }
+                }
+
+                @Override
+                public void onParseException(@NonNull ParseException e) {
+                    fail(msg + e.getMessage());
+                }
+
+                @Override
+                public void onQueryException(@NonNull ErrnoException e) {
+                    fail(msg + e.getMessage());
+                }
+            };
+            mDns.query(network, dname, CLASS_IN, TYPE_AAAA, FLAG_NO_CACHE_LOOKUP,
+                    mExecutor, null, callback);
+            try {
+                assertTrue(msg + "but no answer after " + TIMEOUT_MS + "ms.",
+                        latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+            } catch (InterruptedException e) {
+                fail(msg + "Waiting for DNS lookup was interrupted");
+            }
+        }
+    }
+
+    public void testQueryNXDomain() {
+        final String dname = "test1-nx.metric.gstatic.com";
+        final String msg = "Query with InetAddressAnswerCallback " + dname;
+        for (Network network : getTestableNetworks()) {
+            final CountDownLatch latch = new CountDownLatch(1);
+            final DnsResolver.InetAddressAnswerCallback callback =
+                    new DnsResolver.InetAddressAnswerCallback() {
+                @Override
+                public void onAnswer(@NonNull List<InetAddress> answerList) {
+                    if (answerList.size() == 0) {
+                        latch.countDown();
+                        return;
+                    }
+                    fail(msg + " but get valid answers");
+                }
+
+                @Override
+                public void onParseException(@NonNull ParseException e) {
+                    fail(msg + e.getMessage());
+                }
+
+                @Override
+                public void onQueryException(@NonNull ErrnoException e) {
+                    fail(msg + e.getMessage());
+                }
+            };
+            mDns.query(network, dname, CLASS_IN, TYPE_AAAA, FLAG_NO_CACHE_LOOKUP,
+                    mExecutor, null, callback);
+            try {
+                assertTrue(msg + " but no answer after " + TIMEOUT_MS + "ms.",
+                        latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+            } catch (InterruptedException e) {
+                fail(msg + " Waiting for DNS lookup was interrupted");
+            }
+        }
+    }
+
+    /**
+     * A query callback that ensures that the query is cancelled and that onAnswer is never
+     * called. If the query succeeds before it is cancelled, needRetry will return true so the
+     * test can retry.
+     */
+    class VerifyCancelCallback extends DnsResolver.RawAnswerCallback {
+        private static final int CANCEL_TIMEOUT = 3_000;
+
+        private final CountDownLatch mLatch = new CountDownLatch(1);
+        private final String mMsg;
+        private final CancellationSignal mCancelSignal;
+
+        VerifyCancelCallback(@NonNull String msg, @NonNull CancellationSignal cancelSignal) {
+            this.mMsg = msg;
+            this.mCancelSignal = cancelSignal;
+        }
+
+        public boolean needRetry() throws InterruptedException {
+            return mLatch.await(CANCEL_TIMEOUT, TimeUnit.MILLISECONDS);
+        }
+
+        @Override
+        public void onAnswer(@NonNull byte[] answer) {
+            if (mCancelSignal.isCanceled()) {
+                fail(mMsg + " should not have returned any answers");
+            }
+            mLatch.countDown();
+        }
+
+        @Override
+        public void onParseException(@NonNull ParseException e) {
+            fail(mMsg + e.getMessage());
+        }
+
+        @Override
+        public void onQueryException(@NonNull ErrnoException e) {
+            fail(mMsg + e.getMessage());
+        }
+    }
+
+    public void testQueryCancel() throws ErrnoException {
+        final String dname = "www.google.com";
+        final String msg = "Test cancel query " + dname;
+        // Start a DNS query and the cancel it immediately. Use VerifyCancelCallback to expect
+        // that the query is cancelled before it succeeds. If it is not cancelled before it
+        // succeeds, retry the test until it is.
+        for (Network network : getTestableNetworks()) {
+            boolean retry = false;
+            int round = 0;
+            do {
+                if (++round > CANCEL_RETRY_TIMES) {
+                    fail(msg + " cancel failed " + CANCEL_RETRY_TIMES + " times");
+                }
+                final CountDownLatch latch = new CountDownLatch(1);
+                final CancellationSignal cancelSignal = new CancellationSignal();
+                final VerifyCancelCallback callback = new VerifyCancelCallback(msg, cancelSignal);
+                mDns.query(network, dname, CLASS_IN, TYPE_AAAA, FLAG_EMPTY,
+                        mExecutor, cancelSignal, callback);
+                mExecutor.execute(() -> {
+                    cancelSignal.cancel();
+                    latch.countDown();
+                });
+                try {
+                    retry = callback.needRetry();
+                    assertTrue(msg + " query was not cancelled",
+                        latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+                } catch (InterruptedException e) {
+                    fail(msg + "Waiting for DNS lookup was interrupted");
+                }
+            } while (retry);
+        }
+    }
+
+    public void testQueryBlobCancel() throws ErrnoException {
+        final byte[] blob = new byte[]{
+            /* Header */
+            0x55, 0x66, /* Transaction ID */
+            0x01, 0x00, /* Flags */
+            0x00, 0x01, /* Questions */
+            0x00, 0x00, /* Answer RRs */
+            0x00, 0x00, /* Authority RRs */
+            0x00, 0x00, /* Additional RRs */
+            /* Queries */
+            0x03, 0x77, 0x77, 0x77, 0x06, 0x67, 0x6F, 0x6F, 0x67, 0x6c, 0x65,
+            0x03, 0x63, 0x6f, 0x6d, 0x00, /* Name */
+            0x00, 0x01, /* Type */
+            0x00, 0x01  /* Class */
+        };
+        final String msg = "Test cancel raw Query " + byteArrayToHexString(blob);
+        // Start a DNS query and the cancel it immediately. Use VerifyCancelCallback to expect
+        // that the query is cancelled before it succeeds. If it is not cancelled before it
+        // succeeds, retry the test until it is.
+        for (Network network : getTestableNetworks()) {
+            boolean retry = false;
+            int round = 0;
+            do {
+                if (++round > CANCEL_RETRY_TIMES) {
+                    fail(msg + " cancel failed " + CANCEL_RETRY_TIMES + " times");
+                }
+                final CountDownLatch latch = new CountDownLatch(1);
+                final CancellationSignal cancelSignal = new CancellationSignal();
+                final VerifyCancelCallback callback = new VerifyCancelCallback(msg, cancelSignal);
+                mDns.query(network, blob, FLAG_EMPTY, mExecutor, cancelSignal, callback);
+                mExecutor.execute(() -> {
+                    cancelSignal.cancel();
+                    latch.countDown();
+                });
+                try {
+                    retry = callback.needRetry();
+                    assertTrue(msg + " cancel is not done",
+                        latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+                } catch (InterruptedException e) {
+                    fail(msg + " Waiting for DNS lookup was interrupted");
+                }
+            } while (retry);
+        }
+    }
+
+    public void testCancelBeforeQuery() throws ErrnoException {
+        final String dname = "www.google.com";
+        final String msg = "Test cancelled query " + dname;
+        for (Network network : getTestableNetworks()) {
+            final RawAnswerCallbackImpl callback = new RawAnswerCallbackImpl(msg, 3_000);
+            final CancellationSignal cancelSignal = new CancellationSignal();
+            cancelSignal.cancel();
+            mDns.query(network, dname, CLASS_IN, TYPE_AAAA, FLAG_EMPTY,
+                    mExecutor, cancelSignal, callback);
+            try {
+                assertTrue(msg + " should not return any answers",
+                        !callback.waitForAnswer());
+            } catch (InterruptedException e) {
+                fail(msg + " Waiting for DNS lookup was interrupted");
+            }
+        }
+    }
+
+    /**
+     * A query callback for InetAddress that ensures that the query is
+     * cancelled and that onAnswer is never called. If the query succeeds
+     * before it is cancelled, needRetry will return true so the
+     * test can retry.
+     */
+    class VerifyCancelInetAddressCallback extends DnsResolver.InetAddressAnswerCallback {
+        private static final int CANCEL_TIMEOUT = 3_000;
+
+        private final CountDownLatch mLatch = new CountDownLatch(1);
+        private final String mMsg;
+        private final List<InetAddress> mAnswers;
+        private final CancellationSignal mCancelSignal;
+
+        VerifyCancelInetAddressCallback(@NonNull String msg, @Nullable CancellationSignal cancel) {
+            this.mMsg = msg;
+            this.mCancelSignal = cancel;
+            mAnswers = new ArrayList<>();
+        }
+
+        public boolean waitForAnswer() throws InterruptedException {
+            return mLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS);
+        }
+
+        public boolean needRetry() throws InterruptedException {
+            return mLatch.await(CANCEL_TIMEOUT, TimeUnit.MILLISECONDS);
+        }
+
+        public boolean isAnswerEmpty() {
+            return mAnswers.isEmpty();
+        }
+
+        @Override
+        public void onAnswer(@NonNull List<InetAddress> answerList) {
+            if (mCancelSignal != null && mCancelSignal.isCanceled()) {
+                fail(mMsg + " should not have returned any answers");
+            }
+            mAnswers.clear();
+            mAnswers.addAll(answerList);
+            mLatch.countDown();
+        }
+
+        @Override
+        public void onParseException(@NonNull ParseException e) {
+            fail(mMsg + e.getMessage());
+        }
+
+        @Override
+        public void onQueryException(@NonNull ErrnoException e) {
+            fail(mMsg + e.getMessage());
+        }
+    }
+
+    public void testQueryForInetAddress() {
+        final String dname = "www.google.com";
+        final String msg = "Test query for InetAddress " + dname;
+        for (Network network : getTestableNetworks()) {
+            final VerifyCancelInetAddressCallback callback =
+                    new VerifyCancelInetAddressCallback(msg, null);
+            mDns.query(network, dname, FLAG_NO_CACHE_LOOKUP,
+                    mExecutor, null, callback);
+            try {
+                assertTrue(msg + " but no answer after " + TIMEOUT_MS + "ms.",
+                        callback.waitForAnswer());
+                assertTrue(msg + " returned 0 results", !callback.isAnswerEmpty());
+            } catch (InterruptedException e) {
+                fail(msg + " Waiting for DNS lookup was interrupted");
+            }
+        }
+    }
+
+    public void testQueryCancelForInetAddress() throws ErrnoException {
+        final String dname = "www.google.com";
+        final String msg = "Test cancel query for InetAddress " + dname;
+        // Start a DNS query and the cancel it immediately. Use VerifyCancelCallback to expect
+        // that the query is cancelled before it succeeds. If it is not cancelled before it
+        // succeeds, retry the test until it is.
+        for (Network network : getTestableNetworks()) {
+            boolean retry = false;
+            int round = 0;
+            do {
+                if (++round > CANCEL_RETRY_TIMES) {
+                    fail(msg + " cancel failed " + CANCEL_RETRY_TIMES + " times");
+                }
+                final CountDownLatch latch = new CountDownLatch(1);
+                final CancellationSignal cancelSignal = new CancellationSignal();
+                final VerifyCancelInetAddressCallback callback =
+                        new VerifyCancelInetAddressCallback(msg, cancelSignal);
+                mDns.query(network, dname, FLAG_EMPTY, mExecutor, cancelSignal, callback);
+                mExecutor.execute(() -> {
+                    cancelSignal.cancel();
+                    latch.countDown();
+                });
+                try {
+                    retry = callback.needRetry();
+                    assertTrue(msg + " query was not cancelled",
+                        latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+                } catch (InterruptedException e) {
+                    fail(msg + "Waiting for DNS lookup was interrupted");
+                }
+            } while (retry);
+        }
+    }
+}
diff --git a/tests/cts/net/src/android/net/cts/DnsTest.java b/tests/cts/net/src/android/net/cts/DnsTest.java
index 84231c2..746dcb0 100644
--- a/tests/cts/net/src/android/net/cts/DnsTest.java
+++ b/tests/cts/net/src/android/net/cts/DnsTest.java
@@ -287,7 +287,7 @@
         final NetworkCallback callback = new NetworkCallback() {
             @Override
             public void onLinkPropertiesChanged(Network network, LinkProperties lp) {
-                if (lp.hasGlobalIPv6Address()) {
+                if (lp.hasGlobalIpv6Address()) {
                     latch.countDown();
                 }
             }
diff --git a/tests/cts/net/src/android/net/cts/InetAddressesTest.java b/tests/cts/net/src/android/net/cts/InetAddressesTest.java
new file mode 100644
index 0000000..7837ce9
--- /dev/null
+++ b/tests/cts/net/src/android/net/cts/InetAddressesTest.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2018 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.net.InetAddresses;
+import java.net.InetAddress;
+import junitparams.JUnitParamsRunner;
+import junitparams.Parameters;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+@RunWith(JUnitParamsRunner.class)
+public class InetAddressesTest {
+
+    public static String[][] validNumericAddressesAndStringRepresentation() {
+        return new String[][] {
+            // Regular IPv4.
+            { "1.2.3.4", "1.2.3.4" },
+
+            // Regular IPv6.
+            { "2001:4860:800d::68", "2001:4860:800d::68" },
+            { "1234:5678::9ABC:DEF0", "1234:5678::9abc:def0" },
+            { "2001:cdba:9abc:5678::", "2001:cdba:9abc:5678::" },
+            { "::2001:cdba:9abc:5678", "::2001:cdba:9abc:5678" },
+            { "64:ff9b::1.2.3.4", "64:ff9b::102:304" },
+
+            { "::9abc:5678", "::154.188.86.120" },
+
+            // Mapped IPv4
+            { "::ffff:127.0.0.1", "127.0.0.1" },
+
+            // Android does not recognize Octal (leading 0) cases: they are treated as decimal.
+            { "0177.00.00.01", "177.0.0.1" },
+
+            // Verify that examples from JavaDoc work correctly.
+            { "192.0.2.1", "192.0.2.1" },
+            { "2001:db8::1:2", "2001:db8::1:2" },
+        };
+    }
+
+    public static String[] invalidNumericAddresses() {
+        return new String[] {
+            "",
+            " ",
+            "\t",
+            "\n",
+            "1.2.3.4.",
+            "1.2.3",
+            "1.2",
+            "1",
+            "1234",
+            "0",
+            "0x1.0x2.0x3.0x4",
+            "0x7f.0x00.0x00.0x01",
+            "0256.00.00.01",
+            "fred",
+            "www.google.com",
+            // IPv6 encoded for use in URL as defined in RFC 2732
+            "[fe80::6:2222]",
+        };
+    }
+
+    @Parameters(method = "validNumericAddressesAndStringRepresentation")
+    @Test
+    public void parseNumericAddress(String address, String expectedString) {
+        InetAddress inetAddress = InetAddresses.parseNumericAddress(address);
+        assertEquals(expectedString, inetAddress.getHostAddress());
+    }
+
+    @Parameters(method = "invalidNumericAddresses")
+    @Test
+    public void test_parseNonNumericAddress(String address) {
+        try {
+            InetAddress inetAddress = InetAddresses.parseNumericAddress(address);
+            fail(String.format(
+                "Address %s is not numeric but was parsed as %s", address, inetAddress));
+        } catch (IllegalArgumentException e) {
+            assertThat(e.getMessage()).contains(address);
+        }
+    }
+
+    @Test
+    public void test_parseNumericAddress_null() {
+        try {
+            InetAddress inetAddress = InetAddresses.parseNumericAddress(null);
+            fail(String.format("null is not numeric but was parsed as %s", inetAddress));
+        } catch (NullPointerException e) {
+            // expected
+        }
+    }
+
+    @Parameters(method = "validNumericAddressesAndStringRepresentation")
+    @Test
+    public void test_isNumericAddress(String address, String unused) {
+        assertTrue("expected '" + address + "' to be treated as numeric",
+            InetAddresses.isNumericAddress(address));
+    }
+
+    @Parameters(method = "invalidNumericAddresses")
+    @Test
+    public void test_isNotNumericAddress(String address) {
+        assertFalse("expected '" + address + "' to be treated as non-numeric",
+            InetAddresses.isNumericAddress(address));
+    }
+
+    @Test
+    public void test_isNumericAddress_null() {
+        try {
+            InetAddresses.isNumericAddress(null);
+            fail("expected null to throw a NullPointerException");
+        } catch (NullPointerException e) {
+            // expected
+        }
+    }
+}
diff --git a/tests/cts/net/src/android/net/cts/IpSecBaseTest.java b/tests/cts/net/src/android/net/cts/IpSecBaseTest.java
index 7132ecf..35d0f48 100644
--- a/tests/cts/net/src/android/net/cts/IpSecBaseTest.java
+++ b/tests/cts/net/src/android/net/cts/IpSecBaseTest.java
@@ -19,6 +19,7 @@
 import static org.junit.Assert.assertArrayEquals;
 
 import android.content.Context;
+import android.net.ConnectivityManager;
 import android.net.IpSecAlgorithm;
 import android.net.IpSecManager;
 import android.net.IpSecTransform;
@@ -66,11 +67,13 @@
     protected static final byte[] AUTH_KEY = getKey(256);
     protected static final byte[] CRYPT_KEY = getKey(256);
 
+    protected ConnectivityManager mCM;
     protected IpSecManager mISM;
 
     protected void setUp() throws Exception {
         super.setUp();
         mISM = (IpSecManager) getContext().getSystemService(Context.IPSEC_SERVICE);
+        mCM = (ConnectivityManager) getContext().getSystemService(Context.CONNECTIVITY_SERVICE);
     }
 
     protected static byte[] getKey(int bitLength) {
diff --git a/tests/cts/net/src/android/net/cts/IpSecManagerTest.java b/tests/cts/net/src/android/net/cts/IpSecManagerTest.java
index a18b2f0..3387064 100644
--- a/tests/cts/net/src/android/net/cts/IpSecManagerTest.java
+++ b/tests/cts/net/src/android/net/cts/IpSecManagerTest.java
@@ -21,8 +21,6 @@
 import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertTrue;
 
-import android.content.Context;
-import android.net.ConnectivityManager;
 import android.net.IpSecAlgorithm;
 import android.net.IpSecManager;
 import android.net.IpSecTransform;
@@ -37,25 +35,15 @@
 import java.net.DatagramSocket;
 import java.net.Inet6Address;
 import java.net.InetAddress;
-import java.net.UnknownHostException;
 import java.util.Arrays;
 
 public class IpSecManagerTest extends IpSecBaseTest {
 
     private static final String TAG = IpSecManagerTest.class.getSimpleName();
 
-    private ConnectivityManager mCM;
-
-    private static InetAddress IpAddress(String addrString) {
-        try {
-            return InetAddress.getByName(addrString);
-        } catch (UnknownHostException e) {
-            throw new IllegalArgumentException("Invalid IP address: " + e);
-        }
-    }
-
-    private static final InetAddress GOOGLE_DNS_4 = IpAddress("8.8.8.8");
-    private static final InetAddress GOOGLE_DNS_6 = IpAddress("2001:4860:4860::8888");
+    private static final InetAddress GOOGLE_DNS_4 = InetAddress.parseNumericAddress("8.8.8.8");
+    private static final InetAddress GOOGLE_DNS_6 =
+            InetAddress.parseNumericAddress("2001:4860:4860::8888");
 
     private static final InetAddress[] GOOGLE_DNS_LIST =
             new InetAddress[] {GOOGLE_DNS_4, GOOGLE_DNS_6};
@@ -78,7 +66,6 @@
 
     protected void setUp() throws Exception {
         super.setUp();
-        mCM = (ConnectivityManager) getContext().getSystemService(Context.CONNECTIVITY_SERVICE);
     }
 
     /*
diff --git a/tests/cts/net/src/android/net/cts/IpSecManagerTunnelTest.java b/tests/cts/net/src/android/net/cts/IpSecManagerTunnelTest.java
new file mode 100644
index 0000000..5dc9b63
--- /dev/null
+++ b/tests/cts/net/src/android/net/cts/IpSecManagerTunnelTest.java
@@ -0,0 +1,176 @@
+/*
+ * Copyright (C) 2018 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 static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import android.net.IpSecAlgorithm;
+import android.net.IpSecManager;
+import android.net.IpSecTransform;
+import android.net.Network;
+
+import com.android.compatibility.common.util.SystemUtil;
+
+import java.net.Inet6Address;
+import java.net.InetAddress;
+import java.net.InterfaceAddress;
+import java.net.NetworkInterface;
+
+public class IpSecManagerTunnelTest extends IpSecBaseTest {
+
+    private static final String TAG = IpSecManagerTunnelTest.class.getSimpleName();
+    private static final int IP4_PREFIX_LEN = 24;
+    private static final int IP6_PREFIX_LEN = 48;
+    private static final InetAddress OUTER_ADDR4 = InetAddress.parseNumericAddress("192.0.2.0");
+    private static final InetAddress OUTER_ADDR6 =
+            InetAddress.parseNumericAddress("2001:db8:f00d::1");
+    private static final InetAddress INNER_ADDR4 = InetAddress.parseNumericAddress("10.0.0.1");
+    private static final InetAddress INNER_ADDR6 =
+            InetAddress.parseNumericAddress("2001:db8:d00d::1");
+
+    private Network mUnderlyingNetwork;
+    private Network mIpSecNetwork;
+
+    protected void setUp() throws Exception {
+        super.setUp();
+    }
+
+    protected void tearDown() {
+        setAppop(false);
+    }
+
+    private void setAppop(boolean allow) {
+        // Under normal circumstances, the MANAGE_IPSEC_TUNNELS appop would be auto-granted by the
+        // telephony framework, and the only permission that is sufficient is NETWORK_STACK. So we
+        // shell out the appop manager, to give us the right appop permissions.
+        String cmd =
+                "appops set "
+                        + mContext.getPackageName()
+                        + " MANAGE_IPSEC_TUNNELS "
+                + (allow ? "allow" : "deny");
+        SystemUtil.runShellCommand(cmd);
+    }
+
+    public void testSecurityExceptionsCreateTunnelInterface() throws Exception {
+        // Ensure we don't have the appop. Permission is not requested in the Manifest
+        setAppop(false);
+
+        // Security exceptions are thrown regardless of IPv4/IPv6. Just test one
+        try {
+            mISM.createIpSecTunnelInterface(OUTER_ADDR6, OUTER_ADDR6, mUnderlyingNetwork);
+            fail("Did not throw SecurityException for Tunnel creation without appop");
+        } catch (SecurityException expected) {
+        }
+    }
+
+    public void testSecurityExceptionsBuildTunnelTransform() throws Exception {
+        // Ensure we don't have the appop. Permission is not requested in the Manifest
+        setAppop(false);
+
+        // Security exceptions are thrown regardless of IPv4/IPv6. Just test one
+        try (IpSecManager.SecurityParameterIndex spi =
+                mISM.allocateSecurityParameterIndex(OUTER_ADDR4);
+                IpSecTransform transform =
+                        new IpSecTransform.Builder(mContext)
+                                .buildTunnelModeTransform(OUTER_ADDR4, spi)) {
+            fail("Did not throw SecurityException for Transform creation without appop");
+        } catch (SecurityException expected) {
+        }
+    }
+
+    private void checkTunnel(InetAddress inner, InetAddress outer, boolean useEncap)
+            throws Exception {
+        setAppop(true);
+        int innerPrefixLen = inner instanceof Inet6Address ? IP6_PREFIX_LEN : IP4_PREFIX_LEN;
+
+        try (IpSecManager.SecurityParameterIndex spi = mISM.allocateSecurityParameterIndex(outer);
+                IpSecManager.IpSecTunnelInterface tunnelIntf =
+                        mISM.createIpSecTunnelInterface(outer, outer, mCM.getActiveNetwork());
+                IpSecManager.UdpEncapsulationSocket encapSocket =
+                        mISM.openUdpEncapsulationSocket()) {
+
+            IpSecTransform.Builder transformBuilder = new IpSecTransform.Builder(mContext);
+            transformBuilder.setEncryption(
+                    new IpSecAlgorithm(IpSecAlgorithm.CRYPT_AES_CBC, CRYPT_KEY));
+            transformBuilder.setAuthentication(
+                    new IpSecAlgorithm(
+                            IpSecAlgorithm.AUTH_HMAC_SHA256, AUTH_KEY, AUTH_KEY.length * 4));
+
+            if (useEncap) {
+                transformBuilder.setIpv4Encapsulation(encapSocket, encapSocket.getPort());
+            }
+
+            // Check transform application
+            try (IpSecTransform transform = transformBuilder.buildTunnelModeTransform(outer, spi)) {
+                mISM.applyTunnelModeTransform(tunnelIntf, IpSecManager.DIRECTION_IN, transform);
+                mISM.applyTunnelModeTransform(tunnelIntf, IpSecManager.DIRECTION_OUT, transform);
+
+                // TODO: Test to ensure that send/receive works with these transforms.
+            }
+
+            // Check interface was created
+            NetworkInterface netIntf = NetworkInterface.getByName(tunnelIntf.getInterfaceName());
+            assertNotNull(netIntf);
+
+            // Add addresses and check
+            tunnelIntf.addAddress(inner, innerPrefixLen);
+            for (InterfaceAddress intfAddr : netIntf.getInterfaceAddresses()) {
+                assertEquals(intfAddr.getAddress(), inner);
+                assertEquals(intfAddr.getNetworkPrefixLength(), innerPrefixLen);
+            }
+
+            // Remove addresses and check
+            tunnelIntf.removeAddress(inner, innerPrefixLen);
+            assertTrue(netIntf.getInterfaceAddresses().isEmpty());
+
+            // Check interface was cleaned up
+            tunnelIntf.close();
+            netIntf = NetworkInterface.getByName(tunnelIntf.getInterfaceName());
+            assertNull(netIntf);
+        }
+    }
+
+    /*
+     * Create, add and remove addresses, then teardown tunnel
+     */
+    public void testTunnelV4InV4() throws Exception {
+        checkTunnel(INNER_ADDR4, OUTER_ADDR4, false);
+    }
+
+    public void testTunnelV4InV4UdpEncap() throws Exception {
+        checkTunnel(INNER_ADDR4, OUTER_ADDR4, true);
+    }
+
+    public void testTunnelV4InV6() throws Exception {
+        checkTunnel(INNER_ADDR4, OUTER_ADDR6, false);
+    }
+
+    public void testTunnelV6InV4() throws Exception {
+        checkTunnel(INNER_ADDR6, OUTER_ADDR4, false);
+    }
+
+    public void testTunnelV6InV4UdpEncap() throws Exception {
+        checkTunnel(INNER_ADDR6, OUTER_ADDR4, true);
+    }
+
+    public void testTunnelV6InV6() throws Exception {
+        checkTunnel(INNER_ADDR6, OUTER_ADDR6, false);
+    }
+}
diff --git a/tests/cts/net/src/android/net/cts/IpSecSysctlTest.java b/tests/cts/net/src/android/net/cts/IpSecSysctlTest.java
deleted file mode 100644
index b362282..0000000
--- a/tests/cts/net/src/android/net/cts/IpSecSysctlTest.java
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright (C) 2018 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.system.ErrnoException;
-import android.system.Os;
-import android.system.OsConstants;
-import android.system.StructStat;
-import android.test.AndroidTestCase;
-
-import java.io.File;
-import java.io.FileDescriptor;
-import java.io.IOException;
-
-/**
- * Tests for multinetwork sysctl functionality.
- */
-public class IpSecSysctlTest extends SysctlBaseTest {
-
-    // SPI expiration sysctls. Must be present and set greater than 1h.
-    private static final String SPI_TIMEOUT_SYSCTL = "/proc/sys/net/core/xfrm_acq_expires";
-    private static final int MIN_ACQ_EXPIRES = 3600;
-
-    /**
-     * Checks that SPI default timeouts are overridden, and set to a reasonable length of time
-     */
-    public void testProcFiles() throws ErrnoException, IOException, NumberFormatException {
-        int value = getIntValue(SPI_TIMEOUT_SYSCTL);
-        assertAtLeast(SPI_TIMEOUT_SYSCTL, value, MIN_ACQ_EXPIRES);
-    }
-}
diff --git a/tests/cts/net/src/android/net/cts/MultinetworkApiTest.java b/tests/cts/net/src/android/net/cts/MultinetworkApiTest.java
index b2c9d9b..c3e65b7 100644
--- a/tests/cts/net/src/android/net/cts/MultinetworkApiTest.java
+++ b/tests/cts/net/src/android/net/cts/MultinetworkApiTest.java
@@ -43,6 +43,12 @@
     private static native int runSetprocnetwork(long networkHandle);
     private static native int runSetsocknetwork(long networkHandle);
     private static native int runDatagramCheck(long networkHandle);
+    private static native int runResNapiMalformedCheck(long networkHandle);
+    private static native int runResNcancelCheck(long networkHandle);
+    private static native int runResNqueryCheck(long networkHandle);
+    private static native int runResNsendCheck(long networkHandle);
+
+
 
     private ConnectivityManager mCM;
 
@@ -175,4 +181,14 @@
             fail();
         } catch (IllegalArgumentException e) {}
     }
+
+    public void testResNApi() {
+        for (Network network : getTestableNetworks()) {
+            // Throws AssertionError directly in jni function if test fail.
+            runResNqueryCheck(network.getNetworkHandle());
+            runResNsendCheck(network.getNetworkHandle());
+            runResNcancelCheck(network.getNetworkHandle());
+            runResNapiMalformedCheck(network.getNetworkHandle());
+        }
+    }
 }
diff --git a/tests/cts/net/src/android/net/cts/MultinetworkSysctlTest.java b/tests/cts/net/src/android/net/cts/MultinetworkSysctlTest.java
deleted file mode 100644
index 1d0c111..0000000
--- a/tests/cts/net/src/android/net/cts/MultinetworkSysctlTest.java
+++ /dev/null
@@ -1,73 +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 android.net.cts;
-
-import android.system.ErrnoException;
-import android.system.Os;
-import android.system.OsConstants;
-import android.system.StructStat;
-import android.test.AndroidTestCase;
-
-import java.io.File;
-import java.io.FileDescriptor;
-import java.io.IOException;
-
-/**
- * Tests for multinetwork sysctl functionality.
- */
-public class MultinetworkSysctlTest extends SysctlBaseTest {
-
-    // Global sysctls. Must be present and set to 1.
-    private static final String[] GLOBAL_SYSCTLS = {
-        "/proc/sys/net/ipv4/fwmark_reflect",
-        "/proc/sys/net/ipv6/fwmark_reflect",
-        "/proc/sys/net/ipv4/tcp_fwmark_accept",
-    };
-
-    // Per-interface IPv6 autoconf sysctls.
-    private static final String IPV6_SYSCTL_DIR = "/proc/sys/net/ipv6/conf";
-    private static final String AUTOCONF_SYSCTL = "accept_ra_rt_table";
-
-    /**
-     * Checks that the sysctls for multinetwork kernel features are present and
-     * enabled. The necessary kernel commits are:
-     *
-     * Mainline Linux:
-     *   e110861 net: add a sysctl to reflect the fwmark on replies
-     *   1b3c61d net: Use fwmark reflection in PMTU discovery.
-     *   84f39b0 net: support marking accepting TCP sockets
-     *
-     * Common Android tree (e.g., 3.10):
-     *   a03f539 net: ipv6: autoconf routes into per-device tables
-     */
-     public void testProcFiles() throws ErrnoException, IOException, NumberFormatException {
-         for (String sysctl : GLOBAL_SYSCTLS) {
-             int value = getIntValue(sysctl);
-             assertEquals(sysctl, 1, value);
-         }
-
-         File[] interfaceDirs = new File(IPV6_SYSCTL_DIR).listFiles();
-         for (File interfaceDir : interfaceDirs) {
-             if (interfaceDir.getName().equals("all") || interfaceDir.getName().equals("lo")) {
-                 continue;
-             }
-             String sysctl = new File(interfaceDir, AUTOCONF_SYSCTL).getAbsolutePath();
-             int value = getIntValue(sysctl);
-             assertLess(sysctl, value, 0);
-         }
-     }
-}
diff --git a/tests/cts/net/src/android/net/cts/SysctlBaseTest.java b/tests/cts/net/src/android/net/cts/SysctlBaseTest.java
deleted file mode 100644
index a5966d4..0000000
--- a/tests/cts/net/src/android/net/cts/SysctlBaseTest.java
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * Copyright (C) 2018 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.system.ErrnoException;
-import android.system.Os;
-import android.system.OsConstants;
-import android.system.StructStat;
-import android.test.AndroidTestCase;
-
-import java.io.File;
-import java.io.FileDescriptor;
-import java.io.IOException;
-
-/**
- * Tests for multinetwork sysctl functionality.
- */
-public class SysctlBaseTest extends AndroidTestCase {
-
-    // Expected mode, UID, and GID of sysctl files.
-    private static final int SYSCTL_MODE = 0100644;
-    private static final int SYSCTL_UID = 0;
-    private static final int SYSCTL_GID = 0;
-
-    private void checkSysctlPermissions(String fileName) throws ErrnoException {
-        StructStat stat = Os.stat(fileName);
-        assertEquals("mode of " + fileName + ":", SYSCTL_MODE, stat.st_mode);
-        assertEquals("UID of " + fileName + ":", SYSCTL_UID, stat.st_uid);
-        assertEquals("GID of " + fileName + ":", SYSCTL_GID, stat.st_gid);
-    }
-
-    protected void assertLess(String sysctl, int a, int b) {
-        assertTrue("value of " + sysctl + ": expected < " + b + " but was: " + a, a < b);
-    }
-
-    protected void assertAtLeast(String sysctl, int a, int b) {
-        assertTrue("value of " + sysctl + ": expected >= " + b + " but was: " + a, a >= b);
-    }
-
-    private String readFile(String fileName) throws ErrnoException, IOException {
-        byte[] buf = new byte[1024];
-        FileDescriptor fd = Os.open(fileName, 0, OsConstants.O_RDONLY);
-        int bytesRead = Os.read(fd, buf, 0, buf.length);
-        assertLess("length of " + fileName + ":", bytesRead, buf.length);
-        return new String(buf);
-    }
-
-    /*
-     * Checks permissions and retrieves the sysctl's value. Retrieval of value should always use
-     * this method
-     */
-    protected int getIntValue(String filename) throws ErrnoException, IOException {
-        checkSysctlPermissions(filename);
-        return Integer.parseInt(readFile(filename).trim());
-    }
-}
diff --git a/tests/cts/net/src/android/net/cts/TrafficStatsTest.java b/tests/cts/net/src/android/net/cts/TrafficStatsTest.java
index a8743fa..503ba51 100755
--- a/tests/cts/net/src/android/net/cts/TrafficStatsTest.java
+++ b/tests/cts/net/src/android/net/cts/TrafficStatsTest.java
@@ -16,14 +16,14 @@
 
 package android.net.cts;
 
+import android.content.pm.PackageManager;
+import android.net.NetworkStats;
 import android.net.TrafficStats;
 import android.os.Process;
+import android.os.SystemProperties;
 import android.test.AndroidTestCase;
 import android.util.Log;
 
-import java.io.BufferedReader;
-import java.io.FileNotFoundException;
-import java.io.FileReader;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
@@ -99,6 +99,7 @@
         final int byteCount = 1024;
         final int packetCount = 1024;
 
+        TrafficStats.startDataProfiling(null);
         final ServerSocket server = new ServerSocket(0);
         new Thread("TrafficStatsTest.testTrafficStatsForLocalhost") {
             @Override
@@ -153,6 +154,7 @@
             Thread.sleep(1000);
         } catch (InterruptedException e) {
         }
+        NetworkStats testStats = TrafficStats.stopDataProfiling(null);
 
         long mobileTxPacketsAfter = TrafficStats.getMobileTxPackets();
         long mobileRxPacketsAfter = TrafficStats.getMobileRxPackets();
@@ -194,6 +196,24 @@
             Log.i(LOG_TAG, "lingering traffic data: " + deltaTxOtherPackets + "/" + deltaRxOtherPackets);
         }
 
+        // Check the per uid stats read from data profiling have the stats expected. The data
+        // profiling snapshot is generated from readNetworkStatsDetail() method in
+        // networkStatsService and in this way we can verify the detail networkStats of a given uid
+        // is correct.
+        NetworkStats.Entry entry = testStats.getTotal(null, Process.myUid());
+        assertTrue("txPackets detail: " + entry.txPackets + " uidTxPackets: " + uidTxDeltaPackets,
+            entry.txPackets >= packetCount + minExpectedExtraPackets
+            && entry.txPackets <= uidTxDeltaPackets);
+        assertTrue("rxPackets detail: " + entry.rxPackets + " uidRxPackets: " + uidRxDeltaPackets,
+            entry.rxPackets >= packetCount + minExpectedExtraPackets
+            && entry.rxPackets <= uidRxDeltaPackets);
+        assertTrue("txBytes detail: " + entry.txBytes + " uidTxDeltaBytes: " + uidTxDeltaBytes,
+            entry.txBytes >= tcpPacketToIpBytes(packetCount, byteCount)
+            + tcpPacketToIpBytes(minExpectedExtraPackets, 0) && entry.txBytes <= uidTxDeltaBytes);
+        assertTrue("rxBytes detail: " + entry.rxBytes + " uidRxDeltaBytes: " + uidRxDeltaBytes,
+            entry.rxBytes >= tcpPacketToIpBytes(packetCount, byteCount)
+            + tcpPacketToIpBytes(minExpectedExtraPackets, 0) && entry.rxBytes <= uidRxDeltaBytes);
+
         assertTrue("uidtxp: " + uidTxPacketsBefore + " -> " + uidTxPacketsAfter + " delta=" + uidTxDeltaPackets +
             " Wanted: " + uidTxDeltaPackets + ">=" + packetCount + "+" + minExpectedExtraPackets + " && " +
             uidTxDeltaPackets + "<=" + packetCount + "+" + packetCount + "+" + maxExpectedExtraPackets + "+" + deltaTxOtherPackets,
@@ -216,19 +236,37 @@
             uidRxDeltaBytes <= tcpPacketToIpBytes(packetCount, byteCount) + tcpPacketToIpBytes(packetCount + maxExpectedExtraPackets + deltaRxOtherPackets, 0));
 
         // Localhost traffic *does* count against total stats.
-        // Fudge by 132 packets of 1500 bytes not related to the test.
+        // Check the total stats increased after test data transfer over localhost has been made.
         assertTrue("ttxp: " + totalTxPacketsBefore + " -> " + totalTxPacketsAfter,
-            totalTxPacketsAfter >= totalTxPacketsBefore + uidTxDeltaPackets &&
-            totalTxPacketsAfter <= totalTxPacketsBefore + uidTxDeltaPackets + 132);
+                totalTxPacketsAfter >= totalTxPacketsBefore + uidTxDeltaPackets);
         assertTrue("trxp: " + totalRxPacketsBefore + " -> " + totalRxPacketsAfter,
-            totalRxPacketsAfter >= totalRxPacketsBefore + uidRxDeltaPackets &&
-            totalRxPacketsAfter <= totalRxPacketsBefore + uidRxDeltaPackets + 132);
+                totalRxPacketsAfter >= totalRxPacketsBefore + uidRxDeltaPackets);
         assertTrue("ttxb: " + totalTxBytesBefore + " -> " + totalTxBytesAfter,
-            totalTxBytesAfter >= totalTxBytesBefore + uidTxDeltaBytes &&
-            totalTxBytesAfter <= totalTxBytesBefore + uidTxDeltaBytes + 132 * 1500);
+                totalTxBytesAfter >= totalTxBytesBefore + uidTxDeltaBytes);
         assertTrue("trxb: " + totalRxBytesBefore + " -> " + totalRxBytesAfter,
-            totalRxBytesAfter >= totalRxBytesBefore + uidRxDeltaBytes &&
-            totalRxBytesAfter <= totalRxBytesBefore + uidRxDeltaBytes + 132 * 1500);
+                totalRxBytesAfter >= totalRxBytesBefore + uidRxDeltaBytes);
+
+        // If the adb TCP port is opened, this test may be run by adb over network.
+        // Huge amount of data traffic might go through the network and accounted into total packets
+        // stats. The upper bound check would be meaningless.
+        // TODO: Consider precisely calculate the traffic accounted due to adb over network and
+        //       subtract it when checking upper bound instead of skip checking.
+        final PackageManager pm = mContext.getPackageManager();
+        if (SystemProperties.getInt("persist.adb.tcp.port", -1) > -1
+                || SystemProperties.getInt("service.adb.tcp.port", -1) > -1
+                || !pm.hasSystemFeature(PackageManager.FEATURE_USB_ACCESSORY)) {
+            Log.i(LOG_TAG, "adb is running over the network, skip the upper bound check");
+        } else {
+            // Fudge by 132 packets of 1500 bytes not related to the test.
+            assertTrue("ttxp: " + totalTxPacketsBefore + " -> " + totalTxPacketsAfter,
+                    totalTxPacketsAfter <= totalTxPacketsBefore + uidTxDeltaPackets + 132);
+            assertTrue("trxp: " + totalRxPacketsBefore + " -> " + totalRxPacketsAfter,
+                    totalRxPacketsAfter <= totalRxPacketsBefore + uidRxDeltaPackets + 132);
+            assertTrue("ttxb: " + totalTxBytesBefore + " -> " + totalTxBytesAfter,
+                    totalTxBytesAfter <= totalTxBytesBefore + uidTxDeltaBytes + 132 * 1500);
+            assertTrue("trxb: " + totalRxBytesBefore + " -> " + totalRxBytesAfter,
+                    totalRxBytesAfter <= totalRxBytesBefore + uidRxDeltaBytes + 132 * 1500);
+        }
 
         // Localhost traffic should *not* count against mobile stats,
         // There might be some other traffic, but nowhere near 1MB.
@@ -244,6 +282,5 @@
         assertTrue("mrxb: " + mobileRxBytesBefore + " -> " + mobileRxBytesAfter,
             mobileRxBytesAfter >= mobileRxBytesBefore &&
             mobileRxBytesAfter <= mobileRxBytesBefore + 200000);
-
     }
 }
diff --git a/tests/cts/net/src/android/net/cts/UriTest.java b/tests/cts/net/src/android/net/cts/UriTest.java
index 5c54cda..40b8fb7 100644
--- a/tests/cts/net/src/android/net/cts/UriTest.java
+++ b/tests/cts/net/src/android/net/cts/UriTest.java
@@ -22,6 +22,7 @@
 import android.test.AndroidTestCase;
 import java.io.File;
 import java.util.Arrays;
+import java.util.ArrayList;
 
 public class UriTest extends AndroidTestCase {
     public void testParcelling() {
@@ -73,6 +74,12 @@
         assertEquals("new", b.getFragment());
         assertEquals("bar", b.getSchemeSpecificPart());
         assertEquals("foo", b.getScheme());
+
+        a = Uri.fromParts("scheme", "[2001:db8::dead:e1f]/foo", "bar");
+        b = a.buildUpon().fragment("qux").build();
+        assertEquals("qux", b.getFragment());
+        assertEquals("[2001:db8::dead:e1f]/foo", b.getSchemeSpecificPart());
+        assertEquals("scheme", b.getScheme());
     }
 
     public void testStringUri() {
@@ -120,6 +127,38 @@
         assertEquals("a.foo.com", uri.getHost());
         assertEquals(-1, uri.getPort());
         assertEquals("\\.example.com/path", uri.getPath());
+
+        uri = Uri.parse("https://[2001:db8::dead:e1f]/foo");
+        assertEquals("[2001:db8::dead:e1f]", uri.getAuthority());
+        assertNull(uri.getUserInfo());
+        assertEquals("[2001:db8::dead:e1f]", uri.getHost());
+        assertEquals(-1, uri.getPort());
+        assertEquals("/foo", uri.getPath());
+        assertEquals(null, uri.getFragment());
+        assertEquals("//[2001:db8::dead:e1f]/foo", uri.getSchemeSpecificPart());
+
+        uri = Uri.parse("https://[2001:db8::dead:e1f]/#foo");
+        assertEquals("[2001:db8::dead:e1f]", uri.getAuthority());
+        assertNull(uri.getUserInfo());
+        assertEquals("[2001:db8::dead:e1f]", uri.getHost());
+        assertEquals(-1, uri.getPort());
+        assertEquals("/", uri.getPath());
+        assertEquals("foo", uri.getFragment());
+        assertEquals("//[2001:db8::dead:e1f]/", uri.getSchemeSpecificPart());
+
+        uri = Uri.parse(
+                "https://some:user@[2001:db8::dead:e1f]:1234/foo?corge=thud&corge=garp#bar");
+        assertEquals("some:user@[2001:db8::dead:e1f]:1234", uri.getAuthority());
+        assertEquals("some:user", uri.getUserInfo());
+        assertEquals("[2001:db8::dead:e1f]", uri.getHost());
+        assertEquals(1234, uri.getPort());
+        assertEquals("/foo", uri.getPath());
+        assertEquals("bar", uri.getFragment());
+        assertEquals("//some:user@[2001:db8::dead:e1f]:1234/foo?corge=thud&corge=garp",
+                uri.getSchemeSpecificPart());
+        assertEquals("corge=thud&corge=garp", uri.getQuery());
+        assertEquals("thud", uri.getQueryParameter("corge"));
+        assertEquals(Arrays.asList("thud", "garp"), uri.getQueryParameters("corge"));
     }
 
     public void testCompareTo() {
@@ -164,22 +203,62 @@
         String encoded = Uri.encode("Bob:/", "/");
         assertEquals(-1, encoded.indexOf(':'));
         assertTrue(encoded.indexOf('/') > -1);
-        assertDecode(null);
-        assertDecode("");
-        assertDecode("Bob");
-        assertDecode(":Bob");
-        assertDecode("::Bob");
-        assertDecode("Bob::Lee");
-        assertDecode("Bob:Lee");
-        assertDecode("Bob::");
-        assertDecode("Bob:");
-        assertDecode("::Bob::");
+        assertEncodeDecodeRoundtripExact(null);
+        assertEncodeDecodeRoundtripExact("");
+        assertEncodeDecodeRoundtripExact("Bob");
+        assertEncodeDecodeRoundtripExact(":Bob");
+        assertEncodeDecodeRoundtripExact("::Bob");
+        assertEncodeDecodeRoundtripExact("Bob::Lee");
+        assertEncodeDecodeRoundtripExact("Bob:Lee");
+        assertEncodeDecodeRoundtripExact("Bob::");
+        assertEncodeDecodeRoundtripExact("Bob:");
+        assertEncodeDecodeRoundtripExact("::Bob::");
+        assertEncodeDecodeRoundtripExact("https:/some:user@[2001:db8::dead:e1f]:1234/foo#bar");
     }
 
-    private void assertDecode(String s) {
+    private static void assertEncodeDecodeRoundtripExact(String s) {
         assertEquals(s, Uri.decode(Uri.encode(s, null)));
     }
 
+    public void testDecode_emptyString_returnsEmptyString() {
+        assertEquals("", Uri.decode(""));
+    }
+
+    public void testDecode_null_returnsNull() {
+        assertNull(Uri.decode(null));
+    }
+
+    public void testDecode_wrongHexDigit() {
+        // %p in the end.
+        assertEquals("ab/$\u0102%\u0840\uFFFD\u0000", Uri.decode("ab%2f$%C4%82%25%e0%a1%80%p"));
+    }
+
+    public void testDecode_secondHexDigitWrong() {
+        // %1p in the end.
+        assertEquals("ab/$\u0102%\u0840\uFFFD\u0001", Uri.decode("ab%2f$%c4%82%25%e0%a1%80%1p"));
+    }
+
+    public void testDecode_endsWithPercent_appendsUnknownCharacter() {
+        // % in the end.
+        assertEquals("ab/$\u0102%\u0840\uFFFD", Uri.decode("ab%2f$%c4%82%25%e0%a1%80%"));
+    }
+
+    public void testDecode_plusNotConverted() {
+        assertEquals("ab/$\u0102%+\u0840", Uri.decode("ab%2f$%c4%82%25+%e0%a1%80"));
+    }
+
+    // Last character needs decoding (make sure we are flushing the buffer with chars to decode).
+    public void testDecode_lastCharacter() {
+        assertEquals("ab/$\u0102%\u0840", Uri.decode("ab%2f$%c4%82%25%e0%a1%80"));
+    }
+
+    // Check that a second row of encoded characters is decoded properly (internal buffers are
+    // reset properly).
+    public void testDecode_secondRowOfEncoded() {
+        assertEquals("ab/$\u0102%\u0840aa\u0840",
+                Uri.decode("ab%2f$%c4%82%25%e0%a1%80aa%e0%a1%80"));
+    }
+
     public void testFromFile() {
         File f = new File("/tmp/bob");
         Uri uri = Uri.fromFile(f);
@@ -425,4 +504,87 @@
                 Uri.parse("HTTP://USER@WWW.ANDROID.COM:100/ABOUT?foo=blah@bar=bleh#c")
                         .normalizeScheme());
     }
+
+    public void testToSafeString_tel() {
+        checkToSafeString("tel:xxxxxx", "tel:Google");
+        checkToSafeString("tel:xxxxxxxxxx", "tel:1234567890");
+        checkToSafeString("tEl:xxx.xxx-xxxx", "tEl:123.456-7890");
+    }
+
+    public void testToSafeString_sip() {
+        checkToSafeString("sip:xxxxxxx@xxxxxxx.xxxxxxxx", "sip:android@android.com:1234");
+        checkToSafeString("sIp:xxxxxxx@xxxxxxx.xxx", "sIp:android@android.com");
+    }
+
+    public void testToSafeString_sms() {
+        checkToSafeString("sms:xxxxxx", "sms:123abc");
+        checkToSafeString("smS:xxx.xxx-xxxx", "smS:123.456-7890");
+    }
+
+    public void testToSafeString_smsto() {
+        checkToSafeString("smsto:xxxxxx", "smsto:123abc");
+        checkToSafeString("SMSTo:xxx.xxx-xxxx", "SMSTo:123.456-7890");
+    }
+
+    public void testToSafeString_mailto() {
+        checkToSafeString("mailto:xxxxxxx@xxxxxxx.xxx", "mailto:android@android.com");
+        checkToSafeString("Mailto:xxxxxxx@xxxxxxx.xxxxxxxxxx",
+                "Mailto:android@android.com/secret");
+    }
+
+    public void testToSafeString_nfc() {
+        checkToSafeString("nfc:xxxxxx", "nfc:123abc");
+        checkToSafeString("nfc:xxx.xxx-xxxx", "nfc:123.456-7890");
+        checkToSafeString("nfc:xxxxxxx@xxxxxxx.xxx", "nfc:android@android.com");
+    }
+
+    public void testToSafeString_http() {
+        checkToSafeString("http://www.android.com/...", "http://www.android.com");
+        checkToSafeString("HTTP://www.android.com/...", "HTTP://www.android.com");
+        checkToSafeString("http://www.android.com/...", "http://www.android.com/");
+        checkToSafeString("http://www.android.com/...", "http://www.android.com/secretUrl?param");
+        checkToSafeString("http://www.android.com/...",
+                "http://user:pwd@www.android.com/secretUrl?param");
+        checkToSafeString("http://www.android.com/...",
+                "http://user@www.android.com/secretUrl?param");
+        checkToSafeString("http://www.android.com/...", "http://www.android.com/secretUrl?param");
+        checkToSafeString("http:///...", "http:///path?param");
+        checkToSafeString("http:///...", "http://");
+        checkToSafeString("http://:12345/...", "http://:12345/");
+    }
+
+    public void testToSafeString_https() {
+        checkToSafeString("https://www.android.com/...", "https://www.android.com/secretUrl?param");
+        checkToSafeString("https://www.android.com:8443/...",
+                "https://user:pwd@www.android.com:8443/secretUrl?param");
+        checkToSafeString("https://www.android.com/...", "https://user:pwd@www.android.com");
+        checkToSafeString("Https://www.android.com/...", "Https://user:pwd@www.android.com");
+    }
+
+    public void testToSafeString_ftp() {
+        checkToSafeString("ftp://ftp.android.com/...", "ftp://ftp.android.com/");
+        checkToSafeString("ftP://ftp.android.com/...", "ftP://anonymous@ftp.android.com/");
+        checkToSafeString("ftp://ftp.android.com:2121/...",
+                "ftp://root:love@ftp.android.com:2121/");
+    }
+
+    public void testToSafeString_rtsp() {
+        checkToSafeString("rtsp://rtsp.android.com/...", "rtsp://rtsp.android.com/");
+        checkToSafeString("rtsp://rtsp.android.com/...", "rtsp://rtsp.android.com/video.mov");
+        checkToSafeString("rtsp://rtsp.android.com/...", "rtsp://rtsp.android.com/video.mov?param");
+        checkToSafeString("RtsP://rtsp.android.com/...", "RtsP://anonymous@rtsp.android.com/");
+        checkToSafeString("rtsp://rtsp.android.com:2121/...",
+                "rtsp://username:password@rtsp.android.com:2121/");
+    }
+
+    public void testToSafeString_notSupport() {
+        checkToSafeString("unsupported://ajkakjah/askdha/secret?secret",
+                "unsupported://ajkakjah/askdha/secret?secret");
+        checkToSafeString("unsupported:ajkakjah/askdha/secret?secret",
+                "unsupported:ajkakjah/askdha/secret?secret");
+    }
+
+    private void checkToSafeString(String expectedSafeString, String original) {
+        assertEquals(expectedSafeString, Uri.parse(original).toSafeString());
+    }
 }
diff --git a/tests/cts/net/src/android/net/http/cts/SslCertificateTest.java b/tests/cts/net/src/android/net/http/cts/SslCertificateTest.java
index 70ae496..95f415c 100644
--- a/tests/cts/net/src/android/net/http/cts/SslCertificateTest.java
+++ b/tests/cts/net/src/android/net/http/cts/SslCertificateTest.java
@@ -230,6 +230,19 @@
         final String EXPECTED = "Issued to: c=ccc,o=testOName,ou=testUName,cn=testCName;\n"
             + "Issued by: e=aeei,c=adb,o=testOName,ou=testUName,cn=testCName;\n";
         assertEquals(EXPECTED, ssl.toString());
+        assertNull(ssl.getX509Certificate());
     }
 
+    public void testGetX509Certificate() {
+        final String TO = "c=ccc,o=testOName,ou=testUName,cn=testCName";
+        final String BY = "e=aeei,c=adb,o=testOName,ou=testUName,cn=testCName";
+        Date validNotBefore = new Date(System.currentTimeMillis() - 1000);
+        Date validNotAfter = new Date(System.currentTimeMillis());
+        SslCertificate ssl = new SslCertificate(TO, BY, validNotBefore, validNotAfter);
+        assertNull(ssl.getX509Certificate());
+
+        X509Certificate cert = new MockX509Certificate();
+        ssl = new SslCertificate(cert);
+        assertSame(cert, ssl.getX509Certificate());
+    }
 }
diff --git a/tests/cts/net/src/android/net/ipv6/cts/PingTest.java b/tests/cts/net/src/android/net/ipv6/cts/PingTest.java
index c23ad30..146fd83 100644
--- a/tests/cts/net/src/android/net/ipv6/cts/PingTest.java
+++ b/tests/cts/net/src/android/net/ipv6/cts/PingTest.java
@@ -61,7 +61,7 @@
 
     /** The beginning of an ICMPv6 echo request: type, code, and uninitialized checksum. */
     private static final byte[] PING_HEADER = new byte[] {
-        (byte) 0x80, (byte) 0x00, (byte) 0x00, (byte) 0x00
+        (byte) ICMP6_ECHO_REQUEST, (byte) 0x00, (byte) 0x00, (byte) 0x00
     };
 
     /**
@@ -135,7 +135,7 @@
         byte[] response = new byte[bytesRead];
         responseBuffer.flip();
         responseBuffer.get(response, 0, bytesRead);
-        assertEquals((byte) 0x81, response[0]);
+        assertEquals((byte) ICMP6_ECHO_REPLY, response[0]);
 
         // Find out what ICMP ID was used in the packet that was sent.
         int id = ((InetSocketAddress) Os.getsockname(s)).getPort();
diff --git a/tests/cts/net/src/android/net/wifi/OWNERS b/tests/cts/net/src/android/net/wifi/OWNERS
new file mode 100644
index 0000000..4a6001b
--- /dev/null
+++ b/tests/cts/net/src/android/net/wifi/OWNERS
@@ -0,0 +1,5 @@
+etancohen@google.com
+lorenzo@google.com
+mplass@google.com
+rpius@google.com
+satk@google.com
diff --git a/tests/cts/net/src/android/net/wifi/aware/OWNERS b/tests/cts/net/src/android/net/wifi/aware/OWNERS
deleted file mode 100644
index 4afc47f..0000000
--- a/tests/cts/net/src/android/net/wifi/aware/OWNERS
+++ /dev/null
@@ -1,2 +0,0 @@
-etancohen@google.com
-satk@google.com
\ No newline at end of file
diff --git a/tests/cts/net/src/android/net/wifi/aware/cts/SingleDeviceTest.java b/tests/cts/net/src/android/net/wifi/aware/cts/SingleDeviceTest.java
index 7277553..389621e 100644
--- a/tests/cts/net/src/android/net/wifi/aware/cts/SingleDeviceTest.java
+++ b/tests/cts/net/src/android/net/wifi/aware/cts/SingleDeviceTest.java
@@ -39,8 +39,11 @@
 import android.net.wifi.aware.WifiAwareSession;
 import android.os.Handler;
 import android.os.HandlerThread;
+import android.os.SystemClock;
 import android.test.AndroidTestCase;
 
+import com.android.compatibility.common.util.SystemUtil;
+
 import java.util.ArrayDeque;
 import java.util.ArrayList;
 import java.util.HashSet;
@@ -56,8 +59,8 @@
 public class SingleDeviceTest extends AndroidTestCase {
     private static final String TAG = "WifiAwareCtsTests";
 
-    // wait for Wi-Fi Aware to become available
-    static private final int WAIT_FOR_AWARE_CHANGE_SECS = 10;
+    // wait for Wi-Fi Aware state changes & network requests callbacks
+    static private final int WAIT_FOR_AWARE_CHANGE_SECS = 10; // 10 seconds
 
     private final Object mLock = new Object();
     private final HandlerThread mHandlerThread = new HandlerThread("SingleDeviceTest");
@@ -363,7 +366,7 @@
         mWifiLock = mWifiManager.createWifiLock(TAG);
         mWifiLock.acquire();
         if (!mWifiManager.isWifiEnabled()) {
-            mWifiManager.setWifiEnabled(true);
+            SystemUtil.runShellCommand("svc wifi enable");
         }
 
         mConnectivityManager = (ConnectivityManager) getContext().getSystemService(
@@ -433,7 +436,7 @@
         // 1. Disable Wi-Fi
         WifiAwareBroadcastReceiver receiver1 = new WifiAwareBroadcastReceiver();
         mContext.registerReceiver(receiver1, intentFilter);
-        mWifiManager.setWifiEnabled(false);
+        SystemUtil.runShellCommand("svc wifi disable");
 
         assertTrue("Timeout waiting for Wi-Fi Aware to change status",
                 receiver1.waitForStateChange());
@@ -442,7 +445,7 @@
         // 2. Enable Wi-Fi
         WifiAwareBroadcastReceiver receiver2 = new WifiAwareBroadcastReceiver();
         mContext.registerReceiver(receiver2, intentFilter);
-        mWifiManager.setWifiEnabled(true);
+        SystemUtil.runShellCommand("svc wifi enable");
 
         assertTrue("Timeout waiting for Wi-Fi Aware to change status",
                 receiver2.waitForStateChange());
@@ -702,7 +705,7 @@
 
     /**
      * Request an Aware data-path (open) as a Responder with an arbitrary peer MAC address. Validate
-     * that times-out.
+     * that receive an onUnavailable() callback.
      */
     public void testDataPathOpenOutOfBandFail() {
         if (!TestUtils.shouldTestWifiAware(getContext())) {
@@ -710,28 +713,32 @@
         }
         MacAddress mac = MacAddress.fromString("00:01:02:03:04:05");
 
+        // 1. initialize Aware: only purpose is to make sure it is available for OOB data-path
         WifiAwareSession session = attachAndGetSession();
 
         PublishConfig publishConfig = new PublishConfig.Builder().setServiceName(
                 "ValidName").build();
         DiscoverySessionCallbackTest discoveryCb = new DiscoverySessionCallbackTest();
-        NetworkCallbackTest networkCb = new NetworkCallbackTest();
+        session.publish(publishConfig, discoveryCb, mHandler);
+        assertTrue("Publish started",
+                discoveryCb.waitForCallback(DiscoverySessionCallbackTest.ON_PUBLISH_STARTED));
 
-        // 1. request an AWARE network
+        // 2. request an AWARE network
+        NetworkCallbackTest networkCb = new NetworkCallbackTest();
         NetworkRequest nr = new NetworkRequest.Builder().addTransportType(
                 NetworkCapabilities.TRANSPORT_WIFI_AWARE).setNetworkSpecifier(
                 session.createNetworkSpecifierOpen(
-                        WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_RESPONDER,
+                        WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_INITIATOR,
                         mac.toByteArray())).build();
-        mConnectivityManager.requestNetwork(nr, networkCb, 2000);
-        assertTrue("OnUnavailable received", networkCb.waitForOnUnavailable());
+        mConnectivityManager.requestNetwork(nr, networkCb);
+        assertTrue("OnUnavailable not received", networkCb.waitForOnUnavailable());
 
         session.close();
     }
 
     /**
      * Request an Aware data-path (encrypted) as a Responder with an arbitrary peer MAC address.
-     * Validate that times-out.
+     * Validate that receive an onUnavailable() callback.
      */
     public void testDataPathPassphraseOutOfBandFail() {
         if (!TestUtils.shouldTestWifiAware(getContext())) {
@@ -739,21 +746,25 @@
         }
         MacAddress mac = MacAddress.fromString("00:01:02:03:04:05");
 
+        // 1. initialize Aware: only purpose is to make sure it is available for OOB data-path
         WifiAwareSession session = attachAndGetSession();
 
         PublishConfig publishConfig = new PublishConfig.Builder().setServiceName(
                 "ValidName").build();
         DiscoverySessionCallbackTest discoveryCb = new DiscoverySessionCallbackTest();
-        NetworkCallbackTest networkCb = new NetworkCallbackTest();
+        session.publish(publishConfig, discoveryCb, mHandler);
+        assertTrue("Publish started",
+                discoveryCb.waitForCallback(DiscoverySessionCallbackTest.ON_PUBLISH_STARTED));
 
-        // 1. request an AWARE network
+        // 2. request an AWARE network
+        NetworkCallbackTest networkCb = new NetworkCallbackTest();
         NetworkRequest nr = new NetworkRequest.Builder().addTransportType(
                 NetworkCapabilities.TRANSPORT_WIFI_AWARE).setNetworkSpecifier(
                 session.createNetworkSpecifierPassphrase(
-                        WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_RESPONDER, mac.toByteArray(),
+                        WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_INITIATOR, mac.toByteArray(),
                         "abcdefghihk")).build();
-        mConnectivityManager.requestNetwork(nr, networkCb, 2000);
-        assertTrue("OnUnavailable received", networkCb.waitForOnUnavailable());
+        mConnectivityManager.requestNetwork(nr, networkCb);
+        assertTrue("OnUnavailable not received", networkCb.waitForOnUnavailable());
 
         session.close();
     }
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 a066ba8..c80e372 100644
--- a/tests/cts/net/src/android/net/wifi/cts/ConcurrencyTest.java
+++ b/tests/cts/net/src/android/net/wifi/cts/ConcurrencyTest.java
@@ -16,34 +16,66 @@
 
 package android.net.wifi.cts;
 
+import static org.junit.Assert.assertNotEquals;
+
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.content.pm.PackageManager;
 import android.net.ConnectivityManager;
 import android.net.ConnectivityManager.NetworkCallback;
 import android.net.Network;
 import android.net.NetworkCapabilities;
+import android.net.NetworkInfo;
 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.provider.Settings;
 import android.test.AndroidTestCase;
+import android.util.Log;
 
+import com.android.compatibility.common.util.SystemUtil;
+
+import java.util.Arrays;
+import java.util.BitSet;
+import java.util.LinkedList;
+import java.util.List;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
 
 public class ConcurrencyTest extends AndroidTestCase {
     private class MySync {
-        int expectedWifiState;
-        int expectedP2pState;
+        static final int WIFI_STATE = 0;
+        static final int P2P_STATE = 1;
+        static final int DISCOVERY_STATE = 2;
+        static final int NETWORK_INFO = 3;
+
+        public BitSet pendingSync = new BitSet();
+
+        public int expectedWifiState;
+        public int expectedP2pState;
+        public int expectedDiscoveryState;
+        public NetworkInfo expectedNetworkInfo;
+    }
+
+    private class MyResponse {
+        public boolean valid = false;
+
+        public boolean success;
+        public int p2pState;
+        public int discoveryState;
+        public NetworkInfo networkInfo;
     }
 
     private WifiManager mWifiManager;
+    private WifiP2pManager mWifiP2pManager;
+    private WifiP2pManager.Channel mWifiP2pChannel;
     private MySync mMySync = new MySync();
+    private MyResponse mMyResponse = new MyResponse();
 
-    private static final String TAG = "WifiInfoTest";
+    private static final String TAG = "ConcurrencyTest";
     private static final int TIMEOUT_MSEC = 6000;
     private static final int WAIT_MSEC = 60;
     private static final int DURATION = 10000;
@@ -54,16 +86,33 @@
             final String action = intent.getAction();
             if (action.equals(WifiManager.WIFI_STATE_CHANGED_ACTION)) {
                 synchronized (mMySync) {
+                    mMySync.pendingSync.set(MySync.WIFI_STATE);
                     mMySync.expectedWifiState = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE,
                             WifiManager.WIFI_STATE_DISABLED);
                     mMySync.notify();
                 }
             } else if(action.equals(WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION)) {
                 synchronized (mMySync) {
+                    mMySync.pendingSync.set(MySync.P2P_STATE);
                     mMySync.expectedP2pState = intent.getIntExtra(WifiP2pManager.EXTRA_WIFI_STATE,
                             WifiP2pManager.WIFI_P2P_STATE_DISABLED);
                     mMySync.notify();
                 }
+            } else if (action.equals(WifiP2pManager.WIFI_P2P_DISCOVERY_CHANGED_ACTION)) {
+                synchronized (mMySync) {
+                    mMySync.pendingSync.set(MySync.DISCOVERY_STATE);
+                    mMySync.expectedDiscoveryState = intent.getIntExtra(
+                            WifiP2pManager.EXTRA_DISCOVERY_STATE,
+                            WifiP2pManager.WIFI_P2P_DISCOVERY_STOPPED);
+                    mMySync.notify();
+                }
+            } else if (action.equals(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION)) {
+                synchronized (mMySync) {
+                    mMySync.pendingSync.set(MySync.NETWORK_INFO);
+                    mMySync.expectedNetworkInfo = (NetworkInfo) intent.getExtra(
+                            WifiP2pManager.EXTRA_NETWORK_INFO, null);
+                    mMySync.notify();
+                }
             }
         }
     };
@@ -79,17 +128,21 @@
         mIntentFilter = new IntentFilter();
         mIntentFilter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
         mIntentFilter.addAction(WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION);
+        mIntentFilter.addAction(WifiP2pManager.WIFI_P2P_DISCOVERY_CHANGED_ACTION);
+        mIntentFilter.addAction(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION);
 
         mContext.registerReceiver(mReceiver, mIntentFilter);
         mWifiManager = (WifiManager) getContext().getSystemService(Context.WIFI_SERVICE);
         assertNotNull(mWifiManager);
         if (mWifiManager.isWifiEnabled()) {
-            assertTrue(mWifiManager.setWifiEnabled(false));
+            SystemUtil.runShellCommand("svc wifi disable");
             Thread.sleep(DURATION);
         }
         assertTrue(!mWifiManager.isWifiEnabled());
         mMySync.expectedWifiState = WifiManager.WIFI_STATE_DISABLED;
         mMySync.expectedP2pState = WifiP2pManager.WIFI_P2P_STATE_DISABLED;
+        mMySync.expectedDiscoveryState = WifiP2pManager.WIFI_P2P_DISCOVERY_STOPPED;
+        mMySync.expectedNetworkInfo = null;
     }
 
     @Override
@@ -106,16 +159,66 @@
         super.tearDown();
     }
 
-    private void waitForBroadcasts() {
+    private boolean waitForBroadcasts(List<Integer> waitSyncList) {
         synchronized (mMySync) {
             long timeout = System.currentTimeMillis() + TIMEOUT_MSEC;
-            while (System.currentTimeMillis() < timeout
-                    && (mMySync.expectedWifiState != WifiManager.WIFI_STATE_ENABLED ||
-                    mMySync.expectedP2pState != WifiP2pManager.WIFI_P2P_STATE_ENABLED)) {
+            while (System.currentTimeMillis() < timeout) {
+                List<Integer> handledSyncList = waitSyncList.stream()
+                        .filter(w -> mMySync.pendingSync.get(w))
+                        .collect(Collectors.toList());
+                handledSyncList.forEach(w -> mMySync.pendingSync.clear(w));
+                waitSyncList.removeAll(handledSyncList);
+                if (waitSyncList.isEmpty()) {
+                    break;
+                }
                 try {
                     mMySync.wait(WAIT_MSEC);
                 } catch (InterruptedException e) { }
             }
+            if (!waitSyncList.isEmpty()) {
+                Log.i(TAG, "Missing broadcast: " + waitSyncList);
+            }
+            return waitSyncList.isEmpty();
+        }
+    }
+
+    private boolean waitForBroadcasts(int waitSingleSync) {
+        return waitForBroadcasts(
+                new LinkedList<Integer>(Arrays.asList(waitSingleSync)));
+    }
+
+    private boolean waitForServiceResponse(MyResponse waitResponse) {
+        synchronized (waitResponse) {
+            long timeout = System.currentTimeMillis() + TIMEOUT_MSEC;
+            while (System.currentTimeMillis() < timeout) {
+                try {
+                    waitResponse.wait(WAIT_MSEC);
+                } catch (InterruptedException e) { }
+
+                if (waitResponse.valid) {
+                    return true;
+                }
+            }
+            return false;
+        }
+    }
+
+    // Return true if location is enabled.
+    private boolean isLocationEnabled() {
+        return Settings.Secure.getInt(getContext().getContentResolver(),
+                Settings.Secure.LOCATION_MODE, Settings.Secure.LOCATION_MODE_OFF)
+                != Settings.Secure.LOCATION_MODE_OFF;
+    }
+
+    // Returns true if the device has location feature.
+    private boolean hasLocationFeature() {
+        return getContext().getPackageManager().hasSystemFeature(PackageManager.FEATURE_LOCATION);
+    }
+
+    private void resetResponse(MyResponse responseObj) {
+        synchronized (responseObj) {
+            responseObj.valid = false;
+            responseObj.networkInfo = null;
         }
     }
 
@@ -124,7 +227,7 @@
      */
     private void enableWifi() throws InterruptedException {
         if (!mWifiManager.isWifiEnabled()) {
-            assertTrue(mWifiManager.setWifiEnabled(true));
+            SystemUtil.runShellCommand("svc wifi enable");
         }
 
         ConnectivityManager cm =
@@ -146,25 +249,212 @@
         cm.unregisterNetworkCallback(networkCallback);
     }
 
-    public void testConcurrency() {
+    private boolean setupWifiP2p() {
         // Cannot support p2p alone
         if (!WifiFeature.isWifiSupported(getContext())) {
             assertTrue(!WifiFeature.isP2pSupported(getContext()));
-            return;
+            return false;
         }
 
         if (!WifiFeature.isP2pSupported(getContext())) {
             // skip the test if p2p is not supported
+            return false;
+        }
+
+        if (!hasLocationFeature()) {
+            Log.d(TAG, "Skipping test as location is not supported");
+            return false;
+        }
+        if (!isLocationEnabled()) {
+            fail("Please enable location for this test - since P-release WiFi Direct"
+                    + " needs Location enabled.");
+        }
+
+        long timeout = System.currentTimeMillis() + TIMEOUT_MSEC;
+        while (!mWifiManager.isWifiEnabled() && System.currentTimeMillis() < timeout) {
+            try {
+                enableWifi();
+            } catch (InterruptedException e) { }
+        }
+
+        assertTrue(mWifiManager.isWifiEnabled());
+
+        assertTrue(waitForBroadcasts(
+                new LinkedList<Integer>(
+                Arrays.asList(MySync.WIFI_STATE, MySync.P2P_STATE))));
+
+        assertEquals(WifiManager.WIFI_STATE_ENABLED, mMySync.expectedWifiState);
+        assertEquals(WifiP2pManager.WIFI_P2P_STATE_ENABLED, mMySync.expectedP2pState);
+
+        mWifiP2pManager =
+                (WifiP2pManager) getContext().getSystemService(Context.WIFI_P2P_SERVICE);
+        mWifiP2pChannel = mWifiP2pManager.initialize(
+                getContext(), getContext().getMainLooper(), null);
+
+        assertNotNull(mWifiP2pManager);
+        assertNotNull(mWifiP2pChannel);
+
+        assertTrue(waitForBroadcasts(MySync.NETWORK_INFO));
+        // wait for changing to EnabledState
+        assertNotNull(mMySync.expectedNetworkInfo);
+        assertTrue(mMySync.expectedNetworkInfo.isAvailable());
+
+        return true;
+    }
+
+    public void testConcurrency() {
+        if (!setupWifiP2p()) {
             return;
         }
 
-        // Enable wifi
-        assertTrue(mWifiManager.setWifiEnabled(true));
+        resetResponse(mMyResponse);
+        mWifiP2pManager.requestP2pState(mWifiP2pChannel, new WifiP2pManager.P2pStateListener() {
+            @Override
+            public void onP2pStateAvailable(int state) {
+                synchronized (mMyResponse) {
+                    mMyResponse.valid = true;
+                    mMyResponse.p2pState = state;
+                    mMyResponse.notify();
+                }
+            }
+        });
+        assertTrue(waitForServiceResponse(mMyResponse));
+        assertEquals(WifiP2pManager.WIFI_P2P_STATE_ENABLED, mMyResponse.p2pState);
+    }
 
-        waitForBroadcasts();
+    public void testRequestDiscoveryState() {
+        if (!setupWifiP2p()) {
+            return;
+        }
 
-        assertTrue(mMySync.expectedWifiState == WifiManager.WIFI_STATE_ENABLED);
-        assertTrue(mMySync.expectedP2pState == WifiP2pManager.WIFI_P2P_STATE_ENABLED);
+        resetResponse(mMyResponse);
+        mWifiP2pManager.requestDiscoveryState(
+                mWifiP2pChannel, new WifiP2pManager.DiscoveryStateListener() {
+                    @Override
+                    public void onDiscoveryStateAvailable(int state) {
+                        synchronized (mMyResponse) {
+                            mMyResponse.valid = true;
+                            mMyResponse.discoveryState = state;
+                            mMyResponse.notify();
+                        }
+                    }
+                });
+        assertTrue(waitForServiceResponse(mMyResponse));
+        assertEquals(WifiP2pManager.WIFI_P2P_DISCOVERY_STOPPED, mMyResponse.discoveryState);
+
+        resetResponse(mMyResponse);
+        mWifiP2pManager.discoverPeers(mWifiP2pChannel, new WifiP2pManager.ActionListener() {
+            @Override
+            public void onSuccess() {
+                synchronized (mMyResponse) {
+                    mMyResponse.valid = true;
+                    mMyResponse.success = true;
+                    mMyResponse.notify();
+                }
+            }
+
+            @Override
+            public void onFailure(int reason) {
+                synchronized (mMyResponse) {
+                    Log.d(TAG, "discoveryPeers failure reason: " + reason);
+                    mMyResponse.valid = true;
+                    mMyResponse.success = false;
+                    mMyResponse.notify();
+                }
+            }
+        });
+        assertTrue(waitForServiceResponse(mMyResponse));
+        assertTrue(mMyResponse.success);
+        assertTrue(waitForBroadcasts(MySync.DISCOVERY_STATE));
+
+        resetResponse(mMyResponse);
+        mWifiP2pManager.requestDiscoveryState(mWifiP2pChannel,
+                new WifiP2pManager.DiscoveryStateListener() {
+                    @Override
+                    public void onDiscoveryStateAvailable(int state) {
+                        synchronized (mMyResponse) {
+                            mMyResponse.valid = true;
+                            mMyResponse.discoveryState = state;
+                            mMyResponse.notify();
+                        }
+                    }
+                });
+        assertTrue(waitForServiceResponse(mMyResponse));
+        assertEquals(WifiP2pManager.WIFI_P2P_DISCOVERY_STARTED, mMyResponse.discoveryState);
+
+        mWifiP2pManager.stopPeerDiscovery(mWifiP2pChannel, null);
+    }
+
+    public void testRequestNetworkInfo() {
+        if (!setupWifiP2p()) {
+            return;
+        }
+
+        resetResponse(mMyResponse);
+        mWifiP2pManager.requestNetworkInfo(mWifiP2pChannel,
+                new WifiP2pManager.NetworkInfoListener() {
+                    @Override
+                    public void onNetworkInfoAvailable(NetworkInfo info) {
+                        synchronized (mMyResponse) {
+                            mMyResponse.valid = true;
+                            mMyResponse.networkInfo = info;
+                            mMyResponse.notify();
+                        }
+                    }
+                });
+        assertTrue(waitForServiceResponse(mMyResponse));
+        assertNotNull(mMyResponse.networkInfo);
+        // The state might be IDLE, DISCONNECTED, FAILED before a connection establishment.
+        // Just ensure the state is NOT CONNECTED.
+        assertNotEquals(NetworkInfo.DetailedState.CONNECTED,
+                mMySync.expectedNetworkInfo.getDetailedState());
+
+        resetResponse(mMyResponse);
+        mWifiP2pManager.createGroup(mWifiP2pChannel, new WifiP2pManager.ActionListener() {
+            @Override
+            public void onSuccess() {
+                synchronized (mMyResponse) {
+                    mMyResponse.valid = true;
+                    mMyResponse.success = true;
+                    mMyResponse.notify();
+                }
+            }
+
+            @Override
+            public void onFailure(int reason) {
+                synchronized (mMyResponse) {
+                    Log.d(TAG, "createGroup failure reason: " + reason);
+                    mMyResponse.valid = true;
+                    mMyResponse.success = false;
+                    mMyResponse.notify();
+                }
+            }
+        });
+        assertTrue(waitForServiceResponse(mMyResponse));
+        assertTrue(mMyResponse.success);
+        assertTrue(waitForBroadcasts(MySync.NETWORK_INFO));
+        assertNotNull(mMySync.expectedNetworkInfo);
+        assertEquals(NetworkInfo.DetailedState.CONNECTED,
+                mMySync.expectedNetworkInfo.getDetailedState());
+
+        resetResponse(mMyResponse);
+        mWifiP2pManager.requestNetworkInfo(mWifiP2pChannel,
+                new WifiP2pManager.NetworkInfoListener() {
+                    @Override
+                    public void onNetworkInfoAvailable(NetworkInfo info) {
+                        synchronized (mMyResponse) {
+                            mMyResponse.valid = true;
+                            mMyResponse.networkInfo = info;
+                            mMyResponse.notify();
+                        }
+                    }
+                });
+        assertTrue(waitForServiceResponse(mMyResponse));
+        assertNotNull(mMyResponse.networkInfo);
+        assertEquals(NetworkInfo.DetailedState.CONNECTED,
+                mMyResponse.networkInfo.getDetailedState());
+
+        mWifiP2pManager.removeGroup(mWifiP2pChannel, null);
     }
 
 }
diff --git a/tests/cts/net/src/android/net/wifi/cts/MulticastLockTest.java b/tests/cts/net/src/android/net/wifi/cts/MulticastLockTest.java
new file mode 100644
index 0000000..54fe9c7
--- /dev/null
+++ b/tests/cts/net/src/android/net/wifi/cts/MulticastLockTest.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2019 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.wifi.cts;
+
+import android.content.Context;
+import android.net.wifi.WifiManager;
+import android.net.wifi.WifiManager.MulticastLock;
+import android.test.AndroidTestCase;
+
+public class MulticastLockTest extends AndroidTestCase {
+
+    private static final String WIFI_TAG = "MulticastLockTest";
+
+    /**
+     * Verify acquire and release of Multicast locks
+     */
+    public void testMulticastLock() {
+        if (!WifiFeature.isWifiSupported(getContext())) {
+            // skip the test if WiFi is not supported
+            return;
+        }
+        WifiManager wm = (WifiManager) getContext().getSystemService(Context.WIFI_SERVICE);
+        MulticastLock mcl = wm.createMulticastLock(WIFI_TAG);
+
+        mcl.setReferenceCounted(true);
+        assertFalse(mcl.isHeld());
+        mcl.acquire();
+        assertTrue(mcl.isHeld());
+        mcl.release();
+        assertFalse(mcl.isHeld());
+        mcl.acquire();
+        mcl.acquire();
+        assertTrue(mcl.isHeld());
+        mcl.release();
+        assertTrue(mcl.isHeld());
+        mcl.release();
+        assertFalse(mcl.isHeld());
+        assertNotNull(mcl.toString());
+        try {
+            mcl.release();
+            fail("should throw out exception because release is called"
+                    +" a greater number of times than acquire");
+        } catch (RuntimeException e) {
+            // expected
+        }
+
+        mcl = wm.createMulticastLock(WIFI_TAG);
+        mcl.setReferenceCounted(false);
+        assertFalse(mcl.isHeld());
+        mcl.acquire();
+        assertTrue(mcl.isHeld());
+        mcl.release();
+        assertFalse(mcl.isHeld());
+        mcl.acquire();
+        mcl.acquire();
+        assertTrue(mcl.isHeld());
+        mcl.release();
+        assertFalse(mcl.isHeld());
+        assertNotNull(mcl.toString());
+        // releasing again after release: but ignored for non-referenced locks
+        mcl.release();
+    }
+}
diff --git a/tests/cts/net/src/android/net/wifi/cts/ScanResultTest.java b/tests/cts/net/src/android/net/wifi/cts/ScanResultTest.java
index 8a22bef..836df61 100644
--- a/tests/cts/net/src/android/net/wifi/cts/ScanResultTest.java
+++ b/tests/cts/net/src/android/net/wifi/cts/ScanResultTest.java
@@ -28,6 +28,8 @@
 import android.test.AndroidTestCase;
 import android.util.Log;
 
+import com.android.compatibility.common.util.SystemUtil;
+
 public class ScanResultTest extends AndroidTestCase {
     private static class MySync {
         int expectedState = STATE_NULL;
@@ -121,7 +123,11 @@
     private void setWifiEnabled(boolean enable) throws Exception {
         synchronized (mMySync) {
             mMySync.expectedState = STATE_WIFI_CHANGING;
-            assertTrue(mWifiManager.setWifiEnabled(enable));
+            if (enable) {
+                SystemUtil.runShellCommand("svc wifi enable");
+            } else {
+                SystemUtil.runShellCommand("svc wifi disable");
+            }
             waitForBroadcast(TIMEOUT_MSEC, STATE_WIFI_CHANGED);
        }
     }
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 d3235da..0fce1ee 100644
--- a/tests/cts/net/src/android/net/wifi/cts/WifiEnterpriseConfigTest.java
+++ b/tests/cts/net/src/android/net/wifi/cts/WifiEnterpriseConfigTest.java
@@ -18,13 +18,14 @@
 
 import android.content.Context;
 import android.content.pm.PackageManager;
-import android.net.wifi.WifiConfiguration;
 import android.net.wifi.WifiEnterpriseConfig;
 import android.net.wifi.WifiEnterpriseConfig.Eap;
 import android.net.wifi.WifiEnterpriseConfig.Phase2;
 import android.net.wifi.WifiManager;
 import android.test.AndroidTestCase;
 
+import com.android.compatibility.common.util.SystemUtil;
+
 import java.io.ByteArrayInputStream;
 import java.security.KeyFactory;
 import java.security.PrivateKey;
@@ -33,7 +34,7 @@
 import java.security.spec.PKCS8EncodedKeySpec;
 
 public class WifiEnterpriseConfigTest extends AndroidTestCase {
-    private  WifiManager mWifiManager;
+    private WifiManager mWifiManager;
 
     private static final String SSID = "\"TestSSID\"";
     private static final String IDENTITY = "identity";
@@ -687,7 +688,7 @@
         mWifiManager = (WifiManager) mContext
                 .getSystemService(Context.WIFI_SERVICE);
         assertNotNull(mWifiManager);
-        mWifiManager.setWifiEnabled(true);
+        SystemUtil.runShellCommand("svc wifi enable");
         Thread.sleep(ENABLE_DELAY);
         if (hasWifi()) {
             assertTrue(mWifiManager.isWifiEnabled());
@@ -774,33 +775,6 @@
         assertTrue(config.getDomainSuffixMatch().equals(DOM_SUBJECT_MATCH));
     }
 
-    public void testAddEapNetwork() {
-        if (!hasWifi()) {
-            return;
-        }
-
-        WifiConfiguration config = new WifiConfiguration();
-        WifiEnterpriseConfig enterpriseConfig = new WifiEnterpriseConfig();
-        enterpriseConfig.setEapMethod(Eap.PWD);
-        enterpriseConfig.setIdentity(IDENTITY);
-        enterpriseConfig.setPassword(PASSWORD);
-        config.SSID = SSID;
-        config.enterpriseConfig = enterpriseConfig;
-
-        int netId = mWifiManager.addNetwork(config);
-        assertTrue(doesSsidExist(SSID));
-        mWifiManager.removeNetwork(netId);
-        assertFalse(doesSsidExist(SSID));
-    }
-
-    private boolean doesSsidExist(String ssid) {
-        for (final WifiConfiguration w : mWifiManager.getConfiguredNetworks()) {
-            if (w.SSID.equals(ssid))
-                return true;
-        }
-        return false;
-    }
-
     public void testEnterpriseConfigDoesNotPrintPassword() {
         WifiEnterpriseConfig enterpriseConfig = new WifiEnterpriseConfig();
         final String identity = "IdentityIsOkayToBeDisplayedHere";
diff --git a/tests/cts/net/src/android/net/wifi/cts/WifiInfoTest.java b/tests/cts/net/src/android/net/wifi/cts/WifiInfoTest.java
index 5983cb7..77598ed 100644
--- a/tests/cts/net/src/android/net/wifi/cts/WifiInfoTest.java
+++ b/tests/cts/net/src/android/net/wifi/cts/WifiInfoTest.java
@@ -29,6 +29,7 @@
 import android.test.AndroidTestCase;
 
 import com.android.compatibility.common.util.PollingCheck;
+import com.android.compatibility.common.util.SystemUtil;
 
 import java.util.concurrent.Callable;
 
@@ -104,7 +105,11 @@
     private void setWifiEnabled(boolean enable) throws Exception {
         synchronized (mMySync) {
             mMySync.expectedState = STATE_WIFI_CHANGING;
-            assertTrue(mWifiManager.setWifiEnabled(enable));
+            if (enable) {
+                SystemUtil.runShellCommand("svc wifi enable");
+            } else {
+                SystemUtil.runShellCommand("svc wifi disable");
+            }
             long timeout = System.currentTimeMillis() + TIMEOUT_MSEC;
             while (System.currentTimeMillis() < timeout
                     && mMySync.expectedState == STATE_WIFI_CHANGING)
@@ -134,6 +139,8 @@
         wifiInfo.getBSSID();
         wifiInfo.getIpAddress();
         wifiInfo.getLinkSpeed();
+        wifiInfo.getTxLinkSpeedMbps();
+        wifiInfo.getRxLinkSpeedMbps();
         wifiInfo.getRssi();
         wifiInfo.getHiddenSSID();
         wifiInfo.getMacAddress();
diff --git a/tests/cts/net/src/android/net/wifi/cts/WifiManager_WifiLockTest.java b/tests/cts/net/src/android/net/wifi/cts/WifiLockTest.java
similarity index 74%
rename from tests/cts/net/src/android/net/wifi/cts/WifiManager_WifiLockTest.java
rename to tests/cts/net/src/android/net/wifi/cts/WifiLockTest.java
index 3cdd56a..0703e60 100644
--- a/tests/cts/net/src/android/net/wifi/cts/WifiManager_WifiLockTest.java
+++ b/tests/cts/net/src/android/net/wifi/cts/WifiLockTest.java
@@ -21,17 +21,31 @@
 import android.net.wifi.WifiManager.WifiLock;
 import android.test.AndroidTestCase;
 
-public class WifiManager_WifiLockTest extends AndroidTestCase {
+public class WifiLockTest extends AndroidTestCase {
 
-    private static final String WIFI_TAG = "WifiManager_WifiLockTest";
+    private static final String WIFI_TAG = "WifiLockTest";
 
-    public void testWifiLock() {
+    /**
+     * Verify acquire and release of High Performance wifi locks
+     */
+    public void testHiPerfWifiLock() {
+        testWifiLock(WifiManager.WIFI_MODE_FULL_HIGH_PERF);
+    }
+
+    /**
+     * Verify acquire and release of Low latency wifi locks
+     */
+    public void testLowLatencyWifiLock() {
+        testWifiLock(WifiManager.WIFI_MODE_FULL_LOW_LATENCY);
+    }
+
+    private void testWifiLock(int lockType) {
         if (!WifiFeature.isWifiSupported(getContext())) {
             // skip the test if WiFi is not supported
             return;
         }
         WifiManager wm = (WifiManager) getContext().getSystemService(Context.WIFI_SERVICE);
-        WifiLock wl = wm.createWifiLock(WIFI_TAG);
+        WifiLock wl = wm.createWifiLock(lockType, WIFI_TAG);
 
         wl.setReferenceCounted(true);
         assertFalse(wl.isHeld());
@@ -55,7 +69,7 @@
             // expected
         }
 
-        wl = wm.createWifiLock(WIFI_TAG);
+        wl = wm.createWifiLock(lockType, WIFI_TAG);
         wl.setReferenceCounted(false);
         assertFalse(wl.isHeld());
         wl.acquire();
@@ -68,7 +82,7 @@
         wl.release();
         assertFalse(wl.isHeld());
         assertNotNull(wl.toString());
-        // should be ignored
+        // releasing again after release: but ignored for non-referenced locks
         wl.release();
     }
 }
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 2ed0124..1d66682 100644
--- a/tests/cts/net/src/android/net/wifi/cts/WifiManagerTest.java
+++ b/tests/cts/net/src/android/net/wifi/cts/WifiManagerTest.java
@@ -21,33 +21,40 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.net.ConnectivityManager;
 import android.net.NetworkInfo;
 import android.net.wifi.ScanResult;
 import android.net.wifi.WifiConfiguration;
-import android.net.wifi.WifiConfiguration.Status;
 import android.net.wifi.WifiManager;
 import android.net.wifi.WifiManager.TxPacketCountListener;
 import android.net.wifi.WifiManager.WifiLock;
 import android.net.wifi.hotspot2.PasspointConfiguration;
 import android.net.wifi.hotspot2.pps.Credential;
 import android.net.wifi.hotspot2.pps.HomeSp;
+import android.os.Process;
 import android.os.SystemClock;
 import android.provider.Settings;
+import android.support.test.uiautomator.UiDevice;
 import android.test.AndroidTestCase;
+import android.util.ArraySet;
 import android.util.Log;
 
-import com.android.compatibility.common.util.WifiConfigCreator;
+import androidx.test.InstrumentationRegistry;
+
+import com.android.compatibility.common.util.SystemUtil;
 
 import java.net.HttpURLConnection;
 import java.net.URL;
 import java.security.MessageDigest;
 import java.security.cert.X509Certificate;
-import java.util.HashSet;
 import java.util.List;
-import java.util.Set;
+import java.util.Objects;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicInteger;
+import java.util.stream.Collectors;
 
 public class WifiManagerTest extends AndroidTestCase {
     private static class MySync {
@@ -60,6 +67,7 @@
     private List<ScanResult> mScanResults = null;
     private NetworkInfo mNetworkInfo;
     private Object mLOHSLock = new Object();
+    private UiDevice mUiDevice;
 
     // Please refer to WifiManager
     private static final int MIN_RSSI = -100;
@@ -74,9 +82,6 @@
 
     private static final String TAG = "WifiManagerTest";
     private static final String SSID1 = "\"WifiManagerTest\"";
-    private static final String SSID2 = "\"WifiManagerTestModified\"";
-    private static final String PROXY_TEST_SSID = "SomeProxyAp";
-    private static final String ADD_NETWORK_EXCEPTION_SUBSTR = "addNetwork";
     // A full single scan duration is about 6-7 seconds if country code is set
     // to US. If country code is set to world mode (00), we would expect a scan
     // duration of roughly 8 seconds. So we set scan timeout as 9 seconds here.
@@ -84,11 +89,16 @@
     private static final int TIMEOUT_MSEC = 6000;
     private static final int WAIT_MSEC = 60;
     private static final int DURATION = 10000;
+    private static final int DURATION_SCREEN_TOGGLE = 2000;
     private static final int WIFI_SCAN_TEST_INTERVAL_MILLIS = 60 * 1000;
     private static final int WIFI_SCAN_TEST_CACHE_DELAY_MILLIS = 3 * 60 * 1000;
     private static final int WIFI_SCAN_TEST_ITERATIONS = 5;
 
+    private static final int ENFORCED_NUM_NETWORK_SUGGESTIONS_PER_APP = 50;
+
     private static final String TEST_PAC_URL = "http://www.example.com/proxy.pac";
+    private static final String MANAGED_PROVISIONING_PACKAGE_NAME
+            = "com.android.managedprovisioning";
 
     private IntentFilter mIntentFilter;
     private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
@@ -156,6 +166,8 @@
         mWifiLock.acquire();
         if (!mWifiManager.isWifiEnabled())
             setWifiEnabled(true);
+        mUiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
+        turnScreenOnNoDelay();
         Thread.sleep(DURATION);
         assertTrue(mWifiManager.isWifiEnabled());
         synchronized (mMySync) {
@@ -186,8 +198,8 @@
             } else {
                 mMySync.expectedState = (enable ? STATE_WIFI_ENABLED : STATE_WIFI_DISABLED);
             }
-            // now trigger the change
-            assertTrue(mWifiManager.setWifiEnabled(enable));
+            // now trigger the change using shell commands.
+            SystemUtil.runShellCommand("svc wifi " + (enable ? "enable" : "disable"));
             waitForExpectedWifiState(enable);
         }
     }
@@ -236,7 +248,6 @@
     private void connectWifi() throws Exception {
         synchronized (mMySync) {
             if (mNetworkInfo.getState() == NetworkInfo.State.CONNECTED) return;
-            assertTrue(mWifiManager.reconnect());
             long timeout = System.currentTimeMillis() + TIMEOUT_MSEC;
             while (System.currentTimeMillis() < timeout
                     && mNetworkInfo.getState() != NetworkInfo.State.CONNECTED)
@@ -262,20 +273,13 @@
     }
 
     /**
-     * test point of wifiManager actions:
-     * 1.reconnect
-     * 2.reassociate
-     * 3.disconnect
-     * 4.createWifiLock
+     * Test creation of WifiManager Lock.
      */
-    public void testWifiManagerActions() throws Exception {
+    public void testWifiManagerLock() throws Exception {
         if (!WifiFeature.isWifiSupported(getContext())) {
             // skip the test if WiFi is not supported
             return;
         }
-        assertTrue(mWifiManager.reconnect());
-        assertTrue(mWifiManager.reassociate());
-        assertTrue(mWifiManager.disconnect());
         final String TAG = "Test";
         assertNotNull(mWifiManager.createWifiLock(TAG));
         assertNotNull(mWifiManager.createWifiLock(WifiManager.WIFI_MODE_FULL, TAG));
@@ -396,123 +400,8 @@
         return getContext().getPackageManager().hasSystemFeature(PackageManager.FEATURE_LOCATION);
     }
 
-    /**
-     * test point of wifiManager NetWork:
-     * 1.add NetWork
-     * 2.update NetWork
-     * 3.remove NetWork
-     * 4.enable NetWork
-     * 5.disable NetWork
-     * 6.configured Networks
-     * 7.save configure;
-     */
-    public void testWifiManagerNetWork() throws Exception {
-        if (!WifiFeature.isWifiSupported(getContext())) {
-            // skip the test if WiFi is not supported
-            return;
-        }
-
-        // store the list of enabled networks, so they can be re-enabled after test completes
-        Set<String> enabledSsids = getEnabledNetworks(mWifiManager.getConfiguredNetworks());
-        try {
-            WifiConfiguration wifiConfiguration;
-            // add a WifiConfig
-            final int notExist = -1;
-            List<WifiConfiguration> wifiConfiguredNetworks = mWifiManager.getConfiguredNetworks();
-            int pos = findConfiguredNetworks(SSID1, wifiConfiguredNetworks);
-            if (notExist != pos) {
-                wifiConfiguration = wifiConfiguredNetworks.get(pos);
-                mWifiManager.removeNetwork(wifiConfiguration.networkId);
-            }
-            pos = findConfiguredNetworks(SSID1, wifiConfiguredNetworks);
-            assertEquals(notExist, pos);
-            final int size = wifiConfiguredNetworks.size();
-
-            wifiConfiguration = new WifiConfiguration();
-            wifiConfiguration.SSID = SSID1;
-            wifiConfiguration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
-            int netId = mWifiManager.addNetwork(wifiConfiguration);
-            assertTrue(existSSID(SSID1));
-
-            wifiConfiguredNetworks = mWifiManager.getConfiguredNetworks();
-            assertEquals(size + 1, wifiConfiguredNetworks.size());
-            pos = findConfiguredNetworks(SSID1, wifiConfiguredNetworks);
-            assertTrue(notExist != pos);
-
-            // Enable & disable network
-            boolean disableOthers = true;
-            assertTrue(mWifiManager.enableNetwork(netId, disableOthers));
-            wifiConfiguration = mWifiManager.getConfiguredNetworks().get(pos);
-            assertEquals(Status.ENABLED, wifiConfiguration.status);
-
-            assertTrue(mWifiManager.disableNetwork(netId));
-            wifiConfiguration = mWifiManager.getConfiguredNetworks().get(pos);
-            assertEquals(Status.DISABLED, wifiConfiguration.status);
-
-            // Update a WifiConfig
-            wifiConfiguration = wifiConfiguredNetworks.get(pos);
-            wifiConfiguration.SSID = SSID2;
-            netId = mWifiManager.updateNetwork(wifiConfiguration);
-            assertFalse(existSSID(SSID1));
-            assertTrue(existSSID(SSID2));
-
-            // Remove a WifiConfig
-            assertTrue(mWifiManager.removeNetwork(netId));
-            assertFalse(mWifiManager.removeNetwork(notExist));
-            assertFalse(existSSID(SSID1));
-            assertFalse(existSSID(SSID2));
-
-            assertTrue(mWifiManager.saveConfiguration());
-        } finally {
-            reEnableNetworks(enabledSsids, mWifiManager.getConfiguredNetworks());
-            mWifiManager.saveConfiguration();
-        }
-    }
-
-    /**
-     * Verifies that addNetwork() fails for WifiConfigurations containing a non-null http proxy when
-     * the caller doesn't have OVERRIDE_WIFI_CONFIG permission, DeviceOwner or ProfileOwner device
-     * management policies
-     */
-    public void testSetHttpProxy_PermissionFail() throws Exception {
-        if (!WifiFeature.isWifiSupported(getContext())) {
-            // skip the test if WiFi is not supported
-            return;
-        }
-        WifiConfigCreator configCreator = new WifiConfigCreator(getContext());
-        boolean exceptionThrown = false;
-        try {
-            configCreator.addHttpProxyNetworkVerifyAndRemove(
-                    PROXY_TEST_SSID, TEST_PAC_URL);
-        } catch (IllegalStateException e) {
-            // addHttpProxyNetworkVerifyAndRemove throws three IllegalStateException,
-            // expect it to throw for the addNetwork operation
-            if (e.getMessage().contains(ADD_NETWORK_EXCEPTION_SUBSTR)) {
-                exceptionThrown = true;
-            }
-        }
-        assertTrue(exceptionThrown);
-    }
-
-    private Set<String> getEnabledNetworks(List<WifiConfiguration> configuredNetworks) {
-        Set<String> ssids = new HashSet<String>();
-        for (WifiConfiguration wifiConfig : configuredNetworks) {
-            if (Status.ENABLED == wifiConfig.status || Status.CURRENT == wifiConfig.status) {
-                ssids.add(wifiConfig.SSID);
-                Log.i(TAG, String.format("remembering enabled network %s", wifiConfig.SSID));
-            }
-        }
-        return ssids;
-    }
-
-    private void reEnableNetworks(Set<String> enabledSsids,
-            List<WifiConfiguration> configuredNetworks) {
-        for (WifiConfiguration wifiConfig : configuredNetworks) {
-            if (enabledSsids.contains(wifiConfig.SSID)) {
-                mWifiManager.enableNetwork(wifiConfig.networkId, false);
-                Log.i(TAG, String.format("re-enabling network %s", wifiConfig.SSID));
-            }
-        }
+    private boolean hasAutomotiveFeature() {
+        return getContext().getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE);
     }
 
     public void testSignal() {
@@ -621,164 +510,6 @@
         assertTrue(i < 15);
     }
 
-    /**
-     * Verify Passpoint configuration management APIs (add, remove, get) for a Passpoint
-     * configuration with an user credential.
-     *
-     * @throws Exception
-     */
-    public void testAddPasspointConfigWithUserCredential() throws Exception {
-        if (!WifiFeature.isWifiSupported(getContext())) {
-            // skip the test if WiFi is not supported
-            return;
-        }
-        testAddPasspointConfig(generatePasspointConfig(generateUserCredential()));
-    }
-
-    /**
-     * Verify Passpoint configuration management APIs (add, remove, get) for a Passpoint
-     * configuration with a certificate credential.
-     *
-     * @throws Exception
-     */
-    public void testAddPasspointConfigWithCertCredential() throws Exception {
-        if (!WifiFeature.isWifiSupported(getContext())) {
-            // skip the test if WiFi is not supported
-            return;
-        }
-        testAddPasspointConfig(generatePasspointConfig(generateCertCredential()));
-    }
-
-    /**
-     * Verify Passpoint configuration management APIs (add, remove, get) for a Passpoint
-     * configuration with a SIm credential.
-     *
-     * @throws Exception
-     */
-    public void testAddPasspointConfigWithSimCredential() throws Exception {
-        if (!WifiFeature.isWifiSupported(getContext())) {
-            // skip the test if WiFi is not supported
-            return;
-        }
-        testAddPasspointConfig(generatePasspointConfig(generateSimCredential()));
-    }
-
-    /**
-     * Helper function for generating a {@link PasspointConfiguration} for testing.
-     *
-     * @return {@link PasspointConfiguration}
-     */
-    private PasspointConfiguration generatePasspointConfig(Credential credential) {
-        PasspointConfiguration config = new PasspointConfiguration();
-        config.setCredential(credential);
-
-        // Setup HomeSp.
-        HomeSp homeSp = new HomeSp();
-        homeSp.setFqdn("Test.com");
-        homeSp.setFriendlyName("Test Provider");
-        homeSp.setRoamingConsortiumOis(new long[] {0x11223344});
-        config.setHomeSp(homeSp);
-
-        return config;
-    }
-
-    /**
-     * Helper function for generating an user credential for testing.
-     *
-     * @return {@link Credential}
-     */
-    private Credential generateUserCredential() {
-        Credential credential = new Credential();
-        credential.setRealm("test.net");
-        Credential.UserCredential userCred = new Credential.UserCredential();
-        userCred.setEapType(21 /* EAP_TTLS */);
-        userCred.setUsername("username");
-        userCred.setPassword("password");
-        userCred.setNonEapInnerMethod("PAP");
-        credential.setUserCredential(userCred);
-        credential.setCaCertificate(FakeKeys.CA_PUBLIC_CERT);
-        return credential;
-    }
-
-    /**
-     * Helper function for generating a certificate credential for testing.
-     *
-     * @return {@link Credential}
-     */
-    private Credential generateCertCredential() throws Exception {
-        Credential credential = new Credential();
-        credential.setRealm("test.net");
-        Credential.CertificateCredential certCredential = new Credential.CertificateCredential();
-        certCredential.setCertType("x509v3");
-        certCredential.setCertSha256Fingerprint(
-                MessageDigest.getInstance("SHA-256").digest(FakeKeys.CLIENT_CERT.getEncoded()));
-        credential.setCertCredential(certCredential);
-        credential.setCaCertificate(FakeKeys.CA_PUBLIC_CERT);
-        credential.setClientCertificateChain(new X509Certificate[] {FakeKeys.CLIENT_CERT});
-        credential.setClientPrivateKey(FakeKeys.RSA_KEY1);
-        return credential;
-    }
-
-    /**
-     * Helper function for generating a SIM credential for testing.
-     *
-     * @return {@link Credential}
-     */
-    private Credential generateSimCredential() throws Exception {
-        Credential credential = new Credential();
-        credential.setRealm("test.net");
-        Credential.SimCredential simCredential = new Credential.SimCredential();
-        simCredential.setImsi("1234*");
-        simCredential.setEapType(18 /* EAP_SIM */);
-        credential.setSimCredential(simCredential);
-        return credential;
-    }
-
-    /**
-     * Helper function verifying Passpoint configuration management APIs (add, remove, get) for
-     * a given configuration.
-     *
-     * @param config The configuration to test with
-     */
-    private void testAddPasspointConfig(PasspointConfiguration config) throws Exception {
-        try {
-
-            // obtain number of passpoint networks already present in device (preloaded)
-            List<PasspointConfiguration> preConfigList = mWifiManager.getPasspointConfigurations();
-            int numOfNetworks = preConfigList.size();
-
-            // add new (test) configuration
-            mWifiManager.addOrUpdatePasspointConfiguration(config);
-
-            // Certificates and keys will be set to null after it is installed to the KeyStore by
-            // WifiManager.  Reset them in the expected config so that it can be used to compare
-            // against the retrieved config.
-            config.getCredential().setCaCertificate(null);
-            config.getCredential().setClientCertificateChain(null);
-            config.getCredential().setClientPrivateKey(null);
-
-            // retrieve the configuration and verify it. The retrieved list may not be in order -
-            // check all configs to see if any match
-            List<PasspointConfiguration> configList = mWifiManager.getPasspointConfigurations();
-            assertEquals(numOfNetworks + 1, configList.size());
-
-            boolean anyMatch = false;
-            for (PasspointConfiguration passpointConfiguration : configList) {
-                if (passpointConfiguration.equals(config)) {
-                    anyMatch = true;
-                    break;
-                }
-            }
-            assertTrue(anyMatch);
-
-            // remove the (test) configuration and verify number of installed configurations
-            mWifiManager.removePasspointConfiguration(config.getHomeSp().getFqdn());
-            assertEquals(mWifiManager.getPasspointConfigurations().size(), numOfNetworks);
-        } catch (UnsupportedOperationException e) {
-            // Passpoint build config |config_wifi_hotspot2_enabled| is disabled, so noop.
-        }
-    }
-
     public class TestLocalOnlyHotspotCallback extends WifiManager.LocalOnlyHotspotCallback {
         Object hotspotLock;
         WifiManager.LocalOnlyHotspotReservation reservation = null;
@@ -835,6 +566,11 @@
             // check if we got the callback
             assertTrue(callback.onStartedCalled);
             assertNotNull(callback.reservation.getWifiConfiguration());
+            if (!hasAutomotiveFeature()) {
+                assertEquals(
+                        WifiConfiguration.AP_BAND_2GHZ,
+                        callback.reservation.getWifiConfiguration().apBand);
+            }
             assertFalse(callback.onFailedCalled);
             assertFalse(callback.onStoppedCalled);
         }
@@ -890,37 +626,37 @@
     }
 
     /**
-     * Verify calls to setWifiEnabled from a non-settings app while softap mode is active do not
-     * exit softap mode.
-     *
-     * This test uses the LocalOnlyHotspot API to enter softap mode.  This should also be true when
-     * tethering is started.
-     * Note: Location mode must be enabled for this test.
+     * Verify calls to deprecated API's all fail for non-settings apps targeting >= Q SDK.
      */
-    public void testSetWifiEnabledByAppDoesNotStopHotspot() throws Exception {
+    public void testDeprecatedApis() throws Exception {
         if (!WifiFeature.isWifiSupported(getContext())) {
             // skip the test if WiFi is not supported
             return;
         }
-        // check that softap mode is supported by the device
-        if (!mWifiManager.isPortableHotspotSupported()) {
-            return;
-        }
+        setWifiEnabled(true);
+        connectWifi(); // ensures that there is at-least 1 saved network on the device.
+
+        WifiConfiguration wifiConfiguration = new WifiConfiguration();
+        wifiConfiguration.SSID = SSID1;
+        wifiConfiguration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
+
+        assertEquals(WifiConfiguration.INVALID_NETWORK_ID,
+                mWifiManager.addNetwork(wifiConfiguration));
+        assertEquals(WifiConfiguration.INVALID_NETWORK_ID,
+                mWifiManager.updateNetwork(wifiConfiguration));
+        assertFalse(mWifiManager.enableNetwork(0, true));
+        assertFalse(mWifiManager.disableNetwork(0));
+        assertFalse(mWifiManager.removeNetwork(0));
+        assertFalse(mWifiManager.disconnect());
+        assertFalse(mWifiManager.reconnect());
+        assertFalse(mWifiManager.reassociate());
+        assertTrue(mWifiManager.getConfiguredNetworks().isEmpty());
 
         boolean wifiEnabled = mWifiManager.isWifiEnabled();
-
-        if (wifiEnabled) {
-            // disable wifi so we have something to turn on (some devices may be able to run
-            // simultaneous modes)
-            setWifiEnabled(false);
-        }
-
-        TestLocalOnlyHotspotCallback callback = startLocalOnlyHotspot();
-
-        // now we should fail to turn on wifi
-        assertFalse(mWifiManager.setWifiEnabled(true));
-
-        stopLocalOnlyHotspot(callback, wifiEnabled);
+        // now we should fail to toggle wifi state.
+        assertFalse(mWifiManager.setWifiEnabled(!wifiEnabled));
+        Thread.sleep(DURATION);
+        assertEquals(wifiEnabled, mWifiManager.isWifiEnabled());
     }
 
     /**
@@ -969,4 +705,324 @@
 
         stopLocalOnlyHotspot(callback, wifiEnabled);
     }
+
+    /**
+     * Verify that the {@link android.Manifest.permission#NETWORK_STACK} permission is never held by
+     * any package.
+     * <p>
+     * No apps should <em>ever</em> attempt to acquire this permission, since it would give those
+     * apps extremely broad access to connectivity functionality.
+     */
+    public void testNetworkStackPermission() {
+        final PackageManager pm = getContext().getPackageManager();
+
+        final List<PackageInfo> holding = pm.getPackagesHoldingPermissions(new String[] {
+                android.Manifest.permission.NETWORK_STACK
+        }, PackageManager.MATCH_UNINSTALLED_PACKAGES);
+        for (PackageInfo pi : holding) {
+            fail("The NETWORK_STACK permission must not be held by " + pi.packageName
+                    + " and must be revoked for security reasons");
+        }
+    }
+
+    /**
+     * Verify that the {@link android.Manifest.permission#NETWORK_SETTINGS} permission is
+     * never held by any package.
+     * <p>
+     * Only Settings, SysUi, NetworkStack and shell apps should <em>ever</em> attempt to acquire
+     * this permission, since it would give those apps extremely broad access to connectivity
+     * functionality.  The permission is intended to be granted to only those apps with direct user
+     * access and no others.
+     */
+    public void testNetworkSettingsPermission() {
+        final PackageManager pm = getContext().getPackageManager();
+
+        final ArraySet<String> allowedPackages = new ArraySet();
+        final ArraySet<Integer> allowedUIDs = new ArraySet();
+        // explicitly add allowed UIDs
+        allowedUIDs.add(Process.SYSTEM_UID);
+        allowedUIDs.add(Process.SHELL_UID);
+        allowedUIDs.add(Process.PHONE_UID);
+        allowedUIDs.add(Process.NETWORK_STACK_UID);
+
+        // only quick settings is allowed to bind to the BIND_QUICK_SETTINGS_TILE permission, using
+        // this fact to determined allowed package name for sysui
+        String validPkg = "";
+        final List<PackageInfo> sysuiPackage = pm.getPackagesHoldingPermissions(new String[] {
+                android.Manifest.permission.BIND_QUICK_SETTINGS_TILE
+        }, PackageManager.MATCH_UNINSTALLED_PACKAGES);
+
+        if (sysuiPackage.size() > 1) {
+            fail("The BIND_QUICK_SETTINGS_TILE permission must only be held by one package");
+        }
+
+        if (sysuiPackage.size() == 1) {
+            validPkg = sysuiPackage.get(0).packageName;
+            allowedPackages.add(validPkg);
+        }
+
+        // the captive portal flow also currently holds the NETWORK_SETTINGS permission
+        final Intent intent = new Intent(ConnectivityManager.ACTION_CAPTIVE_PORTAL_SIGN_IN);
+        final ResolveInfo ri = pm.resolveActivity(intent, PackageManager.MATCH_DISABLED_COMPONENTS);
+        if (ri != null) {
+            allowedPackages.add(ri.activityInfo.packageName);
+        }
+
+        final List<PackageInfo> holding = pm.getPackagesHoldingPermissions(new String[] {
+                android.Manifest.permission.NETWORK_SETTINGS
+        }, PackageManager.MATCH_UNINSTALLED_PACKAGES);
+        for (PackageInfo pi : holding) {
+            String packageName = pi.packageName;
+            if (allowedPackages.contains(packageName)) {
+                // this is an explicitly allowed package
+            } else {
+                // now check if the packages are from allowed UIDs
+                boolean allowed = false;
+                try {
+                    if (allowedUIDs.contains(pm.getPackageUid(packageName, 0))) {
+                        allowed = true;
+                        Log.d(TAG, packageName + " is on an allowed UID");
+                    }
+                } catch (PackageManager.NameNotFoundException e) { }
+                if (!allowed) {
+                    fail("The NETWORK_SETTINGS permission must not be held by "
+                            + packageName + " and must be revoked for security reasons");
+                }
+            }
+        }
+    }
+
+    /**
+     * Verify that the {@link android.Manifest.permission#NETWORK_SETUP_WIZARD} permission is
+     * only held by the device setup wizard application.
+     * <p>
+     * Only the SetupWizard app should <em>ever</em> attempt to acquire this
+     * permission, since it would give those apps extremely broad access to connectivity
+     * functionality.  The permission is intended to be granted to only the device setup wizard.
+     */
+    public void testNetworkSetupWizardPermission() {
+        final PackageManager pm = getContext().getPackageManager();
+
+        final Intent intent = new Intent(Intent.ACTION_MAIN);
+        intent.addCategory(Intent.CATEGORY_SETUP_WIZARD);
+        final ResolveInfo ri = pm.resolveActivity(intent, PackageManager.MATCH_DISABLED_COMPONENTS);
+        String validPkg = "";
+        if (ri != null) {
+            validPkg = ri.activityInfo.packageName;
+        }
+
+        final List<PackageInfo> holding = pm.getPackagesHoldingPermissions(new String[] {
+                android.Manifest.permission.NETWORK_SETUP_WIZARD
+        }, PackageManager.MATCH_UNINSTALLED_PACKAGES);
+        for (PackageInfo pi : holding) {
+            if (!Objects.equals(pi.packageName, validPkg)) {
+                fail("The NETWORK_SETUP_WIZARD permission must not be held by " + pi.packageName
+                    + " and must be revoked for security reasons [" + validPkg +"]");
+            }
+        }
+    }
+
+    /**
+     * Verify that the {@link android.Manifest.permission#NETWORK_MANAGED_PROVISIONING} permission
+     * is only held by the device managed provisioning application.
+     * <p>
+     * Only the ManagedProvisioning app should <em>ever</em> attempt to acquire this
+     * permission, since it would give those apps extremely broad access to connectivity
+     * functionality.  The permission is intended to be granted to only the device managed
+     * provisioning.
+     */
+    public void testNetworkManagedProvisioningPermission() {
+        final PackageManager pm = getContext().getPackageManager();
+
+        // TODO(b/115980767): Using hardcoded package name. Need a better mechanism to find the
+        // managed provisioning app.
+        // Ensure that the package exists.
+        final Intent intent = new Intent(Intent.ACTION_MAIN);
+        intent.setPackage(MANAGED_PROVISIONING_PACKAGE_NAME);
+        final ResolveInfo ri = pm.resolveActivity(intent, PackageManager.MATCH_DISABLED_COMPONENTS);
+        String validPkg = "";
+        if (ri != null) {
+            validPkg = ri.activityInfo.packageName;
+        }
+
+        final List<PackageInfo> holding = pm.getPackagesHoldingPermissions(new String[] {
+                android.Manifest.permission.NETWORK_MANAGED_PROVISIONING
+        }, PackageManager.MATCH_UNINSTALLED_PACKAGES);
+        for (PackageInfo pi : holding) {
+            if (!Objects.equals(pi.packageName, validPkg)) {
+                fail("The NETWORK_MANAGED_PROVISIONING permission must not be held by "
+                        + pi.packageName + " and must be revoked for security reasons ["
+                        + validPkg +"]");
+            }
+        }
+    }
+
+    /**
+     * Verify that the {@link android.Manifest.permission#WIFI_SET_DEVICE_MOBILITY_STATE} permission
+     * is held by at most one application.
+     */
+    public void testWifiSetDeviceMobilityStatePermission() {
+        final PackageManager pm = getContext().getPackageManager();
+
+        final List<PackageInfo> holding = pm.getPackagesHoldingPermissions(new String[] {
+                android.Manifest.permission.WIFI_SET_DEVICE_MOBILITY_STATE
+        }, PackageManager.MATCH_UNINSTALLED_PACKAGES);
+
+        List<String> uniquePackageNames = holding
+                .stream()
+                .map(pi -> pi.packageName)
+                .distinct()
+                .collect(Collectors.toList());
+
+        if (uniquePackageNames.size() > 1) {
+            fail("The WIFI_SET_DEVICE_MOBILITY_STATE permission must not be held by more than one "
+                    + "application, but is held by " + uniquePackageNames.size() + " applications: "
+                    + String.join(", ", uniquePackageNames));
+        }
+    }
+
+    /**
+     * Verify that the {@link android.Manifest.permission#NETWORK_CARRIER_PROVISIONING} permission
+     * is held by at most one application.
+     */
+    public void testNetworkCarrierProvisioningPermission() {
+        final PackageManager pm = getContext().getPackageManager();
+
+        final List<PackageInfo> holding = pm.getPackagesHoldingPermissions(new String[] {
+                android.Manifest.permission.NETWORK_CARRIER_PROVISIONING
+        }, PackageManager.MATCH_UNINSTALLED_PACKAGES);
+
+        List<String> uniquePackageNames = holding
+                .stream()
+                .map(pi -> pi.packageName)
+                .distinct()
+                .collect(Collectors.toList());
+
+        if (uniquePackageNames.size() > 1) {
+            fail("The NETWORK_CARRIER_PROVISIONING permission must not be held by more than one "
+                    + "application, but is held by " + uniquePackageNames.size() + " applications: "
+                    + String.join(", ", uniquePackageNames));
+        }
+    }
+
+    /**
+     * Verify that the {@link android.Manifest.permission#WIFI_UPDATE_USABILITY_STATS_SCORE}
+     * permission is held by at most one application.
+     */
+    public void testUpdateWifiUsabilityStatsScorePermission() {
+        final PackageManager pm = getContext().getPackageManager();
+
+        final List<PackageInfo> holding = pm.getPackagesHoldingPermissions(new String[] {
+                android.Manifest.permission.WIFI_UPDATE_USABILITY_STATS_SCORE
+        }, PackageManager.MATCH_UNINSTALLED_PACKAGES);
+
+        List<String> uniquePackageNames = holding
+                .stream()
+                .map(pi -> pi.packageName)
+                .distinct()
+                .collect(Collectors.toList());
+
+        if (uniquePackageNames.size() > 1) {
+            fail("The WIFI_UPDATE_USABILITY_STATS_SCORE permission must not be held by more than "
+                + "one application, but is held by " + uniquePackageNames.size() + " applications: "
+                + String.join(", ", uniquePackageNames));
+        }
+    }
+
+    private void turnScreenOnNoDelay() throws Exception {
+        mUiDevice.executeShellCommand("input keyevent KEYCODE_WAKEUP");
+        mUiDevice.executeShellCommand("wm dismiss-keyguard");
+    }
+
+    private void turnScreenOn() throws Exception {
+        turnScreenOnNoDelay();
+        // Since the screen on/off intent is ordered, they will not be sent right now.
+        Thread.sleep(DURATION_SCREEN_TOGGLE);
+    }
+
+    private void turnScreenOff() throws Exception {
+        mUiDevice.executeShellCommand("input keyevent KEYCODE_SLEEP");
+        // Since the screen on/off intent is ordered, they will not be sent right now.
+        Thread.sleep(DURATION_SCREEN_TOGGLE);
+    }
+
+    /**
+     * Verify that Wi-Fi scanning is not turned off when the screen turns off while wifi is disabled
+     * but location is on.
+     * @throws Exception
+     */
+    public void testScreenOffDoesNotTurnOffWifiScanningWhenWifiDisabled() throws Exception {
+        if (!WifiFeature.isWifiSupported(getContext())) {
+            // skip the test if WiFi is not supported
+            return;
+        }
+        if (!hasLocationFeature()) {
+            // skip the test if location is not supported
+            return;
+        }
+        if (!isLocationEnabled()) {
+            fail("Please enable location for this test - since Marshmallow WiFi scan results are"
+                    + " empty when location is disabled!");
+        }
+        if(!mWifiManager.isScanAlwaysAvailable()) {
+            fail("Please enable Wi-Fi scanning for this test!");
+        }
+        setWifiEnabled(false);
+        turnScreenOn();
+        assertWifiScanningIsOn();
+        // Toggle screen and verify Wi-Fi scanning is still on.
+        turnScreenOff();
+        assertWifiScanningIsOn();
+        turnScreenOn();
+        assertWifiScanningIsOn();
+    }
+
+    /**
+     * Verify that Wi-Fi scanning is not turned off when the screen turns off while wifi is enabled.
+     * @throws Exception
+     */
+    public void testScreenOffDoesNotTurnOffWifiScanningWhenWifiEnabled() throws Exception {
+        if (!WifiFeature.isWifiSupported(getContext())) {
+            // skip the test if WiFi is not supported
+            return;
+        }
+        if (!hasLocationFeature()) {
+            // skip the test if location is not supported
+            return;
+        }
+        if (!isLocationEnabled()) {
+            fail("Please enable location for this test - since Marshmallow WiFi scan results are"
+                    + " empty when location is disabled!");
+        }
+        if(!mWifiManager.isScanAlwaysAvailable()) {
+            fail("Please enable Wi-Fi scanning for this test!");
+        }
+        setWifiEnabled(true);
+        turnScreenOn();
+        assertWifiScanningIsOn();
+        // Toggle screen and verify Wi-Fi scanning is still on.
+        turnScreenOff();
+        assertWifiScanningIsOn();
+        turnScreenOn();
+        assertWifiScanningIsOn();
+    }
+
+    /**
+     * Verify that the platform supports a reasonable number of suggestions per app.
+     * @throws Exception
+     */
+    public void testMaxNumberOfNetworkSuggestionsPerApp() throws Exception {
+        if (!WifiFeature.isWifiSupported(getContext())) {
+            // skip the test if WiFi is not supported
+            return;
+        }
+        assertTrue(mWifiManager.getMaxNumberOfNetworkSuggestionsPerApp()
+                > ENFORCED_NUM_NETWORK_SUGGESTIONS_PER_APP);
+    }
+
+    private void assertWifiScanningIsOn() {
+        if(!mWifiManager.isScanAlwaysAvailable()) {
+            fail("Wi-Fi scanning should be on.");
+        }
+    }
 }
diff --git a/tests/cts/net/src/android/net/wifi/p2p/cts/WifiP2pConfigTest.java b/tests/cts/net/src/android/net/wifi/p2p/cts/WifiP2pConfigTest.java
new file mode 100644
index 0000000..639db8a
--- /dev/null
+++ b/tests/cts/net/src/android/net/wifi/p2p/cts/WifiP2pConfigTest.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2019 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.wifi.p2p.cts;
+
+import android.net.MacAddress;
+import android.net.wifi.p2p.WifiP2pConfig;
+import android.net.wifi.p2p.WifiP2pGroup;
+import android.test.AndroidTestCase;
+
+public class WifiP2pConfigTest extends AndroidTestCase {
+    static final String TEST_NETWORK_NAME = "DIRECT-xy-Hello";
+    static final String TEST_PASSPHRASE = "8etterW0r1d";
+    static final int TEST_OWNER_BAND = WifiP2pConfig.GROUP_OWNER_BAND_5GHZ;
+    static final int TEST_OWNER_FREQ = 2447;
+    static final String TEST_DEVICE_ADDRESS = "aa:bb:cc:dd:ee:ff";
+
+    public void testWifiP2pConfigBuilderForPersist() {
+        WifiP2pConfig.Builder builder = new WifiP2pConfig.Builder();
+        builder.setNetworkName(TEST_NETWORK_NAME)
+                .setPassphrase(TEST_PASSPHRASE)
+                .setGroupOperatingBand(TEST_OWNER_BAND)
+                .setDeviceAddress(MacAddress.fromString(TEST_DEVICE_ADDRESS))
+                .enablePersistentMode(true);
+        WifiP2pConfig config = builder.build();
+
+        assertTrue(config.deviceAddress.equals(TEST_DEVICE_ADDRESS));
+        assertTrue(config.networkName.equals(TEST_NETWORK_NAME));
+        assertTrue(config.passphrase.equals(TEST_PASSPHRASE));
+        assertEquals(config.groupOwnerBand, TEST_OWNER_BAND);
+        assertEquals(config.netId, WifiP2pGroup.PERSISTENT_NET_ID);
+    }
+
+    public void testWifiP2pConfigBuilderForNonPersist() {
+        WifiP2pConfig.Builder builder = new WifiP2pConfig.Builder();
+        builder.setNetworkName(TEST_NETWORK_NAME)
+                .setPassphrase(TEST_PASSPHRASE)
+                .setGroupOperatingFrequency(TEST_OWNER_FREQ)
+                .setDeviceAddress(MacAddress.fromString(TEST_DEVICE_ADDRESS))
+                .enablePersistentMode(false);
+        WifiP2pConfig config = builder.build();
+
+        assertTrue(config.deviceAddress.equals(TEST_DEVICE_ADDRESS));
+        assertTrue(config.networkName.equals(TEST_NETWORK_NAME));
+        assertTrue(config.passphrase.equals(TEST_PASSPHRASE));
+        assertEquals(config.groupOwnerBand, TEST_OWNER_FREQ);
+        assertEquals(config.netId, WifiP2pGroup.TEMPORARY_NET_ID);
+    }
+}
diff --git a/tests/cts/net/src/android/net/wifi/rtt/OWNERS b/tests/cts/net/src/android/net/wifi/rtt/OWNERS
deleted file mode 100644
index 4afc47f..0000000
--- a/tests/cts/net/src/android/net/wifi/rtt/OWNERS
+++ /dev/null
@@ -1,2 +0,0 @@
-etancohen@google.com
-satk@google.com
\ No newline at end of file
diff --git a/tests/cts/net/src/android/net/wifi/rtt/cts/TestBase.java b/tests/cts/net/src/android/net/wifi/rtt/cts/TestBase.java
index 57ea2a5..07d5718 100644
--- a/tests/cts/net/src/android/net/wifi/rtt/cts/TestBase.java
+++ b/tests/cts/net/src/android/net/wifi/rtt/cts/TestBase.java
@@ -32,6 +32,8 @@
 import android.os.HandlerThread;
 import android.test.AndroidTestCase;
 
+import com.android.compatibility.common.util.SystemUtil;
+
 import java.util.List;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.Executor;
@@ -93,7 +95,7 @@
         mWifiLock = mWifiManager.createWifiLock(TAG);
         mWifiLock.acquire();
         if (!mWifiManager.isWifiEnabled()) {
-            mWifiManager.setWifiEnabled(true);
+            SystemUtil.runShellCommand("svc wifi enable");
         }
 
         IntentFilter intentFilter = new IntentFilter();
diff --git a/tests/cts/net/src/android/net/wifi/rtt/cts/WifiRttTest.java b/tests/cts/net/src/android/net/wifi/rtt/cts/WifiRttTest.java
index 74a0c3d..20791c0 100644
--- a/tests/cts/net/src/android/net/wifi/rtt/cts/WifiRttTest.java
+++ b/tests/cts/net/src/android/net/wifi/rtt/cts/WifiRttTest.java
@@ -16,11 +16,9 @@
 
 package android.net.wifi.rtt.cts;
 
-import android.content.IntentFilter;
 import android.net.wifi.ScanResult;
 import android.net.wifi.rtt.RangingRequest;
 import android.net.wifi.rtt.RangingResult;
-import android.net.wifi.rtt.WifiRttManager;
 
 import com.android.compatibility.common.util.DeviceReportLog;
 import com.android.compatibility.common.util.ResultType;
@@ -64,7 +62,10 @@
 
         // Scan for IEEE 802.11mc supporting APs
         ScanResult testAp = scanForTestAp(NUM_SCANS_SEARCHING_FOR_IEEE80211MC_AP);
-        assertTrue("Cannot find test AP", testAp != null);
+        assertTrue(
+                "Cannot find any test APs which support RTT / IEEE 802.11mc - please verify that "
+                        + "your test setup includes them!",
+                testAp != null);
 
         // Perform RTT operations
         RangingRequest request = new RangingRequest.Builder().addAccessPoint(testAp).build();