Merge "Add lockdownEnabled parameter to always-on VPN API" into nyc-dev
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index e65462b..43d9bf3 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -774,8 +774,13 @@
      * @hide
      */
     public Network getActiveNetworkForUid(int uid) {
+        return getActiveNetworkForUid(uid, false);
+    }
+
+    /** {@hide} */
+    public Network getActiveNetworkForUid(int uid, boolean ignoreBlocked) {
         try {
-            return mService.getActiveNetworkForUid(uid);
+            return mService.getActiveNetworkForUid(uid, ignoreBlocked);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -838,8 +843,13 @@
      * {@hide}
      */
     public NetworkInfo getActiveNetworkInfoForUid(int uid) {
+        return getActiveNetworkInfoForUid(uid, false);
+    }
+
+    /** {@hide} */
+    public NetworkInfo getActiveNetworkInfoForUid(int uid, boolean ignoreBlocked) {
         try {
-            return mService.getActiveNetworkInfoForUid(uid);
+            return mService.getActiveNetworkInfoForUid(uid, ignoreBlocked);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -882,8 +892,13 @@
      *        is not valid.
      */
     public NetworkInfo getNetworkInfo(Network network) {
+        return getNetworkInfoForUid(network, Process.myUid(), false);
+    }
+
+    /** {@hide} */
+    public NetworkInfo getNetworkInfoForUid(Network network, int uid, boolean ignoreBlocked) {
         try {
-            return mService.getNetworkInfoForNetwork(network);
+            return mService.getNetworkInfoForUid(network, uid, ignoreBlocked);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl
index e7a072c..0d518cc1 100644
--- a/core/java/android/net/IConnectivityManager.aidl
+++ b/core/java/android/net/IConnectivityManager.aidl
@@ -44,11 +44,11 @@
 interface IConnectivityManager
 {
     Network getActiveNetwork();
-    Network getActiveNetworkForUid(int uid);
+    Network getActiveNetworkForUid(int uid, boolean ignoreBlocked);
     NetworkInfo getActiveNetworkInfo();
-    NetworkInfo getActiveNetworkInfoForUid(int uid);
+    NetworkInfo getActiveNetworkInfoForUid(int uid, boolean ignoreBlocked);
     NetworkInfo getNetworkInfo(int networkType);
-    NetworkInfo getNetworkInfoForNetwork(in Network network);
+    NetworkInfo getNetworkInfoForUid(in Network network, int uid, boolean ignoreBlocked);
     NetworkInfo[] getAllNetworkInfo();
     Network getNetworkForType(int networkType);
     Network[] getAllNetworks();
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 100ada8..06255a2 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -29,8 +29,12 @@
 import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED;
 import static android.net.NetworkPolicyManager.RULE_ALLOW_ALL;
+import static android.net.NetworkPolicyManager.RULE_ALLOW_METERED;
 import static android.net.NetworkPolicyManager.RULE_REJECT_ALL;
 import static android.net.NetworkPolicyManager.RULE_REJECT_METERED;
+import static android.net.NetworkPolicyManager.RULE_TEMPORARY_ALLOW_METERED;
+import static android.net.NetworkPolicyManager.RULE_UNKNOWN;
+
 import android.annotation.Nullable;
 import android.app.BroadcastOptions;
 import android.app.Notification;
@@ -96,8 +100,10 @@
 import android.security.KeyStore;
 import android.telephony.TelephonyManager;
 import android.text.TextUtils;
+import android.util.ArraySet;
 import android.util.LocalLog;
 import android.util.LocalLog.ReadOnlyLocalLog;
+import android.util.Log;
 import android.util.Pair;
 import android.util.Slog;
 import android.util.SparseArray;
@@ -121,9 +127,9 @@
 import com.android.server.am.BatteryStatsService;
 import com.android.server.connectivity.DataConnectionStats;
 import com.android.server.connectivity.KeepaliveTracker;
-import com.android.server.connectivity.NetworkDiagnostics;
 import com.android.server.connectivity.Nat464Xlat;
 import com.android.server.connectivity.NetworkAgentInfo;
+import com.android.server.connectivity.NetworkDiagnostics;
 import com.android.server.connectivity.NetworkMonitor;
 import com.android.server.connectivity.PacManager;
 import com.android.server.connectivity.PermissionMonitor;
@@ -131,8 +137,8 @@
 import com.android.server.connectivity.Vpn;
 import com.android.server.net.BaseNetworkObserver;
 import com.android.server.net.LockdownVpnTracker;
+
 import com.google.android.collect.Lists;
-import com.google.android.collect.Sets;
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
@@ -152,11 +158,11 @@
 import java.util.Collection;
 import java.util.HashMap;
 import java.util.HashSet;
-import java.util.SortedSet;
-import java.util.TreeSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
+import java.util.SortedSet;
+import java.util.TreeSet;
 
 /**
  * @hide
@@ -202,9 +208,14 @@
     /** Lock around {@link #mUidRules} and {@link #mMeteredIfaces}. */
     private Object mRulesLock = new Object();
     /** Currently active network rules by UID. */
+    @GuardedBy("mRulesLock")
     private SparseIntArray mUidRules = new SparseIntArray();
     /** Set of ifaces that are costly. */
-    private HashSet<String> mMeteredIfaces = Sets.newHashSet();
+    @GuardedBy("mRulesLock")
+    private ArraySet<String> mMeteredIfaces = new ArraySet<>();
+    /** Flag indicating if background data is restricted. */
+    @GuardedBy("mRulesLock")
+    private boolean mRestrictBackground;
 
     final private Context mContext;
     private int mNetworkPreference;
@@ -651,7 +662,8 @@
         mTelephonyManager = (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
 
         try {
-            mPolicyManager.registerListener(mPolicyListener);
+            mPolicyManager.setConnectivityListener(mPolicyListener);
+            mRestrictBackground = mPolicyManager.getRestrictBackground();
         } catch (RemoteException e) {
             // ouch, no rules updates means some processes may never get network
             loge("unable to register INetworkPolicyListener" + e.toString());
@@ -729,7 +741,7 @@
 
         //set up the listener for user state for creating user VPNs
         IntentFilter intentFilter = new IntentFilter();
-        intentFilter.addAction(Intent.ACTION_USER_STARTING);
+        intentFilter.addAction(Intent.ACTION_USER_STARTED);
         intentFilter.addAction(Intent.ACTION_USER_STOPPED);
         intentFilter.addAction(Intent.ACTION_USER_ADDED);
         intentFilter.addAction(Intent.ACTION_USER_REMOVED);
@@ -819,7 +831,7 @@
         throw new IllegalStateException("No free netIds");
     }
 
-    private NetworkState getFilteredNetworkState(int networkType, int uid) {
+    private NetworkState getFilteredNetworkState(int networkType, int uid, boolean ignoreBlocked) {
         if (mLegacyTypeTracker.isTypeSupported(networkType)) {
             final NetworkAgentInfo nai = mLegacyTypeTracker.getNetworkForType(networkType);
             final NetworkState state;
@@ -834,7 +846,7 @@
                 state = new NetworkState(info, new LinkProperties(), new NetworkCapabilities(),
                         null, null, null);
             }
-            filterNetworkStateForUid(state, uid);
+            filterNetworkStateForUid(state, uid, ignoreBlocked);
             return state;
         } else {
             return NetworkState.EMPTY;
@@ -890,22 +902,36 @@
     /**
      * Check if UID should be blocked from using the network with the given LinkProperties.
      */
-    private boolean isNetworkWithLinkPropertiesBlocked(LinkProperties lp, int uid) {
-        final boolean networkCostly;
+    private boolean isNetworkWithLinkPropertiesBlocked(LinkProperties lp, int uid,
+            boolean ignoreBlocked) {
+        // Networks aren't blocked when ignoring blocked status
+        if (ignoreBlocked) return false;
+        // Networks are never blocked for system services
+        if (uid < Process.FIRST_APPLICATION_UID) return false;
+
+        final boolean networkMetered;
         final int uidRules;
 
         final String iface = (lp == null ? "" : lp.getInterfaceName());
         synchronized (mRulesLock) {
-            networkCostly = mMeteredIfaces.contains(iface);
-            uidRules = mUidRules.get(uid, RULE_ALLOW_ALL);
+            networkMetered = mMeteredIfaces.contains(iface);
+            uidRules = mUidRules.get(uid, RULE_UNKNOWN);
         }
 
-        if (uidRules == RULE_REJECT_ALL) {
-            return true;
-        } else if ((uidRules == RULE_REJECT_METERED) && networkCostly) {
-            return true;
-        } else {
-            return false;
+        switch (uidRules) {
+            case RULE_ALLOW_ALL:
+            case RULE_ALLOW_METERED:
+            case RULE_TEMPORARY_ALLOW_METERED:
+                return false;
+            case RULE_REJECT_METERED:
+                return networkMetered;
+            case RULE_REJECT_ALL:
+                return true;
+            case RULE_UNKNOWN:
+            default:
+                // When background data is restricted device-wide, the default
+                // behavior for apps should be like RULE_REJECT_METERED
+                return mRestrictBackground ? networkMetered : false;
         }
     }
 
@@ -930,10 +956,10 @@
      * on {@link #isNetworkWithLinkPropertiesBlocked}, or
      * {@link NetworkInfo#isMetered()} based on network policies.
      */
-    private void filterNetworkStateForUid(NetworkState state, int uid) {
+    private void filterNetworkStateForUid(NetworkState state, int uid, boolean ignoreBlocked) {
         if (state == null || state.networkInfo == null || state.linkProperties == null) return;
 
-        if (isNetworkWithLinkPropertiesBlocked(state.linkProperties, uid)) {
+        if (isNetworkWithLinkPropertiesBlocked(state.linkProperties, uid, ignoreBlocked)) {
             state.networkInfo.setDetailedState(DetailedState.BLOCKED, null, null);
         }
         if (mLockdownTracker != null) {
@@ -962,7 +988,7 @@
         enforceAccessPermission();
         final int uid = Binder.getCallingUid();
         final NetworkState state = getUnfilteredActiveNetworkState(uid);
-        filterNetworkStateForUid(state, uid);
+        filterNetworkStateForUid(state, uid, false);
         maybeLogBlockedNetworkInfo(state.networkInfo, uid);
         return state.networkInfo;
     }
@@ -970,16 +996,16 @@
     @Override
     public Network getActiveNetwork() {
         enforceAccessPermission();
-        return getActiveNetworkForUidInternal(Binder.getCallingUid());
+        return getActiveNetworkForUidInternal(Binder.getCallingUid(), false);
     }
 
     @Override
-    public Network getActiveNetworkForUid(int uid) {
+    public Network getActiveNetworkForUid(int uid, boolean ignoreBlocked) {
         enforceConnectivityInternalPermission();
-        return getActiveNetworkForUidInternal(uid);
+        return getActiveNetworkForUidInternal(uid, ignoreBlocked);
     }
 
-    private Network getActiveNetworkForUidInternal(final int uid) {
+    private Network getActiveNetworkForUidInternal(final int uid, boolean ignoreBlocked) {
         final int user = UserHandle.getUserId(uid);
         int vpnNetId = NETID_UNSET;
         synchronized (mVpns) {
@@ -994,7 +1020,10 @@
             if (nai != null) return nai.network;
         }
         nai = getDefaultNetwork();
-        if (nai != null && isNetworkWithLinkPropertiesBlocked(nai.linkProperties, uid)) nai = null;
+        if (nai != null
+                && isNetworkWithLinkPropertiesBlocked(nai.linkProperties, uid, ignoreBlocked)) {
+            nai = null;
+        }
         return nai != null ? nai.network : null;
     }
 
@@ -1006,10 +1035,10 @@
     }
 
     @Override
-    public NetworkInfo getActiveNetworkInfoForUid(int uid) {
+    public NetworkInfo getActiveNetworkInfoForUid(int uid, boolean ignoreBlocked) {
         enforceConnectivityInternalPermission();
         final NetworkState state = getUnfilteredActiveNetworkState(uid);
-        filterNetworkStateForUid(state, uid);
+        filterNetworkStateForUid(state, uid, ignoreBlocked);
         return state.networkInfo;
     }
 
@@ -1023,22 +1052,21 @@
             // getUnfilteredActiveNetworkState.
             final NetworkState state = getUnfilteredActiveNetworkState(uid);
             if (state.networkInfo != null && state.networkInfo.getType() == networkType) {
-                filterNetworkStateForUid(state, uid);
+                filterNetworkStateForUid(state, uid, false);
                 return state.networkInfo;
             }
         }
-        final NetworkState state = getFilteredNetworkState(networkType, uid);
+        final NetworkState state = getFilteredNetworkState(networkType, uid, false);
         return state.networkInfo;
     }
 
     @Override
-    public NetworkInfo getNetworkInfoForNetwork(Network network) {
+    public NetworkInfo getNetworkInfoForUid(Network network, int uid, boolean ignoreBlocked) {
         enforceAccessPermission();
-        final int uid = Binder.getCallingUid();
         final NetworkAgentInfo nai = getNetworkAgentInfoForNetwork(network);
         if (nai != null) {
             final NetworkState state = nai.getNetworkState();
-            filterNetworkStateForUid(state, uid);
+            filterNetworkStateForUid(state, uid, ignoreBlocked);
             return state.networkInfo;
         } else {
             return null;
@@ -1063,8 +1091,8 @@
     public Network getNetworkForType(int networkType) {
         enforceAccessPermission();
         final int uid = Binder.getCallingUid();
-        NetworkState state = getFilteredNetworkState(networkType, uid);
-        if (!isNetworkWithLinkPropertiesBlocked(state.linkProperties, uid)) {
+        NetworkState state = getFilteredNetworkState(networkType, uid, false);
+        if (!isNetworkWithLinkPropertiesBlocked(state.linkProperties, uid, false)) {
             return state.network;
         }
         return null;
@@ -1381,6 +1409,11 @@
             if (LOGD_RULES) {
                 log("onRestrictBackgroundChanged(restrictBackground=" + restrictBackground + ")");
             }
+
+            synchronized (mRulesLock) {
+                mRestrictBackground = restrictBackground;
+            }
+
             if (restrictBackground) {
                 log("onRestrictBackgroundChanged(true): disabling tethering");
                 mTethering.untetherAll();
@@ -1824,6 +1857,10 @@
         pw.decreaseIndent();
         pw.println();
 
+        pw.print("Restrict background: ");
+        pw.println(mRestrictBackground);
+        pw.println();
+
         pw.println("Network Requests:");
         pw.increaseIndent();
         for (NetworkRequestInfo nri : mNetworkRequests.values()) {
@@ -1942,7 +1979,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 +1990,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 +2029,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 +2315,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,11 +2799,11 @@
                     ") 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;
+            if (isNetworkWithLinkPropertiesBlocked(nai.linkProperties, uid, false)) return;
 
             nai.networkMonitor.sendMessage(NetworkMonitor.CMD_FORCE_REEVALUATION, uid);
         }
@@ -3662,7 +3700,7 @@
             final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
             if (userId == UserHandle.USER_NULL) return;
 
-            if (Intent.ACTION_USER_STARTING.equals(action)) {
+            if (Intent.ACTION_USER_STARTED.equals(action)) {
                 onUserStart(userId);
             } else if (Intent.ACTION_USER_STOPPED.equals(action)) {
                 onUserStop(userId);
@@ -4280,10 +4318,10 @@
         Collection<InetAddress> dnses = newLp.getDnsServers();
         if (DBG) log("Setting DNS servers for network " + netId + " to " + dnses);
         try {
-            mNetd.setDnsServersForNetwork(
+            mNetd.setDnsConfigurationForNetwork(
                     netId, NetworkUtils.makeStrings(dnses), newLp.getDomains());
         } catch (Exception e) {
-            loge("Exception in setDnsServersForNetwork: " + e);
+            loge("Exception in setDnsConfigurationForNetwork: " + e);
         }
         final NetworkAgentInfo defaultNai = getDefaultNetwork();
         if (defaultNai != null && defaultNai.network.netId == netId) {
@@ -4505,7 +4543,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 +4842,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 +4864,11 @@
                 return;
             }
             networkAgent.created = true;
+        }
+
+        if (!networkAgent.everConnected && state == NetworkInfo.State.CONNECTED) {
+            networkAgent.everConnected = true;
+
             updateLinkProperties(networkAgent, null);
             notifyIfacesChangedForNetworkStats();
 
diff --git a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
index 3201060..d487bd0 100644
--- a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
+++ b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
@@ -48,15 +48,21 @@
 // 1. registered, uncreated, disconnected, unvalidated
 //    This state is entered when a NetworkFactory registers a NetworkAgent in any state except
 //    the CONNECTED state.
-// 2. registered, uncreated, connected, unvalidated
-//    This state is entered when a registered NetworkAgent transitions to the CONNECTED state
-//    ConnectivityService will tell netd to create the network and immediately transition to
-//    state #3.
-// 3. registered, created, connected, unvalidated
+// 2. registered, uncreated, connecting, unvalidated
+//    This state is entered when a registered NetworkAgent for a VPN network transitions to the
+//    CONNECTING state (TODO: go through this state for every network, not just VPNs).
+//    ConnectivityService will tell netd to create the network early in order to add extra UID
+//    routing rules referencing the netID. These rules need to be in place before the network is
+//    connected to avoid racing against client apps trying to connect to a half-setup network.
+// 3. registered, uncreated, connected, unvalidated
+//    This state is entered when a registered NetworkAgent transitions to the CONNECTED state.
+//    ConnectivityService will tell netd to create the network if it was not already created, and
+//    immediately transition to state #4.
+// 4. registered, created, connected, unvalidated
 //    If this network can satisfy the default NetworkRequest, then NetworkMonitor will
 //    probe for Internet connectivity.
 //    If this network cannot satisfy the default NetworkRequest, it will immediately be
-//    transitioned to state #4.
+//    transitioned to state #5.
 //    A network may remain in this state if NetworkMonitor fails to find Internet connectivity,
 //    for example:
 //    a. a captive portal is present, or
@@ -64,14 +70,14 @@
 //    c. a wireless connection stops transfering packets temporarily (e.g. device is in elevator
 //       or tunnel) but does not disconnect from the AP/cell tower, or
 //    d. a stand-alone device offering a WiFi AP without an uplink for configuration purposes.
-// 4. registered, created, connected, validated
+// 5. registered, created, connected, validated
 //
 // The device's default network connection:
 // ----------------------------------------
-// Networks in states #3 and #4 may be used as a device's default network connection if they
+// Networks in states #4 and #5 may be used as a device's default network connection if they
 // satisfy the default NetworkRequest.
-// A network, that satisfies the default NetworkRequest, in state #4 should always be chosen
-// in favor of a network, that satisfies the default NetworkRequest, in state #3.
+// A network, that satisfies the default NetworkRequest, in state #5 should always be chosen
+// in favor of a network, that satisfies the default NetworkRequest, in state #4.
 // When deciding between two networks, that both satisfy the default NetworkRequest, to select
 // for the default network connection, the one with the higher score should be chosen.
 //
@@ -83,14 +89,14 @@
 // c. airplane mode is turned on, or
 // d. a wireless connection disconnects from AP/cell tower entirely (e.g. device is out of range
 //    of AP for an extended period of time, or switches to another AP without roaming)
-// then that network can transition from any state (#1-#4) to unregistered.  This happens by
+// then that network can transition from any state (#1-#5) to unregistered.  This happens by
 // the transport disconnecting their NetworkAgent's AsyncChannel with ConnectivityManager.
 // ConnectivityService also tells netd to destroy the network.
 //
 // When ConnectivityService disconnects a network:
 // -----------------------------------------------
 // If a network has no chance of satisfying any requests (even if it were to become validated
-// and enter state #4), ConnectivityService will disconnect the NetworkAgent's AsyncChannel.
+// and enter state #5), ConnectivityService will disconnect the NetworkAgent's AsyncChannel.
 // If the network ever for any period of time had satisfied a NetworkRequest (i.e. had been
 // the highest scoring that satisfied the NetworkRequest's constraints), but is no longer the
 // highest scoring network for any NetworkRequest, then there will be a 30s pause before
@@ -110,10 +116,14 @@
     public NetworkCapabilities networkCapabilities;
     public final NetworkMonitor networkMonitor;
     public final NetworkMisc networkMisc;
-    // Indicates if netd has been told to create this Network.  Once created the appropriate routing
-    // rules are setup and routes are added so packets can begin flowing over the Network.
+    // Indicates if netd has been told to create this Network. From this point on the appropriate
+    // routing rules are setup and routes are added so packets can begin flowing over the Network.
     // This is a sticky bit; once set it is never cleared.
     public boolean created;
+    // Set to true after the first time this network is marked as CONNECTED. Once set, the network
+    // shows up in API calls, is able to satisfy NetworkRequests and can become the default network.
+    // This is a sticky bit; once set it is never cleared.
+    public boolean everConnected;
     // Set to true if this Network successfully passed validation or if it did not satisfy the
     // default NetworkRequest in which case validation will not be attempted.
     // This is a sticky bit; once set it is never cleared even if future validation attempts fail.