Add tests for PAC proxies.
Test: this is a change that adds a test Bug: 138810051 Bug: 140610528 Change-Id: I6e7b77320f6baf32ac2f98634fa2e3231d1bf12f
This commit is contained in:
committed by
Chalard Jean
parent
c8fee363bb
commit
40b524448c
@@ -861,8 +861,7 @@ 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.
|
||||
@VisibleForTesting
|
||||
protected final ProxyTracker mProxyTracker;
|
||||
private final ProxyTracker mProxyTracker;
|
||||
|
||||
final private SettingsObserver mSettingsObserver;
|
||||
|
||||
@@ -1846,6 +1845,13 @@ public class ConnectivityService extends IConnectivityManager.Stub
|
||||
mHandler.sendEmptyMessage(EVENT_INGRESS_RATE_LIMIT_CHANGED);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
void simulateUpdateProxyInfo(@Nullable final Network network,
|
||||
@NonNull final ProxyInfo proxyInfo) {
|
||||
Message.obtain(mHandler, EVENT_PROXY_HAS_CHANGED,
|
||||
new Pair<>(network, proxyInfo)).sendToTarget();
|
||||
}
|
||||
|
||||
private void handleAlwaysOnNetworkRequest(
|
||||
NetworkRequest networkRequest, String settingName, boolean defaultValue) {
|
||||
final boolean enable = toBool(Settings.Global.getInt(
|
||||
|
||||
@@ -11276,6 +11276,212 @@ public class ConnectivityServiceTest {
|
||||
assertEquals(testProxyInfo, mService.getProxyForNetwork(null));
|
||||
}
|
||||
|
||||
/*
|
||||
* Note for maintainers about how PAC proxies are implemented in Android.
|
||||
*
|
||||
* Generally, a proxy is just a hostname and a port to which requests are sent, instead of
|
||||
* sending them directly. Most HTTP libraries know to use this protocol, and the Java
|
||||
* language has properties to help handling these :
|
||||
* https://docs.oracle.com/javase/8/docs/technotes/guides/net/proxies.html
|
||||
* Unfortunately these properties are very old and do not take multi-networking into account.
|
||||
*
|
||||
* A PAC proxy consists of a javascript file stored on a server, and the client is expected to
|
||||
* download the file and interpret the javascript for each HTTP request to know where to direct
|
||||
* it. The device must therefore run code (namely, a javascript interpreter) to interpret the
|
||||
* PAC file correctly. Most HTTP libraries do not know how to do this, since they do not
|
||||
* embark a javascript interpreter (and it would be generally unreasonable for them to do
|
||||
* so). Some apps, notably browsers, do know how to do this, but they are the exception rather
|
||||
* than the rule.
|
||||
* So to support most apps, Android embarks the javascript interpreter. When a network is
|
||||
* configured to have a PAC proxy, Android will first set the ProxyInfo object to an object
|
||||
* that contains the PAC URL (to communicate that to apps that know how to use it), then
|
||||
* download the PAC file and start a native process which will open a server on localhost,
|
||||
* and uses the interpreter inside WebView to interpret the PAC file. This server takes
|
||||
* a little bit of time to start and will listen on a random port. When the port is known,
|
||||
* the framework receives a notification and it updates the ProxyInfo in LinkProperties
|
||||
* as well as send a broadcast to inform apps. This new ProxyInfo still contains the PAC URL,
|
||||
* but it also contains "localhost" as the host and the port that the server listens to as
|
||||
* the port. This will let HTTP libraries that don't have a javascript interpreter work,
|
||||
* because they'll send the requests to this server running on localhost on the correct port,
|
||||
* and this server will do what is appropriate with each request according to the PAC file.
|
||||
*
|
||||
* Note that at the time of this writing, Android does not support starting multiple local
|
||||
* PAC servers, though this would be possible in theory by just starting multiple instances
|
||||
* of this process running their server on different ports. As a stopgap measure, Android
|
||||
* keeps one local server which is always the one for the default network. If a non-default
|
||||
* network has a PAC proxy, it will have a LinkProperties with a port of -1, which means it
|
||||
* could still be used by apps that know how to use the PAC URL directly, but not by HTTP
|
||||
* libs that don't know how to do that. When a network with a PAC proxy becomes the default,
|
||||
* the local server is started. When a network without a PAC proxy becomes the default, the
|
||||
* local server is stopped if it is running (and the LP for the old default network should
|
||||
* be reset to have a port of -1).
|
||||
*
|
||||
* In principle, each network can be configured with a different proxy (typically in the
|
||||
* network settings for a Wi-Fi network). These end up exposed in the LinkProperties of the
|
||||
* relevant network.
|
||||
* Android also exposes ConnectivityManager#getDefaultProxy, which is simply the proxy for
|
||||
* the default network. This was retrofitted from a time where Android did not support multiple
|
||||
* concurrent networks, hence the difficult architecture.
|
||||
* Note that there is also a "global" proxy that can be set by the DPM. When this is set,
|
||||
* it overrides the proxy settings for every single network at the same time – that is, the
|
||||
* system behaves as if the global proxy is the configured proxy for each network.
|
||||
*
|
||||
* Generally there are four ways for apps to look up the proxy.
|
||||
* - Looking up the ProxyInfo in the LinkProperties for a network.
|
||||
* - Listening to the PROXY_CHANGED_ACTION broadcast
|
||||
* - Calling ConnectivityManager#getDefaultProxy, or ConnectivityManager#getProxyForNetwork
|
||||
* which can be used to retrieve the proxy for any given network or the default network by
|
||||
* passing null.
|
||||
* - Reading the standard JVM properties (http{s,}.proxy{Host,Port}). See the Java
|
||||
* distribution documentation for details on how these are supposed to work :
|
||||
* https://docs.oracle.com/javase/8/docs/technotes/guides/net/proxies.html
|
||||
* In Android, these are set by ActivityThread in each process in response to the broadcast.
|
||||
* Many apps actually use these, and it's important they work because it's part of the
|
||||
* Java standard, meaning they need to be set for existing Java libs to work on Android.
|
||||
*/
|
||||
@Test
|
||||
public void testPacProxy() throws Exception {
|
||||
final Uri pacUrl = Uri.parse("https://pac-url");
|
||||
mCellAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
|
||||
mCellAgent.connect(true);
|
||||
|
||||
final TestNetworkCallback wifiCallback = new TestNetworkCallback();
|
||||
final NetworkRequest wifiRequest = new NetworkRequest.Builder()
|
||||
.addTransportType(TRANSPORT_WIFI).build();
|
||||
mCm.registerNetworkCallback(wifiRequest, wifiCallback);
|
||||
|
||||
mWiFiAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
|
||||
mWiFiAgent.connect(true);
|
||||
wifiCallback.expectAvailableThenValidatedCallbacks(mWiFiAgent);
|
||||
|
||||
final ProxyInfo initialProxyInfo = ProxyInfo.buildPacProxy(pacUrl);
|
||||
final LinkProperties testLinkProperties = new LinkProperties();
|
||||
testLinkProperties.setHttpProxy(initialProxyInfo);
|
||||
mWiFiAgent.sendLinkProperties(testLinkProperties);
|
||||
wifiCallback.expect(CallbackEntry.LINK_PROPERTIES_CHANGED, mWiFiAgent);
|
||||
|
||||
// At first the local PAC proxy server is unstarted (see the description of what the local
|
||||
// server is in the comment at the top of this method). It will contain the PAC URL and a
|
||||
// port of -1 because it is unstarted. Check that all ways of getting that proxy info
|
||||
// returns the same object that was initially created.
|
||||
final ProxyInfo unstartedDefaultProxyInfo = mService.getProxyForNetwork(null);
|
||||
final ProxyInfo unstartedWifiProxyInfo = mService.getProxyForNetwork(
|
||||
mWiFiAgent.getNetwork());
|
||||
final LinkProperties unstartedLp =
|
||||
mService.getLinkProperties(mWiFiAgent.getNetwork());
|
||||
|
||||
assertEquals(initialProxyInfo, unstartedDefaultProxyInfo);
|
||||
assertEquals(initialProxyInfo, unstartedWifiProxyInfo);
|
||||
assertEquals(initialProxyInfo, unstartedLp.getHttpProxy());
|
||||
|
||||
// Make sure the cell network is unaffected. The LP are per-network and each network has
|
||||
// its own configuration. The default proxy and broadcast are system-wide, and the JVM
|
||||
// properties are per-process, and therefore can have only one value at any given time,
|
||||
// so the code sets them to the proxy of the default network (TODO : really, since the
|
||||
// default process is per-network, the JVM properties (http.proxyHost family – see
|
||||
// the comment at the top of the method for details about these) also should be per-network
|
||||
// and even the broadcast contents should be but none of this is implemented). The LP are
|
||||
// still per-network, and a process that wants to use a non-default network is supposed to
|
||||
// look up the proxy in its LP and it has to be correct.
|
||||
assertNull(mService.getLinkProperties(mCellAgent.getNetwork()).getHttpProxy());
|
||||
assertNull(mService.getProxyForNetwork(mCellAgent.getNetwork()));
|
||||
|
||||
// Simulate PacManager sending the notification that the local server has started
|
||||
final ProxyInfo servingProxyInfo = new ProxyInfo(pacUrl, 2097);
|
||||
final ExpectedBroadcast b1 = expectProxyChangeAction(servingProxyInfo);
|
||||
mService.simulateUpdateProxyInfo(mWiFiAgent.getNetwork(), servingProxyInfo);
|
||||
// wifiCallback.expect(CallbackEntry.LINK_PROPERTIES_CHANGED, mWiFiAgent);
|
||||
b1.expectBroadcast();
|
||||
|
||||
final ProxyInfo startedDefaultProxyInfo = mService.getProxyForNetwork(null);
|
||||
final ProxyInfo startedWifiProxyInfo = mService.getProxyForNetwork(
|
||||
mWiFiAgent.getNetwork());
|
||||
final LinkProperties startedLp = mService.getLinkProperties(mWiFiAgent.getNetwork());
|
||||
// TODO : activate these tests when b/138810051 is fixed.
|
||||
// assertEquals(servingProxyInfo, startedDefaultProxyInfo);
|
||||
// assertEquals(servingProxyInfo, startedWifiProxyInfo);
|
||||
// assertEquals(servingProxyInfo, startedLp.getHttpProxy());
|
||||
// // Make sure the cell network is still unaffected
|
||||
// assertNull(mService.getLinkProperties(mCellAgent.getNetwork()).getHttpProxy());
|
||||
// assertNull(mService.getProxyForNetwork(mCellAgent.getNetwork()));
|
||||
//
|
||||
// final Uri ethPacUrl = Uri.parse("https://ethernet-pac-url");
|
||||
// final TestableNetworkCallback ethernetCallback = new TestableNetworkCallback();
|
||||
// final NetworkRequest ethernetRequest = new NetworkRequest.Builder()
|
||||
// .addTransportType(TRANSPORT_ETHERNET).build();
|
||||
// mEthernetAgent = new TestNetworkAgentWrapper(TRANSPORT_ETHERNET);
|
||||
// mCm.registerNetworkCallback(ethernetRequest, ethernetCallback);
|
||||
// mEthernetAgent.connect(true);
|
||||
// ethernetCallback.expectAvailableThenValidatedCallbacks(mEthernetAgent);
|
||||
// // Wifi is no longer the default, so it should get a callback to LP changed with a PAC
|
||||
// // proxy but a port of -1 (since the local proxy doesn't serve wifi now)
|
||||
// wifiCallback.expect(LINK_PROPERTIES_CHANGED, mWiFiAgent,
|
||||
// lp -> lp.getLp().getHttpProxy().getPort() == -1
|
||||
// && lp.getLp().getHttpProxy().isPacProxy());
|
||||
// wifiCallback.expect(CallbackEntry.LOSING, mWiFiAgent);
|
||||
//
|
||||
// final ProxyInfo ethProxy = ProxyInfo.buildPacProxy(ethPacUrl);
|
||||
// final LinkProperties ethLinkProperties = new LinkProperties();
|
||||
// ethLinkProperties.setHttpProxy(ethProxy);
|
||||
// mEthernetAgent.sendLinkProperties(ethLinkProperties);
|
||||
// ethernetCallback.expect(CallbackEntry.LINK_PROPERTIES_CHANGED, mEthernetAgent);
|
||||
// // Default network is Ethernet
|
||||
// assertEquals(ethProxy, mService.getProxyForNetwork(null));
|
||||
// assertEquals(ethProxy, mService.getProxyForNetwork(mEthernetAgent.getNetwork()));
|
||||
// // Proxy info for WiFi ideally should be the old one with the old server still running,
|
||||
// // but as the PAC code only handles one server at any given time, this can't work. Wifi
|
||||
// // having null as a proxy also won't work (apps using WiFi will try to access the network
|
||||
// // without proxy) but is not as bad as having the new proxy (that would send requests
|
||||
// // over the default network).
|
||||
// assertEquals(initialProxyInfo, mService.getProxyForNetwork(mWiFiAgent.getNetwork()));
|
||||
// assertNull(mService.getProxyForNetwork(mCellAgent.getNetwork()));
|
||||
//
|
||||
// final ProxyInfo servingEthProxy = new ProxyInfo(pacUrl, 2099);
|
||||
// final ExpectedBroadcast b2 = expectProxyChangeAction(servingEthProxy);
|
||||
// final ExpectedBroadcast b3 = expectProxyChangeAction(servingProxyInfo);
|
||||
//
|
||||
// mService.simulateUpdateProxyInfo(mEthernetAgent.getNetwork(), servingEthProxy);
|
||||
// ethernetCallback.expect(CallbackEntry.LINK_PROPERTIES_CHANGED, mEthernetAgent);
|
||||
// assertEquals(servingEthProxy, mService.getProxyForNetwork(null));
|
||||
// assertEquals(servingEthProxy, mService.getProxyForNetwork(
|
||||
// mEthernetAgent.getNetwork()));
|
||||
// assertEquals(initialProxyInfo,
|
||||
// mService.getProxyForNetwork(mWiFiAgent.getNetwork()));
|
||||
// assertNull(mService.getProxyForNetwork(mCellAgent.getNetwork()));
|
||||
// b2.expectBroadcast();
|
||||
//
|
||||
// // Ethernet disconnects, back to WiFi
|
||||
// mEthernetAgent.disconnect();
|
||||
// ethernetCallback.expect(CallbackEntry.LOST, mEthernetAgent);
|
||||
// wifiCallback.assertNoCallback();
|
||||
//
|
||||
// assertEquals(initialProxyInfo, mService.getProxyForNetwork(null));
|
||||
// assertEquals(initialProxyInfo,
|
||||
// mService.getProxyForNetwork(mWiFiAgent.getNetwork()));
|
||||
// assertNull(mService.getProxyForNetwork(mCellAgent.getNetwork()));
|
||||
//
|
||||
// // After a while the PAC file for wifi is resolved again
|
||||
// mService.simulateUpdateProxyInfo(mWiFiAgent.getNetwork(), servingProxyInfo);
|
||||
// wifiCallback.expect(CallbackEntry.LINK_PROPERTIES_CHANGED, mWiFiAgent);
|
||||
// assertEquals(servingProxyInfo, mService.getProxyForNetwork(null));
|
||||
// assertEquals(servingProxyInfo,
|
||||
// mService.getProxyForNetwork(mWiFiAgent.getNetwork()));
|
||||
// assertNull(mService.getProxyForNetwork(mCellAgent.getNetwork()));
|
||||
// b3.expectBroadcast();
|
||||
//
|
||||
// // Expect a broadcast after wifi disconnected. The proxy might be default proxy or an
|
||||
// // empty proxy built by buildDirectProxy. See {@link ProxyTracker.sendProxyBroadcast}.
|
||||
// // Thus here expects a broadcast will be received but does not verify the content of the
|
||||
// // proxy.
|
||||
// final ExpectedBroadcast b4 = expectProxyChangeAction();
|
||||
// mWiFiAgent.disconnect();
|
||||
// b4.expectBroadcast();
|
||||
// wifiCallback.expect(CallbackEntry.LOST, mWiFiAgent);
|
||||
// assertNull(mService.getProxyForNetwork(null));
|
||||
assertNull(mService.getLinkProperties(mCellAgent.getNetwork()).getHttpProxy());
|
||||
assertNull(mService.getGlobalProxy());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetProxyForVPN() throws Exception {
|
||||
final ProxyInfo testProxyInfo = ProxyInfo.buildDirectProxy("test", 8888);
|
||||
|
||||
Reference in New Issue
Block a user