[NS06] Implement the don't-reap mechanism

This exposes a mechanism for network providers to tell
the network stack that a given network must be kept up
for some specific reason. This is meant to be easier
for them than to have to file a request, in particular
because there is no guaranteed way to make sure the
request will be best matched by any given network.

Test: new test for this
Bug: 167544279
Merged-In: I3c2563d4ae4e3715d0c6270344ba8f7ef067872f
Merged-In: I238a3ee5ee9262477a23b897e4141769dd1505d1
Change-Id: I238a3ee5ee9262477a23b897e4141769dd1505d1
  (cherry-picked from ag/13929760)
diff --git a/service/src/com/android/server/ConnectivityService.java b/service/src/com/android/server/ConnectivityService.java
index f7d8aa0..9540e06 100644
--- a/service/src/com/android/server/ConnectivityService.java
+++ b/service/src/com/android/server/ConnectivityService.java
@@ -3974,6 +3974,12 @@
     //   then it should be lingered.
     private boolean unneeded(NetworkAgentInfo nai, UnneededFor reason) {
         ensureRunningOnConnectivityServiceThread();
+
+        if (!nai.everConnected || nai.isVPN() || nai.isInactive()
+                || nai.getScore().getKeepConnectedReason() != NetworkScore.KEEP_CONNECTED_NONE) {
+            return false;
+        }
+
         final int numRequests;
         switch (reason) {
             case TEARDOWN:
@@ -3987,9 +3993,8 @@
                 return true;
         }
 
-        if (!nai.everConnected || nai.isVPN() || nai.isInactive() || numRequests > 0) {
-            return false;
-        }
+        if (numRequests > 0) return false;
+
         for (NetworkRequestInfo nri : mNetworkRequests.values()) {
             if (reason == UnneededFor.LINGER
                     && !nri.isMultilayerRequest()
diff --git a/service/src/com/android/server/connectivity/FullScore.java b/service/src/com/android/server/connectivity/FullScore.java
index 9326d69..375d005 100644
--- a/service/src/com/android/server/connectivity/FullScore.java
+++ b/service/src/com/android/server/connectivity/FullScore.java
@@ -19,12 +19,14 @@
 import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
 import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED;
 import static android.net.NetworkCapabilities.TRANSPORT_VPN;
+import static android.net.NetworkScore.KEEP_CONNECTED_NONE;
 
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.net.NetworkAgentConfig;
 import android.net.NetworkCapabilities;
 import android.net.NetworkScore;
+import android.net.NetworkScore.KeepConnectedReason;
 
 import com.android.internal.annotations.VisibleForTesting;
 
@@ -95,9 +97,13 @@
     // Bitmask of all the policies applied to this score.
     private final long mPolicies;
 
-    FullScore(final int legacyInt, final long policies) {
+    private final int mKeepConnectedReason;
+
+    FullScore(final int legacyInt, final long policies,
+            @KeepConnectedReason final int keepConnectedReason) {
         mLegacyInt = legacyInt;
         mPolicies = policies;
+        mKeepConnectedReason = keepConnectedReason;
     }
 
     /**
@@ -110,14 +116,15 @@
      */
     public static FullScore fromNetworkScore(@NonNull final NetworkScore score,
             @NonNull final NetworkCapabilities caps, @NonNull final NetworkAgentConfig config) {
-        return withPolicies(score.getLegacyInt(), caps.hasCapability(NET_CAPABILITY_VALIDATED),
+        return withPolicies(score.getLegacyInt(), score.getKeepConnectedReason(),
+                caps.hasCapability(NET_CAPABILITY_VALIDATED),
                 caps.hasTransport(TRANSPORT_VPN),
                 config.explicitlySelected,
                 config.acceptUnvalidated);
     }
 
     /**
-     * Given a score supplied by the NetworkAgent, produce a prospective score for an offer.
+     * Given a score supplied by a NetworkProvider, produce a prospective score for an offer.
      *
      * NetworkOffers have score filters that are compared to the scores of actual networks
      * to see if they could possibly beat the current satisfier. Some things the agent can't
@@ -139,8 +146,8 @@
         final boolean everUserSelected = false;
         // Don't assume the user will accept unvalidated connectivity.
         final boolean acceptUnvalidated = false;
-        return withPolicies(score.getLegacyInt(), mayValidate, vpn, everUserSelected,
-                acceptUnvalidated);
+        return withPolicies(score.getLegacyInt(), KEEP_CONNECTED_NONE,
+                mayValidate, vpn, everUserSelected, acceptUnvalidated);
     }
 
     /**
@@ -152,13 +159,15 @@
      */
     public FullScore mixInScore(@NonNull final NetworkCapabilities caps,
             @NonNull final NetworkAgentConfig config) {
-        return withPolicies(mLegacyInt, caps.hasCapability(NET_CAPABILITY_VALIDATED),
+        return withPolicies(mLegacyInt, mKeepConnectedReason,
+                caps.hasCapability(NET_CAPABILITY_VALIDATED),
                 caps.hasTransport(TRANSPORT_VPN),
                 config.explicitlySelected,
                 config.acceptUnvalidated);
     }
 
     private static FullScore withPolicies(@NonNull final int legacyInt,
+            @KeepConnectedReason final int keepConnectedReason,
             final boolean isValidated,
             final boolean isVpn,
             final boolean everUserSelected,
@@ -167,7 +176,8 @@
                 (isValidated         ? 1L << POLICY_IS_VALIDATED : 0)
                 | (isVpn             ? 1L << POLICY_IS_VPN : 0)
                 | (everUserSelected  ? 1L << POLICY_EVER_USER_SELECTED : 0)
-                | (acceptUnvalidated ? 1L << POLICY_ACCEPT_UNVALIDATED : 0));
+                | (acceptUnvalidated ? 1L << POLICY_ACCEPT_UNVALIDATED : 0),
+                keepConnectedReason);
     }
 
     /**
@@ -219,13 +229,21 @@
         return 0 != (mPolicies & (1L << policy));
     }
 
+    /**
+     * Returns the keep-connected reason, or KEEP_CONNECTED_NONE.
+     */
+    public int getKeepConnectedReason() {
+        return mKeepConnectedReason;
+    }
+
     // Example output :
     // Score(50 ; Policies : EVER_USER_SELECTED&IS_VALIDATED)
     @Override
     public String toString() {
         final StringJoiner sj = new StringJoiner(
                 "&", // delimiter
-                "Score(" + mLegacyInt + " ; Policies : ", // prefix
+                "Score(" + mLegacyInt + " ; KeepConnected : " + mKeepConnectedReason
+                        + " ; Policies : ", // prefix
                 ")"); // suffix
         for (int i = NetworkScore.MIN_AGENT_MANAGED_POLICY;
                 i <= NetworkScore.MAX_AGENT_MANAGED_POLICY; ++i) {