resolved conflicts for merge of 248d8d2c to master
Change-Id: Idd0ad9b1504fddf68c4c4cc04731de1eddd204b3
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 697bde9..f920874 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -465,6 +465,21 @@
}
/**
+ * Checks if the given network type is backed by a Wi-Fi radio.
+ *
+ * @hide
+ */
+ public static boolean isNetworkTypeWifi(int networkType) {
+ switch (networkType) {
+ case TYPE_WIFI:
+ case TYPE_WIFI_P2P:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ /**
* Specifies the preferred network type. When the device has more
* than one type available the preferred network type will be used.
* Note that this made sense when we only had 2 network types,
diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl
index 4600c1a..d6a3e37 100644
--- a/core/java/android/net/IConnectivityManager.aidl
+++ b/core/java/android/net/IConnectivityManager.aidl
@@ -37,6 +37,9 @@
/** {@hide} */
interface IConnectivityManager
{
+ // Keep this in sync with framework/native/services/connectivitymanager/ConnectivityManager.h
+ void markSocketAsUser(in ParcelFileDescriptor socket, int uid);
+
void setNetworkPreference(int pref);
int getNetworkPreference();
diff --git a/core/java/android/net/NetworkUtils.java b/core/java/android/net/NetworkUtils.java
index 4ab479e..b24d396 100644
--- a/core/java/android/net/NetworkUtils.java
+++ b/core/java/android/net/NetworkUtils.java
@@ -21,6 +21,7 @@
import java.net.Inet6Address;
import java.net.UnknownHostException;
import java.util.Collection;
+import java.util.Locale;
import android.util.Log;
@@ -103,6 +104,11 @@
public native static String getDhcpError();
/**
+ * Set the SO_MARK of {@code socketfd} to {@code mark}
+ */
+ public native static void markSocket(int socketfd, int mark);
+
+ /**
* Convert a IPv4 address from an integer to an InetAddress.
* @param hostAddress an int corresponding to the IPv4 address in network byte order
*/
@@ -223,7 +229,7 @@
public static InetAddress hexToInet6Address(String addrHexString)
throws IllegalArgumentException {
try {
- return numericToInetAddress(String.format("%s:%s:%s:%s:%s:%s:%s:%s",
+ return numericToInetAddress(String.format(Locale.US, "%s:%s:%s:%s:%s:%s:%s:%s",
addrHexString.substring(0,4), addrHexString.substring(4,8),
addrHexString.substring(8,12), addrHexString.substring(12,16),
addrHexString.substring(16,20), addrHexString.substring(20,24),
diff --git a/core/jni/android_net_NetUtils.cpp b/core/jni/android_net_NetUtils.cpp
index faae11e..526159f 100644
--- a/core/jni/android_net_NetUtils.cpp
+++ b/core/jni/android_net_NetUtils.cpp
@@ -17,6 +17,7 @@
#define LOG_TAG "NetUtils"
#include "jni.h"
+#include "JNIHelp.h"
#include <utils/misc.h>
#include <android_runtime/AndroidRuntime.h>
#include <utils/Log.h>
@@ -239,6 +240,13 @@
return env->NewStringUTF(::dhcp_get_errmsg());
}
+static void android_net_utils_markSocket(JNIEnv *env, jobject thiz, jint socket, jint mark)
+{
+ if (setsockopt(socket, SOL_SOCKET, SO_MARK, &mark, sizeof(mark)) < 0) {
+ jniThrowException(env, "java/lang/IllegalStateException", "Error marking socket");
+ }
+}
+
// ----------------------------------------------------------------------------
/*
@@ -255,6 +263,7 @@
{ "stopDhcp", "(Ljava/lang/String;)Z", (void *)android_net_utils_stopDhcp },
{ "releaseDhcpLease", "(Ljava/lang/String;)Z", (void *)android_net_utils_releaseDhcpLease },
{ "getDhcpError", "()Ljava/lang/String;", (void*) android_net_utils_getDhcpError },
+ { "markSocket", "(II)V", (void*) android_net_utils_markSocket },
};
int register_android_net_NetworkUtils(JNIEnv* env)
diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java
index 54bcdcb..2231e60 100644
--- a/services/java/com/android/server/ConnectivityService.java
+++ b/services/java/com/android/server/ConnectivityService.java
@@ -97,6 +97,7 @@
import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.util.Slog;
+import android.util.SparseArray;
import android.util.SparseIntArray;
import android.util.Xml;
@@ -110,6 +111,7 @@
import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.XmlUtils;
import com.android.server.am.BatteryStatsService;
+import com.android.server.connectivity.DataConnectionStats;
import com.android.server.connectivity.Nat464Xlat;
import com.android.server.connectivity.Tethering;
import com.android.server.connectivity.Vpn;
@@ -118,6 +120,8 @@
import com.google.android.collect.Lists;
import com.google.android.collect.Sets;
+import com.android.internal.annotations.GuardedBy;
+
import dalvik.system.DexClassLoader;
import org.xmlpull.v1.XmlPullParser;
@@ -179,7 +183,8 @@
private KeyStore mKeyStore;
- private Vpn mVpn;
+ @GuardedBy("mVpns")
+ private final SparseArray<Vpn> mVpns = new SparseArray<Vpn>();
private VpnCallback mVpnCallback = new VpnCallback();
private boolean mLockdownEnabled;
@@ -380,6 +385,7 @@
// the set of network types that can only be enabled by system/sig apps
List mProtectedNetworks;
+ private DataConnectionStats mDataConnectionStats;
private AtomicInteger mEnableFailFastMobileDataTag = new AtomicInteger(0);
TelephonyManager mTelephonyManager;
@@ -593,10 +599,13 @@
mTethering.getTetherableWifiRegexs().length != 0 ||
mTethering.getTetherableBluetoothRegexs().length != 0) &&
mTethering.getUpstreamIfaceTypes().length != 0);
+ //set up the listener for user state for creating user VPNs
- mVpn = new Vpn(mContext, mVpnCallback, mNetd, this);
- mVpn.startMonitoring(mContext, mTrackerHandler);
-
+ IntentFilter intentFilter = new IntentFilter();
+ intentFilter.addAction(Intent.ACTION_USER_STARTING);
+ intentFilter.addAction(Intent.ACTION_USER_STOPPING);
+ mContext.registerReceiverAsUser(
+ mUserIntentReceiver, UserHandle.ALL, intentFilter, null, null);
mClat = new Nat464Xlat(mContext, mNetd, this, mTrackerHandler);
try {
@@ -616,6 +625,9 @@
mCaptivePortalTracker = CaptivePortalTracker.makeCaptivePortalTracker(mContext, this);
loadGlobalProxy();
+
+ mDataConnectionStats = new DataConnectionStats(mContext);
+ mDataConnectionStats.startMonitoring();
}
/**
@@ -1749,6 +1761,16 @@
"ConnectivityService");
}
+ private void enforceMarkNetworkSocketPermission() {
+ //Media server special case
+ if (Binder.getCallingUid() == Process.MEDIA_UID) {
+ return;
+ }
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.MARK_NETWORK_SOCKET,
+ "ConnectivityService");
+ }
+
/**
* Handle a {@code DISCONNECTED} event. If this pertains to the non-active
* network, we ignore it. If it is for the active network, we send out a
@@ -2320,7 +2342,11 @@
// Tell VPN the interface is down. It is a temporary
// but effective fix to make VPN aware of the change.
if ((resetMask & NetworkUtils.RESET_IPV4_ADDRESSES) != 0) {
- mVpn.interfaceStatusChanged(iface, false);
+ synchronized(mVpns) {
+ for (int i = 0; i < mVpns.size(); i++) {
+ mVpns.valueAt(i).interfaceStatusChanged(iface, false);
+ }
+ }
}
}
if (resetDns) {
@@ -2577,7 +2603,6 @@
try {
mNetd.setDnsServersForInterface(iface, NetworkUtils.makeStrings(dnses), domains);
- mNetd.setDefaultInterfaceForDns(iface);
for (InetAddress dns : dnses) {
++last;
String key = "net.dns" + last;
@@ -3207,6 +3232,10 @@
Settings.Global.HTTP_PROXY);
if (!TextUtils.isEmpty(proxy)) {
String data[] = proxy.split(":");
+ if (data.length == 0) {
+ return;
+ }
+
String proxyHost = data[0];
int proxyPort = 8080;
if (data.length > 1) {
@@ -3218,6 +3247,8 @@
}
ProxyProperties p = new ProxyProperties(data[0], proxyPort, "");
setGlobalProxy(p);
+ } else {
+ setGlobalProxy(null);
}
}
@@ -3317,8 +3348,12 @@
throwIfLockdownEnabled();
try {
int type = mActiveDefaultNetwork;
+ int user = UserHandle.getUserId(Binder.getCallingUid());
if (ConnectivityManager.isNetworkTypeValid(type) && mNetTrackers[type] != null) {
- mVpn.protect(socket, mNetTrackers[type].getLinkProperties().getInterfaceName());
+ synchronized(mVpns) {
+ mVpns.get(user).protect(socket,
+ mNetTrackers[type].getLinkProperties().getInterfaceName());
+ }
return true;
}
} catch (Exception e) {
@@ -3342,7 +3377,27 @@
@Override
public boolean prepareVpn(String oldPackage, String newPackage) {
throwIfLockdownEnabled();
- return mVpn.prepare(oldPackage, newPackage);
+ int user = UserHandle.getUserId(Binder.getCallingUid());
+ synchronized(mVpns) {
+ return mVpns.get(user).prepare(oldPackage, newPackage);
+ }
+ }
+
+ @Override
+ public void markSocketAsUser(ParcelFileDescriptor socket, int uid) {
+ enforceMarkNetworkSocketPermission();
+ final long token = Binder.clearCallingIdentity();
+ try {
+ int mark = mNetd.getMarkForUid(uid);
+ // Clear the mark on the socket if no mark is needed to prevent socket reuse issues
+ if (mark == -1) {
+ mark = 0;
+ }
+ NetworkUtils.markSocket(socket.getFd(), mark);
+ } catch (RemoteException e) {
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
}
/**
@@ -3355,7 +3410,10 @@
@Override
public ParcelFileDescriptor establishVpn(VpnConfig config) {
throwIfLockdownEnabled();
- return mVpn.establish(config);
+ int user = UserHandle.getUserId(Binder.getCallingUid());
+ synchronized(mVpns) {
+ return mVpns.get(user).establish(config);
+ }
}
/**
@@ -3369,7 +3427,10 @@
if (egress == null) {
throw new IllegalStateException("Missing active network connection");
}
- mVpn.startLegacyVpn(profile, mKeyStore, egress);
+ int user = UserHandle.getUserId(Binder.getCallingUid());
+ synchronized(mVpns) {
+ mVpns.get(user).startLegacyVpn(profile, mKeyStore, egress);
+ }
}
/**
@@ -3381,7 +3442,10 @@
@Override
public LegacyVpnInfo getLegacyVpnInfo() {
throwIfLockdownEnabled();
- return mVpn.getLegacyVpnInfo();
+ int user = UserHandle.getUserId(Binder.getCallingUid());
+ synchronized(mVpns) {
+ return mVpns.get(user).getLegacyVpnInfo();
+ }
}
/**
@@ -3402,7 +3466,7 @@
mHandler.obtainMessage(EVENT_VPN_STATE_CHANGED, info).sendToTarget();
}
- public void override(List<String> dnsServers, List<String> searchDomains) {
+ public void override(String iface, List<String> dnsServers, List<String> searchDomains) {
if (dnsServers == null) {
restore();
return;
@@ -3434,7 +3498,7 @@
// Apply DNS changes.
synchronized (mDnsLock) {
- updateDnsLocked("VPN", "VPN", addresses, domains);
+ updateDnsLocked("VPN", iface, addresses, domains);
mDnsOverridden = true;
}
@@ -3463,6 +3527,67 @@
}
}
}
+
+ public void protect(ParcelFileDescriptor socket) {
+ try {
+ final int mark = mNetd.getMarkForProtect();
+ NetworkUtils.markSocket(socket.getFd(), mark);
+ } catch (RemoteException e) {
+ }
+ }
+
+ public void setRoutes(String interfaze, List<RouteInfo> routes) {
+ for (RouteInfo route : routes) {
+ try {
+ mNetd.setMarkedForwardingRoute(interfaze, route);
+ } catch (RemoteException e) {
+ }
+ }
+ }
+
+ public void setMarkedForwarding(String interfaze) {
+ try {
+ mNetd.setMarkedForwarding(interfaze);
+ } catch (RemoteException e) {
+ }
+ }
+
+ public void clearMarkedForwarding(String interfaze) {
+ try {
+ mNetd.clearMarkedForwarding(interfaze);
+ } catch (RemoteException e) {
+ }
+ }
+
+ public void addUserForwarding(String interfaze, int uid) {
+ int uidStart = uid * UserHandle.PER_USER_RANGE;
+ int uidEnd = uidStart + UserHandle.PER_USER_RANGE - 1;
+ addUidForwarding(interfaze, uidStart, uidEnd);
+ }
+
+ public void clearUserForwarding(String interfaze, int uid) {
+ int uidStart = uid * UserHandle.PER_USER_RANGE;
+ int uidEnd = uidStart + UserHandle.PER_USER_RANGE - 1;
+ clearUidForwarding(interfaze, uidStart, uidEnd);
+ }
+
+ public void addUidForwarding(String interfaze, int uidStart, int uidEnd) {
+ try {
+ mNetd.setUidRangeRoute(interfaze,uidStart, uidEnd);
+ mNetd.setDnsInterfaceForUidRange(interfaze, uidStart, uidEnd);
+ } catch (RemoteException e) {
+ }
+
+ }
+
+ public void clearUidForwarding(String interfaze, int uidStart, int uidEnd) {
+ try {
+ mNetd.clearUidRangeRoute(interfaze, uidStart, uidEnd);
+ mNetd.clearDnsInterfaceForUidRange(uidStart, uidEnd);
+ } catch (RemoteException e) {
+ }
+
+ }
}
@Override
@@ -3483,7 +3608,11 @@
final String profileName = new String(mKeyStore.get(Credentials.LOCKDOWN_VPN));
final VpnProfile profile = VpnProfile.decode(
profileName, mKeyStore.get(Credentials.VPN + profileName));
- setLockdownTracker(new LockdownVpnTracker(mContext, mNetd, this, mVpn, profile));
+ int user = UserHandle.getUserId(Binder.getCallingUid());
+ synchronized(mVpns) {
+ setLockdownTracker(new LockdownVpnTracker(mContext, mNetd, this, mVpns.get(user),
+ profile));
+ }
} else {
setLockdownTracker(null);
}
@@ -3564,7 +3693,7 @@
}
@Override
- public int checkMobileProvisioning(boolean sendNotification, int suggestedTimeOutMs,
+ public int checkMobileProvisioning(final boolean sendNotification, int suggestedTimeOutMs,
final ResultReceiver resultReceiver) {
log("checkMobileProvisioning: E sendNotification=" + sendNotification
+ " suggestedTimeOutMs=" + suggestedTimeOutMs
@@ -3599,6 +3728,10 @@
log("CheckMp.onComplete: send result");
resultReceiver.send(result, null);
}
+ if (!sendNotification) {
+ log("CheckMp.onComplete: done, not sending notification");
+ return;
+ }
NetworkInfo ni =
mNetTrackers[ConnectivityManager.TYPE_MOBILE_HIPRI].getNetworkInfo();
switch(result) {
@@ -4119,4 +4252,43 @@
return url;
}
+
+ private void onUserStart(int userId) {
+ synchronized(mVpns) {
+ Vpn userVpn = mVpns.get(userId);
+ if (userVpn != null) {
+ loge("Starting user already has a VPN");
+ return;
+ }
+ userVpn = new Vpn(mContext, mVpnCallback, mNetd, this, userId);
+ mVpns.put(userId, userVpn);
+ userVpn.startMonitoring(mContext, mTrackerHandler);
+ }
+ }
+
+ private void onUserStop(int userId) {
+ synchronized(mVpns) {
+ Vpn userVpn = mVpns.get(userId);
+ if (userVpn == null) {
+ loge("Stopping user has no VPN");
+ return;
+ }
+ mVpns.delete(userId);
+ }
+ }
+
+ private BroadcastReceiver mUserIntentReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ final String action = intent.getAction();
+ final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
+ if (userId == UserHandle.USER_NULL) return;
+
+ if (Intent.ACTION_USER_STARTING.equals(action)) {
+ onUserStart(userId);
+ } else if (Intent.ACTION_USER_STOPPING.equals(action)) {
+ onUserStop(userId);
+ }
+ }
+ };
}
diff --git a/services/java/com/android/server/connectivity/Nat464Xlat.java b/services/java/com/android/server/connectivity/Nat464Xlat.java
index 59403c5..a15d678 100644
--- a/services/java/com/android/server/connectivity/Nat464Xlat.java
+++ b/services/java/com/android/server/connectivity/Nat464Xlat.java
@@ -147,17 +147,24 @@
" added, mIsRunning = " + mIsRunning + " -> true");
mIsRunning = true;
- // Get the network configuration of the clat interface, store it
- // in our link properties, and stack it on top of the interface
- // it's running on.
+ // Create the LinkProperties for the clat interface by fetching the
+ // IPv4 address for the interface and adding an IPv4 default route,
+ // then stack the LinkProperties on top of the link it's running on.
+ // Although the clat interface is a point-to-point tunnel, we don't
+ // point the route directly at the interface because some apps don't
+ // understand routes without gateways (see, e.g., http://b/9597256
+ // http://b/9597516). Instead, set the next hop of the route to the
+ // clat IPv4 address itself (for those apps, it doesn't matter what
+ // the IP of the gateway is, only that there is one).
try {
InterfaceConfiguration config = mNMService.getInterfaceConfig(iface);
+ LinkAddress clatAddress = config.getLinkAddress();
mLP.clear();
mLP.setInterfaceName(iface);
- RouteInfo ipv4Default = new RouteInfo(new LinkAddress(Inet4Address.ANY, 0), null,
- iface);
+ RouteInfo ipv4Default = new RouteInfo(new LinkAddress(Inet4Address.ANY, 0),
+ clatAddress.getAddress(), iface);
mLP.addRoute(ipv4Default);
- mLP.addLinkAddress(config.getLinkAddress());
+ mLP.addLinkAddress(clatAddress);
mTracker.addStackedLink(mLP);
Slog.i(TAG, "Adding stacked link. tracker LP: " +
mTracker.getLinkProperties());
diff --git a/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java
index cdc4d78..ed74fdf 100644
--- a/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java
@@ -878,8 +878,7 @@
mAlarmManager.remove(isA(PendingIntent.class));
expectLastCall().anyTimes();
- mAlarmManager.setInexactRepeating(
- eq(AlarmManager.ELAPSED_REALTIME), anyLong(), anyLong(), isA(PendingIntent.class));
+ mAlarmManager.set(eq(AlarmManager.ELAPSED_REALTIME), anyLong(), anyLong(), isA(PendingIntent.class), false);
expectLastCall().atLeastOnce();
mNetManager.setGlobalAlert(anyLong());