Snap for 7473808 from 1b64ae6b8715fc00e8549d0b6355a004f981c97a to sc-release
Change-Id: I488124e3daae8da7ef56f47bf1a7678a0e4beda3
diff --git a/framework/src/android/net/NetworkCapabilities.java b/framework/src/android/net/NetworkCapabilities.java
index ed9df56..ec71d3d 100644
--- a/framework/src/android/net/NetworkCapabilities.java
+++ b/framework/src/android/net/NetworkCapabilities.java
@@ -1585,6 +1585,28 @@
}
/**
+ * 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>
* This test only checks whether equal range objects are in both sets. It will
@@ -1600,19 +1622,7 @@
*/
@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);
}
/**
diff --git a/service/src/com/android/server/ConnectivityService.java b/service/src/com/android/server/ConnectivityService.java
index 48ae7a0..899286b 100644
--- a/service/src/com/android/server/ConnectivityService.java
+++ b/service/src/com/android/server/ConnectivityService.java
@@ -7348,6 +7348,8 @@
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 @@
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();
diff --git a/tests/common/java/android/net/NetworkAgentConfigTest.kt b/tests/common/java/android/net/NetworkAgentConfigTest.kt
index 2b45b3d..afaae1c 100644
--- a/tests/common/java/android/net/NetworkAgentConfigTest.kt
+++ b/tests/common/java/android/net/NetworkAgentConfigTest.kt
@@ -59,6 +59,7 @@
@Test @IgnoreUpTo(Build.VERSION_CODES.Q)
fun testBuilder() {
+ val testExtraInfo = "mylegacyExtraInfo"
val config = NetworkAgentConfig.Builder().apply {
setExplicitlySelected(true)
setLegacyType(ConnectivityManager.TYPE_ETHERNET)
@@ -67,6 +68,7 @@
setUnvalidatedConnectivityAcceptable(true)
setLegacyTypeName("TEST_NETWORK")
if (isAtLeastS()) {
+ setLegacyExtraInfo(testExtraInfo)
setNat64DetectionEnabled(false)
setProvisioningNotificationEnabled(false)
setBypassableVpn(true)
@@ -80,6 +82,7 @@
assertTrue(config.isUnvalidatedConnectivityAcceptable())
assertEquals("TEST_NETWORK", config.getLegacyTypeName())
if (isAtLeastS()) {
+ assertEquals(testExtraInfo, config.getLegacyExtraInfo())
assertFalse(config.isNat64DetectionEnabled())
assertFalse(config.isProvisioningNotificationEnabled())
assertTrue(config.isBypassableVpn())
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/VpnTest.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/VpnTest.java
index 532fd86..8485263 100755
--- a/tests/cts/hostside/app/src/com/android/cts/net/hostside/VpnTest.java
+++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/VpnTest.java
@@ -17,6 +17,7 @@
package com.android.cts.net.hostside;
import static android.Manifest.permission.NETWORK_SETTINGS;
+import static android.net.ConnectivityManager.TYPE_VPN;
import static android.net.NetworkCapabilities.TRANSPORT_VPN;
import static android.os.Process.INVALID_UID;
import static android.system.OsConstants.AF_INET;
@@ -759,6 +760,7 @@
assertEquals(vpnNetwork, mCM.getActiveNetwork());
assertNotEqual(defaultNetwork, vpnNetwork);
maybeExpectVpnTransportInfo(vpnNetwork);
+ assertTrue(mCM.getNetworkInfo(vpnNetwork).getType() == TYPE_VPN);
if (SdkLevel.isAtLeastS()) {
// Check that system default network callback has not seen any network changes, even
diff --git a/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java b/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
index acec9a4..ae59dc4 100644
--- a/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
+++ b/tests/cts/net/src/android/net/cts/ConnectivityManagerTest.java
@@ -152,6 +152,7 @@
import com.android.networkstack.apishim.common.ConnectivityManagerShim;
import com.android.testutils.CompatUtil;
import com.android.testutils.DevSdkIgnoreRule;
+import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo;
import com.android.testutils.DevSdkIgnoreRuleKt;
import com.android.testutils.RecorderCallback.CallbackEntry;
import com.android.testutils.SkipPresubmit;
@@ -674,6 +675,31 @@
.build();
}
+ @Test @IgnoreUpTo(Build.VERSION_CODES.Q)
+ public void testIsPrivateDnsBroken() throws InterruptedException {
+ final String invalidPrivateDnsServer = "invalidhostname.example.com";
+ final String goodPrivateDnsServer = "dns.google";
+ mCtsNetUtils.storePrivateDnsSetting();
+ final TestableNetworkCallback cb = new TestableNetworkCallback();
+ mCm.registerNetworkCallback(makeWifiNetworkRequest(), cb);
+ try {
+ // Verifying the good private DNS sever
+ mCtsNetUtils.setPrivateDnsStrictMode(goodPrivateDnsServer);
+ final Network networkForPrivateDns = mCtsNetUtils.ensureWifiConnected();
+ cb.eventuallyExpect(CallbackEntry.NETWORK_CAPS_UPDATED, NETWORK_CALLBACK_TIMEOUT_MS,
+ entry -> (!((CallbackEntry.CapabilitiesChanged) entry).getCaps()
+ .isPrivateDnsBroken()) && networkForPrivateDns.equals(entry.getNetwork()));
+
+ // Verifying the broken private DNS sever
+ mCtsNetUtils.setPrivateDnsStrictMode(invalidPrivateDnsServer);
+ cb.eventuallyExpect(CallbackEntry.NETWORK_CAPS_UPDATED, NETWORK_CALLBACK_TIMEOUT_MS,
+ entry -> (((CallbackEntry.CapabilitiesChanged) entry).getCaps()
+ .isPrivateDnsBroken()) && networkForPrivateDns.equals(entry.getNetwork()));
+ } finally {
+ mCtsNetUtils.restorePrivateDnsSetting();
+ }
+ }
+
/**
* Exercises both registerNetworkCallback and unregisterNetworkCallback. This checks to
* see if we get a callback for the TRANSPORT_WIFI transport type being available.
diff --git a/tests/cts/net/src/android/net/cts/NetworkRequestTest.java b/tests/cts/net/src/android/net/cts/NetworkRequestTest.java
index bca4456..637ed26 100644
--- a/tests/cts/net/src/android/net/cts/NetworkRequestTest.java
+++ b/tests/cts/net/src/android/net/cts/NetworkRequestTest.java
@@ -30,6 +30,7 @@
import static junit.framework.Assert.fail;
+import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
@@ -456,6 +457,21 @@
}
}
+ @Test @IgnoreUpTo(Build.VERSION_CODES.R)
+ public void testGetCapabilities() {
+ final int[] netCapabilities = new int[] {
+ NET_CAPABILITY_INTERNET,
+ NET_CAPABILITY_NOT_ROAMING };
+ final NetworkCapabilities.Builder builder = NetworkCapabilities.Builder
+ .withoutDefaultCapabilities();
+ for (int capability : netCapabilities) builder.addCapability(capability);
+ final NetworkRequest nr = new NetworkRequest.Builder()
+ .clearCapabilities()
+ .setCapabilities(builder.build())
+ .build();
+ assertArrayEquals(netCapabilities, nr.getCapabilities());
+ }
+
@Test
public void testBuildRequestFromExistingRequestWithBuilder() {
assumeTrue(TestUtils.shouldTestSApis());
diff --git a/tests/unit/java/com/android/server/ConnectivityServiceTest.java b/tests/unit/java/com/android/server/ConnectivityServiceTest.java
index 4530650..be7239d 100644
--- a/tests/unit/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/unit/java/com/android/server/ConnectivityServiceTest.java
@@ -486,6 +486,7 @@
@Mock VpnProfileStore mVpnProfileStore;
@Mock SystemConfigManager mSystemConfigManager;
@Mock Resources mResources;
+ @Mock ProxyTracker mProxyTracker;
private ArgumentCaptor<ResolverParamsParcel> mResolverParamsParcelCaptor =
ArgumentCaptor.forClass(ResolverParamsParcel.class);
@@ -1280,10 +1281,14 @@
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 @@
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 @@
@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 @@
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