Allow connecting with no requests if we're already connected.

Currently, we drop all connection start attempts if we do not
have any NetworkRequests that we can fulfil. This can happen if
the default network has a score that's higher than our score
filter. The reason for this is that we don't want to connect a
new network if we're not already connected: if we did,
ConnectivityService would immediately request that the network be
torn down.

However, it does make sense to connect if we are already
connected, because that means that we ourselves are the
highest-scoring network - otherwise, ConnectivityService would
have torn us down. So, when deciding whether to process a
connection request, allow it if mNetworkAgent is non-null, even
if there are no NetworkRequests that we can fulfil.

In order to do this, move the check from startConnectToNetwork to
the code that processes CMD_START_CONNECT, because it's not safe
to access mNetworkAgent except from the handler thread. Moving
the check also seems more correct in general, because
the return value of hasConnectionRequests() could change between
when startConnectToNetwork is called and when the resulting
CMD_START_CONNECT is processed.

The connect requests are only allowed from the settings/sysui. We don't
allow any non-privileged apps to be able to override the user's decision
to stay connected to a network with no internet access (which causes the
mNetworkAgent.releaseNetworkFor()).

Bug: 65262556
Test: Unit tests for START_CONNECT handling for 4 scenarios:
a) START_CONNECT with the default network request.
b) START_CONNECT with no network request.
c) START_CONNECT with no network request, but already connected
d) START_CONNECT with no network request, but already connected, but
from non-privileged app.
Test: Settings can connect to a new network when wifi has a score of 100

Change-Id: Iad582b5109a81f238f335eaac3e7264c5da6e9b1
diff --git a/service/java/com/android/server/wifi/WifiConnectivityManager.java b/service/java/com/android/server/wifi/WifiConnectivityManager.java
index 7e730c8..6bfce74 100644
--- a/service/java/com/android/server/wifi/WifiConnectivityManager.java
+++ b/service/java/com/android/server/wifi/WifiConnectivityManager.java
@@ -30,6 +30,7 @@
 import android.net.wifi.WifiScanner.ScanSettings;
 import android.os.Handler;
 import android.os.Looper;
+import android.os.Process;
 import android.util.LocalLog;
 import android.util.Log;
 
@@ -741,7 +742,7 @@
                 localLog("connectToNetwork: Connect to " + targetAssociationId + " from "
                         + currentAssociationId);
             }
-            mStateMachine.startConnectToNetwork(candidate.networkId, targetBssid);
+            mStateMachine.startConnectToNetwork(candidate.networkId, Process.WIFI_UID, targetBssid);
         }
     }
 
diff --git a/service/java/com/android/server/wifi/WifiStateMachine.java b/service/java/com/android/server/wifi/WifiStateMachine.java
index a3054c9..9e12446 100644
--- a/service/java/com/android/server/wifi/WifiStateMachine.java
+++ b/service/java/com/android/server/wifi/WifiStateMachine.java
@@ -1321,7 +1321,7 @@
 
     /**
      * Initiates connection to a network specified by the user/app. This method checks if the
-     * requesting app holds the WIFI_CONFIG_OVERRIDE permission.
+     * requesting app holds the NETWORK_SETTINGS permission.
      *
      * @param netId Id network to initiate connection.
      * @param uid UID of the app requesting the connection.
@@ -1350,7 +1350,7 @@
             logi("connectToUserSelectNetwork already connecting/connected=" + netId);
         } else {
             mWifiConnectivityManager.prepareForForcedConnection(netId);
-            startConnectToNetwork(netId, SUPPLICANT_BSSID_ANY);
+            startConnectToNetwork(netId, uid, SUPPLICANT_BSSID_ANY);
         }
         return true;
     }
@@ -5178,7 +5178,23 @@
                 case CMD_START_CONNECT:
                     /* connect command coming from auto-join */
                     netId = message.arg1;
+                    int uid = message.arg2;
                     bssid = (String) message.obj;
