Merge "Add API for proxy configuration over VPN."
This commit is contained in:
@@ -39,12 +39,12 @@ import java.util.Locale;
|
||||
*/
|
||||
public class ProxyInfo implements Parcelable {
|
||||
|
||||
private String mHost;
|
||||
private int mPort;
|
||||
private String mExclusionList;
|
||||
private String[] mParsedExclusionList;
|
||||
private final String mHost;
|
||||
private final int mPort;
|
||||
private final String mExclusionList;
|
||||
private final String[] mParsedExclusionList;
|
||||
private final Uri mPacFileUrl;
|
||||
|
||||
private Uri mPacFileUrl;
|
||||
/**
|
||||
*@hide
|
||||
*/
|
||||
@@ -96,7 +96,8 @@ public class ProxyInfo implements Parcelable {
|
||||
public ProxyInfo(String host, int port, String exclList) {
|
||||
mHost = host;
|
||||
mPort = port;
|
||||
setExclusionList(exclList);
|
||||
mExclusionList = exclList;
|
||||
mParsedExclusionList = parseExclusionList(mExclusionList);
|
||||
mPacFileUrl = Uri.EMPTY;
|
||||
}
|
||||
|
||||
@@ -107,7 +108,8 @@ public class ProxyInfo implements Parcelable {
|
||||
public ProxyInfo(Uri pacFileUrl) {
|
||||
mHost = LOCAL_HOST;
|
||||
mPort = LOCAL_PORT;
|
||||
setExclusionList(LOCAL_EXCL_LIST);
|
||||
mExclusionList = LOCAL_EXCL_LIST;
|
||||
mParsedExclusionList = parseExclusionList(mExclusionList);
|
||||
if (pacFileUrl == null) {
|
||||
throw new NullPointerException();
|
||||
}
|
||||
@@ -121,7 +123,8 @@ public class ProxyInfo implements Parcelable {
|
||||
public ProxyInfo(String pacFileUrl) {
|
||||
mHost = LOCAL_HOST;
|
||||
mPort = LOCAL_PORT;
|
||||
setExclusionList(LOCAL_EXCL_LIST);
|
||||
mExclusionList = LOCAL_EXCL_LIST;
|
||||
mParsedExclusionList = parseExclusionList(mExclusionList);
|
||||
mPacFileUrl = Uri.parse(pacFileUrl);
|
||||
}
|
||||
|
||||
@@ -132,13 +135,22 @@ public class ProxyInfo implements Parcelable {
|
||||
public ProxyInfo(Uri pacFileUrl, int localProxyPort) {
|
||||
mHost = LOCAL_HOST;
|
||||
mPort = localProxyPort;
|
||||
setExclusionList(LOCAL_EXCL_LIST);
|
||||
mExclusionList = LOCAL_EXCL_LIST;
|
||||
mParsedExclusionList = parseExclusionList(mExclusionList);
|
||||
if (pacFileUrl == null) {
|
||||
throw new NullPointerException();
|
||||
}
|
||||
mPacFileUrl = pacFileUrl;
|
||||
}
|
||||
|
||||
private static String[] parseExclusionList(String exclusionList) {
|
||||
if (exclusionList == null) {
|
||||
return new String[0];
|
||||
} else {
|
||||
return exclusionList.toLowerCase(Locale.ROOT).split(",");
|
||||
}
|
||||
}
|
||||
|
||||
private ProxyInfo(String host, int port, String exclList, String[] parsedExclList) {
|
||||
mHost = host;
|
||||
mPort = port;
|
||||
@@ -159,6 +171,10 @@ public class ProxyInfo implements Parcelable {
|
||||
mExclusionList = source.getExclusionListAsString();
|
||||
mParsedExclusionList = source.mParsedExclusionList;
|
||||
} else {
|
||||
mHost = null;
|
||||
mPort = 0;
|
||||
mExclusionList = null;
|
||||
mParsedExclusionList = null;
|
||||
mPacFileUrl = Uri.EMPTY;
|
||||
}
|
||||
}
|
||||
@@ -214,24 +230,14 @@ public class ProxyInfo implements Parcelable {
|
||||
return mExclusionList;
|
||||
}
|
||||
|
||||
// comma separated
|
||||
private void setExclusionList(String exclusionList) {
|
||||
mExclusionList = exclusionList;
|
||||
if (mExclusionList == null) {
|
||||
mParsedExclusionList = new String[0];
|
||||
} else {
|
||||
mParsedExclusionList = exclusionList.toLowerCase(Locale.ROOT).split(",");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
public boolean isValid() {
|
||||
if (!Uri.EMPTY.equals(mPacFileUrl)) return true;
|
||||
return Proxy.PROXY_VALID == Proxy.validate(mHost == null ? "" : mHost,
|
||||
mPort == 0 ? "" : Integer.toString(mPort),
|
||||
mExclusionList == null ? "" : mExclusionList);
|
||||
mPort == 0 ? "" : Integer.toString(mPort),
|
||||
mExclusionList == null ? "" : mExclusionList);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -262,7 +268,7 @@ public class ProxyInfo implements Parcelable {
|
||||
sb.append("] ");
|
||||
sb.append(Integer.toString(mPort));
|
||||
if (mExclusionList != null) {
|
||||
sb.append(" xl=").append(mExclusionList);
|
||||
sb.append(" xl=").append(mExclusionList);
|
||||
}
|
||||
} else {
|
||||
sb.append("[ProxyProperties.mHost == null]");
|
||||
@@ -308,8 +314,8 @@ public class ProxyInfo implements Parcelable {
|
||||
*/
|
||||
public int hashCode() {
|
||||
return ((null == mHost) ? 0 : mHost.hashCode())
|
||||
+ ((null == mExclusionList) ? 0 : mExclusionList.hashCode())
|
||||
+ mPort;
|
||||
+ ((null == mExclusionList) ? 0 : mExclusionList.hashCode())
|
||||
+ mPort;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -352,8 +358,7 @@ public class ProxyInfo implements Parcelable {
|
||||
}
|
||||
String exclList = in.readString();
|
||||
String[] parsedExclList = in.readStringArray();
|
||||
ProxyInfo proxyProperties =
|
||||
new ProxyInfo(host, port, exclList, parsedExclList);
|
||||
ProxyInfo proxyProperties = new ProxyInfo(host, port, exclList, parsedExclList);
|
||||
return proxyProperties;
|
||||
}
|
||||
|
||||
|
||||
@@ -515,7 +515,8 @@ public class ConnectivityService extends IConnectivityManager.Stub
|
||||
|
||||
// A helper object to track the current default HTTP proxy. ConnectivityService needs to tell
|
||||
// the world when it changes.
|
||||
private final ProxyTracker mProxyTracker;
|
||||
@VisibleForTesting
|
||||
protected final ProxyTracker mProxyTracker;
|
||||
|
||||
final private SettingsObserver mSettingsObserver;
|
||||
|
||||
@@ -824,7 +825,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
|
||||
mPolicyManagerInternal = checkNotNull(
|
||||
LocalServices.getService(NetworkPolicyManagerInternal.class),
|
||||
"missing NetworkPolicyManagerInternal");
|
||||
mProxyTracker = new ProxyTracker(context, mHandler, EVENT_PROXY_HAS_CHANGED);
|
||||
mProxyTracker = makeProxyTracker();
|
||||
|
||||
mNetd = NetdService.getInstance();
|
||||
mKeyStore = KeyStore.getInstance();
|
||||
@@ -990,6 +991,11 @@ public class ConnectivityService extends IConnectivityManager.Stub
|
||||
deps);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
protected ProxyTracker makeProxyTracker() {
|
||||
return new ProxyTracker(mContext, mHandler, EVENT_PROXY_HAS_CHANGED);
|
||||
}
|
||||
|
||||
private static NetworkCapabilities createDefaultNetworkCapabilitiesForUid(int uid) {
|
||||
final NetworkCapabilities netCap = new NetworkCapabilities();
|
||||
netCap.addCapability(NET_CAPABILITY_INTERNET);
|
||||
@@ -3736,20 +3742,46 @@ public class ConnectivityService extends IConnectivityManager.Stub
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns information about the proxy a certain network is using. If given a null network, it
|
||||
* it will return the proxy for the bound network for the caller app or the default proxy if
|
||||
* none.
|
||||
*
|
||||
* @param network the network we want to get the proxy information for.
|
||||
* @return Proxy information if a network has a proxy configured, or otherwise null.
|
||||
*/
|
||||
@Override
|
||||
public ProxyInfo getProxyForNetwork(Network network) {
|
||||
if (network == null) return mProxyTracker.getDefaultProxy();
|
||||
final ProxyInfo globalProxy = mProxyTracker.getGlobalProxy();
|
||||
if (globalProxy != null) return globalProxy;
|
||||
if (!NetworkUtils.queryUserAccess(Binder.getCallingUid(), network.netId)) return null;
|
||||
// Don't call getLinkProperties() as it requires ACCESS_NETWORK_STATE permission, which
|
||||
// caller may not have.
|
||||
if (network == null) {
|
||||
// Get the network associated with the calling UID.
|
||||
final Network activeNetwork = getActiveNetworkForUidInternal(Binder.getCallingUid(),
|
||||
true);
|
||||
if (activeNetwork == null) {
|
||||
return null;
|
||||
}
|
||||
return getLinkPropertiesProxyInfo(activeNetwork);
|
||||
} else if (queryUserAccess(Binder.getCallingUid(), network.netId)) {
|
||||
// Don't call getLinkProperties() as it requires ACCESS_NETWORK_STATE permission, which
|
||||
// caller may not have.
|
||||
return getLinkPropertiesProxyInfo(network);
|
||||
}
|
||||
// No proxy info available if the calling UID does not have network access.
|
||||
return null;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
protected boolean queryUserAccess(int uid, int netId) {
|
||||
return NetworkUtils.queryUserAccess(uid, netId);
|
||||
}
|
||||
|
||||
private ProxyInfo getLinkPropertiesProxyInfo(Network network) {
|
||||
final NetworkAgentInfo nai = getNetworkAgentInfoForNetwork(network);
|
||||
if (nai == null) return null;
|
||||
synchronized (nai) {
|
||||
final ProxyInfo proxyInfo = nai.linkProperties.getHttpProxy();
|
||||
if (proxyInfo == null) return null;
|
||||
return new ProxyInfo(proxyInfo);
|
||||
final ProxyInfo linkHttpProxy = nai.linkProperties.getHttpProxy();
|
||||
return linkHttpProxy == null ? null : new ProxyInfo(linkHttpProxy);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3773,11 +3805,10 @@ public class ConnectivityService extends IConnectivityManager.Stub
|
||||
mProxyTracker.setDefaultProxy(proxy);
|
||||
}
|
||||
|
||||
// If the proxy has changed from oldLp to newLp, resend proxy broadcast with default proxy.
|
||||
// This method gets called when any network changes proxy, but the broadcast only ever contains
|
||||
// the default proxy (even if it hasn't changed).
|
||||
// TODO: Deprecate the broadcast extras as they aren't necessarily applicable in a multi-network
|
||||
// world where an app might be bound to a non-default network.
|
||||
// If the proxy has changed from oldLp to newLp, resend proxy broadcast. This method gets called
|
||||
// when any network changes proxy.
|
||||
// TODO: Remove usage of broadcast extras as they are deprecated and not applicable in a
|
||||
// multi-network world where an app might be bound to a non-default network.
|
||||
private void updateProxy(LinkProperties newLp, LinkProperties oldLp) {
|
||||
ProxyInfo newProxyInfo = newLp == null ? null : newLp.getHttpProxy();
|
||||
ProxyInfo oldProxyInfo = oldLp == null ? null : oldLp.getHttpProxy();
|
||||
@@ -5975,12 +6006,6 @@ public class ConnectivityService extends IConnectivityManager.Stub
|
||||
}
|
||||
scheduleUnvalidatedPrompt(networkAgent);
|
||||
|
||||
if (networkAgent.isVPN()) {
|
||||
// Temporarily disable the default proxy (not global).
|
||||
mProxyTracker.setDefaultProxyEnabled(false);
|
||||
// TODO: support proxy per network.
|
||||
}
|
||||
|
||||
// Whether a particular NetworkRequest listen should cause signal strength thresholds to
|
||||
// be communicated to a particular NetworkAgent depends only on the network's immutable,
|
||||
// capabilities, so it only needs to be done once on initial connect, not every time the
|
||||
@@ -5999,10 +6024,16 @@ public class ConnectivityService extends IConnectivityManager.Stub
|
||||
} else if (state == NetworkInfo.State.DISCONNECTED) {
|
||||
networkAgent.asyncChannel.disconnect();
|
||||
if (networkAgent.isVPN()) {
|
||||
mProxyTracker.setDefaultProxyEnabled(true);
|
||||
updateUids(networkAgent, networkAgent.networkCapabilities, null);
|
||||
}
|
||||
disconnectAndDestroyNetwork(networkAgent);
|
||||
if (networkAgent.isVPN()) {
|
||||
// As the active or bound network changes for apps, broadcast the default proxy, as
|
||||
// apps may need to update their proxy data. This is called after disconnecting from
|
||||
// VPN to make sure we do not broadcast the old proxy data.
|
||||
// TODO(b/122649188): send the broadcast only to VPN users.
|
||||
mProxyTracker.sendProxyBroadcast();
|
||||
}
|
||||
} else if ((oldInfo != null && oldInfo.getState() == NetworkInfo.State.SUSPENDED) ||
|
||||
state == NetworkInfo.State.SUSPENDED) {
|
||||
// going into or coming out of SUSPEND: re-score and notify
|
||||
|
||||
@@ -309,22 +309,4 @@ public class ProxyTracker {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable or disable the default proxy.
|
||||
*
|
||||
* This sets the flag for enabling/disabling the default proxy and sends the broadcast
|
||||
* if applicable.
|
||||
* @param enabled whether the default proxy should be enabled.
|
||||
*/
|
||||
public void setDefaultProxyEnabled(final boolean enabled) {
|
||||
synchronized (mProxyLock) {
|
||||
if (mDefaultProxyEnabled != enabled) {
|
||||
mDefaultProxyEnabled = enabled;
|
||||
if (mGlobalProxy == null && mDefaultProxy != null) {
|
||||
sendProxyBroadcast();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -123,6 +123,7 @@ import android.net.NetworkRequest;
|
||||
import android.net.NetworkSpecifier;
|
||||
import android.net.NetworkStack;
|
||||
import android.net.NetworkUtils;
|
||||
import android.net.ProxyInfo;
|
||||
import android.net.RouteInfo;
|
||||
import android.net.SocketKeepalive;
|
||||
import android.net.UidRange;
|
||||
@@ -161,6 +162,7 @@ import com.android.server.connectivity.DefaultNetworkMetrics;
|
||||
import com.android.server.connectivity.IpConnectivityMetrics;
|
||||
import com.android.server.connectivity.MockableSystemProperties;
|
||||
import com.android.server.connectivity.Nat464Xlat;
|
||||
import com.android.server.connectivity.ProxyTracker;
|
||||
import com.android.server.connectivity.Tethering;
|
||||
import com.android.server.connectivity.Vpn;
|
||||
import com.android.server.net.NetworkPinner;
|
||||
@@ -1006,6 +1008,11 @@ public class ConnectivityServiceTest {
|
||||
return mock(Tethering.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ProxyTracker makeProxyTracker() {
|
||||
return mock(ProxyTracker.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int reserveNetId() {
|
||||
while (true) {
|
||||
@@ -1028,6 +1035,11 @@ public class ConnectivityServiceTest {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean queryUserAccess(int uid, int netId) {
|
||||
return true;
|
||||
}
|
||||
|
||||
public Nat464Xlat getNat464Xlat(MockNetworkAgent mna) {
|
||||
return getNetworkAgentInfoForNetwork(mna.getNetwork()).clatd;
|
||||
}
|
||||
@@ -5132,4 +5144,84 @@ public class ConnectivityServiceTest {
|
||||
mCellNetworkAgent.sendLinkProperties(lp);
|
||||
verifyTcpBufferSizeChange(TEST_TCP_BUFFER_SIZES);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetGlobalProxyForNetwork() {
|
||||
final ProxyInfo testProxyInfo = ProxyInfo.buildDirectProxy("test", 8888);
|
||||
mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
|
||||
final Network wifiNetwork = mWiFiNetworkAgent.getNetwork();
|
||||
when(mService.mProxyTracker.getGlobalProxy()).thenReturn(testProxyInfo);
|
||||
assertEquals(testProxyInfo, mService.getProxyForNetwork(wifiNetwork));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetProxyForActiveNetwork() {
|
||||
final ProxyInfo testProxyInfo = ProxyInfo.buildDirectProxy("test", 8888);
|
||||
mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
|
||||
mWiFiNetworkAgent.connect(true);
|
||||
waitForIdle();
|
||||
assertNull(mService.getProxyForNetwork(null));
|
||||
|
||||
final LinkProperties testLinkProperties = new LinkProperties();
|
||||
testLinkProperties.setHttpProxy(testProxyInfo);
|
||||
|
||||
mWiFiNetworkAgent.sendLinkProperties(testLinkProperties);
|
||||
waitForIdle();
|
||||
|
||||
assertEquals(testProxyInfo, mService.getProxyForNetwork(null));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetProxyForVPN() {
|
||||
final ProxyInfo testProxyInfo = ProxyInfo.buildDirectProxy("test", 8888);
|
||||
|
||||
// Set up a WiFi network with no proxy
|
||||
mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
|
||||
mWiFiNetworkAgent.connect(true);
|
||||
waitForIdle();
|
||||
assertNull(mService.getProxyForNetwork(null));
|
||||
|
||||
// Set up a VPN network with a proxy
|
||||
final int uid = Process.myUid();
|
||||
final MockNetworkAgent vpnNetworkAgent = new MockNetworkAgent(TRANSPORT_VPN);
|
||||
final ArraySet<UidRange> ranges = new ArraySet<>();
|
||||
ranges.add(new UidRange(uid, uid));
|
||||
mMockVpn.setUids(ranges);
|
||||
LinkProperties testLinkProperties = new LinkProperties();
|
||||
testLinkProperties.setHttpProxy(testProxyInfo);
|
||||
vpnNetworkAgent.sendLinkProperties(testLinkProperties);
|
||||
waitForIdle();
|
||||
|
||||
// Connect to VPN with proxy
|
||||
mMockVpn.setNetworkAgent(vpnNetworkAgent);
|
||||
vpnNetworkAgent.connect(true);
|
||||
mMockVpn.connect();
|
||||
waitForIdle();
|
||||
|
||||
// Test that the VPN network returns a proxy, and the WiFi does not.
|
||||
assertEquals(testProxyInfo, mService.getProxyForNetwork(vpnNetworkAgent.getNetwork()));
|
||||
assertEquals(testProxyInfo, mService.getProxyForNetwork(null));
|
||||
assertNull(mService.getProxyForNetwork(mWiFiNetworkAgent.getNetwork()));
|
||||
|
||||
// Test that the VPN network returns no proxy when it is set to null.
|
||||
testLinkProperties.setHttpProxy(null);
|
||||
vpnNetworkAgent.sendLinkProperties(testLinkProperties);
|
||||
waitForIdle();
|
||||
assertNull(mService.getProxyForNetwork(vpnNetworkAgent.getNetwork()));
|
||||
assertNull(mService.getProxyForNetwork(null));
|
||||
|
||||
// Set WiFi proxy and check that the vpn proxy is still null.
|
||||
testLinkProperties.setHttpProxy(testProxyInfo);
|
||||
mWiFiNetworkAgent.sendLinkProperties(testLinkProperties);
|
||||
waitForIdle();
|
||||
assertNull(mService.getProxyForNetwork(null));
|
||||
|
||||
// Disconnect from VPN and check that the active network, which is now the WiFi, has the
|
||||
// correct proxy setting.
|
||||
vpnNetworkAgent.disconnect();
|
||||
waitForIdle();
|
||||
assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork());
|
||||
assertEquals(testProxyInfo, mService.getProxyForNetwork(mWiFiNetworkAgent.getNetwork()));
|
||||
assertEquals(testProxyInfo, mService.getProxyForNetwork(null));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user