Send a proxy broadcast when apps moved from/to a VPN

When the apps moved from/to a VPN, a proxy broadcast is needed to
inform the apps that the proxy might be changed since the default
network satisfied by the apps might also changed.
Since the framework does not track the defautlt network of every
apps, thus, this is done when:
  1. VPN connects/disconnects.
  2. List of uids that apply to the VPN has changed.
While 1 is already covered by the current design, the CL implements
2 in order to fulfill the case that different networks have
different proxies.

Bug: 178727215
Test: atest FrameworksNetTests
Original-Change: https://android-review.googlesource.com/1717735
Merged-In: Ifa103dd66394026d752b407a1bee740c9fcdad2b
Change-Id: Ifa103dd66394026d752b407a1bee740c9fcdad2b
This commit is contained in:
Lucas Lin
2021-06-15 09:28:16 +00:00
parent 52e3fa127a
commit 950a65f8cc
3 changed files with 162 additions and 19 deletions

View File

@@ -484,6 +484,7 @@ public class ConnectivityServiceTest {
@Mock VpnProfileStore mVpnProfileStore;
@Mock SystemConfigManager mSystemConfigManager;
@Mock Resources mResources;
@Mock ProxyTracker mProxyTracker;
private ArgumentCaptor<ResolverParamsParcel> mResolverParamsParcelCaptor =
ArgumentCaptor.forClass(ResolverParamsParcel.class);
@@ -1278,10 +1279,14 @@ public class ConnectivityServiceTest {
return mMockNetworkAgent;
}
public void establish(LinkProperties lp, int uid, Set<UidRange> ranges, boolean validated,
boolean hasInternet, boolean isStrictMode) throws Exception {
private void setOwnerAndAdminUid(int uid) throws Exception {
mNetworkCapabilities.setOwnerUid(uid);
mNetworkCapabilities.setAdministratorUids(new int[]{uid});
}
public void establish(LinkProperties lp, int uid, Set<UidRange> ranges, boolean validated,
boolean hasInternet, boolean isStrictMode) throws Exception {
setOwnerAndAdminUid(uid);
registerAgent(false, ranges, lp);
connect(validated, hasInternet, isStrictMode);
waitForIdle();
@@ -1636,7 +1641,7 @@ public class ConnectivityServiceTest {
doReturn(mNetIdManager).when(deps).makeNetIdManager();
doReturn(mNetworkStack).when(deps).getNetworkStack();
doReturn(mSystemProperties).when(deps).getSystemProperties();
doReturn(mock(ProxyTracker.class)).when(deps).makeProxyTracker(any(), any());
doReturn(mProxyTracker).when(deps).makeProxyTracker(any(), any());
doReturn(true).when(deps).queryUserAccess(anyInt(), any(), any());
doAnswer(inv -> {
mPolicyTracker = new WrappedMultinetworkPolicyTracker(
@@ -10380,16 +10385,23 @@ public class ConnectivityServiceTest {
@Test
public void testVpnUidRangesUpdate() throws Exception {
LinkProperties lp = new LinkProperties();
// Set up a WiFi network without proxy.
mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
mWiFiNetworkAgent.connect(true);
assertNull(mService.getProxyForNetwork(null));
assertNull(mCm.getDefaultProxy());
final LinkProperties lp = new LinkProperties();
lp.setInterfaceName("tun0");
lp.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), null));
lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), null));
final UidRange vpnRange = PRIMARY_UIDRANGE;
Set<UidRange> vpnRanges = Collections.singleton(vpnRange);
final Set<UidRange> vpnRanges = Collections.singleton(vpnRange);
mMockVpn.establish(lp, VPN_UID, vpnRanges);
assertVpnUidRangesUpdated(true, vpnRanges, VPN_UID);
// VPN is connected but proxy is not set, so there is no need to send proxy broadcast.
verify(mProxyTracker, never()).sendProxyBroadcast();
reset(mMockNetd);
// Update to new range which is old range minus APP1, i.e. only APP2
final Set<UidRange> newRanges = new HashSet<>(Arrays.asList(
new UidRange(vpnRange.start, APP1_UID - 1),
@@ -10399,6 +10411,101 @@ public class ConnectivityServiceTest {
assertVpnUidRangesUpdated(true, newRanges, VPN_UID);
assertVpnUidRangesUpdated(false, vpnRanges, VPN_UID);
// Uid has changed but proxy is not set, so there is no need to send proxy broadcast.
verify(mProxyTracker, never()).sendProxyBroadcast();
final ProxyInfo testProxyInfo = ProxyInfo.buildDirectProxy("test", 8888);
lp.setHttpProxy(testProxyInfo);
mMockVpn.sendLinkProperties(lp);
waitForIdle();
// Proxy is set, so send a proxy broadcast.
verify(mProxyTracker, times(1)).sendProxyBroadcast();
reset(mProxyTracker);
mMockVpn.setUids(vpnRanges);
waitForIdle();
// Uid has changed and proxy is already set, so send a proxy broadcast.
verify(mProxyTracker, times(1)).sendProxyBroadcast();
reset(mProxyTracker);
// Proxy is removed, send a proxy broadcast.
lp.setHttpProxy(null);
mMockVpn.sendLinkProperties(lp);
waitForIdle();
verify(mProxyTracker, times(1)).sendProxyBroadcast();
reset(mProxyTracker);
// Proxy is added in WiFi(default network), setDefaultProxy will be called.
final LinkProperties wifiLp = mCm.getLinkProperties(mWiFiNetworkAgent.getNetwork());
assertNotNull(wifiLp);
wifiLp.setHttpProxy(testProxyInfo);
mWiFiNetworkAgent.sendLinkProperties(wifiLp);
waitForIdle();
verify(mProxyTracker, times(1)).setDefaultProxy(eq(testProxyInfo));
reset(mProxyTracker);
}
@Test
public void testProxyBroadcastWillBeSentWhenVpnHasProxyAndConnects() throws Exception {
// Set up a WiFi network without proxy.
mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
mWiFiNetworkAgent.connect(true);
assertNull(mService.getProxyForNetwork(null));
assertNull(mCm.getDefaultProxy());
final LinkProperties lp = new LinkProperties();
lp.setInterfaceName("tun0");
lp.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), null));
lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), null));
final ProxyInfo testProxyInfo = ProxyInfo.buildDirectProxy("test", 8888);
lp.setHttpProxy(testProxyInfo);
final UidRange vpnRange = PRIMARY_UIDRANGE;
final Set<UidRange> vpnRanges = Collections.singleton(vpnRange);
mMockVpn.setOwnerAndAdminUid(VPN_UID);
mMockVpn.registerAgent(false, vpnRanges, lp);
// In any case, the proxy broadcast won't be sent before VPN goes into CONNECTED state.
// Otherwise, the app that calls ConnectivityManager#getDefaultProxy() when it receives the
// proxy broadcast will get null.
verify(mProxyTracker, never()).sendProxyBroadcast();
mMockVpn.connect(true /* validated */, true /* hasInternet */, false /* isStrictMode */);
waitForIdle();
assertVpnUidRangesUpdated(true, vpnRanges, VPN_UID);
// Vpn is connected with proxy, so the proxy broadcast will be sent to inform the apps to
// update their proxy data.
verify(mProxyTracker, times(1)).sendProxyBroadcast();
}
@Test
public void testProxyBroadcastWillBeSentWhenTheProxyOfNonDefaultNetworkHasChanged()
throws Exception {
// Set up a CELLULAR network without proxy.
mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
mCellNetworkAgent.connect(true);
assertNull(mService.getProxyForNetwork(null));
assertNull(mCm.getDefaultProxy());
// CELLULAR network should be the default network.
assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork());
// Set up a WiFi network without proxy.
mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
mWiFiNetworkAgent.connect(true);
assertNull(mService.getProxyForNetwork(null));
assertNull(mCm.getDefaultProxy());
// WiFi network should be the default network.
assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork());
// CELLULAR network is not the default network.
assertNotEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork());
// CELLULAR network is not the system default network, but it might be a per-app default
// network. The proxy broadcast should be sent once its proxy has changed.
final LinkProperties cellularLp = new LinkProperties();
cellularLp.setInterfaceName(MOBILE_IFNAME);
final ProxyInfo testProxyInfo = ProxyInfo.buildDirectProxy("test", 8888);
cellularLp.setHttpProxy(testProxyInfo);
mCellNetworkAgent.sendLinkProperties(cellularLp);
waitForIdle();
verify(mProxyTracker, times(1)).sendProxyBroadcast();
}
@Test