+
+                    synchronized (mWifiReqCountLock) {
+                        if (!hasConnectionRequests()) {
+                            if (mNetworkAgent == null) {
+                                loge("CMD_START_CONNECT but no requests and not connected,"
+                                        + " bailing");
+                                break;
+                            } else if (!mWifiPermissionsUtil.checkNetworkSettingsPermission(uid)) {
+                                loge("CMD_START_CONNECT but no requests and connected, but app "
+                                        + "does not have sufficient permissions, bailing");
+                                break;
+                            }
+                        }
+                    }
+
                     config = mWifiConfigManager.getConfiguredNetworkWithPassword(netId);
                     logd("CMD_START_CONNECT sup state "
                             + mSupplicantStateTracker.getSupplicantStateName()
@@ -5278,7 +5294,7 @@
                             // start a new connection with the updated credentials.
                             logi("SAVE_NETWORK credential changed for config=" + config.configKey()
                                     + ", Reconnecting.");
-                            startConnectToNetwork(netId, SUPPLICANT_BSSID_ANY);
+                            startConnectToNetwork(netId, message.sendingUid, SUPPLICANT_BSSID_ANY);
                         } else {
                             if (result.hasProxyChanged()) {
                                 log("Reconfiguring proxy on connection");
@@ -6121,7 +6137,7 @@
         WifiConfiguration config = getCurrentWifiConfiguration();
         if (shouldEvaluateWhetherToSendExplicitlySelected(config)) {
             boolean prompt =
-                    mWifiPermissionsUtil.checkConfigOverridePermission(config.lastConnectUid);
+                    mWifiPermissionsUtil.checkNetworkSettingsPermission(config.lastConnectUid);
             if (mVerboseLoggingEnabled) {
                 log("Network selected by UID " + config.lastConnectUid + " prompt=" + prompt);
             }
@@ -7097,14 +7113,11 @@
      * Automatically connect to the network specified
      *
      * @param networkId ID of the network to connect to
+     * @param uid UID of the app triggering the connection.
      * @param bssid BSSID of the network
      */
-    public void startConnectToNetwork(int networkId, String bssid) {
-        synchronized (mWifiReqCountLock) {
-            if (hasConnectionRequests()) {
-                sendMessage(CMD_START_CONNECT, networkId, 0, bssid);
-            }
-        }
+    public void startConnectToNetwork(int networkId, int uid, String bssid) {
+        sendMessage(CMD_START_CONNECT, networkId, uid, bssid);
     }
 
     /**
diff --git a/tests/wifitests/src/com/android/server/wifi/WifiConnectivityManagerTest.java b/tests/wifitests/src/com/android/server/wifi/WifiConnectivityManagerTest.java
index 6420fac..5fb7007 100644
--- a/tests/wifitests/src/com/android/server/wifi/WifiConnectivityManagerTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/WifiConnectivityManagerTest.java
@@ -41,6 +41,7 @@
 import android.net.wifi.WifiScanner.ScanListener;
 import android.net.wifi.WifiScanner.ScanSettings;
 import android.net.wifi.WifiSsid;
+import android.os.Process;
 import android.os.SystemClock;
 import android.os.WorkSource;
 import android.os.test.TestLooper;
@@ -314,7 +315,8 @@
         mWifiConnectivityManager.handleConnectionStateChanged(
                 WifiConnectivityManager.WIFI_STATE_DISCONNECTED);
 
-        verify(mWifiStateMachine).startConnectToNetwork(CANDIDATE_NETWORK_ID, CANDIDATE_BSSID);
+        verify(mWifiStateMachine).startConnectToNetwork(
+                CANDIDATE_NETWORK_ID, Process.WIFI_UID, CANDIDATE_BSSID);
     }
 
     /**
@@ -333,7 +335,8 @@
         mWifiConnectivityManager.handleConnectionStateChanged(
                 WifiConnectivityManager.WIFI_STATE_CONNECTED);
 
-        verify(mWifiStateMachine).startConnectToNetwork(CANDIDATE_NETWORK_ID, CANDIDATE_BSSID);
+        verify(mWifiStateMachine).startConnectToNetwork(
+                CANDIDATE_NETWORK_ID, Process.WIFI_UID, CANDIDATE_BSSID);
     }
 
     /**
@@ -353,7 +356,7 @@
         mWifiConnectivityManager.handleScreenStateChanged(true);
 
         verify(mWifiStateMachine, atLeastOnce()).startConnectToNetwork(
-                CANDIDATE_NETWORK_ID, CANDIDATE_BSSID);
+                CANDIDATE_NETWORK_ID, Process.WIFI_UID, CANDIDATE_BSSID);
     }
 
     /**
@@ -373,7 +376,7 @@
         mWifiConnectivityManager.handleScreenStateChanged(true);
 
         verify(mWifiStateMachine, atLeastOnce()).startConnectToNetwork(
-                CANDIDATE_NETWORK_ID, CANDIDATE_BSSID);
+                CANDIDATE_NETWORK_ID, Process.WIFI_UID, CANDIDATE_BSSID);
     }
 
     /**
@@ -400,7 +403,7 @@
         mWifiConnectivityManager.handleScreenStateChanged(true);
 
         verify(mWifiStateMachine, times(0)).startConnectToNetwork(
-                CANDIDATE_NETWORK_ID, CANDIDATE_BSSID);
+                CANDIDATE_NETWORK_ID, Process.WIFI_UID, CANDIDATE_BSSID);
     }
 
     /**
@@ -438,7 +441,7 @@
 
         // Verify that we attempt to connect upto the rate.
         verify(mWifiStateMachine, times(numAttempts)).startConnectToNetwork(
-                CANDIDATE_NETWORK_ID, CANDIDATE_BSSID);
+                CANDIDATE_NETWORK_ID, Process.WIFI_UID, CANDIDATE_BSSID);
     }
 
     /**
@@ -479,7 +482,7 @@
 
         // Verify that all the connection attempts went through
         verify(mWifiStateMachine, times(numAttempts)).startConnectToNetwork(
-                CANDIDATE_NETWORK_ID, CANDIDATE_BSSID);
+                CANDIDATE_NETWORK_ID, Process.WIFI_UID, CANDIDATE_BSSID);
     }
 
     /**
@@ -523,7 +526,7 @@
 
         // Verify that all the connection attempts went through
         verify(mWifiStateMachine, times(numAttempts)).startConnectToNetwork(
-                CANDIDATE_NETWORK_ID, CANDIDATE_BSSID);
+                CANDIDATE_NETWORK_ID, Process.WIFI_UID, CANDIDATE_BSSID);
     }
 
     /**
@@ -1152,7 +1155,8 @@
 
         // Verify that WCM receives the scan results and initiates a connection
         // to the network.
-        verify(mWifiStateMachine).startConnectToNetwork(CANDIDATE_NETWORK_ID, CANDIDATE_BSSID);
+        verify(mWifiStateMachine).startConnectToNetwork(
+                CANDIDATE_NETWORK_ID, Process.WIFI_UID, CANDIDATE_BSSID);
     }
 
     /**
@@ -1178,7 +1182,7 @@
 
         // No roaming because no full band scan results.
         verify(mWifiStateMachine, times(0)).startConnectToNetwork(
-                CANDIDATE_NETWORK_ID, CANDIDATE_BSSID);
+                CANDIDATE_NETWORK_ID, Process.WIFI_UID, CANDIDATE_BSSID);
 
         // Set up as full band scan results.
         when(mScanData.isAllChannelsScanned()).thenReturn(true);
@@ -1188,7 +1192,8 @@
         mWifiConnectivityManager.forceConnectivityScan();
 
         // Roaming attempt because full band scan results are available.
-        verify(mWifiStateMachine).startConnectToNetwork(CANDIDATE_NETWORK_ID, CANDIDATE_BSSID);
+        verify(mWifiStateMachine).startConnectToNetwork(
+                CANDIDATE_NETWORK_ID, Process.WIFI_UID, CANDIDATE_BSSID);
     }
 
     /**
@@ -1441,7 +1446,7 @@
                 WifiConnectivityManager.WIFI_STATE_DISCONNECTED);
 
         verify(mWifiStateMachine).startConnectToNetwork(
-                CANDIDATE_NETWORK_ID, WifiStateMachine.SUPPLICANT_BSSID_ANY);
+                CANDIDATE_NETWORK_ID, Process.WIFI_UID, WifiStateMachine.SUPPLICANT_BSSID_ANY);
     }
 
     /*
@@ -1477,7 +1482,8 @@
         mWifiConnectivityManager.handleConnectionStateChanged(
                 WifiConnectivityManager.WIFI_STATE_DISCONNECTED);
 
-        verify(mWifiStateMachine).startConnectToNetwork(CANDIDATE_NETWORK_ID, CANDIDATE_BSSID);
+        verify(mWifiStateMachine).startConnectToNetwork(
+                CANDIDATE_NETWORK_ID, Process.WIFI_UID, CANDIDATE_BSSID);
     }
 
     /*
@@ -1497,7 +1503,8 @@
         mWifiConnectivityManager.handleConnectionStateChanged(
                 WifiConnectivityManager.WIFI_STATE_DISCONNECTED);
 
-        verify(mWifiStateMachine).startConnectToNetwork(CANDIDATE_NETWORK_ID, CANDIDATE_BSSID);
+        verify(mWifiStateMachine).startConnectToNetwork(
+                CANDIDATE_NETWORK_ID, Process.WIFI_UID, CANDIDATE_BSSID);
     }
 
     /*
@@ -1529,7 +1536,8 @@
         mWifiConnectivityManager.handleConnectionStateChanged(
                 WifiConnectivityManager.WIFI_STATE_DISCONNECTED);
 
-        verify(mWifiStateMachine).startConnectToNetwork(CANDIDATE_NETWORK_ID, CANDIDATE_BSSID);
+        verify(mWifiStateMachine).startConnectToNetwork(
+                CANDIDATE_NETWORK_ID, Process.WIFI_UID, CANDIDATE_BSSID);
     }
 
     /**
@@ -1619,7 +1627,7 @@
                 WifiConnectivityManager.WIFI_STATE_DISCONNECTED);
 
         verify(mWifiStateMachine, times(0)).startConnectToNetwork(
-                CANDIDATE_NETWORK_ID, CANDIDATE_BSSID);
+                CANDIDATE_NETWORK_ID, Process.WIFI_UID, CANDIDATE_BSSID);
     }
 
     /*
diff --git a/tests/wifitests/src/com/android/server/wifi/WifiStateMachineTest.java b/tests/wifitests/src/com/android/server/wifi/WifiStateMachineTest.java
index 5b0f596..ecb4c95 100644
--- a/tests/wifitests/src/com/android/server/wifi/WifiStateMachineTest.java
+++ b/tests/wifitests/src/com/android/server/wifi/WifiStateMachineTest.java
@@ -47,6 +47,9 @@
 import android.net.ConnectivityManager;
 import android.net.DhcpResults;
 import android.net.LinkProperties;
+import android.net.NetworkCapabilities;
+import android.net.NetworkFactory;
+import android.net.NetworkRequest;
 import android.net.dhcp.DhcpClient;
 import android.net.ip.IpManager;
 import android.net.wifi.IApInterface;
@@ -123,6 +126,7 @@
 import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.CountDownLatch;
+import java.util.function.Consumer;
 
 /**
  * Unit tests for {@link com.android.server.wifi.WifiStateMachine}.
@@ -137,9 +141,11 @@
             (ActivityManager.isLowRamDeviceStatic()
                     ? WifiStateMachine.NUM_LOG_RECS_VERBOSE_LOW_MEMORY
                     : WifiStateMachine.NUM_LOG_RECS_VERBOSE);
-    private static final int FRAMEWORK_NETWORK_ID = 7;
+    private static final int FRAMEWORK_NETWORK_ID = 0;
     private static final int TEST_RSSI = -54;
     private static final int TEST_NETWORK_ID = 54;
+    private static final int TEST_VALID_NETWORK_SCORE = 54;
+    private static final int TEST_OUTSCORED_NETWORK_SCORE = 100;
     private static final int WPS_SUPPLICANT_NETWORK_ID = 5;
     private static final int WPS_FRAMEWORK_NETWORK_ID = 10;
     private static final String DEFAULT_TEST_SSID = "\"GoogleGuest\"";
@@ -230,7 +236,7 @@
                 mAlarmManager.getAlarmManager());
 
         when(context.getSystemService(Context.CONNECTIVITY_SERVICE)).thenReturn(
-                mock(ConnectivityManager.class));
+                mConnectivityManager);
 
         when(context.getOpPackageName()).thenReturn(OP_PACKAGE_NAME);
 
@@ -318,6 +324,7 @@
     HandlerThread mP2pThread;
     HandlerThread mSyncThread;
     AsyncChannel  mWsmAsyncChannel;
+    AsyncChannel  mNetworkFactoryChannel;
     TestAlarmManager mAlarmManager;
     MockWifiMonitor mWifiMonitor;
     TestLooper mLooper;
@@ -326,6 +333,7 @@
     FrameworkFacade mFrameworkFacade;
     IpManager.Callback mIpManagerCallback;
     PhoneStateListener mPhoneStateListener;
+    NetworkRequest mDefaultNetworkRequest;
 
     final ArgumentCaptor<SoftApManager.Listener> mSoftApManagerListenerCaptor =
                     ArgumentCaptor.forClass(SoftApManager.Listener.class);
@@ -361,6 +369,7 @@
     @Mock Clock mClock;
     @Mock ScanDetailCache mScanDetailCache;
     @Mock BaseWifiDiagnostics mWifiDiagnostics;
+    @Mock ConnectivityManager mConnectivityManager;
 
     public WifiStateMachineTest() throws Exception {
     }
@@ -449,15 +458,11 @@
             }
         }).when(mTelephonyManager).listen(any(PhoneStateListener.class), anyInt());
 
+        when(mWifiPermissionsUtil.checkNetworkSettingsPermission(anyInt())).thenReturn(true);
         initializeWsm();
     }
 
-    private void initializeWsm() throws Exception {
-        mWsm = new WifiStateMachine(mContext, mFrameworkFacade, mLooper.getLooper(),
-                mUserManager, mWifiInjector, mBackupManagerProxy, mCountryCode, mWifiNative,
-                mWrongPasswordNotifier);
-        mWsmThread = getWsmHandlerThread(mWsm);
-
+    private void registerAsyncChannel(Consumer<AsyncChannel> consumer, Messenger messenger) {
         final AsyncChannel channel = new AsyncChannel();
         Handler handler = new Handler(mLooper.getLooper()) {
             @Override
@@ -465,7 +470,7 @@
                 switch (msg.what) {
                     case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED:
                         if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) {
-                            mWsmAsyncChannel = channel;
+                            consumer.accept(channel);
                         } else {
                             Log.d(TAG, "Failed to connect Command channel " + this);
                         }
@@ -477,15 +482,50 @@
             }
         };
 
-        channel.connect(mContext, handler, mWsm.getMessenger());
+        channel.connect(mContext, handler, messenger);
         mLooper.dispatchAll();
-        /* Now channel is supposed to be connected */
+    }
+
+    private void initializeWsm() throws Exception {
+        mWsm = new WifiStateMachine(mContext, mFrameworkFacade, mLooper.getLooper(),
+                mUserManager, mWifiInjector, mBackupManagerProxy, mCountryCode, mWifiNative,
+                mWrongPasswordNotifier);
+        mWsmThread = getWsmHandlerThread(mWsm);
+
+        registerAsyncChannel((x) -> {
+            mWsmAsyncChannel = x;
+        }, mWsm.getMessenger());
 
         mBinderToken = Binder.clearCallingIdentity();
 
         /* Send the BOOT_COMPLETED message to setup some WSM state. */
         mWsm.sendMessage(WifiStateMachine.CMD_BOOT_COMPLETED);
         mLooper.dispatchAll();
+
+        /* Simulate the initial NetworkRequest sent in by ConnectivityService. */
+        ArgumentCaptor<Messenger> captor = ArgumentCaptor.forClass(Messenger.class);
+        verify(mConnectivityManager, atLeast(2)).registerNetworkFactory(
+                captor.capture(), anyString());
+        Messenger networkFactoryMessenger = captor.getAllValues().get(0);
+        registerAsyncChannel((x) -> {
+            mNetworkFactoryChannel = x;
+        }, networkFactoryMessenger);
+
+        mDefaultNetworkRequest = new NetworkRequest.Builder()
+                .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
+                .build();
+        sendDefaultNetworkRequest(TEST_VALID_NETWORK_SCORE);
+    }
+
+    /**
+     * Helper function to resend the cached network request (id == 0) with the specified score.
+     * Note: If you need to add a separate network request, don't use the builder to create one
+     * since the new request object will again default to id == 0.
+     */
+    private void sendDefaultNetworkRequest(int score) {
+        mNetworkFactoryChannel.sendMessage(
+                NetworkFactory.CMD_REQUEST_NETWORK, score, 0, mDefaultNetworkRequest);
+        mLooper.dispatchAll();
     }
 
     @After
@@ -501,6 +541,7 @@
         mSyncThread = null;
         mWsmAsyncChannel = null;
         mWsm = null;
+        mNetworkFactoryChannel = null;
     }
 
     @Test
@@ -1043,22 +1084,81 @@
                 hiddenNetworkSet);
     }
 
