Support NetworkCallbacks based on signal strength.

Bug: 21405941
Change-Id: I2ed8a5aeb8dac464a4305671ed22abcacb485bc9
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 08754c2..49d35b8 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -150,6 +150,8 @@
 import java.util.Collection;
 import java.util.HashMap;
 import java.util.HashSet;
+import java.util.SortedSet;
+import java.util.TreeSet;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
@@ -1934,7 +1936,7 @@
                                 (NetworkCapabilities)msg.obj;
                         if (networkCapabilities.hasCapability(NET_CAPABILITY_CAPTIVE_PORTAL) ||
                                 networkCapabilities.hasCapability(NET_CAPABILITY_VALIDATED)) {
-                            Slog.wtf(TAG, "BUG: " + nai + " has stateful capability.");
+                            Slog.wtf(TAG, "BUG: " + nai + " has CS-managed capability.");
                         }
                         if (nai.created && !nai.networkCapabilities.equalImmutableCapabilities(
                                 networkCapabilities)) {
@@ -2250,6 +2252,13 @@
     private void handleRegisterNetworkRequest(NetworkRequestInfo nri) {
         mNetworkRequests.put(nri.request, nri);
         mNetworkRequestInfoLogs.log("REGISTER " + nri);
+        if (!nri.isRequest) {
+            for (NetworkAgentInfo network : mNetworkAgentInfos.values()) {
+                if (network.satisfiesImmutableCapabilitiesOf(nri.request)) {
+                    updateSignalStrengthThresholds(network);
+                }
+            }
+        }
         rematchAllNetworksAndRequests(null, 0);
         if (nri.isRequest && mNetworkForRequestId.get(nri.request.requestId) == null) {
             sendUpdatedScoreToFactories(nri.request, 0);
@@ -2363,6 +2372,9 @@
                 // if this listen request applies and remove it.
                 for (NetworkAgentInfo nai : mNetworkAgentInfos.values()) {
                     nai.networkRequests.remove(nri.request.requestId);
+                    if (nai.satisfiesImmutableCapabilitiesOf(nri.request)) {
+                        updateSignalStrengthThresholds(nai);
+                    }
                 }
             }
             callCallbackForRequest(nri, null, ConnectivityManager.CALLBACK_RELEASED);
@@ -3599,6 +3611,34 @@
         }
     }
 
+    private int[] getSignalStrengthThresholds(NetworkAgentInfo nai) {
+        final SortedSet<Integer> thresholds = new TreeSet();
+        synchronized (nai) {
+            for (NetworkRequestInfo nri : mNetworkRequests.values()) {
+                if (nri.request.networkCapabilities.hasSignalStrength() &&
+                        nai.satisfiesImmutableCapabilitiesOf(nri.request)) {
+                    thresholds.add(nri.request.networkCapabilities.getSignalStrength());
+                }
+            }
+        }
+        // We can't just do something like:
+        //     return thresholds.toArray(new int[thresholds.size()]);
+        // because autoboxing does not work for primitive arrays.
+        final int[] out = new int[thresholds.size()];
+        int pos = 0;
+        for (Integer threshold : thresholds) {
+            out[pos] = threshold;
+            pos++;
+        }
+        return out;
+    }
+
+    private void updateSignalStrengthThresholds(NetworkAgentInfo nai) {
+        nai.asyncChannel.sendMessage(
+                android.net.NetworkAgent.CMD_SET_SIGNAL_STRENGTH_THRESHOLDS,
+                0, 0, getSignalStrengthThresholds(nai));
+    }
+
     @Override
     public NetworkRequest requestNetwork(NetworkCapabilities networkCapabilities,
             Messenger messenger, int timeoutMs, IBinder binder, int legacyType) {
@@ -4523,6 +4563,15 @@
                 // TODO: support proxy per network.
             }
 
+            // Whether a particular NetworkRequest listen should cause signal strength thresholds to
+            // be communicated to a particular NetworkAgent depends only on the network's immutable,
+            // capabilities, so it only needs to be done once on initial connect, not every time the
+            // network's capabilities change. Note that we do this before rematching the network,
+            // so we could decide to tear it down immediately afterwards. That's fine though - on
+            // disconnection NetworkAgents should stop any signal strength monitoring they have been
+            // doing.
+            updateSignalStrengthThresholds(networkAgent);
+
             // Consider network even though it is not yet validated.
             rematchNetworkAndRequests(networkAgent, ReapUnvalidatedNetworks.REAP);