Merge "Handle v4-mapped v6 address in Struct parsing" into main
diff --git a/service/Android.bp b/service/Android.bp
index 250693f..7def200 100644
--- a/service/Android.bp
+++ b/service/Android.bp
@@ -185,7 +185,7 @@
"androidx.annotation_annotation",
"connectivity-net-module-utils-bpf",
"connectivity_native_aidl_interface-lateststable-java",
- "dnsresolver_aidl_interface-V11-java",
+ "dnsresolver_aidl_interface-V12-java",
"modules-utils-shell-command-handler",
"net-utils-device-common",
"net-utils-device-common-ip",
diff --git a/service/src/com/android/server/ConnectivityService.java b/service/src/com/android/server/ConnectivityService.java
index dc09237..f877bb0 100755
--- a/service/src/com/android/server/ConnectivityService.java
+++ b/service/src/com/android/server/ConnectivityService.java
@@ -5273,7 +5273,14 @@
private boolean isNetworkPotentialSatisfier(
@NonNull final NetworkAgentInfo candidate, @NonNull final NetworkRequestInfo nri) {
- // listen requests won't keep up a network satisfying it. If this is not a multilayer
+ // While destroyed network sometimes satisfy requests (including occasionally newly
+ // satisfying requests), *potential* satisfiers are networks that might beat a current
+ // champion if they validate. As such, a destroyed network is never a potential satisfier,
+ // because it's never a good idea to keep a destroyed network in case it validates.
+ // For example, declaring it a potential satisfier would keep an unvalidated destroyed
+ // candidate after it's been replaced by another unvalidated network.
+ if (candidate.isDestroyed()) return false;
+ // Listen requests won't keep up a network satisfying it. If this is not a multilayer
// request, return immediately. For multilayer requests, check to see if any of the
// multilayer requests may have a potential satisfier.
if (!nri.isMultilayerRequest() && (nri.mRequests.get(0).isListen()
@@ -5291,8 +5298,12 @@
if (req.isListen() || req.isListenForBest()) {
continue;
}
- // If this Network is already the best Network for a request, or if
- // there is hope for it to become one if it validated, then it is needed.
+ // If there is hope for this network might validate and subsequently become the best
+ // network for that request, then it is needed. Note that this network can't already
+ // be the best for this request, or it would be the current satisfier, and therefore
+ // there would be no need to call this method to find out if it is a *potential*
+ // satisfier ("unneeded", the only caller, only calls this if this network currently
+ // satisfies no request).
if (candidate.satisfies(req)) {
// As soon as a network is found that satisfies a request, return. Specifically for
// multilayer requests, returning as soon as a NetworkAgentInfo satisfies a request
@@ -8898,7 +8909,7 @@
// This network might have been underlying another network. Propagate its capabilities.
propagateUnderlyingNetworkCapabilities(nai.network);
- if (!newNc.equalsTransportTypes(prevNc)) {
+ if (meteredChanged || !newNc.equalsTransportTypes(prevNc)) {
mDnsManager.updateCapabilitiesForNetwork(nai.network.getNetId(), newNc);
}
diff --git a/service/src/com/android/server/connectivity/DnsManager.java b/service/src/com/android/server/connectivity/DnsManager.java
index 81b2289..894bcc4 100644
--- a/service/src/com/android/server/connectivity/DnsManager.java
+++ b/service/src/com/android/server/connectivity/DnsManager.java
@@ -326,10 +326,14 @@
}
/**
- * When creating a new network or transport types are changed in a specific network,
- * capabilities are always saved to a hashMap before update dns config.
- * When destroying network, the specific network will be removed from the hashMap.
- * The hashMap is always accessed on the same thread.
+ * Update {@link NetworkCapabilities} stored in this instance.
+ *
+ * In order to ensure that the resolver has access to necessary information when other events
+ * occur, capabilities are always saved to a hashMap before updating the DNS configuration
+ * whenever a new network is created, transport types are modified, or metered capabilities are
+ * altered for a network. When a network is destroyed, the corresponding entry is removed from
+ * the hashMap. To prevent concurrency issues, the hashMap should always be accessed from the
+ * same thread.
*/
public void updateCapabilitiesForNetwork(int netId, @NonNull final NetworkCapabilities nc) {
mNetworkCapabilitiesMap.put(netId, nc);
@@ -385,6 +389,7 @@
: useTls ? paramsParcel.servers // Opportunistic
: new String[0]; // Off
paramsParcel.transportTypes = nc.getTransportTypes();
+ paramsParcel.meteredNetwork = nc.isMetered();
// Prepare to track the validation status of the DNS servers in the
// resolver config when private DNS is in opportunistic or strict mode.
if (useTls) {
@@ -398,12 +403,13 @@
}
Log.d(TAG, String.format("sendDnsConfigurationForNetwork(%d, %s, %s, %d, %d, %d, %d, "
- + "%d, %d, %s, %s)", paramsParcel.netId, Arrays.toString(paramsParcel.servers),
- Arrays.toString(paramsParcel.domains), paramsParcel.sampleValiditySeconds,
- paramsParcel.successThreshold, paramsParcel.minSamples,
- paramsParcel.maxSamples, paramsParcel.baseTimeoutMsec,
+ + "%d, %d, %s, %s, %s, %b)", paramsParcel.netId,
+ Arrays.toString(paramsParcel.servers), Arrays.toString(paramsParcel.domains),
+ paramsParcel.sampleValiditySeconds, paramsParcel.successThreshold,
+ paramsParcel.minSamples, paramsParcel.maxSamples, paramsParcel.baseTimeoutMsec,
paramsParcel.retryCount, paramsParcel.tlsName,
- Arrays.toString(paramsParcel.tlsServers)));
+ Arrays.toString(paramsParcel.tlsServers),
+ Arrays.toString(paramsParcel.transportTypes), paramsParcel.meteredNetwork));
try {
mDnsResolver.setResolverConfiguration(paramsParcel);
diff --git a/staticlibs/tests/unit/src/com/android/testutils/HandlerUtilsTest.kt b/staticlibs/tests/unit/src/com/android/testutils/HandlerUtilsTest.kt
index 0f6fa48..440b836 100644
--- a/staticlibs/tests/unit/src/com/android/testutils/HandlerUtilsTest.kt
+++ b/staticlibs/tests/unit/src/com/android/testutils/HandlerUtilsTest.kt
@@ -27,7 +27,7 @@
import org.junit.runners.JUnit4
private const val ATTEMPTS = 50 // Causes testWaitForIdle to take about 150ms on aosp_crosshatch-eng
-private const val TIMEOUT_MS = 200
+private const val TIMEOUT_MS = 1000
@RunWith(JUnit4::class)
class HandlerUtilsTest {
diff --git a/tests/unit/java/com/android/server/ConnectivityServiceTest.java b/tests/unit/java/com/android/server/ConnectivityServiceTest.java
index 82fb651..3649c5e 100755
--- a/tests/unit/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/unit/java/com/android/server/ConnectivityServiceTest.java
@@ -417,7 +417,6 @@
import com.android.server.connectivity.QosCallbackTracker;
import com.android.server.connectivity.TcpKeepaliveController;
import com.android.server.connectivity.UidRangeUtils;
-import com.android.server.connectivity.Vpn;
import com.android.server.connectivity.VpnProfileStore;
import com.android.server.net.NetworkPinner;
import com.android.testutils.DevSdkIgnoreRule;
@@ -1497,13 +1496,8 @@
return uidRangesForUids(CollectionUtils.toIntArray(uids));
}
- private static Looper startHandlerThreadAndReturnLooper() {
- final HandlerThread handlerThread = new HandlerThread("MockVpnThread");
- handlerThread.start();
- return handlerThread.getLooper();
- }
-
- private class MockVpn extends Vpn implements TestableNetworkCallback.HasNetwork {
+ // Helper class to mock vpn interaction.
+ private class MockVpn implements TestableNetworkCallback.HasNetwork {
// Note : Please do not add any new instrumentation here. If you need new instrumentation,
// please add it in CSTest and use subclasses of CSTest instead of adding more
// tools in ConnectivityServiceTest.
@@ -1511,45 +1505,23 @@
// Careful ! This is different from mNetworkAgent, because MockNetworkAgent does
// not inherit from NetworkAgent.
private TestNetworkAgentWrapper mMockNetworkAgent;
+ // Initialize a stored NetworkCapabilities following the defaults of VPN. The TransportInfo
+ // should at least be updated to a valid VPN type before usage, see registerAgent(...).
+ private NetworkCapabilities mNetworkCapabilities = new NetworkCapabilities.Builder()
+ .addTransportType(NetworkCapabilities.TRANSPORT_VPN)
+ .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN)
+ .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED)
+ .setTransportInfo(new VpnTransportInfo(
+ VpnManager.TYPE_VPN_NONE,
+ null /* sessionId */,
+ false /* bypassable */,
+ false /* longLivedTcpConnectionsExpensive */))
+ .build();
private boolean mAgentRegistered = false;
private int mVpnType = VpnManager.TYPE_VPN_SERVICE;
- private UnderlyingNetworkInfo mUnderlyingNetworkInfo;
private String mSessionKey;
- public MockVpn(int userId) {
- super(startHandlerThreadAndReturnLooper(), mServiceContext,
- new Dependencies() {
- @Override
- public boolean isCallerSystem() {
- return true;
- }
-
- @Override
- public DeviceIdleInternal getDeviceIdleInternal() {
- return mDeviceIdleInternal;
- }
- },
- mNetworkManagementService, mMockNetd, userId, mVpnProfileStore,
- new SystemServices(mServiceContext) {
- @Override
- public String settingsSecureGetStringForUser(String key, int userId) {
- switch (key) {
- // Settings keys not marked as @Readable are not readable from
- // non-privileged apps, unless marked as testOnly=true
- // (atest refuses to install testOnly=true apps), even if mocked
- // in the content provider, because
- // Settings.Secure.NameValueCache#getStringForUser checks the key
- // before querying the mock settings provider.
- case Settings.Secure.ALWAYS_ON_VPN_APP:
- return null;
- default:
- return super.settingsSecureGetStringForUser(key, userId);
- }
- }
- }, new Ikev2SessionCreator());
- }
-
public void setUids(Set<UidRange> uids) {
mNetworkCapabilities.setUids(UidRange.toIntRanges(uids));
if (mAgentRegistered) {
@@ -1561,7 +1533,6 @@
mVpnType = vpnType;
}
- @Override
public Network getNetwork() {
return (mMockNetworkAgent == null) ? null : mMockNetworkAgent.getNetwork();
}
@@ -1570,7 +1541,6 @@
return null == mMockNetworkAgent ? null : mMockNetworkAgent.getNetworkAgentConfig();
}
- @Override
public int getActiveVpnType() {
return mVpnType;
}
@@ -1584,14 +1554,11 @@
private void registerAgent(boolean isAlwaysMetered, Set<UidRange> uids, LinkProperties lp)
throws Exception {
if (mAgentRegistered) throw new IllegalStateException("already registered");
- updateState(NetworkInfo.DetailedState.CONNECTING, "registerAgent");
- mConfig = new VpnConfig();
- mConfig.session = "MySession12345";
+ final String session = "MySession12345";
setUids(uids);
if (!isAlwaysMetered) mNetworkCapabilities.addCapability(NET_CAPABILITY_NOT_METERED);
- mInterface = VPN_IFNAME;
mNetworkCapabilities.setTransportInfo(new VpnTransportInfo(getActiveVpnType(),
- mConfig.session));
+ session));
mMockNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_VPN, lp,
mNetworkCapabilities);
mMockNetworkAgent.waitForIdle(TIMEOUT_MS);
@@ -1605,9 +1572,7 @@
mAgentRegistered = true;
verify(mMockNetd).networkCreate(nativeNetworkConfigVpn(getNetwork().netId,
!mMockNetworkAgent.isBypassableVpn(), mVpnType));
- updateState(NetworkInfo.DetailedState.CONNECTED, "registerAgent");
mNetworkCapabilities.set(mMockNetworkAgent.getNetworkCapabilities());
- mNetworkAgent = mMockNetworkAgent.getNetworkAgent();
}
private void registerAgent(Set<UidRange> uids) throws Exception {
@@ -1667,23 +1632,20 @@
public void disconnect() {
if (mMockNetworkAgent != null) {
mMockNetworkAgent.disconnect();
- updateState(NetworkInfo.DetailedState.DISCONNECTED, "disconnect");
}
mAgentRegistered = false;
setUids(null);
// Remove NET_CAPABILITY_INTERNET or MockNetworkAgent will refuse to connect later on.
mNetworkCapabilities.removeCapability(NET_CAPABILITY_INTERNET);
- mInterface = null;
}
- private synchronized void startLegacyVpn() {
- updateState(DetailedState.CONNECTING, "startLegacyVpn");
+ private void startLegacyVpn() {
+ // Do nothing.
}
// Mock the interaction of IkeV2VpnRunner start. In the context of ConnectivityService,
// setVpnDefaultForUids() is the main interaction and a sessionKey is stored.
- private synchronized void startPlatformVpn() {
- updateState(DetailedState.CONNECTING, "startPlatformVpn");
+ private void startPlatformVpn() {
mSessionKey = UUID.randomUUID().toString();
// Assuming no disallowed applications
final Set<Range<Integer>> ranges = UidRange.toIntRanges(Set.of(PRIMARY_UIDRANGE));
@@ -1692,7 +1654,6 @@
waitForIdle();
}
- @Override
public void startLegacyVpnPrivileged(VpnProfile profile,
@Nullable Network underlying, @NonNull LinkProperties egress) {
switch (profile.type) {
@@ -1714,8 +1675,7 @@
}
}
- @Override
- public synchronized void stopVpnRunnerPrivileged() {
+ public void stopVpnRunnerPrivileged() {
if (mSessionKey != null) {
// Clear vpn network preference.
mCm.setVpnDefaultForUids(mSessionKey, Collections.EMPTY_LIST);
@@ -1724,20 +1684,7 @@
disconnect();
}
- @Override
- public synchronized UnderlyingNetworkInfo getUnderlyingNetworkInfo() {
- if (mUnderlyingNetworkInfo != null) return mUnderlyingNetworkInfo;
-
- return super.getUnderlyingNetworkInfo();
- }
-
- private synchronized void setUnderlyingNetworkInfo(
- UnderlyingNetworkInfo underlyingNetworkInfo) {
- mUnderlyingNetworkInfo = underlyingNetworkInfo;
- }
-
- @Override
- public synchronized boolean setUnderlyingNetworks(@Nullable Network[] networks) {
+ public boolean setUnderlyingNetworks(@Nullable Network[] networks) {
if (!mAgentRegistered) return false;
mMockNetworkAgent.setUnderlyingNetworks(
(networks == null) ? null : Arrays.asList(networks));
@@ -1774,11 +1721,6 @@
waitForIdle();
}
- private void mockVpn(int uid) {
- int userId = UserHandle.getUserId(uid);
- mMockVpn = new MockVpn(userId);
- }
-
private void mockUidNetworkingBlocked() {
doAnswer(i -> isUidBlocked(mBlockedReasons, i.getArgument(1))
).when(mNetworkPolicyManager).isUidNetworkingBlocked(anyInt(), anyBoolean());
@@ -1990,7 +1932,7 @@
mService.systemReadyInternal();
verify(mMockDnsResolver).registerUnsolicitedEventListener(any());
- mockVpn(Process.myUid());
+ mMockVpn = new MockVpn();
mCm.bindProcessToNetwork(null);
mQosCallbackTracker = mock(QosCallbackTracker.class);
@@ -10268,7 +10210,6 @@
// Init lockdown state to simulate LockdownVpnTracker behavior.
mCm.setLegacyLockdownVpnEnabled(true);
- mMockVpn.setEnableTeardown(false);
final List<Range<Integer>> ranges =
intRangesPrimaryExcludingUids(Collections.EMPTY_LIST /* excludedeUids */);
mCm.setRequireVpnForUids(true /* requireVpn */, ranges);
@@ -12596,9 +12537,6 @@
mMockVpn.establish(new LinkProperties(), vpnOwnerUid, vpnRange);
assertVpnUidRangesUpdated(true, vpnRange, vpnOwnerUid);
- final UnderlyingNetworkInfo underlyingNetworkInfo =
- new UnderlyingNetworkInfo(vpnOwnerUid, VPN_IFNAME, new ArrayList<>());
- mMockVpn.setUnderlyingNetworkInfo(underlyingNetworkInfo);
mDeps.setConnectionOwnerUid(42);
}
diff --git a/tests/unit/java/com/android/server/connectivity/DnsManagerTest.java b/tests/unit/java/com/android/server/connectivity/DnsManagerTest.java
index 9a6fd38..545ed16 100644
--- a/tests/unit/java/com/android/server/connectivity/DnsManagerTest.java
+++ b/tests/unit/java/com/android/server/connectivity/DnsManagerTest.java
@@ -139,7 +139,8 @@
assertEquals(actual.tlsConnectTimeoutMs, expected.tlsConnectTimeoutMs);
assertResolverOptionsEquals(actual.resolverOptions, expected.resolverOptions);
assertContainsExactly(actual.transportTypes, expected.transportTypes);
- assertFieldCountEquals(16, ResolverParamsParcel.class);
+ assertEquals(actual.meteredNetwork, expected.meteredNetwork);
+ assertFieldCountEquals(17, ResolverParamsParcel.class);
}
@Before
@@ -379,6 +380,7 @@
expectedParams.tlsServers = new String[]{"3.3.3.3", "4.4.4.4"};
expectedParams.transportTypes = TEST_TRANSPORT_TYPES;
expectedParams.resolverOptions = null;
+ expectedParams.meteredNetwork = true;
assertResolverParamsEquals(actualParams, expectedParams);
}
diff --git a/tests/unit/java/com/android/server/connectivityservice/CSDestroyedNetworkTests.kt b/tests/unit/java/com/android/server/connectivityservice/CSDestroyedNetworkTests.kt
new file mode 100644
index 0000000..d40c035
--- /dev/null
+++ b/tests/unit/java/com/android/server/connectivityservice/CSDestroyedNetworkTests.kt
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server
+
+import android.net.NetworkCapabilities
+import android.net.NetworkCapabilities.TRANSPORT_WIFI
+import android.net.NetworkRequest
+import com.android.testutils.RecorderCallback.CallbackEntry.Lost
+import com.android.testutils.TestableNetworkCallback
+import org.junit.Test
+
+private const val LONG_TIMEOUT_MS = 5_000
+
+class CSDestroyedNetworkTests : CSTest() {
+ @Test
+ fun testDestroyNetworkNotKeptWhenUnvalidated() {
+ val nc = NetworkCapabilities.Builder()
+ .addTransportType(TRANSPORT_WIFI)
+ .build()
+
+ val nr = NetworkRequest.Builder()
+ .clearCapabilities()
+ .addTransportType(TRANSPORT_WIFI)
+ .build()
+ val cbRequest = TestableNetworkCallback()
+ val cbCallback = TestableNetworkCallback()
+ cm.requestNetwork(nr, cbRequest)
+ cm.registerNetworkCallback(nr, cbCallback)
+
+ val firstAgent = Agent(nc = nc)
+ firstAgent.connect()
+ cbCallback.expectAvailableCallbacks(firstAgent.network, validated = false)
+
+ firstAgent.unregisterAfterReplacement(LONG_TIMEOUT_MS)
+
+ val secondAgent = Agent(nc = nc)
+ secondAgent.connect()
+ cbCallback.expectAvailableCallbacks(secondAgent.network, validated = false)
+
+ cbCallback.expect<Lost>(timeoutMs = 500) { it.network == firstAgent.network }
+ }
+}
diff --git a/tests/unit/java/com/android/server/connectivityservice/base/CSAgentWrapper.kt b/tests/unit/java/com/android/server/connectivityservice/base/CSAgentWrapper.kt
index 094ded3..8860895 100644
--- a/tests/unit/java/com/android/server/connectivityservice/base/CSAgentWrapper.kt
+++ b/tests/unit/java/com/android/server/connectivityservice/base/CSAgentWrapper.kt
@@ -34,7 +34,6 @@
import android.net.NetworkTestResultParcelable
import android.net.networkstack.NetworkStackClientBase
import android.os.HandlerThread
-import com.android.modules.utils.build.SdkLevel
import com.android.testutils.RecorderCallback.CallbackEntry.Available
import com.android.testutils.RecorderCallback.CallbackEntry.Lost
import com.android.testutils.TestableNetworkCallback
@@ -168,5 +167,6 @@
cb.eventuallyExpect<Lost> { it.network == agent.network }
}
+ fun unregisterAfterReplacement(timeoutMs: Int) = agent.unregisterAfterReplacement(timeoutMs)
fun sendLocalNetworkConfig(lnc: LocalNetworkConfig) = agent.sendLocalNetworkConfig(lnc)
}