-    @Test
-    public void connect() throws Exception {
-        initializeAndAddNetworkAndVerifySuccess();
-        when(mWifiConfigManager.enableNetwork(eq(0), eq(true), anyInt())).thenReturn(true);
-        when(mWifiConfigManager.checkAndUpdateLastConnectUid(eq(0), anyInt())).thenReturn(true);
+    private void setupAndStartConnectSequence(WifiConfiguration config) throws Exception {
+        when(mWifiConfigManager.enableNetwork(eq(config.networkId), eq(true), anyInt()))
+                .thenReturn(true);
+        when(mWifiConfigManager.checkAndUpdateLastConnectUid(eq(config.networkId), anyInt()))
+                .thenReturn(true);
+        when(mWifiConfigManager.getConfiguredNetwork(eq(config.networkId)))
+                .thenReturn(config);
+        when(mWifiConfigManager.getConfiguredNetworkWithPassword(eq(config.networkId)))
+                .thenReturn(config);
 
         mWsm.setOperationalMode(WifiStateMachine.CONNECT_MODE);
         mLooper.dispatchAll();
         verify(mWifiNative).removeAllNetworks();
 
         mLooper.startAutoDispatch();
-        assertTrue(mWsm.syncEnableNetwork(mWsmAsyncChannel, 0, true));
+        assertTrue(mWsm.syncEnableNetwork(mWsmAsyncChannel, config.networkId, true));
         mLooper.stopAutoDispatch();
+    }
 
-        verify(mWifiConfigManager).enableNetwork(eq(0), eq(true), anyInt());
-        verify(mWifiConnectivityManager).setUserConnectChoice(eq(0));
+    private void validateSuccessfulConnectSequence(WifiConfiguration config) {
+        verify(mWifiConfigManager).enableNetwork(eq(config.networkId), eq(true), anyInt());
+        verify(mWifiConnectivityManager).setUserConnectChoice(eq(config.networkId));
+        verify(mWifiConnectivityManager).prepareForForcedConnection(eq(config.networkId));
+        verify(mWifiConfigManager).getConfiguredNetworkWithPassword(eq(config.networkId));
+        verify(mWifiNative).connectToNetwork(eq(config));
+    }
+
+    private void validateFailureConnectSequence(WifiConfiguration config) {
+        verify(mWifiConfigManager).enableNetwork(eq(config.networkId), eq(true), anyInt());
+        verify(mWifiConnectivityManager).setUserConnectChoice(eq(config.networkId));
+        verify(mWifiConnectivityManager).prepareForForcedConnection(eq(config.networkId));
+        verify(mWifiConfigManager, never()).getConfiguredNetworkWithPassword(eq(config.networkId));
+        verify(mWifiNative, never()).connectToNetwork(eq(config));
+    }
+
+    /**
+     * Tests the network connection initiation sequence with the default network request pending
+     * from WifiNetworkFactory.
+     * This simulates the connect sequence using the public
+     * {@link WifiManager#enableNetwork(int, boolean)} and ensures that we invoke
+     * {@link WifiNative#connectToNetwork(WifiConfiguration)}.
+     */
+    @Test
+    public void triggerConnect() throws Exception {
+        loadComponentsInStaMode();
+        WifiConfiguration config = WifiConfigurationTestUtil.createOpenNetwork();
+        config.networkId = FRAMEWORK_NETWORK_ID;
+        setupAndStartConnectSequence(config);
+        validateSuccessfulConnectSequence(config);
+    }
+
+    /**
+     * Tests the network connection initiation sequence with no network request pending from
+     * from WifiNetworkFactory.
+     * This simulates the connect sequence using the public
+     * {@link WifiManager#enableNetwork(int, boolean)} and ensures that we don't invoke
+     * {@link WifiNative#connectToNetwork(WifiConfiguration)}.
+     */
+    @Test
+    public void triggerConnectWithNoNetworkRequest() throws Exception {
+        loadComponentsInStaMode();
+        // Change the network score to remove the network request.
+        sendDefaultNetworkRequest(TEST_OUTSCORED_NETWORK_SCORE);
+        WifiConfiguration config = WifiConfigurationTestUtil.createOpenNetwork();
+        config.networkId = FRAMEWORK_NETWORK_ID;
+        setupAndStartConnectSequence(config);
+        validateFailureConnectSequence(config);
+    }
+
+    /**
+     * Tests the entire successful network connection flow.
+     */
+    @Test
+    public void connect() throws Exception {
+        triggerConnect();
         when(mWifiConfigManager.getScanDetailCacheForNetwork(FRAMEWORK_NETWORK_ID))
                 .thenReturn(mScanDetailCache);
 
@@ -1094,6 +1194,54 @@
         assertEquals("ConnectedState", getCurrentState().getName());
     }
 
+    /**
+     * Tests the network connection initiation sequence with no network request pending from
+     * from WifiNetworkFactory when we're already connected to a different network.
+     * This simulates the connect sequence using the public
+     * {@link WifiManager#enableNetwork(int, boolean)} and ensures that we invoke
+     * {@link WifiNative#connectToNetwork(WifiConfiguration)}.
+     */
+    @Test
+    public void triggerConnectWithNoNetworkRequestAndAlreadyConnected() throws Exception {
+        // Simulate the first connection.
+        connect();
+
+        // Change the network score to remove the network request.
+        sendDefaultNetworkRequest(TEST_OUTSCORED_NETWORK_SCORE);
+
+        WifiConfiguration config = WifiConfigurationTestUtil.createOpenNetwork();
+        config.networkId = FRAMEWORK_NETWORK_ID + 1;
+        setupAndStartConnectSequence(config);
+        validateSuccessfulConnectSequence(config);
+        verify(mWifiPermissionsUtil, times(2)).checkNetworkSettingsPermission(anyInt());
+    }
+
+    /**
+     * Tests the network connection initiation sequence from a non-privileged app with no network
+     * request pending from from WifiNetworkFactory when we're already connected to a different
+     * network.
+     * This simulates the connect sequence using the public
+     * {@link WifiManager#enableNetwork(int, boolean)} and ensures that we don't invoke
+     * {@link WifiNative#connectToNetwork(WifiConfiguration)}.
+     */
+    @Test
+    public void triggerConnectWithNoNetworkRequestAndAlreadyConnectedButNonPrivilegedApp()
+            throws Exception {
+        // Simulate the first connection.
+        connect();
+
+        // Change the network score to remove the network request.
+        sendDefaultNetworkRequest(TEST_OUTSCORED_NETWORK_SCORE);
+
+        when(mWifiPermissionsUtil.checkNetworkSettingsPermission(anyInt())).thenReturn(false);
+
+        WifiConfiguration config = WifiConfigurationTestUtil.createOpenNetwork();
+        config.networkId = FRAMEWORK_NETWORK_ID + 1;
+        setupAndStartConnectSequence(config);
+        validateFailureConnectSequence(config);
+        verify(mWifiPermissionsUtil, times(2)).checkNetworkSettingsPermission(anyInt());
+    }
+
     @Test
     public void connectWithNoEnablePermission() throws Exception {
         initializeAndAddNetworkAndVerifySuccess();