Merge "Create CTS test folder for IKE and EAP" into rvc-dev
diff --git a/tests/cts/net/assets/OWNERS b/tests/cts/net/assets/OWNERS
new file mode 100644
index 0000000..14edd1d
--- /dev/null
+++ b/tests/cts/net/assets/OWNERS
@@ -0,0 +1,4 @@
+# Bug component: 31808
+etancohen@google.com
+lorenzo@google.com
+satk@google.com
diff --git a/tests/cts/net/assets/ValidPasspointProfile.base64 b/tests/cts/net/assets/ValidPasspointProfile.base64
new file mode 100644
index 0000000..3f60eb8
--- /dev/null
+++ b/tests/cts/net/assets/ValidPasspointProfile.base64
@@ -0,0 +1 @@
+Q29udGVudC1UeXBlOiBtdWx0aXBhcnQvbWl4ZWQ7IGJvdW5kYXJ5PXtib3VuZGFyeX0KQ29udGVudC1UcmFuc2Zlci1FbmNvZGluZzogYmFzZTY0CgotLXtib3VuZGFyeX0KQ29udGVudC1UeXBlOiBhcHBsaWNhdGlvbi94LXBhc3Nwb2ludC1wcm9maWxlCkNvbnRlbnQtVHJhbnNmZXItRW5jb2Rpbmc6IGJhc2U2NAoKUEUxbmJYUlVjbVZsSUhodGJHNXpQU0p6ZVc1amJXdzZaRzFrWkdZeExqSWlQZ29nSUR4V1pYSkVWRVErTVM0eVBDOVdaWEpFVkVRK0NpQWdQRTV2WkdVK0NpQWdJQ0E4VG05a1pVNWhiV1UrVUdWeVVISnZkbWxrWlhKVGRXSnpZM0pwY0hScGIyNDhMMDV2WkdWT1lXMWxQZ29nSUNBZ1BGSlVVSEp2Y0dWeWRHbGxjejRLSUNBZ0lDQWdQRlI1Y0dVK0NpQWdJQ0FnSUNBZ1BFUkVSazVoYldVK2RYSnVPbmRtWVRwdGJ6cG9iM1J6Y0c5ME1tUnZkREF0Y0dWeWNISnZkbWxrWlhKemRXSnpZM0pwY0hScGIyNDZNUzR3UEM5RVJFWk9ZVzFsUGdvZ0lDQWdJQ0E4TDFSNWNHVStDaUFnSUNBOEwxSlVVSEp2Y0dWeWRHbGxjejRLSUNBZ0lEeE9iMlJsUGdvZ0lDQWdJQ0E4VG05a1pVNWhiV1UrYVRBd01Ud3ZUbTlrWlU1aGJXVStDaUFnSUNBZ0lEeE9iMlJsUGdvZ0lDQWdJQ0FnSUR4T2IyUmxUbUZ0WlQ1SWIyMWxVMUE4TDA1dlpHVk9ZVzFsUGdvZ0lDQWdJQ0FnSUR4T2IyUmxQZ29nSUNBZ0lDQWdJQ0FnUEU1dlpHVk9ZVzFsUGtaeWFXVnVaR3g1VG1GdFpUd3ZUbTlrWlU1aGJXVStDaUFnSUNBZ0lDQWdJQ0E4Vm1Gc2RXVStRVlJVSUZCaGMzTndiMmx1ZER3dlZtRnNkV1UrQ2lBZ0lDQWdJQ0FnUEM5T2IyUmxQZ29nSUNBZ0lDQWdJRHhPYjJSbFBnb2dJQ0FnSUNBZ0lDQWdQRTV2WkdWT1lXMWxQa1pSUkU0OEwwNXZaR1ZPWVcxbFBnb2dJQ0FnSUNBZ0lDQWdQRlpoYkhWbFBtRjBkSGRwWm1rdVkyOXRQQzlXWVd4MVpUNEtJQ0FnSUNBZ0lDQThMMDV2WkdVK0NpQWdJQ0FnSUR3dlRtOWtaVDRLSUNBZ0lDQWdQRTV2WkdVK0NpQWdJQ0FnSUNBZ1BFNXZaR1ZPWVcxbFBrTnlaV1JsYm5ScFlXdzhMMDV2WkdWT1lXMWxQZ29nSUNBZ0lDQWdJRHhPYjJSbFBnb2dJQ0FnSUNBZ0lDQWdQRTV2WkdWT1lXMWxQbEpsWVd4dFBDOU9iMlJsVG1GdFpUNEtJQ0FnSUNBZ0lDQWdJRHhXWVd4MVpUNTNiR0Z1TG0xdVl6UXhNQzV0WTJNek1UQXVNMmR3Y0c1bGRIZHZjbXN1YjNKblBDOVdZV3gxWlQ0S0lDQWdJQ0FnSUNBOEwwNXZaR1UrQ2lBZ0lDQWdJQ0FnUEU1dlpHVStDaUFnSUNBZ0lDQWdJQ0E4VG05a1pVNWhiV1UrVTBsTlBDOU9iMlJsVG1GdFpUNEtJQ0FnSUNBZ0lDQWdJRHhPYjJSbFBnb2dJQ0FnSUNBZ0lDQWdJQ0E4VG05a1pVNWhiV1UrU1UxVFNUd3ZUbTlrWlU1aGJXVStDaUFnSUNBZ0lDQWdJQ0FnSUR4V1lXeDFaVDR6TVRBME1UQXFQQzlXWVd4MVpUNEtJQ0FnSUNBZ0lDQWdJRHd2VG05a1pUNEtJQ0FnSUNBZ0lDQWdJQ0FnUEU1dlpHVStDaUFnSUNBZ0lDQWdJQ0FnSUNBZ1BFNXZaR1ZPWVcxbFBrVkJVRlI1Y0dVOEwwNXZaR1ZPWVcxbFBnb2dJQ0FnSUNBZ0lDQWdJQ0FnSUR4V1lXeDFaVDR5TXp3dlZtRnNkV1UrQ2lBZ0lDQWdJQ0FnSUNBZ0lEd3ZUbTlrWlQ0S0lDQWdJQ0FnSUNBOEwwNXZaR1UrQ2lBZ0lDQWdJRHd2VG05a1pUNEtJQ0FnSUR3dlRtOWtaVDRLSUNBOEwwNXZaR1UrQ2p3dlRXZHRkRlJ5WldVKwotLXtib3VuZGFyeX0tLQo=
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 ba0832f..d91bce8 100644
--- a/tests/cts/net/src/android/net/wifi/cts/ConcurrencyTest.java
+++ b/tests/cts/net/src/android/net/wifi/cts/ConcurrencyTest.java
@@ -30,15 +30,23 @@
 import android.net.NetworkInfo;
 import android.net.NetworkRequest;
 import android.net.wifi.WifiManager;
+import android.net.wifi.p2p.WifiP2pDevice;
+import android.net.wifi.p2p.WifiP2pGroup;
+import android.net.wifi.p2p.WifiP2pGroupList;
+import android.net.wifi.p2p.WifiP2pInfo;
 import android.net.wifi.p2p.WifiP2pManager;
+import android.net.wifi.p2p.nsd.WifiP2pServiceInfo;
+import android.net.wifi.p2p.nsd.WifiP2pUpnpServiceInfo;
 import android.provider.Settings;
 import android.platform.test.annotations.AppModeFull;
 import android.test.AndroidTestCase;
 import android.util.Log;
 
+import com.android.compatibility.common.util.ShellIdentityUtils;
 import com.android.compatibility.common.util.SystemUtil;
 
 import java.util.Arrays;
+import java.util.ArrayList;
 import java.util.BitSet;
 import java.util.LinkedList;
 import java.util.List;
@@ -69,6 +77,10 @@
         public int p2pState;
         public int discoveryState;
         public NetworkInfo networkInfo;
+        public WifiP2pInfo p2pInfo;
+        public String deviceName;
+        public WifiP2pGroupList persistentGroups;
+        public WifiP2pGroup group = new WifiP2pGroup();
     }
 
     private WifiManager mWifiManager;
@@ -76,6 +88,7 @@
     private WifiP2pManager.Channel mWifiP2pChannel;
     private MySync mMySync = new MySync();
     private MyResponse mMyResponse = new MyResponse();
+    private boolean mWasVerboseLoggingEnabled;
 
     private static final String TAG = "ConcurrencyTest";
     private static final int TIMEOUT_MSEC = 6000;
@@ -119,6 +132,27 @@
         }
     };
 
+    private WifiP2pManager.ActionListener mActionListener = 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, "failure reason: " + reason);
+                mMyResponse.valid = true;
+                mMyResponse.success = false;
+                mMyResponse.notify();
+            }
+        }
+    };
+
     @Override
     protected void setUp() throws Exception {
        super.setUp();
@@ -127,6 +161,7 @@
             // skip the test if WiFi && p2p are not supported
             return;
         }
+
         mIntentFilter = new IntentFilter();
         mIntentFilter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
         mIntentFilter.addAction(WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION);
@@ -140,6 +175,13 @@
             SystemUtil.runShellCommand("svc wifi disable");
             Thread.sleep(DURATION);
         }
+
+        // turn on verbose logging for tests
+        mWasVerboseLoggingEnabled = ShellIdentityUtils.invokeWithShellPermissions(
+                () -> mWifiManager.isVerboseLoggingEnabled());
+        ShellIdentityUtils.invokeWithShellPermissions(
+                () -> mWifiManager.setVerboseLoggingEnabled(true));
+
         assertTrue(!mWifiManager.isWifiEnabled());
         mMySync.expectedWifiState = WifiManager.WIFI_STATE_DISABLED;
         mMySync.expectedP2pState = WifiP2pManager.WIFI_P2P_STATE_DISABLED;
@@ -157,6 +199,9 @@
         }
         mContext.unregisterReceiver(mReceiver);
 
+        ShellIdentityUtils.invokeWithShellPermissions(
+                () -> mWifiManager.setVerboseLoggingEnabled(mWasVerboseLoggingEnabled));
+
         enableWifi();
         super.tearDown();
     }
@@ -221,6 +266,10 @@
         synchronized (responseObj) {
             responseObj.valid = false;
             responseObj.networkInfo = null;
+            responseObj.p2pInfo = null;
+            responseObj.deviceName = null;
+            responseObj.persistentGroups = null;
+            responseObj.group = null;
         }
     }
 
@@ -344,26 +393,7 @@
         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();
-                }
-            }
-        });
+        mWifiP2pManager.discoverPeers(mWifiP2pChannel, mActionListener);
         assertTrue(waitForServiceResponse(mMyResponse));
         assertTrue(mMyResponse.success);
         assertTrue(waitForBroadcasts(MySync.DISCOVERY_STATE));
@@ -411,26 +441,7 @@
                 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();
-                }
-            }
-        });
+        mWifiP2pManager.createGroup(mWifiP2pChannel, mActionListener);
         assertTrue(waitForServiceResponse(mMyResponse));
         assertTrue(mMyResponse.success);
         assertTrue(waitForBroadcasts(MySync.NETWORK_INFO));
@@ -455,7 +466,239 @@
         assertEquals(NetworkInfo.DetailedState.CONNECTED,
                 mMyResponse.networkInfo.getDetailedState());
 
-        mWifiP2pManager.removeGroup(mWifiP2pChannel, null);
+        resetResponse(mMyResponse);
+        mWifiP2pManager.requestConnectionInfo(mWifiP2pChannel,
+                new WifiP2pManager.ConnectionInfoListener() {
+                    @Override
+                    public void onConnectionInfoAvailable(WifiP2pInfo info) {
+                        synchronized (mMyResponse) {
+                            mMyResponse.valid = true;
+                            mMyResponse.p2pInfo = new WifiP2pInfo(info);
+                            mMyResponse.notify();
+                        }
+                    }
+                });
+        assertTrue(waitForServiceResponse(mMyResponse));
+        assertNotNull(mMyResponse.p2pInfo);
+        assertTrue(mMyResponse.p2pInfo.groupFormed);
+        assertTrue(mMyResponse.p2pInfo.isGroupOwner);
+
+        resetResponse(mMyResponse);
+        mWifiP2pManager.requestGroupInfo(mWifiP2pChannel,
+                new WifiP2pManager.GroupInfoListener() {
+                    @Override
+                    public void onGroupInfoAvailable(WifiP2pGroup group) {
+                        synchronized (mMyResponse) {
+                            mMyResponse.group = new WifiP2pGroup(group);
+                            mMyResponse.valid = true;
+                            mMyResponse.notify();
+                        }
+                    }
+                });
+        assertTrue(waitForServiceResponse(mMyResponse));
+        assertNotNull(mMyResponse.group);
+        assertNotEquals(0, mMyResponse.group.getFrequency());
+        assertTrue(mMyResponse.group.getNetworkId() >= 0);
+
+        resetResponse(mMyResponse);
+        mWifiP2pManager.removeGroup(mWifiP2pChannel, mActionListener);
+        assertTrue(waitForServiceResponse(mMyResponse));
+        assertTrue(mMyResponse.success);
     }
 
