Merge "Send a proxy broadcast when apps moved from/to a VPN" into sc-dev
This commit is contained in:
@@ -1584,6 +1584,28 @@ public final class NetworkCapabilities implements Parcelable {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compare if the given NetworkCapabilities have the same UIDs.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public static boolean hasSameUids(@Nullable NetworkCapabilities nc1,
|
||||
@Nullable NetworkCapabilities nc2) {
|
||||
final Set<UidRange> uids1 = (nc1 == null) ? null : nc1.mUids;
|
||||
final Set<UidRange> uids2 = (nc2 == null) ? null : nc2.mUids;
|
||||
if (null == uids1) return null == uids2;
|
||||
if (null == uids2) return false;
|
||||
// Make a copy so it can be mutated to check that all ranges in uids2 also are in uids.
|
||||
final Set<UidRange> uids = new ArraySet<>(uids2);
|
||||
for (UidRange range : uids1) {
|
||||
if (!uids.contains(range)) {
|
||||
return false;
|
||||
}
|
||||
uids.remove(range);
|
||||
}
|
||||
return uids.isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests if the set of UIDs that this network applies to is the same as the passed network.
|
||||
* <p>
|
||||
@@ -1600,19 +1622,7 @@ public final class NetworkCapabilities implements Parcelable {
|
||||
*/
|
||||
@VisibleForTesting
|
||||
public boolean equalsUids(@NonNull NetworkCapabilities nc) {
|
||||
Set<UidRange> comparedUids = nc.mUids;
|
||||
if (null == comparedUids) return null == mUids;
|
||||
if (null == mUids) return false;
|
||||
// Make a copy so it can be mutated to check that all ranges in mUids
|
||||
// also are in uids.
|
||||
final Set<UidRange> uids = new ArraySet<>(mUids);
|
||||
for (UidRange range : comparedUids) {
|
||||
if (!uids.contains(range)) {
|
||||
return false;
|
||||
}
|
||||
uids.remove(range);
|
||||
}
|
||||
return uids.isEmpty();
|
||||
return hasSameUids(nc, this);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -7348,6 +7348,8 @@ public class ConnectivityService extends IConnectivityManager.Stub
|
||||
mDnsManager.updateTransportsForNetwork(
|
||||
nai.network.getNetId(), newNc.getTransportTypes());
|
||||
}
|
||||
|
||||
maybeSendProxyBroadcast(nai, prevNc, newNc);
|
||||
}
|
||||
|
||||
/** Convenience method to update the capabilities for a given network. */
|
||||
@@ -7440,6 +7442,30 @@ public class ConnectivityService extends IConnectivityManager.Stub
|
||||
maybeCloseSockets(nai, ranges, exemptUids);
|
||||
}
|
||||
|
||||
private boolean isProxySetOnAnyDefaultNetwork() {
|
||||
ensureRunningOnConnectivityServiceThread();
|
||||
for (final NetworkRequestInfo nri : mDefaultNetworkRequests) {
|
||||
final NetworkAgentInfo nai = nri.getSatisfier();
|
||||
if (nai != null && nai.linkProperties.getHttpProxy() != null) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void maybeSendProxyBroadcast(NetworkAgentInfo nai, NetworkCapabilities prevNc,
|
||||
NetworkCapabilities newNc) {
|
||||
// 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.
|
||||
// TODO: Try to track the default network that apps use and only send a proxy broadcast when
|
||||
// that happens to prevent false alarms.
|
||||
if (nai.isVPN() && nai.everConnected && !NetworkCapabilities.hasSameUids(prevNc, newNc)
|
||||
&& (nai.linkProperties.getHttpProxy() != null || isProxySetOnAnyDefaultNetwork())) {
|
||||
mProxyTracker.sendProxyBroadcast();
|
||||
}
|
||||
}
|
||||
|
||||
private void updateUids(NetworkAgentInfo nai, NetworkCapabilities prevNc,
|
||||
NetworkCapabilities newNc) {
|
||||
Set<UidRange> prevRanges = null == prevNc ? null : prevNc.getUidRanges();
|
||||
|
||||
@@ -486,6 +486,7 @@ public class ConnectivityServiceTest {
|
||||
@Mock VpnProfileStore mVpnProfileStore;
|
||||
@Mock SystemConfigManager mSystemConfigManager;
|
||||
@Mock Resources mResources;
|
||||
@Mock ProxyTracker mProxyTracker;
|
||||
|
||||
private ArgumentCaptor<ResolverParamsParcel> mResolverParamsParcelCaptor =
|
||||
ArgumentCaptor.forClass(ResolverParamsParcel.class);
|
||||
@@ -1280,10 +1281,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();
|
||||
@@ -1638,7 +1643,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(
|
||||
@@ -10382,16 +10387,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),
|
||||
@@ -10401,6 +10413,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
|
||||
|
||||
Reference in New Issue
Block a user