Merge commit '052a0da' into merge2
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 6359f08..c531e7e 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -25,7 +25,6 @@
import android.net.NetworkUtils;
import android.os.Binder;
import android.os.Build.VERSION_CODES;
-import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
@@ -38,7 +37,6 @@
import android.os.ServiceManager;
import android.provider.Settings;
import android.telephony.SubscriptionManager;
-import android.telephony.TelephonyManager;
import android.util.ArrayMap;
import android.util.Log;
@@ -312,6 +310,14 @@
public static final String EXTRA_IS_CAPTIVE_PORTAL = "captivePortal";
/**
+ * Action used to display a dialog that asks the user whether to connect to a network that is
+ * not validated. This intent is used to start the dialog in settings via startActivity.
+ *
+ * @hide
+ */
+ public static final String ACTION_PROMPT_UNVALIDATED = "android.net.conn.PROMPT_UNVALIDATED";
+
+ /**
* The absence of a connection type.
* @hide
*/
@@ -334,6 +340,9 @@
* same network interface as {@link #TYPE_MOBILE} or it may use a different
* one. This is used by applications needing to talk to the carrier's
* Multimedia Messaging Service servers.
+ *
+ * @deprecated Applications should instead use {@link #requestNetwork} to request a network that
+ * provides the {@link NetworkCapabilities#NET_CAPABILITY_MMS} capability.
*/
public static final int TYPE_MOBILE_MMS = 2;
/**
@@ -341,6 +350,9 @@
* same network interface as {@link #TYPE_MOBILE} or it may use a different
* one. This is used by applications needing to talk to the carrier's
* Secure User Plane Location servers for help locating the device.
+ *
+ * @deprecated Applications should instead use {@link #requestNetwork} to request a network that
+ * provides the {@link NetworkCapabilities#NET_CAPABILITY_SUPL} capability.
*/
public static final int TYPE_MOBILE_SUPL = 3;
/**
@@ -353,9 +365,10 @@
/**
* A High Priority Mobile data connection. This network type uses the
* same network interface as {@link #TYPE_MOBILE} but the routing setup
- * is different. Only requesting processes will have access to the
- * Mobile DNS servers and only IP's explicitly requested via {@link #requestRouteToHost}
- * will route over this interface if no default route exists.
+ * is different.
+ *
+ * @deprecated Applications should instead use {@link #requestNetwork} to request a network that
+ * uses the {@link NetworkCapabilities#TRANSPORT_CELLULAR} transport.
*/
public static final int TYPE_MOBILE_HIPRI = 5;
/**
@@ -415,7 +428,7 @@
*/
public static final int TYPE_MOBILE_IA = 14;
-/**
+ /**
* Emergency PDN connection for emergency calls
* {@hide}
*/
@@ -529,6 +542,8 @@
return "MOBILE_EMERGENCY";
case TYPE_PROXY:
return "PROXY";
+ case TYPE_VPN:
+ return "VPN";
default:
return Integer.toString(type);
}
@@ -774,7 +789,7 @@
}
/**
- * Returns an array of of {@link NetworkCapabilities} objects, representing
+ * Returns an array of {@link android.net.NetworkCapabilities} objects, representing
* the Networks that applications run by the given user will use by default.
* @hide
*/
@@ -868,11 +883,11 @@
}
/**
- * Get the {@link NetworkCapabilities} for the given {@link Network}. This
+ * Get the {@link android.net.NetworkCapabilities} for the given {@link Network}. This
* will return {@code null} if the network is unknown.
*
* @param network The {@link Network} object identifying the network in question.
- * @return The {@link NetworkCapabilities} for the network, or {@code null}.
+ * @return The {@link android.net.NetworkCapabilities} for the network, or {@code null}.
*/
public NetworkCapabilities getNetworkCapabilities(Network network) {
try {
@@ -896,6 +911,7 @@
* always indicates failure.
*
* @deprecated Deprecated in favor of the cleaner {@link #requestNetwork} api.
+ * @removed
*/
public int startUsingNetworkFeature(int networkType, String feature) {
NetworkCapabilities netCap = networkCapabilitiesForFeature(networkType, feature);
@@ -943,6 +959,7 @@
* always indicates failure.
*
* @deprecated Deprecated in favor of the cleaner {@link #requestNetwork} api.
+ * @removed
*/
public int stopUsingNetworkFeature(int networkType, String feature) {
NetworkCapabilities netCap = networkCapabilitiesForFeature(networkType, feature);
@@ -1221,6 +1238,7 @@
*
* @deprecated Deprecated in favor of the {@link #requestNetwork},
* {@link #bindProcessToNetwork} and {@link Network#getSocketFactory} api.
+ * @removed
*/
public boolean requestRouteToHost(int networkType, int hostAddress) {
return requestRouteToHostAddress(networkType, NetworkUtils.intToInetAddress(hostAddress));
@@ -1239,6 +1257,7 @@
* @hide
* @deprecated Deprecated in favor of the {@link #requestNetwork} and
* {@link #bindProcessToNetwork} api.
+ * @removed
*/
public boolean requestRouteToHostAddress(int networkType, InetAddress hostAddress) {
try {
@@ -2176,7 +2195,7 @@
* changes capabilities but still satisfies the stated need.
*
* @param network The {@link Network} whose capabilities have changed.
- * @param networkCapabilities The new {@link NetworkCapabilities} for this network.
+ * @param networkCapabilities The new {@link android.net.NetworkCapabilities} for this network.
*/
public void onCapabilitiesChanged(Network network,
NetworkCapabilities networkCapabilities) {}
@@ -2418,7 +2437,7 @@
}
/**
- * Request a network to satisfy a set of {@link NetworkCapabilities}.
+ * Request a network to satisfy a set of {@link android.net.NetworkCapabilities}.
*
* This {@link NetworkRequest} will live until released via
* {@link #unregisterNetworkCallback} or the calling application exits.
@@ -2437,7 +2456,7 @@
}
/**
- * Request a network to satisfy a set of {@link NetworkCapabilities}, limited
+ * Request a network to satisfy a set of {@link android.net.NetworkCapabilities}, limited
* by a timeout.
*
* This function behaves identically to the non-timedout version, but if a suitable
@@ -2484,7 +2503,7 @@
/**
- * Request a network to satisfy a set of {@link NetworkCapabilities}.
+ * Request a network to satisfy a set of {@link android.net.NetworkCapabilities}.
*
* This function behaves identically to the version that takes a NetworkCallback, but instead
* of {@link NetworkCallback} a {@link PendingIntent} is used. This means
@@ -2560,17 +2579,21 @@
}
/**
- * Request connectivityservice to refresh network capabilities for the given
- * {@link network}. This method returns true if the network is still active, false
- * otherwise. Notice the method call assumes the caller has registered for
- * listening NetworkCapabilities updates.
+ * Requests bandwidth update for a given {@link Network} and returns whether the update request
+ * is accepted by ConnectivityService. Once accepted, ConnectivityService will poll underlying
+ * network connection for updated bandwidth information. The caller will be notified via
+ * {@link ConnectivityManager.NetworkCallback} if there is an update. Notice that this
+ * method assumes that the caller has previously called {@link #registerNetworkCallback} to
+ * listen for network changes.
*
* @param network{@link Network} specifying which network you're interested.
+ * @return {@code true} on success, {@code false} if the {@link Network} is no longer valid.
+ *
* @hide
*/
- public boolean requestBwUpdate(Network network) {
+ public boolean requestBandwidthUpdate(Network network) {
try {
- return mService.requestBwUpdate(network);
+ return mService.requestBandwidthUpdate(network);
} catch (RemoteException e) {
return false;
}
@@ -2596,6 +2619,40 @@
}
/**
+ * Informs the system whether it should switch to {@code network} regardless of whether it is
+ * validated or not. If {@code accept} is true, and the network was explicitly selected by the
+ * user (e.g., by selecting a Wi-Fi network in the Settings app), then the network will become
+ * the system default network regardless of any other network that's currently connected. If
+ * {@code always} is true, then the choice is remembered, so that the next time the user
+ * connects to this network, the system will switch to it.
+ *
+ * <p>This method requires the caller to hold the permission
+ * {@link android.Manifest.permission#CONNECTIVITY_INTERNAL}
+ *
+ * @param network The network to accept.
+ * @param accept Whether to accept the network even if unvalidated.
+ * @param always Whether to remember this choice in the future.
+ *
+ * @hide
+ */
+ public void setAcceptUnvalidated(Network network, boolean accept, boolean always) {
+ try {
+ mService.setAcceptUnvalidated(network, accept, always);
+ } catch (RemoteException e) {}
+ }
+
+ /**
+ * Resets all connectivity manager settings back to factory defaults.
+ * @hide
+ */
+ public void factoryReset() {
+ try {
+ mService.factoryReset();
+ } catch (RemoteException e) {
+ }
+ }
+
+ /**
* Binds the current process to {@code network}. All Sockets created in the future
* (and not explicitly bound via a bound SocketFactory from
* {@link Network#getSocketFactory() Network.getSocketFactory()}) will be bound to
@@ -2648,7 +2705,12 @@
if (NetworkUtils.bindProcessToNetwork(netId)) {
// Set HTTP proxy system properties to match network.
// TODO: Deprecate this static method and replace it with a non-static version.
- Proxy.setHttpProxySystemProperty(getInstance().getDefaultProxy());
+ try {
+ Proxy.setHttpProxySystemProperty(getInstance().getDefaultProxy());
+ } catch (SecurityException e) {
+ // The process doesn't have ACCESS_NETWORK_STATE, so we can't fetch the proxy.
+ Log.e(TAG, "Can't set proxy properties", e);
+ }
// Must flush DNS cache as new network may have different DNS resolutions.
InetAddress.clearDnsCache();
// Must flush socket pool as idle sockets will be bound to previous network and may
diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl
index 6e06aa5..77200a5 100644
--- a/core/java/android/net/IConnectivityManager.aidl
+++ b/core/java/android/net/IConnectivityManager.aidl
@@ -33,6 +33,7 @@
import com.android.internal.net.LegacyVpnInfo;
import com.android.internal.net.VpnConfig;
+import com.android.internal.net.VpnInfo;
import com.android.internal.net.VpnProfile;
/**
@@ -117,6 +118,8 @@
LegacyVpnInfo getLegacyVpnInfo();
+ VpnInfo[] getAllVpnInfo();
+
boolean updateLockdownVpn();
void captivePortalCheckCompleted(in NetworkInfo info, boolean isCaptivePortal);
@@ -133,7 +136,7 @@
void registerNetworkFactory(in Messenger messenger, in String name);
- boolean requestBwUpdate(in Network network);
+ boolean requestBandwidthUpdate(in Network network);
void unregisterNetworkFactory(in Messenger messenger);
@@ -156,9 +159,13 @@
void releaseNetworkRequest(in NetworkRequest networkRequest);
+ void setAcceptUnvalidated(in Network network, boolean accept, boolean always);
+
int getRestoreDefaultNetworkDelay(int networkType);
boolean addVpnAddress(String address, int prefixLength);
boolean removeVpnAddress(String address, int prefixLength);
boolean setUnderlyingNetworksForVpn(in Network[] networks);
+
+ void factoryReset();
}
diff --git a/core/java/android/net/IpPrefix.java b/core/java/android/net/IpPrefix.java
index b268986..6b4f2d5 100644
--- a/core/java/android/net/IpPrefix.java
+++ b/core/java/android/net/IpPrefix.java
@@ -170,6 +170,21 @@
}
/**
+ * Determines whether the prefix contains the specified address.
+ *
+ * @param address An {@link InetAddress} to test.
+ * @return {@code true} if the prefix covers the given address.
+ */
+ public boolean contains(InetAddress address) {
+ byte[] addrBytes = (address == null) ? null : address.getAddress();
+ if (addrBytes == null || addrBytes.length != this.address.length) {
+ return false;
+ }
+ NetworkUtils.maskRawAddress(addrBytes, prefixLength);
+ return Arrays.equals(this.address, addrBytes);
+ }
+
+ /**
* 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::/64"}.
diff --git a/core/java/android/net/Network.java b/core/java/android/net/Network.java
index 4ce9e68..67ecb5d 100644
--- a/core/java/android/net/Network.java
+++ b/core/java/android/net/Network.java
@@ -16,7 +16,6 @@
package android.net;
-import android.net.NetworkUtils;
import android.os.Parcelable;
import android.os.Parcel;
import android.system.ErrnoException;
@@ -27,22 +26,20 @@
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.MalformedURLException;
-import java.net.ProxySelector;
import java.net.Socket;
import java.net.SocketAddress;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.net.URL;
import java.net.URLConnection;
-import java.net.URLStreamHandler;
-import java.util.concurrent.atomic.AtomicReference;
import javax.net.SocketFactory;
import com.android.okhttp.ConnectionPool;
-import com.android.okhttp.HostResolver;
import com.android.okhttp.HttpHandler;
import com.android.okhttp.HttpsHandler;
import com.android.okhttp.OkHttpClient;
+import com.android.okhttp.OkUrlFactory;
+import com.android.okhttp.internal.Internal;
/**
* Identifies a {@code Network}. This is supplied to applications via
@@ -63,10 +60,10 @@
// Objects used to perform per-network operations such as getSocketFactory
// and openConnection, and a lock to protect access to them.
private volatile NetworkBoundSocketFactory mNetworkBoundSocketFactory = null;
- // mLock should be used to control write access to mConnectionPool and mHostResolver.
+ // mLock should be used to control write access to mConnectionPool and mNetwork.
// maybeInitHttpClient() must be called prior to reading either variable.
private volatile ConnectionPool mConnectionPool = null;
- private volatile HostResolver mHostResolver = null;
+ private volatile com.android.okhttp.internal.Network mNetwork = null;
private Object mLock = new Object();
// Default connection pool values. These are evaluated at startup, just
@@ -220,10 +217,10 @@
// out) ConnectionPools.
private void maybeInitHttpClient() {
synchronized (mLock) {
- if (mHostResolver == null) {
- mHostResolver = new HostResolver() {
+ if (mNetwork == null) {
+ mNetwork = new com.android.okhttp.internal.Network() {
@Override
- public InetAddress[] getAllByName(String host) throws UnknownHostException {
+ public InetAddress[] resolveInetAddresses(String host) throws UnknownHostException {
return Network.this.getAllByName(host);
}
};
@@ -280,22 +277,25 @@
if (proxy == null) throw new IllegalArgumentException("proxy is null");
maybeInitHttpClient();
String protocol = url.getProtocol();
- OkHttpClient client;
- // TODO: HttpHandler creates OkHttpClients that share the default ResponseCache.
+ OkUrlFactory okUrlFactory;
+ // TODO: HttpHandler creates OkUrlFactory instances that share the default ResponseCache.
// Could this cause unexpected behavior?
if (protocol.equals("http")) {
- client = HttpHandler.createHttpOkHttpClient(proxy);
+ okUrlFactory = HttpHandler.createHttpOkUrlFactory(proxy);
} else if (protocol.equals("https")) {
- client = HttpsHandler.createHttpsOkHttpClient(proxy);
+ okUrlFactory = HttpsHandler.createHttpsOkUrlFactory(proxy);
} else {
- // OkHttpClient only supports HTTP and HTTPS and returns a null URLStreamHandler if
+ // OkHttp only supports HTTP and HTTPS and returns a null URLStreamHandler if
// passed another protocol.
throw new MalformedURLException("Invalid URL or unrecognized protocol " + protocol);
}
- return client.setSocketFactory(getSocketFactory())
- .setHostResolver(mHostResolver)
- .setConnectionPool(mConnectionPool)
- .open(url);
+ OkHttpClient client = okUrlFactory.client();
+ client.setSocketFactory(getSocketFactory()).setConnectionPool(mConnectionPool);
+
+ // Use internal APIs to change the Network.
+ Internal.instance.setNetwork(client, mNetwork);
+
+ return okUrlFactory.open(url);
}
/**
diff --git a/core/java/android/net/NetworkAgent.java b/core/java/android/net/NetworkAgent.java
index c33aa2f..9c3a623 100644
--- a/core/java/android/net/NetworkAgent.java
+++ b/core/java/android/net/NetworkAgent.java
@@ -21,15 +21,12 @@
import android.os.Looper;
import android.os.Message;
import android.os.Messenger;
-import android.os.Parcel;
-import android.os.Parcelable;
import android.util.Log;
import com.android.internal.util.AsyncChannel;
import com.android.internal.util.Protocol;
import java.util.ArrayList;
-import java.util.concurrent.atomic.AtomicBoolean;
/**
* A Utility class for handling for communicating between bearer-specific
@@ -113,7 +110,7 @@
public static final int EVENT_UID_RANGES_REMOVED = BASE + 6;
/**
- * Sent by ConnectivitySerice to the NetworkAgent to inform the agent of the
+ * Sent by ConnectivityService 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.
*
@@ -128,15 +125,25 @@
* 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.
+ *
+ * obj = boolean indicating whether to use this network even if unvalidated
*/
public static final int EVENT_SET_EXPLICITLY_SELECTED = BASE + 8;
/**
- * Sent by ConnectivityService to the NetworkAgent to inform the agent to pull
+ * Sent by ConnectivityService to the NetworkAgent to inform the agent of
+ * whether the network should in the future be used even if not validated.
+ * This decision is made by the user, but it is the network transport's
+ * responsibility to remember it.
+ *
+ * arg1 = 1 if true, 0 if false
+ */
+ public static final int CMD_SAVE_ACCEPT_UNVALIDATED = BASE + 9;
+
+ /** Sent by ConnectivityService to the NetworkAgent to inform the agent to pull
* the underlying network connection for updated bandwidth information.
*/
- public static final int CMD_REQUEST_BANDWIDTH_UPDATE = BASE + 9;
-
+ public static final int CMD_REQUEST_BANDWIDTH_UPDATE = BASE + 10;
public NetworkAgent(Looper looper, Context context, String logTag, NetworkInfo ni,
NetworkCapabilities nc, LinkProperties lp, int score) {
@@ -216,6 +223,9 @@
networkStatus(msg.arg1);
break;
}
+ case CMD_SAVE_ACCEPT_UNVALIDATED: {
+ saveAcceptUnvalidated(msg.arg1 != 0);
+ }
}
}
@@ -284,10 +294,16 @@
/**
* 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.
+ * Network can be given special treatment at that time. If {@code acceptUnvalidated} is
+ * {@code true}, then the system will switch to this network. If it is {@code false} and the
+ * network cannot be validated, the system will ask the user whether to switch to this network.
+ * If the user confirms and selects "don't ask again", then the system will call
+ * {@link #saveAcceptUnvalidated} to persist the user's choice. Thus, if the transport ever
+ * calls this method with {@code acceptUnvalidated} set to {@code false}, it must also implement
+ * {@link #saveAcceptUnvalidated} to respect the user's choice.
*/
- public void explicitlySelected() {
- queueOrSendMessage(EVENT_SET_EXPLICITLY_SELECTED, 0);
+ public void explicitlySelected(boolean acceptUnvalidated) {
+ queueOrSendMessage(EVENT_SET_EXPLICITLY_SELECTED, acceptUnvalidated);
}
/**
@@ -323,6 +339,16 @@
protected void networkStatus(int status) {
}
+ /**
+ * Called when the user asks to remember the choice to use this network even if unvalidated.
+ * The transport is responsible for remembering the choice, and the next time the user connects
+ * to the network, should explicitlySelected with {@code acceptUnvalidated} set to {@code true}.
+ * This method will only be called if {@link #explicitlySelected} was called with
+ * {@code acceptUnvalidated} set to {@code false}.
+ */
+ protected void saveAcceptUnvalidated(boolean accept) {
+ }
+
protected void log(String s) {
Log.d(LOG_TAG, "NetworkAgent: " + s);
}
diff --git a/core/java/android/net/NetworkMisc.java b/core/java/android/net/NetworkMisc.java
index b92c9e3..5511a24 100644
--- a/core/java/android/net/NetworkMisc.java
+++ b/core/java/android/net/NetworkMisc.java
@@ -45,6 +45,13 @@
public boolean explicitlySelected;
/**
+ * Set if the user desires to use this network even if it is unvalidated. This field has meaning
+ * only if {#link explicitlySelected} is true. If it is, this field must also be set to the
+ * appropriate value based on previous user choice.
+ */
+ public boolean acceptUnvalidated;
+
+ /**
* For mobile networks, this is the subscriber ID (such as IMSI).
*/
public String subscriberId;
@@ -56,6 +63,7 @@
if (nm != null) {
allowBypass = nm.allowBypass;
explicitlySelected = nm.explicitlySelected;
+ acceptUnvalidated = nm.acceptUnvalidated;
subscriberId = nm.subscriberId;
}
}
@@ -69,6 +77,7 @@
public void writeToParcel(Parcel out, int flags) {
out.writeInt(allowBypass ? 1 : 0);
out.writeInt(explicitlySelected ? 1 : 0);
+ out.writeInt(acceptUnvalidated ? 1 : 0);
out.writeString(subscriberId);
}
@@ -78,6 +87,7 @@
NetworkMisc networkMisc = new NetworkMisc();
networkMisc.allowBypass = in.readInt() != 0;
networkMisc.explicitlySelected = in.readInt() != 0;
+ networkMisc.acceptUnvalidated = in.readInt() != 0;
networkMisc.subscriberId = in.readString();
return networkMisc;
}
diff --git a/core/java/android/net/NetworkRequest.java b/core/java/android/net/NetworkRequest.java
index 5a09b46..7838b47 100644
--- a/core/java/android/net/NetworkRequest.java
+++ b/core/java/android/net/NetworkRequest.java
@@ -19,8 +19,6 @@
import android.os.Parcel;
import android.os.Parcelable;
-import java.util.concurrent.atomic.AtomicInteger;
-
/**
* Defines a request for a network, made through {@link NetworkRequest.Builder} and used
* to request a network via {@link ConnectivityManager#requestNetwork} or listen for changes
diff --git a/core/java/android/net/ProxyInfo.java b/core/java/android/net/ProxyInfo.java
index a3cad77..2c90909 100644
--- a/core/java/android/net/ProxyInfo.java
+++ b/core/java/android/net/ProxyInfo.java
@@ -21,8 +21,6 @@
import android.os.Parcelable;
import android.text.TextUtils;
-import org.apache.http.client.HttpClient;
-
import java.net.InetSocketAddress;
import java.net.URLConnection;
import java.util.List;
@@ -31,8 +29,9 @@
/**
* Describes a proxy configuration.
*
- * Proxy configurations are already integrated within the Apache HTTP stack.
- * So {@link URLConnection} and {@link HttpClient} will use them automatically.
+ * Proxy configurations are already integrated within the {@code java.net} and
+ * Apache HTTP stack. So {@link URLConnection} and Apache's {@code HttpClient} will use
+ * them automatically.
*
* Other HTTP stacks will need to obtain the proxy info from
* {@link Proxy#PROXY_CHANGE_ACTION} broadcast as the extra {@link Proxy#EXTRA_PROXY_INFO}.
diff --git a/core/java/android/net/RouteInfo.java b/core/java/android/net/RouteInfo.java
index cfd20a0..90a2460 100644
--- a/core/java/android/net/RouteInfo.java
+++ b/core/java/android/net/RouteInfo.java
@@ -367,13 +367,7 @@
* @return {@code true} if the destination and prefix length cover the given address.
*/
public boolean matches(InetAddress destination) {
- if (destination == null) return false;
-
- // match the route destination and destination with prefix length
- InetAddress dstNet = NetworkUtils.getNetworkPart(destination,
- mDestination.getPrefixLength());
-
- return mDestination.getAddress().equals(dstNet);
+ return mDestination.contains(destination);
}
/**
diff --git a/core/java/android/net/StaticIpConfiguration.java b/core/java/android/net/StaticIpConfiguration.java
index 365f2b6..7f1b179 100644
--- a/core/java/android/net/StaticIpConfiguration.java
+++ b/core/java/android/net/StaticIpConfiguration.java
@@ -21,7 +21,6 @@
import android.os.Parcel;
import java.net.InetAddress;
-import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
@@ -188,6 +187,7 @@
for (InetAddress dnsServer : dnsServers) {
NetworkUtils.parcelInetAddress(dest, dnsServer, flags);
}
+ dest.writeString(domains);
}
protected static void readFromParcel(StaticIpConfiguration s, Parcel in) {
@@ -198,5 +198,6 @@
for (int i = 0; i < size; i++) {
s.dnsServers.add(NetworkUtils.unparcelInetAddress(in));
}
+ s.domains = in.readString();
}
}
diff --git a/core/jni/android_net_NetUtils.cpp b/core/jni/android_net_NetUtils.cpp
index 52da137..60e8ed0 100644
--- a/core/jni/android_net_NetUtils.cpp
+++ b/core/jni/android_net_NetUtils.cpp
@@ -19,7 +19,6 @@
#include "jni.h"
#include "JNIHelp.h"
#include "NetdClient.h"
-#include "resolv_netid.h"
#include <utils/misc.h>
#include <android_runtime/AndroidRuntime.h>
#include <utils/Log.h>
@@ -34,6 +33,8 @@
#include <netinet/udp.h>
#include <cutils/properties.h>
+#include "core_jni_helpers.h"
+
extern "C" {
int ifc_enable(const char *ifname);
int ifc_disable(const char *ifname);
@@ -315,27 +316,26 @@
int register_android_net_NetworkUtils(JNIEnv* env)
{
- jclass dhcpResultsClass = env->FindClass("android/net/DhcpResults");
- LOG_FATAL_IF(dhcpResultsClass == NULL, "Unable to find class android/net/DhcpResults");
- dhcpResultsFieldIds.clear =
- env->GetMethodID(dhcpResultsClass, "clear", "()V");
- dhcpResultsFieldIds.setIpAddress =
- env->GetMethodID(dhcpResultsClass, "setIpAddress", "(Ljava/lang/String;I)Z");
- dhcpResultsFieldIds.setGateway =
- env->GetMethodID(dhcpResultsClass, "setGateway", "(Ljava/lang/String;)Z");
- dhcpResultsFieldIds.addDns =
- env->GetMethodID(dhcpResultsClass, "addDns", "(Ljava/lang/String;)Z");
- dhcpResultsFieldIds.setDomains =
- env->GetMethodID(dhcpResultsClass, "setDomains", "(Ljava/lang/String;)V");
- dhcpResultsFieldIds.setServerAddress =
- env->GetMethodID(dhcpResultsClass, "setServerAddress", "(Ljava/lang/String;)Z");
- dhcpResultsFieldIds.setLeaseDuration =
- env->GetMethodID(dhcpResultsClass, "setLeaseDuration", "(I)V");
- dhcpResultsFieldIds.setVendorInfo =
- env->GetMethodID(dhcpResultsClass, "setVendorInfo", "(Ljava/lang/String;)V");
+ jclass dhcpResultsClass = FindClassOrDie(env, "android/net/DhcpResults");
- return AndroidRuntime::registerNativeMethods(env,
- NETUTILS_PKG_NAME, gNetworkUtilMethods, NELEM(gNetworkUtilMethods));
+ dhcpResultsFieldIds.clear = GetMethodIDOrDie(env, dhcpResultsClass, "clear", "()V");
+ dhcpResultsFieldIds.setIpAddress =GetMethodIDOrDie(env, dhcpResultsClass, "setIpAddress",
+ "(Ljava/lang/String;I)Z");
+ dhcpResultsFieldIds.setGateway = GetMethodIDOrDie(env, dhcpResultsClass, "setGateway",
+ "(Ljava/lang/String;)Z");
+ dhcpResultsFieldIds.addDns = GetMethodIDOrDie(env, dhcpResultsClass, "addDns",
+ "(Ljava/lang/String;)Z");
+ dhcpResultsFieldIds.setDomains = GetMethodIDOrDie(env, dhcpResultsClass, "setDomains",
+ "(Ljava/lang/String;)V");
+ dhcpResultsFieldIds.setServerAddress = GetMethodIDOrDie(env, dhcpResultsClass,
+ "setServerAddress", "(Ljava/lang/String;)Z");
+ dhcpResultsFieldIds.setLeaseDuration = GetMethodIDOrDie(env, dhcpResultsClass,
+ "setLeaseDuration", "(I)V");
+ dhcpResultsFieldIds.setVendorInfo = GetMethodIDOrDie(env, dhcpResultsClass, "setVendorInfo",
+ "(Ljava/lang/String;)V");
+
+ return RegisterMethodsOrDie(env, NETUTILS_PKG_NAME, gNetworkUtilMethods,
+ NELEM(gNetworkUtilMethods));
}
}; // namespace android
diff --git a/core/tests/coretests/src/android/net/IpPrefixTest.java b/core/tests/coretests/src/android/net/IpPrefixTest.java
index cf278fb..fcc6389 100644
--- a/core/tests/coretests/src/android/net/IpPrefixTest.java
+++ b/core/tests/coretests/src/android/net/IpPrefixTest.java
@@ -29,6 +29,10 @@
public class IpPrefixTest extends TestCase {
+ private static InetAddress Address(String addr) {
+ return InetAddress.parseNumericAddress(addr);
+ }
+
// Explicitly cast everything to byte because "error: possible loss of precision".
private static final byte[] IPV4_BYTES = { (byte) 192, (byte) 0, (byte) 2, (byte) 4};
private static final byte[] IPV6_BYTES = {
@@ -209,6 +213,34 @@
}
@SmallTest
+ public void testContains() {
+ IpPrefix p = new IpPrefix("2001:db8:f00::ace:d00d/127");
+ assertTrue(p.contains(Address("2001:db8:f00::ace:d00c")));
+ assertTrue(p.contains(Address("2001:db8:f00::ace:d00d")));
+ assertFalse(p.contains(Address("2001:db8:f00::ace:d00e")));
+ assertFalse(p.contains(Address("2001:db8:f00::bad:d00d")));
+ assertFalse(p.contains(Address("2001:4868:4860::8888")));
+ assertFalse(p.contains(null));
+ assertFalse(p.contains(Address("8.8.8.8")));
+
+ p = new IpPrefix("192.0.2.0/23");
+ assertTrue(p.contains(Address("192.0.2.43")));
+ assertTrue(p.contains(Address("192.0.3.21")));
+ assertFalse(p.contains(Address("192.0.0.21")));
+ assertFalse(p.contains(Address("8.8.8.8")));
+ assertFalse(p.contains(Address("2001:4868:4860::8888")));
+
+ IpPrefix ipv6Default = new IpPrefix("::/0");
+ assertTrue(ipv6Default.contains(Address("2001:db8::f00")));
+ assertFalse(ipv6Default.contains(Address("192.0.2.1")));
+
+ IpPrefix ipv4Default = new IpPrefix("0.0.0.0/0");
+ assertTrue(ipv4Default.contains(Address("255.255.255.255")));
+ assertTrue(ipv4Default.contains(Address("192.0.2.1")));
+ assertFalse(ipv4Default.contains(Address("2001:db8::f00")));
+ }
+
+ @SmallTest
public void testHashCode() {
IpPrefix p;
int oldCode = -1;
diff --git a/core/tests/coretests/src/android/net/NetworkStatsTest.java b/core/tests/coretests/src/android/net/NetworkStatsTest.java
index 9ee4e20..a470de1 100644
--- a/core/tests/coretests/src/android/net/NetworkStatsTest.java
+++ b/core/tests/coretests/src/android/net/NetworkStatsTest.java
@@ -18,6 +18,8 @@
import static android.net.NetworkStats.SET_DEFAULT;
import static android.net.NetworkStats.SET_FOREGROUND;
+import static android.net.NetworkStats.SET_DBG_VPN_IN;
+import static android.net.NetworkStats.SET_DBG_VPN_OUT;
import static android.net.NetworkStats.SET_ALL;
import static android.net.NetworkStats.IFACE_ALL;
import static android.net.NetworkStats.TAG_NONE;
@@ -320,6 +322,92 @@
red.combineAllValues(blue);
}
+ public void testMigrateTun() throws Exception {
+ final int tunUid = 10030;
+ final String tunIface = "tun0";
+ final String underlyingIface = "wlan0";
+ final int testTag1 = 8888;
+ NetworkStats delta = new NetworkStats(TEST_START, 17)
+ .addValues(tunIface, 10100, SET_DEFAULT, TAG_NONE, 39605L, 46L, 12259L, 55L, 0L)
+ .addValues(tunIface, 10100, SET_FOREGROUND, TAG_NONE, 0L, 0L, 0L, 0L, 0L)
+ .addValues(tunIface, 10120, SET_DEFAULT, TAG_NONE, 72667L, 197L, 43909L, 241L, 0L)
+ .addValues(tunIface, 10120, SET_FOREGROUND, TAG_NONE, 9297L, 17L, 4128L, 21L, 0L)
+ // VPN package also uses some traffic through unprotected network.
+ .addValues(tunIface, tunUid, SET_DEFAULT, TAG_NONE, 4983L, 10L, 1801L, 12L, 0L)
+ .addValues(tunIface, tunUid, SET_FOREGROUND, TAG_NONE, 0L, 0L, 0L, 0L, 0L)
+ // Tag entries
+ .addValues(tunIface, 10120, SET_DEFAULT, testTag1, 21691L, 41L, 13820L, 51L, 0L)
+ .addValues(tunIface, 10120, SET_FOREGROUND, testTag1, 1281L, 2L, 665L, 2L, 0L)
+ // Irrelevant entries
+ .addValues(TEST_IFACE, 10100, SET_DEFAULT, TAG_NONE, 1685L, 5L, 2070L, 6L, 0L)
+ // Underlying Iface entries
+ .addValues(underlyingIface, 10100, SET_DEFAULT, TAG_NONE, 5178L, 8L, 2139L, 11L, 0L)
+ .addValues(underlyingIface, 10100, SET_FOREGROUND, TAG_NONE, 0L, 0L, 0L, 0L, 0L)
+ .addValues(underlyingIface, tunUid, SET_DEFAULT, TAG_NONE, 149873L, 287L,
+ 59217L /* smaller than sum(tun0) */, 299L /* smaller than sum(tun0) */, 0L)
+ .addValues(underlyingIface, tunUid, SET_FOREGROUND, TAG_NONE, 0L, 0L, 0L, 0L, 0L);
+
+ assertTrue(delta.migrateTun(tunUid, tunIface, underlyingIface));
+ assertEquals(21, delta.size());
+
+ // tunIface and TEST_IFACE entries are not changed.
+ assertValues(delta, 0, tunIface, 10100, SET_DEFAULT, TAG_NONE,
+ 39605L, 46L, 12259L, 55L, 0L);
+ assertValues(delta, 1, tunIface, 10100, SET_FOREGROUND, TAG_NONE, 0L, 0L, 0L, 0L, 0L);
+ assertValues(delta, 2, tunIface, 10120, SET_DEFAULT, TAG_NONE,
+ 72667L, 197L, 43909L, 241L, 0L);
+ assertValues(delta, 3, tunIface, 10120, SET_FOREGROUND, TAG_NONE,
+ 9297L, 17L, 4128L, 21L, 0L);
+ assertValues(delta, 4, tunIface, tunUid, SET_DEFAULT, TAG_NONE,
+ 4983L, 10L, 1801L, 12L, 0L);
+ assertValues(delta, 5, tunIface, tunUid, SET_FOREGROUND, TAG_NONE, 0L, 0L, 0L, 0L, 0L);
+ assertValues(delta, 6, tunIface, 10120, SET_DEFAULT, testTag1,
+ 21691L, 41L, 13820L, 51L, 0L);
+ assertValues(delta, 7, tunIface, 10120, SET_FOREGROUND, testTag1, 1281L, 2L, 665L, 2L, 0L);
+ assertValues(delta, 8, TEST_IFACE, 10100, SET_DEFAULT, TAG_NONE, 1685L, 5L, 2070L, 6L, 0L);
+
+ // Existing underlying Iface entries are updated
+ assertValues(delta, 9, underlyingIface, 10100, SET_DEFAULT, TAG_NONE,
+ 44783L, 54L, 13829L, 60L, 0L);
+ assertValues(delta, 10, underlyingIface, 10100, SET_FOREGROUND, TAG_NONE,
+ 0L, 0L, 0L, 0L, 0L);
+
+ // VPN underlying Iface entries are updated
+ assertValues(delta, 11, underlyingIface, tunUid, SET_DEFAULT, TAG_NONE,
+ 28304L, 27L, 1719L, 12L, 0L);
+ assertValues(delta, 12, underlyingIface, tunUid, SET_FOREGROUND, TAG_NONE,
+ 0L, 0L, 0L, 0L, 0L);
+
+ // New entries are added for new application's underlying Iface traffic
+ assertContains(delta, underlyingIface, 10120, SET_DEFAULT, TAG_NONE,
+ 72667L, 197L, 41872l, 219L, 0L);
+ assertContains(delta, underlyingIface, 10120, SET_FOREGROUND, TAG_NONE,
+ 9297L, 17L, 3936, 19L, 0L);
+ assertContains(delta, underlyingIface, 10120, SET_DEFAULT, testTag1,
+ 21691L, 41L, 13179L, 46L, 0L);
+ assertContains(delta, underlyingIface, 10120, SET_FOREGROUND, testTag1,
+ 1281L, 2L, 634L, 1L, 0L);
+
+ // New entries are added for debug purpose
+ assertContains(delta, underlyingIface, 10100, SET_DBG_VPN_IN, TAG_NONE,
+ 39605L, 46L, 11690, 49, 0);
+ assertContains(delta, underlyingIface, 10120, SET_DBG_VPN_IN, TAG_NONE,
+ 81964, 214, 45808, 238, 0);
+ assertContains(delta, underlyingIface, tunUid, SET_DBG_VPN_IN, TAG_NONE,
+ 4983, 10, 1717, 10, 0);
+ assertContains(delta, underlyingIface, tunUid, SET_DBG_VPN_OUT, TAG_NONE,
+ 126552, 270, 59215, 297, 0);
+
+ }
+
+ private static void assertContains(NetworkStats stats, String iface, int uid, int set,
+ int tag, long rxBytes, long rxPackets, long txBytes, long txPackets, long operations) {
+ int index = stats.findIndex(iface, uid, set, tag);
+ assertTrue(index != -1);
+ assertValues(stats, index, iface, uid, set, tag,
+ rxBytes, rxPackets, txBytes, txPackets, operations);
+ }
+
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/core/tests/coretests/src/android/net/RouteInfoTest.java b/core/tests/coretests/src/android/net/RouteInfoTest.java
index 0b88bc7..831fefd 100644
--- a/core/tests/coretests/src/android/net/RouteInfoTest.java
+++ b/core/tests/coretests/src/android/net/RouteInfoTest.java
@@ -90,6 +90,7 @@
assertFalse(r.matches(Address("2001:db8:f00::ace:d00e")));
assertFalse(r.matches(Address("2001:db8:f00::bad:d00d")));
assertFalse(r.matches(Address("2001:4868:4860::8888")));
+ assertFalse(r.matches(Address("8.8.8.8")));
r = new PatchedRouteInfo(Prefix("192.0.2.0/23"), null, "wlan0");
assertTrue(r.matches(Address("192.0.2.43")));
diff --git a/core/tests/coretests/src/android/net/StaticIpConfigurationTest.java b/core/tests/coretests/src/android/net/StaticIpConfigurationTest.java
new file mode 100644
index 0000000..59f780f
--- /dev/null
+++ b/core/tests/coretests/src/android/net/StaticIpConfigurationTest.java
@@ -0,0 +1,225 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net;
+
+import android.net.IpPrefix;
+import android.net.LinkAddress;
+import android.net.RouteInfo;
+import android.net.StaticIpConfiguration;
+import android.os.Parcel;
+
+import java.net.InetAddress;
+import java.util.HashSet;
+
+import junit.framework.TestCase;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import static org.junit.Assert.*;
+
+
+public class StaticIpConfigurationTest extends TestCase {
+
+ private static final String ADDRSTR = "192.0.2.2/25";
+ private static final LinkAddress ADDR = new LinkAddress(ADDRSTR);
+ private static final InetAddress GATEWAY = IpAddress("192.0.2.1");
+ private static final InetAddress OFFLINKGATEWAY = IpAddress("192.0.2.129");
+ private static final InetAddress DNS1 = IpAddress("8.8.8.8");
+ private static final InetAddress DNS2 = IpAddress("8.8.4.4");
+ private static final InetAddress DNS3 = IpAddress("4.2.2.2");
+ private static final String IFACE = "eth0";
+
+ private static InetAddress IpAddress(String addr) {
+ return InetAddress.parseNumericAddress(addr);
+ }
+
+ private void checkEmpty(StaticIpConfiguration s) {
+ assertNull(s.ipAddress);
+ assertNull(s.gateway);
+ assertNull(s.domains);
+ assertEquals(0, s.dnsServers.size());
+ }
+
+ private boolean isEqual(StaticIpConfiguration s1, StaticIpConfiguration s2) {
+ return s1.equals(s2);
+ }
+
+ private void assertEquals(StaticIpConfiguration s1, StaticIpConfiguration s2) {
+ assertTrue(isEqual(s1, s2));
+ }
+
+ private void assertNotEquals(StaticIpConfiguration s1, StaticIpConfiguration s2) {
+ assertFalse(isEqual(s1, s2));
+ }
+
+ private StaticIpConfiguration makeTestObject() {
+ StaticIpConfiguration s = new StaticIpConfiguration();
+ s.ipAddress = ADDR;
+ s.gateway = GATEWAY;
+ s.dnsServers.add(DNS1);
+ s.dnsServers.add(DNS2);
+ s.dnsServers.add(DNS3);
+ s.domains = "google.com";
+ return s;
+ }
+
+ @SmallTest
+ public void testConstructor() {
+ StaticIpConfiguration s = new StaticIpConfiguration();
+ checkEmpty(s);
+ }
+
+ @SmallTest
+ public void testCopyAndClear() {
+ StaticIpConfiguration empty = new StaticIpConfiguration((StaticIpConfiguration) null);
+ checkEmpty(empty);
+
+ StaticIpConfiguration s1 = makeTestObject();
+ StaticIpConfiguration s2 = new StaticIpConfiguration(s1);
+ assertEquals(s1, s2);
+ s2.clear();
+ assertEquals(empty, s2);
+ }
+
+ @SmallTest
+ public void testHashCodeAndEquals() {
+ HashSet<Integer> hashCodes = new HashSet();
+ hashCodes.add(0);
+
+ StaticIpConfiguration s = new StaticIpConfiguration();
+ // Check that this hash code is nonzero and different from all the ones seen so far.
+ assertTrue(hashCodes.add(s.hashCode()));
+
+ s.ipAddress = ADDR;
+ assertTrue(hashCodes.add(s.hashCode()));
+
+ s.gateway = GATEWAY;
+ assertTrue(hashCodes.add(s.hashCode()));
+
+ s.dnsServers.add(DNS1);
+ assertTrue(hashCodes.add(s.hashCode()));
+
+ s.dnsServers.add(DNS2);
+ assertTrue(hashCodes.add(s.hashCode()));
+
+ s.dnsServers.add(DNS3);
+ assertTrue(hashCodes.add(s.hashCode()));
+
+ s.domains = "example.com";
+ assertTrue(hashCodes.add(s.hashCode()));
+
+ assertFalse(s.equals(null));
+ assertEquals(s, s);
+
+ StaticIpConfiguration s2 = new StaticIpConfiguration(s);
+ assertEquals(s, s2);
+
+ s.ipAddress = new LinkAddress(DNS1, 32);
+ assertNotEquals(s, s2);
+
+ s2 = new StaticIpConfiguration(s);
+ s.domains = "foo";
+ assertNotEquals(s, s2);
+
+ s2 = new StaticIpConfiguration(s);
+ s.gateway = DNS2;
+ assertNotEquals(s, s2);
+
+ s2 = new StaticIpConfiguration(s);
+ s.dnsServers.add(DNS3);
+ assertNotEquals(s, s2);
+ }
+
+ @SmallTest
+ public void testToLinkProperties() {
+ LinkProperties expected = new LinkProperties();
+ expected.setInterfaceName(IFACE);
+
+ StaticIpConfiguration s = new StaticIpConfiguration();
+ assertEquals(expected, s.toLinkProperties(IFACE));
+
+ final RouteInfo connectedRoute = new RouteInfo(new IpPrefix(ADDRSTR), null, IFACE);
+ s.ipAddress = ADDR;
+ expected.addLinkAddress(ADDR);
+ expected.addRoute(connectedRoute);
+ assertEquals(expected, s.toLinkProperties(IFACE));
+
+ s.gateway = GATEWAY;
+ RouteInfo defaultRoute = new RouteInfo(new IpPrefix("0.0.0.0/0"), GATEWAY, IFACE);
+ expected.addRoute(defaultRoute);
+ assertEquals(expected, s.toLinkProperties(IFACE));
+
+ s.gateway = OFFLINKGATEWAY;
+ expected.removeRoute(defaultRoute);
+ defaultRoute = new RouteInfo(new IpPrefix("0.0.0.0/0"), OFFLINKGATEWAY, IFACE);
+ expected.addRoute(defaultRoute);
+
+ RouteInfo gatewayRoute = new RouteInfo(new IpPrefix("192.0.2.129/32"), null, IFACE);
+ expected.addRoute(gatewayRoute);
+ assertEquals(expected, s.toLinkProperties(IFACE));
+
+ s.dnsServers.add(DNS1);
+ expected.addDnsServer(DNS1);
+ assertEquals(expected, s.toLinkProperties(IFACE));
+
+ s.dnsServers.add(DNS2);
+ s.dnsServers.add(DNS3);
+ expected.addDnsServer(DNS2);
+ expected.addDnsServer(DNS3);
+ assertEquals(expected, s.toLinkProperties(IFACE));
+
+ s.domains = "google.com";
+ expected.setDomains("google.com");
+ assertEquals(expected, s.toLinkProperties(IFACE));
+
+ s.gateway = null;
+ expected.removeRoute(defaultRoute);
+ expected.removeRoute(gatewayRoute);
+ assertEquals(expected, s.toLinkProperties(IFACE));
+
+ // Without knowing the IP address, we don't have a directly-connected route, so we can't
+ // tell if the gateway is off-link or not and we don't add a host route. This isn't a real
+ // configuration, but we should at least not crash.
+ s.gateway = OFFLINKGATEWAY;
+ s.ipAddress = null;
+ expected.removeLinkAddress(ADDR);
+ expected.removeRoute(connectedRoute);
+ expected.addRoute(defaultRoute);
+ assertEquals(expected, s.toLinkProperties(IFACE));
+ }
+
+ private StaticIpConfiguration passThroughParcel(StaticIpConfiguration s) {
+ Parcel p = Parcel.obtain();
+ StaticIpConfiguration s2 = null;
+ try {
+ s.writeToParcel(p, 0);
+ p.setDataPosition(0);
+ s2 = StaticIpConfiguration.CREATOR.createFromParcel(p);
+ } finally {
+ p.recycle();
+ }
+ assertNotNull(s2);
+ return s2;
+ }
+
+ @SmallTest
+ public void testParceling() {
+ StaticIpConfiguration s = makeTestObject();
+ StaticIpConfiguration s2 = passThroughParcel(s);
+ assertEquals(s, s2);
+ }
+}
+
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 64a924a..12a99b0 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -16,7 +16,6 @@
package com.android.server;
-import static android.Manifest.permission.MANAGE_NETWORK_POLICY;
import static android.Manifest.permission.RECEIVE_DATA_ACTIVITY_CHANGE;
import static android.net.ConnectivityManager.CONNECTIVITY_ACTION;
import static android.net.ConnectivityManager.NETID_UNSET;
@@ -27,6 +26,7 @@
import static android.net.NetworkPolicyManager.RULE_ALLOW_ALL;
import static android.net.NetworkPolicyManager.RULE_REJECT_METERED;
+import android.annotation.Nullable;
import android.app.AlarmManager;
import android.app.Notification;
import android.app.NotificationManager;
@@ -99,6 +99,7 @@
import com.android.internal.net.LegacyVpnInfo;
import com.android.internal.net.NetworkStatsFactory;
import com.android.internal.net.VpnConfig;
+import com.android.internal.net.VpnInfo;
import com.android.internal.net.VpnProfile;
import com.android.internal.telephony.DctConstants;
import com.android.internal.util.AsyncChannel;
@@ -161,6 +162,10 @@
private static final String NETWORK_RESTORE_DELAY_PROP_NAME =
"android.telephony.apn-restore";
+ // How long to wait before putting up a "This network doesn't have an Internet connection,
+ // connect anyway?" dialog after the user selects a network that doesn't validate.
+ private static final int PROMPT_UNVALIDATED_DELAY_MS = 8 * 1000;
+
// How long to delay to removal of a pending intent based request.
// See Settings.Secure.CONNECTIVITY_RELEASE_PENDING_INTENT_DELAY_MS
private final int mReleasePendingIntentDelayMs;
@@ -324,6 +329,19 @@
*/
private static final int EVENT_RELEASE_NETWORK_REQUEST_WITH_INTENT = 27;
+ /**
+ * used to specify whether a network should be used even if unvalidated.
+ * arg1 = whether to accept the network if it's unvalidated (1 or 0)
+ * arg2 = whether to remember this choice in the future (1 or 0)
+ * obj = network
+ */
+ private static final int EVENT_SET_ACCEPT_UNVALIDATED = 28;
+
+ /**
+ * used to ask the user to confirm a connection to an unvalidated network.
+ * obj = network
+ */
+ private static final int EVENT_PROMPT_UNVALIDATED = 29;
/** Handler used for internal events. */
final private InternalHandler mHandler;
@@ -1511,7 +1529,7 @@
NetworkCapabilities.TRANSPORT_WIFI)) {
timeout = Settings.Global.getInt(mContext.getContentResolver(),
Settings.Global.DATA_ACTIVITY_TIMEOUT_WIFI,
- 0);
+ 5);
type = ConnectivityManager.TYPE_WIFI;
} else {
// do not track any other networks
@@ -1867,6 +1885,7 @@
loge("ERROR: created network explicitly selected.");
}
nai.networkMisc.explicitlySelected = true;
+ nai.networkMisc.acceptUnvalidated = (boolean) msg.obj;
break;
}
case NetworkMonitor.EVENT_NETWORK_TESTED: {
@@ -1890,6 +1909,9 @@
android.net.NetworkAgent.CMD_REPORT_NETWORK_STATUS,
(valid ? NetworkAgent.VALID_NETWORK : NetworkAgent.INVALID_NETWORK),
0, null);
+
+ // TODO: trigger a NetworkCapabilities update so that the dialog can know
+ // that the network is now validated and close itself.
}
break;
}
@@ -2049,6 +2071,8 @@
ReapUnvalidatedNetworks.DONT_REAP);
}
}
+ NetworkFactoryInfo nfi = mNetworkFactoryInfos.remove(msg.replyTo);
+ if (DBG && nfi != null) log("unregisterNetworkFactory for " + nfi.name);
}
// If this method proves to be too slow then we can maintain a separate
@@ -2251,6 +2275,91 @@
}
}
+ public void setAcceptUnvalidated(Network network, boolean accept, boolean always) {
+ enforceConnectivityInternalPermission();
+ mHandler.sendMessage(mHandler.obtainMessage(EVENT_SET_ACCEPT_UNVALIDATED,
+ accept ? 1 : 0, always ? 1: 0, network));
+ }
+
+ private void handleSetAcceptUnvalidated(Network network, boolean accept, boolean always) {
+ if (DBG) log("handleSetAcceptUnvalidated network=" + network +
+ " accept=" + accept + " always=" + always);
+
+ NetworkAgentInfo nai = getNetworkAgentInfoForNetwork(network);
+ if (nai == null) {
+ // Nothing to do.
+ return;
+ }
+
+ if (nai.everValidated) {
+ // The network validated while the dialog box was up. Don't make any changes. There's a
+ // TODO in the dialog code to make it go away if the network validates; once that's
+ // implemented, taking action here will be confusing.
+ return;
+ }
+
+ if (!nai.networkMisc.explicitlySelected) {
+ Slog.wtf(TAG, "BUG: setAcceptUnvalidated non non-explicitly selected network");
+ }
+
+ if (accept != nai.networkMisc.acceptUnvalidated) {
+ int oldScore = nai.getCurrentScore();
+ nai.networkMisc.acceptUnvalidated = accept;
+ rematchAllNetworksAndRequests(nai, oldScore);
+ sendUpdatedScoreToFactories(nai);
+ }
+
+ if (always) {
+ nai.asyncChannel.sendMessage(
+ NetworkAgent.CMD_SAVE_ACCEPT_UNVALIDATED, accept ? 1 : 0);
+ }
+
+ // TODO: should we also disconnect from the network if accept is false?
+ }
+
+ private void scheduleUnvalidatedPrompt(NetworkAgentInfo nai) {
+ mHandler.sendMessageDelayed(
+ mHandler.obtainMessage(EVENT_PROMPT_UNVALIDATED, nai.network),
+ PROMPT_UNVALIDATED_DELAY_MS);
+ }
+
+ private void handlePromptUnvalidated(Network network) {
+ NetworkAgentInfo nai = getNetworkAgentInfoForNetwork(network);
+
+ // Only prompt if the network is unvalidated and was explicitly selected by the user, and if
+ // we haven't already been told to switch to it regardless of whether it validated or not.
+ if (nai == null || nai.everValidated ||
+ !nai.networkMisc.explicitlySelected || nai.networkMisc.acceptUnvalidated) {
+ return;
+ }
+
+ // TODO: What should we do if we've already switched to this network because we had no
+ // better option? There are two obvious alternatives.
+ //
+ // 1. Decide that there's no point prompting because this is our only usable network.
+ // However, because we didn't prompt, if later on a validated network comes along, we'll
+ // either a) silently switch to it - bad if the user wanted to connect to stay on this
+ // unvalidated network - or b) prompt the user at that later time - bad because the user
+ // might not understand why they are now being prompted.
+ //
+ // 2. Always prompt the user, even if we have no other network to use. The user could then
+ // try to find an alternative network to join (remember, if we got here, then the user
+ // selected this network manually). This is bad because the prompt isn't really very
+ // useful.
+ //
+ // For now we do #1, but we can revisit that later.
+ if (isDefaultNetwork(nai)) {
+ return;
+ }
+
+ Intent intent = new Intent(ConnectivityManager.ACTION_PROMPT_UNVALIDATED);
+ intent.putExtra(ConnectivityManager.EXTRA_NETWORK, network);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ intent.setClassName("com.android.settings",
+ "com.android.settings.wifi.WifiNoInternetDialog");
+ mContext.startActivityAsUser(intent, UserHandle.CURRENT);
+ }
+
private class InternalHandler extends Handler {
public InternalHandler(Looper looper) {
super(looper);
@@ -2321,6 +2430,14 @@
handleReleaseNetworkRequest((NetworkRequest) msg.obj, msg.arg1);
break;
}
+ case EVENT_SET_ACCEPT_UNVALIDATED: {
+ handleSetAcceptUnvalidated((Network) msg.obj, msg.arg1 != 0, msg.arg2 != 0);
+ break;
+ }
+ case EVENT_PROMPT_UNVALIDATED: {
+ handlePromptUnvalidated((Network) msg.obj);
+ break;
+ }
case EVENT_SYSTEM_READY: {
for (NetworkAgentInfo nai : mNetworkAgentInfos.values()) {
nai.networkMonitor.systemReady = true;
@@ -2812,7 +2929,6 @@
* Return the information of the ongoing legacy VPN. This method is used
* by VpnSettings and not available in ConnectivityManager. Permissions
* are checked in Vpn class.
- * @hide
*/
@Override
public LegacyVpnInfo getLegacyVpnInfo() {
@@ -2824,6 +2940,56 @@
}
/**
+ * Return the information of all ongoing VPNs. This method is used by NetworkStatsService
+ * and not available in ConnectivityManager.
+ */
+ @Override
+ public VpnInfo[] getAllVpnInfo() {
+ enforceConnectivityInternalPermission();
+ if (mLockdownEnabled) {
+ return new VpnInfo[0];
+ }
+
+ synchronized(mVpns) {
+ List<VpnInfo> infoList = new ArrayList<>();
+ for (int i = 0; i < mVpns.size(); i++) {
+ VpnInfo info = createVpnInfo(mVpns.valueAt(i));
+ if (info != null) {
+ infoList.add(info);
+ }
+ }
+ return infoList.toArray(new VpnInfo[infoList.size()]);
+ }
+ }
+
+ /**
+ * @return VPN information for accounting, or null if we can't retrieve all required
+ * information, e.g primary underlying iface.
+ */
+ @Nullable
+ private VpnInfo createVpnInfo(Vpn vpn) {
+ VpnInfo info = vpn.getVpnInfo();
+ if (info == null) {
+ return null;
+ }
+ Network[] underlyingNetworks = vpn.getUnderlyingNetworks();
+ // see VpnService.setUnderlyingNetworks()'s javadoc about how to interpret
+ // the underlyingNetworks list.
+ if (underlyingNetworks == null) {
+ NetworkAgentInfo defaultNetwork = getDefaultNetwork();
+ if (defaultNetwork != null && defaultNetwork.linkProperties != null) {
+ info.primaryUnderlyingIface = getDefaultNetwork().linkProperties.getInterfaceName();
+ }
+ } else if (underlyingNetworks.length > 0) {
+ LinkProperties linkProperties = getLinkProperties(underlyingNetworks[0]);
+ if (linkProperties != null) {
+ info.primaryUnderlyingIface = linkProperties.getInterfaceName();
+ }
+ }
+ return info.primaryUnderlyingIface == null ? null : info;
+ }
+
+ /**
* Returns the information of the ongoing VPN. This method is used by VpnDialogs and
* not available in ConnectivityManager.
* Permissions are checked in Vpn class.
@@ -2970,7 +3136,7 @@
notification.icon = icon;
notification.flags = Notification.FLAG_AUTO_CANCEL;
notification.tickerText = title;
- notification.color = mContext.getResources().getColor(
+ notification.color = mContext.getColor(
com.android.internal.R.color.system_notification_accent_color);
notification.setLatestEventInfo(mContext, title, details, notification.contentIntent);
notification.contentIntent = intent;
@@ -3298,7 +3464,7 @@
}
@Override
- public boolean requestBwUpdate(Network network) {
+ public boolean requestBandwidthUpdate(Network network) {
enforceAccessPermission();
NetworkAgentInfo nai = null;
if (network == null) {
@@ -3362,10 +3528,34 @@
getCallingUid(), 0, operation));
}
+ // In order to implement the compatibility measure for pre-M apps that call
+ // WifiManager.enableNetwork(..., true) without also binding to that network explicitly,
+ // WifiManager registers a network listen for the purpose of calling setProcessDefaultNetwork.
+ // This ensures it has permission to do so.
+ private boolean hasWifiNetworkListenPermission(NetworkCapabilities nc) {
+ if (nc == null) {
+ return false;
+ }
+ int[] transportTypes = nc.getTransportTypes();
+ if (transportTypes.length != 1 || transportTypes[0] != NetworkCapabilities.TRANSPORT_WIFI) {
+ return false;
+ }
+ try {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.ACCESS_WIFI_STATE,
+ "ConnectivityService");
+ } catch (SecurityException e) {
+ return false;
+ }
+ return true;
+ }
+
@Override
public NetworkRequest listenForNetwork(NetworkCapabilities networkCapabilities,
Messenger messenger, IBinder binder) {
- enforceAccessPermission();
+ if (!hasWifiNetworkListenPermission(networkCapabilities)) {
+ enforceAccessPermission();
+ }
NetworkRequest networkRequest = new NetworkRequest(new NetworkCapabilities(
networkCapabilities), TYPE_NONE, nextNetworkRequestId());
@@ -4097,8 +4287,10 @@
networkAgent.created = true;
updateLinkProperties(networkAgent, null);
notifyIfacesChanged();
- notifyNetworkCallbacks(networkAgent, ConnectivityManager.CALLBACK_PRECHECK);
+
networkAgent.networkMonitor.sendMessage(NetworkMonitor.CMD_NETWORK_CONNECTED);
+ scheduleUnvalidatedPrompt(networkAgent);
+
if (networkAgent.isVPN()) {
// Temporarily disable the default proxy (not global).
synchronized (mProxyLock) {
@@ -4111,9 +4303,13 @@
}
// TODO: support proxy per network.
}
+
// Consider network even though it is not yet validated.
rematchNetworkAndRequests(networkAgent, NascentState.NOT_JUST_VALIDATED,
ReapUnvalidatedNetworks.REAP);
+
+ // This has to happen after matching the requests, because callbacks are just requests.
+ notifyNetworkCallbacks(networkAgent, ConnectivityManager.CALLBACK_PRECHECK);
} else if (state == NetworkInfo.State.DISCONNECTED ||
state == NetworkInfo.State.SUSPENDED) {
networkAgent.asyncChannel.disconnect();
@@ -4268,8 +4464,39 @@
public boolean setUnderlyingNetworksForVpn(Network[] networks) {
throwIfLockdownEnabled();
int user = UserHandle.getUserId(Binder.getCallingUid());
+ boolean success;
synchronized (mVpns) {
- return mVpns.get(user).setUnderlyingNetworks(networks);
+ success = mVpns.get(user).setUnderlyingNetworks(networks);
+ }
+ if (success) {
+ notifyIfacesChanged();
+ }
+ return success;
+ }
+
+ @Override
+ public void factoryReset() {
+ enforceConnectivityInternalPermission();
+ // Turn airplane mode off
+ setAirplaneMode(false);
+
+ // Untether
+ for (String tether : getTetheredIfaces()) {
+ untether(tether);
+ }
+
+ // Turn VPN off
+ VpnConfig vpnConfig = getVpnConfig();
+ if (vpnConfig != null) {
+ if (vpnConfig.legacy) {
+ prepareVpn(VpnConfig.LEGACY_VPN, VpnConfig.LEGACY_VPN);
+ } else {
+ // Prevent this app from initiating VPN connections in the future without
+ // user intervention.
+ setVpnPackageAuthorization(false);
+
+ prepareVpn(vpnConfig.user, VpnConfig.LEGACY_VPN);
+ }
}
}
}
diff --git a/services/core/java/com/android/server/connectivity/Nat464Xlat.java b/services/core/java/com/android/server/connectivity/Nat464Xlat.java
index 3fa21d0..a9eaeee 100644
--- a/services/core/java/com/android/server/connectivity/Nat464Xlat.java
+++ b/services/core/java/com/android/server/connectivity/Nat464Xlat.java
@@ -22,16 +22,13 @@
import java.net.Inet4Address;
import android.content.Context;
-import android.net.IConnectivityManager;
import android.net.InterfaceConfiguration;
import android.net.LinkAddress;
import android.net.LinkProperties;
import android.net.NetworkAgent;
-import android.net.NetworkUtils;
import android.net.RouteInfo;
import android.os.Handler;
import android.os.Message;
-import android.os.Messenger;
import android.os.INetworkManagementService;
import android.os.RemoteException;
import android.util.Slog;
diff --git a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
index 2d1f939..8a7c902 100644
--- a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
+++ b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
@@ -71,7 +71,10 @@
private static final int UNVALIDATED_SCORE_PENALTY = 40;
// Score for explicitly connected network.
- private static final int EXPLICITLY_SELECTED_NETWORK_SCORE = 100;
+ //
+ // This ensures that a) the explicitly selected network is never trumped by anything else, and
+ // b) the explicitly selected network is never torn down.
+ private static final int MAXIMUM_NETWORK_SCORE = 100;
// The list of NetworkRequests being satisfied by this Network.
public final SparseArray<NetworkRequest> networkRequests = new SparseArray<NetworkRequest>();
@@ -123,13 +126,18 @@
// score. The NetworkScore class would provide a nice place to centralize score constants
// so they are not scattered about the transports.
- int score = currentScore;
+ // If this network is explicitly selected and the user has decided to use it even if it's
+ // unvalidated, give it the maximum score. Also give it the maximum score if it's explicitly
+ // selected and we're trying to see what its score could be. This ensures that we don't tear
+ // down an explicitly selected network before the user gets a chance to prefer it when
+ // a higher-scoring network (e.g., Ethernet) is available.
+ if (networkMisc.explicitlySelected && (networkMisc.acceptUnvalidated || pretendValidated)) {
+ return MAXIMUM_NETWORK_SCORE;
+ }
+ int score = currentScore;
if (!everValidated && !pretendValidated) score -= UNVALIDATED_SCORE_PENALTY;
if (score < 0) score = 0;
-
- if (networkMisc.explicitlySelected) score = EXPLICITLY_SELECTED_NETWORK_SCORE;
-
return score;
}
@@ -156,7 +164,9 @@
networkCapabilities + "} Score{" + getCurrentScore() + "} " +
"everValidated{" + everValidated + "} lastValidated{" + lastValidated + "} " +
"created{" + created + "} " +
- "explicitlySelected{" + networkMisc.explicitlySelected + "} }";
+ "explicitlySelected{" + networkMisc.explicitlySelected + "} " +
+ "acceptUnvalidated{" + networkMisc.acceptUnvalidated + "} " +
+ "}";
}
public String name() {
diff --git a/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java
index 00af71d..90b4f43 100644
--- a/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java
@@ -39,6 +39,7 @@
import static android.text.format.DateUtils.MINUTE_IN_MILLIS;
import static android.text.format.DateUtils.WEEK_IN_MILLIS;
import static com.android.server.net.NetworkStatsService.ACTION_NETWORK_STATS_POLL;
+import static org.easymock.EasyMock.anyInt;
import static org.easymock.EasyMock.anyLong;
import static org.easymock.EasyMock.capture;
import static org.easymock.EasyMock.createMock;
@@ -879,7 +880,7 @@
expectLastCall().anyTimes();
mAlarmManager.set(eq(AlarmManager.ELAPSED_REALTIME), anyLong(), anyLong(), anyLong(),
- isA(PendingIntent.class), isA(WorkSource.class),
+ anyInt(), isA(PendingIntent.class), isA(WorkSource.class),
isA(AlarmManager.AlarmClockInfo.class));
expectLastCall().atLeastOnce();