+    private String getDeviceName() {
+        resetResponse(mMyResponse);
+        mWifiP2pManager.requestDeviceInfo(mWifiP2pChannel,
+                new WifiP2pManager.DeviceInfoListener() {
+                    @Override
+                    public void onDeviceInfoAvailable(WifiP2pDevice wifiP2pDevice) {
+                        synchronized (mMyResponse) {
+                            mMyResponse.deviceName = wifiP2pDevice.deviceName;
+                            mMyResponse.valid = true;
+                            mMyResponse.notify();
+                        }
+                    }
+                });
+        assertTrue(waitForServiceResponse(mMyResponse));
+        return mMyResponse.deviceName;
+    }
+
+    public void testSetDeviceName() {
+        if (!setupWifiP2p()) {
+            return;
+        }
+
+        String testDeviceName = "test";
+        String originalDeviceName = getDeviceName();
+        assertNotNull(originalDeviceName);
+
+        ShellIdentityUtils.invokeWithShellPermissions(() -> {
+            mWifiP2pManager.setDeviceName(
+                    mWifiP2pChannel, testDeviceName, mActionListener);
+            assertTrue(waitForServiceResponse(mMyResponse));
+            assertTrue(mMyResponse.success);
+        });
+
+        String currentDeviceName = getDeviceName();
+        assertEquals(testDeviceName, currentDeviceName);
+
+        // restore the device name at the end
+        ShellIdentityUtils.invokeWithShellPermissions(() -> {
+            mWifiP2pManager.setDeviceName(
+                    mWifiP2pChannel, originalDeviceName, mActionListener);
+            assertTrue(waitForServiceResponse(mMyResponse));
+            assertTrue(mMyResponse.success);
+        });
+    }
+
+    private WifiP2pGroupList getPersistentGroups() {
+        resetResponse(mMyResponse);
+        ShellIdentityUtils.invokeWithShellPermissions(() -> {
+            mWifiP2pManager.requestPersistentGroupInfo(mWifiP2pChannel,
+                    new WifiP2pManager.PersistentGroupInfoListener() {
+                        @Override
+                        public void onPersistentGroupInfoAvailable(WifiP2pGroupList groups) {
+                            synchronized (mMyResponse) {
+                                mMyResponse.persistentGroups = groups;
+                                mMyResponse.valid = true;
+                                mMyResponse.notify();
+                            }
+                        }
+                    });
+            assertTrue(waitForServiceResponse(mMyResponse));
+        });
+        return mMyResponse.persistentGroups;
+    }
+
+    public void testPersistentGroupOperation() {
+        if (!setupWifiP2p()) {
+            return;
+        }
+
+        resetResponse(mMyResponse);
+        mWifiP2pManager.createGroup(mWifiP2pChannel, mActionListener);
+        assertTrue(waitForServiceResponse(mMyResponse));
+        assertTrue(mMyResponse.success);
+        assertTrue(waitForBroadcasts(MySync.NETWORK_INFO));
+        assertNotNull(mMySync.expectedNetworkInfo);
+        assertEquals(NetworkInfo.DetailedState.CONNECTED,
+                mMySync.expectedNetworkInfo.getDetailedState());
+
+        resetResponse(mMyResponse);
+        mWifiP2pManager.removeGroup(mWifiP2pChannel, mActionListener);
+        assertTrue(waitForServiceResponse(mMyResponse));
+        assertTrue(mMyResponse.success);
+
+        WifiP2pGroupList persistentGroups = getPersistentGroups();
+        assertNotNull(persistentGroups);
+        assertEquals(1, persistentGroups.getGroupList().size());
+
+        resetResponse(mMyResponse);
+        final int firstNetworkId = persistentGroups.getGroupList().get(0).getNetworkId();
+        ShellIdentityUtils.invokeWithShellPermissions(() -> {
+            mWifiP2pManager.deletePersistentGroup(mWifiP2pChannel,
+                    firstNetworkId,
+                    mActionListener);
+            assertTrue(waitForServiceResponse(mMyResponse));
+            assertTrue(mMyResponse.success);
+        });
+
+        persistentGroups = getPersistentGroups();
+        assertNotNull(persistentGroups);
+        assertEquals(0, persistentGroups.getGroupList().size());
+
+        resetResponse(mMyResponse);
+        mWifiP2pManager.createGroup(mWifiP2pChannel, mActionListener);
+        assertTrue(waitForServiceResponse(mMyResponse));
+        assertTrue(mMyResponse.success);
+        assertTrue(waitForBroadcasts(MySync.NETWORK_INFO));
+        assertNotNull(mMySync.expectedNetworkInfo);
+        assertEquals(NetworkInfo.DetailedState.CONNECTED,
+                mMySync.expectedNetworkInfo.getDetailedState());
+
+        resetResponse(mMyResponse);
+        mWifiP2pManager.removeGroup(mWifiP2pChannel, mActionListener);
+        assertTrue(waitForServiceResponse(mMyResponse));
+        assertTrue(mMyResponse.success);
+
+        resetResponse(mMyResponse);
+        ShellIdentityUtils.invokeWithShellPermissions(() -> {
+            mWifiP2pManager.factoryReset(mWifiP2pChannel, mActionListener);
+            assertTrue(waitForServiceResponse(mMyResponse));
+            assertTrue(mMyResponse.success);
+        });
+
+        persistentGroups = getPersistentGroups();
+        assertNotNull(persistentGroups);
+        assertEquals(0, persistentGroups.getGroupList().size());
+    }
+
+    public void testP2pListening() {
+        if (!setupWifiP2p()) {
+            return;
+        }
+
+        resetResponse(mMyResponse);
+        ShellIdentityUtils.invokeWithShellPermissions(() -> {
+            mWifiP2pManager.setWifiP2pChannels(mWifiP2pChannel, 6, 11, mActionListener);
+            assertTrue(waitForServiceResponse(mMyResponse));
+            assertTrue(mMyResponse.success);
+        });
+
+        resetResponse(mMyResponse);
+        ShellIdentityUtils.invokeWithShellPermissions(() -> {
+            mWifiP2pManager.startListening(mWifiP2pChannel, mActionListener);
+            assertTrue(waitForServiceResponse(mMyResponse));
+            assertTrue(mMyResponse.success);
+        });
+
+        resetResponse(mMyResponse);
+        ShellIdentityUtils.invokeWithShellPermissions(() -> {
+            mWifiP2pManager.stopListening(mWifiP2pChannel, mActionListener);
+            assertTrue(waitForServiceResponse(mMyResponse));
+            assertTrue(mMyResponse.success);
+        });
+    }
+
+    public void testP2pService() {
+        if (!setupWifiP2p()) {
+            return;
+        }
+
+        // This only store the listener to the WifiP2pManager internal variable, nothing to fail.
+        mWifiP2pManager.setServiceResponseListener(mWifiP2pChannel,
+                new WifiP2pManager.ServiceResponseListener() {
+                    @Override
+                    public void onServiceAvailable(
+                            int protocolType, byte[] responseData, WifiP2pDevice srcDevice) {
+                    }
+                });
+
+        resetResponse(mMyResponse);
+        List<String> services = new ArrayList<String>();
+        services.add("urn:schemas-upnp-org:service:AVTransport:1");
+        services.add("urn:schemas-upnp-org:service:ConnectionManager:1");
+        WifiP2pServiceInfo rendererService = WifiP2pUpnpServiceInfo.newInstance(
+                "6859dede-8574-59ab-9332-123456789011",
+                "urn:schemas-upnp-org:device:MediaRenderer:1",
+                services);
+        mWifiP2pManager.addLocalService(mWifiP2pChannel,
+                rendererService,
+                mActionListener);
+        assertTrue(waitForServiceResponse(mMyResponse));
+        assertTrue(mMyResponse.success);
+
+        resetResponse(mMyResponse);
+        mWifiP2pManager.removeLocalService(mWifiP2pChannel,
+                rendererService,
+                mActionListener);
+        assertTrue(waitForServiceResponse(mMyResponse));
+        assertTrue(mMyResponse.success);
+
+        resetResponse(mMyResponse);
+        mWifiP2pManager.clearLocalServices(mWifiP2pChannel,
+                mActionListener);
+        assertTrue(waitForServiceResponse(mMyResponse));
+        assertTrue(mMyResponse.success);
+    }
 }
diff --git a/tests/cts/net/src/android/net/wifi/cts/EasyConnectStatusCallbackTest.java b/tests/cts/net/src/android/net/wifi/cts/EasyConnectStatusCallbackTest.java
new file mode 100644
index 0000000..eef50a0
--- /dev/null
+++ b/tests/cts/net/src/android/net/wifi/cts/EasyConnectStatusCallbackTest.java
@@ -0,0 +1,170 @@
+/*
+ * Copyright (C) 2020 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 static android.net.wifi.EasyConnectStatusCallback.EASY_CONNECT_EVENT_FAILURE_TIMEOUT;
+import static android.net.wifi.WifiConfiguration.SECURITY_TYPE_PSK;
+import static android.net.wifi.WifiManager.EASY_CONNECT_NETWORK_ROLE_STA;
+
+import android.app.UiAutomation;
+import android.content.Context;
+import android.net.wifi.EasyConnectStatusCallback;
+import android.net.wifi.WifiConfiguration;
+import android.net.wifi.WifiManager;
+import android.os.Handler;
+import android.os.HandlerExecutor;
+import android.os.HandlerThread;
+import android.test.AndroidTestCase;
+import android.util.SparseArray;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import java.util.concurrent.Executor;
+
+public class EasyConnectStatusCallbackTest extends AndroidTestCase {
+    private static final String TEST_SSID = "\"testSsid\"";
+    private static final String TEST_PASSPHRASE = "\"testPassword\"";
+    private static final int TEST_WAIT_DURATION_MS = 12_000; // Long delay is necessary, see below
+    private WifiManager mWifiManager;
+    private static final String TEST_DPP_URI =
+            "DPP:C:81/1;I:Easy_Connect_Demo;M:000102030405;"
+                    + "K:MDkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDIgACDmtXD1Sz6/5B4YRdmTkbkkFLDwk8f0yRnfm1Go"
+                    + "kpx/0=;;";
+    private final HandlerThread mHandlerThread = new HandlerThread("EasyConnectTest");
+    protected final Executor mExecutor;
+    {
+        mHandlerThread.start();
+        mExecutor = new HandlerExecutor(new Handler(mHandlerThread.getLooper()));
+    }
+    private final Object mLock = new Object();
+    private boolean mOnFailureCallback = false;
+    private int mErrorCode;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        if (!WifiFeature.isWifiSupported(getContext())) {
+            // skip the test if WiFi is not supported
+            return;
+        }
+
+        mWifiManager = (WifiManager) getContext().getSystemService(Context.WIFI_SERVICE);
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        super.tearDown();
+    }
+
+    private EasyConnectStatusCallback mEasyConnectStatusCallback = new EasyConnectStatusCallback() {
+        @Override
+        public void onEnrolleeSuccess(int newNetworkId) {
+
+        }
+
+        @Override
+        public void onConfiguratorSuccess(int code) {
+
+        }
+
+        @Override
+        public void onProgress(int code) {
+
+        }
+
+        @Override
+        public void onFailure(int code) {
+            synchronized (mLock) {
+                mOnFailureCallback = true;
+                mErrorCode = code;
+                mLock.notify();
+            }
+        }
+
+        public void onFailure(int code, String ssid, SparseArray<int[]> channelListArray,
+                int[] operatingClassArray) {
+            synchronized (mLock) {
+                mOnFailureCallback = true;
+                mErrorCode = code;
+                mLock.notify();
+            }
+        }
+    };
+
+    /**
+     * Tests {@link android.net.wifi.EasyConnectStatusCallback} class.
+     *
+     * Since Easy Connect requires 2 devices, start Easy Connect session and expect an error.
+     */
+    public void testConfiguratorInitiatorOnFailure() throws Exception {
+        if (!WifiFeature.isWifiSupported(getContext())) {
+            // skip the test if WiFi is not supported
+            return;
+        }
+        UiAutomation uiAutomation = InstrumentationRegistry.getInstrumentation().getUiAutomation();
+        try {
+            uiAutomation.adoptShellPermissionIdentity();
+            WifiConfiguration config;
+            config = new WifiConfiguration();
+            config.SSID = TEST_SSID;
+            config.preSharedKey = TEST_PASSPHRASE;
+            config.setSecurityParams(SECURITY_TYPE_PSK);
+            int networkId = mWifiManager.addNetwork(config);
+            assertFalse(networkId == -1);
+            synchronized (mLock) {
+                mWifiManager.startEasyConnectAsConfiguratorInitiator(TEST_DPP_URI, networkId,
+                        EASY_CONNECT_NETWORK_ROLE_STA, mExecutor, mEasyConnectStatusCallback);
+                // Note: A long delay is necessary because there is no enrollee, and the system
+                // tries to discover it. We will wait for a timeout error to occur.
+                mLock.wait(TEST_WAIT_DURATION_MS);
+            }
+            mWifiManager.removeNetwork(networkId);
+            assertTrue(mOnFailureCallback);
+            assertEquals(EASY_CONNECT_EVENT_FAILURE_TIMEOUT, mErrorCode);
+            mWifiManager.stopEasyConnectSession();
+        } finally {
+            uiAutomation.dropShellPermissionIdentity();
+        }
+    }
+
+    /**
+     * Tests {@link android.net.wifi.EasyConnectStatusCallback} class.
+     *
+     * Since Easy Connect requires 2 devices, start Easy Connect session and expect an error.
+     */
+    public void testEnrolleeInitiatorOnFailure() throws Exception {
+        if (!WifiFeature.isWifiSupported(getContext())) {
+            // skip the test if WiFi is not supported
+            return;
+        }
+        UiAutomation uiAutomation = InstrumentationRegistry.getInstrumentation().getUiAutomation();
+        try {
+            uiAutomation.adoptShellPermissionIdentity();
+            synchronized (mLock) {
+                mWifiManager.startEasyConnectAsEnrolleeInitiator(TEST_DPP_URI, mExecutor,
+                        mEasyConnectStatusCallback);
+                // Note: A long delay is necessary because there is no configurator, and the system
+                // tries to discover it. We will wait for a timeout error to occur.
+                mLock.wait(TEST_WAIT_DURATION_MS);
+            }
+            assertTrue(mOnFailureCallback);
+            assertEquals(EASY_CONNECT_EVENT_FAILURE_TIMEOUT, mErrorCode);
+            mWifiManager.stopEasyConnectSession();
+        } finally {
+            uiAutomation.dropShellPermissionIdentity();
+        }
+    }
+}
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 3347cb6..161b0b4 100644
--- a/tests/cts/net/src/android/net/wifi/cts/ScanResultTest.java
+++ b/tests/cts/net/src/android/net/wifi/cts/ScanResultTest.java
@@ -16,6 +16,10 @@
 
 package android.net.wifi.cts;
 
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import java.nio.ByteBuffer;
 import java.util.List;
 
 import android.content.BroadcastReceiver;
@@ -23,12 +27,12 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.net.wifi.ScanResult;
+import android.net.wifi.ScanResult.InformationElement;
 import android.net.wifi.WifiInfo;
 import android.net.wifi.WifiManager;
 import android.net.wifi.WifiManager.WifiLock;
 import android.platform.test.annotations.AppModeFull;
 import android.test.AndroidTestCase;
-import android.util.Log;
 
 import com.android.compatibility.common.util.SystemUtil;
 
@@ -57,6 +61,14 @@
     private static final int SCAN_MAX_RETRY_COUNT = 6;
     private static final int SCAN_FIND_BSSID_MAX_RETRY_COUNT = 5;
     private static final long SCAN_FIND_BSSID_WAIT_MSEC = 5_000L;
+
+    private static final String TEST_SSID = "TEST_SSID";
+    public static final String TEST_BSSID = "04:ac:fe:45:34:10";
+    public static final String TEST_CAPS = "CCMP";
+    public static final int TEST_LEVEL = -56;
+    public static final int TEST_FREQUENCY = 2412;
+    public static final long TEST_TIMESTAMP = 4660L;
+
     private IntentFilter mIntentFilter;
     private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
         @Override
@@ -100,13 +112,13 @@
 
         mContext.registerReceiver(mReceiver, mIntentFilter);
         mWifiManager = (WifiManager) getContext().getSystemService(Context.WIFI_SERVICE);
-        assertNotNull(mWifiManager);
+        assertThat(mWifiManager).isNotNull();
         mWifiLock = mWifiManager.createWifiLock(TAG);
         mWifiLock.acquire();
         if (!mWifiManager.isWifiEnabled())
             setWifiEnabled(true);
         Thread.sleep(ENABLE_WAIT_MSEC);
-        assertTrue(mWifiManager.isWifiEnabled());
+        assertThat(mWifiManager.isWifiEnabled()).isTrue();
         mMySync.expectedState = STATE_NULL;
     }
 
@@ -150,14 +162,50 @@
             // skip the test if WiFi is not supported
             return;
         }
-        List<ScanResult> scanResults = mWifiManager.getScanResults();
         // this test case should in Wifi environment
-        for (int i = 0; i < scanResults.size(); i++) {
-            ScanResult mScanResult = scanResults.get(i);
-            assertNotNull(mScanResult.toString());
+        for (ScanResult scanResult : mWifiManager.getScanResults()) {
+            assertThat(scanResult.toString()).isNotNull();
+
+            for (InformationElement ie : scanResult.getInformationElements()) {
+                testInformationElementCopyConstructor(ie);
+                testInformationElementFields(ie);
+            }
+
+            assertThat(scanResult.getWifiStandard()).isAnyOf(
+                    ScanResult.WIFI_STANDARD_UNKNOWN,
+                    ScanResult.WIFI_STANDARD_LEGACY,
+                    ScanResult.WIFI_STANDARD_11N,
+                    ScanResult.WIFI_STANDARD_11AC,
+                    ScanResult.WIFI_STANDARD_11AX
+            );
+
+            scanResult.isPasspointNetwork();
         }
     }
 
