Split network agent created state from connected state

Network creation setup sometimes involves extra steps after actually
calling into netd to create the underlying network, rules & routes &
to begin allowing sockets bound to it.

For example, VPN networks can set a UID whitelist or blacklist. This
should happen:

  - AFTER there is a netID & network created in netd as
    network-specific rules will need to be tied to / point at it. Those
    rules are tied to the lifecycle of netd's network which is tracked
    by `NetworkAgentInfo.created` on the frameworks side.

  - BEFORE the CONNECTED broadcast and network callbacks have been sent
    out so that we don't create a race condition between clients that
    want to use the network and the server actually having the network
    ready

The race condition existed prior to this change and required any client
making use of network callbacks to sleep for a short amount of time after
receiving to actually be able to use the network.

Among other things, that race condition is now fixed.

Bug: 26694104
Change-Id: Ied92f5588a98c3e97f456bc98b676bf201ab5472
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index d85827e..5530143 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -1942,7 +1942,7 @@
                             networkCapabilities.hasCapability(NET_CAPABILITY_VALIDATED)) {
                         Slog.wtf(TAG, "BUG: " + nai + " has CS-managed capability.");
                     }
-                    if (nai.created && !nai.networkCapabilities.equalImmutableCapabilities(
+                    if (nai.everConnected && !nai.networkCapabilities.equalImmutableCapabilities(
                             networkCapabilities)) {
                         Slog.wtf(TAG, "BUG: " + nai + " changed immutable capabilities: "
                                 + nai.networkCapabilities + " -> " + networkCapabilities);
@@ -1953,13 +1953,14 @@
                 case NetworkAgent.EVENT_NETWORK_PROPERTIES_CHANGED: {
                     if (VDBG) {
                         log("Update of LinkProperties for " + nai.name() +
-                                "; created=" + nai.created);
+                                "; created=" + nai.created +
+                                "; everConnected=" + nai.everConnected);
                     }
                     LinkProperties oldLp = nai.linkProperties;
                     synchronized (nai) {
                         nai.linkProperties = (LinkProperties)msg.obj;
                     }
-                    if (nai.created) updateLinkProperties(nai, oldLp);
+                    if (nai.everConnected) updateLinkProperties(nai, oldLp);
                     break;
                 }
                 case NetworkAgent.EVENT_NETWORK_INFO_CHANGED: {
@@ -1991,8 +1992,8 @@
                     break;
                 }
                 case NetworkAgent.EVENT_SET_EXPLICITLY_SELECTED: {
-                    if (nai.created && !nai.networkMisc.explicitlySelected) {
-                        loge("ERROR: created network explicitly selected.");
+                    if (nai.everConnected && !nai.networkMisc.explicitlySelected) {
+                        loge("ERROR: already-connected network explicitly selected.");
                     }
                     nai.networkMisc.explicitlySelected = true;
                     nai.networkMisc.acceptUnvalidated = (boolean) msg.obj;
@@ -2277,7 +2278,7 @@
     // This is whether it is satisfying any NetworkRequests or were it to become validated,
     // would it have a chance of satisfying any NetworkRequests.
     private boolean unneeded(NetworkAgentInfo nai) {
-        if (!nai.created || nai.isVPN() || nai.lingering) return false;
+        if (!nai.everConnected || nai.isVPN() || nai.lingering) return false;
         for (NetworkRequestInfo nri : mNetworkRequests.values()) {
             // If this Network is already the highest scoring Network for a request, or if
             // there is hope for it to become one if it validated, then it is needed.
@@ -2761,9 +2762,9 @@
                     ") by " + uid);
         }
         synchronized (nai) {
-            // Validating an uncreated network could result in a call to rematchNetworkAndRequests()
-            // which isn't meant to work on uncreated networks.
-            if (!nai.created) return;
+            // Validating a network that has not yet connected could result in a call to
+            // rematchNetworkAndRequests() which is not meant to work on such networks.
+            if (!nai.everConnected) return;
 
             if (isNetworkWithLinkPropertiesBlocked(nai.linkProperties, uid)) return;
 
@@ -4505,7 +4506,7 @@
     //               validated) of becoming the highest scoring network.
     private void rematchNetworkAndRequests(NetworkAgentInfo newNetwork,
             ReapUnvalidatedNetworks reapUnvalidatedNetworks) {
-        if (!newNetwork.created) return;
+        if (!newNetwork.everConnected) return;
         boolean keep = newNetwork.isVPN();
         boolean isNewDefault = false;
         NetworkAgentInfo oldDefaultNetwork = null;
@@ -4804,7 +4805,9 @@
                     " to " + state);
         }
 
-        if (state == NetworkInfo.State.CONNECTED && !networkAgent.created) {
+        if (!networkAgent.created
+                && (state == NetworkInfo.State.CONNECTED
+                || (state == NetworkInfo.State.CONNECTING && networkAgent.isVPN()))) {
             try {
                 // This should never fail.  Specifying an already in use NetID will cause failure.
                 if (networkAgent.isVPN()) {
@@ -4824,6 +4827,11 @@
                 return;
             }
             networkAgent.created = true;
+        }
+
+        if (!networkAgent.everConnected && state == NetworkInfo.State.CONNECTED) {
+            networkAgent.everConnected = true;
+
             updateLinkProperties(networkAgent, null);
             notifyIfacesChangedForNetworkStats();