Merge "Remove needless locking of mRulesLock that caused deadlocks." into lmp-dev
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 7c69a7d..e3cbef5 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -1402,6 +1402,20 @@
return (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
}
+ /** {@hide */
+ public static final void enforceTetherChangePermission(Context context) {
+ if (context.getResources().getStringArray(
+ com.android.internal.R.array.config_mobile_hotspot_provision_app).length == 2) {
+ // Have a provisioning app - must only let system apps (which check this app)
+ // turn on tethering
+ context.enforceCallingOrSelfPermission(
+ android.Manifest.permission.CONNECTIVITY_INTERNAL, "ConnectivityService");
+ } else {
+ context.enforceCallingOrSelfPermission(
+ android.Manifest.permission.CHANGE_NETWORK_STATE, "ConnectivityService");
+ }
+ }
+
/**
* Get the set of tetherable, available interfaces. This list is limited by
* device configuration and current interface existence.
diff --git a/core/java/android/net/IpPrefix.java b/core/java/android/net/IpPrefix.java
index f1fa3eb..b268986 100644
--- a/core/java/android/net/IpPrefix.java
+++ b/core/java/android/net/IpPrefix.java
@@ -172,7 +172,7 @@
/**
* Returns a string representation of this {@code IpPrefix}.
*
- * @return a string such as {@code "192.0.2.0/24"} or {@code "2001:db8:1:2::"}.
+ * @return a string such as {@code "192.0.2.0/24"} or {@code "2001:db8:1:2::/64"}.
*/
public String toString() {
try {
diff --git a/core/java/android/net/LinkProperties.java b/core/java/android/net/LinkProperties.java
index 3d6a132..662c576 100644
--- a/core/java/android/net/LinkProperties.java
+++ b/core/java/android/net/LinkProperties.java
@@ -381,7 +381,8 @@
return new RouteInfo(
route.getDestination(),
route.getGateway(),
- mIfaceName);
+ mIfaceName,
+ route.getType());
}
/**
diff --git a/core/java/android/net/NetworkAgent.java b/core/java/android/net/NetworkAgent.java
index 80e5b91..b83198d 100644
--- a/core/java/android/net/NetworkAgent.java
+++ b/core/java/android/net/NetworkAgent.java
@@ -120,6 +120,25 @@
*/
public static final int EVENT_UNBLOCK_ADDRESS_FAMILY = BASE + 8;
+ /**
+ * Sent by ConnectivitySerice to the NetworkAgent to inform the agent of the
+ * networks status - whether we could use the network or could not, due to
+ * either a bad network configuration (no internet link) or captive portal.
+ *
+ * arg1 = either {@code VALID_NETWORK} or {@code INVALID_NETWORK}
+ */
+ public static final int CMD_REPORT_NETWORK_STATUS = BASE + 9;
+
+ public static final int VALID_NETWORK = 1;
+ public static final int INVALID_NETWORK = 2;
+
+ /**
+ * Sent by the NetworkAgent to ConnectivityService to indicate this network was
+ * explicitly selected. This should be sent before the NetworkInfo is marked
+ * CONNECTED so it can be given special treatment at that time.
+ */
+ public static final int EVENT_SET_EXPLICITLY_SELECTED = BASE + 10;
+
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);
@@ -181,6 +200,14 @@
log("Unhandled Message " + msg);
break;
}
+ case CMD_REPORT_NETWORK_STATUS: {
+ if (VDBG) {
+ log("CMD_REPORT_NETWORK_STATUS(" +
+ (msg.arg1 == VALID_NETWORK ? "VALID)" : "INVALID)"));
+ }
+ networkStatus(msg.arg1);
+ break;
+ }
}
}
@@ -261,6 +288,15 @@
}
/**
+ * Called by the bearer to indicate this network was manually selected by the user.
+ * This should be called before the NetworkInfo is marked CONNECTED so that this
+ * Network can be given special treatment at that time.
+ */
+ public void explicitlySelected() {
+ queueOrSendMessage(EVENT_SET_EXPLICITLY_SELECTED, 0);
+ }
+
+ /**
* Called when ConnectivityService has indicated they no longer want this network.
* The parent factory should (previously) have received indication of the change
* as well, either canceling NetworkRequests or altering their score such that this
@@ -268,6 +304,24 @@
*/
abstract protected void unwanted();
+ /**
+ * Called when the system determines the usefulness of this network.
+ *
+ * Networks claiming internet connectivity will have their internet
+ * connectivity verified.
+ *
+ * Currently there are two possible values:
+ * {@code VALID_NETWORK} if the system is happy with the connection,
+ * {@code INVALID_NETWORK} if the system is not happy.
+ * TODO - add indications of captive portal-ness and related success/failure,
+ * ie, CAPTIVE_SUCCESS_NETWORK, CAPTIVE_NETWORK for successful login and detection
+ *
+ * This may be called multiple times as the network status changes and may
+ * generate false negatives if we lose ip connectivity before the link is torn down.
+ */
+ protected void networkStatus(int status) {
+ }
+
protected void log(String s) {
Log.d(LOG_TAG, "NetworkAgent: " + s);
}
diff --git a/core/java/android/net/NetworkInfo.java b/core/java/android/net/NetworkInfo.java
index 7664c95..393637e 100644
--- a/core/java/android/net/NetworkInfo.java
+++ b/core/java/android/net/NetworkInfo.java
@@ -411,8 +411,7 @@
/**
* Report the extra information about the network state, if any was
- * provided by the lower networking layers.,
- * if one is available.
+ * provided by the lower networking layers.
* @return the extra information, or null if not available
*/
public String getExtraInfo() {
diff --git a/core/java/android/net/NetworkMisc.java b/core/java/android/net/NetworkMisc.java
index 34f6cf4..5d2a43d 100644
--- a/core/java/android/net/NetworkMisc.java
+++ b/core/java/android/net/NetworkMisc.java
@@ -25,12 +25,32 @@
* @hide
*/
public class NetworkMisc implements Parcelable {
+
/**
* If the {@link Network} is a VPN, whether apps are allowed to bypass the VPN. This is set by
* a {@link VpnService} and used by {@link ConnectivityService} when creating a VPN.
*/
public boolean allowBypass;
+ /**
+ * Set if the network was manually/explicitly connected to by the user either from settings
+ * or a 3rd party app. For example, turning on cell data is not explicit but tapping on a wifi
+ * ap in the wifi settings to trigger a connection is explicit. A 3rd party app asking to
+ * connect to a particular access point is also explicit, though this may change in the future
+ * as we want apps to use the multinetwork apis.
+ */
+ public boolean explicitlySelected;
+
+ public NetworkMisc() {
+ }
+
+ public NetworkMisc(NetworkMisc nm) {
+ if (nm != null) {
+ allowBypass = nm.allowBypass;
+ explicitlySelected = nm.explicitlySelected;
+ }
+ }
+
@Override
public int describeContents() {
return 0;
@@ -39,6 +59,7 @@
@Override
public void writeToParcel(Parcel out, int flags) {
out.writeInt(allowBypass ? 1 : 0);
+ out.writeInt(explicitlySelected ? 1 : 0);
}
public static final Creator<NetworkMisc> CREATOR = new Creator<NetworkMisc>() {
@@ -46,6 +67,7 @@
public NetworkMisc createFromParcel(Parcel in) {
NetworkMisc networkMisc = new NetworkMisc();
networkMisc.allowBypass = in.readInt() != 0;
+ networkMisc.explicitlySelected = in.readInt() != 0;
return networkMisc;
}
diff --git a/core/java/android/net/RouteInfo.java b/core/java/android/net/RouteInfo.java
index a4ec80c..cfd20a0 100644
--- a/core/java/android/net/RouteInfo.java
+++ b/core/java/android/net/RouteInfo.java
@@ -62,6 +62,23 @@
*/
private final String mInterface;
+
+ /** Unicast route. @hide */
+ public static final int RTN_UNICAST = 1;
+
+ /** Unreachable route. @hide */
+ public static final int RTN_UNREACHABLE = 7;
+
+ /** Throw route. @hide */
+ public static final int RTN_THROW = 9;
+
+ /**
+ * The type of this route; one of the RTN_xxx constants above.
+ */
+ private final int mType;
+
+ // Derived data members.
+ // TODO: remove these.
private final boolean mIsHost;
private final boolean mHasGateway;
@@ -82,7 +99,26 @@
*
* @hide
*/
- public RouteInfo(IpPrefix destination, InetAddress gateway, String iface) {
+ public RouteInfo(IpPrefix destination, InetAddress gateway, String iface, int type) {
+ switch (type) {
+ case RTN_UNICAST:
+ case RTN_UNREACHABLE:
+ case RTN_THROW:
+ // TODO: It would be nice to ensure that route types that don't have nexthops or
+ // interfaces, such as unreachable or throw, can't be created if an interface or
+ // a gateway is specified. This is a bit too complicated to do at the moment
+ // because:
+ //
+ // - LinkProperties sets the interface on routes added to it, and modifies the
+ // interfaces of all the routes when its interface name changes.
+ // - Even when the gateway is null, we store a non-null gateway here.
+ //
+ // For now, we just rely on the code that sets routes to do things properly.
+ break;
+ default:
+ throw new IllegalArgumentException("Unknown route type " + type);
+ }
+
if (destination == null) {
if (gateway != null) {
if (gateway instanceof Inet4Address) {
@@ -117,10 +153,18 @@
mDestination = destination; // IpPrefix objects are immutable.
mGateway = gateway; // InetAddress objects are immutable.
mInterface = iface; // Strings are immutable.
+ mType = type;
mIsHost = isHost();
}
/**
+ * @hide
+ */
+ public RouteInfo(IpPrefix destination, InetAddress gateway, String iface) {
+ this(destination, gateway, iface, RTN_UNICAST);
+ }
+
+ /**
* @hide
*/
public RouteInfo(LinkAddress destination, InetAddress gateway, String iface) {
@@ -150,6 +194,8 @@
/**
* @hide
+ *
+ * TODO: Remove this.
*/
public RouteInfo(LinkAddress destination, InetAddress gateway) {
this(destination, gateway, null);
@@ -188,6 +234,13 @@
/**
* @hide
*/
+ public RouteInfo(IpPrefix destination, int type) {
+ this(destination, null, null, type);
+ }
+
+ /**
+ * @hide
+ */
public static RouteInfo makeHostRoute(InetAddress host, String iface) {
return makeHostRoute(host, null, iface);
}
@@ -249,12 +302,23 @@
}
/**
+ * Retrieves the type of this route.
+ *
+ * @return The type of this route; one of the {@code RTN_xxx} constants defined in this class.
+ *
+ * @hide
+ */
+ public int getType() {
+ return mType;
+ }
+
+ /**
* Indicates if this route is a default route (ie, has no destination specified).
*
* @return {@code true} if the destination has a prefix length of 0.
*/
public boolean isDefaultRoute() {
- return mDestination.getPrefixLength() == 0;
+ return mType == RTN_UNICAST && mDestination.getPrefixLength() == 0;
}
/**
@@ -345,9 +409,18 @@
public String toString() {
String val = "";
if (mDestination != null) val = mDestination.toString();
- val += " ->";
- if (mGateway != null) val += " " + mGateway.getHostAddress();
- if (mInterface != null) val += " " + mInterface;
+ if (mType == RTN_UNREACHABLE) {
+ val += " unreachable";
+ } else if (mType == RTN_THROW) {
+ val += " throw";
+ } else {
+ val += " ->";
+ if (mGateway != null) val += " " + mGateway.getHostAddress();
+ if (mInterface != null) val += " " + mInterface;
+ if (mType != RTN_UNICAST) {
+ val += " unknown type " + mType;
+ }
+ }
return val;
}
@@ -364,7 +437,8 @@
return Objects.equals(mDestination, target.getDestination()) &&
Objects.equals(mGateway, target.getGateway()) &&
- Objects.equals(mInterface, target.getInterface());
+ Objects.equals(mInterface, target.getInterface()) &&
+ mType == target.getType();
}
/**
@@ -373,7 +447,8 @@
public int hashCode() {
return (mDestination.hashCode() * 41)
+ (mGateway == null ? 0 :mGateway.hashCode() * 47)
- + (mInterface == null ? 0 :mInterface.hashCode() * 67);
+ + (mInterface == null ? 0 :mInterface.hashCode() * 67)
+ + (mType * 71);
}
/**
@@ -391,6 +466,7 @@
byte[] gatewayBytes = (mGateway == null) ? null : mGateway.getAddress();
dest.writeByteArray(gatewayBytes);
dest.writeString(mInterface);
+ dest.writeInt(mType);
}
/**
@@ -408,8 +484,9 @@
} catch (UnknownHostException e) {}
String iface = in.readString();
+ int type = in.readInt();
- return new RouteInfo(dest, gateway, iface);
+ return new RouteInfo(dest, gateway, iface, type);
}
public RouteInfo[] newArray(int size) {
diff --git a/core/tests/coretests/src/android/net/NetworkStatsTest.java b/core/tests/coretests/src/android/net/NetworkStatsTest.java
index 6331964..9ee4e20 100644
--- a/core/tests/coretests/src/android/net/NetworkStatsTest.java
+++ b/core/tests/coretests/src/android/net/NetworkStatsTest.java
@@ -310,6 +310,16 @@
assertEquals(128L + 512L, clone.getTotalBytes());
}
+ public void testAddWhenEmpty() throws Exception {
+ final NetworkStats red = new NetworkStats(TEST_START, -1);
+ final NetworkStats blue = new NetworkStats(TEST_START, 5)
+ .addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 128L, 8L, 0L, 2L, 20L)
+ .addValues(TEST_IFACE2, 100, SET_DEFAULT, TAG_NONE, 512L, 32L, 0L, 0L, 0L);
+
+ // We're mostly checking that we don't crash
+ red.combineAllValues(blue);
+ }
+
private static void assertValues(NetworkStats stats, int index, String iface, int uid, int set,
int tag, long rxBytes, long rxPackets, long txBytes, long txPackets, long operations) {
final NetworkStats.Entry entry = stats.getValues(index, null);
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index ef9bf51..5c43f6b 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -1353,13 +1353,6 @@
"ConnectivityService");
}
- // TODO Make this a special check when it goes public
- private void enforceTetherChangePermission() {
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.CHANGE_NETWORK_STATE,
- "ConnectivityService");
- }
-
private void enforceTetherAccessPermission() {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.ACCESS_NETWORK_STATE,
@@ -1921,6 +1914,15 @@
}
break;
}
+ case NetworkAgent.EVENT_SET_EXPLICITLY_SELECTED: {
+ NetworkAgentInfo nai = mNetworkAgentInfos.get(msg.replyTo);
+ if (nai == null) {
+ loge("EVENT_SET_EXPLICITLY_SELECTED from unknown NetworkAgent");
+ break;
+ }
+ nai.networkMisc.explicitlySelected = true;
+ break;
+ }
case NetworkMonitor.EVENT_NETWORK_TESTED: {
NetworkAgentInfo nai = (NetworkAgentInfo)msg.obj;
if (isLiveNetworkAgent(nai, "EVENT_NETWORK_VALIDATED")) {
@@ -1931,6 +1933,11 @@
rematchNetworkAndRequests(nai);
}
updateInetCondition(nai, valid);
+ // Let the NetworkAgent know the state of its network
+ nai.asyncChannel.sendMessage(
+ android.net.NetworkAgent.CMD_REPORT_NETWORK_STATUS,
+ (valid ? NetworkAgent.VALID_NETWORK : NetworkAgent.INVALID_NETWORK),
+ 0, null);
}
break;
}
@@ -2371,8 +2378,7 @@
// javadoc from interface
public int tether(String iface) {
- enforceTetherChangePermission();
-
+ ConnectivityManager.enforceTetherChangePermission(mContext);
if (isTetheringSupported()) {
return mTethering.tether(iface);
} else {
@@ -2382,7 +2388,7 @@
// javadoc from interface
public int untether(String iface) {
- enforceTetherChangePermission();
+ ConnectivityManager.enforceTetherChangePermission(mContext);
if (isTetheringSupported()) {
return mTethering.untether(iface);
@@ -2431,7 +2437,7 @@
}
public int setUsbTethering(boolean enable) {
- enforceTetherChangePermission();
+ ConnectivityManager.enforceTetherChangePermission(mContext);
if (isTetheringSupported()) {
return mTethering.setUsbTethering(enable);
} else {
@@ -2513,6 +2519,7 @@
nai = mNetworkForNetId.get(network.netId);
}
if (nai == null) return;
+ if (DBG) log("reportBadNetwork(" + nai.name() + ") by " + uid);
synchronized (nai) {
if (isNetworkBlocked(nai, uid)) return;
@@ -4241,7 +4248,7 @@
NetworkAgentInfo nai = new NetworkAgentInfo(messenger, new AsyncChannel(),
new NetworkInfo(networkInfo), new LinkProperties(linkProperties),
new NetworkCapabilities(networkCapabilities), currentScore, mContext, mTrackerHandler,
- networkMisc);
+ new NetworkMisc(networkMisc));
synchronized (this) {
nai.networkMonitor.systemReady = mSystemReady;
}
@@ -4543,8 +4550,10 @@
for (NetworkRequestInfo nri : mNetworkRequests.values()) {
NetworkAgentInfo currentNetwork = mNetworkForRequestId.get(nri.request.requestId);
if (newNetwork == currentNetwork) {
- if (DBG) log("Network " + newNetwork.name() + " was already satisfying" +
- " request " + nri.request.requestId + ". No change.");
+ if (DBG) {
+ log("Network " + newNetwork.name() + " was already satisfying" +
+ " request " + nri.request.requestId + ". No change.");
+ }
keep = true;
continue;
}
diff --git a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
index 957d705..15ffc0d 100644
--- a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
+++ b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
@@ -53,6 +53,9 @@
// Penalty applied to scores of Networks that have not been validated.
private static final int UNVALIDATED_SCORE_PENALTY = 40;
+ // Score for explicitly connected network.
+ private static final int EXPLICITLY_SELECTED_NETWORK_SCORE = 100;
+
// The list of NetworkRequests being satisfied by this Network.
public final SparseArray<NetworkRequest> networkRequests = new SparseArray<NetworkRequest>();
public final ArrayList<NetworkRequest> networkLingered = new ArrayList<NetworkRequest>();
@@ -95,9 +98,10 @@
int score = currentScore;
if (!validated) score -= UNVALIDATED_SCORE_PENALTY;
-
if (score < 0) score = 0;
+ if (networkMisc.explicitlySelected) score = EXPLICITLY_SELECTED_NETWORK_SCORE;
+
return score;
}
@@ -110,7 +114,8 @@
network + "} lp{" +
linkProperties + "} nc{" +
networkCapabilities + "} Score{" + getCurrentScore() + "} " +
- "validated{" + validated + "} created{" + created + "} }";
+ "validated{" + validated + "} created{" + created + "} " +
+ "explicitlySelected{" + networkMisc.explicitlySelected + "} }";
}
public String name() {