+    private void testInformationElementCopyConstructor(InformationElement ie) {
+        InformationElement copy = new InformationElement(ie);
+
+        assertThat(copy.getId()).isEqualTo(ie.getId());
+        assertThat(copy.getIdExt()).isEqualTo(ie.getIdExt());
+        assertThat(copy.getBytes()).isEqualTo(ie.getBytes());
+    }
+
+    private void testInformationElementFields(InformationElement ie) {
+        // id is 1 octet
+        int id = ie.getId();
+        assertThat(id).isAtLeast(0);
+        assertThat(id).isAtMost(255);
+
+        // idExt is 0 or 1 octet
+        int idExt = ie.getIdExt();
+        assertThat(idExt).isAtLeast(0);
+        assertThat(idExt).isAtMost(255);
+
+        ByteBuffer bytes = ie.getBytes();
+        assertThat(bytes).isNotNull();
+    }
+
     /* Multiple scans to ensure bssid is updated */
     private void scanAndWait() throws Exception {
         synchronized (mMySync) {
@@ -186,7 +234,7 @@
         for (ScanResult result : scanResults) {
             BSSID = result.BSSID;
             timestamp = result.timestamp;
-            assertTrue(timestamp != 0);
+            assertThat(timestamp).isNotEqualTo(0);
             break;
         }
 
@@ -196,11 +244,34 @@
         for (ScanResult result : scanResults) {
             if (result.BSSID.equals(BSSID)) {
                 long timeDiff = (result.timestamp - timestamp) / 1000;
-                assertTrue (timeDiff > 0);
-                assertTrue (timeDiff < 6 * SCAN_WAIT_MSEC);
+                assertThat(timeDiff).isGreaterThan(0L);
+                assertThat(timeDiff).isLessThan(6L * SCAN_WAIT_MSEC);
             }
         }
+    }
 
+    /** Test that the copy constructor copies fields correctly. */
+    public void testScanResultConstructors() throws Exception {
+        if (!WifiFeature.isWifiSupported(getContext())) {
+            // skip the test if WiFi is not supported
+            return;
+        }
+
+        ScanResult scanResult = new ScanResult();
+        scanResult.SSID = TEST_SSID;
+        scanResult.BSSID = TEST_BSSID;
+        scanResult.capabilities = TEST_CAPS;
+        scanResult.level = TEST_LEVEL;
+        scanResult.frequency = TEST_FREQUENCY;
+        scanResult.timestamp = TEST_TIMESTAMP;
+
+        ScanResult scanResult2 = new ScanResult(scanResult);
+        assertThat(scanResult2.SSID).isEqualTo(TEST_SSID);
+        assertThat(scanResult2.BSSID).isEqualTo(TEST_BSSID);
+        assertThat(scanResult2.capabilities).isEqualTo(TEST_CAPS);
+        assertThat(scanResult2.level).isEqualTo(TEST_LEVEL);
+        assertThat(scanResult2.frequency).isEqualTo(TEST_FREQUENCY);
+        assertThat(scanResult2.timestamp).isEqualTo(TEST_TIMESTAMP);
     }
 
     public void testScanResultMatchesWifiInfo() throws Exception {
@@ -211,7 +282,7 @@
 
         // This test case should run while connected to Wifi
         final WifiInfo wifiInfo = mWifiManager.getConnectionInfo();
-        assertNotNull(wifiInfo);
+        assertThat(wifiInfo).isNotNull();
 
         ScanResult currentNetwork = null;
         for (int i = 0; i < SCAN_FIND_BSSID_MAX_RETRY_COUNT; i++) {
@@ -225,16 +296,17 @@
             }
             Thread.sleep(SCAN_FIND_BSSID_WAIT_MSEC);
         }
-        assertNotNull("Current network not found in scan results", currentNetwork);
+        assertWithMessage("Current network not found in scan results")
+                .that(currentNetwork).isNotNull();
 
         String wifiInfoSsidQuoted = wifiInfo.getSSID();
         String scanResultSsidUnquoted = currentNetwork.SSID;
 
-        assertEquals(
+        assertWithMessage(
                 "SSID mismatch: make sure this isn't a hidden network or an SSID containing "
-                        + "non-UTF-8 characters - neither is supported by this CTS test.",
-                wifiInfoSsidQuoted,
-                "\"" + scanResultSsidUnquoted + "\"");
-        assertEquals(wifiInfo.getFrequency(), currentNetwork.frequency);
+                        + "non-UTF-8 characters - neither is supported by this CTS test.")
+                .that("\"" + scanResultSsidUnquoted + "\"")
+                .isEqualTo(wifiInfoSsidQuoted);
+        assertThat(currentNetwork.frequency).isEqualTo(wifiInfo.getFrequency());
     }
 }
diff --git a/tests/cts/net/src/android/net/wifi/cts/WifiFrameworkInitializerTest.java b/tests/cts/net/src/android/net/wifi/cts/WifiFrameworkInitializerTest.java
new file mode 100644
index 0000000..d714ed6
--- /dev/null
+++ b/tests/cts/net/src/android/net/wifi/cts/WifiFrameworkInitializerTest.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2020 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.net.wifi.WifiFrameworkInitializer;
+import android.test.AndroidTestCase;
+
+public class WifiFrameworkInitializerTest extends AndroidTestCase {
+    /**
+     * WifiFrameworkInitializer.registerServiceWrappers() should only be called by
+     * SystemServiceRegistry during boot up when Wifi is first initialized. Calling this API at
+     * any other time should throw an exception.
+     */
+    public void testRegisterServiceWrappers_failsWhenCalledOutsideOfSystemServiceRegistry() {
+        if (!WifiFeature.isWifiSupported(getContext())) {
+            // skip the test if WiFi is not supported
+            return;
+        }
+        try {
+            WifiFrameworkInitializer.registerServiceWrappers();
+            fail("Expected exception when calling "
+                    + "WifiFrameworkInitializer.registerServiceWrappers() outside of "
+                    + "SystemServiceRegistry!");
+        } catch (IllegalStateException expected) {}
+    }
+}
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 6f94fea..557710d 100644
--- a/tests/cts/net/src/android/net/wifi/cts/WifiInfoTest.java
+++ b/tests/cts/net/src/android/net/wifi/cts/WifiInfoTest.java
@@ -33,6 +33,7 @@
 import com.android.compatibility.common.util.PollingCheck;
 import com.android.compatibility.common.util.SystemUtil;
 
+import java.nio.charset.StandardCharsets;
 import java.util.concurrent.Callable;
 
 @AppModeFull(reason = "Cannot get WifiManager in instant app mode")
@@ -49,6 +50,12 @@
     private static final int STATE_WIFI_CHANGING = 1;
     private static final int STATE_WIFI_CHANGED = 2;
 
+    private static final String TEST_SSID = "Test123";
+    private static final String TEST_BSSID = "12:12:12:12:12:12";
+    private static final int TEST_RSSI = -60;
+    private static final int TEST_NETWORK_ID = 5;
+    private static final int TEST_NETWORK_ID2 = 6;
+
     private static final String TAG = "WifiInfoTest";
     private static final int TIMEOUT_MSEC = 6000;
     private static final int WAIT_MSEC = 60;
@@ -207,4 +214,42 @@
         assertThat(wifiInfo.getMaxSupportedTxLinkSpeedMbps()).isAtLeast(-1);
         assertThat(wifiInfo.getMaxSupportedRxLinkSpeedMbps()).isAtLeast(-1);
     }
+
+    /**
+     * Test that the WifiInfo Builder returns the same values that was set, and that
+     * calling build multiple times returns different instances.
+     */
+    public void testWifiInfoBuilder() throws Exception {
+        WifiInfo.Builder builder = new WifiInfo.Builder()
+                .setSsid(TEST_SSID.getBytes(StandardCharsets.UTF_8))
+                .setBssid(TEST_BSSID)
+                .setRssi(TEST_RSSI)
+                .setNetworkId(TEST_NETWORK_ID);
+
+        WifiInfo info1 = builder.build();
+
+        assertThat(info1.getSSID()).isEqualTo("\"" + TEST_SSID + "\"");
+        assertThat(info1.getBSSID()).isEqualTo(TEST_BSSID);
+        assertThat(info1.getRssi()).isEqualTo(TEST_RSSI);
+        assertThat(info1.getNetworkId()).isEqualTo(TEST_NETWORK_ID);
+
+        WifiInfo info2 = builder
+                .setNetworkId(TEST_NETWORK_ID2)
+                .build();
+
+        // different instances
+        assertThat(info1).isNotSameAs(info2);
+
+        // assert that info1 didn't change
+        assertThat(info1.getSSID()).isEqualTo("\"" + TEST_SSID + "\"");
+        assertThat(info1.getBSSID()).isEqualTo(TEST_BSSID);
+        assertThat(info1.getRssi()).isEqualTo(TEST_RSSI);
+        assertThat(info1.getNetworkId()).isEqualTo(TEST_NETWORK_ID);
+
+        // assert that info2 changed
+        assertThat(info2.getSSID()).isEqualTo("\"" + TEST_SSID + "\"");
+        assertThat(info2.getBSSID()).isEqualTo(TEST_BSSID);
+        assertThat(info2.getRssi()).isEqualTo(TEST_RSSI);
+        assertThat(info2.getNetworkId()).isEqualTo(TEST_NETWORK_ID2);
+    }
 }
diff --git a/tests/cts/net/src/android/net/wifi/cts/WifiLockTest.java b/tests/cts/net/src/android/net/wifi/cts/WifiLockTest.java
index 6ac92d4..fee9ef0 100644
--- a/tests/cts/net/src/android/net/wifi/cts/WifiLockTest.java
+++ b/tests/cts/net/src/android/net/wifi/cts/WifiLockTest.java
@@ -19,6 +19,7 @@
 import android.content.Context;
 import android.net.wifi.WifiManager;
 import android.net.wifi.WifiManager.WifiLock;
+import android.os.WorkSource;
 import android.platform.test.annotations.AppModeFull;
 import android.test.AndroidTestCase;
 
@@ -50,6 +51,7 @@
         WifiLock wl = wm.createWifiLock(lockType, WIFI_TAG);
 
         wl.setReferenceCounted(true);
+        wl.setWorkSource(new WorkSource());
         assertFalse(wl.isHeld());
         wl.acquire();
         assertTrue(wl.isHeld());
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 743454f..870cf72 100644
--- a/tests/cts/net/src/android/net/wifi/cts/WifiManagerTest.java
+++ b/tests/cts/net/src/android/net/wifi/cts/WifiManagerTest.java
@@ -16,6 +16,13 @@
 
 package android.net.wifi.cts;
 
+import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
+import static android.net.wifi.WifiConfiguration.INVALID_NETWORK_ID;
+import static android.net.wifi.WifiManager.TrafficStateCallback.DATA_ACTIVITY_INOUT;
+
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import static org.junit.Assert.assertNotEquals;
 
 import android.app.UiAutomation;
 import android.content.BroadcastReceiver;
@@ -25,20 +32,30 @@
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
+import android.net.util.MacAddressUtils;
 import android.net.ConnectivityManager;
+import android.net.LinkProperties;
 import android.net.MacAddress;
+import android.net.Network;
+import android.net.NetworkCapabilities;
 import android.net.NetworkInfo;
+import android.net.NetworkRequest;
 import android.net.wifi.ScanResult;
 import android.net.wifi.SoftApConfiguration;
 import android.net.wifi.WifiConfiguration;
+import android.net.wifi.WifiInfo;
 import android.net.wifi.WifiManager;
 import android.net.wifi.WifiManager.WifiLock;
+import android.net.wifi.WifiNetworkConnectionStatistics;
+import android.net.wifi.hotspot2.ConfigParser;
+import android.net.wifi.hotspot2.PasspointConfiguration;
 import android.os.Process;
 import android.os.SystemClock;
 import android.os.UserHandle;
 import android.platform.test.annotations.AppModeFull;
 import android.provider.Settings;
 import android.support.test.uiautomator.UiDevice;
+import android.telephony.TelephonyManager;
 import android.test.AndroidTestCase;
 import android.text.TextUtils;
 import android.util.ArraySet;
@@ -46,12 +63,24 @@
 
 import androidx.test.platform.app.InstrumentationRegistry;
 
+import com.android.compatibility.common.util.PollingCheck;
+import com.android.compatibility.common.util.ShellIdentityUtils;
 import com.android.compatibility.common.util.SystemUtil;
+import com.android.compatibility.common.util.ThrowingRunnable;
 
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.net.HttpURLConnection;
+import java.net.URL;
 import java.util.List;
+import java.util.Locale;
 import java.util.Objects;
+import java.util.concurrent.Callable;
 import java.util.concurrent.ConcurrentLinkedQueue;
 import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
 import java.util.concurrent.TimeUnit;
 import java.util.stream.Collectors;
 
@@ -62,12 +91,14 @@
     }
 
     private WifiManager mWifiManager;
+    private ConnectivityManager mConnectivityManager;
     private WifiLock mWifiLock;
     private static MySync mMySync;
     private List<ScanResult> mScanResults = null;
     private NetworkInfo mNetworkInfo;
-    private Object mLOHSLock = new Object();
+    private final Object mLock = new Object();
     private UiDevice mUiDevice;
+    private boolean mWasVerboseLoggingEnabled;
 
     // Please refer to WifiManager
     private static final int MIN_RSSI = -100;
@@ -88,8 +119,9 @@
     private static final int SCAN_TIMEOUT_MSEC = 9000;
     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 = 10_000;
     private static final int DURATION_SCREEN_TOGGLE = 2000;
+    private static final int DURATION_SETTINGS_TOGGLE = 1_000;
     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;
@@ -103,6 +135,9 @@
     private static final String TEST_SSID_UNQUOTED = "testSsid1";
     private static final MacAddress TEST_MAC = MacAddress.fromString("aa:bb:cc:dd:ee:ff");
     private static final String TEST_PASSPHRASE = "passphrase";
+    private static final String PASSPOINT_INSTALLATION_FILE_WITH_CA_CERT =
+            "assets/ValidPasspointProfile.base64";
+    private static final String TYPE_WIFI_CONFIG = "application/x-wifi-config";
 
     private IntentFilter mIntentFilter;
     private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
@@ -165,7 +200,15 @@
 
         mContext.registerReceiver(mReceiver, mIntentFilter);
         mWifiManager = (WifiManager) getContext().getSystemService(Context.WIFI_SERVICE);
+        mConnectivityManager = getContext().getSystemService(ConnectivityManager.class);
         assertNotNull(mWifiManager);
+
+        // turn on verbose logging for tests
+        mWasVerboseLoggingEnabled = ShellIdentityUtils.invokeWithShellPermissions(
+                () -> mWifiManager.isVerboseLoggingEnabled());
+        ShellIdentityUtils.invokeWithShellPermissions(
+                () -> mWifiManager.setVerboseLoggingEnabled(true));
+
         mWifiLock = mWifiManager.createWifiLock(TAG);
         mWifiLock.acquire();
         if (!mWifiManager.isWifiEnabled())
@@ -177,6 +220,10 @@
         synchronized (mMySync) {
             mMySync.expectedState = STATE_NULL;
         }
+
+        List<WifiConfiguration> savedNetworks = ShellIdentityUtils.invokeWithShellPermissions(
+                mWifiManager::getConfiguredNetworks);
+        assertFalse("Need at least one saved network", savedNetworks.isEmpty());
     }
 
     @Override
@@ -190,6 +237,8 @@
             setWifiEnabled(true);
         mWifiLock.release();
         mContext.unregisterReceiver(mReceiver);
+        ShellIdentityUtils.invokeWithShellPermissions(
+                () -> mWifiManager.setVerboseLoggingEnabled(mWasVerboseLoggingEnabled));
         Thread.sleep(DURATION);
         super.tearDown();
     }
