From 98a4c4d25b551c8f84786c8cbdfec50e32a64658 Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Mon, 6 Jul 2015 23:50:27 +0900 Subject: [PATCH] Support NetworkCallbacks based on signal strength. Bug: 21405941 Change-Id: I2ed8a5aeb8dac464a4305671ed22abcacb485bc9 --- core/java/android/net/NetworkAgent.java | 20 +++++ .../java/android/net/NetworkCapabilities.java | 80 ++++++++++++++++++- core/java/android/net/NetworkRequest.java | 18 +++++ .../android/server/ConnectivityService.java | 50 +++++++++++- .../server/connectivity/NetworkAgentInfo.java | 6 ++ 5 files changed, 170 insertions(+), 4 deletions(-) diff --git a/core/java/android/net/NetworkAgent.java b/core/java/android/net/NetworkAgent.java index 85a584a8ca..0af6e7c698 100644 --- a/core/java/android/net/NetworkAgent.java +++ b/core/java/android/net/NetworkAgent.java @@ -184,6 +184,14 @@ public abstract class NetworkAgent extends Handler { */ public static final int EVENT_PACKET_KEEPALIVE = BASE + 13; + /** + * Sent by ConnectivityService to inform this network transport of signal strength thresholds + * that when crossed should trigger a system wakeup and a NetworkCapabilities update. + * + * obj = int[] describing signal strength thresholds. + */ + public static final int CMD_SET_SIGNAL_STRENGTH_THRESHOLDS = BASE + 14; + public NetworkAgent(Looper looper, Context context, String logTag, NetworkInfo ni, NetworkCapabilities nc, LinkProperties lp, int score) { this(looper, context, logTag, ni, nc, lp, score, null); @@ -286,6 +294,11 @@ public abstract class NetworkAgent extends Handler { stopPacketKeepalive(msg); break; } + + case CMD_SET_SIGNAL_STRENGTH_THRESHOLDS: { + setSignalStrengthThresholds((int[]) msg.obj); + break; + } } } @@ -445,6 +458,13 @@ public abstract class NetworkAgent extends Handler { queueOrSendMessage(EVENT_PACKET_KEEPALIVE, slot, reason); } + /** + * Called by ConnectivityService to inform this network transport of signal strength thresholds + * that when crossed should trigger a system wakeup and a NetworkCapabilities update. + */ + protected void setSignalStrengthThresholds(int[] thresholds) { + } + protected void log(String s) { Log.d(LOG_TAG, "NetworkAgent: " + s); } diff --git a/core/java/android/net/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java index d69d00fdbe..847b77726e 100644 --- a/core/java/android/net/NetworkCapabilities.java +++ b/core/java/android/net/NetworkCapabilities.java @@ -48,6 +48,7 @@ public final class NetworkCapabilities implements Parcelable { mLinkUpBandwidthKbps = nc.mLinkUpBandwidthKbps; mLinkDownBandwidthKbps = nc.mLinkDownBandwidthKbps; mNetworkSpecifier = nc.mNetworkSpecifier; + mSignalStrength = nc.mSignalStrength; } } @@ -60,6 +61,7 @@ public final class NetworkCapabilities implements Parcelable { mNetworkCapabilities = mTransportTypes = 0; mLinkUpBandwidthKbps = mLinkDownBandwidthKbps = 0; mNetworkSpecifier = null; + mSignalStrength = SIGNAL_STRENGTH_UNSPECIFIED; } /** @@ -300,6 +302,7 @@ public final class NetworkCapabilities implements Parcelable { return "unknown non-requestable capabilities " + Long.toHexString(mNetworkCapabilities); } if (mLinkUpBandwidthKbps != 0 || mLinkDownBandwidthKbps != 0) return "link bandwidth"; + if (hasSignalStrength()) return "signalStrength"; return null; } @@ -568,6 +571,68 @@ public final class NetworkCapabilities implements Parcelable { } } + /** + * Magic value that indicates no signal strength provided. A request specifying this value is + * always satisfied. + * + * @hide + */ + public static final int SIGNAL_STRENGTH_UNSPECIFIED = Integer.MIN_VALUE; + + /** + * Signal strength. This is a signed integer, and higher values indicate better signal. + * The exact units are bearer-dependent. For example, Wi-Fi uses RSSI. + */ + private int mSignalStrength; + + /** + * Sets the signal strength. This is a signed integer, with higher values indicating a stronger + * signal. The exact units are bearer-dependent. For example, Wi-Fi uses the same RSSI units + * reported by WifiManager. + *

+ * Note that when used to register a network callback, this specifies the minimum acceptable + * signal strength. When received as the state of an existing network it specifies the current + * value. A value of code SIGNAL_STRENGTH_UNSPECIFIED} means no value when received and has no + * effect when requesting a callback. + * + * @param signalStrength the bearer-specific signal strength. + * @hide + */ + public void setSignalStrength(int signalStrength) { + mSignalStrength = signalStrength; + } + + /** + * Returns {@code true} if this object specifies a signal strength. + * + * @hide + */ + public boolean hasSignalStrength() { + return mSignalStrength > SIGNAL_STRENGTH_UNSPECIFIED; + } + + /** + * Retrieves the signal strength. + * + * @return The bearer-specific signal strength. + * @hide + */ + public int getSignalStrength() { + return mSignalStrength; + } + + private void combineSignalStrength(NetworkCapabilities nc) { + this.mSignalStrength = Math.max(this.mSignalStrength, nc.mSignalStrength); + } + + private boolean satisfiedBySignalStrength(NetworkCapabilities nc) { + return this.mSignalStrength <= nc.mSignalStrength; + } + + private boolean equalsSignalStrength(NetworkCapabilities nc) { + return this.mSignalStrength == nc.mSignalStrength; + } + /** * Combine a set of Capabilities to this one. Useful for coming up with the complete set * @hide @@ -577,6 +642,7 @@ public final class NetworkCapabilities implements Parcelable { combineTransportTypes(nc); combineLinkBandwidths(nc); combineSpecifiers(nc); + combineSignalStrength(nc); } /** @@ -593,7 +659,8 @@ public final class NetworkCapabilities implements Parcelable { satisfiedByNetCapabilities(nc, onlyImmutable) && satisfiedByTransportTypes(nc) && (onlyImmutable || satisfiedByLinkBandwidths(nc)) && - satisfiedBySpecifier(nc)); + satisfiedBySpecifier(nc) && + (onlyImmutable || satisfiedBySignalStrength(nc))); } /** @@ -638,6 +705,7 @@ public final class NetworkCapabilities implements Parcelable { return (equalsNetCapabilities(that) && equalsTransportTypes(that) && equalsLinkBandwidths(that) && + equalsSignalStrength(that) && equalsSpecifier(that)); } @@ -649,7 +717,8 @@ public final class NetworkCapabilities implements Parcelable { ((int)(mTransportTypes >> 32) * 7) + (mLinkUpBandwidthKbps * 11) + (mLinkDownBandwidthKbps * 13) + - (TextUtils.isEmpty(mNetworkSpecifier) ? 0 : mNetworkSpecifier.hashCode() * 17)); + (TextUtils.isEmpty(mNetworkSpecifier) ? 0 : mNetworkSpecifier.hashCode() * 17) + + (mSignalStrength * 19)); } @Override @@ -663,7 +732,9 @@ public final class NetworkCapabilities implements Parcelable { dest.writeInt(mLinkUpBandwidthKbps); dest.writeInt(mLinkDownBandwidthKbps); dest.writeString(mNetworkSpecifier); + dest.writeInt(mSignalStrength); } + public static final Creator CREATOR = new Creator() { @Override @@ -675,6 +746,7 @@ public final class NetworkCapabilities implements Parcelable { netCap.mLinkUpBandwidthKbps = in.readInt(); netCap.mLinkDownBandwidthKbps = in.readInt(); netCap.mNetworkSpecifier = in.readString(); + netCap.mSignalStrength = in.readInt(); return netCap; } @Override @@ -731,6 +803,8 @@ public final class NetworkCapabilities implements Parcelable { String specifier = (mNetworkSpecifier == null ? "" : " Specifier: <" + mNetworkSpecifier + ">"); - return "[" + transports + capabilities + upBand + dnBand + specifier + "]"; + String signalStrength = (hasSignalStrength() ? " SignalStrength: " + mSignalStrength : ""); + + return "[" + transports + capabilities + upBand + dnBand + specifier + signalStrength + "]"; } } diff --git a/core/java/android/net/NetworkRequest.java b/core/java/android/net/NetworkRequest.java index e61594ce8b..4f570dcc0d 100644 --- a/core/java/android/net/NetworkRequest.java +++ b/core/java/android/net/NetworkRequest.java @@ -185,6 +185,24 @@ public class NetworkRequest implements Parcelable { mNetworkCapabilities.setNetworkSpecifier(networkSpecifier); return this; } + + /** + * Sets the signal strength. This is a signed integer, with higher values indicating a + * stronger signal. The exact units are bearer-dependent. For example, Wi-Fi uses the same + * RSSI units reported by WifiManager. + *

+ * Note that when used to register a network callback, this specifies the minimum acceptable + * signal strength. When received as the state of an existing network it specifies the + * current value. A value of {@code SIGNAL_STRENGTH_UNSPECIFIED} means no value when + * received and has no effect when requesting a callback. + * + * @param signalStrength the bearer-specific signal strength. + * @hide + */ + public Builder setSignalStrength(int signalStrength) { + mNetworkCapabilities.setSignalStrength(signalStrength); + return this; + } } // implement the Parcelable interface diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java index e71bd19f3f..c35e4cb46a 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.Arrays; 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; @@ -1914,7 +1916,7 @@ public class ConnectivityService extends IConnectivityManager.Stub (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)) { @@ -2274,6 +2276,9 @@ public class ConnectivityService extends IConnectivityManager.Stub bestNetwork = network; } } + if (!nri.isRequest && network.satisfiesImmutableCapabilitiesOf(nri.request)) { + updateSignalStrengthThresholds(network); + } } if (bestNetwork != null) { if (DBG) log("using " + bestNetwork.name()); @@ -2419,6 +2424,9 @@ public class ConnectivityService extends IConnectivityManager.Stub // 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); @@ -3664,6 +3672,34 @@ public class ConnectivityService extends IConnectivityManager.Stub } } + private int[] getSignalStrengthThresholds(NetworkAgentInfo nai) { + final SortedSet 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) { @@ -4132,6 +4168,9 @@ public class ConnectivityService extends IConnectivityManager.Stub networkAgent.networkCapabilities = networkCapabilities; } rematchAllNetworksAndRequests(networkAgent, networkAgent.getCurrentScore(), nascent); + // TODO: reduce the number of callbacks where possible. For example, only send signal + // strength changes if the NetworkRequest used to register the callback specified a + // signalStrength. notifyNetworkCallbacks(networkAgent, ConnectivityManager.CALLBACK_CAP_CHANGED); } } @@ -4594,6 +4633,15 @@ public class ConnectivityService extends IConnectivityManager.Stub // 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, NascentState.NOT_JUST_VALIDATED, ReapUnvalidatedNetworks.REAP); diff --git a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java index 51c6628d1f..b4fea378eb 100644 --- a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java +++ b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java @@ -128,6 +128,12 @@ public class NetworkAgentInfo { request.networkCapabilities.satisfiedByNetworkCapabilities(networkCapabilities); } + public boolean satisfiesImmutableCapabilitiesOf(NetworkRequest request) { + return created && + request.networkCapabilities.satisfiedByImmutableNetworkCapabilities( + networkCapabilities); + } + public boolean isVPN() { return networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_VPN); }