Fix a bug where the PAC proxy port is not set correctly.
Test: new test for this behavior in the preliminary change
Test: FrameworksNetTests NetworkStackTests
Fixes: 138810051
Fixes: 140610528
Change-Id: I95a979d232fb60ece2e33e972bf5d66d20357a1f
diff --git a/framework/src/android/net/ProxyInfo.java b/framework/src/android/net/ProxyInfo.java
index 0deda37..adf2376 100644
--- a/framework/src/android/net/ProxyInfo.java
+++ b/framework/src/android/net/ProxyInfo.java
@@ -47,6 +47,8 @@
private final int mPort;
private final String mExclusionList;
private final String[] mParsedExclusionList;
+ // Uri.EMPTY if none.
+ @NonNull
private final Uri mPacFileUrl;
/**
@@ -256,6 +258,14 @@
return proxy;
}
+ /**
+ * @hide
+ * @return whether this proxy uses a Proxy Auto Configuration URL.
+ */
+ public boolean isPacProxy() {
+ return !Uri.EMPTY.equals(mPacFileUrl);
+ }
+
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
diff --git a/service/src/com/android/server/ConnectivityService.java b/service/src/com/android/server/ConnectivityService.java
index c95295c..7cf8672 100755
--- a/service/src/com/android/server/ConnectivityService.java
+++ b/service/src/com/android/server/ConnectivityService.java
@@ -34,6 +34,7 @@
import static android.net.ConnectivityManager.BLOCKED_METERED_REASON_MASK;
import static android.net.ConnectivityManager.BLOCKED_REASON_LOCKDOWN_VPN;
import static android.net.ConnectivityManager.BLOCKED_REASON_NONE;
+import static android.net.ConnectivityManager.CALLBACK_IP_CHANGED;
import static android.net.ConnectivityManager.CONNECTIVITY_ACTION;
import static android.net.ConnectivityManager.FIREWALL_RULE_ALLOW;
import static android.net.ConnectivityManager.FIREWALL_RULE_DEFAULT;
@@ -548,7 +549,7 @@
/**
* PAC manager has received new port.
*/
- private static final int EVENT_PROXY_HAS_CHANGED = 16;
+ private static final int EVENT_PAC_PROXY_HAS_CHANGED = 16;
/**
* used internally when registering NetworkProviders
@@ -1322,7 +1323,7 @@
*/
public ProxyTracker makeProxyTracker(@NonNull Context context,
@NonNull Handler connServiceHandler) {
- return new ProxyTracker(context, connServiceHandler, EVENT_PROXY_HAS_CHANGED);
+ return new ProxyTracker(context, connServiceHandler, EVENT_PAC_PROXY_HAS_CHANGED);
}
/**
@@ -1868,7 +1869,7 @@
@VisibleForTesting
void simulateUpdateProxyInfo(@Nullable final Network network,
@NonNull final ProxyInfo proxyInfo) {
- Message.obtain(mHandler, EVENT_PROXY_HAS_CHANGED,
+ Message.obtain(mHandler, EVENT_PAC_PROXY_HAS_CHANGED,
new Pair<>(network, proxyInfo)).sendToTarget();
}
@@ -4654,6 +4655,21 @@
rematchAllNetworksAndRequests();
mLingerMonitor.noteDisconnect(nai);
+ if (null == getDefaultNetwork() && nai.linkProperties.getHttpProxy() != null) {
+ // The obvious place to do this would be in makeDefault(), however makeDefault() is
+ // not called by the rematch in this case. This is because the code above unset
+ // this network from the default request's satisfier, and that is what the rematch
+ // is using as its source data to know what the old satisfier was. So as far as the
+ // rematch above is concerned, the old default network was null.
+ // Therefore if there is no new default, the default network was null and is still
+ // null, thus there was no change so makeDefault() is not called. So if the old
+ // network had a proxy and there is no new default, the proxy tracker should be told
+ // that there is no longer a default proxy.
+ // Strictly speaking this is not essential because having a proxy setting when
+ // there is no network is harmless, but it's still counter-intuitive so reset to null.
+ mProxyTracker.setDefaultProxy(null);
+ }
+
// Immediate teardown.
if (nai.teardownDelayMs == 0) {
destroyNetwork(nai);
@@ -5676,9 +5692,9 @@
mProxyTracker.loadDeprecatedGlobalHttpProxy();
break;
}
- case EVENT_PROXY_HAS_CHANGED: {
+ case EVENT_PAC_PROXY_HAS_CHANGED: {
final Pair<Network, ProxyInfo> arg = (Pair<Network, ProxyInfo>) msg.obj;
- handleApplyDefaultProxy(arg.second);
+ handlePacProxyServiceStarted(arg.first, arg.second);
break;
}
case EVENT_REGISTER_NETWORK_PROVIDER: {
@@ -6133,12 +6149,19 @@
return mProxyTracker.getGlobalProxy();
}
- private void handleApplyDefaultProxy(@Nullable ProxyInfo proxy) {
- if (proxy != null && TextUtils.isEmpty(proxy.getHost())
- && Uri.EMPTY.equals(proxy.getPacFileUrl())) {
- proxy = null;
- }
+ private void handlePacProxyServiceStarted(@Nullable Network net, @Nullable ProxyInfo proxy) {
mProxyTracker.setDefaultProxy(proxy);
+ final NetworkAgentInfo nai = getDefaultNetwork();
+ // TODO : this method should check that net == nai.network, unfortunately at this point
+ // 'net' is always null in practice (see PacProxyService#sendPacBroadcast). PAC proxy
+ // is only ever installed on the default network so in practice this is okay.
+ if (null == nai) return;
+ // PAC proxies only work on the default network. Therefore, only the default network
+ // should have its link properties fixed up for PAC proxies.
+ mProxyTracker.updateDefaultNetworkProxyPortForPAC(nai.linkProperties, nai.network);
+ if (nai.everConnected()) {
+ notifyNetworkCallbacks(nai, ConnectivityManager.CALLBACK_IP_CHANGED);
+ }
}
// If the proxy has changed from oldLp to newLp, resend proxy broadcast. This method gets called
@@ -7964,6 +7987,7 @@
// updateMtu(lp, null);
// }
if (isDefaultNetwork(networkAgent)) {
+ mProxyTracker.updateDefaultNetworkProxyPortForPAC(newLp, null);
updateTcpBufferSizes(newLp.getTcpBufferSizes());
}
@@ -7976,7 +8000,7 @@
mDnsManager.updatePrivateDnsStatus(netId, newLp);
if (isDefaultNetwork(networkAgent)) {
- handleApplyDefaultProxy(newLp.getHttpProxy());
+ mProxyTracker.setDefaultProxy(newLp.getHttpProxy());
} else if (networkAgent.everConnected()) {
updateProxy(newLp, oldLp);
}
@@ -9023,6 +9047,17 @@
}
}
+ private void resetHttpProxyForNonDefaultNetwork(NetworkAgentInfo oldDefaultNetwork) {
+ if (null == oldDefaultNetwork) return;
+ // The network stopped being the default. If it was using a PAC proxy, then the
+ // proxy needs to be reset, otherwise HTTP requests on this network may be sent
+ // to the local proxy server, which would forward them over the newly default network.
+ final ProxyInfo proxyInfo = oldDefaultNetwork.linkProperties.getHttpProxy();
+ if (null == proxyInfo || !proxyInfo.isPacProxy()) return;
+ oldDefaultNetwork.linkProperties.setHttpProxy(new ProxyInfo(proxyInfo.getPacFileUrl()));
+ notifyNetworkCallbacks(oldDefaultNetwork, CALLBACK_IP_CHANGED);
+ }
+
private void makeDefault(@NonNull final NetworkRequestInfo nri,
@Nullable final NetworkAgentInfo oldDefaultNetwork,
@Nullable final NetworkAgentInfo newDefaultNetwork) {
@@ -9048,8 +9083,9 @@
mLingerMonitor.noteLingerDefaultNetwork(oldDefaultNetwork, newDefaultNetwork);
}
mNetworkActivityTracker.updateDataActivityTracking(newDefaultNetwork, oldDefaultNetwork);
- handleApplyDefaultProxy(null != newDefaultNetwork
+ mProxyTracker.setDefaultProxy(null != newDefaultNetwork
? newDefaultNetwork.linkProperties.getHttpProxy() : null);
+ resetHttpProxyForNonDefaultNetwork(oldDefaultNetwork);
updateTcpBufferSizes(null != newDefaultNetwork
? newDefaultNetwork.linkProperties.getTcpBufferSizes() : null);
notifyIfacesChangedForNetworkStats();
diff --git a/service/src/com/android/server/connectivity/ProxyTracker.java b/service/src/com/android/server/connectivity/ProxyTracker.java
index b3cbb2a..bda4b8f 100644
--- a/service/src/com/android/server/connectivity/ProxyTracker.java
+++ b/service/src/com/android/server/connectivity/ProxyTracker.java
@@ -27,6 +27,7 @@
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
+import android.net.LinkProperties;
import android.net.Network;
import android.net.PacProxyManager;
import android.net.Proxy;
@@ -95,6 +96,7 @@
}
public void onPacProxyInstalled(@Nullable Network network, @NonNull ProxyInfo proxy) {
+ Log.i(TAG, "PAC proxy installed on network " + network + " : " + proxy);
mConnectivityServiceHandler
.sendMessage(mConnectivityServiceHandler
.obtainMessage(mEvent, new Pair<>(network, proxy)));
@@ -328,6 +330,12 @@
* @param proxyInfo the proxy spec, or null for no proxy.
*/
public void setDefaultProxy(@Nullable ProxyInfo proxyInfo) {
+ // The code has been accepting empty proxy objects forever, so for backward
+ // compatibility it should continue doing so.
+ if (proxyInfo != null && TextUtils.isEmpty(proxyInfo.getHost())
+ && Uri.EMPTY.equals(proxyInfo.getPacFileUrl())) {
+ proxyInfo = null;
+ }
synchronized (mProxyLock) {
if (Objects.equals(mDefaultProxy, proxyInfo)) return;
if (proxyInfo != null && !proxyInfo.isValid()) {
@@ -355,4 +363,51 @@
}
}
}
+
+ private boolean isPacProxy(@Nullable final ProxyInfo info) {
+ return null != info && info.isPacProxy();
+ }
+
+ /**
+ * Adjust the proxy in the link properties if necessary.
+ *
+ * It is necessary when the proxy in the passed property is for PAC, and the default proxy
+ * is also for PAC. This is because the original LinkProperties from the network agent don't
+ * include the port for the local proxy as it's not known at creation time, but this class
+ * knows it after the proxy service is started.
+ *
+ * This is safe because there can only ever be one proxy service running on the device, so
+ * if the ProxyInfo in the LinkProperties is for PAC, then the port is necessarily the one
+ * ProxyTracker knows about.
+ *
+ * @param lp the LinkProperties to fix up.
+ * @param network the network of the local proxy server.
+ */
+ // TODO: Leave network unused to support local proxy server per network in the future.
+ public void updateDefaultNetworkProxyPortForPAC(@NonNull final LinkProperties lp,
+ @Nullable Network network) {
+ final ProxyInfo defaultProxy = getDefaultProxy();
+ if (isPacProxy(lp.getHttpProxy()) && isPacProxy(defaultProxy)) {
+ synchronized (mProxyLock) {
+ // At this time, this method can only be called for the default network's LP.
+ // Therefore the PAC file URL in the LP must match the one in the default proxy,
+ // and we just update the port.
+ // Note that the global proxy, if any, is set out of band by the DPM and becomes
+ // the default proxy (it overrides it, see {@link getDefaultProxy}). The PAC URL
+ // in the global proxy might not be the one in the LP of the default
+ // network, so discount this case.
+ if (null == mGlobalProxy && !lp.getHttpProxy().getPacFileUrl()
+ .equals(defaultProxy.getPacFileUrl())) {
+ throw new IllegalStateException("Unexpected discrepancy between proxy in LP of "
+ + "default network and default proxy. The former has a PAC URL of "
+ + lp.getHttpProxy().getPacFileUrl() + " while the latter has "
+ + defaultProxy.getPacFileUrl());
+ }
+ }
+ // If this network has a PAC proxy and proxy tracker already knows about
+ // it, now is the right time to patch it in. If proxy tracker does not know
+ // about it yet, then it will be patched in when it learns about it.
+ lp.setHttpProxy(defaultProxy);
+ }
+ }
}
diff --git a/tests/unit/java/com/android/server/ConnectivityServiceTest.java b/tests/unit/java/com/android/server/ConnectivityServiceTest.java
index c79c295..29677fc 100755
--- a/tests/unit/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/unit/java/com/android/server/ConnectivityServiceTest.java
@@ -2506,10 +2506,14 @@
}
private ExpectedBroadcast expectProxyChangeAction(ProxyInfo proxy) {
+ return expectProxyChangeAction(actualProxy -> proxy.equals(actualProxy));
+ }
+
+ private ExpectedBroadcast expectProxyChangeAction(Predicate<ProxyInfo> tester) {
return registerBroadcastReceiverThat(PROXY_CHANGE_ACTION, 1, intent -> {
final ProxyInfo actualProxy = (ProxyInfo) intent.getExtra(Proxy.EXTRA_PROXY_INFO,
ProxyInfo.buildPacProxy(Uri.EMPTY));
- return proxy.equals(actualProxy);
+ return tester.test(actualProxy);
});
}
@@ -11398,8 +11402,16 @@
@Test
public void testPacProxy() throws Exception {
final Uri pacUrl = Uri.parse("https://pac-url");
+
+ final TestNetworkCallback cellCallback = new TestNetworkCallback();
+ final NetworkRequest cellRequest = new NetworkRequest.Builder()
+ .addTransportType(TRANSPORT_CELLULAR).build();
+ // Request cell to make sure it doesn't disconnect at an arbitrary point in the test,
+ // which would make testing the callbacks on it difficult.
+ mCm.requestNetwork(cellRequest, cellCallback);
mCellAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
mCellAgent.connect(true);
+ cellCallback.expectAvailableThenValidatedCallbacks(mCellAgent);
final TestNetworkCallback wifiCallback = new TestNetworkCallback();
final NetworkRequest wifiRequest = new NetworkRequest.Builder()
@@ -11409,12 +11421,14 @@
mWiFiAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
mWiFiAgent.connect(true);
wifiCallback.expectAvailableThenValidatedCallbacks(mWiFiAgent);
+ cellCallback.assertNoCallback();
final ProxyInfo initialProxyInfo = ProxyInfo.buildPacProxy(pacUrl);
final LinkProperties testLinkProperties = new LinkProperties();
testLinkProperties.setHttpProxy(initialProxyInfo);
mWiFiAgent.sendLinkProperties(testLinkProperties);
wifiCallback.expect(CallbackEntry.LINK_PROPERTIES_CHANGED, mWiFiAgent);
+ cellCallback.assertNoCallback();
// 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
@@ -11444,98 +11458,154 @@
// Simulate PacManager sending the notification that the local server has started
final ProxyInfo servingProxyInfo = new ProxyInfo(pacUrl, 2097);
- final ExpectedBroadcast b1 = expectProxyChangeAction(servingProxyInfo);
+ final ExpectedBroadcast servingProxyBroadcast = expectProxyChangeAction(servingProxyInfo);
mService.simulateUpdateProxyInfo(mWiFiAgent.getNetwork(), servingProxyInfo);
-// wifiCallback.expect(CallbackEntry.LINK_PROPERTIES_CHANGED, mWiFiAgent);
- b1.expectBroadcast();
+ wifiCallback.expect(CallbackEntry.LINK_PROPERTIES_CHANGED, mWiFiAgent);
+ cellCallback.assertNoCallback();
+ servingProxyBroadcast.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));
+ 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()));
+
+ // Start an ethernet network which will become the default, in order to test what happens
+ // to the proxy of wifi while manipulating the proxy of ethernet.
+ 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());
+ // Wifi is lingered as it was the default but is no longer serving any request.
+ wifiCallback.expect(CallbackEntry.LOSING, mWiFiAgent);
+
+ // Now arrange for Ethernet to have a PAC proxy.
+ 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()));
+
+ // Expect the local PAC proxy server starts to serve the proxy on Ethernet. Use
+ // simulateUpdateProxyInfo to simulate this, and check that the callback is called to
+ // inform apps of the port and that the various networks have the expected proxies in
+ // their link properties.
+ final ProxyInfo servingEthProxy = new ProxyInfo(ethPacUrl, 2099);
+ final ExpectedBroadcast servingEthProxyBroadcast = expectProxyChangeAction(servingEthProxy);
+ final ExpectedBroadcast servingProxyBroadcast2 = 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()));
+ servingEthProxyBroadcast.expectBroadcast();
+
+ // Ethernet disconnects, back to WiFi
+ mEthernetAgent.disconnect();
+ ethernetCallback.expect(CallbackEntry.LOST, mEthernetAgent);
+
+ // WiFi is now the default network again. However, the local proxy server does not serve
+ // WiFi at this time, so at this time a proxy with port -1 is still the correct value.
+ // In time, the local proxy server for ethernet will be downed and the local proxy server
+ // for WiFi will be restarted, and WiFi will see an update to its LP with the new port,
+ // but in this test this won't happen until the test simulates the local proxy starting
+ // up for WiFi (which is done just a few lines below). This is therefore the perfect place
+ // to check that WiFi is unaffected until the new local proxy starts up.
+ wifiCallback.assertNoCallback();
+ assertEquals(initialProxyInfo, mService.getProxyForNetwork(null));
+ assertEquals(initialProxyInfo, mService.getProxyForNetwork(mWiFiAgent.getNetwork()));
+ assertNull(mService.getProxyForNetwork(mCellAgent.getNetwork()));
+ // Note : strictly speaking, for correctness here apps should expect a broadcast with a
+ // port of -1, since that's the current default proxy. The code does not send this
+ // broadcast. In practice though, not sending it is more useful since the new proxy will
+ // start momentarily, so broadcasting and getting all apps to update and retry once now
+ // and again in 250ms is kind of counter-productive, so don't fix this bug.
+
+ // After a while the PAC file for wifi is resolved again and the local proxy server
+ // starts up. This should cause a LP event to inform clients of the port to access the
+ // proxy server for wifi.
+ 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()));
+ servingProxyBroadcast2.expectBroadcast();
+
+ // Expect a broadcast for an empty proxy after wifi disconnected, because cell is now
+ // the default network and it doesn't have a proxy. Whether "no proxy" is a null pointer
+ // or a ProxyInfo with an empty host doesn't matter because both are correct, so this test
+ // accepts both.
+ final ExpectedBroadcast emptyProxyBroadcast = expectProxyChangeAction(
+ proxy -> proxy == null || TextUtils.isEmpty(proxy.getHost()));
+ mWiFiAgent.disconnect();
+ emptyProxyBroadcast.expectBroadcast();
+ wifiCallback.expect(CallbackEntry.LOST, mWiFiAgent);
+ assertNull(mService.getProxyForNetwork(null));
assertNull(mService.getLinkProperties(mCellAgent.getNetwork()).getHttpProxy());
assertNull(mService.getGlobalProxy());
+
+ mCm.unregisterNetworkCallback(cellCallback);
+ }
+
+ @Test
+ public void testPacProxy_NetworkDisconnects_BroadcastSent() throws Exception {
+ // Make a WiFi network with a PAC URI.
+ final Uri pacUrl = Uri.parse("https://pac-url");
+ final ProxyInfo initialProxyInfo = ProxyInfo.buildPacProxy(pacUrl);
+ final LinkProperties testLinkProperties = new LinkProperties();
+ testLinkProperties.setHttpProxy(initialProxyInfo);
+
+ final TestNetworkCallback wifiCallback = new TestNetworkCallback();
+ final NetworkRequest wifiRequest = new NetworkRequest.Builder()
+ .addTransportType(TRANSPORT_WIFI).build();
+ mCm.registerNetworkCallback(wifiRequest, wifiCallback);
+
+ mWiFiAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI, testLinkProperties);
+ mWiFiAgent.connect(true);
+ // Wifi is up, but the local proxy server hasn't started yet.
+ wifiCallback.expectAvailableThenValidatedCallbacks(mWiFiAgent);
+
+ // Simulate PacManager sending the notification that the local server has started
+ final ProxyInfo servingProxyInfo = new ProxyInfo(pacUrl, 2097);
+ final ExpectedBroadcast servingProxyBroadcast = expectProxyChangeAction(servingProxyInfo);
+ mService.simulateUpdateProxyInfo(mWiFiAgent.getNetwork(), servingProxyInfo);
+ wifiCallback.expect(CallbackEntry.LINK_PROPERTIES_CHANGED, mWiFiAgent);
+ servingProxyBroadcast.expectBroadcast();
+
+ // Now disconnect Wi-Fi and make sure there is a broadcast for some empty proxy. Whether
+ // the "empty" proxy is a null pointer or a ProxyInfo with an empty host doesn't matter
+ // because both are correct, so this test accepts both.
+ final ExpectedBroadcast emptyProxyBroadcast = expectProxyChangeAction(
+ proxy -> proxy == null || TextUtils.isEmpty(proxy.getHost()));
+ mWiFiAgent.disconnect();
+ wifiCallback.expect(CallbackEntry.LOST, mWiFiAgent);
+ emptyProxyBroadcast.expectBroadcast();
}
@Test