@@ -242,15 +291,24 @@
         }
     }
 
-    private void connectWifi() throws Exception {
+    private void waitForNetworkInfoState(NetworkInfo.State state) throws Exception {
         synchronized (mMySync) {
-            if (mNetworkInfo.getState() == NetworkInfo.State.CONNECTED) return;
+            if (mNetworkInfo.getState() == state) return;
             long timeout = System.currentTimeMillis() + TIMEOUT_MSEC;
             while (System.currentTimeMillis() < timeout
-                    && mNetworkInfo.getState() != NetworkInfo.State.CONNECTED)
+                    && mNetworkInfo.getState() != state)
                 mMySync.wait(WAIT_MSEC);
-            assertTrue(mNetworkInfo.getState() == NetworkInfo.State.CONNECTED);
+            assertTrue(mNetworkInfo.getState() == state);
         }
+
+    }
+
+    private void waitForConnection() throws Exception {
+        waitForNetworkInfoState(NetworkInfo.State.CONNECTED);
+    }
+
+    private void waitForDisconnection() throws Exception {
+        waitForNetworkInfoState(NetworkInfo.State.DISCONNECTED);
     }
 
     private boolean existSSID(String ssid) {
@@ -423,6 +481,33 @@
         assertTrue(WifiManager.compareSignalLevel(rssiA, rssiB) > 0);
     }
 
+    /**
+     * Test that {@link WifiManager#calculateSignalLevel(int)} returns a value in the range
+     * [0, {@link WifiManager#getMaxSignalLevel()}], and its value is monotonically increasing as
+     * the RSSI increases.
+     */
+    public void testCalculateSignalLevel() {
+        if (!WifiFeature.isWifiSupported(getContext())) {
+            // skip the test if WiFi is not supported
+            return;
+        }
+
+        int maxSignalLevel = mWifiManager.getMaxSignalLevel();
+
+        int prevSignalLevel = 0;
+        for (int rssi = -150; rssi <= 50; rssi++) {
+            int signalLevel = mWifiManager.calculateSignalLevel(rssi);
+
+            // between [0, maxSignalLevel]
+            assertWithMessage("For RSSI=%s", rssi).that(signalLevel).isAtLeast(0);
+            assertWithMessage("For RSSI=%s", rssi).that(signalLevel).isAtMost(maxSignalLevel);
+
+            // calculateSignalLevel(rssi) <= calculateSignalLevel(rssi + 1)
+            assertWithMessage("For RSSI=%s", rssi).that(signalLevel).isAtLeast(prevSignalLevel);
+            prevSignalLevel = signalLevel;
+        }
+    }
+
     private static class TestLocalOnlyHotspotCallback extends WifiManager.LocalOnlyHotspotCallback {
         Object hotspotLock;
         WifiManager.LocalOnlyHotspotReservation reservation = null;
@@ -468,12 +553,12 @@
             fail("Please enable location for this test");
         }
 
-        TestLocalOnlyHotspotCallback callback = new TestLocalOnlyHotspotCallback(mLOHSLock);
-        synchronized (mLOHSLock) {
+        TestLocalOnlyHotspotCallback callback = new TestLocalOnlyHotspotCallback(mLock);
+        synchronized (mLock) {
             try {
                 mWifiManager.startLocalOnlyHotspot(callback, null);
                 // now wait for callback
-                mLOHSLock.wait(DURATION);
+                mLock.wait(DURATION);
             } catch (InterruptedException e) {
             }
             // check if we got the callback
@@ -543,15 +628,15 @@
             return;
         }
         setWifiEnabled(true);
-        connectWifi(); // ensures that there is at-least 1 saved network on the device.
+        waitForConnection(); // 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,
+        assertEquals(INVALID_NETWORK_ID,
                 mWifiManager.addNetwork(wifiConfiguration));
-        assertEquals(WifiConfiguration.INVALID_NETWORK_ID,
+        assertEquals(INVALID_NETWORK_ID,
                 mWifiManager.updateNetwork(wifiConfiguration));
         assertFalse(mWifiManager.enableNetwork(0, true));
         assertFalse(mWifiManager.disableNetwork(0));
@@ -590,7 +675,7 @@
         TestLocalOnlyHotspotCallback callback = startLocalOnlyHotspot();
 
         // now make a second request - this should fail.
-        TestLocalOnlyHotspotCallback callback2 = new TestLocalOnlyHotspotCallback(mLOHSLock);
+        TestLocalOnlyHotspotCallback callback2 = new TestLocalOnlyHotspotCallback(mLock);
         try {
             mWifiManager.startLocalOnlyHotspot(callback2, null);
         } catch (IllegalStateException e) {
@@ -641,7 +726,7 @@
                 .setPassphrase(TEST_PASSPHRASE, SoftApConfiguration.SECURITY_TYPE_WPA2_PSK)
                 .build();
         TestExecutor executor = new TestExecutor();
-        TestLocalOnlyHotspotCallback callback = new TestLocalOnlyHotspotCallback(mLOHSLock);
+        TestLocalOnlyHotspotCallback callback = new TestLocalOnlyHotspotCallback(mLock);
         UiAutomation uiAutomation = InstrumentationRegistry.getInstrumentation().getUiAutomation();
         try {
             uiAutomation.adoptShellPermissionIdentity();
@@ -671,6 +756,51 @@
     }
 
     /**
+     * Read the content of the given resource file into a String.
+     *
+     * @param filename String name of the file
+     * @return String
+     * @throws IOException
+     */
+    private String loadResourceFile(String filename) throws IOException {
+        InputStream in = getClass().getClassLoader().getResourceAsStream(filename);
+        BufferedReader reader = new BufferedReader(new InputStreamReader(in));
+        StringBuilder builder = new StringBuilder();
+        String line;
+        while ((line = reader.readLine()) != null) {
+            builder.append(line).append("\n");
+        }
+        return builder.toString();
+    }
+
+    /**
+     * Verify that changing the mac randomization setting of a Passpoint configuration.
+     */
+    public void testMacRandomizationSettingPasspoint() throws Exception {
+        String configStr = loadResourceFile(PASSPOINT_INSTALLATION_FILE_WITH_CA_CERT);
+        PasspointConfiguration config =
+                ConfigParser.parsePasspointConfig(TYPE_WIFI_CONFIG, configStr.getBytes());
+        UiAutomation uiAutomation = InstrumentationRegistry.getInstrumentation().getUiAutomation();
+        try {
+            uiAutomation.adoptShellPermissionIdentity();
+
+            mWifiManager.addOrUpdatePasspointConfiguration(config);
+            List<PasspointConfiguration> passpointConfigs =
+                    mWifiManager.getPasspointConfigurations();
+            PasspointConfiguration passpointConfig = passpointConfigs.get(0);
+            assertEquals(1, passpointConfigs.size());
+            assertTrue("Mac randomization should be enabled for passpoint networks by default.",
+                    passpointConfig.isMacRandomizationEnabled());
+
+            String fqdn = passpointConfig.getHomeSp().getFqdn();
+            mWifiManager.setMacRandomizationSettingPasspointEnabled(fqdn, false);
+            assertFalse("Mac randomization should be disabled by the API call.",
+                    mWifiManager.getPasspointConfigurations().get(0).isMacRandomizationEnabled());
+        } finally {
+            uiAutomation.dropShellPermissionIdentity();
+        }
+    }
+    /**
      * Verify that the {@link android.Manifest.permission#NETWORK_STACK} permission is never held by
      * any package.
      * <p>
@@ -933,6 +1063,29 @@
         Thread.sleep(DURATION_SCREEN_TOGGLE);
     }
 
+    private void assertWifiScanningIsOn() {
+        if (!mWifiManager.isScanAlwaysAvailable()) {
+            fail("Wi-Fi scanning should be on.");
+        }
+    }
+
+    private void runWithScanningEnabled(ThrowingRunnable r) throws Exception {
+        boolean wasScanEnabledForTest = false;
+        if (!mWifiManager.isScanAlwaysAvailable()) {
+            ShellIdentityUtils.invokeWithShellPermissions(
+                    () -> mWifiManager.setScanAlwaysAvailable(true));
+            wasScanEnabledForTest = true;
+        }
+        try {
+            r.run();
+        } finally {
+            if (wasScanEnabledForTest) {
+                ShellIdentityUtils.invokeWithShellPermissions(
+                        () -> mWifiManager.setScanAlwaysAvailable(false));
+            }
+        }
+    }
+
     /**
      * Verify that Wi-Fi scanning is not turned off when the screen turns off while wifi is disabled
      * but location is on.
@@ -951,17 +1104,16 @@
             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();
+        runWithScanningEnabled(() -> {
+            setWifiEnabled(false);
+            turnScreenOn();
+            assertWifiScanningIsOn();
+            // Toggle screen and verify Wi-Fi scanning is still on.
+            turnScreenOff();
+            assertWifiScanningIsOn();
+            turnScreenOn();
+            assertWifiScanningIsOn();
+        });
     }
 
     /**
@@ -981,17 +1133,16 @@
             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();
+        runWithScanningEnabled(() -> {
+            setWifiEnabled(true);
+            turnScreenOn();
+            assertWifiScanningIsOn();
+            // Toggle screen and verify Wi-Fi scanning is still on.
+            turnScreenOff();
+            assertWifiScanningIsOn();
+            turnScreenOn();
+            assertWifiScanningIsOn();
+        });
     }
 
     /**
@@ -1007,9 +1158,622 @@
                 > ENFORCED_NUM_NETWORK_SUGGESTIONS_PER_APP);
     }
 
-    private void assertWifiScanningIsOn() {
-        if(!mWifiManager.isScanAlwaysAvailable()) {
-            fail("Wi-Fi scanning should be on.");
+    private static class TestActionListener implements WifiManager.ActionListener {
+        private final Object mLock;
+        public boolean onSuccessCalled = false;
+        public boolean onFailedCalled = false;
+        public int failureReason = -1;
+
+        TestActionListener(Object lock) {
+            mLock = lock;
         }
+
+        @Override
+        public void onSuccess() {
+            synchronized (mLock) {
+                onSuccessCalled = true;
+                mLock.notify();
+            }
+        }
+
+        @Override
+        public void onFailure(int reason) {
+            synchronized (mLock) {
+                onFailedCalled = true;
+                failureReason = reason;
+                mLock.notify();
+            }
+        }
+    }
+
+    /**
+     * Triggers connection to one of the saved networks using {@link WifiManager#connect(
+     * int, WifiManager.ActionListener)} or {@link WifiManager#connect(WifiConfiguration,
+     * WifiManager.ActionListener)}
+     *
+     * @param withNetworkId Use networkId for triggering connection, false for using
+     *                      WifiConfiguration.
+     * @throws Exception
+     */
+    private void testConnect(boolean withNetworkId) throws Exception {
+        TestActionListener actionListener = new TestActionListener(mLock);
+        UiAutomation uiAutomation = InstrumentationRegistry.getInstrumentation().getUiAutomation();
+        List<WifiConfiguration> savedNetworks = null;
+        try {
+            uiAutomation.adoptShellPermissionIdentity();
+            // These below API's only work with privileged permissions (obtained via shell identity
+            // for test)
+            savedNetworks = mWifiManager.getConfiguredNetworks();
+
+            // Disable all the saved networks to trigger disconnect & disable autojoin.
+            for (WifiConfiguration network : savedNetworks) {
+                assertTrue(mWifiManager.disableNetwork(network.networkId));
+            }
+            waitForDisconnection();
+
+            // Now trigger connection to the first saved network.
+            synchronized (mLock) {
+                try {
+                    if (withNetworkId) {
+                        mWifiManager.connect(savedNetworks.get(0).networkId, actionListener);
+                    } else {
+                        mWifiManager.connect(savedNetworks.get(0), actionListener);
+                    }
+                    // now wait for callback
+                    mLock.wait(DURATION);
+                } catch (InterruptedException e) {
+                }
+            }
+            // check if we got the success callback
+            assertTrue(actionListener.onSuccessCalled);
+            // Wait for connection to complete & ensure we are connected to the saved network.
+            waitForConnection();
+            assertEquals(savedNetworks.get(0).networkId,
+                    mWifiManager.getConnectionInfo().getNetworkId());
+        } finally {
+            // Re-enable all saved networks before exiting.
+            if (savedNetworks != null) {
+                for (WifiConfiguration network : savedNetworks) {
+                    mWifiManager.enableNetwork(network.networkId, false);
+                }
+            }
+            uiAutomation.dropShellPermissionIdentity();
+        }
+    }
+
+    /**
+     * Tests {@link WifiManager#connect(int, WifiManager.ActionListener)} to an existing saved
+     * network.
+     */
+    public void testConnectWithNetworkId() throws Exception {
+        if (!WifiFeature.isWifiSupported(getContext())) {
+            // skip the test if WiFi is not supported
+            return;
+        }
+        testConnect(true);
+    }
+
+    /**
+     * Tests {@link WifiManager#connect(WifiConfiguration, WifiManager.ActionListener)} to an
+     * existing saved network.
+     */
+    public void testConnectWithWifiConfiguration() throws Exception {
+        if (!WifiFeature.isWifiSupported(getContext())) {
+            // skip the test if WiFi is not supported
+            return;
+        }
+        testConnect(false);
+
+    }
+
+    private static class TestNetworkCallback extends ConnectivityManager.NetworkCallback {
+        private final Object mLock;
+        public boolean onAvailableCalled = false;
+        public Network network;
+        public NetworkCapabilities networkCapabilities;
+
+        TestNetworkCallback(Object lock) {
+            mLock = lock;
+        }
+
+        @Override
+        public void onAvailable(Network network, NetworkCapabilities networkCapabilities,
+                LinkProperties linkProperties, boolean blocked) {
+            synchronized (mLock) {
+                onAvailableCalled = true;
+                this.network = network;
+                this.networkCapabilities = networkCapabilities;
+                mLock.notify();
+            }
+        }
+    }
+
+    private void waitForNetworkCallbackAndCheckForMeteredness(boolean expectMetered) {
+        TestNetworkCallback networkCallbackListener = new TestNetworkCallback(mLock);
+        synchronized (mLock) {
+            try {
+                // File a request for wifi network.
+                mConnectivityManager.registerNetworkCallback(
+                        new NetworkRequest.Builder()
+                                .addTransportType(TRANSPORT_WIFI)
+                                .build(),
+                        networkCallbackListener);
+                // now wait for callback
+                mLock.wait(DURATION);
+            } catch (InterruptedException e) {
+            }
+        }
+        assertTrue(networkCallbackListener.onAvailableCalled);
+        assertNotEquals(expectMetered, networkCallbackListener.networkCapabilities.hasCapability(
+                NetworkCapabilities.NET_CAPABILITY_NOT_METERED));
+    }
+
+    /**
+     * Tests {@link WifiManager#save(WifiConfiguration, WifiManager.ActionListener)} by marking
+     * an existing saved network metered.
+     */
+    public void testSave() throws Exception {
+        if (!WifiFeature.isWifiSupported(getContext())) {
+            // skip the test if WiFi is not supported
+            return;
+        }
+        TestActionListener actionListener = new TestActionListener(mLock);
+        UiAutomation uiAutomation = InstrumentationRegistry.getInstrumentation().getUiAutomation();
+        List<WifiConfiguration> savedNetworks = null;
+        WifiConfiguration savedNetwork = null;
+        try {
+            uiAutomation.adoptShellPermissionIdentity();
+            // These below API's only work with privileged permissions (obtained via shell identity
+            // for test)
+            savedNetworks = mWifiManager.getConfiguredNetworks();
+
+            // Ensure that the saved network is not metered.
+            savedNetwork = savedNetworks.get(0);
+            assertNotEquals("Ensure that the saved network is configured as unmetered",
+                    savedNetwork.meteredOverride,
+                    WifiConfiguration.METERED_OVERRIDE_METERED);
+
+            // Trigger a scan & wait for connection to one of the saved networks.
+            mWifiManager.startScan();
+            waitForConnection();
+
+            // Check the network capabilities to ensure that the network is marked not metered.
+            waitForNetworkCallbackAndCheckForMeteredness(false);
+
+            // Now mark the network metered and save.
+            synchronized (mLock) {
+                try {
+                    WifiConfiguration modSavedNetwork = new WifiConfiguration(savedNetwork);
+                    modSavedNetwork.meteredOverride = WifiConfiguration.METERED_OVERRIDE_METERED;
+                    mWifiManager.save(modSavedNetwork, actionListener);
+                    // now wait for callback
+                    mLock.wait(DURATION);
+                } catch (InterruptedException e) {
+                }
+            }
+            // check if we got the success callback
+            assertTrue(actionListener.onSuccessCalled);
+            // Check the network capabilities to ensure that the network is marked metered now.
+            waitForNetworkCallbackAndCheckForMeteredness(true);
+
+        } finally {
+            // Restore original network config (restore the meteredness back);
+            if (savedNetwork != null) {
+                mWifiManager.updateNetwork(savedNetwork);
+            }
+            uiAutomation.dropShellPermissionIdentity();
+        }
+    }
+
+    /**
+     * Tests {@link WifiManager#forget(int, WifiManager.ActionListener)} by adding/removing a new
+     * network.
+     */
+    public void testForget() throws Exception {
+        if (!WifiFeature.isWifiSupported(getContext())) {
+            // skip the test if WiFi is not supported
+            return;
+        }
+        TestActionListener actionListener = new TestActionListener(mLock);
+        UiAutomation uiAutomation = InstrumentationRegistry.getInstrumentation().getUiAutomation();
+        int newNetworkId = INVALID_NETWORK_ID;
+        try {
+            uiAutomation.adoptShellPermissionIdentity();
+            // These below API's only work with privileged permissions (obtained via shell identity
+            // for test)
+            List<WifiConfiguration> savedNetworks = mWifiManager.getConfiguredNetworks();
+
+            WifiConfiguration newOpenNetwork = new WifiConfiguration();
+            newOpenNetwork.SSID = "\"" + TEST_SSID_UNQUOTED + "\"";
+            newNetworkId = mWifiManager.addNetwork(newOpenNetwork);
+            assertNotEquals(INVALID_NETWORK_ID, newNetworkId);
+
+            assertEquals(savedNetworks.size() + 1, mWifiManager.getConfiguredNetworks().size());
+
+            // Now remove the network
+            synchronized (mLock) {
+                try {
+                    mWifiManager.forget(newNetworkId, actionListener);
+                    // now wait for callback
+                    mLock.wait(DURATION);
+                } catch (InterruptedException e) {
+                }
+            }
+            // check if we got the success callback
+            assertTrue(actionListener.onSuccessCalled);
+
+            // Ensure that the new network has been successfully removed.
+            assertEquals(savedNetworks.size(), mWifiManager.getConfiguredNetworks().size());
+        } finally {
+            // For whatever reason, if the forget fails, try removing using the public remove API.
+            if (newNetworkId != INVALID_NETWORK_ID) mWifiManager.removeNetwork(newNetworkId);
+            uiAutomation.dropShellPermissionIdentity();
+        }
+    }
+
+    /**
+     * Tests {@link WifiManager#getFactoryMacAddresses()} returns at least one valid MAC address.
+     */
+    public void testGetFactoryMacAddresses() throws Exception {
+        if (!WifiFeature.isWifiSupported(getContext())) {
+            // skip the test if WiFi is not supported
+            return;
+        }
+        TestActionListener actionListener = new TestActionListener(mLock);
+        UiAutomation uiAutomation = InstrumentationRegistry.getInstrumentation().getUiAutomation();
+        int newNetworkId = INVALID_NETWORK_ID;
+        try {
+            uiAutomation.adoptShellPermissionIdentity();
+            // Obtain the factory MAC address
+            String[] macAddresses = mWifiManager.getFactoryMacAddresses();
+            assertTrue("At list one MAC address should be returned.", macAddresses.length > 0);
+            try {
+                MacAddress mac = MacAddress.fromString(macAddresses[0]);
+                assertNotEquals(WifiInfo.DEFAULT_MAC_ADDRESS, mac);
+                assertFalse(MacAddressUtils.isMulticastAddress(mac));
+            } catch (IllegalArgumentException e) {
+                fail("Factory MAC address is invalid");
+            }
+        } finally {
+            uiAutomation.dropShellPermissionIdentity();
+        }
+    }
+
+    /**
+     * Tests {@link WifiManager#isApMacRandomizationSupported()} does not crash.
+     */
+    public void testIsApMacRandomizationSupported() throws Exception {
+        if (!WifiFeature.isWifiSupported(getContext())) {
+            // skip the test if WiFi is not supported
+            return;
+        }
+        mWifiManager.isApMacRandomizationSupported();
+    }
+
+    /**
+     * Tests {@link WifiManager#isConnectedMacRandomizationSupported()} does not crash.
+     */
+    public void testIsConnectedMacRandomizationSupported() throws Exception {
+        if (!WifiFeature.isWifiSupported(getContext())) {
+            // skip the test if WiFi is not supported
+            return;
+        }
+        mWifiManager.isConnectedMacRandomizationSupported();
+    }
+
+    /**
+     * Tests {@link WifiManager#isPreferredNetworkOffloadSupported()} does not crash.
+     */
+    public void testIsPreferredNetworkOffloadSupported() throws Exception {
+        if (!WifiFeature.isWifiSupported(getContext())) {
+            // skip the test if WiFi is not supported
+            return;
+        }
+        mWifiManager.isPreferredNetworkOffloadSupported();
+    }
+
+    private static class TestTrafficStateCallback implements WifiManager.TrafficStateCallback {
+        private final Object mLock;
+        public boolean onStateChangedCalled = false;
+        public int state = -1;
+
+        TestTrafficStateCallback(Object lock) {
+            mLock = lock;
+        }
+
+        @Override
+        public void onStateChanged(int state) {
+            synchronized (mLock) {
+                onStateChangedCalled = true;
+                this.state = state;
+                mLock.notify();
+            }
+        }
+    }
+
+    private void sendTraffic() {
+        for (int i = 0; i < 10; i ++) {
+            // Do some network operations
+            HttpURLConnection connection = null;
+            try {
+                URL url = new URL("http://www.google.com/");
+                connection = (HttpURLConnection) url.openConnection();
+                connection.setInstanceFollowRedirects(false);
+                connection.setConnectTimeout(TIMEOUT_MSEC);
+                connection.setReadTimeout(TIMEOUT_MSEC);
+                connection.setUseCaches(false);
+                connection.getInputStream();
+            } catch (Exception e) {
+                // ignore
+            } finally {
+                if (connection != null) connection.disconnect();
+            }
+        }
+    }
+
+    /**
+     * Tests {@link WifiManager#registerTrafficStateCallback(Executor,
+     * WifiManager.TrafficStateCallback)} by sending some traffic.
+     */
+    public void testTrafficStateCallback() throws Exception {
+        if (!WifiFeature.isWifiSupported(getContext())) {
+            // skip the test if WiFi is not supported
+            return;
+        }
+        TestTrafficStateCallback trafficStateCallback = new TestTrafficStateCallback(mLock);
+        UiAutomation uiAutomation = InstrumentationRegistry.getInstrumentation().getUiAutomation();
+        try {
+            uiAutomation.adoptShellPermissionIdentity();
+            // Trigger a scan & wait for connection to one of the saved networks.
+            mWifiManager.startScan();
+            waitForConnection();
+
+            // Turn screen on for wifi traffic polling.
+            turnScreenOn();
+            synchronized (mLock) {
+                try {
+                    mWifiManager.registerTrafficStateCallback(
+                            Executors.newSingleThreadExecutor(), trafficStateCallback);
+                    // Send some traffic to trigger the traffic state change callbacks.
+                    sendTraffic();
+                    // now wait for callback
+                    mLock.wait(DURATION);
+                } catch (InterruptedException e) {
+                }
+            }
+            // check if we got the state changed callback
+            assertTrue(trafficStateCallback.onStateChangedCalled);
+            assertEquals(DATA_ACTIVITY_INOUT, trafficStateCallback.state);
+        } finally {
+            turnScreenOff();
+            mWifiManager.unregisterTrafficStateCallback(trafficStateCallback);
+            uiAutomation.dropShellPermissionIdentity();
+        }
+    }
+
+    /**
+     * Tests {@link WifiManager#setScanAlwaysAvailable(boolean)} &
+     * {@link WifiManager#isScanAlwaysAvailable()}.
+     */
+    public void testScanAlwaysAvailable() throws Exception {
+        if (!WifiFeature.isWifiSupported(getContext())) {
+            // skip the test if WiFi is not supported
+            return;
+        }
+        UiAutomation uiAutomation = InstrumentationRegistry.getInstrumentation().getUiAutomation();
+        Boolean currState = null;
+        try {
+            uiAutomation.adoptShellPermissionIdentity();
+            currState = mWifiManager.isScanAlwaysAvailable();
+            boolean newState = !currState;
+            mWifiManager.setScanAlwaysAvailable(newState);
+            PollingCheck.check(
+                    "Wifi settings toggle failed!",
+                    DURATION_SETTINGS_TOGGLE,
+                    () -> mWifiManager.isScanAlwaysAvailable() == newState);
+            assertEquals(newState, mWifiManager.isScanAlwaysAvailable());
+        } finally {
+            if (currState != null) mWifiManager.setScanAlwaysAvailable(currState);
+            uiAutomation.dropShellPermissionIdentity();
+        }
+    }
+
+    /**
+     * Tests {@link WifiManager#setScanThrottleEnabled(boolean)} &
+     * {@link WifiManager#isScanThrottleEnabled()}.
+     */
+    public void testScanThrottleEnabled() throws Exception {
+        if (!WifiFeature.isWifiSupported(getContext())) {
+            // skip the test if WiFi is not supported
+            return;
+        }
+        UiAutomation uiAutomation = InstrumentationRegistry.getInstrumentation().getUiAutomation();
+        Boolean currState = null;
+        try {
+            uiAutomation.adoptShellPermissionIdentity();
+            currState = mWifiManager.isScanThrottleEnabled();
+            boolean newState = !currState;
+            mWifiManager.setScanThrottleEnabled(newState);
+            PollingCheck.check(
+                    "Wifi settings toggle failed!",
+                    DURATION_SETTINGS_TOGGLE,
+                    () -> mWifiManager.isScanThrottleEnabled() == newState);
+            assertEquals(newState, mWifiManager.isScanThrottleEnabled());
+        } finally {
+            if (currState != null) mWifiManager.setScanThrottleEnabled(currState);
+            uiAutomation.dropShellPermissionIdentity();
+        }
+    }
+
+    /**
+     * Tests {@link WifiManager#setAutoWakeupEnabled(boolean)} &
+     * {@link WifiManager#isAutoWakeupEnabled()}.
+     */
+    public void testAutoWakeUpEnabled() throws Exception {
+        if (!WifiFeature.isWifiSupported(getContext())) {
+            // skip the test if WiFi is not supported
+            return;
+        }
+        UiAutomation uiAutomation = InstrumentationRegistry.getInstrumentation().getUiAutomation();
+        Boolean currState = null;
+        try {
+            uiAutomation.adoptShellPermissionIdentity();
+            currState = mWifiManager.isAutoWakeupEnabled();
+            boolean newState = !currState;
+            mWifiManager.setAutoWakeupEnabled(newState);
+            PollingCheck.check(
+                    "Wifi settings toggle failed!",
+                    DURATION_SETTINGS_TOGGLE,
+                    () -> mWifiManager.isAutoWakeupEnabled() == newState);
+            assertEquals(newState, mWifiManager.isAutoWakeupEnabled());
+        } finally {
+            if (currState != null) mWifiManager.setAutoWakeupEnabled(currState);
+            uiAutomation.dropShellPermissionIdentity();
+        }
+    }
+
+    /**
+     * Tests {@link WifiManager#setVerboseLoggingEnabled(boolean)} &
+     * {@link WifiManager#isVerboseLoggingEnabled()}.
+     */
+    public void testVerboseLoggingEnabled() throws Exception {
+        if (!WifiFeature.isWifiSupported(getContext())) {
+            // skip the test if WiFi is not supported
+            return;
+        }
+        UiAutomation uiAutomation = InstrumentationRegistry.getInstrumentation().getUiAutomation();
+        Boolean currState = null;
+        try {
+            uiAutomation.adoptShellPermissionIdentity();
+            currState = mWifiManager.isVerboseLoggingEnabled();
+            boolean newState = !currState;
+            mWifiManager.setVerboseLoggingEnabled(newState);
+            PollingCheck.check(
+                    "Wifi settings toggle failed!",
+                    DURATION_SETTINGS_TOGGLE,
+                    () -> mWifiManager.isVerboseLoggingEnabled() == newState);
+            assertEquals(newState, mWifiManager.isVerboseLoggingEnabled());
+        } finally {
+            if (currState != null) mWifiManager.setVerboseLoggingEnabled(currState);
+            uiAutomation.dropShellPermissionIdentity();
+        }
+    }
+
+    /**
+     * Tests {@link WifiManager#factoryReset()}.
+     *
+     * Note: This test assumes that the device only has 1 or more saved networks before the test.
+     * The test will restore those when the test exits. But, it does not restore the softap
+     * configuration, suggestions, etc which will also have been lost on factory reset.
+     */
+    public void testFactoryReset() throws Exception {
+        if (!WifiFeature.isWifiSupported(getContext())) {
+            // skip the test if WiFi is not supported
+            return;
+        }
+        UiAutomation uiAutomation = InstrumentationRegistry.getInstrumentation().getUiAutomation();
+        List<WifiConfiguration> savedNetworks = null;
+        try {
+            uiAutomation.adoptShellPermissionIdentity();
+            // These below API's only work with privileged permissions (obtained via shell identity
+            // for test)
+            savedNetworks = mWifiManager.getConfiguredNetworks();
+
+            mWifiManager.factoryReset();
+            // Ensure all the saved networks are removed.
+            assertEquals(0, mWifiManager.getConfiguredNetworks().size());
+        } finally {
+            // Restore the original saved networks.
+            if (savedNetworks != null) {
+                for (WifiConfiguration network : savedNetworks) {
+                    mWifiManager.save(network, null);
+                }
+            }
+            uiAutomation.dropShellPermissionIdentity();
+        }
+    }
+
+    /**
+     * Test {@link WifiNetworkConnectionStatistics} does not crash.
+     * TODO(b/150891569): deprecate it in Android S, this API is not used anywhere.
+     */
+    public void testWifiNetworkConnectionStatistics() {
+        new WifiNetworkConnectionStatistics();
+        WifiNetworkConnectionStatistics stats = new WifiNetworkConnectionStatistics(0, 0);
+        new WifiNetworkConnectionStatistics(stats);
+    }
+
+    /**
+     * Test that the wifi country code is either null, or a length-2 string.
+     */
+    public void testGetCountryCode() throws Exception {
+        if (!WifiFeature.isWifiSupported(getContext())) {
+            // skip the test if WiFi is not supported
+            return;
+        }
+
+        String wifiCountryCode = ShellIdentityUtils.invokeWithShellPermissions(
+                mWifiManager::getCountryCode);
+
+        if (wifiCountryCode == null) {
+            return;
+        }
+        assertEquals(2, wifiCountryCode.length());
+
+        // assert that the country code is all uppercase
+        assertEquals(wifiCountryCode.toUpperCase(Locale.US), wifiCountryCode);
+
+        String telephonyCountryCode = getContext().getSystemService(TelephonyManager.class)
+                .getNetworkCountryIso();
+        assertEquals(telephonyCountryCode, wifiCountryCode.toLowerCase(Locale.US));
+    }
+
+    /**
+     * Test that {@link WifiManager#getCurrentNetwork()} returns a Network obeject consistent
+     * with {@link ConnectivityManager#registerNetworkCallback} when connected to a Wifi network,
+     * and returns null when not connected.
+     */
+    public void testGetCurrentNetwork() throws Exception {
+        if (!WifiFeature.isWifiSupported(getContext())) {
+            // skip the test if WiFi is not supported
+            return;
+        }
+
+        // wait for Wifi to be connected
+        PollingCheck.check(
+                "Wifi not connected - Please ensure there is a saved network in range of this "
+                        + "device",
+                20000,
+                () -> mWifiManager.getConnectionInfo().getNetworkId() != -1);
+
+        Network wifiCurrentNetwork = ShellIdentityUtils.invokeWithShellPermissions(
+                mWifiManager::getCurrentNetwork);
+        assertNotNull(wifiCurrentNetwork);
+
+        TestNetworkCallback networkCallbackListener = new TestNetworkCallback(mLock);
+        synchronized (mLock) {
+            try {
+                // File a request for wifi network.
+                mConnectivityManager.registerNetworkCallback(
+                        new NetworkRequest.Builder()
+                                .addTransportType(TRANSPORT_WIFI)
+                                .build(),
+                        networkCallbackListener);
+                // now wait for callback
+                mLock.wait(DURATION);
+            } catch (InterruptedException e) {
+            }
+        }
+        assertTrue(networkCallbackListener.onAvailableCalled);
+        Network connectivityCurrentNetwork = networkCallbackListener.network;
+        assertEquals(connectivityCurrentNetwork, wifiCurrentNetwork);
+
+        setWifiEnabled(false);
+        PollingCheck.check(
+                "Wifi not disconnected!",
+                20000,
+                () -> mWifiManager.getConnectionInfo().getNetworkId() == -1);
+
+        assertNull(ShellIdentityUtils.invokeWithShellPermissions(mWifiManager::getCurrentNetwork));
     }
 }
diff --git a/tests/cts/net/src/android/net/wifi/cts/WifiMigrationTest.java b/tests/cts/net/src/android/net/wifi/cts/WifiMigrationTest.java
new file mode 100644
index 0000000..6e19a21
--- /dev/null
+++ b/tests/cts/net/src/android/net/wifi/cts/WifiMigrationTest.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2020 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 static org.junit.Assert.assertNotNull;
+
+import android.net.wifi.SoftApConfiguration;
+import android.net.wifi.WifiConfiguration;
+import android.net.wifi.WifiMigration;
+import android.test.AndroidTestCase;
+
+import java.util.Arrays;
+import java.util.List;
+
+public class WifiMigrationTest extends AndroidTestCase {
+    private static final String TEST_SSID_UNQUOTED = "testSsid1";
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        if (!WifiFeature.isWifiSupported(getContext())) {
+            // skip the test if WiFi is not supported
+            return;
+        }
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        if (!WifiFeature.isWifiSupported(getContext())) {
+            // skip the test if WiFi is not supported
+            super.tearDown();
+            return;
+        }
+        super.tearDown();
+    }
+
+    /**
+     * Tests {@link android.net.wifi.WifiMigration.ConfigStoreMigrationData} class.
+     */
+    public void testWifiMigrationConfigStoreDataBuilder() throws Exception {
+        if (!WifiFeature.isWifiSupported(getContext())) {
+            // skip the test if WiFi is not supported
+            return;
+        }
+        WifiConfiguration savedNetwork1 = new WifiConfiguration();
+        savedNetwork1.SSID = "\"test1\"";
+        WifiConfiguration savedNetwork2 = new WifiConfiguration();
+        savedNetwork1.SSID = "\"test2\"";
+        List<WifiConfiguration> savedNetworks = Arrays.asList(savedNetwork1, savedNetwork2);
+
+        SoftApConfiguration softApConfiguration = new SoftApConfiguration.Builder()
+                .setSsid("\"test3\"")
+                .build();
+
+        WifiMigration.ConfigStoreMigrationData migrationData =
+                new WifiMigration.ConfigStoreMigrationData.Builder()
+                        .setUserSavedNetworkConfigurations(savedNetworks)
+                        .setUserSoftApConfiguration(softApConfiguration)
+                        .build();
+
+        assertNotNull(migrationData);
+        assertEquals(savedNetworks.size(),
+                migrationData.getUserSavedNetworkConfigurations().size());
+        assertEquals(savedNetwork1.SSID,
+                migrationData.getUserSavedNetworkConfigurations().get(0).SSID);
+        assertEquals(savedNetwork2.SSID,
+                migrationData.getUserSavedNetworkConfigurations().get(1).SSID);
+        assertEquals(softApConfiguration.getSsid(),
+                migrationData.getUserSoftApConfiguration().getSsid());
+    }
+
+    /**
+     * Tests {@link android.net.wifi.WifiMigration.ConfigStoreMigrationData} class.
+     */
+    public void testWifiMigrationSettingsDataBuilder() throws Exception {
+        if (!WifiFeature.isWifiSupported(getContext())) {
+            // skip the test if WiFi is not supported
+            return;
+        }
+        WifiMigration.SettingsMigrationData migrationData =
+                new WifiMigration.SettingsMigrationData.Builder()
+                        .setScanAlwaysAvailable(true)
+                        .setP2pFactoryResetPending(true)
+                        .setScanThrottleEnabled(true)
+                        .setSoftApTimeoutEnabled(true)
+                        .setWakeUpEnabled(true)
+                        .setVerboseLoggingEnabled(true)
+                        .setP2pDeviceName(TEST_SSID_UNQUOTED)
+                        .build();
+
+        assertNotNull(migrationData);
+        assertTrue(migrationData.isScanAlwaysAvailable());
+        assertTrue(migrationData.isP2pFactoryResetPending());
+        assertTrue(migrationData.isScanThrottleEnabled());
+        assertTrue(migrationData.isSoftApTimeoutEnabled());
+        assertTrue(migrationData.isWakeUpEnabled());
+        assertTrue(migrationData.isVerboseLoggingEnabled());
+        assertEquals(TEST_SSID_UNQUOTED, migrationData.getP2pDeviceName());
+    }
+}
diff --git a/tests/cts/net/src/android/net/wifi/cts/WifiNetworkSpecifierTest.java b/tests/cts/net/src/android/net/wifi/cts/WifiNetworkSpecifierTest.java
new file mode 100644
index 0000000..96cf45f
--- /dev/null
+++ b/tests/cts/net/src/android/net/wifi/cts/WifiNetworkSpecifierTest.java
@@ -0,0 +1,509 @@
+/*
+ * Copyright (C) 2020 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 static android.net.NetworkCapabilitiesProto.TRANSPORT_WIFI;
+import static android.os.Process.myUid;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.app.UiAutomation;
+import android.content.Context;
+import android.net.ConnectivityManager;
+import android.net.LinkProperties;
+import android.net.MacAddress;
+import android.net.Network;
+import android.net.NetworkCapabilities;
+import android.net.NetworkRequest;
+import android.net.wifi.ScanResult;
+import android.net.wifi.WifiConfiguration;
+import android.net.wifi.WifiInfo;
+import android.net.wifi.WifiManager;
+import android.net.wifi.WifiManager.NetworkRequestMatchCallback;
+import android.net.wifi.WifiNetworkSpecifier;
+import android.os.PatternMatcher;
+import android.os.WorkSource;
+import android.platform.test.annotations.AppModeFull;
+import android.support.test.uiautomator.UiDevice;
+import android.test.AndroidTestCase;
+import android.text.TextUtils;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.android.compatibility.common.util.PollingCheck;
+import com.android.compatibility.common.util.ShellIdentityUtils;
+import com.android.compatibility.common.util.SystemUtil;
+
+import java.util.List;
+import java.util.concurrent.Executors;
+
+/**
+ * Tests the entire connection flow using {@link WifiNetworkSpecifier} embedded in a
+ * {@link NetworkRequest} & passed into {@link ConnectivityManager#requestNetwork(NetworkRequest,
+ * ConnectivityManager.NetworkCallback)}.
+ *
+ * Assumes that all the saved networks is either open/WPA1/WPA2/WPA3 authenticated network.
+ * TODO(b/150716005): Use assumeTrue for wifi support check.
+ */
+@AppModeFull(reason = "Cannot get WifiManager in instant app mode")
+public class WifiNetworkSpecifierTest extends AndroidTestCase {
+    private static final String TAG = "WifiNetworkSpecifierTest";
+
+    private WifiManager mWifiManager;
+    private ConnectivityManager mConnectivityManager;
+    private UiDevice mUiDevice;
+    private final Object mLock = new Object();
+    private final Object mUiLock = new Object();
+    private WifiConfiguration mTestNetwork;
+    private boolean mWasVerboseLoggingEnabled;
+
+    private static final int DURATION = 10_000;
+    private static final int DURATION_UI_INTERACTION = 15_000;
+    private static final int DURATION_NETWORK_CONNECTION = 30_000;
+    private static final int DURATION_SCREEN_TOGGLE = 2000;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        if (!WifiFeature.isWifiSupported(getContext())) {
+            // skip the test if WiFi is not supported
+            return;
+        }
+        mWifiManager = (WifiManager) getContext().getSystemService(Context.WIFI_SERVICE);
+        mConnectivityManager = getContext().getSystemService(ConnectivityManager.class);
+        assertNotNull(mWifiManager);
+
+        // turn on verbose logging for tests
+        mWasVerboseLoggingEnabled = ShellIdentityUtils.invokeWithShellPermissions(
+                () -> mWifiManager.isVerboseLoggingEnabled());
+        ShellIdentityUtils.invokeWithShellPermissions(
+                () -> mWifiManager.setVerboseLoggingEnabled(true));
+
+        if (!mWifiManager.isWifiEnabled()) setWifiEnabled(true);
+        mUiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
+        turnScreenOn();
+        PollingCheck.check("Wifi not enabled", DURATION, () -> mWifiManager.isWifiEnabled());
+
+        List<WifiConfiguration> savedNetworks = ShellIdentityUtils.invokeWithShellPermissions(
+                () -> mWifiManager.getPrivilegedConfiguredNetworks());
+        assertFalse("Need at least one saved network", savedNetworks.isEmpty());
+        // Pick any one of the saved networks on the device (assumes that it is in range)
+        mTestNetwork = savedNetworks.get(0);
+        // Disconnect & disable auto-join on the saved network to prevent auto-connect from
+        // interfering with the test.
+        ShellIdentityUtils.invokeWithShellPermissions(
+                () -> mWifiManager.disableNetwork(mTestNetwork.networkId));
+        // wait for Wifi to be disconnected
+        PollingCheck.check(
+                "Wifi not disconnected",
+                20000,
+                () -> mWifiManager.getConnectionInfo().getNetworkId() == -1);
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        if (!WifiFeature.isWifiSupported(getContext())) {
+            // skip the test if WiFi is not supported
+            super.tearDown();
+            return;
+        }
+        if (!mWifiManager.isWifiEnabled()) setWifiEnabled(true);
+        turnScreenOff();
+        ShellIdentityUtils.invokeWithShellPermissions(
+                () -> mWifiManager.enableNetwork(mTestNetwork.networkId, false));
+        ShellIdentityUtils.invokeWithShellPermissions(
+                () -> mWifiManager.setVerboseLoggingEnabled(mWasVerboseLoggingEnabled));
+        super.tearDown();
+    }
+
+    private void setWifiEnabled(boolean enable) throws Exception {
+        // now trigger the change using shell commands.
+        SystemUtil.runShellCommand("svc wifi " + (enable ? "enable" : "disable"));
+    }
+
+    private void turnScreenOn() throws Exception {
+        mUiDevice.executeShellCommand("input keyevent KEYCODE_WAKEUP");
+        mUiDevice.executeShellCommand("wm dismiss-keyguard");
+        // 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);
+    }
+
+    private static class TestNetworkCallback extends ConnectivityManager.NetworkCallback {
+        private final Object mLock;
+        public boolean onAvailableCalled = false;
+        public boolean onUnavailableCalled = false;
+        public NetworkCapabilities networkCapabilities;
+
+        TestNetworkCallback(Object lock) {
+            mLock = lock;
+        }
+
+        @Override
+        public void onAvailable(Network network, NetworkCapabilities networkCapabilities,
+                LinkProperties linkProperties, boolean blocked) {
+            synchronized (mLock) {
+                onAvailableCalled = true;
+                this.networkCapabilities = networkCapabilities;
+                mLock.notify();
+            }
+        }
+
+        @Override
+        public void onUnavailable() {
+            synchronized (mLock) {
+                onUnavailableCalled = true;
+                mLock.notify();
+            }
+        }
+    }
+
+    private static class TestNetworkRequestMatchCallback implements NetworkRequestMatchCallback {
+        private final Object mLock;
+
+        public boolean onRegistrationCalled = false;
+        public boolean onAbortCalled = false;
+        public boolean onMatchCalled = false;
+        public boolean onConnectSuccessCalled = false;
+        public boolean onConnectFailureCalled = false;
+        public WifiManager.NetworkRequestUserSelectionCallback userSelectionCallback = null;
+        public List<ScanResult> matchedScanResults = null;
+
+        TestNetworkRequestMatchCallback(Object lock) {
+            mLock = lock;
+        }
+
+        @Override
+        public void onUserSelectionCallbackRegistration(
+                WifiManager.NetworkRequestUserSelectionCallback userSelectionCallback) {
+            synchronized (mLock) {
+                onRegistrationCalled = true;
+                this.userSelectionCallback = userSelectionCallback;
+                mLock.notify();
+            }
+        }
+
+        @Override
+        public void onAbort() {
+            synchronized (mLock) {
+                onAbortCalled = true;
+                mLock.notify();
+            }
+        }
+
+        @Override
+        public void onMatch(List<ScanResult> scanResults) {
+            synchronized (mLock) {
+                // This can be invoked multiple times. So, ignore after the first one to avoid
+                // disturbing the rest of the test sequence.
+                if (onMatchCalled) return;
+                onMatchCalled = true;
+                matchedScanResults = scanResults;
+                mLock.notify();
+            }
+        }
+
+        @Override
+        public void onUserSelectionConnectSuccess(WifiConfiguration config) {
+            synchronized (mLock) {
+                onConnectSuccessCalled = true;
+                mLock.notify();
+            }
+        }
+
+        @Override
+        public void onUserSelectionConnectFailure(WifiConfiguration config) {
+            synchronized (mLock) {
+                onConnectFailureCalled = true;
+                mLock.notify();
+            }
+        }
+    }
+
+    private void handleUiInteractions(boolean shouldUserReject) {
+        UiAutomation uiAutomation = InstrumentationRegistry.getInstrumentation().getUiAutomation();
+        TestNetworkRequestMatchCallback networkRequestMatchCallback =
+                new TestNetworkRequestMatchCallback(mUiLock);
+        try {
+            uiAutomation.adoptShellPermissionIdentity();
+
+            // 1. Wait for registration callback.
+            synchronized (mUiLock) {
+                try {
+                    mWifiManager.registerNetworkRequestMatchCallback(
+                            Executors.newSingleThreadExecutor(), networkRequestMatchCallback);
+                    // now wait for the registration callback first.
+                    mUiLock.wait(DURATION_UI_INTERACTION);
+                } catch (InterruptedException e) {
+                }
+            }
+            assertTrue(networkRequestMatchCallback.onRegistrationCalled);
+            assertNotNull(networkRequestMatchCallback.userSelectionCallback);
+
+            // 2. Wait for matching scan results
+            synchronized (mUiLock) {
+                try {
+                    // now wait for the registration callback first.
+                    mUiLock.wait(DURATION_UI_INTERACTION);
+                } catch (InterruptedException e) {
+                }
+            }
+            assertTrue(networkRequestMatchCallback.onMatchCalled);
+            assertNotNull(networkRequestMatchCallback.matchedScanResults);
+            assertThat(networkRequestMatchCallback.matchedScanResults.size()).isAtLeast(1);
+
+            // 3. Trigger connection to one of the matched networks or reject the request.
+            if (shouldUserReject) {
+                networkRequestMatchCallback.userSelectionCallback.reject();
+            } else {
+                networkRequestMatchCallback.userSelectionCallback.select(mTestNetwork);
+            }
+
+            // 4. Wait for connection success or abort.
+            synchronized (mUiLock) {
+                try {
+                    // now wait for the registration callback first.
+                    mUiLock.wait(DURATION_UI_INTERACTION);
+                } catch (InterruptedException e) {
+                }
+            }
+            if (shouldUserReject) {
+                assertTrue(networkRequestMatchCallback.onAbortCalled);
+            } else {
+                assertTrue(networkRequestMatchCallback.onConnectSuccessCalled);
+            }
+        } finally {
+            mWifiManager.unregisterNetworkRequestMatchCallback(networkRequestMatchCallback);
+            uiAutomation.dropShellPermissionIdentity();
+        }
+    }
+
+    /**
+     * Tests the entire connection flow using the provided specifier.
+     *
+     * @param specifier Specifier to use for network request.
+     * @param shouldUserReject Whether to simulate user rejection or not.
+     */
+    private void testConnectionFlowWithSpecifier(
+            WifiNetworkSpecifier specifier, boolean shouldUserReject) {
+        // Fork a thread to handle the UI interactions.
+        Thread uiThread = new Thread(() -> handleUiInteractions(shouldUserReject));
+
+        // File the network request & wait for the callback.
+        TestNetworkCallback networkCallbackListener = new TestNetworkCallback(mLock);
+        synchronized (mLock) {
+            try {
+                // File a request for wifi network.
+                mConnectivityManager.requestNetwork(
+                        new NetworkRequest.Builder()
+                                .addTransportType(TRANSPORT_WIFI)
+                                .setNetworkSpecifier(specifier)
+                                .build(),
+                        networkCallbackListener);
+                // Wait for the request to reach the wifi stack before kick-starting the UI
+                // interactions.
+                Thread.sleep(100);
+                // Start the UI interactions.
+                uiThread.run();
+                // now wait for callback
+                mLock.wait(DURATION_NETWORK_CONNECTION);
+            } catch (InterruptedException e) {
+            }
+        }
+        if (shouldUserReject) {
+            assertTrue(networkCallbackListener.onUnavailableCalled);
+        } else {
+            assertTrue(networkCallbackListener.onAvailableCalled);
+        }
+
+        try {
+            // Ensure that the UI interaction thread has completed.
+            uiThread.join(DURATION_UI_INTERACTION);
+        } catch (InterruptedException e) {
+            fail("UI interaction interrupted");
+        }
+
+        // Release the request after the test.
+        mConnectivityManager.unregisterNetworkCallback(networkCallbackListener);
+    }
+
+    private void testSuccessfulConnectionWithSpecifier(WifiNetworkSpecifier specifier) {
+        testConnectionFlowWithSpecifier(specifier, false);
+    }
+
+    private void testUserRejectionWithSpecifier(WifiNetworkSpecifier specifier) {
+        testConnectionFlowWithSpecifier(specifier, true);
+    }
+
+    private WifiNetworkSpecifier.Builder createSpecifierBuilderWithCredentialFromSavedNetwork() {
+        WifiNetworkSpecifier.Builder specifierBuilder = new WifiNetworkSpecifier.Builder();
+        if (mTestNetwork.preSharedKey != null) {
+            if (mTestNetwork.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.WPA_PSK)) {
+                specifierBuilder.setWpa2Passphrase(mTestNetwork.preSharedKey);
+            } else if (mTestNetwork.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.SAE)) {
+                specifierBuilder.setWpa3Passphrase(mTestNetwork.preSharedKey);
+            } else {
+                fail("Unsupported security type found in saved networks");
+            }
+        } else if (!mTestNetwork.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.OWE)) {
+            specifierBuilder.setIsEnhancedOpen(false);
+        } else if (!mTestNetwork.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.NONE)) {
+            fail("Unsupported security type found in saved networks");
+        }
+        specifierBuilder.setIsHiddenSsid(mTestNetwork.hiddenSSID);
+        return specifierBuilder;
+    }
+
+    /**
+     * Tests the entire connection flow using a specific SSID in the specifier.
+     */
+    public void testConnectionWithSpecificSsid() {
+        if (!WifiFeature.isWifiSupported(getContext())) {
+            // skip the test if WiFi is not supported
+            return;
+        }
+        WifiNetworkSpecifier specifier = createSpecifierBuilderWithCredentialFromSavedNetwork()
+                .setSsid(WifiInfo.sanitizeSsid(mTestNetwork.SSID))
+                .build();
+        testSuccessfulConnectionWithSpecifier(specifier);
+    }
+
+    /**
+     * Tests the entire connection flow using a SSID pattern in the specifier.
+     */
+    public void testConnectionWithSsidPattern() {
+        if (!WifiFeature.isWifiSupported(getContext())) {
+            // skip the test if WiFi is not supported
+            return;
+        }
+        // Creates a ssid pattern by dropping the last char in the saved network & pass that
+        // as a prefix match pattern in the request.
+        String ssidUnquoted = WifiInfo.sanitizeSsid(mTestNetwork.SSID);
+        assertThat(ssidUnquoted.length()).isAtLeast(2);
+        String ssidPrefix = ssidUnquoted.substring(0, ssidUnquoted.length() - 1);
+        // Note: The match may return more than 1 network in this case since we use a prefix match,
+        // But, we will still ensure that the UI interactions in the test still selects the
+        // saved network for connection.
+        WifiNetworkSpecifier specifier = createSpecifierBuilderWithCredentialFromSavedNetwork()
+                .setSsidPattern(new PatternMatcher(ssidPrefix, PatternMatcher.PATTERN_PREFIX))
+                .build();
+        testSuccessfulConnectionWithSpecifier(specifier);
+    }
+
+    private static class TestScanResultsCallback extends WifiManager.ScanResultsCallback {
+        private final Object mLock;
+        public boolean onAvailableCalled = false;
+
+        TestScanResultsCallback(Object lock) {
+            mLock = lock;
+        }
+
+        @Override
+        public void onScanResultsAvailable() {
+            synchronized (mLock) {
+                onAvailableCalled = true;
+                mLock.notify();
+            }
+        }
+    }
+
+    /**
+     * Loops through all available scan results and finds the first match for the saved network.
+     *
+     * Note:
+     * a) If there are more than 2 networks with the same SSID, but different credential type, then
+     * this matching may pick the wrong one.
+     */
+    private ScanResult findScanResultMatchingSavedNetwork() {
+        // Trigger a scan to get fresh scan results.
+        TestScanResultsCallback scanResultsCallback = new TestScanResultsCallback(mLock);
+        synchronized (mLock) {
+            try {
+                mWifiManager.registerScanResultsCallback(
+                        Executors.newSingleThreadExecutor(), scanResultsCallback);
+                mWifiManager.startScan(new WorkSource(myUid()));
+                // now wait for callback
+                mLock.wait(DURATION_NETWORK_CONNECTION);
+            } catch (InterruptedException e) {
+            } finally {
+                mWifiManager.unregisterScanResultsCallback(scanResultsCallback);
+            }
+        }
+        List<ScanResult> scanResults = mWifiManager.getScanResults();
+        if (scanResults == null || scanResults.isEmpty()) fail("No scan results available");
+        for (ScanResult scanResult : scanResults) {
+            if (TextUtils.equals(scanResult.SSID, WifiInfo.sanitizeSsid(mTestNetwork.SSID))) {
+                return scanResult;
+            }
+        }
+        fail("No matching scan results found");
+        return null;
+    }
+
+    /**
+     * Tests the entire connection flow using a specific BSSID in the specifier.
+     */
+    public void testConnectionWithSpecificBssid() {
+        if (!WifiFeature.isWifiSupported(getContext())) {
+            // skip the test if WiFi is not supported
+            return;
+        }
+        ScanResult scanResult = findScanResultMatchingSavedNetwork();
+        WifiNetworkSpecifier specifier = createSpecifierBuilderWithCredentialFromSavedNetwork()
+                .setBssid(MacAddress.fromString(scanResult.BSSID))
+                .build();
+        testSuccessfulConnectionWithSpecifier(specifier);
+    }
+
+    /**
+     * Tests the entire connection flow using a BSSID pattern in the specifier.
+     */
+    public void testConnectionWithBssidPattern() {
+        if (!WifiFeature.isWifiSupported(getContext())) {
+            // skip the test if WiFi is not supported
+            return;
+        }
+        ScanResult scanResult = findScanResultMatchingSavedNetwork();
+        // Note: The match may return more than 1 network in this case since we use a prefix match,
+        // But, we will still ensure that the UI interactions in the test still selects the
+        // saved network for connection.
+        WifiNetworkSpecifier specifier = createSpecifierBuilderWithCredentialFromSavedNetwork()
+                .setBssidPattern(MacAddress.fromString(scanResult.BSSID),
+                        MacAddress.fromString("ff:ff:ff:00:00:00"))
+                .build();
+        testSuccessfulConnectionWithSpecifier(specifier);
+    }
+
+    /**
+     * Tests the entire connection flow using a BSSID pattern in the specifier.
+     */
+    public void testUserRejectionWithSpecificSsid() {
+        if (!WifiFeature.isWifiSupported(getContext())) {
+            // skip the test if WiFi is not supported
+            return;
+        }
+        WifiNetworkSpecifier specifier = createSpecifierBuilderWithCredentialFromSavedNetwork()
+                .setSsid(WifiInfo.sanitizeSsid(mTestNetwork.SSID))
+                .build();
+        testUserRejectionWithSpecifier(specifier);
+    }
+}
diff --git a/tests/cts/net/src/android/net/wifi/cts/WifiNetworkSuggestionTest.java b/tests/cts/net/src/android/net/wifi/cts/WifiNetworkSuggestionTest.java
new file mode 100644
index 0000000..994b6c9
--- /dev/null
+++ b/tests/cts/net/src/android/net/wifi/cts/WifiNetworkSuggestionTest.java
@@ -0,0 +1,245 @@
+/*
+ * Copyright (C) 2020 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 static android.net.wifi.WifiEnterpriseConfig.Eap.AKA;
+
+import android.net.MacAddress;
+import android.net.wifi.WifiEnterpriseConfig;
+import android.net.wifi.WifiNetworkSuggestion;
+import android.net.wifi.hotspot2.PasspointConfiguration;
+import android.net.wifi.hotspot2.pps.Credential;
+import android.net.wifi.hotspot2.pps.HomeSp;
+import android.telephony.TelephonyManager;
+import android.test.AndroidTestCase;
+
+public class WifiNetworkSuggestionTest extends AndroidTestCase {
+    private static final String TEST_SSID = "testSsid";
+    private static final String TEST_BSSID = "00:df:aa:bc:12:23";
+    private static final String TEST_PASSPHRASE = "testPassword";
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        if (!WifiFeature.isWifiSupported(getContext())) {
+            // skip the test if WiFi is not supported
+            return;
+        }
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        if (!WifiFeature.isWifiSupported(getContext())) {
+            // skip the test if WiFi is not supported
+            super.tearDown();
+            return;
+        }
+        super.tearDown();
+    }
+
+    private WifiNetworkSuggestion.Builder createBuilderWithCommonParams() {
+        return createBuilderWithCommonParams(false);
+    }
+
+    private WifiNetworkSuggestion.Builder createBuilderWithCommonParams(boolean isPasspoint) {
+        WifiNetworkSuggestion.Builder builder = new WifiNetworkSuggestion.Builder();
+        if (!isPasspoint) {
+            builder.setSsid(TEST_SSID);
+            builder.setBssid(MacAddress.fromString(TEST_BSSID));
+            builder.setIsEnhancedOpen(false);
+            builder.setIsHiddenSsid(true);
+        }
+        builder.setPriority(0);
+        builder.setIsAppInteractionRequired(true);
+        builder.setIsUserInteractionRequired(true);
+        builder.setIsMetered(true);
+        builder.setCarrierId(TelephonyManager.UNKNOWN_CARRIER_ID);
+        builder.setCredentialSharedWithUser(true);
+        builder.setIsInitialAutojoinEnabled(true);
+        builder.setUntrusted(false);
+        return builder;
+    }
+
+    private void validateCommonParams(WifiNetworkSuggestion suggestion) {
+        validateCommonParams(suggestion, false);
+    }
+
+    private void validateCommonParams(WifiNetworkSuggestion suggestion, boolean isPasspoint) {
+        assertNotNull(suggestion);
+        assertNotNull(suggestion.getWifiConfiguration());
+        if (!isPasspoint) {
+            assertEquals(TEST_SSID, suggestion.getSsid());
+            assertEquals(TEST_BSSID, suggestion.getBssid().toString());
+            assertFalse(suggestion.isEnhancedOpen());
+            assertTrue(suggestion.isHiddenSsid());
+        }
+        assertEquals(0, suggestion.getPriority());
+        assertTrue(suggestion.isAppInteractionRequired());
+        assertTrue(suggestion.isUserInteractionRequired());
+        assertTrue(suggestion.isMetered());
+        assertTrue(suggestion.isCredentialSharedWithUser());
+        assertTrue(suggestion.isInitialAutojoinEnabled());
+        assertFalse(suggestion.isUntrusted());
+    }
+
+    /**
+     * Tests {@link android.net.wifi.WifiNetworkSuggestion.Builder} class.
+     */
+    public void testBuilderWithWpa2Passphrase() throws Exception {
+        if (!WifiFeature.isWifiSupported(getContext())) {
+            // skip the test if WiFi is not supported
+            return;
+        }
+        WifiNetworkSuggestion suggestion =
+                createBuilderWithCommonParams()
+                .setWpa2Passphrase(TEST_PASSPHRASE)
+                .build();
+        validateCommonParams(suggestion);
+        assertEquals(TEST_PASSPHRASE, suggestion.getPassphrase());
+        assertNotNull(suggestion.getEnterpriseConfig());
+        assertNull(suggestion.getPasspointConfig());
+    }
+
+    /**
+     * Tests {@link android.net.wifi.WifiNetworkSuggestion.Builder} class.
+     */
+    public void testBuilderWithWpa3Passphrase() throws Exception {
+        if (!WifiFeature.isWifiSupported(getContext())) {
+            // skip the test if WiFi is not supported
+            return;
+        }
+        WifiNetworkSuggestion suggestion =
+                createBuilderWithCommonParams()
+                        .setWpa3Passphrase(TEST_PASSPHRASE)
+                        .build();
+        validateCommonParams(suggestion);
+        assertEquals(TEST_PASSPHRASE, suggestion.getPassphrase());
+        assertNotNull(suggestion.getEnterpriseConfig());
+        assertNull(suggestion.getPasspointConfig());
+    }
+
+    /**
+     * Tests {@link android.net.wifi.WifiNetworkSuggestion.Builder} class.
+     */
+    public void testBuilderWithWapiPassphrase() throws Exception {
+        if (!WifiFeature.isWifiSupported(getContext())) {
+            // skip the test if WiFi is not supported
+            return;
+        }
+        WifiNetworkSuggestion suggestion =
+                createBuilderWithCommonParams()
+                        .setWapiPassphrase(TEST_PASSPHRASE)
+                        .build();
+        validateCommonParams(suggestion);
+        assertEquals(TEST_PASSPHRASE, suggestion.getPassphrase());
+        assertNotNull(suggestion.getEnterpriseConfig());
+        assertNull(suggestion.getPasspointConfig());
+    }
+
+    private static WifiEnterpriseConfig createEnterpriseConfig() {
+        WifiEnterpriseConfig config = new WifiEnterpriseConfig();
+        config.setEapMethod(AKA);
+        return config;
+    }
+
+    /**
+     * Tests {@link android.net.wifi.WifiNetworkSuggestion.Builder} class.
+     */
+    public void testBuilderWithWpa2Enterprise() throws Exception {
+        if (!WifiFeature.isWifiSupported(getContext())) {
+            // skip the test if WiFi is not supported
+            return;
+        }
+        WifiEnterpriseConfig enterpriseConfig = createEnterpriseConfig();
+        WifiNetworkSuggestion suggestion =
+                createBuilderWithCommonParams()
+                        .setWpa2EnterpriseConfig(enterpriseConfig)
+                        .build();
+        validateCommonParams(suggestion);
+        assertNull(suggestion.getPassphrase());
+        assertNotNull(suggestion.getEnterpriseConfig());
+        assertEquals(enterpriseConfig.getEapMethod(),
+                suggestion.getEnterpriseConfig().getEapMethod());
+        assertNull(suggestion.getPasspointConfig());
+    }
+
+    /**
+     * Tests {@link android.net.wifi.WifiNetworkSuggestion.Builder} class.
+     */
+    public void testBuilderWithWpa3Enterprise() throws Exception {
+        if (!WifiFeature.isWifiSupported(getContext())) {
+            // skip the test if WiFi is not supported
+            return;
+        }
+        WifiEnterpriseConfig enterpriseConfig = createEnterpriseConfig();
+        WifiNetworkSuggestion suggestion =
+                createBuilderWithCommonParams()
+                        .setWpa3EnterpriseConfig(enterpriseConfig)
+                        .build();
+        validateCommonParams(suggestion);
+        assertNull(suggestion.getPassphrase());
+        assertNotNull(suggestion.getEnterpriseConfig());
+        assertEquals(enterpriseConfig.getEapMethod(),
+                suggestion.getEnterpriseConfig().getEapMethod());
+        assertNull(suggestion.getPasspointConfig());
+    }
+
+    /**
+     * Helper function for creating a {@link PasspointConfiguration} for testing.
+     *
+     * @return {@link PasspointConfiguration}
+     */
+    private static PasspointConfiguration createPasspointConfig() {
+        HomeSp homeSp = new HomeSp();
+        homeSp.setFqdn("fqdn");
+        homeSp.setFriendlyName("friendly name");
+        homeSp.setRoamingConsortiumOis(new long[] {0x55, 0x66});
+        Credential cred = new Credential();
+        cred.setRealm("realm");
+        cred.setUserCredential(null);
+        cred.setCertCredential(null);
+        cred.setSimCredential(new Credential.SimCredential());
+        cred.getSimCredential().setImsi("1234*");
+        cred.getSimCredential().setEapType(23); // EAP-AKA
+        cred.setCaCertificate(null);
+        cred.setClientCertificateChain(null);
+        cred.setClientPrivateKey(null);
+        PasspointConfiguration config = new PasspointConfiguration();
+        config.setHomeSp(homeSp);
+        config.setCredential(cred);
+        return config;
+    }
+
+    /**
+     * Tests {@link android.net.wifi.WifiNetworkSuggestion.Builder} class.
+     */
+    public void testBuilderWithPasspointConfig() throws Exception {
+        if (!WifiFeature.isWifiSupported(getContext())) {
+            // skip the test if WiFi is not supported
+            return;
+        }
+        PasspointConfiguration passpointConfig = createPasspointConfig();
+        WifiNetworkSuggestion suggestion =
+                createBuilderWithCommonParams(true)
+                        .setPasspointConfig(passpointConfig)
+                        .build();
+        validateCommonParams(suggestion, true);
+        assertNull(suggestion.getPassphrase());
+        assertNotNull(suggestion.getEnterpriseConfig());
+        assertEquals(passpointConfig, suggestion.getPasspointConfig());
+    }
+}
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
index ee7e1ed..0a2a2e6 100644
--- a/tests/cts/net/src/android/net/wifi/p2p/cts/WifiP2pConfigTest.java
+++ b/tests/cts/net/src/android/net/wifi/p2p/cts/WifiP2pConfigTest.java
@@ -28,6 +28,24 @@
     private static final int TEST_OWNER_FREQ = 2447;
     private static final String TEST_DEVICE_ADDRESS = "aa:bb:cc:dd:ee:ff";
 
+    public void testWifiP2pConfigCopyConstructor() {
+        WifiP2pConfig config = new WifiP2pConfig.Builder()
+                .setNetworkName(TEST_NETWORK_NAME)
+                .setPassphrase(TEST_PASSPHRASE)
+                .setGroupOperatingBand(TEST_OWNER_BAND)
+                .setDeviceAddress(MacAddress.fromString(TEST_DEVICE_ADDRESS))
+                .enablePersistentMode(true)
+                .build();
+
+        WifiP2pConfig copiedConfig = new WifiP2pConfig(config);
+
+        assertEquals(copiedConfig.deviceAddress, TEST_DEVICE_ADDRESS);
+        assertEquals(copiedConfig.getNetworkName(), TEST_NETWORK_NAME);
+        assertEquals(copiedConfig.getPassphrase(), TEST_PASSPHRASE);
+        assertEquals(copiedConfig.getGroupOwnerBand(), TEST_OWNER_BAND);
+        assertEquals(copiedConfig.getNetworkId(), WifiP2pGroup.NETWORK_ID_PERSISTENT);
+    }
+
     public void testWifiP2pConfigBuilderForPersist() {
         WifiP2pConfig config = new WifiP2pConfig.Builder()
                 .setNetworkName(TEST_NETWORK_NAME)
diff --git a/tests/cts/net/src/android/net/wifi/p2p/cts/WifiP2pDeviceTest.java b/tests/cts/net/src/android/net/wifi/p2p/cts/WifiP2pDeviceTest.java
new file mode 100644
index 0000000..1510d7c
--- /dev/null
+++ b/tests/cts/net/src/android/net/wifi/p2p/cts/WifiP2pDeviceTest.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2020 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.InetAddresses;
+import android.net.wifi.p2p.WifiP2pDevice;
+import android.test.AndroidTestCase;
+
+public class WifiP2pDeviceTest extends AndroidTestCase {
+
+    public void testDefaultWpsMethodSupportCheck() {
+        WifiP2pDevice dev = new WifiP2pDevice();
+
+        assertFalse(dev.wpsPbcSupported());
+        assertFalse(dev.wpsDisplaySupported());
+        assertFalse(dev.wpsKeypadSupported());
+    }
+
+    public void testDefaultDeviceCapabilityCheck() {
+        WifiP2pDevice dev = new WifiP2pDevice();
+
+        assertFalse(dev.isServiceDiscoveryCapable());
+    }
+}
diff --git a/tests/cts/net/src/android/net/wifi/p2p/cts/WifiP2pInfoTest.java b/tests/cts/net/src/android/net/wifi/p2p/cts/WifiP2pInfoTest.java
new file mode 100644
index 0000000..8504f15
--- /dev/null
+++ b/tests/cts/net/src/android/net/wifi/p2p/cts/WifiP2pInfoTest.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2020 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.InetAddresses;
+import android.net.wifi.p2p.WifiP2pInfo;
+import android.test.AndroidTestCase;
+
+public class WifiP2pInfoTest extends AndroidTestCase {
+
+    public String TEST_GROUP_OWNER_ADDRESS = "192.168.43.1";
+
+    public void testWifiP2pInfoNoGroup() {
+        WifiP2pInfo info = new WifiP2pInfo();
+        info.groupFormed = false;
+
+        WifiP2pInfo copiedInfo = new WifiP2pInfo(info);
+        assertEquals(info.groupFormed, copiedInfo.groupFormed);
+        assertEquals(info.isGroupOwner, copiedInfo.isGroupOwner);
+        assertEquals(info.groupOwnerAddress, copiedInfo.groupOwnerAddress);
+    }
+
+    public void testWifiP2pInfoGroupOwner() {
+        WifiP2pInfo info = new WifiP2pInfo();
+        info.groupFormed = true;
+        info.isGroupOwner = true;
+        info.groupOwnerAddress = InetAddresses.parseNumericAddress(TEST_GROUP_OWNER_ADDRESS);
+
+        WifiP2pInfo copiedInfo = new WifiP2pInfo(info);
+        assertEquals(info.groupFormed, copiedInfo.groupFormed);
+        assertEquals(info.isGroupOwner, copiedInfo.isGroupOwner);
+        assertEquals(info.groupOwnerAddress, copiedInfo.groupOwnerAddress);
+    }
+
+    public void testWifiP2pInfoGroupClient() {
+        WifiP2pInfo info = new WifiP2pInfo();
+        info.groupFormed = true;
+        info.isGroupOwner = false;
+        info.groupOwnerAddress = InetAddresses.parseNumericAddress(TEST_GROUP_OWNER_ADDRESS);
+
+        WifiP2pInfo copiedInfo = new WifiP2pInfo(info);
+        assertEquals(info.groupFormed, copiedInfo.groupFormed);
+        assertEquals(info.isGroupOwner, copiedInfo.isGroupOwner);
+        assertEquals(info.groupOwnerAddress, copiedInfo.groupOwnerAddress);
+    }
+}
diff --git a/tests/cts/net/src/android/net/wifi/p2p/cts/WifiP2pServiceRequestTest.java b/tests/cts/net/src/android/net/wifi/p2p/cts/WifiP2pServiceRequestTest.java
new file mode 100644
index 0000000..b363b1e
--- /dev/null
+++ b/tests/cts/net/src/android/net/wifi/p2p/cts/WifiP2pServiceRequestTest.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2020 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.wifi.p2p.nsd.WifiP2pServiceInfo;
+import android.net.wifi.p2p.nsd.WifiP2pServiceRequest;
+import android.net.wifi.p2p.nsd.WifiP2pUpnpServiceRequest;
+import android.test.AndroidTestCase;
+import android.util.Log;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Locale;
+import java.util.stream.Collectors;
+
+public class WifiP2pServiceRequestTest extends AndroidTestCase {
+
+    private final int TEST_UPNP_VERSION = 0x10;
+    private final String TEST_UPNP_QUERY = "ssdp:all";
+
+    private String bin2HexStr(byte[] data) {
+        StringBuffer sb = new StringBuffer();
+        for (byte b: data) {
+            sb.append(String.format(Locale.US, "%02x", b & 0xff));
+        }
+        return sb.toString();
+    }
+
+    public void testValidRawRequest() throws IllegalArgumentException {
+        StringBuffer sb = new StringBuffer();
+        sb.append(String.format(Locale.US, "%02x", TEST_UPNP_VERSION));
+        sb.append(bin2HexStr(TEST_UPNP_QUERY.getBytes()));
+
+        WifiP2pServiceRequest rawRequest =
+                WifiP2pServiceRequest.newInstance(
+                        WifiP2pServiceInfo.SERVICE_TYPE_UPNP,
+                        sb.toString());
+
+        WifiP2pUpnpServiceRequest upnpRequest =
+                WifiP2pUpnpServiceRequest.newInstance(
+                        TEST_UPNP_QUERY);
+
+        assertEquals(rawRequest, upnpRequest);
+    }
+
+    public void testInvalidRawRequest() {
+        StringBuffer sb = new StringBuffer();
+        sb.append(String.format(Locale.US, "%02x", TEST_UPNP_VERSION));
+        sb.append(bin2HexStr(TEST_UPNP_QUERY.getBytes()));
+        sb.append("x");
+
+        try {
+            WifiP2pServiceRequest request =
+                    WifiP2pServiceRequest.newInstance(
+                            WifiP2pServiceInfo.SERVICE_TYPE_UPNP, sb.toString());
+            fail("Expected IllegalArgumentException");
+        } catch (IllegalArgumentException ex) {
+            return;
+        }
+    }
+}
diff --git a/tests/cts/net/src/android/net/wifi/p2p/cts/WifiP2pWfdInfoTest.java b/tests/cts/net/src/android/net/wifi/p2p/cts/WifiP2pWfdInfoTest.java
new file mode 100644
index 0000000..75df5bf
--- /dev/null
+++ b/tests/cts/net/src/android/net/wifi/p2p/cts/WifiP2pWfdInfoTest.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2020 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.wifi.p2p.WifiP2pWfdInfo;
+import android.test.AndroidTestCase;
+
+public class WifiP2pWfdInfoTest extends AndroidTestCase {
+
+    private final int TEST_DEVICE_TYPE = WifiP2pWfdInfo.DEVICE_TYPE_WFD_SOURCE;
+    private final boolean TEST_DEVICE_ENABLE_STATUS = true;
+    private final boolean TEST_SESSION_STATUS = true;
+    private final int TEST_CONTROL_PORT = 9999;
+    private final int TEST_MAX_THROUGHPUT = 1024;
+    private final boolean TEST_CONTENT_PROTECTION_SUPPORTED_STATUS = true;
+
+    public void testWifiP2pWfdInfo() {
+        WifiP2pWfdInfo info = new WifiP2pWfdInfo();
+
+        info.setDeviceType(TEST_DEVICE_TYPE);
+        info.setEnabled(TEST_DEVICE_ENABLE_STATUS);
+        info.setSessionAvailable(true);
+        info.setControlPort(TEST_CONTROL_PORT);
+        info.setMaxThroughput(TEST_MAX_THROUGHPUT);
+        info.setContentProtectionSupported(true);
+
+        WifiP2pWfdInfo copiedInfo = new WifiP2pWfdInfo(info);
+        assertEquals(TEST_DEVICE_TYPE, copiedInfo.getDeviceType());
+        assertEquals(TEST_DEVICE_ENABLE_STATUS, copiedInfo.isEnabled());
+        assertEquals(TEST_SESSION_STATUS, copiedInfo.isSessionAvailable());
+        assertEquals(TEST_CONTROL_PORT, copiedInfo.getControlPort());
+        assertEquals(TEST_MAX_THROUGHPUT, copiedInfo.getMaxThroughput());
+        assertEquals(TEST_CONTENT_PROTECTION_SUPPORTED_STATUS,
+                copiedInfo.isContentProtectionSupported());
+    }
+}
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 44a9cc2..d5361d7 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
@@ -163,7 +163,7 @@
 
         // Analyze results
         assertTrue("Wi-Fi RTT failure rate exceeds threshold: FAIL=" + numFailures + ", ITERATIONS="
-                        + NUM_OF_RTT_ITERATIONS,
+                        + NUM_OF_RTT_ITERATIONS + ", AP RSSI=" + testAp.level,
                 numFailures <= NUM_OF_RTT_ITERATIONS * MAX_FAILURE_RATE_PERCENT / 100);
         if (numFailures != NUM_OF_RTT_ITERATIONS) {
             double distanceAvg = distanceSum / (NUM_OF_RTT_ITERATIONS - numFailures);